heyio 1.1.2 → 1.1.3

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.
@@ -35,6 +35,11 @@ Every squad MUST have:
35
35
  ## GitHub Self-Review Limitation
36
36
  All squad agents share the repo owner's gh identity. GitHub blocks self-approval. Veto reviewers use --comment with "LGTM" instead of --approve. Merge criteria: all veto-capable members have posted approving comments + CI passes + no conflicts.
37
37
  ${selfEditBlock}
38
+ ## Source Code Convention
39
+ - All repositories are cloned to ~/source/{owner}/{repo}
40
+ - When creating a squad with a repo_url, the repo is automatically cloned there
41
+ - When working with a squad's code, always use ~/source/{owner}/{repo} as the working directory
42
+
38
43
  ## Environment
39
44
  - OS: ${process.platform}
40
45
  - Working directory: ${process.cwd()}
@@ -67,7 +67,38 @@ export function createTools() {
67
67
  handler: async ({ name, universe, repo_url }) => {
68
68
  const { createSquad } = await import("../store/squads.js");
69
69
  const squad = createSquad(name, universe, repo_url);
70
- return `Squad "${name}" created with universe "${universe}". ID: ${squad.id}, Slug: ${squad.slug}. Wiki path: ~/.io/wiki/squads/${squad.slug}/`;
70
+ let cloneMsg = "";
71
+ if (repo_url) {
72
+ const { execSync } = await import("node:child_process");
73
+ const { homedir } = await import("node:os");
74
+ const { existsSync, mkdirSync } = await import("node:fs");
75
+ const { join } = await import("node:path");
76
+ // Extract owner/repo from URL (supports https and git@ formats)
77
+ const match = repo_url.match(/[/:]([^/]+)\/([^/.]+?)(?:\.git)?$/);
78
+ if (match) {
79
+ const [, owner, repo] = match;
80
+ const sourceDir = join(homedir(), "source", owner, repo);
81
+ if (!existsSync(sourceDir)) {
82
+ const parentDir = join(homedir(), "source", owner);
83
+ if (!existsSync(parentDir))
84
+ mkdirSync(parentDir, { recursive: true });
85
+ try {
86
+ execSync(`git clone ${repo_url} ${sourceDir}`, {
87
+ encoding: "utf-8",
88
+ timeout: 120_000,
89
+ });
90
+ cloneMsg = ` Repo cloned to ~/source/${owner}/${repo}.`;
91
+ }
92
+ catch (err) {
93
+ cloneMsg = ` (Clone failed: ${err.stderr?.trim() || err.message})`;
94
+ }
95
+ }
96
+ else {
97
+ cloneMsg = ` Repo already exists at ~/source/${owner}/${repo}.`;
98
+ }
99
+ }
100
+ }
101
+ return `Squad "${name}" created with universe "${universe}". ID: ${squad.id}, Slug: ${squad.slug}. Wiki path: ~/.io/wiki/squads/${squad.slug}/${cloneMsg}`;
71
102
  },
72
103
  }),
73
104
  defineTool("squad_add_agent", {
@@ -288,52 +319,8 @@ export function createTools() {
288
319
  },
289
320
  }),
290
321
  // --- Web Tools ---
291
- defineTool("web_search", {
292
- description: "Search the web for information. Returns relevant results with titles, URLs, and snippets.",
293
- parameters: z.object({
294
- query: z.string().describe("Search query"),
295
- }),
296
- handler: async ({ query }) => {
297
- const { execSync } = await import("node:child_process");
298
- try {
299
- // Use ddgr (DuckDuckGo CLI) if available, otherwise fall back to a curl-based approach
300
- const output = execSync(`ddgr --json --num 5 ${JSON.stringify(query)}`, { encoding: "utf-8", timeout: 15_000 });
301
- return output.trim();
302
- }
303
- catch {
304
- // Fallback: use DuckDuckGo HTML lite
305
- try {
306
- const encoded = encodeURIComponent(query);
307
- const output = execSync(`curl -sL "https://lite.duckduckgo.com/lite/?q=${encoded}" | grep -oP '(?<=<a rel="nofollow" href=")[^"]+' | head -10`, { encoding: "utf-8", timeout: 15_000 });
308
- return output.trim() || "No results found.";
309
- }
310
- catch {
311
- return "Web search unavailable. Try using shell_exec with curl to fetch a specific URL.";
312
- }
313
- }
314
- },
315
- }),
316
- defineTool("web_fetch", {
317
- description: "Fetch the content of a web page or API endpoint. Returns the response body (truncated to 50KB).",
318
- parameters: z.object({
319
- url: z.string().describe("URL to fetch"),
320
- headers: z.record(z.string(), z.string()).optional().describe("Optional HTTP headers"),
321
- }),
322
- handler: async ({ url, headers }) => {
323
- try {
324
- const headerArgs = headers
325
- ? Object.entries(headers).map(([k, v]) => `-H "${k}: ${v}"`).join(" ")
326
- : "";
327
- const { execSync } = await import("node:child_process");
328
- const output = execSync(`curl -sL --max-time 30 ${headerArgs} ${JSON.stringify(url)}`, { encoding: "utf-8", timeout: 35_000, maxBuffer: 1024 * 1024 });
329
- // Truncate to 50KB
330
- return output.length > 50_000 ? output.slice(0, 50_000) + "\n...(truncated)" : output;
331
- }
332
- catch (err) {
333
- return `Error fetching URL: ${err.message}`;
334
- }
335
- },
336
- }),
322
+ // NOTE: web_search and web_fetch are provided by the Copilot SDK as built-in tools.
323
+ // Do not define them here to avoid conflicts.
337
324
  ];
338
325
  }
339
326
  //# sourceMappingURL=tools.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heyio",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "IO — a personal AI assistant daemon built on the GitHub Copilot SDK",
5
5
  "bin": {
6
6
  "io": "dist/index.js"