tune-basic-toolset 0.1.14 → 0.1.16
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 +11 -26
- package/package.json +1 -1
- package/src/patch.tool.js +21 -45
- package/src/sh.schema.json +4 -0
- package/src/sh.tool.js +15 -3
- package/src/shp.proc.js +0 -31
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Basic toolset for [Tune](https://github.com/iovdin/tune).
|
|
|
10
10
|
- [wf](#wf) write file
|
|
11
11
|
- [patch](#patch) patch file
|
|
12
12
|
- [append](#append) append to file
|
|
13
|
-
- [sh](#sh) execute shell command
|
|
13
|
+
- [sh](#sh) execute shell command locally and remotely
|
|
14
14
|
- [cmd](#cmd) execute Windows cmd command
|
|
15
15
|
- [powershell](#powershell) execute PowerShell command
|
|
16
16
|
- [grep](#grep) search for patterns in text or files
|
|
@@ -25,7 +25,6 @@ Basic toolset for [Tune](https://github.com/iovdin/tune).
|
|
|
25
25
|
- [message](#message) talk to another chat/agent (separate context)
|
|
26
26
|
- [Processors](#processors)
|
|
27
27
|
- [proc](#proc) converts tool to processor
|
|
28
|
-
- [shp](#shp) include shell command output
|
|
29
28
|
- [init](#init) set initial value
|
|
30
29
|
- [json_format](#json_format) make LLM respond with JSON
|
|
31
30
|
- [log](#log) save LLM payload
|
|
@@ -130,7 +129,7 @@ appended
|
|
|
130
129
|
```
|
|
131
130
|
|
|
132
131
|
### `sh`
|
|
133
|
-
Execute shell command
|
|
132
|
+
Execute shell command locally or with ssh
|
|
134
133
|
```chat
|
|
135
134
|
user: @sh
|
|
136
135
|
find with ripgrep where echo is used
|
|
@@ -141,6 +140,15 @@ tool_result:
|
|
|
141
140
|
./README.md: const text = "s: \@echo\nu: hello world";
|
|
142
141
|
./tools/echo.txt:you are echo, you print everything back
|
|
143
142
|
./tools/README.md:* `echo.txt` - to debug variable expansions and context
|
|
143
|
+
|
|
144
|
+
user:
|
|
145
|
+
check contents directory on root\@host.com
|
|
146
|
+
assistant:
|
|
147
|
+
tool_call: sh {"host": "root@host.com"}
|
|
148
|
+
ls
|
|
149
|
+
tool_result:
|
|
150
|
+
file1
|
|
151
|
+
file2
|
|
144
152
|
```
|
|
145
153
|
|
|
146
154
|
### `cmd`
|
|
@@ -463,29 +471,6 @@ execut python script text="384 * 123" and insert back result
|
|
|
463
471
|
@{| proc py 384 * 123 }
|
|
464
472
|
```
|
|
465
473
|
|
|
466
|
-
### `shp`
|
|
467
|
-
Insert shell command output
|
|
468
|
-
```chat
|
|
469
|
-
system:
|
|
470
|
-
include project file list to system prompt
|
|
471
|
-
@{| shp git ls-files }
|
|
472
|
-
|
|
473
|
-
include buffer content on osx
|
|
474
|
-
@{| shp pbpaste }
|
|
475
|
-
|
|
476
|
-
include current date
|
|
477
|
-
@{| shp date }
|
|
478
|
-
|
|
479
|
-
pipe filename content to shell command
|
|
480
|
-
@{ a.log | shp tail }
|
|
481
|
-
|
|
482
|
-
@{ a.log | shp grep pattern }
|
|
483
|
-
|
|
484
|
-
print screen of one of tmux session
|
|
485
|
-
@{| shp tmux capture-pane -t 0 -p }
|
|
486
|
-
|
|
487
|
-
```
|
|
488
|
-
|
|
489
474
|
### `init`
|
|
490
475
|
Set default value for non set variables
|
|
491
476
|
|
package/package.json
CHANGED
package/src/patch.tool.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
const fs = require('fs').promises;
|
|
2
2
|
|
|
3
3
|
// Patch tool to apply custom diffs marked with <<<<<<< ORIGINAL and >>>>>>> UPDATED
|
|
4
|
-
//
|
|
5
|
-
module.exports = async function patch({ text, filename }, ctx) {
|
|
6
|
-
if (!text || !filename) {
|
|
7
|
-
return "No patch text or filename provided";
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Match: <<<<<<< ORIGINAL ... ======= ... >>>>>>> UPDATED
|
|
11
|
-
// Be tolerant to CRLF/LF and optional trailing text/spaces on the markers.
|
|
12
|
-
const patchRegex = /<{6,}\s*ORIGINAL[^\n]*\r?\n([\s\S]*?)=+[^\n]*\r?\n([\s\S]*?)>{6,}\s*UPDATED[^\n]*(?:\r?\n|$)/g;
|
|
4
|
+
// Handles patches with context and applies only the segments between markers.
|
|
13
5
|
|
|
6
|
+
module.exports = async function patch({ text, filename }, ctx) {
|
|
7
|
+
// Regex to match each patch block
|
|
8
|
+
// Be lenient about the number of conflict marker characters because some
|
|
9
|
+
// environments may trim one or more > or < characters.
|
|
10
|
+
const patchRegex = /<{6,}\s*ORIGINAL[^\n]*\n([\s\S]*?)=+\n([\s\S]*?)>{6,}\s*UPDATED[^\n]*(?:\n|$)/g;
|
|
14
11
|
const patches = [];
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
let match;
|
|
13
|
+
|
|
14
|
+
// Extract all old/new segments
|
|
15
|
+
while ((match = patchRegex.exec(text)) !== null) {
|
|
16
|
+
const oldPart = match[1].replace(/^\n+|\n+$/g, "");
|
|
17
|
+
const newPart = match[2].replace(/^\n+|\n+$/g, "");
|
|
19
18
|
patches.push({ oldPart, newPart });
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -25,41 +24,18 @@ module.exports = async function patch({ text, filename }, ctx) {
|
|
|
25
24
|
|
|
26
25
|
let fileContent = await ctx.read(filename);
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
// Escape special
|
|
30
|
-
|
|
31
|
-
//
|
|
27
|
+
for (const { oldPart, newPart } of patches) {
|
|
28
|
+
// Escape regex special chars in oldPart.
|
|
29
|
+
// Do NOT relax all whitespace to \s+; that can swallow preceding newlines.
|
|
30
|
+
// Only normalize line endings so CRLF in patches can match LF in files.
|
|
31
|
+
let escaped = oldPart.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
32
32
|
escaped = escaped.replace(/\r?\n/g, "\\r?\\n");
|
|
33
|
-
|
|
34
|
-
// Keep newlines strict so structure must still match.
|
|
35
|
-
escaped = escaped.replace(/[ \t]+/g, "[ \\t]*");
|
|
36
|
-
return new RegExp(escaped, "g");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const totalSegments = patches.length;
|
|
40
|
-
let appliedSegments = 0;
|
|
41
|
-
let totalReplacements = 0;
|
|
33
|
+
const oldRegex = new RegExp(escaped, "g");
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
let matches = 0;
|
|
46
|
-
fileContent = fileContent.replace(re, () => {
|
|
47
|
-
matches += 1;
|
|
48
|
-
return newPart;
|
|
49
|
-
});
|
|
50
|
-
if (matches > 0) {
|
|
51
|
-
appliedSegments += 1;
|
|
52
|
-
totalReplacements += matches;
|
|
53
|
-
}
|
|
35
|
+
// Perform replacement using a function to avoid replacement string ambiguities
|
|
36
|
+
fileContent = fileContent.replace(oldRegex, () => newPart);
|
|
54
37
|
}
|
|
55
38
|
|
|
56
39
|
await ctx.write(filename, fileContent);
|
|
57
|
-
|
|
58
|
-
if (appliedSegments === 0) {
|
|
59
|
-
return `no matches applied (0/${totalSegments})`;
|
|
60
|
-
}
|
|
61
|
-
if (appliedSegments < totalSegments) {
|
|
62
|
-
return `patched partially (${appliedSegments}/${totalSegments}), replacements: ${totalReplacements}`;
|
|
63
|
-
}
|
|
64
|
-
return `patched (${appliedSegments}/${totalSegments}), replacements: ${totalReplacements}`;
|
|
40
|
+
return "patched";
|
|
65
41
|
};
|
package/src/sh.schema.json
CHANGED
package/src/sh.tool.js
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
const { execSync } = require('child_process');
|
|
1
|
+
const { spawnSync, execSync } = require('child_process');
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
//TODO look back do not escape escaped
|
|
4
|
+
const escape = (str) => str.replace(/'/g, "\\'")
|
|
5
|
+
|
|
6
|
+
module.exports = async function sh({ text, host }) {
|
|
4
7
|
let result = "";
|
|
5
8
|
try {
|
|
9
|
+
let cmd = text
|
|
10
|
+
let args = []
|
|
11
|
+
if (host) {
|
|
12
|
+
cmd = "ssh"
|
|
13
|
+
args = [host, text]
|
|
14
|
+
}
|
|
6
15
|
// Increase maxBuffer to reduce ERR_CHILD_PROCESS_STDIO_MAXBUFFER risk on large outputs
|
|
7
|
-
result =
|
|
16
|
+
result = spawnSync(cmd, args, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024, shell: true });
|
|
17
|
+
result = (result.stdout || "") + (result.stderr || "")
|
|
18
|
+
return result
|
|
8
19
|
} catch (e) {
|
|
20
|
+
return e
|
|
9
21
|
const stderr = e && typeof e.stderr !== "undefined" ? String(e.stderr || "") : "";
|
|
10
22
|
const stdout = e && typeof e.stdout !== "undefined" ? String(e.stdout || "") : "";
|
|
11
23
|
|
package/src/shp.proc.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const { execSync, spawnSync } = require('child_process');
|
|
2
|
-
|
|
3
|
-
const shp = async (node, args, ctx) => ({
|
|
4
|
-
type: 'text',
|
|
5
|
-
read: async () => {
|
|
6
|
-
let input = null;
|
|
7
|
-
|
|
8
|
-
if (node && node.type === 'text') {
|
|
9
|
-
input = await node.read();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
let result;
|
|
13
|
-
try {
|
|
14
|
-
if (input !== null) {
|
|
15
|
-
const res = spawnSync(args.trim() || "sh", {
|
|
16
|
-
input,
|
|
17
|
-
encoding: 'utf8',
|
|
18
|
-
shell: true
|
|
19
|
-
});
|
|
20
|
-
result = (res.stdout || '') + (res.stderr || '');
|
|
21
|
-
} else {
|
|
22
|
-
result = execSync(args.trim(), { encoding: 'utf8' });
|
|
23
|
-
}
|
|
24
|
-
} catch (e) {
|
|
25
|
-
result = e.stderr + e.stdout;
|
|
26
|
-
}
|
|
27
|
-
return result;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
module.exports = shp;
|