@jcheesepkg/nanobot 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.
Files changed (130) hide show
  1. package/README.md +139 -0
  2. package/dist/_virtual/_rolldown/runtime.mjs +40 -0
  3. package/dist/agent/context.d.mts +44 -0
  4. package/dist/agent/context.d.mts.map +1 -0
  5. package/dist/agent/context.mjs +153 -0
  6. package/dist/agent/context.mjs.map +1 -0
  7. package/dist/agent/loop.d.mts +56 -0
  8. package/dist/agent/loop.d.mts.map +1 -0
  9. package/dist/agent/loop.mjs +232 -0
  10. package/dist/agent/loop.mjs.map +1 -0
  11. package/dist/agent/memory.d.mts +30 -0
  12. package/dist/agent/memory.d.mts.map +1 -0
  13. package/dist/agent/memory.mjs +77 -0
  14. package/dist/agent/memory.mjs.map +1 -0
  15. package/dist/agent/skills.d.mts +43 -0
  16. package/dist/agent/skills.d.mts.map +1 -0
  17. package/dist/agent/skills.mjs +159 -0
  18. package/dist/agent/skills.mjs.map +1 -0
  19. package/dist/agent/subagent.d.mts +39 -0
  20. package/dist/agent/subagent.d.mts.map +1 -0
  21. package/dist/agent/subagent.mjs +167 -0
  22. package/dist/agent/subagent.mjs.map +1 -0
  23. package/dist/agent/tools/base.d.mts +16 -0
  24. package/dist/agent/tools/base.d.mts.map +1 -0
  25. package/dist/agent/tools/base.mjs +19 -0
  26. package/dist/agent/tools/base.mjs.map +1 -0
  27. package/dist/agent/tools/cron.d.mts +49 -0
  28. package/dist/agent/tools/cron.d.mts.map +1 -0
  29. package/dist/agent/tools/cron.mjs +101 -0
  30. package/dist/agent/tools/cron.mjs.map +1 -0
  31. package/dist/agent/tools/filesystem.d.mts +87 -0
  32. package/dist/agent/tools/filesystem.d.mts.map +1 -0
  33. package/dist/agent/tools/filesystem.mjs +145 -0
  34. package/dist/agent/tools/filesystem.mjs.map +1 -0
  35. package/dist/agent/tools/message.d.mts +44 -0
  36. package/dist/agent/tools/message.d.mts.map +1 -0
  37. package/dist/agent/tools/message.mjs +68 -0
  38. package/dist/agent/tools/message.mjs.map +1 -0
  39. package/dist/agent/tools/registry.d.mts +29 -0
  40. package/dist/agent/tools/registry.d.mts.map +1 -0
  41. package/dist/agent/tools/registry.mjs +49 -0
  42. package/dist/agent/tools/registry.mjs.map +1 -0
  43. package/dist/agent/tools/shell.d.mts +36 -0
  44. package/dist/agent/tools/shell.d.mts.map +1 -0
  45. package/dist/agent/tools/shell.mjs +73 -0
  46. package/dist/agent/tools/shell.mjs.map +1 -0
  47. package/dist/agent/tools/spawn.d.mts +35 -0
  48. package/dist/agent/tools/spawn.d.mts.map +1 -0
  49. package/dist/agent/tools/spawn.mjs +50 -0
  50. package/dist/agent/tools/spawn.mjs.map +1 -0
  51. package/dist/agent/tools/web.d.mts +63 -0
  52. package/dist/agent/tools/web.d.mts.map +1 -0
  53. package/dist/agent/tools/web.mjs +195 -0
  54. package/dist/agent/tools/web.mjs.map +1 -0
  55. package/dist/bus/events.d.mts +29 -0
  56. package/dist/bus/events.d.mts.map +1 -0
  57. package/dist/bus/events.mjs +30 -0
  58. package/dist/bus/events.mjs.map +1 -0
  59. package/dist/bus/queue.d.mts +38 -0
  60. package/dist/bus/queue.d.mts.map +1 -0
  61. package/dist/bus/queue.mjs +90 -0
  62. package/dist/bus/queue.mjs.map +1 -0
  63. package/dist/channels/base.d.mts +31 -0
  64. package/dist/channels/base.d.mts.map +1 -0
  65. package/dist/channels/base.mjs +49 -0
  66. package/dist/channels/base.mjs.map +1 -0
  67. package/dist/channels/manager.d.mts +30 -0
  68. package/dist/channels/manager.d.mts.map +1 -0
  69. package/dist/channels/manager.mjs +100 -0
  70. package/dist/channels/manager.mjs.map +1 -0
  71. package/dist/channels/telegram.d.mts +23 -0
  72. package/dist/channels/telegram.d.mts.map +1 -0
  73. package/dist/channels/telegram.mjs +161 -0
  74. package/dist/channels/telegram.mjs.map +1 -0
  75. package/dist/cli/index.d.mts +1 -0
  76. package/dist/cli/index.mjs +337 -0
  77. package/dist/cli/index.mjs.map +1 -0
  78. package/dist/config/loader.d.mts +14 -0
  79. package/dist/config/loader.d.mts.map +1 -0
  80. package/dist/config/loader.mjs +40 -0
  81. package/dist/config/loader.mjs.map +1 -0
  82. package/dist/config/schema.d.mts +724 -0
  83. package/dist/config/schema.d.mts.map +1 -0
  84. package/dist/config/schema.mjs +123 -0
  85. package/dist/config/schema.mjs.map +1 -0
  86. package/dist/cron/service.d.mts +42 -0
  87. package/dist/cron/service.d.mts.map +1 -0
  88. package/dist/cron/service.mjs +256 -0
  89. package/dist/cron/service.mjs.map +1 -0
  90. package/dist/cron/types.d.mts +55 -0
  91. package/dist/cron/types.d.mts.map +1 -0
  92. package/dist/cron/types.mjs +30 -0
  93. package/dist/cron/types.mjs.map +1 -0
  94. package/dist/heartbeat/service.d.mts +29 -0
  95. package/dist/heartbeat/service.d.mts.map +1 -0
  96. package/dist/heartbeat/service.mjs +96 -0
  97. package/dist/heartbeat/service.mjs.map +1 -0
  98. package/dist/index.d.mts +23 -0
  99. package/dist/index.d.mts.map +1 -0
  100. package/dist/index.mjs +23 -0
  101. package/dist/index.mjs.map +1 -0
  102. package/dist/providers/base.d.mts +65 -0
  103. package/dist/providers/base.d.mts.map +1 -0
  104. package/dist/providers/base.mjs +1 -0
  105. package/dist/providers/openai-provider.d.mts +27 -0
  106. package/dist/providers/openai-provider.d.mts.map +1 -0
  107. package/dist/providers/openai-provider.mjs +67 -0
  108. package/dist/providers/openai-provider.mjs.map +1 -0
  109. package/dist/providers/transcription.d.mts +14 -0
  110. package/dist/providers/transcription.d.mts.map +1 -0
  111. package/dist/providers/transcription.mjs +46 -0
  112. package/dist/providers/transcription.mjs.map +1 -0
  113. package/dist/session/manager.d.mts +56 -0
  114. package/dist/session/manager.d.mts.map +1 -0
  115. package/dist/session/manager.mjs +144 -0
  116. package/dist/session/manager.mjs.map +1 -0
  117. package/dist/utils/helpers.d.mts +26 -0
  118. package/dist/utils/helpers.d.mts.map +1 -0
  119. package/dist/utils/helpers.mjs +60 -0
  120. package/dist/utils/helpers.mjs.map +1 -0
  121. package/dist/utils/which.d.mts +6 -0
  122. package/dist/utils/which.d.mts.map +1 -0
  123. package/dist/utils/which.mjs +20 -0
  124. package/dist/utils/which.mjs.map +1 -0
  125. package/package.json +45 -0
  126. package/skills/cron/SKILL.md +40 -0
  127. package/skills/github/SKILL.md +48 -0
  128. package/skills/skill-creator/SKILL.md +73 -0
  129. package/skills/summarize/SKILL.md +47 -0
  130. package/skills/weather/SKILL.md +43 -0
