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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tune-basic-toolset",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Basic toolset for tune",
5
5
  "main": "src/index.js",
6
6
  "files": [
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
- // More tolerant to whitespace differences on each line and reports per-block success.
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 m;
16
- while ((m = patchRegex.exec(text)) !== null) {
17
- const oldPart = String(m[1]).replace(/^\s*\r?\n+|\r?\n+\s*$/g, "");
18
- const newPart = String(m[2]).replace(/^\s*\r?\n+|\r?\n+\s*$/g, "");
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
- function buildPattern(oldStr) {
29
- // Escape special regex chars
30
- let escaped = oldStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
31
- // Normalize line endings to \r?\n so CRLF/LF both match
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
- // Tolerate indentation/space differences (spaces or tabs), zero-or-more
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
- for (const { oldPart, newPart } of patches) {
44
- const re = buildPattern(oldPart);
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
  };
@@ -6,6 +6,10 @@
6
6
  "text": {
7
7
  "type": "string",
8
8
  "description": "The shell command to execute"
9
+ },
10
+ "host": {
11
+ "type": "string",
12
+ "description": "remote host like user@host.com to execute the shell comand on (uses ssh)"
9
13
  }
10
14
  },
11
15
  "required": ["text"]
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
- module.exports = async function sh({ text }) {
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 = execSync(text, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
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;