agentool 1.0.0 → 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.
Files changed (80) hide show
  1. package/README.md +132 -21
  2. package/dist/chunk-44AFQ2B7.js +30 -0
  3. package/dist/{chunk-Y7KOKDFP.js → chunk-4HIATLKI.js} +1 -4
  4. package/dist/chunk-4HXAKPQH.cjs +36 -0
  5. package/dist/chunk-56CL4JCW.cjs +53 -0
  6. package/dist/{chunk-U2YMJM25.cjs → chunk-5O55DKOB.cjs} +1 -4
  7. package/dist/{chunk-IMZQ7ELK.cjs → chunk-5TDZF4IM.cjs} +10 -9
  8. package/dist/{chunk-JCTBB7H2.cjs → chunk-6DJSWTWQ.cjs} +1 -1
  9. package/dist/{chunk-ZHCMEQJJ.js → chunk-ACGW44YT.js} +3 -3
  10. package/dist/chunk-E6NBEYZD.js +51 -0
  11. package/dist/{chunk-7QL4BQCH.js → chunk-ECYT46FP.js} +1 -1
  12. package/dist/{chunk-QEJV2KZ4.cjs → chunk-HNP7JDQC.cjs} +14 -14
  13. package/dist/chunk-HNUL2CID.cjs +34 -0
  14. package/dist/chunk-IEX4NOVN.cjs +48 -0
  15. package/dist/chunk-IRRNYFI5.js +48 -0
  16. package/dist/chunk-L5JH4I77.cjs +51 -0
  17. package/dist/chunk-LK6SQH2G.cjs +30 -0
  18. package/dist/chunk-LTE5NG4D.js +53 -0
  19. package/dist/{chunk-TBVHHF3H.cjs → chunk-OM2UFTGS.cjs} +3 -3
  20. package/dist/{chunk-S6QEY7UY.js → chunk-P6Z5XFDS.js} +2 -2
  21. package/dist/chunk-S7IVHOA6.js +75 -0
  22. package/dist/chunk-TMW3XKKJ.js +34 -0
  23. package/dist/{chunk-XKG2A3EW.js → chunk-UDIG7332.js} +14 -14
  24. package/dist/chunk-VXZ4RKJI.js +36 -0
  25. package/dist/{chunk-6MDPYALY.js → chunk-XAQGZ374.js} +10 -9
  26. package/dist/{chunk-MXFW3XY6.cjs → chunk-ZBLQV6UO.cjs} +2 -2
  27. package/dist/chunk-ZFQZWXOI.cjs +75 -0
  28. package/dist/edit/index.cjs +2 -2
  29. package/dist/edit/index.d.cts +2 -2
  30. package/dist/edit/index.d.ts +2 -2
  31. package/dist/edit/index.js +1 -1
  32. package/dist/grep/index.cjs +2 -2
  33. package/dist/grep/index.js +1 -1
  34. package/dist/index.cjs +42 -11
  35. package/dist/index.d.cts +6 -1
  36. package/dist/index.d.ts +6 -1
  37. package/dist/index.js +53 -22
  38. package/dist/lsp/index.cjs +2 -2
  39. package/dist/lsp/index.d.cts +6 -9
  40. package/dist/lsp/index.d.ts +6 -9
  41. package/dist/lsp/index.js +1 -1
  42. package/dist/read/index.cjs +2 -2
  43. package/dist/read/index.js +1 -1
  44. package/dist/task-create/index.cjs +9 -0
  45. package/dist/task-create/index.d.cts +19 -0
  46. package/dist/task-create/index.d.ts +19 -0
  47. package/dist/task-create/index.js +9 -0
  48. package/dist/task-get/index.cjs +9 -0
  49. package/dist/task-get/index.d.cts +15 -0
  50. package/dist/task-get/index.d.ts +15 -0
  51. package/dist/task-get/index.js +9 -0
  52. package/dist/task-list/index.cjs +9 -0
  53. package/dist/task-list/index.d.cts +11 -0
  54. package/dist/task-list/index.d.ts +11 -0
  55. package/dist/task-list/index.js +9 -0
  56. package/dist/task-update/index.cjs +9 -0
  57. package/dist/task-update/index.d.cts +31 -0
  58. package/dist/task-update/index.d.ts +31 -0
  59. package/dist/task-update/index.js +9 -0
  60. package/dist/tool-search/index.cjs +8 -0
  61. package/dist/tool-search/index.d.cts +18 -0
  62. package/dist/tool-search/index.d.ts +18 -0
  63. package/dist/tool-search/index.js +8 -0
  64. package/dist/web-fetch/index.cjs +2 -2
  65. package/dist/web-fetch/index.d.cts +0 -2
  66. package/dist/web-fetch/index.d.ts +0 -2
  67. package/dist/web-fetch/index.js +1 -1
  68. package/dist/web-search/index.cjs +8 -0
  69. package/dist/web-search/index.d.cts +21 -0
  70. package/dist/web-search/index.d.ts +21 -0
  71. package/dist/web-search/index.js +8 -0
  72. package/dist/write/index.cjs +2 -2
  73. package/dist/write/index.js +1 -1
  74. package/package.json +31 -6
  75. package/dist/chunk-FAEGCFTO.js +0 -136
  76. package/dist/chunk-XLD2Y3SS.cjs +0 -136
  77. package/dist/task/index.cjs +0 -8
  78. package/dist/task/index.d.cts +0 -67
  79. package/dist/task/index.d.ts +0 -67
  80. package/dist/task/index.js +0 -8
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # agentool
4
4
 
