heyio 1.2.2 → 1.2.4
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.
- package/dist/copilot/skills.js +4 -2
- package/dist/copilot/system-message.js +12 -0
- package/dist/copilot/tools.js +10 -8
- package/dist/store/instances.js +8 -13
- package/package.json +1 -1
package/dist/copilot/skills.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { join, basename } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { exec } from "node:child_process";
|
|
4
|
+
import { promisify } from "node:util";
|
|
4
5
|
import { PATHS } from "../paths.js";
|
|
6
|
+
const execAsync = promisify(exec);
|
|
5
7
|
export async function listSkills() {
|
|
6
8
|
if (!existsSync(PATHS.skills))
|
|
7
9
|
return [];
|
|
@@ -35,7 +37,7 @@ export async function addSkill(url) {
|
|
|
35
37
|
if (existsSync(dest)) {
|
|
36
38
|
throw new Error(`Skill "${slug}" is already installed.`);
|
|
37
39
|
}
|
|
38
|
-
|
|
40
|
+
await execAsync(`git clone --depth 1 ${url} ${dest}`);
|
|
39
41
|
// Verify SKILL.md exists
|
|
40
42
|
if (!existsSync(join(dest, "SKILL.md"))) {
|
|
41
43
|
rmSync(dest, { recursive: true, force: true });
|
|
@@ -31,6 +31,18 @@ You are IO, a personal AI assistant daemon. You run 24/7 on the user's machine,
|
|
|
31
31
|
- If the user says "do this" for a complex task → use squad_meeting with execute_after=true
|
|
32
32
|
- If the user says "just do it" or it's a simple task → use squad_delegate directly
|
|
33
33
|
|
|
34
|
+
## HARD RULE: Squad Ownership Boundary
|
|
35
|
+
If a project has a squad assigned to it, you (the orchestrator) must NEVER:
|
|
36
|
+
- Research, analyze, or investigate the project's code, issues, or state yourself
|
|
37
|
+
- Attempt any work — even preliminary analysis — before delegating
|
|
38
|
+
- "Look into" or "check on" something before passing it to the squad
|
|
39
|
+
|
|
40
|
+
When a request comes in about a squad-owned project, you IMMEDIATELY delegate to that squad's team lead with no pre-processing. The squad handles ALL work including research, analysis, planning, and execution.
|
|
41
|
+
|
|
42
|
+
The ONLY thing you are allowed to do regarding a squad-owned project (without delegating) is:
|
|
43
|
+
- Answer questions about what the squad has already done (using feed/task history)
|
|
44
|
+
- Report squad status, task progress, or past deliverables
|
|
45
|
+
|
|
34
46
|
## Squad Coverage Requirements
|
|
35
47
|
Every squad MUST have:
|
|
36
48
|
1. A dedicated team lead (PM/Senior Engineer, coordination-only — **never writes code**)
|
package/dist/copilot/tools.js
CHANGED
|
@@ -69,10 +69,12 @@ export function createTools() {
|
|
|
69
69
|
const squad = createSquad(name, universe, repo_url);
|
|
70
70
|
let cloneMsg = "";
|
|
71
71
|
if (repo_url) {
|
|
72
|
-
const {
|
|
72
|
+
const { exec } = await import("node:child_process");
|
|
73
|
+
const { promisify } = await import("node:util");
|
|
73
74
|
const { existsSync, mkdirSync } = await import("node:fs");
|
|
74
75
|
const { join } = await import("node:path");
|
|
75
76
|
const { PATHS } = await import("../paths.js");
|
|
77
|
+
const execAsync = promisify(exec);
|
|
76
78
|
// Extract owner/repo from URL (supports https and git@ formats)
|
|
77
79
|
const match = repo_url.match(/[/:]([^/]+)\/([^/.]+?)(?:\.git)?$/);
|
|
78
80
|
if (match) {
|
|
@@ -83,8 +85,7 @@ export function createTools() {
|
|
|
83
85
|
if (!existsSync(parentDir))
|
|
84
86
|
mkdirSync(parentDir, { recursive: true });
|
|
85
87
|
try {
|
|
86
|
-
|
|
87
|
-
encoding: "utf-8",
|
|
88
|
+
await execAsync(`git clone ${repo_url} ${sourceDir}`, {
|
|
88
89
|
timeout: 120_000,
|
|
89
90
|
});
|
|
90
91
|
cloneMsg = ` Repo cloned to ~/.io/source/${owner}/${repo}.`;
|
|
@@ -313,22 +314,23 @@ export function createTools() {
|
|
|
313
314
|
cwd: z.string().optional().describe("Working directory (defaults to home directory)"),
|
|
314
315
|
}),
|
|
315
316
|
handler: async ({ command, cwd }) => {
|
|
316
|
-
const {
|
|
317
|
+
const { exec } = await import("node:child_process");
|
|
318
|
+
const { promisify } = await import("node:util");
|
|
317
319
|
const { homedir } = await import("node:os");
|
|
320
|
+
const execAsync = promisify(exec);
|
|
318
321
|
try {
|
|
319
|
-
const
|
|
322
|
+
const { stdout } = await execAsync(command, {
|
|
320
323
|
cwd: cwd ?? homedir(),
|
|
321
|
-
encoding: "utf-8",
|
|
322
324
|
timeout: 60_000,
|
|
323
325
|
maxBuffer: 1024 * 1024,
|
|
324
326
|
env: { ...process.env, GH_PROMPT_DISABLED: "1" },
|
|
325
327
|
});
|
|
326
|
-
return
|
|
328
|
+
return stdout.trim() || "(no output)";
|
|
327
329
|
}
|
|
328
330
|
catch (err) {
|
|
329
331
|
const stderr = err.stderr?.toString().trim() ?? "";
|
|
330
332
|
const stdout = err.stdout?.toString().trim() ?? "";
|
|
331
|
-
return `Error (exit ${err.
|
|
333
|
+
return `Error (exit ${err.code}): ${stderr || stdout || err.message}`;
|
|
332
334
|
}
|
|
333
335
|
},
|
|
334
336
|
}),
|
package/dist/store/instances.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
3
4
|
import { getDb } from "./db.js";
|
|
5
|
+
const execAsync = promisify(exec);
|
|
4
6
|
const MAX_INSTANCES_PER_SQUAD = 3;
|
|
5
7
|
export async function createInstance(squadId, branch) {
|
|
6
8
|
const db = getDb();
|
|
@@ -19,18 +21,13 @@ export async function createInstance(squadId, branch) {
|
|
|
19
21
|
const id = randomUUID();
|
|
20
22
|
const worktreePath = `/tmp/io-worktrees/${squadId}/${branch}`;
|
|
21
23
|
// Create git worktree
|
|
24
|
+
const repoCwd = squad.repo_url.startsWith("/") ? squad.repo_url : process.cwd();
|
|
22
25
|
try {
|
|
23
|
-
|
|
24
|
-
cwd: squad.repo_url.startsWith("/") ? squad.repo_url : process.cwd(),
|
|
25
|
-
stdio: "pipe",
|
|
26
|
-
});
|
|
26
|
+
await execAsync(`git worktree add ${worktreePath} -b ${branch}`, { cwd: repoCwd });
|
|
27
27
|
}
|
|
28
|
-
catch
|
|
28
|
+
catch {
|
|
29
29
|
// Branch may already exist
|
|
30
|
-
|
|
31
|
-
cwd: squad.repo_url.startsWith("/") ? squad.repo_url : process.cwd(),
|
|
32
|
-
stdio: "pipe",
|
|
33
|
-
});
|
|
30
|
+
await execAsync(`git worktree add ${worktreePath} ${branch}`, { cwd: repoCwd });
|
|
34
31
|
}
|
|
35
32
|
db.prepare(`INSERT INTO instances (id, squad_id, branch, worktree_path, status)
|
|
36
33
|
VALUES (?, ?, ?, ?, 'active')`).run(id, squadId, branch, worktreePath);
|
|
@@ -45,9 +42,7 @@ export async function destroyInstance(instanceId) {
|
|
|
45
42
|
throw new Error(`Instance ${instanceId} not found.`);
|
|
46
43
|
// Remove worktree
|
|
47
44
|
try {
|
|
48
|
-
|
|
49
|
-
stdio: "pipe",
|
|
50
|
-
});
|
|
45
|
+
await execAsync(`git worktree remove ${instance.worktree_path} --force`);
|
|
51
46
|
}
|
|
52
47
|
catch {
|
|
53
48
|
// Already removed or doesn't exist
|