tune-basic-toolset 0.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 ADDED
@@ -0,0 +1,517 @@
1
+ # Tune basic toolset
2
+ Basic toolset for [Tune](https://github.com/iovdin/tune).
3
+
4
+ ##### Index
5
+ - [Setup](#setup)
6
+ - [Text Editor](#text-editor)
7
+ - [JavaScript Project](#javascript-project)
8
+ - [Tools](#tools)
9
+ - [rf](#rf) read file
10
+ - [wf](#wf) write file
11
+ - [patch](#patch) patch file
12
+ - [append](#append) append to file
13
+ - [sh](#sh) execute shell command
14
+ - [osa](#osa) manage reminders/notes/calendar (AppleScript/macOS)
15
+ - [jina_r](#jina_r) fetch webpage content
16
+ - [turn](#turn) turn based agent
17
+ - [list](#list) keep list of tasks todo (loops for LLM)
18
+ - [py](#py) run python code
19
+ - [js](#js) run javascript code
20
+ - [message](#message) talk to another chat/agent
21
+ - [Processors](#processors)
22
+ - [shp](#shp) include shell command output
23
+ - [init](#init) set initial value
24
+ - [json_format](#json_format) make LLM respond with JSON
25
+ - [log](#log) save LLM payload
26
+ - [mock](#mock) set variables inline
27
+ - [linenum](#linenum) prepend line numbers
28
+ - [text](#text) convert any variable to text variable
29
+ - [resolve](#resolve) resolve a variable
30
+ - [prop](#prop) set additional properties of LLM
31
+ - [head](#head) take first N lines of a file
32
+ - [tail](#tail) take last N lines of a file or LLM payload
33
+ - [slice](#slice) take lines from <start> to <finish> of a file
34
+
35
+
36
+ ## Setup
37
+ ### Text Editor
38
+
39
+ Install in your `~/.tune` folder:
40
+
41
+ ```bash
42
+ cd ~/.tune
43
+ npm install tune-basic-toolset
44
+ ```
45
+
46
+ Add to `~/.tune/default.ctx.js`:
47
+
48
+ ```javascript
49
+ const basics = require('tune-basic-toolset')
50
+
51
+ module.exports = [
52
+ ...
53
+ basics()
54
+ ...
55
+ ]
56
+ ```
57
+
58
+ ### JavaScript Project
59
+
60
+ ```bash
61
+ npm install tune-basic-toolset tune-sdk
62
+ ```
63
+
64
+ ```javascript
65
+ const tune = require('tune-sdk')
66
+ const basics = require('tune-basic-toolset')
67
+
68
+ const ctx = tune.makeContext(
69
+ basics({ expose: ["rf", "wf"], mount: "tools" })
70
+ )
71
+ ```
72
+
73
+ ## Tools
74
+ [Tools](https://iovdin.github.io/tune/template-language/tools) is a function that llm can run on your local machine or server
75
+
76
+ ### `rf`
77
+ Read file
78
+ ```chat
79
+ user: @rf
80
+ can you read README.md?
81
+ tool_call: rf {"filename":"README.md"}
82
+ tool_result:
83
+ @README.md
84
+ ```
85
+ It accepts an optional `linenum` parameter that prepends line numbers to the file (useful for patching).
86
+
87
+ ### `wf`
88
+ Write to a file
89
+ ```chat
90
+ user: @wf
91
+ make a hello world javascript
92
+ tool_call: wf {"filename":"helloWorld.js"}
93
+ console.log('Hello, World!');
94
+ tool_result:
95
+ written
96
+ ```
97
+
98
+ ### `patch`
99
+ ```chat
100
+ user: @patch
101
+ translate "hello world" in helloWorld.js to dutch
102
+ tool_call: patch {"filename":"helloWorld.js"}
103
+ <<<<<<< ORIGINAL
104
+ console.log('Hello, World!');
105
+ =======
106
+ console.log('Hallo, Wereld!');
107
+ >>>>>>> UPDATED
108
+ tool_result:
109
+ patched
110
+ ```
111
+
112
+
113
+ ### `append`
114
+ ```chat
115
+ user: @append
116
+ Add a todo that i want to buy a car and go to the moon
117
+ tool_call: append {"filename":"todo.md"}
118
+ - buy a car
119
+ - fly to the moon
120
+ tool_result:
121
+ appended
122
+ ```
123
+
124
+ ### `sh`
125
+ Execute shell command
126
+ ```chat
127
+ user: @sh
128
+ find with ripgrep where echo is used
129
+ tool_call: sh
130
+ rg 'echo' ./
131
+ tool_result:
132
+ ./README.md: echo: "You are echo, you print everything back",
133
+ ./README.md: const text = "s: \@echo\nu: hello world";
134
+ ./tools/echo.txt:you are echo, you print everything back
135
+ ./tools/README.md:* `echo.txt` - to debug variable expansions and context
136
+ ```
137
+
138
+ ### `osa`
139
+ AppleScript tool, manage reminders, notes, calendar etc on osx
140
+ ```chat
141
+ system: @osa @gpt-4.1
142
+ you're personal assistant with access to osx apps
143
+ like reminder, calendar etc
144
+
145
+ user:
146
+ can you remind me in 2 hours that i have meeting with jack
147
+
148
+ assistant:
149
+
150
+ tool_call: osa
151
+ tell application "Reminders" to make new reminder with properties {name:"Meeting with Jack", remind me date:(current date) + 2 * hours}
152
+ tool_result:
153
+ reminder id x-apple-reminder://849EA642-B7E0-4E16-94AA-B50FF1BC282D
154
+ ```
155
+
156
+ ### `jina_r`
157
+ Fetch webpage content with [jina reader](https://jina.ai/reader) api.
158
+ Works without an API key up to a limited rate. Set JINA_KEY in .env for higher rate limits.
159
+
160
+ ```chat
161
+ user: @jina_r
162
+ take https://iovdin.github.io/tune/ page content
163
+ and put it into `home` directory
164
+ then make short summary
165
+
166
+ tool_call: jina_r {"url":"https://iovdin.github.io/tune/","filename":"home/tune_page_content.md"}
167
+
168
+ tool_result:
169
+ @home/tune_page_content.md
170
+
171
+ assistant:
172
+ The content from the "Tune" page has been successfully retrieved and saved into the `home/tune_page_content.md` file. Below is a short summary of the page:
173
+
174
+ ---
175
+
176
+ **Summary of Tune - Your Everyday LLM Toolkit:**
177
+
178
+ Tune is a versatile toolkit designed for developers and users to effectively interact with Large Language Models (LLMs). It allows users to chat with AI, build applications, and create AI agents in a simple manner.
179
+ <cut for brevity>
180
+ ```
181
+
182
+
183
+ ### `turn`
184
+ A way to switch roles when building multistep agents [read more](https://iovdin.github.io/tune/examples/multi-agent)
185
+ ```chat
186
+ system: @turn @gpt-4o
187
+ You're playing 20 questions game.
188
+ You switch turns between 'thinker' and 'player' agent.
189
+ Current agent stored in agent.txt file
190
+ 'player' always plays first
191
+
192
+ @@agent|init
193
+ assistant:
194
+ Is it a living thing?
195
+
196
+ tool_call: turn {"role":"thinker","filename":"agent.txt"}
197
+ tool_result: now it is turn of thinker to reply
198
+
199
+ assistant:
200
+ No.
201
+
202
+ tool_call: turn {"role":"player","filename":"agent.txt"}
203
+ tool_result: now it is turn of player to reply
204
+
205
+ assistant:
206
+ Is it something that can be used indoors?
207
+
208
+ tool_call: turn {"role":"thinker","filename":"agent.txt"}
209
+ tool_result: now it is turn of thinker to reply
210
+
211
+ assistant:
212
+ Yes.
213
+
214
+ ...
215
+ ```
216
+
217
+ ### `list`
218
+ Keep list of tasks to do
219
+
220
+ ```chat
221
+ system: @list
222
+ You manage todo list in file todo.txt
223
+
224
+ user:
225
+ Today i need to refactor xyz
226
+ and then release abc
227
+
228
+ assistant:
229
+
230
+ tool_call: list {"filename":"todo.txt"}
231
+ todo - refactor xyz
232
+ todo - release abc
233
+
234
+ tool_result:
235
+ list updated
236
+
237
+ user:
238
+ ok, i've finished refactoring xyz
239
+
240
+ assistant:
241
+
242
+ tool_call: list {"filename":"todo.txt"}
243
+ done - refactor xyz
244
+
245
+ tool_result:
246
+ list updated
247
+ ```
248
+
249
+
250
+ ### `py`
251
+ execute python code
252
+ ```chat
253
+ user: @py
254
+ 1234 + 4311
255
+
256
+ tool_call: py
257
+ 1234 + 4311
258
+
259
+ tool_result:
260
+ 5545
261
+ ```
262
+
263
+ ### `js`
264
+ execute javascript code
265
+ ```chat
266
+ user: @js
267
+ 1234 + 4311
268
+
269
+ tool_call: js {"inputType":"commonjs"}
270
+ 1234 + 4311
271
+
272
+ tool_result:
273
+ 5545
274
+
275
+ ```
276
+
277
+ ### `message`
278
+ Talk to another chat/agent via tool call.
279
+ Orchestrate or evaulate other agents/chats.
280
+ ```chat
281
+ system:
282
+ @message
283
+ Your goal is to talk to Groot at `groot.prompt` system prompt
284
+ and try to make him say anything but 'I am Groot'
285
+
286
+ tool_call: message {"filename":"groot.chat","system":"groot.prompt"}
287
+ Hello Groot! How are you feeling today?
288
+
289
+ tool_result:
290
+ I am Groot!
291
+
292
+ tool_call: message {"filename":"groot.chat","system":"groot.prompt"}
293
+ What do you think about trees?
294
+
295
+ tool_result:
296
+ I am Groot!
297
+
298
+ tool_call: message {"filename":"groot.chat","system":"groot.prompt"}
299
+ Can you tell me a joke?
300
+
301
+ tool_result:
302
+ Sure! Why did the tree go to the dentist?
303
+
304
+ Because it had a root canal!
305
+ ```
306
+
307
+ The content of `groot.chat` is then:
308
+ ```chat
309
+ system: @@groot.prompt
310
+ user:
311
+ Hello Groot! How are you feeling today?
312
+
313
+ assistant:
314
+ I am Groot!
315
+
316
+ user:
317
+ What do you think about trees?
318
+
319
+ assistant:
320
+ I am Groot!
321
+
322
+ user:
323
+ Can you tell me a joke?
324
+
325
+ assistant:
326
+ Sure! Why did the tree go to the dentist?
327
+
328
+ Because it had a root canal!
329
+ ```
330
+
331
+
332
+ ## Processors
333
+ [Processors](https://iovdin.github.io/tune/template-language/processors) is a way to modify variable or insert new ones into chat.
334
+
335
+ ### `shp`
336
+ Insert shell command output
337
+ ```chat
338
+ system:
339
+ include project file list to system prompt
340
+ @{| shp git ls-files }
341
+
342
+ include buffer content on osx
343
+ @{| shp pbpaste }
344
+
345
+ include current date
346
+ @{| shp date }
347
+
348
+ pipe filename content to shell command
349
+ @{ a.log | shp tail }
350
+
351
+ @{ a.log | shp grep pattern }
352
+ ```
353
+
354
+ ### `init`
355
+ Set default value for non set variables
356
+
357
+ ```chat
358
+ system:
359
+ @memory|init
360
+ if memory does not exist the chat will fail
361
+ ```
362
+
363
+ ### `json_format`
364
+ Set llm response format to json [read more](https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat).
365
+
366
+ Without arguments it sets
367
+ ```json
368
+ "response_format": {
369
+ "type": "json_object"
370
+ }
371
+ ```
372
+
373
+ ```chat
374
+ system:
375
+ @{ gpt-4o | json_format }
376
+ please reply in json format:
377
+ { "message": "Your reply"}
378
+
379
+ user:
380
+ hi how are you?
381
+
382
+ assistant:
383
+ { "message": "I'm just a virtual assistant, so I don't have feelings, but I'm here and ready to help you! How can I assist you today?" }
384
+
385
+ ```
386
+
387
+ with argument it sets
388
+ ```json
389
+ "response_format": {
390
+ "type": "json_schema",
391
+ "json_schema": { "schema": "<contents of the referenced schema file>" }
392
+ }
393
+ ```
394
+
395
+ ```chat
396
+ system:
397
+ @{ gpt-4o | json_format path/to/schema.json }
398
+ ```
399
+
400
+ ### `log`
401
+ Save LLM payload to a json or chat file, used for debugging
402
+ ```chat
403
+ system:
404
+ @{ gpt-4o | log path/to/log.json }
405
+ @{ gpt-4o | log path/to/log.chat }
406
+ ```
407
+
408
+ ### `mock`
409
+ Set variables inline in chat.
410
+ ```
411
+ system: @{| mock hello=world }
412
+ @echo
413
+ user:
414
+ @hello
415
+ assistant:
416
+ world
417
+ ```
418
+
419
+ ### `linenum`
420
+ Prepend line numbers to a file content.
421
+ Useful when patching file.
422
+ ```chat
423
+ system:
424
+ @echo
425
+ user:
426
+ @{ helloWorld | linenum }
427
+ assistant:
428
+ 1 | console.log('Hello, World!');
429
+ ```
430
+
431
+ ### `text`
432
+ Treat special files (`.ctx.js`, `.llm.js`, `.tool.js`) like text
433
+ ```chat
434
+ system:
435
+ @echo
436
+
437
+ user:
438
+ content
439
+ @rf.tool.mjs
440
+
441
+ assistant:
442
+ content
443
+
444
+ user:
445
+ content
446
+ @{ rf.tool.mjs | text}
447
+
448
+ assistant:
449
+ content
450
+ import { promises as fs } from 'fs';
451
+ import { relative, dirname } from 'path'
452
+ ....
453
+ ```
454
+
455
+ ### `resolve`
456
+ Given filename resolve it and include
457
+
458
+ ```chat
459
+ @{ filename | resolve }
460
+ ```
461
+
462
+ see `examples/queryimage` example
463
+
464
+ ### `prop`
465
+ set additional properties for llm
466
+
467
+ ```chat
468
+ system:
469
+ @{ o3-mini | prop reasoning_effort=low temperature=2.0 }
470
+ ```
471
+
472
+ ### `head`
473
+ Take first *N* lines of text from a file or variable. Default is 20 lines.
474
+
475
+ ```chat
476
+ user:
477
+ @{ filename.txt | head 10 } # first 10 lines
478
+ ```
479
+
480
+ ### `tail`
481
+ Take last *N* lines of text from a file or variable. Default is 20 lines.
482
+
483
+ ```chat
484
+ user:
485
+ @{ filename.txt | tail 15 } # last 15 lines
486
+ ```
487
+
488
+ You can limit llm request context with tail like
489
+ ```chat
490
+ system:
491
+ @{ gpt-4.1 | tail 2 } # take last 2 messages from the chat + system message
492
+
493
+ user:
494
+ 1
495
+
496
+ assistant:
497
+ 2
498
+
499
+ user:
500
+ 3
501
+
502
+ assistant:
503
+ 4
504
+ ```
505
+
506
+
507
+ ### `slice`
508
+ Extract a range of lines from a file or variable.
509
+
510
+ ```chat
511
+ user:
512
+ @{ filename.txt | slice 5 15 } # lines 5 to 15 inclusive
513
+ @{ filename.txt | slice 10 } # from line 10 to end
514
+ @{ filename.txt | slice -10 -1 } # last 10 lines
515
+ @{ filename.txt | slice -20 } # last 20 lines
516
+ @{ filename.txt | slice 1 20 } # first 20 lines (like head 20)
517
+ ```
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "tune-basic-toolset",
3
+ "version": "0.1.0",
4
+ "description": "Basic toolset for tune",
5
+ "main": "src/index.js",
6
+ "files": [
7
+ "src/",
8
+ "README.md",
9
+ "LICENSE"
10
+ ],
11
+ "keywords": [
12
+ "ai",
13
+ "chat",
14
+ "tools",
15
+ "tune"
16
+ ],
17
+ "author": "Ilya Ovdin <iovdin@gmail.com>",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/iovdin/tune-basic-toolset"
21
+ },
22
+ "license": "MIT",
23
+ "peerDependencies": {
24
+ "tune-sdk": "latest",
25
+ "tune-fs": "latest"
26
+ },
27
+ "devDependencies": {
28
+ "tune-sdk": "latest",
29
+ "tune-fs": "latest"
30
+ },
31
+ "dependencies": {
32
+ "escodegen": "^2.1.0",
33
+ "esprima": "^4.0.1"
34
+ }
35
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "description": "Append text to a file",
3
+ "parameters": {
4
+ "type": "object",
5
+ "properties": {
6
+ "filename": {
7
+ "type": "string",
8
+ "description": "Name of the file to append text to"
9
+ },
10
+ "text": {
11
+ "type": "string",
12
+ "description": "Text content to append to the file"
13
+ }
14
+ },
15
+ "required": ["filename", "text"]
16
+ }
17
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = async function append({filename, text}) {
2
+ const content = await this.read(filename) || "";
3
+ await this.write(filename, `${content}\n${text}`);
4
+ return "done";
5
+ };
@@ -0,0 +1,24 @@
1
+ module.exports = async function head(node, args, context) {
2
+ // Only transform text nodes
3
+ if (!node || node.type !== 'text') {
4
+ return node;
5
+ }
6
+
7
+ // Parse the number of lines argument, default is 20
8
+ const n = parseInt(args, 10);
9
+ const linesCount = (isNaN(n) || n <= 0) ? 20 : n;
10
+
11
+ const newNode = Object.assign({}, node);
12
+
13
+ newNode.read = async () => {
14
+ const text = await node.read();
15
+ // Split text into lines
16
+ const lines = text.split(/\r?\n/);
17
+ // Take first linesCount lines
18
+ const firstLines = lines.slice(0, linesCount);
19
+ // Join back into string
20
+ return firstLines.join('\n');
21
+ };
22
+
23
+ return newNode;
24
+ };
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ const { tools } = require('tune-fs')
2
+
3
+ // TODO js tool packages include
4
+ module.exports = (...opts) =>
5
+ tools({ ...opts, path: __dirname })
6
+
@@ -0,0 +1,19 @@
1
+ module.exports = async function init(node, args, ctx) {
2
+ if (node) {
3
+ return node;
4
+ }
5
+ const content = (args || "").trim();
6
+
7
+ // include file
8
+ if (content.indexOf("@") === 0) {
9
+ return {
10
+ type: "text",
11
+ read: async () => this.read(content.replace(/^@{1,2}/, ""))
12
+ }
13
+ }
14
+
15
+ return {
16
+ type: "text",
17
+ read: async () => content
18
+ }
19
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "description": "Fetch content from a specified URL",
3
+ "parameters": {
4
+ "type": "object",
5
+ "properties": {
6
+ "url": {
7
+ "type": "string",
8
+ "description": "The URL of the website to fetch content from returns markdown"
9
+ },
10
+ "filename": {
11
+ "type": "string",
12
+ "description": "save result to this filename"
13
+ },
14
+ "links": {
15
+ "type": "boolean",
16
+ "description": "provide links in the end"
17
+ }
18
+ },
19
+ "required": ["url"]
20
+ }
21
+ }
@@ -0,0 +1,27 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ module.exports = async function fetchFromUrl({ url, filename, links }, ctx) {
5
+ const wurl = `https://r.jina.ai/${url}`;
6
+ const headers = {};
7
+
8
+ const apiKey = await ctx.read("JINA_KEY");
9
+ if (apiKey) {
10
+ headers.Authorization = `Bearer ${apiKey}`;
11
+ }
12
+
13
+ if (links) {
14
+ headers['X-With-Links-Summary'] = true;
15
+ }
16
+
17
+ const response = await fetch(wurl, { headers } );
18
+ if (!response.ok) {
19
+ throw new Error(`Error: ${response.status} ${response.statusText}`);
20
+ }
21
+ const res = await response.text();
22
+ if (filename) {
23
+ await ctx.write(filename, res);
24
+ return `@${filename}`;
25
+ }
26
+ return res.replace(/@/g, "\\@");
27
+ };
@@ -0,0 +1,19 @@
1
+ {
2
+ "description": "Execute a given JavaScript code snippet and return the result",
3
+ "parameters": {
4
+ "type": "object",
5
+ "properties": {
6
+ "text": {
7
+ "type": "string",
8
+ "description": "The JavaScript code to be executed"
9
+ },
10
+ "inputType": {
11
+ "type": "string",
12
+ "enum": ["module", "commonjs"],
13
+ "description": "Type of script (module or commonjs), default it commonjs",
14
+ "default": "commonjs"
15
+ }
16
+ },
17
+ "required": ["text", "inputType"]
18
+ }
19
+ }