heyio 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -308,6 +308,13 @@ export async function initOrchestrator(copilotClient) {
308
308
  catch (err) {
309
309
  console.error("[io] Could not validate models:", err instanceof Error ? err.message : err);
310
310
  }
311
+ // Log built-in tools for diagnostics
312
+ try {
313
+ const toolsList = await copilotClient.rpc.tools.list({});
314
+ const toolNames = toolsList.tools.map((t) => t.name);
315
+ console.error(`[io] Built-in tools: ${toolNames.join(", ")}`);
316
+ }
317
+ catch { /* non-fatal */ }
311
318
  // Start health check timer
312
319
  healthCheckTimer = setInterval(() => {
313
320
  if (!client || client.getState() !== "connected") {
@@ -263,7 +263,153 @@ export function createTools(deps) {
263
263
  }
264
264
  },
265
265
  });
266
- return [wikiRead, wikiWrite, wikiSearch, squadCreate, squadRecall, squadStatus, squadLogDecision, shell, fileOps, bash, readFile];
266
+ // Override built-in view tool
267
+ const viewTool = defineTool("view", {
268
+ description: "View a file's contents or list a directory.",
269
+ skipPermission: true,
270
+ overridesBuiltInTool: true,
271
+ parameters: z.object({
272
+ path: z.string().describe("Path to the file or directory"),
273
+ view_range: z.array(z.number()).optional().describe("Line range [start, end] to view"),
274
+ }),
275
+ handler: async ({ path: filePath, view_range }) => {
276
+ console.error(`[io] view tool called: ${filePath}`);
277
+ try {
278
+ const resolved = resolve(filePath);
279
+ if (!existsSync(resolved))
280
+ return `Not found: ${filePath}`;
281
+ const stat = statSync(resolved);
282
+ if (stat.isDirectory()) {
283
+ const entries = readdirSync(resolved);
284
+ return entries
285
+ .map((e) => {
286
+ const full = join(resolved, e);
287
+ try {
288
+ return statSync(full).isDirectory() ? `${e}/` : e;
289
+ }
290
+ catch {
291
+ return e;
292
+ }
293
+ })
294
+ .join("\n") || "(empty directory)";
295
+ }
296
+ const text = readFileSync(resolved, "utf-8");
297
+ if (view_range && view_range.length === 2) {
298
+ const lines = text.split("\n");
299
+ const start = Math.max(0, view_range[0] - 1);
300
+ const end = view_range[1] === -1 ? lines.length : Math.min(lines.length, view_range[1]);
301
+ return lines.slice(start, end).map((l, i) => `${start + i + 1}. ${l}`).join("\n");
302
+ }
303
+ if (text.length > 8000) {
304
+ return text.slice(0, 8000) + "\n\n[…truncated]";
305
+ }
306
+ return text;
307
+ }
308
+ catch (err) {
309
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
310
+ }
311
+ },
312
+ });
313
+ // Override built-in grep tool
314
+ const grepTool = defineTool("grep", {
315
+ description: "Search file contents using a pattern.",
316
+ skipPermission: true,
317
+ overridesBuiltInTool: true,
318
+ parameters: z.object({
319
+ pattern: z.string().describe("Search pattern (regex)"),
320
+ path: z.string().optional().describe("Directory or file to search"),
321
+ include: z.string().optional().describe("Glob pattern to filter files (e.g., '*.ts')"),
322
+ }),
323
+ handler: async ({ pattern, path: searchPath, include }) => {
324
+ console.error(`[io] grep tool called: ${pattern} in ${searchPath || "."}`);
325
+ try {
326
+ let cmd = `grep -rn "${pattern.replace(/"/g, '\\"')}"`;
327
+ if (include)
328
+ cmd += ` --include="${include}"`;
329
+ cmd += ` ${searchPath || "."}`;
330
+ const result = execSync(cmd, {
331
+ encoding: "utf-8",
332
+ timeout: 30_000,
333
+ maxBuffer: 1024 * 1024,
334
+ });
335
+ const output = result.trim();
336
+ if (output.length > 8000) {
337
+ return output.slice(0, 8000) + "\n\n[…truncated]";
338
+ }
339
+ return output || "(no matches)";
340
+ }
341
+ catch (err) {
342
+ const execErr = err;
343
+ if (execErr.status === 1)
344
+ return "(no matches)";
345
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
346
+ }
347
+ },
348
+ });
349
+ // Override built-in str_replace_editor tool
350
+ const strReplaceEditor = defineTool("str_replace_editor", {
351
+ description: "View, create, or edit files using string replacement.",
352
+ skipPermission: true,
353
+ overridesBuiltInTool: true,
354
+ parameters: z.object({
355
+ command: z.enum(["view", "create", "str_replace", "insert"]).describe("Command to execute"),
356
+ path: z.string().describe("File path"),
357
+ old_str: z.string().optional().describe("String to replace (for str_replace)"),
358
+ new_str: z.string().optional().describe("Replacement string"),
359
+ file_text: z.string().optional().describe("Content for create"),
360
+ insert_line: z.number().optional().describe("Line number for insert"),
361
+ view_range: z.array(z.number()).optional().describe("Line range [start, end]"),
362
+ }),
363
+ handler: async ({ command, path: filePath, old_str, new_str, file_text, insert_line, view_range }) => {
364
+ console.error(`[io] str_replace_editor tool called: ${command} ${filePath}`);
365
+ try {
366
+ const resolved = resolve(filePath);
367
+ if (command === "view") {
368
+ if (!existsSync(resolved))
369
+ return `File not found: ${filePath}`;
370
+ const text = readFileSync(resolved, "utf-8");
371
+ if (view_range && view_range.length === 2) {
372
+ const lines = text.split("\n");
373
+ const start = Math.max(0, view_range[0] - 1);
374
+ const end = view_range[1] === -1 ? lines.length : Math.min(lines.length, view_range[1]);
375
+ return lines.slice(start, end).map((l, i) => `${start + i + 1}. ${l}`).join("\n");
376
+ }
377
+ if (text.length > 8000)
378
+ return text.slice(0, 8000) + "\n\n[…truncated]";
379
+ return text;
380
+ }
381
+ if (command === "create") {
382
+ mkdirSync(dirname(resolved), { recursive: true });
383
+ writeFileSync(resolved, file_text || "", "utf-8");
384
+ return `Created: ${filePath}`;
385
+ }
386
+ if (command === "str_replace") {
387
+ if (!existsSync(resolved))
388
+ return `File not found: ${filePath}`;
389
+ const text = readFileSync(resolved, "utf-8");
390
+ if (old_str && !text.includes(old_str))
391
+ return `old_str not found in ${filePath}`;
392
+ const updated = old_str ? text.replace(old_str, new_str || "") : text;
393
+ writeFileSync(resolved, updated, "utf-8");
394
+ return `Updated: ${filePath}`;
395
+ }
396
+ if (command === "insert") {
397
+ if (!existsSync(resolved))
398
+ return `File not found: ${filePath}`;
399
+ const lines = readFileSync(resolved, "utf-8").split("\n");
400
+ const lineNum = insert_line ?? lines.length;
401
+ lines.splice(lineNum, 0, new_str || "");
402
+ writeFileSync(resolved, lines.join("\n"), "utf-8");
403
+ return `Inserted at line ${lineNum} in ${filePath}`;
404
+ }
405
+ return `Unknown command: ${command}`;
406
+ }
407
+ catch (err) {
408
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
409
+ }
410
+ },
411
+ });
412
+ return [wikiRead, wikiWrite, wikiSearch, squadCreate, squadRecall, squadStatus, squadLogDecision, shell, fileOps, bash, readFile, viewTool, grepTool, strReplaceEditor];
267
413
  }
268
414
  function walkDirectory(dir, maxDepth = 3, depth = 0) {
269
415
  if (depth >= maxDepth)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heyio",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "IO — a personal AI assistant built on the GitHub Copilot SDK",
5
5
  "bin": {
6
6
  "io": "dist/index.js"