@johpaz/hive 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 (156) hide show
  1. package/CONTRIBUTING.md +44 -0
  2. package/README.md +310 -0
  3. package/package.json +96 -0
  4. package/packages/cli/package.json +28 -0
  5. package/packages/cli/src/commands/agent-run.ts +168 -0
  6. package/packages/cli/src/commands/agents.ts +398 -0
  7. package/packages/cli/src/commands/chat.ts +142 -0
  8. package/packages/cli/src/commands/config.ts +50 -0
  9. package/packages/cli/src/commands/cron.ts +161 -0
  10. package/packages/cli/src/commands/dev.ts +95 -0
  11. package/packages/cli/src/commands/doctor.ts +133 -0
  12. package/packages/cli/src/commands/gateway.ts +443 -0
  13. package/packages/cli/src/commands/logs.ts +57 -0
  14. package/packages/cli/src/commands/mcp.ts +175 -0
  15. package/packages/cli/src/commands/message.ts +77 -0
  16. package/packages/cli/src/commands/onboard.ts +1868 -0
  17. package/packages/cli/src/commands/security.ts +144 -0
  18. package/packages/cli/src/commands/service.ts +50 -0
  19. package/packages/cli/src/commands/sessions.ts +116 -0
  20. package/packages/cli/src/commands/skills.ts +187 -0
  21. package/packages/cli/src/commands/update.ts +25 -0
  22. package/packages/cli/src/index.ts +185 -0
  23. package/packages/cli/src/utils/token.ts +6 -0
  24. package/packages/code-bridge/README.md +78 -0
  25. package/packages/code-bridge/package.json +18 -0
  26. package/packages/code-bridge/src/index.ts +95 -0
  27. package/packages/code-bridge/src/process-manager.ts +212 -0
  28. package/packages/code-bridge/src/schemas.ts +133 -0
  29. package/packages/core/package.json +46 -0
  30. package/packages/core/src/agent/agent-loop.ts +369 -0
  31. package/packages/core/src/agent/compaction.ts +140 -0
  32. package/packages/core/src/agent/context-compiler.ts +378 -0
  33. package/packages/core/src/agent/context-guard.ts +91 -0
  34. package/packages/core/src/agent/context.ts +138 -0
  35. package/packages/core/src/agent/conversation-store.ts +198 -0
  36. package/packages/core/src/agent/curator.ts +158 -0
  37. package/packages/core/src/agent/hooks.ts +166 -0
  38. package/packages/core/src/agent/index.ts +116 -0
  39. package/packages/core/src/agent/llm-client.ts +503 -0
  40. package/packages/core/src/agent/native-tools.ts +505 -0
  41. package/packages/core/src/agent/prompt-builder.ts +532 -0
  42. package/packages/core/src/agent/providers/index.ts +167 -0
  43. package/packages/core/src/agent/providers.ts +1 -0
  44. package/packages/core/src/agent/reflector.ts +170 -0
  45. package/packages/core/src/agent/service.ts +64 -0
  46. package/packages/core/src/agent/stuck-loop.ts +133 -0
  47. package/packages/core/src/agent/supervisor.ts +39 -0
  48. package/packages/core/src/agent/tracer.ts +102 -0
  49. package/packages/core/src/agent/workspace.ts +110 -0
  50. package/packages/core/src/canvas/canvas-manager.test.ts +161 -0
  51. package/packages/core/src/canvas/canvas-manager.ts +319 -0
  52. package/packages/core/src/canvas/canvas-tools.ts +420 -0
  53. package/packages/core/src/canvas/emitter.ts +115 -0
  54. package/packages/core/src/canvas/index.ts +2 -0
  55. package/packages/core/src/channels/base.ts +138 -0
  56. package/packages/core/src/channels/discord.ts +260 -0
  57. package/packages/core/src/channels/index.ts +7 -0
  58. package/packages/core/src/channels/manager.ts +383 -0
  59. package/packages/core/src/channels/slack.ts +287 -0
  60. package/packages/core/src/channels/telegram.ts +502 -0
  61. package/packages/core/src/channels/webchat.ts +128 -0
  62. package/packages/core/src/channels/whatsapp.ts +375 -0
  63. package/packages/core/src/config/index.ts +12 -0
  64. package/packages/core/src/config/loader.ts +529 -0
  65. package/packages/core/src/events/event-bus.ts +169 -0
  66. package/packages/core/src/gateway/index.ts +5 -0
  67. package/packages/core/src/gateway/initializer.ts +290 -0
  68. package/packages/core/src/gateway/lane-queue.ts +169 -0
  69. package/packages/core/src/gateway/resolver.ts +108 -0
  70. package/packages/core/src/gateway/router.ts +124 -0
  71. package/packages/core/src/gateway/server.ts +3317 -0
  72. package/packages/core/src/gateway/session.ts +95 -0
  73. package/packages/core/src/gateway/slash-commands.ts +192 -0
  74. package/packages/core/src/heartbeat/index.ts +157 -0
  75. package/packages/core/src/index.ts +19 -0
  76. package/packages/core/src/integrations/catalog.ts +286 -0
  77. package/packages/core/src/integrations/env.ts +64 -0
  78. package/packages/core/src/integrations/index.ts +2 -0
  79. package/packages/core/src/memory/index.ts +1 -0
  80. package/packages/core/src/memory/notes.ts +68 -0
  81. package/packages/core/src/plugins/api.ts +128 -0
  82. package/packages/core/src/plugins/index.ts +2 -0
  83. package/packages/core/src/plugins/loader.ts +365 -0
  84. package/packages/core/src/resilience/circuit-breaker.ts +225 -0
  85. package/packages/core/src/security/google-chat.ts +269 -0
  86. package/packages/core/src/security/index.ts +192 -0
  87. package/packages/core/src/security/pairing.ts +250 -0
  88. package/packages/core/src/security/rate-limit.ts +270 -0
  89. package/packages/core/src/security/signal.ts +321 -0
  90. package/packages/core/src/state/store.ts +312 -0
  91. package/packages/core/src/storage/bun-sqlite-store.ts +188 -0
  92. package/packages/core/src/storage/crypto.ts +101 -0
  93. package/packages/core/src/storage/db-context.ts +333 -0
  94. package/packages/core/src/storage/onboarding.ts +1087 -0
  95. package/packages/core/src/storage/schema.ts +541 -0
  96. package/packages/core/src/storage/seed.ts +571 -0
  97. package/packages/core/src/storage/sqlite.ts +387 -0
  98. package/packages/core/src/storage/usage.ts +212 -0
  99. package/packages/core/src/tools/bridge-events.ts +74 -0
  100. package/packages/core/src/tools/browser.ts +275 -0
  101. package/packages/core/src/tools/codebridge.ts +421 -0
  102. package/packages/core/src/tools/coordinator-tools.ts +179 -0
  103. package/packages/core/src/tools/cron.ts +611 -0
  104. package/packages/core/src/tools/exec.ts +140 -0
  105. package/packages/core/src/tools/fs.ts +364 -0
  106. package/packages/core/src/tools/index.ts +12 -0
  107. package/packages/core/src/tools/memory.ts +176 -0
  108. package/packages/core/src/tools/notify.ts +113 -0
  109. package/packages/core/src/tools/project-management.ts +376 -0
  110. package/packages/core/src/tools/project.ts +375 -0
  111. package/packages/core/src/tools/read.ts +158 -0
  112. package/packages/core/src/tools/web.ts +436 -0
  113. package/packages/core/src/tools/workspace.ts +171 -0
  114. package/packages/core/src/utils/benchmark.ts +80 -0
  115. package/packages/core/src/utils/crypto.ts +73 -0
  116. package/packages/core/src/utils/date.ts +42 -0
  117. package/packages/core/src/utils/index.ts +4 -0
  118. package/packages/core/src/utils/logger.ts +388 -0
  119. package/packages/core/src/utils/retry.ts +70 -0
  120. package/packages/core/src/voice/index.ts +583 -0
  121. package/packages/core/tsconfig.json +9 -0
  122. package/packages/mcp/package.json +26 -0
  123. package/packages/mcp/src/config.ts +13 -0
  124. package/packages/mcp/src/index.ts +1 -0
  125. package/packages/mcp/src/logger.ts +42 -0
  126. package/packages/mcp/src/manager.ts +434 -0
  127. package/packages/mcp/src/transports/index.ts +67 -0
  128. package/packages/mcp/src/transports/sse.ts +241 -0
  129. package/packages/mcp/src/transports/websocket.ts +159 -0
  130. package/packages/skills/package.json +21 -0
  131. package/packages/skills/src/bundled/agent_management/SKILL.md +24 -0
  132. package/packages/skills/src/bundled/browser_automation/SKILL.md +30 -0
  133. package/packages/skills/src/bundled/context_compact/SKILL.md +35 -0
  134. package/packages/skills/src/bundled/cron_manager/SKILL.md +52 -0
  135. package/packages/skills/src/bundled/file_manager/SKILL.md +76 -0
  136. package/packages/skills/src/bundled/http_client/SKILL.md +24 -0
  137. package/packages/skills/src/bundled/memory/SKILL.md +42 -0
  138. package/packages/skills/src/bundled/project_management/SKILL.md +26 -0
  139. package/packages/skills/src/bundled/shell/SKILL.md +43 -0
  140. package/packages/skills/src/bundled/system_notify/SKILL.md +52 -0
  141. package/packages/skills/src/bundled/voice/SKILL.md +25 -0
  142. package/packages/skills/src/bundled/web_search/SKILL.md +29 -0
  143. package/packages/skills/src/index.ts +1 -0
  144. package/packages/skills/src/loader.ts +282 -0
  145. package/packages/tools/package.json +43 -0
  146. package/packages/tools/src/browser/browser.test.ts +111 -0
  147. package/packages/tools/src/browser/index.ts +272 -0
  148. package/packages/tools/src/canvas/index.ts +220 -0
  149. package/packages/tools/src/cron/cron.test.ts +164 -0
  150. package/packages/tools/src/cron/index.ts +304 -0
  151. package/packages/tools/src/filesystem/filesystem.test.ts +240 -0
  152. package/packages/tools/src/filesystem/index.ts +379 -0
  153. package/packages/tools/src/git/index.ts +239 -0
  154. package/packages/tools/src/index.ts +4 -0
  155. package/packages/tools/src/shell/detect-env.ts +70 -0
  156. package/packages/tools/tsconfig.json +9 -0