5
- **16 AI agent tools as standalone [Vercel AI SDK](https://sdk.vercel.ai/) modules.**
5
+ **22 AI agent tools as standalone [Vercel AI SDK](https://sdk.vercel.ai/) modules.**
6
6
 
7
7
  <p>
8
8
  <a href="https://www.npmjs.com/package/agentool"><img src="https://img.shields.io/npm/v/agentool?style=flat-square&color=cb3837&logo=npm" alt="npm version" /></a>
@@ -27,13 +27,13 @@ File operations, shell execution, code search, web fetching, and more -- everyth
27
27
 
28
28
  ## Features
29
29
 
30
- - **16 production-ready tools** -- bash, grep, glob, read, edit, write, web-fetch, memory, multi-edit, diff, task, lsp, http-request, context-compaction, ask-user, sleep
30
+ - **22 production-ready tools** -- bash, grep, glob, read, edit, write, web-fetch, web-search, tool-search, memory, multi-edit, diff, task-create, task-get, task-update, task-list, lsp, http-request, context-compaction, ask-user, sleep
31
31
  - **Vercel AI SDK compatible** -- works with `generateText()`, `streamText()`, and any AI SDK provider (OpenAI, Anthropic, Google, etc.)
32
32
  - **Factory + default pattern** -- `createBash({ cwd: '/my/project' })` for custom config, or just use `bash` with zero config
33
33
  - **Dual ESM/CJS** -- works everywhere with proper `exports` map
34
34
  - **TypeScript-first** -- full type declarations, strict mode, no `any`
35
35
  - **Never throws** -- every `execute()` returns a descriptive error string instead of throwing
36
- - **Tree-shakeable** -- 17 subpath exports, only import what you need
36
+ - **Tree-shakeable** -- 22 subpath exports, only import what you need
37
37
 
38
38
  ## Installation
39
39
 
@@ -326,7 +326,58 @@ const result = await webFetch.execute(
326
326
  // Returns markdown content (HTML converted via Turndown, truncated at 100K chars)
327
327
  ```
328
328
 
329
- **Parameters:** `url` (string), `prompt?` (string)
329
+ **Parameters:** `url` (string, must be valid URL)
330
+
331
+ ---
332
+
333
+ ### web-search
334
+
335
+ Search the web with a callback-based implementation (bring your own search provider).
336
+
337
+ ```typescript
338
+ import { createWebSearch } from 'agentool/web-search';
339
+
340
+ const webSearch = createWebSearch({
341
+ onSearch: async (query, { allowed_domains, blocked_domains }) => {
342
+ // Use Tavily, SerpAPI, Google, or any search provider
343
+ const results = await mySearchProvider.search(query, { allowed_domains, blocked_domains });
344
+ return results.map(r => `${r.title}: ${r.url}\n${r.snippet}`).join('\n\n');
345
+ },
346
+ });
347
+
348
+ const result = await webSearch.execute(
349
+ { query: 'TypeScript best practices 2024', allowed_domains: ['typescript-eslint.io'] },
350
+ { toolCallId: 'id', messages: [] },
351
+ );
352
+ ```
353
+
354
+ **Parameters:** `query` (string, min 2 chars), `allowed_domains?` (string[]), `blocked_domains?` (string[])
355
+
356
+ ---
357
+
358
+ ### tool-search
359
+
360
+ Search through a registry of available tools by name or keyword.
361
+
362
+ ```typescript
363
+ import { createToolSearch } from 'agentool/tool-search';
364
+
365
+ const toolSearch = createToolSearch({
366
+ tools: {
367
+ bash: { description: 'Execute shell commands' },
368
+ grep: { description: 'Search file contents with regex' },
369
+ read: { description: 'Read file contents' },
370
+ },
371
+ });
372
+
373
+ const result = await toolSearch.execute(
374
+ { query: 'file', max_results: 3 },
375
+ { toolCallId: 'id', messages: [] },
376
+ );
377
+ // Returns matching tools sorted by relevance
378
+ ```
379
+
380
+ **Parameters:** `query` (string), `max_results?` (number, default 5)
330
381
 
331
382
  ---
332
383
 
@@ -390,33 +441,78 @@ await memory.execute(
390
441
 
391
442
  ---
392
443
 
393
- ### task
444
+ ### task-create
394
445
 
395
- JSON file-based task tracker for agent workflows.
446
+ Create a new task with subject, description, and optional metadata.
396
447
 
397
448
  ```typescript
398
- import { task } from 'agentool/task';
449
+ import { taskCreate } from 'agentool/task-create';
399
450
 
400
- // Create
401
- const created = await task.execute(
402
- { action: 'create', subject: 'Fix login bug', description: 'Auth fails on refresh' },
451
+ const result = await taskCreate.execute(
452
+ { subject: 'Fix login bug', description: 'Auth fails on refresh', metadata: { priority: 'high' } },
403
453
  { toolCallId: 'id', messages: [] },
404
454
  );
455
+ ```
405
456
 
406
- // List all
407
- const list = await task.execute(
408
- { action: 'list' },
457
+ **Parameters:** `subject` (string), `description` (string), `metadata?` (Record<string, unknown>)
458
+
459
+ ---
460
+
461
+ ### task-get
462
+
463
+ Retrieve a task by ID to see full details.
464
+
465
+ ```typescript
466
+ import { taskGet } from 'agentool/task-get';
467
+
468
+ const result = await taskGet.execute(
469
+ { taskId: 'abc123' },
409
470
  { toolCallId: 'id', messages: [] },
410
471
  );
472
+ ```
473
+
474
+ **Parameters:** `taskId` (string)
475
+
476
+ ---
477
+
478
+ ### task-update
479
+
480
+ Update a task's status, owner, metadata, and dependency relationships.
481
+
482
+ ```typescript
483
+ import { taskUpdate } from 'agentool/task-update';
484
+
485
+ // Update status and owner
486
+ await taskUpdate.execute(
487
+ { taskId: 'abc123', status: 'in_progress', owner: 'agent-1' },
488
+ { toolCallId: 'id', messages: [] },
489
+ );
490
+
491
+ // Add dependencies and merge metadata (null deletes a key)
492
+ await taskUpdate.execute(
493
+ { taskId: 'abc123', addBlockedBy: ['def456'], metadata: { priority: null, notes: 'reviewed' } },
494
+ { toolCallId: 'id', messages: [] },
495
+ );
496
+ ```
497
+
498
+ **Parameters:** `taskId` (string), `subject?` (string), `description?` (string), `status?` (`'pending'` | `'in_progress'` | `'completed'` | `'deleted'`), `owner?` (string), `activeForm?` (string), `addBlocks?` (string[]), `addBlockedBy?` (string[]), `metadata?` (Record<string, unknown>)
499
+
500
+ ---
501
+
502
+ ### task-list
503
+
504
+ List all non-deleted tasks with status and dependencies.
505
+
506
+ ```typescript
507
+ import { taskList } from 'agentool/task-list';
411
508
 
412
- // Update status
413
- await task.execute(
414
- { action: 'update', id: 'abc123', status: 'completed' },
509
+ const result = await taskList.execute(
510
+ {},
415
511
  { toolCallId: 'id', messages: [] },
416
512
  );
417
513
  ```
418
514
 
419
- **Parameters:** `action` (`'create'` | `'get'` | `'update'` | `'list'` | `'delete'`), `id?` (string), `subject?` (string), `description?` (string), `status?` (`'pending'` | `'in_progress'` | `'completed'`)
515
+ **Parameters:** none
420
516
 
421
517
  ---
422
518
 
@@ -440,7 +536,7 @@ const result = await lsp.execute(
440
536
  );
441
537
  ```
442
538
 
443
- **Parameters:** `operation` (`'goToDefinition'` | `'findReferences'` | `'hover'` | `'documentSymbol'` | `'workspaceSymbol'` | `'goToImplementation'` | `'incomingCalls'` | `'outgoingCalls'`), `filePath` (string), `line?` (number), `character?` (number), `query?` (string)
539
+ **Parameters:** `operation` (`'goToDefinition'` | `'findReferences'` | `'hover'` | `'documentSymbol'` | `'workspaceSymbol'` | `'goToImplementation'` | `'prepareCallHierarchy'` | `'incomingCalls'` | `'outgoingCalls'`), `filePath` (string), `line` (number, 1-based), `character` (number, 1-based)
444
540
 
445
541
  ---
446
542
 
@@ -552,7 +648,12 @@ Tools that support timeouts extend `TimeoutConfig`:
552
648
  | `bash` | `shell?: string` -- shell binary path |
553
649
  | `read` | `maxLines?: number` -- max lines to return (default: 2000) |
554
650
  | `memory` | `memoryDir?: string` -- storage directory |
555
- | `task` | `tasksFile?: string` -- JSON file path |
651
+ | `task-create` | `tasksFile?: string` -- JSON file path |
652
+ | `task-get` | `tasksFile?: string` -- JSON file path |
653
+ | `task-update` | `tasksFile?: string` -- JSON file path |
654
+ | `task-list` | `tasksFile?: string` -- JSON file path |
655
+ | `web-search` | `onSearch?: (query, opts) => Promise<string>` -- search callback |
656
+ | `tool-search` | `tools?: Record<string, { description }>` -- tool registry |
556
657
  | `lsp` | `servers?: Record<string, LspServerConfig>` -- LSP servers by file extension |
557
658
  | `http-request` | `defaultHeaders?: Record<string, string>` -- headers merged into every request |
558
659
  | `web-fetch` | `maxContentLength?: number`, `userAgent?: string` |
@@ -587,11 +688,16 @@ import {
587
688
  grep, createGrep,
588
689
  glob, createGlob,
589
690
  webFetch, createWebFetch,
691
+ webSearch, createWebSearch,
692
+ toolSearch, createToolSearch,
590
693
  httpRequest, createHttpRequest,
591
694
  memory, createMemory,
592
695
  multiEdit, createMultiEdit,
593
696
  diff, createDiff,
594
- task, createTask,
697
+ taskCreate, createTaskCreate,
698
+ taskGet, createTaskGet,
699
+ taskUpdate, createTaskUpdate,
700
+ taskList, createTaskList,
595
701
  lsp, createLsp,
596
702
  contextCompaction, createContextCompaction,
597
703
  askUser, createAskUser,
@@ -613,7 +719,12 @@ import { httpRequest } from 'agentool/http-request';
613
719
  import { memory } from 'agentool/memory';
614
720
  import { multiEdit } from 'agentool/multi-edit';
615
721
  import { diff } from 'agentool/diff';
616
- import { task } from 'agentool/task';
722
+ import { taskCreate } from 'agentool/task-create';
723
+ import { taskGet } from 'agentool/task-get';
724
+ import { taskUpdate } from 'agentool/task-update';
725
+ import { taskList } from 'agentool/task-list';
726
+ import { webSearch } from 'agentool/web-search';
727
+ import { toolSearch } from 'agentool/tool-search';
617
728
  import { lsp } from 'agentool/lsp';
618
729
  import { contextCompaction } from 'agentool/context-compaction';
619
730
  import { askUser } from 'agentool/ask-user';
@@ -0,0 +1,30 @@
1
+ // src/web-search/index.ts
2
+ import { tool } from "ai";
3
+ import { z } from "zod";
4
+ function createWebSearch(config = {}) {
5
+ return tool({
6
+ description: "Search the web for information using a search query. Results can be filtered by allowed or blocked domains.",
7
+ inputSchema: z.object({
8
+ query: z.string().min(2).describe("The search query to use"),
9
+ allowed_domains: z.array(z.string()).optional().describe("Only include search results from these domains"),
10
+ blocked_domains: z.array(z.string()).optional().describe("Never include search results from these domains")
11
+ }),
12
+ execute: async ({ query, allowed_domains, blocked_domains }) => {
13
+ try {
14
+ if (!config.onSearch) {
15
+ return "Error [web-search]: No search callback configured. Provide onSearch via createWebSearch({ onSearch: async (query, opts) => ... })";
16
+ }
17
+ return await config.onSearch(query, { allowed_domains, blocked_domains });
18
+ } catch (error) {
19
+ const msg = error instanceof Error ? error.message : String(error);
20
+ return `Error [web-search]: ${msg}`;
21
+ }
22
+ }
23
+ });
24
+ }
25
+ var webSearch = createWebSearch();
26
+
27
+ export {
28
+ createWebSearch,
29
+ webSearch
30
+ };
@@ -78,10 +78,7 @@ function createWebFetch(config = {}) {
78
78
  return tool({
79
79
  description: "Fetch a URL and return its content. HTML pages are automatically converted to markdown for easier reading. JSON and other text content is returned as-is. Content is truncated at 100,000 characters to manage context size.",
80
80
  inputSchema: z.object({
81
- url: z.string().describe("The URL to fetch"),
82
- prompt: z.string().optional().describe(
83
- "Optional context about what to extract from the page"
84
- )
81
+ url: z.string().url().describe("The URL to fetch")
85
82
  }),
86
83
  execute: async ({ url }) => {
87
84
  try {
@@ -0,0 +1,36 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+
4
+ var _chunkL5JH4I77cjs = require('./chunk-L5JH4I77.cjs');
5
+
6
+ // src/task-get/index.ts
7
+ var _ai = require('ai');
8
+ var _zod = require('zod');
9
+ var _path = require('path');
10
+ function createTaskGet(config = {}) {
11
+ const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
12
+ const tasksFile = _nullishCoalesce(config.tasksFile, () => ( _path.join.call(void 0, cwd, ".agentool", "tasks.json")));
13
+ return _ai.tool.call(void 0, {
14
+ description: "Retrieve a task by its ID to see full details.",
15
+ inputSchema: _zod.z.object({
16
+ taskId: _zod.z.string().describe("The ID of the task to retrieve")
17
+ }),
18
+ execute: async ({ taskId }) => {
19
+ try {
20
+ const tasks = await _chunkL5JH4I77cjs.loadTasks.call(void 0, tasksFile);
21
+ const found = tasks.find((t) => t.id === taskId);
22
+ if (!found) return `Error [task-get]: Task "${taskId}" not found.`;
23
+ return _chunkL5JH4I77cjs.formatTask.call(void 0, found);
24
+ } catch (error) {
25
+ const msg = error instanceof Error ? error.message : String(error);
26
+ return `Error [task-get]: ${msg}`;
27
+ }
28
+ }
29
+ });
30
+ }
31
+ var taskGet = createTaskGet();
32
+
33
+
34
+
35
+
36
+ exports.createTaskGet = createTaskGet; exports.taskGet = taskGet;
@@ -0,0 +1,53 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+
4
+
5
+
6
+ var _chunkL5JH4I77cjs = require('./chunk-L5JH4I77.cjs');
7
+
8
+ // src/task-create/index.ts
9
+ var _ai = require('ai');
10
+ var _zod = require('zod');
11
+ var _path = require('path');
12
+ function createTaskCreate(config = {}) {
13
+ const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
14
+ const tasksFile = _nullishCoalesce(config.tasksFile, () => ( _path.join.call(void 0, cwd, ".agentool", "tasks.json")));
15
+ return _ai.tool.call(void 0, {
16
+ description: 'Create a new task to track work. Each task gets a unique ID, starts with "pending" status, and can include optional metadata.',
17
+ inputSchema: _zod.z.object({
18
+ subject: _zod.z.string().describe("A brief title for the task"),
19
+ description: _zod.z.string().describe("What needs to be done"),
20
+ metadata: _zod.z.record(_zod.z.string(), _zod.z.unknown()).optional().describe("Arbitrary metadata to attach to the task")
21
+ }),
22
+ execute: async ({ subject, description, metadata }) => {
23
+ try {
24
+ const tasks = await _chunkL5JH4I77cjs.loadTasks.call(void 0, tasksFile);
25
+ const now = (/* @__PURE__ */ new Date()).toISOString();
26
+ const entry = {
27
+ id: _chunkL5JH4I77cjs.generateId.call(void 0, ),
28
+ subject,
29
+ description,
30
+ status: "pending",
31
+ blocks: [],
32
+ blockedBy: [],
33
+ metadata,
34
+ createdAt: now,
35
+ updatedAt: now
36
+ };
37
+ tasks.push(entry);
38
+ await _chunkL5JH4I77cjs.saveTasks.call(void 0, tasksFile, tasks);
39
+ return `Created task ${entry.id}.
40
+ ${_chunkL5JH4I77cjs.formatTask.call(void 0, entry)}`;
41
+ } catch (error) {
42
+ const msg = error instanceof Error ? error.message : String(error);
43
+ return `Error [task-create]: ${msg}`;
44
+ }
45
+ }
46
+ });
47
+ }
48
+ var taskCreate = createTaskCreate();
49
+
50
+
51
+
52
+
53
+ exports.createTaskCreate = createTaskCreate; exports.taskCreate = taskCreate;
@@ -78,10 +78,7 @@ function createWebFetch(config = {}) {
78
78
  return _ai.tool.call(void 0, {
79
79
  description: "Fetch a URL and return its content. HTML pages are automatically converted to markdown for easier reading. JSON and other text content is returned as-is. Content is truncated at 100,000 characters to manage context size.",
80
80
  inputSchema: _zod.z.object({
81
- url: _zod.z.string().describe("The URL to fetch"),
82
- prompt: _zod.z.string().optional().describe(
83
- "Optional context about what to extract from the page"
84
- )
81
+ url: _zod.z.string().url().describe("The URL to fetch")
85
82
  }),
86
83
  execute: async ({ url }) => {
87
84
  try {
@@ -39,13 +39,14 @@ function operationToMethod(operation) {
39
39
  documentSymbol: "textDocument/documentSymbol",
40
40
  workspaceSymbol: "workspace/symbol",
41
41
  goToImplementation: "textDocument/implementation",
42
+ prepareCallHierarchy: "textDocument/prepareCallHierarchy",
42
43
  incomingCalls: "textDocument/prepareCallHierarchy",
43
44
  outgoingCalls: "textDocument/prepareCallHierarchy"
44
45
  };
45
46
  return _nullishCoalesce(map[operation], () => ( operation));
46
47
  }
47
- function buildRequestParams(op, uri, line, char, query) {
48
- if (op === "workspaceSymbol") return { query: _nullishCoalesce(query, () => ( "")) };
48
+ function buildRequestParams(op, uri, line, char) {
49
+ if (op === "workspaceSymbol") return { query: "" };
49
50
  const td = { uri };
50
51
  if (op === "documentSymbol") return { textDocument: td };
51
52
  const pos = { line, character: char };
@@ -128,7 +129,7 @@ async function executeLspOperation(serverConfig, params, timeoutMs) {
128
129
  textDocument: { uri, languageId: langId, version: 1, text: content }
129
130
  });
130
131
  const method = operationToMethod(params.operation);
131
- const reqParams = buildRequestParams(params.operation, uri, _nullishCoalesce(params.line, () => ( 0)), _nullishCoalesce(params.character, () => ( 0)), params.query);
132
+ const reqParams = buildRequestParams(params.operation, uri, _nullishCoalesce(params.line, () => ( 0)), _nullishCoalesce(params.character, () => ( 0)));
132
133
  let response = await rpc(sendRequest(proc, method, reqParams));
133
134
  if (response.error) return `Error [lsp]: Server error: ${response.error.message} (code ${response.error.code})`;
134
135
  if ((params.operation === "incomingCalls" || params.operation === "outgoingCalls") && Array.isArray(response.result) && response.result.length > 0) {
@@ -150,21 +151,21 @@ var LSP_OPERATIONS = [
150
151
  "documentSymbol",
151
152
  "workspaceSymbol",
152
153
  "goToImplementation",
154
+ "prepareCallHierarchy",
153
155
  "incomingCalls",
154
156
  "outgoingCalls"
155
157
  ];
156
158
  function createLsp(config = {}) {
157
159
  const timeoutMs = _nullishCoalesce(config.timeout, () => ( 3e4));
158
160
  return _ai.tool.call(void 0, {
159
- description: "Perform language server operations like go-to-definition, find-references, and hover. Requires LSP server configuration. Supports 8 operations: goToDefinition, findReferences, hover, documentSymbol, workspaceSymbol, goToImplementation, incomingCalls, outgoingCalls.",
161
+ description: "Perform language server operations like go-to-definition, find-references, and hover. Requires LSP server configuration. Supports 9 operations: goToDefinition, findReferences, hover, documentSymbol, workspaceSymbol, goToImplementation, prepareCallHierarchy, incomingCalls, outgoingCalls.",
160
162
  inputSchema: _zod.z.object({
161
163
  operation: _zod.z.enum(LSP_OPERATIONS).describe("The LSP operation to perform"),
162
164
  filePath: _zod.z.string().describe("Path to the file"),
163
- line: _zod.z.number().optional().describe("Line number (0-indexed)"),
164
- character: _zod.z.number().optional().describe("Character offset (0-indexed)"),
165
- query: _zod.z.string().optional().describe("Search query for workspaceSymbol")
165
+ line: _zod.z.number().int().positive().describe("The line number (1-based, as shown in editors)"),
166
+ character: _zod.z.number().int().positive().describe("The character offset (1-based, as shown in editors)")
166
167
  }),
167
- execute: async ({ operation, filePath, line, character, query }) => {
168
+ execute: async ({ operation, filePath, line, character }) => {
168
169
  if (!config.servers || Object.keys(config.servers).length === 0) {
169
170
  return 'Error [lsp]: No LSP servers configured. Provide server configuration via createLsp({ servers: { ".ts": { command: "typescript-language-server", args: ["--stdio"] } } })';
170
171
  }
@@ -177,7 +178,7 @@ function createLsp(config = {}) {
177
178
  try {
178
179
  return await executeLspOperation(
179
180
  serverConfig,
180
- { operation, filePath, line, character, query, cwd: _nullishCoalesce(config.cwd, () => ( process.cwd())) },
181
+ { operation, filePath, line: line - 1, character: character - 1, cwd: _nullishCoalesce(config.cwd, () => ( process.cwd())) },
181
182
  timeoutMs
182
183
  );
183
184
  } catch (error) {
@@ -14,7 +14,7 @@ function createWrite(config = {}) {
14
14
  return _ai.tool.call(void 0, {
15
15
  description: "Write text content to a file, creating parent directories as needed. If the file exists it is overwritten. Use this to create new files or replace existing file contents.",
16
16
  inputSchema: _zod.z.object({
17
- file_path: _zod.z.string().describe("Path to the file to write"),
17
+ file_path: _zod.z.string().describe("The absolute path to the file to write (must be absolute, not relative)"),
18
18
  content: _zod.z.string().describe("Text content to write to the file")
19
19
  }),
20
20
  execute: async ({ file_path, content }) => {
@@ -15,9 +15,9 @@ function createRead(config = {}) {
15
15
  return tool({
16
16
  description: 'Read a file from the local filesystem and return its contents with line numbers. Supports absolute paths, relative paths (resolved against the working directory), and tilde (~) home directory expansion. Returns numbered lines in "lineNumber\\tcontent" format. Use offset and limit to read specific ranges of large files.',
17
17
  inputSchema: z.object({
18
- file_path: z.string().describe("The path to the file to read (absolute, relative, or ~/...)"),
19
- offset: z.number().optional().describe("0-indexed line number to start reading from (default: 0)"),
20
- limit: z.number().optional().describe("Maximum number of lines to return (default: 2000)")
18
+ file_path: z.string().describe("The absolute path to the file to read"),
19
+ offset: z.number().int().nonnegative().optional().describe("The line number to start reading from (default: 0)"),
20
+ limit: z.number().int().positive().optional().describe("The number of lines to read (default: 2000)")
21
21
  }),
22
22
  execute: async ({ file_path, offset, limit }) => {
23
23
  try {
@@ -0,0 +1,51 @@
1
+ // src/shared/task-store.ts
2
+ import { mkdir, readFile, writeFile } from "fs/promises";
3
+ import { dirname } from "path";
4
+ import { randomBytes } from "crypto";
5
+ function generateId() {
6
+ return randomBytes(4).toString("hex");
7
+ }
8
+ async function loadTasks(filePath) {
9
+ try {
10
+ const data = await readFile(filePath, "utf-8");
11
+ return JSON.parse(data);
12
+ } catch {
13
+ return [];
14
+ }
15
+ }
16
+ async function saveTasks(filePath, tasks) {
17
+ await mkdir(dirname(filePath), { recursive: true });
18
+ await writeFile(filePath, JSON.stringify(tasks, null, 2), "utf-8");
19
+ }
20
+ function formatTask(t) {
21
+ const lines = [
22
+ `ID: ${t.id}`,
23
+ `Subject: ${t.subject}`,
24
+ `Description: ${t.description}`,
25
+ `Status: ${t.status}`
26
+ ];
27
+ if (t.owner) lines.push(`Owner: ${t.owner}`);
28
+ if (t.activeForm) lines.push(`Active: ${t.activeForm}`);
29
+ if (t.blocks.length > 0) lines.push(`Blocks: ${t.blocks.join(", ")}`);
30
+ if (t.blockedBy.length > 0) lines.push(`Blocked by: ${t.blockedBy.join(", ")}`);
31
+ if (t.metadata && Object.keys(t.metadata).length > 0) {
32
+ lines.push(`Metadata: ${JSON.stringify(t.metadata)}`);
33
+ }
34
+ lines.push(`Created: ${t.createdAt}`);
35
+ lines.push(`Updated: ${t.updatedAt}`);
36
+ return lines.join("\n");
37
+ }
38
+ function formatTaskSummary(t) {
39
+ const parts = [`#${t.id} [${t.status}] ${t.subject}`];
40
+ if (t.owner) parts.push(`(owner: ${t.owner})`);
41
+ if (t.blockedBy.length > 0) parts.push(`[blocked by ${t.blockedBy.join(", ")}]`);
42
+ return parts.join(" ");
43
+ }
44
+
45
+ export {
46
+ generateId,
47
+ loadTasks,
48
+ saveTasks,
49
+ formatTask,
50
+ formatTaskSummary
51
+ };
@@ -14,7 +14,7 @@ function createWrite(config = {}) {
14
14
  return tool({
15
15
  description: "Write text content to a file, creating parent directories as needed. If the file exists it is overwritten. Use this to create new files or replace existing file contents.",
16
16
  inputSchema: z.object({
17
- file_path: z.string().describe("Path to the file to write"),
17
+ file_path: z.string().describe("The absolute path to the file to write (must be absolute, not relative)"),
18
18
  content: z.string().describe("Text content to write to the file")
19
19
  }),
20
20
  execute: async ({ file_path, content }) => {
@@ -50,20 +50,20 @@ function createGrep(config = {}) {
50
50
  return _ai.tool.call(void 0, {
51
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
52
  inputSchema: _zod.z.object({
53
- pattern: _zod.z.string().describe("Regex pattern to search for"),
54
- path: _zod.z.string().optional().describe("File or directory to search in"),
55
- glob: _zod.z.string().optional().describe('Glob pattern to filter files (e.g. "*.js")'),
56
- output_mode: _zod.z.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode. Defaults to "files_with_matches".'),
57
- "-B": _zod.z.number().optional().describe("Lines of context before each match"),
58
- "-A": _zod.z.number().optional().describe("Lines of context after each match"),
59
- "-C": _zod.z.number().optional().describe("Lines of context around each match"),
60
- context: _zod.z.number().optional().describe("Context lines (alias for -C)"),
61
- "-n": _zod.z.boolean().optional().describe("Show line numbers (content mode, default true)"),
62
- "-i": _zod.z.boolean().optional().describe("Case insensitive search"),
63
- type: _zod.z.string().optional().describe('File type filter (e.g. "js", "py")'),
64
- head_limit: _zod.z.number().optional().describe("Max entries. Default 250, 0 = unlimited."),
65
- offset: _zod.z.number().optional().describe("Skip first N entries. Default 0."),
66
- multiline: _zod.z.boolean().optional().describe("Enable multiline matching")
53
+ pattern: _zod.z.string().describe("The regular expression pattern to search for in file contents"),
54
+ path: _zod.z.string().optional().describe("File or directory to search in (rg PATH). Defaults to current working directory."),
55
+ glob: _zod.z.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob'),
56
+ output_mode: _zod.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": _zod.z.number().optional().describe('Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.'),
58
+ "-A": _zod.z.number().optional().describe('Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.'),
59
+ "-C": _zod.z.number().optional().describe("Alias for context."),
60
+ context: _zod.z.number().optional().describe('Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.'),
61
+ "-n": _zod.z.boolean().optional().describe('Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.'),
62
+ "-i": _zod.z.boolean().optional().describe("Case insensitive search (rg -i)"),
63
+ type: _zod.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: _zod.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: _zod.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: _zod.z.boolean().optional().describe("Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.")
67
67
  }),
68
68
  execute: async (input) => {
69
69
  try {
@@ -0,0 +1,34 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+
4
+ var _chunkL5JH4I77cjs = require('./chunk-L5JH4I77.cjs');
5
+
6
+ // src/task-list/index.ts
7
+ var _ai = require('ai');
8
+ var _zod = require('zod');
9
+ var _path = require('path');
10
+ function createTaskList(config = {}) {
11
+ const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
12
+ const tasksFile = _nullishCoalesce(config.tasksFile, () => ( _path.join.call(void 0, cwd, ".agentool", "tasks.json")));
13
+ return _ai.tool.call(void 0, {
14
+ description: "List all tasks with their status, owner, and dependencies.",
15
+ inputSchema: _zod.z.object({}),
16
+ execute: async () => {
17
+ try {
18
+ const tasks = await _chunkL5JH4I77cjs.loadTasks.call(void 0, tasksFile);
19
+ const visible = tasks.filter((t) => t.status !== "deleted");
20
+ if (visible.length === 0) return "No tasks found.";
21
+ return visible.map(_chunkL5JH4I77cjs.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
+
32
+
33
+
34
+ exports.createTaskList = createTaskList; exports.taskList = taskList;
@@ -0,0 +1,48 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/tool-search/index.ts
2
+ var _ai = require('ai');
3
+ var _zod = require('zod');
4
+ function createToolSearch(config = {}) {
5
+ return _ai.tool.call(void 0, {
6
+ description: "Search for available tools by name or keyword. Returns matching tool names and descriptions.",
7
+ inputSchema: _zod.z.object({
8
+ query: _zod.z.string().describe("Query to find tools by name or keyword"),
9
+ max_results: _zod.z.number().optional().default(5).describe("Max results to return")
10
+ }),
11
+ execute: async ({ query, max_results }) => {
12
+ try {
13
+ const registry = _nullishCoalesce(config.tools, () => ( {}));
14
+ const entries = Object.entries(registry);
15
+ if (entries.length === 0) {
16
+ return "No tools registered. Provide a tools registry via createToolSearch({ tools: { ... } })";
17
+ }
18
+ const lower = query.toLowerCase();
19
+ const scored = entries.map(([name, { description }]) => {
20
+ let score = 0;
21
+ const nameLower = name.toLowerCase();
22
+ const descLower = description.toLowerCase();
23
+ if (nameLower === lower) score += 10;
24
+ else if (nameLower.includes(lower)) score += 5;
25
+ if (descLower.includes(lower)) score += 3;
26
+ for (const word of lower.split(/\s+/)) {
27
+ if (nameLower.includes(word)) score += 2;
28
+ if (descLower.includes(word)) score += 1;
29
+ }
30
+ return { name, description, score };
31
+ }).filter((e) => e.score > 0).sort((a, b) => b.score - a.score).slice(0, max_results);
32
+ if (scored.length === 0) {
33
+ return `No tools matched query "${query}".`;
34
+ }
35
+ return scored.map((e) => `${e.name}: ${e.description}`).join("\n");
36
+ } catch (error) {
37
+ const msg = error instanceof Error ? error.message : String(error);
38
+ return `Error [tool-search]: ${msg}`;
39
+ }
40
+ }
41
+ });
42
+ }
43
+ var toolSearch = createToolSearch();
44
+
45
+
46
+
47
+
48
+ exports.createToolSearch = createToolSearch; exports.toolSearch = toolSearch;