@@ -0,0 +1,167 @@
1
+ import { createInboundMessage, init_events } from "../bus/events.mjs";
2
+ import { ToolRegistry } from "./tools/registry.mjs";
3
+ import { ListDirTool, ReadFileTool, WriteFileTool } from "./tools/filesystem.mjs";
4
+ import { ExecTool } from "./tools/shell.mjs";
5
+ import { WebFetchTool, WebSearchTool } from "./tools/web.mjs";
6
+ import { randomUUID } from "node:crypto";
7
+
8
+ //#region src/agent/subagent.ts
9
+ init_events();
10
+ /**
11
+ * Manages background subagent execution.
12
+ */
13
+ var SubagentManager = class {
14
+ provider;
15
+ workspace;
16
+ bus;
17
+ model;
18
+ braveApiKey;
19
+ execConfig;
20
+ runningTasks = /* @__PURE__ */ new Map();
21
+ constructor(params) {
22
+ this.provider = params.provider;
23
+ this.workspace = params.workspace;
24
+ this.bus = params.bus;
25
+ this.model = params.model ?? params.provider.getDefaultModel();
26
+ this.braveApiKey = params.braveApiKey;
27
+ this.execConfig = params.execConfig ?? {
28
+ timeout: 60,
29
+ restrictToWorkspace: false
30
+ };
31
+ }
32
+ /** Spawn a subagent to execute a task in the background. */
33
+ async spawn(params) {
34
+ const taskId = randomUUID().slice(0, 8);
35
+ const displayLabel = params.label ?? (params.task.length > 30 ? params.task.slice(0, 30) + "..." : params.task);
36
+ const origin = {
37
+ channel: params.originChannel ?? "cli",
38
+ chatId: params.originChatId ?? "direct"
39
+ };
40
+ const controller = new AbortController();
41
+ this.runningTasks.set(taskId, controller);
42
+ this.runSubagent(taskId, params.task, displayLabel, origin).finally(() => this.runningTasks.delete(taskId));
43
+ console.log(`Spawned subagent [${taskId}]: ${displayLabel}`);
44
+ return `Subagent [${displayLabel}] started (id: ${taskId}). I'll notify you when it completes.`;
45
+ }
46
+ async runSubagent(taskId, task, label, origin) {
47
+ console.log(`Subagent [${taskId}] starting task: ${label}`);
48
+ try {
49
+ const tools = new ToolRegistry();
50
+ tools.register(new ReadFileTool());
51
+ tools.register(new WriteFileTool());
52
+ tools.register(new ListDirTool());
53
+ tools.register(new ExecTool({
54
+ workingDir: this.workspace,
55
+ timeout: this.execConfig.timeout,
56
+ restrictToWorkspace: this.execConfig.restrictToWorkspace
57
+ }));
58
+ tools.register(new WebSearchTool({ apiKey: this.braveApiKey }));
59
+ tools.register(new WebFetchTool());
60
+ const messages = [{
61
+ role: "system",
62
+ content: this.buildSubagentPrompt(task)
63
+ }, {
64
+ role: "user",
65
+ content: task
66
+ }];
67
+ const maxIterations = 15;
68
+ let finalResult = null;
69
+ for (let i = 0; i < maxIterations; i++) {
70
+ const response = await this.provider.chat({
71
+ messages,
72
+ tools: tools.getDefinitions(),
73
+ model: this.model
74
+ });
75
+ if (response.hasToolCalls) {
76
+ const toolCallDicts = response.toolCalls.map((tc) => ({
77
+ id: tc.id,
78
+ type: "function",
79
+ function: {
80
+ name: tc.name,
81
+ arguments: JSON.stringify(tc.arguments)
82
+ }
83
+ }));
84
+ messages.push({
85
+ role: "assistant",
86
+ content: response.content ?? "",
87
+ tool_calls: toolCallDicts
88
+ });
89
+ for (const tc of response.toolCalls) {
90
+ console.log(`Subagent [${taskId}] executing: ${tc.name}`);
91
+ const result = await tools.execute(tc.name, tc.arguments);
92
+ messages.push({
93
+ role: "tool",
94
+ tool_call_id: tc.id,
95
+ name: tc.name,
96
+ content: result
97
+ });
98
+ }
99
+ } else {
100
+ finalResult = response.content;
101
+ break;
102
+ }
103
+ }
104
+ finalResult ??= "Task completed but no final response was generated.";
105
+ console.log(`Subagent [${taskId}] completed successfully`);
106
+ await this.announceResult(taskId, label, task, finalResult, origin, "ok");
107
+ } catch (err) {
108
+ const errorMsg = `Error: ${err instanceof Error ? err.message : err}`;
109
+ console.error(`Subagent [${taskId}] failed:`, err);
110
+ await this.announceResult(taskId, label, task, errorMsg, origin, "error");
111
+ }
112
+ }
113
+ async announceResult(taskId, label, task, result, origin, status) {
114
+ const content = `[Subagent '${label}' ${status === "ok" ? "completed successfully" : "failed"}]
115
+
116
+ Task: ${task}
117
+
118
+ Result:
119
+ ${result}
120
+
121
+ Summarize this naturally for the user. Keep it brief (1-2 sentences). Do not mention technical details like "subagent" or task IDs.`;
122
+ const msg = createInboundMessage({
123
+ channel: "system",
124
+ senderId: "subagent",
125
+ chatId: `${origin.channel}:${origin.chatId}`,
126
+ content
127
+ });
128
+ await this.bus.publishInbound(msg);
129
+ }
130
+ buildSubagentPrompt(task) {
131
+ return `# Subagent
132
+
133
+ You are a subagent spawned by the main agent to complete a specific task.
134
+
135
+ ## Your Task
136
+ ${task}
137
+
138
+ ## Rules
139
+ 1. Stay focused - complete only the assigned task, nothing else
140
+ 2. Your final response will be reported back to the main agent
141
+ 3. Do not initiate conversations or take on side tasks
142
+ 4. Be concise but informative in your findings
143
+
144
+ ## What You Can Do
145
+ - Read and write files in the workspace
146
+ - Execute shell commands
147
+ - Search the web and fetch web pages
148
+ - Complete the task thoroughly
149
+
150
+ ## What You Cannot Do
151
+ - Send messages directly to users (no message tool available)
152
+ - Spawn other subagents
153
+ - Access the main agent's conversation history
154
+
155
+ ## Workspace
156
+ Your workspace is at: ${this.workspace}
157
+
158
+ When you have completed the task, provide a clear summary of your findings or actions.`;
159
+ }
160
+ get runningCount() {
161
+ return this.runningTasks.size;
162
+ }
163
+ };
164
+
165
+ //#endregion
166
+ export { SubagentManager };
167
+ //# sourceMappingURL=subagent.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent.mjs","names":[],"sources":["../../src/agent/subagent.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { LLMProvider, ChatMessage } from \"../providers/base.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\nimport { createInboundMessage } from \"../bus/events.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport { ReadFileTool, WriteFileTool, ListDirTool } from \"./tools/filesystem.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\n\n/**\n * Manages background subagent execution.\n */\nexport class SubagentManager {\n private provider: LLMProvider;\n private workspace: string;\n private bus: MessageBus;\n private model: string;\n private braveApiKey?: string;\n private execConfig: ExecToolConfig;\n private runningTasks = new Map<string, AbortController>();\n\n constructor(params: {\n provider: LLMProvider;\n workspace: string;\n bus: MessageBus;\n model?: string;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n }) {\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.bus = params.bus;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.braveApiKey = params.braveApiKey;\n this.execConfig = params.execConfig ?? { timeout: 60, restrictToWorkspace: false };\n }\n\n /** Spawn a subagent to execute a task in the background. */\n async spawn(params: {\n task: string;\n label?: string;\n originChannel?: string;\n originChatId?: string;\n }): Promise<string> {\n const taskId = randomUUID().slice(0, 8);\n const displayLabel =\n params.label ??\n (params.task.length > 30\n ? params.task.slice(0, 30) + \"...\"\n : params.task);\n\n const origin = {\n channel: params.originChannel ?? \"cli\",\n chatId: params.originChatId ?? \"direct\",\n };\n\n const controller = new AbortController();\n this.runningTasks.set(taskId, controller);\n\n // Run in background (don't await)\n this.runSubagent(taskId, params.task, displayLabel, origin)\n .finally(() => this.runningTasks.delete(taskId));\n\n console.log(`Spawned subagent [${taskId}]: ${displayLabel}`);\n return `Subagent [${displayLabel}] started (id: ${taskId}). I'll notify you when it completes.`;\n }\n\n private async runSubagent(\n taskId: string,\n task: string,\n label: string,\n origin: { channel: string; chatId: string },\n ): Promise<void> {\n console.log(`Subagent [${taskId}] starting task: ${label}`);\n\n try {\n // Build subagent tools (no message, no spawn)\n const tools = new ToolRegistry();\n tools.register(new ReadFileTool());\n tools.register(new WriteFileTool());\n tools.register(new ListDirTool());\n tools.register(\n new ExecTool({\n workingDir: this.workspace,\n timeout: this.execConfig.timeout,\n restrictToWorkspace: this.execConfig.restrictToWorkspace,\n }),\n );\n tools.register(new WebSearchTool({ apiKey: this.braveApiKey }));\n tools.register(new WebFetchTool());\n\n const systemPrompt = this.buildSubagentPrompt(task);\n const messages: ChatMessage[] = [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: task },\n ];\n\n const maxIterations = 15;\n let finalResult: string | null = null;\n\n for (let i = 0; i < maxIterations; i++) {\n const response = await this.provider.chat({\n messages,\n tools: tools.getDefinitions(),\n model: this.model,\n });\n\n if (response.hasToolCalls) {\n const toolCallDicts = response.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: response.content ?? \"\",\n tool_calls: toolCallDicts,\n });\n\n for (const tc of response.toolCalls) {\n console.log(\n `Subagent [${taskId}] executing: ${tc.name}`,\n );\n const result = await tools.execute(tc.name, tc.arguments);\n messages.push({\n role: \"tool\",\n tool_call_id: tc.id,\n name: tc.name,\n content: result,\n });\n }\n } else {\n finalResult = response.content;\n break;\n }\n }\n\n finalResult ??= \"Task completed but no final response was generated.\";\n console.log(`Subagent [${taskId}] completed successfully`);\n await this.announceResult(taskId, label, task, finalResult, origin, \"ok\");\n } catch (err) {\n const errorMsg = `Error: ${err instanceof Error ? err.message : err}`;\n console.error(`Subagent [${taskId}] failed:`, err);\n await this.announceResult(\n taskId,\n label,\n task,\n errorMsg,\n origin,\n \"error\",\n );\n }\n }\n\n private async announceResult(\n taskId: string,\n label: string,\n task: string,\n result: string,\n origin: { channel: string; chatId: string },\n status: string,\n ): Promise<void> {\n const statusText =\n status === \"ok\" ? \"completed successfully\" : \"failed\";\n\n const content = `[Subagent '${label}' ${statusText}]\n\nTask: ${task}\n\nResult:\n${result}\n\nSummarize this naturally for the user. Keep it brief (1-2 sentences). Do not mention technical details like \"subagent\" or task IDs.`;\n\n const msg = createInboundMessage({\n channel: \"system\",\n senderId: \"subagent\",\n chatId: `${origin.channel}:${origin.chatId}`,\n content,\n });\n\n await this.bus.publishInbound(msg);\n }\n\n private buildSubagentPrompt(task: string): string {\n return `# Subagent\n\nYou are a subagent spawned by the main agent to complete a specific task.\n\n## Your Task\n${task}\n\n## Rules\n1. Stay focused - complete only the assigned task, nothing else\n2. Your final response will be reported back to the main agent\n3. Do not initiate conversations or take on side tasks\n4. Be concise but informative in your findings\n\n## What You Can Do\n- Read and write files in the workspace\n- Execute shell commands\n- Search the web and fetch web pages\n- Complete the task thoroughly\n\n## What You Cannot Do\n- Send messages directly to users (no message tool available)\n- Spawn other subagents\n- Access the main agent's conversation history\n\n## Workspace\nYour workspace is at: ${this.workspace}\n\nWhen you have completed the task, provide a clear summary of your findings or actions.`;\n }\n\n get runningCount(): number {\n return this.runningTasks.size;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { ToolDefinition } from "../../providers/base.mjs";
2
+
3
+ //#region src/agent/tools/base.d.ts
4
+ /** Abstract base class for tools. */
5
+ declare abstract class Tool {
6
+ abstract readonly name: string;
7
+ abstract readonly description: string;
8
+ abstract readonly parameters: Record<string, unknown>;
9
+ /** Execute the tool with the given arguments. */
10
+ abstract execute(args: Record<string, unknown>): Promise<string>;
11
+ /** Get the tool definition for the LLM. */
12
+ getDefinition(): ToolDefinition;
13
+ }
14
+ //#endregion
15
+ export { Tool };
16
+ //# sourceMappingURL=base.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.mts","names":[],"sources":["../../../src/agent/tools/base.ts"],"mappings":";;;;uBAGsB,IAAA;EAAA,kBACF,IAAA;EAAA,kBACA,WAAA;EAAA,kBACA,UAAA,EAAY,MAAA;EAAA;EAAA,SAGrB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAAA;EAGjD,aAAA,CAAA,GAAiB,cAAA;AAAA"}
@@ -0,0 +1,19 @@
1
+ //#region src/agent/tools/base.ts
2
+ /** Abstract base class for tools. */
3
+ var Tool = class {
4
+ /** Get the tool definition for the LLM. */
5
+ getDefinition() {
6
+ return {
7
+ type: "function",
8
+ function: {
9
+ name: this.name,
10
+ description: this.description,
11
+ parameters: this.parameters
12
+ }
13
+ };
14
+ }
15
+ };
16
+
17
+ //#endregion
18
+ export { Tool };
19
+ //# sourceMappingURL=base.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.mjs","names":[],"sources":["../../../src/agent/tools/base.ts"],"sourcesContent":["import type { ToolDefinition } from \"../../providers/base.js\";\n\n/** Abstract base class for tools. */\nexport abstract class Tool {\n abstract readonly name: string;\n abstract readonly description: string;\n abstract readonly parameters: Record<string, unknown>;\n\n /** Execute the tool with the given arguments. */\n abstract execute(args: Record<string, unknown>): Promise<string>;\n\n /** Get the tool definition for the LLM. */\n getDefinition(): ToolDefinition {\n return {\n type: \"function\",\n function: {\n name: this.name,\n description: this.description,\n parameters: this.parameters,\n },\n };\n }\n}\n"],"mappings":";;AAGA,IAAsB,OAAtB,MAA2B;;CASzB,gBAAgC;AAC9B,SAAO;GACL,MAAM;GACN,UAAU;IACR,MAAM,KAAK;IACX,aAAa,KAAK;IAClB,YAAY,KAAK;IAClB;GACF"}
@@ -0,0 +1,49 @@
1
+ import { Tool } from "./base.mjs";
2
+ import { CronService } from "../../cron/service.mjs";
3
+
4
+ //#region src/agent/tools/cron.d.ts
5
+ /** Tool to schedule reminders and recurring tasks. */
6
+ declare class CronTool extends Tool {
7
+ readonly name = "cron";
8
+ readonly description = "Schedule reminders and recurring tasks. Actions: add, list, remove.";
9
+ readonly parameters: {
10
+ type: string;
11
+ properties: {
12
+ action: {
13
+ type: string;
14
+ enum: string[];
15
+ description: string;
16
+ };
17
+ message: {
18
+ type: string;
19
+ description: string;
20
+ };
21
+ every_seconds: {
22
+ type: string;
23
+ description: string;
24
+ };
25
+ cron_expr: {
26
+ type: string;
27
+ description: string;
28
+ };
29
+ job_id: {
30
+ type: string;
31
+ description: string;
32
+ };
33
+ };
34
+ required: string[];
35
+ };
36
+ private cronService;
37
+ private channel;
38
+ private chatId;
39
+ constructor(cronService: CronService);
40
+ /** Set the current session context for delivery. */
41
+ setContext(channel: string, chatId: string): void;
42
+ execute(args: Record<string, unknown>): Promise<string>;
43
+ private addJob;
44
+ private listJobs;
45
+ private removeJob;
46
+ }
47
+ //#endregion
48
+ export { CronTool };
49
+ //# sourceMappingURL=cron.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron.d.mts","names":[],"sources":["../../../src/agent/tools/cron.ts"],"mappings":";;;;;cAKa,QAAA,SAAiB,IAAA;EAAA,SACnB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BD,WAAA;EAAA,QACA,OAAA;EAAA,QACA,MAAA;cAEI,WAAA,EAAa,WAAA;EAHjB;EASR,UAAA,CAAW,OAAA,UAAiB,MAAA;EAKtB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAAA,QActC,MAAA;EAAA,QA+BA,QAAA;EAAA,QASA,SAAA;AAAA"}
@@ -0,0 +1,101 @@
1
+ import { Tool } from "./base.mjs";
2
+
3
+ //#region src/agent/tools/cron.ts
4
+ /** Tool to schedule reminders and recurring tasks. */
5
+ var CronTool = class extends Tool {
6
+ name = "cron";
7
+ description = "Schedule reminders and recurring tasks. Actions: add, list, remove.";
8
+ parameters = {
9
+ type: "object",
10
+ properties: {
11
+ action: {
12
+ type: "string",
13
+ enum: [
14
+ "add",
15
+ "list",
16
+ "remove"
17
+ ],
18
+ description: "Action to perform"
19
+ },
20
+ message: {
21
+ type: "string",
22
+ description: "Reminder message (for add)"
23
+ },
24
+ every_seconds: {
25
+ type: "integer",
26
+ description: "Interval in seconds (for recurring tasks)"
27
+ },
28
+ cron_expr: {
29
+ type: "string",
30
+ description: "Cron expression like '0 9 * * *' (for scheduled tasks)"
31
+ },
32
+ job_id: {
33
+ type: "string",
34
+ description: "Job ID (for remove)"
35
+ }
36
+ },
37
+ required: ["action"]
38
+ };
39
+ cronService;
40
+ channel = "";
41
+ chatId = "";
42
+ constructor(cronService) {
43
+ super();
44
+ this.cronService = cronService;
45
+ }
46
+ /** Set the current session context for delivery. */
47
+ setContext(channel, chatId) {
48
+ this.channel = channel;
49
+ this.chatId = chatId;
50
+ }
51
+ async execute(args) {
52
+ const action = String(args.action);
53
+ switch (action) {
54
+ case "add": return this.addJob(args);
55
+ case "list": return this.listJobs();
56
+ case "remove": return this.removeJob(args);
57
+ default: return `Unknown action: ${action}`;
58
+ }
59
+ }
60
+ addJob(args) {
61
+ const message = args.message ? String(args.message) : "";
62
+ const everySeconds = args.every_seconds ? Number(args.every_seconds) : null;
63
+ const cronExpr = args.cron_expr ? String(args.cron_expr) : null;
64
+ if (!message) return "Error: message is required for add";
65
+ if (!this.channel || !this.chatId) return "Error: no session context (channel/chatId)";
66
+ let schedule;
67
+ if (everySeconds) schedule = {
68
+ kind: "every",
69
+ everyMs: everySeconds * 1e3
70
+ };
71
+ else if (cronExpr) schedule = {
72
+ kind: "cron",
73
+ expr: cronExpr
74
+ };
75
+ else return "Error: either every_seconds or cron_expr is required";
76
+ const job = this.cronService.addJob({
77
+ name: message.slice(0, 30),
78
+ schedule,
79
+ message,
80
+ deliver: true,
81
+ channel: this.channel,
82
+ to: this.chatId
83
+ });
84
+ return `Created job '${job.name}' (id: ${job.id})`;
85
+ }
86
+ listJobs() {
87
+ const jobs = this.cronService.listJobs();
88
+ if (jobs.length === 0) return "No scheduled jobs.";
89
+ return "Scheduled jobs:\n" + jobs.map((j) => `- ${j.name} (id: ${j.id}, ${j.schedule.kind})`).join("\n");
90
+ }
91
+ removeJob(args) {
92
+ const jobId = args.job_id ? String(args.job_id) : null;
93
+ if (!jobId) return "Error: job_id is required for remove";
94
+ if (this.cronService.removeJob(jobId)) return `Removed job ${jobId}`;
95
+ return `Job ${jobId} not found`;
96
+ }
97
+ };
98
+
99
+ //#endregion
100
+ export { CronTool };
101
+ //# sourceMappingURL=cron.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron.mjs","names":[],"sources":["../../../src/agent/tools/cron.ts"],"sourcesContent":["import { Tool } from \"./base.js\";\nimport type { CronService } from \"../../cron/service.js\";\nimport type { CronSchedule } from \"../../cron/types.js\";\n\n/** Tool to schedule reminders and recurring tasks. */\nexport class CronTool extends Tool {\n readonly name = \"cron\";\n readonly description =\n \"Schedule reminders and recurring tasks. Actions: add, list, remove.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n action: {\n type: \"string\",\n enum: [\"add\", \"list\", \"remove\"],\n description: \"Action to perform\",\n },\n message: {\n type: \"string\",\n description: \"Reminder message (for add)\",\n },\n every_seconds: {\n type: \"integer\",\n description: \"Interval in seconds (for recurring tasks)\",\n },\n cron_expr: {\n type: \"string\",\n description: \"Cron expression like '0 9 * * *' (for scheduled tasks)\",\n },\n job_id: {\n type: \"string\",\n description: \"Job ID (for remove)\",\n },\n },\n required: [\"action\"],\n };\n\n private cronService: CronService;\n private channel = \"\";\n private chatId = \"\";\n\n constructor(cronService: CronService) {\n super();\n this.cronService = cronService;\n }\n\n /** Set the current session context for delivery. */\n setContext(channel: string, chatId: string): void {\n this.channel = channel;\n this.chatId = chatId;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const action = String(args.action);\n switch (action) {\n case \"add\":\n return this.addJob(args);\n case \"list\":\n return this.listJobs();\n case \"remove\":\n return this.removeJob(args);\n default:\n return `Unknown action: ${action}`;\n }\n }\n\n private addJob(args: Record<string, unknown>): string {\n const message = args.message ? String(args.message) : \"\";\n const everySeconds = args.every_seconds\n ? Number(args.every_seconds)\n : null;\n const cronExpr = args.cron_expr ? String(args.cron_expr) : null;\n\n if (!message) return \"Error: message is required for add\";\n if (!this.channel || !this.chatId)\n return \"Error: no session context (channel/chatId)\";\n\n let schedule: CronSchedule;\n if (everySeconds) {\n schedule = { kind: \"every\", everyMs: everySeconds * 1000 };\n } else if (cronExpr) {\n schedule = { kind: \"cron\", expr: cronExpr };\n } else {\n return \"Error: either every_seconds or cron_expr is required\";\n }\n\n const job = this.cronService.addJob({\n name: message.slice(0, 30),\n schedule,\n message,\n deliver: true,\n channel: this.channel,\n to: this.chatId,\n });\n return `Created job '${job.name}' (id: ${job.id})`;\n }\n\n private listJobs(): string {\n const jobs = this.cronService.listJobs();\n if (jobs.length === 0) return \"No scheduled jobs.\";\n const lines = jobs.map(\n (j) => `- ${j.name} (id: ${j.id}, ${j.schedule.kind})`,\n );\n return \"Scheduled jobs:\\n\" + lines.join(\"\\n\");\n }\n\n private removeJob(args: Record<string, unknown>): string {\n const jobId = args.job_id ? String(args.job_id) : null;\n if (!jobId) return \"Error: job_id is required for remove\";\n if (this.cronService.removeJob(jobId)) {\n return `Removed job ${jobId}`;\n }\n return `Job ${jobId} not found`;\n }\n}\n"],"mappings":";;;;AAKA,IAAa,WAAb,cAA8B,KAAK;CACjC,AAAS,OAAO;CAChB,AAAS,cACP;CACF,AAAS,aAAa;EACpB,MAAM;EACN,YAAY;GACV,QAAQ;IACN,MAAM;IACN,MAAM;KAAC;KAAO;KAAQ;KAAS;IAC/B,aAAa;IACd;GACD,SAAS;IACP,MAAM;IACN,aAAa;IACd;GACD,eAAe;IACb,MAAM;IACN,aAAa;IACd;GACD,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,QAAQ;IACN,MAAM;IACN,aAAa;IACd;GACF;EACD,UAAU,CAAC,SAAS;EACrB;CAED,AAAQ;CACR,AAAQ,UAAU;CAClB,AAAQ,SAAS;CAEjB,YAAY,aAA0B;AACpC,SAAO;AACP,OAAK,cAAc;;;CAIrB,WAAW,SAAiB,QAAsB;AAChD,OAAK,UAAU;AACf,OAAK,SAAS;;CAGhB,MAAM,QAAQ,MAAgD;EAC5D,MAAM,SAAS,OAAO,KAAK,OAAO;AAClC,UAAQ,QAAR;GACE,KAAK,MACH,QAAO,KAAK,OAAO,KAAK;GAC1B,KAAK,OACH,QAAO,KAAK,UAAU;GACxB,KAAK,SACH,QAAO,KAAK,UAAU,KAAK;GAC7B,QACE,QAAO,mBAAmB;;;CAIhC,AAAQ,OAAO,MAAuC;EACpD,MAAM,UAAU,KAAK,UAAU,OAAO,KAAK,QAAQ,GAAG;EACtD,MAAM,eAAe,KAAK,gBACtB,OAAO,KAAK,cAAc,GAC1B;EACJ,MAAM,WAAW,KAAK,YAAY,OAAO,KAAK,UAAU,GAAG;AAE3D,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,CAAC,KAAK,WAAW,CAAC,KAAK,OACzB,QAAO;EAET,IAAI;AACJ,MAAI,aACF,YAAW;GAAE,MAAM;GAAS,SAAS,eAAe;GAAM;WACjD,SACT,YAAW;GAAE,MAAM;GAAQ,MAAM;GAAU;MAE3C,QAAO;EAGT,MAAM,MAAM,KAAK,YAAY,OAAO;GAClC,MAAM,QAAQ,MAAM,GAAG,GAAG;GAC1B;GACA;GACA,SAAS;GACT,SAAS,KAAK;GACd,IAAI,KAAK;GACV,CAAC;AACF,SAAO,gBAAgB,IAAI,KAAK,SAAS,IAAI,GAAG;;CAGlD,AAAQ,WAAmB;EACzB,MAAM,OAAO,KAAK,YAAY,UAAU;AACxC,MAAI,KAAK,WAAW,EAAG,QAAO;AAI9B,SAAO,sBAHO,KAAK,KAChB,MAAM,KAAK,EAAE,KAAK,QAAQ,EAAE,GAAG,IAAI,EAAE,SAAS,KAAK,GACrD,CACkC,KAAK,KAAK;;CAG/C,AAAQ,UAAU,MAAuC;EACvD,MAAM,QAAQ,KAAK,SAAS,OAAO,KAAK,OAAO,GAAG;AAClD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,KAAK,YAAY,UAAU,MAAM,CACnC,QAAO,eAAe;AAExB,SAAO,OAAO,MAAM"}
@@ -0,0 +1,87 @@
1
+ import { Tool } from "./base.mjs";
2
+
3
+ //#region src/agent/tools/filesystem.d.ts
4
+ /** Read a file's contents. */
5
+ declare class ReadFileTool extends Tool {
6
+ readonly name = "read_file";
7
+ readonly description = "Read the contents of a file.";
8
+ readonly parameters: {
9
+ type: string;
10
+ properties: {
11
+ path: {
12
+ type: string;
13
+ description: string;
14
+ };
15
+ maxChars: {
16
+ type: string;
17
+ description: string;
18
+ minimum: number;
19
+ };
20
+ };
21
+ required: string[];
22
+ };
23
+ execute(args: Record<string, unknown>): Promise<string>;
24
+ }
25
+ /** Write content to a file. */
26
+ declare class WriteFileTool extends Tool {
27
+ readonly name = "write_file";
28
+ readonly description = "Write content to a file. Creates parent directories if needed.";
29
+ readonly parameters: {
30
+ type: string;
31
+ properties: {
32
+ path: {
33
+ type: string;
34
+ description: string;
35
+ };
36
+ content: {
37
+ type: string;
38
+ description: string;
39
+ };
40
+ };
41
+ required: string[];
42
+ };
43
+ execute(args: Record<string, unknown>): Promise<string>;
44
+ }
45
+ /** Edit a file by replacing text. */
46
+ declare class EditFileTool extends Tool {
47
+ readonly name = "edit_file";
48
+ readonly description = "Edit a file by replacing occurrences of old_text with new_text.";
49
+ readonly parameters: {
50
+ type: string;
51
+ properties: {
52
+ path: {
53
+ type: string;
54
+ description: string;
55
+ };
56
+ old_text: {
57
+ type: string;
58
+ description: string;
59
+ };
60
+ new_text: {
61
+ type: string;
62
+ description: string;
63
+ };
64
+ };
65
+ required: string[];
66
+ };
67
+ execute(args: Record<string, unknown>): Promise<string>;
68
+ }
69
+ /** List directory contents. */
70
+ declare class ListDirTool extends Tool {
71
+ readonly name = "list_dir";
72
+ readonly description = "List files and directories at the given path.";
73
+ readonly parameters: {
74
+ type: string;
75
+ properties: {
76
+ path: {
77
+ type: string;
78
+ description: string;
79
+ };
80
+ };
81
+ required: string[];
82
+ };
83
+ execute(args: Record<string, unknown>): Promise<string>;
84
+ }
85
+ //#endregion
86
+ export { EditFileTool, ListDirTool, ReadFileTool, WriteFileTool };
87
+ //# sourceMappingURL=filesystem.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.mts","names":[],"sources":["../../../src/agent/tools/filesystem.ts"],"mappings":";;;;cAYa,YAAA,SAAqB,IAAA;EAAA,SACvB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;;;;;;;;;;;;;;;EAaH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA;;cAoBnC,aAAA,SAAsB,IAAA;EAAA,SACxB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;;;;;;;;;;;;;;EASH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA;;cAkBnC,YAAA,SAAqB,IAAA;EAAA,SACvB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;EAUH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA;;cAuBnC,WAAA,SAAoB,IAAA;EAAA,SACtB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;;;;;;;;;;EAQH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA"}
@@ -0,0 +1,145 @@
1
+ import { Tool } from "./base.mjs";
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+
5
+ //#region src/agent/tools/filesystem.ts
6
+ /** Read a file's contents. */
7
+ var ReadFileTool = class extends Tool {
8
+ name = "read_file";
9
+ description = "Read the contents of a file.";
10
+ parameters = {
11
+ type: "object",
12
+ properties: {
13
+ path: {
14
+ type: "string",
15
+ description: "File path to read"
16
+ },
17
+ maxChars: {
18
+ type: "integer",
19
+ description: "Max characters to return",
20
+ minimum: 1
21
+ }
22
+ },
23
+ required: ["path"]
24
+ };
25
+ async execute(args) {
26
+ const filePath = String(args.path);
27
+ const maxChars = args.maxChars ? Number(args.maxChars) : void 0;
28
+ try {
29
+ if (!existsSync(filePath)) return `Error: File not found: ${filePath}`;
30
+ let content = readFileSync(filePath, "utf-8");
31
+ if (maxChars && content.length > maxChars) content = content.slice(0, maxChars) + "\n... (truncated)";
32
+ return content;
33
+ } catch (err) {
34
+ return `Error reading file: ${err instanceof Error ? err.message : err}`;
35
+ }
36
+ }
37
+ };
38
+ /** Write content to a file. */
39
+ var WriteFileTool = class extends Tool {
40
+ name = "write_file";
41
+ description = "Write content to a file. Creates parent directories if needed.";
42
+ parameters = {
43
+ type: "object",
44
+ properties: {
45
+ path: {
46
+ type: "string",
47
+ description: "File path to write"
48
+ },
49
+ content: {
50
+ type: "string",
51
+ description: "Content to write"
52
+ }
53
+ },
54
+ required: ["path", "content"]
55
+ };
56
+ async execute(args) {
57
+ const filePath = String(args.path);
58
+ const content = String(args.content);
59
+ try {
60
+ const dir = dirname(filePath);
61
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
62
+ writeFileSync(filePath, content, "utf-8");
63
+ return `Written ${content.length} chars to ${filePath}`;
64
+ } catch (err) {
65
+ return `Error writing file: ${err instanceof Error ? err.message : err}`;
66
+ }
67
+ }
68
+ };
69
+ /** Edit a file by replacing text. */
70
+ var EditFileTool = class extends Tool {
71
+ name = "edit_file";
72
+ description = "Edit a file by replacing occurrences of old_text with new_text.";
73
+ parameters = {
74
+ type: "object",
75
+ properties: {
76
+ path: {
77
+ type: "string",
78
+ description: "File path to edit"
79
+ },
80
+ old_text: {
81
+ type: "string",
82
+ description: "Text to find and replace"
83
+ },
84
+ new_text: {
85
+ type: "string",
86
+ description: "Replacement text"
87
+ }
88
+ },
89
+ required: [
90
+ "path",
91
+ "old_text",
92
+ "new_text"
93
+ ]
94
+ };
95
+ async execute(args) {
96
+ const filePath = String(args.path);
97
+ const oldText = String(args.old_text);
98
+ const newText = String(args.new_text);
99
+ try {
100
+ if (!existsSync(filePath)) return `Error: File not found: ${filePath}`;
101
+ const content = readFileSync(filePath, "utf-8");
102
+ if (!content.includes(oldText)) return `Error: old_text not found in ${filePath}`;
103
+ writeFileSync(filePath, content.replace(oldText, newText), "utf-8");
104
+ return `Edited ${filePath}: replaced ${oldText.length} chars`;
105
+ } catch (err) {
106
+ return `Error editing file: ${err instanceof Error ? err.message : err}`;
107
+ }
108
+ }
109
+ };
110
+ /** List directory contents. */
111
+ var ListDirTool = class extends Tool {
112
+ name = "list_dir";
113
+ description = "List files and directories at the given path.";
114
+ parameters = {
115
+ type: "object",
116
+ properties: { path: {
117
+ type: "string",
118
+ description: "Directory path to list"
119
+ } },
120
+ required: ["path"]
121
+ };
122
+ async execute(args) {
123
+ const dirPath = String(args.path);
124
+ try {
125
+ if (!existsSync(dirPath)) return `Error: Directory not found: ${dirPath}`;
126
+ const entries = readdirSync(dirPath);
127
+ const lines = [];
128
+ for (const entry of entries) try {
129
+ const stat = statSync(join(dirPath, entry));
130
+ const type = stat.isDirectory() ? "dir" : "file";
131
+ const size = stat.isDirectory() ? "" : ` (${stat.size}b)`;
132
+ lines.push(`${type}\t${entry}${size}`);
133
+ } catch {
134
+ lines.push(`?\t${entry}`);
135
+ }
136
+ return lines.length > 0 ? lines.join("\n") : "(empty directory)";
137
+ } catch (err) {
138
+ return `Error listing directory: ${err instanceof Error ? err.message : err}`;
139
+ }
140
+ }
141
+ };
142
+
143
+ //#endregion
144
+ export { EditFileTool, ListDirTool, ReadFileTool, WriteFileTool };
145
+ //# sourceMappingURL=filesystem.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.mjs","names":[],"sources":["../../../src/agent/tools/filesystem.ts"],"sourcesContent":["import {\n readFileSync,\n writeFileSync,\n readdirSync,\n existsSync,\n statSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join, dirname, resolve } from \"node:path\";\nimport { Tool } from \"./base.js\";\n\n/** Read a file's contents. */\nexport class ReadFileTool extends Tool {\n readonly name = \"read_file\";\n readonly description = \"Read the contents of a file.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"File path to read\" },\n maxChars: {\n type: \"integer\",\n description: \"Max characters to return\",\n minimum: 1,\n },\n },\n required: [\"path\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const filePath = String(args.path);\n const maxChars = args.maxChars ? Number(args.maxChars) : undefined;\n\n try {\n if (!existsSync(filePath)) {\n return `Error: File not found: ${filePath}`;\n }\n let content = readFileSync(filePath, \"utf-8\");\n if (maxChars && content.length > maxChars) {\n content = content.slice(0, maxChars) + \"\\n... (truncated)\";\n }\n return content;\n } catch (err) {\n return `Error reading file: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n\n/** Write content to a file. */\nexport class WriteFileTool extends Tool {\n readonly name = \"write_file\";\n readonly description = \"Write content to a file. Creates parent directories if needed.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"File path to write\" },\n content: { type: \"string\", description: \"Content to write\" },\n },\n required: [\"path\", \"content\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const filePath = String(args.path);\n const content = String(args.content);\n\n try {\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(filePath, content, \"utf-8\");\n return `Written ${content.length} chars to ${filePath}`;\n } catch (err) {\n return `Error writing file: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n\n/** Edit a file by replacing text. */\nexport class EditFileTool extends Tool {\n readonly name = \"edit_file\";\n readonly description =\n \"Edit a file by replacing occurrences of old_text with new_text.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"File path to edit\" },\n old_text: { type: \"string\", description: \"Text to find and replace\" },\n new_text: { type: \"string\", description: \"Replacement text\" },\n },\n required: [\"path\", \"old_text\", \"new_text\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const filePath = String(args.path);\n const oldText = String(args.old_text);\n const newText = String(args.new_text);\n\n try {\n if (!existsSync(filePath)) {\n return `Error: File not found: ${filePath}`;\n }\n const content = readFileSync(filePath, \"utf-8\");\n if (!content.includes(oldText)) {\n return `Error: old_text not found in ${filePath}`;\n }\n const newContent = content.replace(oldText, newText);\n writeFileSync(filePath, newContent, \"utf-8\");\n return `Edited ${filePath}: replaced ${oldText.length} chars`;\n } catch (err) {\n return `Error editing file: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n\n/** List directory contents. */\nexport class ListDirTool extends Tool {\n readonly name = \"list_dir\";\n readonly description = \"List files and directories at the given path.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Directory path to list\" },\n },\n required: [\"path\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const dirPath = String(args.path);\n\n try {\n if (!existsSync(dirPath)) {\n return `Error: Directory not found: ${dirPath}`;\n }\n const entries = readdirSync(dirPath);\n const lines: string[] = [];\n for (const entry of entries) {\n try {\n const fullPath = join(dirPath, entry);\n const stat = statSync(fullPath);\n const type = stat.isDirectory() ? \"dir\" : \"file\";\n const size = stat.isDirectory() ? \"\" : ` (${stat.size}b)`;\n lines.push(`${type}\\t${entry}${size}`);\n } catch {\n lines.push(`?\\t${entry}`);\n }\n }\n return lines.length > 0 ? lines.join(\"\\n\") : \"(empty directory)\";\n } catch (err) {\n return `Error listing directory: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n"],"mappings":";;;;;;AAYA,IAAa,eAAb,cAAkC,KAAK;CACrC,AAAS,OAAO;CAChB,AAAS,cAAc;CACvB,AAAS,aAAa;EACpB,MAAM;EACN,YAAY;GACV,MAAM;IAAE,MAAM;IAAU,aAAa;IAAqB;GAC1D,UAAU;IACR,MAAM;IACN,aAAa;IACb,SAAS;IACV;GACF;EACD,UAAU,CAAC,OAAO;EACnB;CAED,MAAM,QAAQ,MAAgD;EAC5D,MAAM,WAAW,OAAO,KAAK,KAAK;EAClC,MAAM,WAAW,KAAK,WAAW,OAAO,KAAK,SAAS,GAAG;AAEzD,MAAI;AACF,OAAI,CAAC,WAAW,SAAS,CACvB,QAAO,0BAA0B;GAEnC,IAAI,UAAU,aAAa,UAAU,QAAQ;AAC7C,OAAI,YAAY,QAAQ,SAAS,SAC/B,WAAU,QAAQ,MAAM,GAAG,SAAS,GAAG;AAEzC,UAAO;WACA,KAAK;AACZ,UAAO,uBAAuB,eAAe,QAAQ,IAAI,UAAU;;;;;AAMzE,IAAa,gBAAb,cAAmC,KAAK;CACtC,AAAS,OAAO;CAChB,AAAS,cAAc;CACvB,AAAS,aAAa;EACpB,MAAM;EACN,YAAY;GACV,MAAM;IAAE,MAAM;IAAU,aAAa;IAAsB;GAC3D,SAAS;IAAE,MAAM;IAAU,aAAa;IAAoB;GAC7D;EACD,UAAU,CAAC,QAAQ,UAAU;EAC9B;CAED,MAAM,QAAQ,MAAgD;EAC5D,MAAM,WAAW,OAAO,KAAK,KAAK;EAClC,MAAM,UAAU,OAAO,KAAK,QAAQ;AAEpC,MAAI;GACF,MAAM,MAAM,QAAQ,SAAS;AAC7B,OAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AAErC,iBAAc,UAAU,SAAS,QAAQ;AACzC,UAAO,WAAW,QAAQ,OAAO,YAAY;WACtC,KAAK;AACZ,UAAO,uBAAuB,eAAe,QAAQ,IAAI,UAAU;;;;;AAMzE,IAAa,eAAb,cAAkC,KAAK;CACrC,AAAS,OAAO;CAChB,AAAS,cACP;CACF,AAAS,aAAa;EACpB,MAAM;EACN,YAAY;GACV,MAAM;IAAE,MAAM;IAAU,aAAa;IAAqB;GAC1D,UAAU;IAAE,MAAM;IAAU,aAAa;IAA4B;GACrE,UAAU;IAAE,MAAM;IAAU,aAAa;IAAoB;GAC9D;EACD,UAAU;GAAC;GAAQ;GAAY;GAAW;EAC3C;CAED,MAAM,QAAQ,MAAgD;EAC5D,MAAM,WAAW,OAAO,KAAK,KAAK;EAClC,MAAM,UAAU,OAAO,KAAK,SAAS;EACrC,MAAM,UAAU,OAAO,KAAK,SAAS;AAErC,MAAI;AACF,OAAI,CAAC,WAAW,SAAS,CACvB,QAAO,0BAA0B;GAEnC,MAAM,UAAU,aAAa,UAAU,QAAQ;AAC/C,OAAI,CAAC,QAAQ,SAAS,QAAQ,CAC5B,QAAO,gCAAgC;AAGzC,iBAAc,UADK,QAAQ,QAAQ,SAAS,QAAQ,EAChB,QAAQ;AAC5C,UAAO,UAAU,SAAS,aAAa,QAAQ,OAAO;WAC/C,KAAK;AACZ,UAAO,uBAAuB,eAAe,QAAQ,IAAI,UAAU;;;;;AAMzE,IAAa,cAAb,cAAiC,KAAK;CACpC,AAAS,OAAO;CAChB,AAAS,cAAc;CACvB,AAAS,aAAa;EACpB,MAAM;EACN,YAAY,EACV,MAAM;GAAE,MAAM;GAAU,aAAa;GAA0B,EAChE;EACD,UAAU,CAAC,OAAO;EACnB;CAED,MAAM,QAAQ,MAAgD;EAC5D,MAAM,UAAU,OAAO,KAAK,KAAK;AAEjC,MAAI;AACF,OAAI,CAAC,WAAW,QAAQ,CACtB,QAAO,+BAA+B;GAExC,MAAM,UAAU,YAAY,QAAQ;GACpC,MAAM,QAAkB,EAAE;AAC1B,QAAK,MAAM,SAAS,QAClB,KAAI;IAEF,MAAM,OAAO,SADI,KAAK,SAAS,MAAM,CACN;IAC/B,MAAM,OAAO,KAAK,aAAa,GAAG,QAAQ;IAC1C,MAAM,OAAO,KAAK,aAAa,GAAG,KAAK,KAAK,KAAK,KAAK;AACtD,UAAM,KAAK,GAAG,KAAK,IAAI,QAAQ,OAAO;WAChC;AACN,UAAM,KAAK,MAAM,QAAQ;;AAG7B,UAAO,MAAM,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG;WACtC,KAAK;AACZ,UAAO,4BAA4B,eAAe,QAAQ,IAAI,UAAU"}