@@ -0,0 +1,379 @@
1
+ import type { Tool } from "@johpaz/hive-core";
2
+ import { z } from "zod";
3
+ import * as fs from "fs/promises";
4
+ import * as path from "path";
5
+
6
+ export interface FileSystemConfig {
7
+ allowedPaths: string[];
8
+ sandboxed: boolean;
9
+ maxFileSize: number;
10
+ }
11
+
12
+ function validatePath(targetPath: string, allowedPaths: string[]): void {
13
+ const resolved = path.resolve(targetPath);
14
+ const isAllowed = allowedPaths.some((allowed) => resolved.startsWith(path.resolve(allowed)));
15
+
16
+ if (!isAllowed) {
17
+ throw new Error(`Path not allowed: ${targetPath}. Allowed paths: ${allowedPaths.join(", ")}`);
18
+ }
19
+ }
20
+
21
+ function getFSConfig(config: Partial<FileSystemConfig>): FileSystemConfig {
22
+ return {
23
+ allowedPaths: config.allowedPaths ?? [process.cwd()],
24
+ sandboxed: config.sandboxed ?? true,
25
+ maxFileSize: config.maxFileSize ?? 10 * 1024 * 1024,
26
+ };
27
+ }
28
+
29
+ export function createFSReadTool(config: Partial<FileSystemConfig> = {}): Tool {
30
+ const fsConfig = getFSConfig(config);
31
+
32
+ return {
33
+ name: "fs_read",
34
+ description: "Read a file from the filesystem",
35
+ parameters: {
36
+ type: "object",
37
+ properties: {
38
+ path: {
39
+ type: "string",
40
+ description: "Path to the file to read",
41
+ },
42
+ encoding: {
43
+ type: "string",
44
+ description: "File encoding (default: utf-8)",
45
+ },
46
+ startLine: {
47
+ type: "number",
48
+ description: "Start reading from this line (1-indexed)",
49
+ },
50
+ endLine: {
51
+ type: "number",
52
+ description: "Stop reading at this line",
53
+ },
54
+ },
55
+ required: ["path"],
56
+ },
57
+ execute: async (params: Record<string, unknown>) => {
58
+ const filePath = params.path as string;
59
+ const encoding = (params.encoding as BufferEncoding) ?? "utf-8";
60
+ const startLine = params.startLine as number | undefined;
61
+ const endLine = params.endLine as number | undefined;
62
+
63
+ if (fsConfig.sandboxed) {
64
+ validatePath(filePath, fsConfig.allowedPaths);
65
+ }
66
+
67
+ const stats = await fs.stat(filePath);
68
+ if (stats.size > fsConfig.maxFileSize) {
69
+ throw new Error(`File too large: ${stats.size} bytes (max: ${fsConfig.maxFileSize})`);
70
+ }
71
+
72
+ let content = await fs.readFile(filePath, encoding);
73
+
74
+ if (startLine !== undefined || endLine !== undefined) {
75
+ const lines = content.split("\n");
76
+ const start = Math.max(0, (startLine ?? 1) - 1);
77
+ const end = endLine !== undefined ? endLine : lines.length;
78
+ content = lines.slice(start, end).join("\n");
79
+ }
80
+
81
+ return {
82
+ success: true,
83
+ path: filePath,
84
+ content,
85
+ size: stats.size,
86
+ modified: stats.mtime.toISOString(),
87
+ };
88
+ },
89
+ };
90
+ }
91
+
92
+ export function createFSWriteTool(config: Partial<FileSystemConfig> = {}): Tool {
93
+ const fsConfig = getFSConfig(config);
94
+
95
+ return {
96
+ name: "fs_write",
97
+ description: "Write content to a file",
98
+ parameters: {
99
+ type: "object",
100
+ properties: {
101
+ path: {
102
+ type: "string",
103
+ description: "Path to the file to write",
104
+ },
105
+ content: {
106
+ type: "string",
107
+ description: "Content to write",
108
+ },
109
+ mode: {
110
+ type: "string",
111
+ enum: ["overwrite", "append"],
112
+ description: "Write mode (default: overwrite)",
113
+ },
114
+ },
115
+ required: ["path", "content"],
116
+ },
117
+ execute: async (params: Record<string, unknown>) => {
118
+ const filePath = params.path as string;
119
+ const content = params.content as string;
120
+ const mode = (params.mode as string) ?? "overwrite";
121
+
122
+ if (fsConfig.sandboxed) {
123
+ validatePath(filePath, fsConfig.allowedPaths);
124
+ }
125
+
126
+ if (content.length > fsConfig.maxFileSize) {
127
+ throw new Error(`Content too large: ${content.length} bytes (max: ${fsConfig.maxFileSize})`);
128
+ }
129
+
130
+ const dir = path.dirname(filePath);
131
+ await fs.mkdir(dir, { recursive: true });
132
+
133
+ if (mode === "append") {
134
+ await fs.appendFile(filePath, content, "utf-8");
135
+ } else {
136
+ await fs.writeFile(filePath, content, "utf-8");
137
+ }
138
+
139
+ return {
140
+ success: true,
141
+ path: filePath,
142
+ bytesWritten: content.length,
143
+ };
144
+ },
145
+ };
146
+ }
147
+
148
+ export function createFSListTool(config: Partial<FileSystemConfig> = {}): Tool {
149
+ const fsConfig = getFSConfig(config);
150
+
151
+ return {
152
+ name: "fs_list",
153
+ description: "List contents of a directory",
154
+ parameters: {
155
+ type: "object",
156
+ properties: {
157
+ path: {
158
+ type: "string",
159
+ description: "Path to the directory",
160
+ },
161
+ recursive: {
162
+ type: "boolean",
163
+ description: "List recursively (default: false)",
164
+ },
165
+ },
166
+ required: ["path"],
167
+ },
168
+ execute: async (params: Record<string, unknown>) => {
169
+ const dirPath = params.path as string;
170
+ const recursive = (params.recursive as boolean) ?? false;
171
+
172
+ if (fsConfig.sandboxed) {
173
+ validatePath(dirPath, fsConfig.allowedPaths);
174
+ }
175
+
176
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
177
+
178
+ const results: Array<{
179
+ name: string;
180
+ path: string;
181
+ type: string;
182
+ size: number;
183
+ modified: string;
184
+ }> = [];
185
+
186
+ for (const entry of entries) {
187
+ const fullPath = path.join(dirPath, entry.name);
188
+ const stats = await fs.stat(fullPath);
189
+
190
+ results.push({
191
+ name: entry.name,
192
+ path: fullPath,
193
+ type: entry.isDirectory() ? "directory" : "file",
194
+ size: stats.size,
195
+ modified: stats.mtime.toISOString(),
196
+ });
197
+ }
198
+
199
+ if (recursive) {
200
+ const subdirs = results.filter((r) => r.type === "directory");
201
+ for (const subdir of subdirs) {
202
+ try {
203
+ const subResult = await createFSListTool(config).execute({ path: subdir.path, recursive: true });
204
+ const subFiles = (subResult as { entries: typeof results }).entries;
205
+ results.push(...subFiles);
206
+ } catch {
207
+ // Skip directories we can't read
208
+ }
209
+ }
210
+ }
211
+
212
+ return {
213
+ success: true,
214
+ path: dirPath,
215
+ entries: results,
216
+ count: results.length,
217
+ };
218
+ },
219
+ };
220
+ }
221
+
222
+ export function createFSMkdirTool(config: Partial<FileSystemConfig> = {}): Tool {
223
+ const fsConfig = getFSConfig(config);
224
+
225
+ return {
226
+ name: "fs_mkdir",
227
+ description: "Create a directory",
228
+ parameters: {
229
+ type: "object",
230
+ properties: {
231
+ path: {
232
+ type: "string",
233
+ description: "Path to create",
234
+ },
235
+ },
236
+ required: ["path"],
237
+ },
238
+ execute: async (params: Record<string, unknown>) => {
239
+ const dirPath = params.path as string;
240
+
241
+ if (fsConfig.sandboxed) {
242
+ validatePath(dirPath, fsConfig.allowedPaths);
243
+ }
244
+
245
+ await fs.mkdir(dirPath, { recursive: true });
246
+
247
+ return { success: true, path: dirPath };
248
+ },
249
+ };
250
+ }
251
+
252
+ export function createFSDeleteTool(config: Partial<FileSystemConfig> = {}): Tool {
253
+ const fsConfig = getFSConfig(config);
254
+
255
+ return {
256
+ name: "fs_delete",
257
+ description: "Delete a file or directory",
258
+ parameters: {
259
+ type: "object",
260
+ properties: {
261
+ path: {
262
+ type: "string",
263
+ description: "Path to delete",
264
+ },
265
+ recursive: {
266
+ type: "boolean",
267
+ description: "Delete recursively for directories",
268
+ },
269
+ },
270
+ required: ["path"],
271
+ },
272
+ execute: async (params: Record<string, unknown>) => {
273
+ const targetPath = params.path as string;
274
+ const recursive = (params.recursive as boolean) ?? false;
275
+
276
+ if (fsConfig.sandboxed) {
277
+ validatePath(targetPath, fsConfig.allowedPaths);
278
+ }
279
+
280
+ const stats = await fs.stat(targetPath);
281
+
282
+ if (stats.isDirectory()) {
283
+ if (recursive) {
284
+ await fs.rm(targetPath, { recursive: true });
285
+ } else {
286
+ await fs.rmdir(targetPath);
287
+ }
288
+ } else {
289
+ await fs.unlink(targetPath);
290
+ }
291
+
292
+ return { success: true, path: targetPath };
293
+ },
294
+ };
295
+ }
296
+
297
+ export function createFSCopyTool(config: Partial<FileSystemConfig> = {}): Tool {
298
+ const fsConfig = getFSConfig(config);
299
+
300
+ return {
301
+ name: "fs_copy",
302
+ description: "Copy a file",
303
+ parameters: {
304
+ type: "object",
305
+ properties: {
306
+ source: {
307
+ type: "string",
308
+ description: "Source file path",
309
+ },
310
+ destination: {
311
+ type: "string",
312
+ description: "Destination file path",
313
+ },
314
+ },
315
+ required: ["source", "destination"],
316
+ },
317
+ execute: async (params: Record<string, unknown>) => {
318
+ const source = params.source as string;
319
+ const destination = params.destination as string;
320
+
321
+ if (fsConfig.sandboxed) {
322
+ validatePath(source, fsConfig.allowedPaths);
323
+ validatePath(destination, fsConfig.allowedPaths);
324
+ }
325
+
326
+ await fs.copyFile(source, destination);
327
+
328
+ return { success: true, source, destination };
329
+ },
330
+ };
331
+ }
332
+
333
+ export function createFSMoveTool(config: Partial<FileSystemConfig> = {}): Tool {
334
+ const fsConfig = getFSConfig(config);
335
+
336
+ return {
337
+ name: "fs_move",
338
+ description: "Move or rename a file",
339
+ parameters: {
340
+ type: "object",
341
+ properties: {
342
+ source: {
343
+ type: "string",
344
+ description: "Source file path",
345
+ },
346
+ destination: {
347
+ type: "string",
348
+ description: "Destination file path",
349
+ },
350
+ },
351
+ required: ["source", "destination"],
352
+ },
353
+ execute: async (params: Record<string, unknown>) => {
354
+ const source = params.source as string;
355
+ const destination = params.destination as string;
356
+
357
+ if (fsConfig.sandboxed) {
358
+ validatePath(source, fsConfig.allowedPaths);
359
+ validatePath(destination, fsConfig.allowedPaths);
360
+ }
361
+
362
+ await fs.rename(source, destination);
363
+
364
+ return { success: true, source, destination };
365
+ },
366
+ };
367
+ }
368
+
369
+ export function createFSTools(config: Partial<FileSystemConfig> = {}): Tool[] {
370
+ return [
371
+ createFSReadTool(config),
372
+ createFSWriteTool(config),
373
+ createFSListTool(config),
374
+ createFSMkdirTool(config),
375
+ createFSDeleteTool(config),
376
+ createFSCopyTool(config),
377
+ createFSMoveTool(config),
378
+ ];
379
+ }
@@ -0,0 +1,239 @@
1
+ import { tool } from "@langchain/core/tools"
2
+ import { z } from "zod"
3
+ import git from "isomorphic-git"
4
+ import http from "isomorphic-git/http/node"
5
+ import * as fs from "fs"
6
+
7
+ export const gitCloneTool = tool(
8
+ async ({ url, dir, branch = "main", depth = 1 }: {
9
+ url: string
10
+ dir: string
11
+ branch?: string
12
+ depth?: number
13
+ }) => {
14
+ try {
15
+ const fsProxy = fs
16
+ await git.clone({
17
+ fs: fsProxy,
18
+ http,
19
+ dir,
20
+ url,
21
+ ref: branch,
22
+ depth,
23
+ })
24
+ return { success: true, message: `Cloned ${url} to ${dir}` }
25
+ } catch (error: any) {
26
+ return { success: false, error: error.message }
27
+ }
28
+ },
29
+ {
30
+ name: "git_clone",
31
+ description: "Clone a git repository to a local directory",
32
+ schema: z.object({
33
+ url: z.string().describe("The URL of the repository to clone"),
34
+ dir: z.string().describe("The local directory to clone into"),
35
+ branch: z.string().optional().describe("The branch to clone (default: main)"),
36
+ depth: z.number().optional().describe("Clone depth (default: 1)"),
37
+ }),
38
+ }
39
+ )
40
+
41
+ export const gitStatusTool = tool(
42
+ async ({ dir }: { dir: string }) => {
43
+ try {
44
+ const fsProxy = fs
45
+ const status = await git.statusMatrix({ fs: fsProxy, dir })
46
+ const files = status
47
+ .filter(([_, head, workdir, stage]) => head !== 1 || workdir !== 1 || stage !== 1)
48
+ .map(([filepath, head, workdir, stage]) => ({
49
+ filepath,
50
+ head: head === 1 ? "same" : "modified",
51
+ workdir: workdir === 1 ? "same" : "modified",
52
+ stage: stage === 1 ? "same" : "modified",
53
+ }))
54
+ return { success: true, status: files }
55
+ } catch (error: any) {
56
+ return { success: false, error: error.message }
57
+ }
58
+ },
59
+ {
60
+ name: "git_status",
61
+ description: "Show the working tree status",
62
+ schema: z.object({
63
+ dir: z.string().describe("The directory of the git repository"),
64
+ }),
65
+ }
66
+ )
67
+
68
+ export const gitPullTool = tool(
69
+ async ({ dir, branch = "main" }: { dir: string; branch?: string }) => {
70
+ try {
71
+ const fsProxy = fs
72
+ await git.pull({
73
+ fs: fsProxy,
74
+ http,
75
+ dir,
76
+ author: { name: "Hive", email: "hive@local" },
77
+ ref: branch,
78
+ })
79
+ return { success: true, message: `Pulled from ${branch}` }
80
+ } catch (error: any) {
81
+ return { success: false, error: error.message }
82
+ }
83
+ },
84
+ {
85
+ name: "git_pull",
86
+ description: "Fetch and merge from a remote",
87
+ schema: z.object({
88
+ dir: z.string().describe("The directory of the git repository"),
89
+ branch: z.string().optional().describe("The branch to pull (default: main)"),
90
+ }),
91
+ }
92
+ )
93
+
94
+ export const gitPushTool = tool(
95
+ async ({ dir, remote = "origin", branch = "main" }: { dir: string; remote?: string; branch?: string }) => {
96
+ try {
97
+ const fsProxy = fs
98
+ await git.push({
99
+ fs: fsProxy,
100
+ http,
101
+ dir,
102
+ remote,
103
+ ref: branch,
104
+ })
105
+ return { success: true, message: `Pushed to ${remote}/${branch}` }
106
+ } catch (error: any) {
107
+ return { success: false, error: error.message }
108
+ }
109
+ },
110
+ {
111
+ name: "git_push",
112
+ description: "Push commits to a remote",
113
+ schema: z.object({
114
+ dir: z.string().describe("The directory of the git repository"),
115
+ remote: z.string().optional().describe("The remote name (default: origin)"),
116
+ branch: z.string().optional().describe("The branch to push (default: main)"),
117
+ }),
118
+ }
119
+ )
120
+
121
+ export const gitCommitTool = tool(
122
+ async ({ dir, message, authorName, authorEmail }: {
123
+ dir: string
124
+ message: string
125
+ authorName?: string
126
+ authorEmail?: string
127
+ }) => {
128
+ try {
129
+ const fsProxy = fs
130
+ const sha = await git.commit({
131
+ fs: fsProxy,
132
+ dir,
133
+ message,
134
+ author: {
135
+ name: authorName || "Hive",
136
+ email: authorEmail || "hive@local",
137
+ },
138
+ })
139
+ return { success: true, sha, message: `Committed: ${sha.slice(0, 7)}` }
140
+ } catch (error: any) {
141
+ return { success: false, error: error.message }
142
+ }
143
+ },
144
+ {
145
+ name: "git_commit",
146
+ description: "Create a new commit",
147
+ schema: z.object({
148
+ dir: z.string().describe("The directory of the git repository"),
149
+ message: z.string().describe("The commit message"),
150
+ authorName: z.string().optional().describe("Author name"),
151
+ authorEmail: z.string().optional().describe("Author email"),
152
+ }),
153
+ }
154
+ )
155
+
156
+ export const gitBranchTool = tool(
157
+ async ({ dir, operation, branchName, startPoint }: {
158
+ dir: string
159
+ operation: "list" | "create" | "delete"
160
+ branchName?: string
161
+ startPoint?: string
162
+ }) => {
163
+ try {
164
+ const fsProxy = fs
165
+
166
+ if (operation === "list") {
167
+ const branches = await git.listBranches({ fs: fsProxy, dir })
168
+ return { success: true, branches }
169
+ }
170
+
171
+ if (operation === "create" && branchName) {
172
+ await git.branch({
173
+ fs: fsProxy,
174
+ dir,
175
+ ref: branchName,
176
+ checkout: !!startPoint,
177
+ })
178
+ return { success: true, message: `Created branch: ${branchName}` }
179
+ }
180
+
181
+ if (operation === "delete" && branchName) {
182
+ await git.deleteBranch({ fs: fsProxy, dir, ref: branchName })
183
+ return { success: true, message: `Deleted branch: ${branchName}` }
184
+ }
185
+
186
+ return { success: false, error: "Invalid operation" }
187
+ } catch (error: any) {
188
+ return { success: false, error: error.message }
189
+ }
190
+ },
191
+ {
192
+ name: "git_branch",
193
+ description: "List, create, or delete branches",
194
+ schema: z.object({
195
+ dir: z.string().describe("The directory of the git repository"),
196
+ operation: z.enum(["list", "create", "delete"]).describe("The operation to perform"),
197
+ branchName: z.string().optional().describe("The name of the branch"),
198
+ startPoint: z.string().optional().describe("Starting commit/branch for new branch"),
199
+ }),
200
+ }
201
+ )
202
+
203
+ export const gitLogTool = tool(
204
+ async ({ dir, limit = 10 }: { dir: string; limit?: number }) => {
205
+ try {
206
+ const fsProxy = fs
207
+ const commits = await git.log({ fs: fsProxy, dir, depth: limit })
208
+ return {
209
+ success: true,
210
+ commits: commits.map((c) => ({
211
+ sha: c.oid,
212
+ message: c.commit.message,
213
+ author: c.commit.author.name,
214
+ date: c.commit.author.timestamp,
215
+ })),
216
+ }
217
+ } catch (error: any) {
218
+ return { success: false, error: error.message }
219
+ }
220
+ },
221
+ {
222
+ name: "git_log",
223
+ description: "Show commit logs",
224
+ schema: z.object({
225
+ dir: z.string().describe("The directory of the git repository"),
226
+ limit: z.number().optional().describe("Number of commits to show (default: 10)"),
227
+ }),
228
+ }
229
+ )
230
+
231
+ export const gitToolsList = [
232
+ gitCloneTool,
233
+ gitStatusTool,
234
+ gitPullTool,
235
+ gitPushTool,
236
+ gitCommitTool,
237
+ gitBranchTool,
238
+ gitLogTool,
239
+ ]
@@ -0,0 +1,4 @@
1
+ export * from "./browser/index.ts";
2
+ export * from "./cron/index.ts";
3
+ export * from "./filesystem/index.ts";
4
+ export * from "./canvas/index.ts";
@@ -0,0 +1,70 @@
1
+ import { tool } from "@langchain/core/tools"
2
+ import { z } from "zod"
3
+ import { which } from "bun"
4
+
5
+ interface ToolInfo {
6
+ name: string
7
+ exe: string
8
+ }
9
+
10
+ export const detectEnvTool = tool(
11
+ async () => {
12
+ const tools: ToolInfo[] = [
13
+ { name: "node", exe: "node" },
14
+ { name: "bun", exe: "bun" },
15
+ { name: "python", exe: "python" },
16
+ { name: "python3", exe: "python3" },
17
+ { name: "docker", exe: "docker" },
18
+ { name: "git", exe: "git" },
19
+ { name: "npm", exe: "npm" },
20
+ { name: "yarn", exe: "yarn" },
21
+ { name: "pnpm", exe: "pnpm" },
22
+ { name: "cargo", exe: "cargo" },
23
+ { name: "go", exe: "go" },
24
+ { name: "java", exe: "java" },
25
+ { name: "ruby", exe: "ruby" },
26
+ { name: "rustc", exe: "rustc" },
27
+ { name: "gcc", exe: "gcc" },
28
+ { name: "make", exe: "make" },
29
+ { name: "curl", exe: "curl" },
30
+ { name: "wget", exe: "wget" },
31
+ { name: "zip", exe: "zip" },
32
+ { name: "unzip", exe: "unzip" },
33
+ ]
34
+
35
+ const results: Record<string, string | null> = {}
36
+
37
+ for (const t of tools) {
38
+ const path = which(t.exe)
39
+ if (path) {
40
+ results[t.name] = path
41
+ } else {
42
+ results[t.name] = null
43
+ }
44
+ }
45
+
46
+ const installed = Object.entries(results)
47
+ .filter(([_, path]) => path !== null)
48
+ .map(([name, path]) => ({ name, path }))
49
+ .sort((a, b) => a.name.localeCompare(b.name))
50
+
51
+ const missing = Object.entries(results)
52
+ .filter(([_, path]) => path === null)
53
+ .map(([name]) => name)
54
+ .sort()
55
+
56
+ return {
57
+ detected: results,
58
+ installed,
59
+ missing,
60
+ summary: `${installed.length} tools detected, ${missing.length} missing`,
61
+ }
62
+ },
63
+ {
64
+ name: "detect_env",
65
+ description: "Detect which development tools are installed on the system. Returns a map of tool names to their paths.",
66
+ schema: z.object({}),
67
+ }
68
+ )
69
+
70
+ export const shellToolsList = [detectEnvTool]
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../core/tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "declaration": true,
6
+ "declarationMap": true
7
+ },
8
+ "include": ["src/**/*"]
9
+ }