agentool 0.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +738 -52
- package/dist/ask-user/index.cjs +8 -0
- package/dist/ask-user/index.d.cts +68 -0
- package/dist/ask-user/index.d.ts +68 -0
- package/dist/ask-user/index.js +8 -0
- package/dist/bash/index.cjs +8 -0
- package/dist/bash/index.d.cts +63 -0
- package/dist/bash/index.d.ts +63 -0
- package/dist/bash/index.js +8 -0
- package/dist/chunk-3EPGFWZV.cjs +30 -0
- package/dist/chunk-3VO6NETR.cjs +79 -0
- package/dist/chunk-44AFQ2B7.js +30 -0
- package/dist/chunk-4HIATLKI.js +112 -0
- package/dist/chunk-4HXAKPQH.cjs +36 -0
- package/dist/chunk-4YI2H55A.js +142 -0
- package/dist/chunk-56CL4JCW.cjs +53 -0
- package/dist/chunk-5NW4OGRI.cjs +99 -0
- package/dist/chunk-5O55DKOB.cjs +112 -0
- package/dist/chunk-5TDZF4IM.cjs +197 -0
- package/dist/chunk-6DJSWTWQ.cjs +40 -0
- package/dist/chunk-6PQLFDGT.js +117 -0
- package/dist/chunk-ACGW44YT.js +47 -0
- package/dist/chunk-CAEVLIQB.cjs +117 -0
- package/dist/chunk-CGTPF6IS.js +90 -0
- package/dist/chunk-E6NBEYZD.js +51 -0
- package/dist/chunk-EA3YV7ZG.js +79 -0
- package/dist/chunk-ECYT46FP.js +40 -0
- package/dist/chunk-FV2R5FFQ.cjs +102 -0
- package/dist/chunk-FW3UJ622.cjs +59 -0
- package/dist/chunk-G3ITTPGX.js +99 -0
- package/dist/chunk-HDKXSKMO.js +30 -0
- package/dist/chunk-HNP7JDQC.cjs +159 -0
- package/dist/chunk-HNUL2CID.cjs +34 -0
- package/dist/chunk-HZAQRHBT.js +99 -0
- package/dist/chunk-I3ONDY7P.js +46 -0
- package/dist/chunk-I6KFFQPV.cjs +58 -0
- package/dist/chunk-IEX4NOVN.cjs +48 -0
- package/dist/chunk-IRRNYFI5.js +48 -0
- package/dist/chunk-K77GC2QI.js +59 -0
- package/dist/chunk-L5JH4I77.cjs +51 -0
- package/dist/chunk-LK6SQH2G.cjs +30 -0
- package/dist/chunk-LPV5CN2K.js +58 -0
- package/dist/chunk-LTE5NG4D.js +53 -0
- package/dist/chunk-MF7CJVIZ.js +40 -0
- package/dist/chunk-MIYA7TNR.cjs +123 -0
- package/dist/chunk-MJCAXASI.js +123 -0
- package/dist/chunk-OM2UFTGS.cjs +47 -0
- package/dist/chunk-ONBH74ZV.cjs +90 -0
- package/dist/chunk-OXLQ7QVL.cjs +40 -0
- package/dist/chunk-P6Z5XFDS.js +73 -0
- package/dist/chunk-QZ5GS6HW.cjs +46 -0
- package/dist/chunk-S7IVHOA6.js +75 -0
- package/dist/chunk-SUSAPI5W.cjs +142 -0
- package/dist/chunk-TMW3XKKJ.js +34 -0
- package/dist/chunk-UDIG7332.js +159 -0
- package/dist/chunk-VLNDEVKS.js +102 -0
- package/dist/chunk-VXZ4RKJI.js +36 -0
- package/dist/chunk-XAQGZ374.js +197 -0
- package/dist/chunk-YPPPGGLA.cjs +99 -0
- package/dist/chunk-ZBLQV6UO.cjs +73 -0
- package/dist/chunk-ZFQZWXOI.cjs +75 -0
- package/dist/context-compaction/index.cjs +8 -0
- package/dist/context-compaction/index.d.cts +77 -0
- package/dist/context-compaction/index.d.ts +77 -0
- package/dist/context-compaction/index.js +8 -0
- package/dist/diff/index.cjs +9 -0
- package/dist/diff/index.d.cts +72 -0
- package/dist/diff/index.d.ts +72 -0
- package/dist/diff/index.js +9 -0
- package/dist/edit/index.cjs +10 -0
- package/dist/edit/index.d.cts +53 -0
- package/dist/edit/index.d.ts +53 -0
- package/dist/edit/index.js +10 -0
- package/dist/glob/index.cjs +10 -0
- package/dist/glob/index.d.cts +47 -0
- package/dist/glob/index.d.ts +47 -0
- package/dist/glob/index.js +10 -0
- package/dist/grep/index.cjs +10 -0
- package/dist/grep/index.d.cts +50 -0
- package/dist/grep/index.d.ts +50 -0
- package/dist/grep/index.js +10 -0
- package/dist/http-request/index.cjs +8 -0
- package/dist/http-request/index.d.cts +60 -0
- package/dist/http-request/index.d.ts +60 -0
- package/dist/http-request/index.js +8 -0
- package/dist/index.cjs +133 -0
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +133 -0
- package/dist/lsp/index.cjs +10 -0
- package/dist/lsp/index.d.cts +35 -0
- package/dist/lsp/index.d.ts +35 -0
- package/dist/lsp/index.js +10 -0
- package/dist/memory/index.cjs +9 -0
- package/dist/memory/index.d.cts +63 -0
- package/dist/memory/index.d.ts +63 -0
- package/dist/memory/index.js +9 -0
- package/dist/multi-edit/index.cjs +11 -0
- package/dist/multi-edit/index.d.cts +72 -0
- package/dist/multi-edit/index.d.ts +72 -0
- package/dist/multi-edit/index.js +11 -0
- package/dist/read/index.cjs +10 -0
- package/dist/read/index.d.cts +67 -0
- package/dist/read/index.d.ts +67 -0
- package/dist/read/index.js +10 -0
- package/dist/sleep/index.cjs +8 -0
- package/dist/sleep/index.d.cts +60 -0
- package/dist/sleep/index.d.ts +60 -0
- package/dist/sleep/index.js +8 -0
- package/dist/task-create/index.cjs +9 -0
- package/dist/task-create/index.d.cts +19 -0
- package/dist/task-create/index.d.ts +19 -0
- package/dist/task-create/index.js +9 -0
- package/dist/task-get/index.cjs +9 -0
- package/dist/task-get/index.d.cts +15 -0
- package/dist/task-get/index.d.ts +15 -0
- package/dist/task-get/index.js +9 -0
- package/dist/task-list/index.cjs +9 -0
- package/dist/task-list/index.d.cts +11 -0
- package/dist/task-list/index.d.ts +11 -0
- package/dist/task-list/index.js +9 -0
- package/dist/task-update/index.cjs +9 -0
- package/dist/task-update/index.d.cts +31 -0
- package/dist/task-update/index.d.ts +31 -0
- package/dist/task-update/index.js +9 -0
- package/dist/tool-search/index.cjs +8 -0
- package/dist/tool-search/index.d.cts +18 -0
- package/dist/tool-search/index.d.ts +18 -0
- package/dist/tool-search/index.js +8 -0
- package/dist/types-3QPDuCXN.d.cts +45 -0
- package/dist/types-3QPDuCXN.d.ts +45 -0
- package/dist/web-fetch/index.cjs +8 -0
- package/dist/web-fetch/index.d.cts +54 -0
- package/dist/web-fetch/index.d.ts +54 -0
- package/dist/web-fetch/index.js +8 -0
- package/dist/web-search/index.cjs +8 -0
- package/dist/web-search/index.d.cts +21 -0
- package/dist/web-search/index.d.ts +21 -0
- package/dist/web-search/index.js +8 -0
- package/dist/write/index.cjs +10 -0
- package/dist/write/index.d.cts +47 -0
- package/dist/write/index.d.ts +47 -0
- package/dist/write/index.js +10 -0
- package/package.json +170 -20
- package/dist/core/index.d.ts +0 -20
- package/dist/core/index.js +0 -1
- package/dist/core/index.js.map +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
var _chunkQZ5GS6HWcjs = require('./chunk-QZ5GS6HW.cjs');
|
|
4
|
+
|
|
5
|
+
// src/diff/index.ts
|
|
6
|
+
var _promises = require('fs/promises');
|
|
7
|
+
var _ai = require('ai');
|
|
8
|
+
var _zod = require('zod');
|
|
9
|
+
|
|
10
|
+
// src/shared/diff.ts
|
|
11
|
+
|
|
12
|
+
var _diff = require('diff');
|
|
13
|
+
function diffStrings(oldContent, newContent, options) {
|
|
14
|
+
if (oldContent === newContent) {
|
|
15
|
+
return "No differences found.";
|
|
16
|
+
}
|
|
17
|
+
const context = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.context]), () => ( 3));
|
|
18
|
+
const oldLabel = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.oldLabel]), () => ( "a"));
|
|
19
|
+
const newLabel = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _3 => _3.newLabel]), () => ( "b"));
|
|
20
|
+
return _diff.createTwoFilesPatch.call(void 0,
|
|
21
|
+
oldLabel,
|
|
22
|
+
newLabel,
|
|
23
|
+
oldContent,
|
|
24
|
+
newContent,
|
|
25
|
+
void 0,
|
|
26
|
+
void 0,
|
|
27
|
+
{ context }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
async function diffFiles(oldFilePath, newFilePath, options) {
|
|
31
|
+
const [oldContent, newContent] = await Promise.all([
|
|
32
|
+
_promises.readFile.call(void 0, oldFilePath, "utf-8"),
|
|
33
|
+
_promises.readFile.call(void 0, newFilePath, "utf-8")
|
|
34
|
+
]);
|
|
35
|
+
return diffStrings(oldContent, newContent, {
|
|
36
|
+
...options,
|
|
37
|
+
oldLabel: oldFilePath,
|
|
38
|
+
newLabel: newFilePath
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/diff/index.ts
|
|
43
|
+
function createDiff(config = {}) {
|
|
44
|
+
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
|
|
45
|
+
return _ai.tool.call(void 0, {
|
|
46
|
+
description: "Generate a unified diff between two files or two strings. Provide file_path + other_file_path to compare files, or old_content + new_content to compare strings. You can also provide file_path with old_content or new_content to compare a file against provided content.",
|
|
47
|
+
inputSchema: _zod.z.object({
|
|
48
|
+
file_path: _zod.z.string().optional().describe("Path to the first file (absolute or relative to cwd)"),
|
|
49
|
+
other_file_path: _zod.z.string().optional().describe("Path to the second file (absolute or relative to cwd)"),
|
|
50
|
+
old_content: _zod.z.string().optional().describe("The original content string"),
|
|
51
|
+
new_content: _zod.z.string().optional().describe("The modified content string")
|
|
52
|
+
}),
|
|
53
|
+
execute: async ({ file_path, other_file_path, old_content, new_content }) => {
|
|
54
|
+
try {
|
|
55
|
+
if (file_path && other_file_path) {
|
|
56
|
+
const resolvedOld = _chunkQZ5GS6HWcjs.expandPath.call(void 0, file_path, cwd);
|
|
57
|
+
const resolvedNew = _chunkQZ5GS6HWcjs.expandPath.call(void 0, other_file_path, cwd);
|
|
58
|
+
return await diffFiles(resolvedOld, resolvedNew);
|
|
59
|
+
}
|
|
60
|
+
if (old_content !== void 0 && new_content !== void 0 && !file_path) {
|
|
61
|
+
return diffStrings(old_content, new_content);
|
|
62
|
+
}
|
|
63
|
+
if (file_path && (old_content !== void 0 || new_content !== void 0)) {
|
|
64
|
+
const resolvedPath = _chunkQZ5GS6HWcjs.expandPath.call(void 0, file_path, cwd);
|
|
65
|
+
const fileContent = await _promises.readFile.call(void 0, resolvedPath, "utf-8");
|
|
66
|
+
if (old_content !== void 0) {
|
|
67
|
+
return diffStrings(old_content, fileContent, {
|
|
68
|
+
oldLabel: "provided",
|
|
69
|
+
newLabel: resolvedPath
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return diffStrings(fileContent, new_content, {
|
|
73
|
+
oldLabel: resolvedPath,
|
|
74
|
+
newLabel: "provided"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return "Error [diff]: Insufficient parameters. Provide either: (1) file_path + other_file_path, (2) old_content + new_content, or (3) file_path + old_content/new_content.";
|
|
78
|
+
} catch (error) {
|
|
79
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
80
|
+
return `Error [diff]: ${msg}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
var diff = createDiff();
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
exports.createDiff = createDiff; exports.diff = diff;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/sleep/index.ts
|
|
2
|
+
var _ai = require('ai');
|
|
3
|
+
var _zod = require('zod');
|
|
4
|
+
function createSleep(config = {}) {
|
|
5
|
+
const maxDuration = _nullishCoalesce(config.maxDuration, () => ( 3e5));
|
|
6
|
+
return _ai.tool.call(void 0, {
|
|
7
|
+
description: "Pause execution for a specified duration. Useful for rate limiting, polling intervals, or waiting for external processes. Maximum duration is 300 seconds (5 minutes).",
|
|
8
|
+
inputSchema: _ai.zodSchema.call(void 0,
|
|
9
|
+
_zod.z.object({
|
|
10
|
+
durationMs: _zod.z.number().describe("Duration to sleep in milliseconds"),
|
|
11
|
+
reason: _zod.z.string().optional().describe("Optional reason for the sleep")
|
|
12
|
+
})
|
|
13
|
+
),
|
|
14
|
+
execute: async ({ durationMs, reason }) => {
|
|
15
|
+
try {
|
|
16
|
+
const clamped = Math.max(0, Math.min(durationMs, maxDuration));
|
|
17
|
+
const start = Date.now();
|
|
18
|
+
await new Promise((resolve) => setTimeout(resolve, clamped));
|
|
19
|
+
const elapsed = Date.now() - start;
|
|
20
|
+
const parts = [`Slept for ${elapsed}ms`];
|
|
21
|
+
if (reason) parts.push(`Reason: ${reason}`);
|
|
22
|
+
if (clamped !== durationMs) {
|
|
23
|
+
parts.push(
|
|
24
|
+
`(clamped from ${durationMs}ms to ${clamped}ms, max: ${maxDuration}ms)`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return parts.join(". ");
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
+
return `Sleep failed: ${message}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
var sleep = createSleep();
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
exports.createSleep = createSleep; exports.sleep = sleep;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyEditToFile,
|
|
3
|
+
findActualString,
|
|
4
|
+
preserveQuoteStyle
|
|
5
|
+
} from "./chunk-G3ITTPGX.js";
|
|
6
|
+
import {
|
|
7
|
+
expandPath
|
|
8
|
+
} from "./chunk-I3ONDY7P.js";
|
|
9
|
+
|
|
10
|
+
// src/edit/index.ts
|
|
11
|
+
import { readFile, writeFile } from "fs/promises";
|
|
12
|
+
import { tool } from "ai";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
function createEdit(config = {}) {
|
|
15
|
+
return tool({
|
|
16
|
+
description: "Perform an exact string replacement in a file. Locates old_string in the file and replaces it with new_string. Supports curly-quote fallback matching. When replace_all is false (default), old_string must appear exactly once.",
|
|
17
|
+
inputSchema: z.object({
|
|
18
|
+
file_path: z.string().describe("The absolute path to the file to modify"),
|
|
19
|
+
old_string: z.string().describe("The exact string to find and replace"),
|
|
20
|
+
new_string: z.string().describe("The replacement string"),
|
|
21
|
+
replace_all: z.boolean().default(false).optional().describe("Replace all occurrences (default: false)")
|
|
22
|
+
}),
|
|
23
|
+
execute: async ({ file_path, old_string, new_string, replace_all }) => {
|
|
24
|
+
try {
|
|
25
|
+
const resolved = expandPath(file_path, config.cwd);
|
|
26
|
+
let content;
|
|
27
|
+
try {
|
|
28
|
+
content = await readFile(resolved, "utf-8");
|
|
29
|
+
} catch (err) {
|
|
30
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
31
|
+
return `Error [edit]: Cannot read file "${resolved}": ${msg}`;
|
|
32
|
+
}
|
|
33
|
+
if (old_string === new_string) {
|
|
34
|
+
return "Error [edit]: old_string and new_string are identical \u2014 nothing to change.";
|
|
35
|
+
}
|
|
36
|
+
const actualOld = findActualString(content, old_string);
|
|
37
|
+
if (actualOld === null) {
|
|
38
|
+
const preview = content.slice(0, 200);
|
|
39
|
+
return `Error [edit]: old_string not found in "${resolved}". File starts with:
|
|
40
|
+
${preview}`;
|
|
41
|
+
}
|
|
42
|
+
if (!replace_all) {
|
|
43
|
+
let count = 0;
|
|
44
|
+
let pos = 0;
|
|
45
|
+
while (pos < content.length) {
|
|
46
|
+
const idx = content.indexOf(actualOld, pos);
|
|
47
|
+
if (idx === -1) break;
|
|
48
|
+
count++;
|
|
49
|
+
pos = idx + 1;
|
|
50
|
+
}
|
|
51
|
+
if (count > 1) {
|
|
52
|
+
return `Error [edit]: old_string appears ${count} times in "${resolved}". Use replace_all to replace every occurrence, or provide a more specific string.`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const styledNew = preserveQuoteStyle(old_string, actualOld, new_string);
|
|
56
|
+
const updated = applyEditToFile(content, actualOld, styledNew, replace_all);
|
|
57
|
+
await writeFile(resolved, updated, "utf-8");
|
|
58
|
+
const snippet = styledNew.length > 0 ? styledNew.slice(0, 200) : "(deletion)";
|
|
59
|
+
return `Successfully edited "${resolved}". Replacement snippet:
|
|
60
|
+
${snippet}`;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
63
|
+
return `Error [edit]: ${msg}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
var edit = createEdit();
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
createEdit,
|
|
72
|
+
edit
|
|
73
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/shared/path.ts
|
|
2
|
+
var _os = require('os');
|
|
3
|
+
var _path = require('path');
|
|
4
|
+
function expandPath(inputPath, baseDir) {
|
|
5
|
+
const actualBaseDir = _nullishCoalesce(baseDir, () => ( process.cwd()));
|
|
6
|
+
if (typeof inputPath !== "string") {
|
|
7
|
+
throw new TypeError(
|
|
8
|
+
`Path must be a string, received ${typeof inputPath}`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
if (typeof actualBaseDir !== "string") {
|
|
12
|
+
throw new TypeError(
|
|
13
|
+
`Base directory must be a string, received ${typeof actualBaseDir}`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
if (inputPath.includes("\0") || actualBaseDir.includes("\0")) {
|
|
17
|
+
throw new Error("Path contains null bytes");
|
|
18
|
+
}
|
|
19
|
+
const trimmed = inputPath.trim();
|
|
20
|
+
if (!trimmed) {
|
|
21
|
+
return _path.normalize.call(void 0, actualBaseDir);
|
|
22
|
+
}
|
|
23
|
+
if (trimmed === "~") {
|
|
24
|
+
return _os.homedir.call(void 0, );
|
|
25
|
+
}
|
|
26
|
+
if (trimmed.startsWith("~/")) {
|
|
27
|
+
return _path.join.call(void 0, _os.homedir.call(void 0, ), trimmed.slice(2));
|
|
28
|
+
}
|
|
29
|
+
if (_path.isAbsolute.call(void 0, trimmed)) {
|
|
30
|
+
return _path.normalize.call(void 0, trimmed);
|
|
31
|
+
}
|
|
32
|
+
return _path.resolve.call(void 0, actualBaseDir, trimmed);
|
|
33
|
+
}
|
|
34
|
+
function toRelativePath(absolutePath, baseDir) {
|
|
35
|
+
const rel = _path.relative.call(void 0, _nullishCoalesce(baseDir, () => ( process.cwd())), absolutePath);
|
|
36
|
+
return rel.startsWith("..") ? absolutePath : rel;
|
|
37
|
+
}
|
|
38
|
+
function containsPathTraversal(inputPath) {
|
|
39
|
+
return /(?:^|[\\/])\.\.(?:[\\/]|$)/.test(inputPath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
exports.expandPath = expandPath; exports.toRelativePath = toRelativePath; exports.containsPathTraversal = containsPathTraversal;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatTask,
|
|
3
|
+
loadTasks,
|
|
4
|
+
saveTasks
|
|
5
|
+
} from "./chunk-E6NBEYZD.js";
|
|
6
|
+
|
|
7
|
+
// src/task-update/index.ts
|
|
8
|
+
import { tool } from "ai";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
function createTaskUpdate(config = {}) {
|
|
12
|
+
const cwd = config.cwd ?? process.cwd();
|
|
13
|
+
const tasksFile = config.tasksFile ?? join(cwd, ".agentool", "tasks.json");
|
|
14
|
+
return tool({
|
|
15
|
+
description: "Update a task by its ID. Can change status, subject, description, owner, metadata, and dependency relationships.",
|
|
16
|
+
inputSchema: z.object({
|
|
17
|
+
taskId: z.string().describe("The ID of the task to update"),
|
|
18
|
+
subject: z.string().optional().describe("New subject for the task"),
|
|
19
|
+
description: z.string().optional().describe("New description"),
|
|
20
|
+
status: z.enum(["pending", "in_progress", "completed", "deleted"]).optional().describe("New status for the task"),
|
|
21
|
+
owner: z.string().optional().describe("New owner for the task"),
|
|
22
|
+
activeForm: z.string().optional().describe("Present continuous form shown in spinner when in_progress"),
|
|
23
|
+
addBlocks: z.array(z.string()).optional().describe("Task IDs that this task blocks"),
|
|
24
|
+
addBlockedBy: z.array(z.string()).optional().describe("Task IDs that block this task"),
|
|
25
|
+
metadata: z.record(z.string(), z.unknown()).optional().describe("Metadata keys to merge. Set key to null to delete.")
|
|
26
|
+
}),
|
|
27
|
+
execute: async (input) => {
|
|
28
|
+
try {
|
|
29
|
+
const tasks = await loadTasks(tasksFile);
|
|
30
|
+
const idx = tasks.findIndex((t) => t.id === input.taskId);
|
|
31
|
+
if (idx === -1) return `Error [task-update]: Task "${input.taskId}" not found.`;
|
|
32
|
+
const entry = tasks[idx];
|
|
33
|
+
if (input.subject !== void 0) entry.subject = input.subject;
|
|
34
|
+
if (input.description !== void 0) entry.description = input.description;
|
|
35
|
+
if (input.status !== void 0) entry.status = input.status;
|
|
36
|
+
if (input.owner !== void 0) entry.owner = input.owner;
|
|
37
|
+
if (input.activeForm !== void 0) entry.activeForm = input.activeForm;
|
|
38
|
+
if (input.addBlocks) {
|
|
39
|
+
for (const id of input.addBlocks) {
|
|
40
|
+
if (!entry.blocks.includes(id)) entry.blocks.push(id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (input.addBlockedBy) {
|
|
44
|
+
for (const id of input.addBlockedBy) {
|
|
45
|
+
if (!entry.blockedBy.includes(id)) entry.blockedBy.push(id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (input.metadata) {
|
|
49
|
+
if (!entry.metadata) entry.metadata = {};
|
|
50
|
+
for (const [key, value] of Object.entries(input.metadata)) {
|
|
51
|
+
if (value === null) {
|
|
52
|
+
delete entry.metadata[key];
|
|
53
|
+
} else {
|
|
54
|
+
entry.metadata[key] = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
59
|
+
tasks[idx] = entry;
|
|
60
|
+
await saveTasks(tasksFile, tasks);
|
|
61
|
+
return `Updated task ${input.taskId}.
|
|
62
|
+
${formatTask(entry)}`;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
65
|
+
return `Error [task-update]: ${msg}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
var taskUpdate = createTaskUpdate();
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
createTaskUpdate,
|
|
74
|
+
taskUpdate
|
|
75
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/bash/index.ts
|
|
2
|
+
var _ai = require('ai');
|
|
3
|
+
var _zod = require('zod');
|
|
4
|
+
|
|
5
|
+
// src/shared/shell.ts
|
|
6
|
+
var _child_process = require('child_process');
|
|
7
|
+
var MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
8
|
+
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
9
|
+
var SIGKILL_GRACE_MS = 5e3;
|
|
10
|
+
async function executeShell(command, options) {
|
|
11
|
+
const shellBin = _nullishCoalesce(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.shell]), () => ( process.env.SHELL)), () => ( "/bin/bash"));
|
|
12
|
+
const timeout = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.timeout]), () => ( DEFAULT_TIMEOUT_MS));
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const child = _child_process.spawn.call(void 0, shellBin, ["-c", command], {
|
|
15
|
+
cwd: _optionalChain([options, 'optionalAccess', _3 => _3.cwd]),
|
|
16
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
17
|
+
// Prevent visible console window on Windows (no-op elsewhere)
|
|
18
|
+
windowsHide: true
|
|
19
|
+
});
|
|
20
|
+
let stdout = "";
|
|
21
|
+
let stderr = "";
|
|
22
|
+
let stdoutBytes = 0;
|
|
23
|
+
let stderrBytes = 0;
|
|
24
|
+
let settled = false;
|
|
25
|
+
let timeoutId;
|
|
26
|
+
let graceId;
|
|
27
|
+
function cleanup() {
|
|
28
|
+
if (timeoutId !== void 0) {
|
|
29
|
+
clearTimeout(timeoutId);
|
|
30
|
+
timeoutId = void 0;
|
|
31
|
+
}
|
|
32
|
+
if (graceId !== void 0) {
|
|
33
|
+
clearTimeout(graceId);
|
|
34
|
+
graceId = void 0;
|
|
35
|
+
}
|
|
36
|
+
if (abortHandler && _optionalChain([options, 'optionalAccess', _4 => _4.signal])) {
|
|
37
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function settle(result) {
|
|
41
|
+
if (settled) return;
|
|
42
|
+
settled = true;
|
|
43
|
+
cleanup();
|
|
44
|
+
resolve(result);
|
|
45
|
+
}
|
|
46
|
+
child.stdout.on("data", (chunk) => {
|
|
47
|
+
if (stdoutBytes >= MAX_BUFFER_BYTES) return;
|
|
48
|
+
const str = chunk.toString();
|
|
49
|
+
const remaining = MAX_BUFFER_BYTES - stdoutBytes;
|
|
50
|
+
if (chunk.length > remaining) {
|
|
51
|
+
stdout += str.slice(0, remaining);
|
|
52
|
+
stdoutBytes = MAX_BUFFER_BYTES;
|
|
53
|
+
} else {
|
|
54
|
+
stdout += str;
|
|
55
|
+
stdoutBytes += chunk.length;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
child.stderr.on("data", (chunk) => {
|
|
59
|
+
if (stderrBytes >= MAX_BUFFER_BYTES) return;
|
|
60
|
+
const str = chunk.toString();
|
|
61
|
+
const remaining = MAX_BUFFER_BYTES - stderrBytes;
|
|
62
|
+
if (chunk.length > remaining) {
|
|
63
|
+
stderr += str.slice(0, remaining);
|
|
64
|
+
stderrBytes = MAX_BUFFER_BYTES;
|
|
65
|
+
} else {
|
|
66
|
+
stderr += str;
|
|
67
|
+
stderrBytes += chunk.length;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
child.on("exit", (code, signal) => {
|
|
71
|
+
const exitCode = code !== null ? code : signal === "SIGKILL" ? 137 : signal === "SIGTERM" ? 143 : 1;
|
|
72
|
+
settle({ stdout, stderr, exitCode });
|
|
73
|
+
});
|
|
74
|
+
child.on("error", (err) => {
|
|
75
|
+
if (settled) return;
|
|
76
|
+
settled = true;
|
|
77
|
+
cleanup();
|
|
78
|
+
reject(err);
|
|
79
|
+
});
|
|
80
|
+
if (timeout > 0) {
|
|
81
|
+
timeoutId = setTimeout(() => {
|
|
82
|
+
timeoutId = void 0;
|
|
83
|
+
if (settled) return;
|
|
84
|
+
child.kill("SIGTERM");
|
|
85
|
+
graceId = setTimeout(() => {
|
|
86
|
+
graceId = void 0;
|
|
87
|
+
if (settled) return;
|
|
88
|
+
child.kill("SIGKILL");
|
|
89
|
+
}, SIGKILL_GRACE_MS);
|
|
90
|
+
}, timeout);
|
|
91
|
+
}
|
|
92
|
+
const abortHandler = _optionalChain([options, 'optionalAccess', _5 => _5.signal]) ? () => {
|
|
93
|
+
if (settled) return;
|
|
94
|
+
child.kill("SIGTERM");
|
|
95
|
+
} : void 0;
|
|
96
|
+
if (abortHandler && _optionalChain([options, 'optionalAccess', _6 => _6.signal])) {
|
|
97
|
+
if (options.signal.aborted) {
|
|
98
|
+
child.kill("SIGTERM");
|
|
99
|
+
} else {
|
|
100
|
+
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/bash/index.ts
|
|
107
|
+
function createBash(config = {}) {
|
|
108
|
+
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
|
|
109
|
+
const timeout = _nullishCoalesce(config.timeout, () => ( 12e4));
|
|
110
|
+
return _ai.tool.call(void 0, {
|
|
111
|
+
description: "Execute a shell command and return its output. Runs the command in a bash shell with the configured working directory. Returns stdout, stderr, and exit code. Use this for running build commands, git operations, system administration, and any other shell tasks.",
|
|
112
|
+
inputSchema: _zod.z.object({
|
|
113
|
+
command: _zod.z.string().describe("The shell command to execute"),
|
|
114
|
+
timeout: _zod.z.number().optional().describe("Timeout in milliseconds (default: 120000)"),
|
|
115
|
+
description: _zod.z.string().optional().describe("Human-readable description of what the command does")
|
|
116
|
+
}),
|
|
117
|
+
execute: async ({ command, timeout: cmdTimeout }) => {
|
|
118
|
+
try {
|
|
119
|
+
const result = await executeShell(command, {
|
|
120
|
+
cwd,
|
|
121
|
+
timeout: _nullishCoalesce(cmdTimeout, () => ( timeout)),
|
|
122
|
+
shell: config.shell
|
|
123
|
+
});
|
|
124
|
+
const parts = [];
|
|
125
|
+
if (result.stdout) parts.push(result.stdout);
|
|
126
|
+
if (result.stderr) parts.push(`STDERR:
|
|
127
|
+
${result.stderr}`);
|
|
128
|
+
if (result.exitCode !== 0) parts.push(`Exit code: ${result.exitCode}`);
|
|
129
|
+
return parts.length > 0 ? parts.join("\n") : `Command completed with exit code ${result.exitCode}`;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
132
|
+
return `Error [bash]: Failed to execute command: ${msg}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
var bash = createBash();
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
exports.createBash = createBash; exports.bash = bash;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatTaskSummary,
|
|
3
|
+
loadTasks
|
|
4
|
+
} from "./chunk-E6NBEYZD.js";
|
|
5
|
+
|
|
6
|
+
// src/task-list/index.ts
|
|
7
|
+
import { tool } from "ai";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
function createTaskList(config = {}) {
|
|
11
|
+
const cwd = config.cwd ?? process.cwd();
|
|
12
|
+
const tasksFile = config.tasksFile ?? join(cwd, ".agentool", "tasks.json");
|
|
13
|
+
return tool({
|
|
14
|
+
description: "List all tasks with their status, owner, and dependencies.",
|
|
15
|
+
inputSchema: z.object({}),
|
|
16
|
+
execute: async () => {
|
|
17
|
+
try {
|
|
18
|
+
const tasks = await loadTasks(tasksFile);
|
|
19
|
+
const visible = tasks.filter((t) => t.status !== "deleted");
|
|
20
|
+
if (visible.length === 0) return "No tasks found.";
|
|
21
|
+
return visible.map(formatTaskSummary).join("\n");
|
|
22
|
+
} catch (error) {
|
|
23
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
24
|
+
return `Error [task-list]: ${msg}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
var taskList = createTaskList();
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
createTaskList,
|
|
33
|
+
taskList
|
|
34
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executeRipgrep
|
|
3
|
+
} from "./chunk-MJCAXASI.js";
|
|
4
|
+
import {
|
|
5
|
+
expandPath,
|
|
6
|
+
toRelativePath
|
|
7
|
+
} from "./chunk-I3ONDY7P.js";
|
|
8
|
+
|
|
9
|
+
// src/grep/index.ts
|
|
10
|
+
import { stat } from "fs/promises";
|
|
11
|
+
import { tool } from "ai";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
var VCS_DIRS = [".git", ".svn", ".hg", ".bzr", ".jj", ".sl"];
|
|
14
|
+
var DEFAULT_HEAD_LIMIT = 250;
|
|
15
|
+
function applyHeadLimit(items, limit, offset = 0) {
|
|
16
|
+
if (limit === 0) return { items: items.slice(offset), appliedLimit: void 0 };
|
|
17
|
+
const cap = limit ?? DEFAULT_HEAD_LIMIT;
|
|
18
|
+
const sliced = items.slice(offset, offset + cap);
|
|
19
|
+
const truncated = items.length - offset > cap;
|
|
20
|
+
return { items: sliced, appliedLimit: truncated ? cap : void 0 };
|
|
21
|
+
}
|
|
22
|
+
function truncationSuffix(appliedLimit, offset) {
|
|
23
|
+
const parts = [];
|
|
24
|
+
if (appliedLimit !== void 0) parts.push(`limit: ${appliedLimit}`);
|
|
25
|
+
if (offset > 0) parts.push(`offset: ${offset}`);
|
|
26
|
+
return parts.length > 0 ? `
|
|
27
|
+
|
|
28
|
+
[Results truncated. ${parts.join(", ")}]` : "";
|
|
29
|
+
}
|
|
30
|
+
function relativizeLine(line, baseCwd, last = false) {
|
|
31
|
+
const idx = last ? line.lastIndexOf(":") : line.indexOf(":");
|
|
32
|
+
if (idx > 0) {
|
|
33
|
+
return toRelativePath(line.substring(0, idx), baseCwd) + line.substring(idx);
|
|
34
|
+
}
|
|
35
|
+
return line;
|
|
36
|
+
}
|
|
37
|
+
function parseGlobPatterns(globFilter) {
|
|
38
|
+
const patterns = [];
|
|
39
|
+
for (const raw of globFilter.split(/\s+/)) {
|
|
40
|
+
if (raw.includes("{") && raw.includes("}")) {
|
|
41
|
+
patterns.push(raw);
|
|
42
|
+
} else {
|
|
43
|
+
patterns.push(...raw.split(",").filter(Boolean));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return patterns;
|
|
47
|
+
}
|
|
48
|
+
function createGrep(config = {}) {
|
|
49
|
+
const cwd = config.cwd ?? process.cwd();
|
|
50
|
+
return tool({
|
|
51
|
+
description: 'Search file contents using ripgrep. Supports regex patterns, context lines, and three output modes: "content" (matching lines), "files_with_matches" (file paths), and "count" (match counts).',
|
|
52
|
+
inputSchema: z.object({
|
|
53
|
+
pattern: z.string().describe("The regular expression pattern to search for in file contents"),
|
|
54
|
+
path: z.string().optional().describe("File or directory to search in (rg PATH). Defaults to current working directory."),
|
|
55
|
+
glob: z.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob'),
|
|
56
|
+
output_mode: z.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode: "content" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), "files_with_matches" shows file paths (supports head_limit), "count" shows match counts (supports head_limit). Defaults to "files_with_matches".'),
|
|
57
|
+
"-B": z.number().optional().describe('Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.'),
|
|
58
|
+
"-A": z.number().optional().describe('Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.'),
|
|
59
|
+
"-C": z.number().optional().describe("Alias for context."),
|
|
60
|
+
context: z.number().optional().describe('Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.'),
|
|
61
|
+
"-n": z.boolean().optional().describe('Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.'),
|
|
62
|
+
"-i": z.boolean().optional().describe("Case insensitive search (rg -i)"),
|
|
63
|
+
type: z.string().optional().describe("File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types."),
|
|
64
|
+
head_limit: z.number().optional().describe('Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 250 when unspecified. Pass 0 for unlimited (use sparingly \u2014 large result sets waste context).'),
|
|
65
|
+
offset: z.number().optional().describe('Skip first N lines/entries before applying head_limit, equivalent to "| tail -n +N | head -N". Works across all output modes. Defaults to 0.'),
|
|
66
|
+
multiline: z.boolean().optional().describe("Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.")
|
|
67
|
+
}),
|
|
68
|
+
execute: async (input) => {
|
|
69
|
+
try {
|
|
70
|
+
const {
|
|
71
|
+
pattern,
|
|
72
|
+
path,
|
|
73
|
+
glob: globFilter,
|
|
74
|
+
type: typeFilter,
|
|
75
|
+
output_mode: outputMode = "files_with_matches",
|
|
76
|
+
"-B": ctxBefore,
|
|
77
|
+
"-A": ctxAfter,
|
|
78
|
+
"-C": ctxC,
|
|
79
|
+
context: ctxAlias,
|
|
80
|
+
"-n": showLineNumbers = true,
|
|
81
|
+
"-i": caseInsensitive = false,
|
|
82
|
+
head_limit: headLimit,
|
|
83
|
+
offset = 0,
|
|
84
|
+
multiline = false
|
|
85
|
+
} = input;
|
|
86
|
+
const absolutePath = path ? expandPath(path, cwd) : cwd;
|
|
87
|
+
const args = ["--hidden"];
|
|
88
|
+
for (const dir of VCS_DIRS) args.push("--glob", `!${dir}`);
|
|
89
|
+
args.push("--max-columns", "500");
|
|
90
|
+
if (multiline) args.push("-U", "--multiline-dotall");
|
|
91
|
+
if (caseInsensitive) args.push("-i");
|
|
92
|
+
if (outputMode === "files_with_matches") args.push("-l");
|
|
93
|
+
else if (outputMode === "count") args.push("-c");
|
|
94
|
+
if (showLineNumbers && outputMode === "content") args.push("-n");
|
|
95
|
+
if (outputMode === "content") {
|
|
96
|
+
if (ctxAlias !== void 0) args.push("-C", ctxAlias.toString());
|
|
97
|
+
else if (ctxC !== void 0) args.push("-C", ctxC.toString());
|
|
98
|
+
else {
|
|
99
|
+
if (ctxBefore !== void 0) args.push("-B", ctxBefore.toString());
|
|
100
|
+
if (ctxAfter !== void 0) args.push("-A", ctxAfter.toString());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (pattern.startsWith("-")) args.push("-e", pattern);
|
|
104
|
+
else args.push(pattern);
|
|
105
|
+
if (typeFilter) args.push("--type", typeFilter);
|
|
106
|
+
if (globFilter) {
|
|
107
|
+
for (const gp of parseGlobPatterns(globFilter)) args.push("--glob", gp);
|
|
108
|
+
}
|
|
109
|
+
const results = await executeRipgrep(args, absolutePath);
|
|
110
|
+
if (results.length === 0) return "No matches found";
|
|
111
|
+
if (outputMode === "content") {
|
|
112
|
+
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
|
|
113
|
+
const lines = items2.map((l) => relativizeLine(l, cwd));
|
|
114
|
+
return lines.join("\n") + truncationSuffix(appliedLimit2, offset);
|
|
115
|
+
}
|
|
116
|
+
if (outputMode === "count") {
|
|
117
|
+
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
|
|
118
|
+
const lines = items2.map((l) => relativizeLine(l, cwd, true));
|
|
119
|
+
let totalMatches = 0;
|
|
120
|
+
let fileCount = 0;
|
|
121
|
+
for (const line of lines) {
|
|
122
|
+
const idx = line.lastIndexOf(":");
|
|
123
|
+
if (idx > 0) {
|
|
124
|
+
const n = parseInt(line.substring(idx + 1), 10);
|
|
125
|
+
if (!isNaN(n)) {
|
|
126
|
+
totalMatches += n;
|
|
127
|
+
fileCount += 1;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return lines.join("\n") + `
|
|
132
|
+
|
|
133
|
+
Total: ${totalMatches} matches in ${fileCount} files` + truncationSuffix(appliedLimit2, offset);
|
|
134
|
+
}
|
|
135
|
+
const stats = await Promise.allSettled(results.map((f) => stat(f)));
|
|
136
|
+
const sorted = results.map((fp, i) => {
|
|
137
|
+
const r = stats[i];
|
|
138
|
+
const mt = r.status === "fulfilled" ? r.value.mtimeMs ?? 0 : 0;
|
|
139
|
+
return [fp, mt];
|
|
140
|
+
}).sort((a, b) => {
|
|
141
|
+
const d = b[1] - a[1];
|
|
142
|
+
return d !== 0 ? d : a[0].localeCompare(b[0]);
|
|
143
|
+
}).map((e) => e[0]);
|
|
144
|
+
const { items, appliedLimit } = applyHeadLimit(sorted, headLimit, offset);
|
|
145
|
+
const relative = items.map((f) => toRelativePath(f, cwd));
|
|
146
|
+
return relative.join("\n") + truncationSuffix(appliedLimit, offset);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
149
|
+
return `Error [grep]: Failed to search: ${msg}`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
var grep = createGrep();
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
createGrep,
|
|
158
|
+
grep
|
|
159
|
+
};
|