heyio 3.0.2 → 3.0.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.
Files changed (63) hide show
  1. package/dist/api/server.js +1 -1
  2. package/dist/api/server.js.map +1 -1
  3. package/dist/logging/logger.d.ts.map +1 -1
  4. package/dist/logging/logger.js +13 -1
  5. package/dist/logging/logger.js.map +1 -1
  6. package/node_modules/@io/shared/package.json +1 -1
  7. package/package.json +7 -2
  8. package/public/assets/index-2RY89H3W.js +336 -0
  9. package/public/assets/index-2RY89H3W.js.map +1 -0
  10. package/public/assets/index-D3cGfBsj.css +1 -0
  11. package/public/index.html +14 -0
  12. package/src/api/middleware/auth.ts +0 -76
  13. package/src/api/notifications.ts +0 -122
  14. package/src/api/routes/activity.ts +0 -29
  15. package/src/api/routes/attachments.ts +0 -93
  16. package/src/api/routes/config.ts +0 -115
  17. package/src/api/routes/conversations.ts +0 -87
  18. package/src/api/routes/health.ts +0 -18
  19. package/src/api/routes/inbox.ts +0 -98
  20. package/src/api/routes/schedules.ts +0 -121
  21. package/src/api/routes/skills.ts +0 -105
  22. package/src/api/routes/squads.ts +0 -145
  23. package/src/api/routes/usage.ts +0 -57
  24. package/src/api/routes/wiki.ts +0 -49
  25. package/src/api/server.ts +0 -186
  26. package/src/config.ts +0 -3
  27. package/src/copilot/client.ts +0 -42
  28. package/src/copilot/health-monitor.ts +0 -85
  29. package/src/copilot/orchestrator.ts +0 -222
  30. package/src/copilot/tools.ts +0 -707
  31. package/src/index.ts +0 -113
  32. package/src/logging/logger.ts +0 -26
  33. package/src/models/index.ts +0 -11
  34. package/src/models/pricing.ts +0 -121
  35. package/src/models/registry.ts +0 -131
  36. package/src/models/token-tracker.ts +0 -151
  37. package/src/scheduler/engine.ts +0 -146
  38. package/src/skills/index.ts +0 -13
  39. package/src/skills/store.ts +0 -188
  40. package/src/squad/agent.ts +0 -326
  41. package/src/squad/autonomy.ts +0 -78
  42. package/src/squad/event-bus.ts +0 -71
  43. package/src/squad/execution/index.ts +0 -17
  44. package/src/squad/execution/instance.ts +0 -186
  45. package/src/squad/execution/meeting.ts +0 -191
  46. package/src/squad/execution/pr.ts +0 -127
  47. package/src/squad/execution/runner.ts +0 -97
  48. package/src/squad/execution/tasks.ts +0 -111
  49. package/src/squad/execution/worktree.ts +0 -138
  50. package/src/squad/hiring.ts +0 -222
  51. package/src/squad/index.ts +0 -17
  52. package/src/squad/manager.ts +0 -337
  53. package/src/squad/name-generator.ts +0 -135
  54. package/src/squad/roles/templates.ts +0 -104
  55. package/src/squad/skill-parser.ts +0 -120
  56. package/src/squad/source-resolver.ts +0 -57
  57. package/src/store/activity.ts +0 -176
  58. package/src/store/db.ts +0 -237
  59. package/src/store/inbox.ts +0 -199
  60. package/src/store/schedules.ts +0 -199
  61. package/src/wiki/index.ts +0 -12
  62. package/src/wiki/store.ts +0 -139
  63. package/tsconfig.json +0 -9
@@ -1,111 +0,0 @@
1
- import { createChildLogger } from '../../logging/logger.js';
2
- import type { Agent } from '../agent.js';
3
- import { getEventBus } from '../event-bus.js';
4
- import type { SquadRuntime } from '../manager.js';
5
- import { type Instance, type InstanceTask, transitionInstance } from './instance.js';
6
-
7
- const logger = () => createChildLogger('task-exec');
8
-
9
- /**
10
- * Execute all tasks in an instance sequentially.
11
- * Team lead reviews each completed task before moving to the next.
12
- */
13
- export async function executeTasks(params: {
14
- instance: Instance;
15
- runtime: SquadRuntime;
16
- }): Promise<void> {
17
- const log = logger();
18
- const { instance, runtime } = params;
19
-
20
- await transitionInstance(instance.id, 'working');
21
-
22
- const teamLead = runtime.members.get('team-lead');
23
- if (!teamLead) throw new Error('No team lead for task execution');
24
-
25
- for (const task of instance.tasks) {
26
- if (task.status === 'done') continue;
27
-
28
- log.info({ taskId: task.id, assignedTo: task.assignedTo }, 'Executing task');
29
- task.status = 'in_progress';
30
-
31
- const agent = runtime.members.get(task.assignedTo);
32
- if (!agent) {
33
- log.warn({ role: task.assignedTo }, 'No agent for assigned role, team lead will handle');
34
- // Reassign to team lead for delegation decision
35
- task.result = `No agent with role '${task.assignedTo}' available.`;
36
- task.status = 'failed';
37
- continue;
38
- }
39
-
40
- try {
41
- // Agent executes the task
42
- const workingDir = instance.worktree?.path ?? '';
43
- const taskPrompt = buildTaskPrompt(task, workingDir, instance);
44
-
45
- const result = await agent.send(taskPrompt);
46
- task.result = result;
47
-
48
- // Team lead reviews
49
- const review = await teamLead.send(
50
- `An agent (${task.assignedTo}) completed a task. Review their work:\n\nTask: ${task.description}\n\nAgent's report:\n${result.slice(0, 2000)}\n\nIs this satisfactory? Reply with:\n- "APPROVED" if the work meets requirements\n- "REDO: <feedback>" if it needs changes`,
51
- );
52
-
53
- if (review.toUpperCase().includes('APPROVED')) {
54
- task.status = 'done';
55
- log.info({ taskId: task.id }, 'Task approved');
56
- } else if (review.toUpperCase().includes('REDO:')) {
57
- // Give agent one more attempt with feedback
58
- const feedback = review.replace(/^.*REDO:\s*/i, '');
59
- const retry = await agent.send(
60
- `Your work on "${task.description}" needs revision. Feedback from team lead:\n\n${feedback}\n\nPlease address this feedback and report your updated results.`,
61
- );
62
- task.result = retry;
63
- task.status = 'done'; // Accept after one retry
64
- log.info({ taskId: task.id }, 'Task completed after revision');
65
- } else {
66
- task.status = 'done';
67
- }
68
- } catch (err) {
69
- log.error({ err, taskId: task.id }, 'Task execution failed');
70
- task.status = 'failed';
71
- task.result = `Error: ${err instanceof Error ? err.message : String(err)}`;
72
- }
73
- }
74
-
75
- // Move to reviewing phase
76
- await transitionInstance(instance.id, 'reviewing');
77
-
78
- // Team lead does final review
79
- const taskSummary = instance.tasks
80
- .map((t) => `- [${t.status}] ${t.description} (${t.assignedTo})`)
81
- .join('\n');
82
-
83
- const finalReview = await teamLead.send(
84
- `All tasks have been executed. Here's the summary:\n\n${taskSummary}\n\nProvide a final assessment. Should we proceed to create a PR, or are there critical issues? Reply with:\n- "READY_FOR_PR: <PR title>" if ready\n- "NEEDS_WORK: <what's missing>" if not ready`,
85
- );
86
-
87
- if (finalReview.toUpperCase().includes('READY_FOR_PR:')) {
88
- instance.meetingLog.push(`[team-lead] Final review: ${finalReview}`);
89
- } else {
90
- // Mark complete anyway — we don't loop indefinitely
91
- instance.meetingLog.push(`[team-lead] Final review (issues noted): ${finalReview}`);
92
- }
93
- }
94
-
95
- function buildTaskPrompt(task: InstanceTask, workingDir: string, instance: Instance): string {
96
- const parts = ['You have been assigned the following task:', `\nTask: ${task.description}`];
97
-
98
- if (workingDir) {
99
- parts.push(`\nWorking directory: ${workingDir}`);
100
- }
101
-
102
- if (instance.issueRef) {
103
- parts.push(`\nRelated issue: ${instance.issueRef}`);
104
- }
105
-
106
- parts.push(
107
- '\nComplete the task to the best of your ability. Use your available tools to read, edit, and test code as needed. Report back with a summary of what you did.',
108
- );
109
-
110
- return parts.join('');
111
- }
@@ -1,138 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import { existsSync, rmSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { createChildLogger } from '../../logging/logger.js';
5
-
6
- const logger = () => createChildLogger('worktree');
7
-
8
- export interface WorktreeInfo {
9
- path: string;
10
- branch: string;
11
- }
12
-
13
- /**
14
- * Create a git worktree for an instance.
15
- * Branch naming: io/{squad-name}/{short-id}
16
- */
17
- export function createWorktree(params: {
18
- repoPath: string;
19
- squadName: string;
20
- instanceId: string;
21
- }): WorktreeInfo {
22
- const log = logger();
23
- const shortId = params.instanceId.slice(0, 8);
24
- const branch = `io/${params.squadName}/${shortId}`;
25
- const worktreePath = join(
26
- params.repoPath,
27
- '..',
28
- '.io-worktrees',
29
- `${params.squadName}-${shortId}`,
30
- );
31
-
32
- // Ensure the repo is a git repository
33
- if (!existsSync(join(params.repoPath, '.git'))) {
34
- throw new Error(`Not a git repository: ${params.repoPath}`);
35
- }
36
-
37
- // Create the worktree with a new branch from HEAD
38
- try {
39
- execSync(`git worktree add -b "${branch}" "${worktreePath}"`, {
40
- cwd: params.repoPath,
41
- stdio: 'pipe',
42
- encoding: 'utf-8',
43
- });
44
- } catch (err) {
45
- // Branch might already exist — try without -b
46
- try {
47
- execSync(`git worktree add "${worktreePath}" "${branch}"`, {
48
- cwd: params.repoPath,
49
- stdio: 'pipe',
50
- encoding: 'utf-8',
51
- });
52
- } catch (innerErr) {
53
- throw new Error(
54
- `Failed to create worktree: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`,
55
- );
56
- }
57
- }
58
-
59
- log.info({ worktreePath, branch }, 'Worktree created');
60
- return { path: worktreePath, branch };
61
- }
62
-
63
- /**
64
- * Remove a git worktree and delete its branch.
65
- */
66
- export function removeWorktree(params: {
67
- repoPath: string;
68
- worktreePath: string;
69
- branch: string;
70
- }): void {
71
- const log = logger();
72
-
73
- try {
74
- execSync(`git worktree remove "${params.worktreePath}" --force`, {
75
- cwd: params.repoPath,
76
- stdio: 'pipe',
77
- encoding: 'utf-8',
78
- });
79
- } catch {
80
- // Worktree might already be removed; try cleaning up the directory
81
- if (existsSync(params.worktreePath)) {
82
- rmSync(params.worktreePath, { recursive: true, force: true });
83
- }
84
- // Prune worktree references
85
- try {
86
- execSync('git worktree prune', { cwd: params.repoPath, stdio: 'pipe' });
87
- } catch {
88
- // ignore
89
- }
90
- }
91
-
92
- // Delete the branch
93
- try {
94
- execSync(`git branch -D "${params.branch}"`, {
95
- cwd: params.repoPath,
96
- stdio: 'pipe',
97
- encoding: 'utf-8',
98
- });
99
- } catch {
100
- // Branch might not exist or be checked out elsewhere
101
- }
102
-
103
- log.info({ branch: params.branch }, 'Worktree removed');
104
- }
105
-
106
- /**
107
- * List all IO-managed worktrees for a repository.
108
- */
109
- export function listWorktrees(repoPath: string): WorktreeInfo[] {
110
- try {
111
- const output = execSync('git worktree list --porcelain', {
112
- cwd: repoPath,
113
- stdio: 'pipe',
114
- encoding: 'utf-8',
115
- });
116
-
117
- const worktrees: WorktreeInfo[] = [];
118
- const blocks = output.split('\n\n');
119
-
120
- for (const block of blocks) {
121
- const lines = block.trim().split('\n');
122
- const pathLine = lines.find((l) => l.startsWith('worktree '));
123
- const branchLine = lines.find((l) => l.startsWith('branch '));
124
-
125
- if (pathLine && branchLine) {
126
- const path = pathLine.replace('worktree ', '');
127
- const branch = branchLine.replace('branch refs/heads/', '');
128
- if (branch.startsWith('io/')) {
129
- worktrees.push({ path, branch });
130
- }
131
- }
132
- }
133
-
134
- return worktrees;
135
- } catch {
136
- return [];
137
- }
138
- }
@@ -1,222 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { basename, join } from 'node:path';
4
- import { createChildLogger } from '../logging/logger.js';
5
- import { ensureSquadWiki } from '../wiki/index.js';
6
- import { addMember, createSquad } from './manager.js';
7
- import { generateSquadNames } from './name-generator.js';
8
- import { QA_TESTER_SKILL, SCRIBE_SKILL, TEAM_LEAD_SKILL } from './roles/templates.js';
9
- import { parseSkillContent } from './skill-parser.js';
10
-
11
- const logger = () => createChildLogger('hiring');
12
-
13
- interface ProjectAnalysis {
14
- name: string;
15
- languages: string[];
16
- frameworks: string[];
17
- hasTests: boolean;
18
- hasCi: boolean;
19
- suggestedSpecialists: string[];
20
- }
21
-
22
- /**
23
- * Analyze a project directory to determine what kind of squad it needs.
24
- */
25
- export function analyzeProject(projectPath: string): ProjectAnalysis {
26
- const name = basename(projectPath);
27
- const languages: string[] = [];
28
- const frameworks: string[] = [];
29
- let hasTests = false;
30
- let hasCi = false;
31
-
32
- // Check for common project indicators
33
- const files = safeReadDir(projectPath);
34
-
35
- // Language detection
36
- if (files.includes('package.json')) {
37
- languages.push('TypeScript/JavaScript');
38
- const pkg = safeReadJson(join(projectPath, 'package.json'));
39
- if (pkg) {
40
- const deps = (pkg.dependencies ?? {}) as Record<string, string>;
41
- const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;
42
- const allDeps = { ...deps, ...devDeps };
43
- if (allDeps.react || allDeps['react-dom']) frameworks.push('React');
44
- if (allDeps.vue) frameworks.push('Vue');
45
- if (allDeps.svelte) frameworks.push('Svelte');
46
- if (allDeps.next) frameworks.push('Next.js');
47
- if (allDeps.express || allDeps.fastify || allDeps.koa) frameworks.push('Node.js Backend');
48
- if (allDeps.vitest || allDeps.jest || allDeps.mocha) hasTests = true;
49
- }
50
- }
51
- if (files.includes('Cargo.toml')) languages.push('Rust');
52
- if (files.includes('go.mod')) languages.push('Go');
53
- if (files.includes('requirements.txt') || files.includes('pyproject.toml'))
54
- languages.push('Python');
55
- if (files.includes('Gemfile')) languages.push('Ruby');
56
- if (files.some((f) => f.endsWith('.csproj') || f.endsWith('.sln'))) languages.push('C#/.NET');
57
-
58
- // CI detection
59
- if (files.includes('.github')) {
60
- const ghDir = safeReadDir(join(projectPath, '.github'));
61
- if (ghDir.includes('workflows')) hasCi = true;
62
- }
63
-
64
- // Suggest specialists based on detected tech
65
- const suggestedSpecialists: string[] = [];
66
- if (frameworks.includes('React') || frameworks.includes('Vue') || frameworks.includes('Svelte'))
67
- suggestedSpecialists.push('frontend-developer');
68
- if (frameworks.includes('Node.js Backend')) suggestedSpecialists.push('backend-developer');
69
- if (languages.includes('Rust')) suggestedSpecialists.push('rust-developer');
70
- if (languages.includes('Go')) suggestedSpecialists.push('go-developer');
71
- if (languages.includes('Python')) suggestedSpecialists.push('python-developer');
72
- if (languages.includes('C#/.NET')) suggestedSpecialists.push('dotnet-developer');
73
-
74
- // If no specialists detected, add a generic one
75
- if (suggestedSpecialists.length === 0 && languages.length > 0) {
76
- suggestedSpecialists.push('developer');
77
- }
78
-
79
- return { name, languages, frameworks, hasTests, hasCi, suggestedSpecialists };
80
- }
81
-
82
- /**
83
- * Generate a specialist SKILL.md from a role name and detected context.
84
- */
85
- function generateSpecialistSkill(role: string, analysis: ProjectAnalysis): string {
86
- const langContext = analysis.languages.join(', ');
87
- const frameworkContext =
88
- analysis.frameworks.length > 0 ? `\nFrameworks: ${analysis.frameworks.join(', ')}` : '';
89
-
90
- return `---
91
- role: ${role}
92
- tools:
93
- - read_file
94
- - edit_file
95
- - run_command
96
- - search_code
97
- veto: false
98
- ---
99
-
100
- # ${titleCase(role)}
101
-
102
- ## Identity
103
- You are a ${titleCase(role)} specializing in ${langContext}.${frameworkContext}
104
-
105
- ## Responsibilities
106
- - Implement features and fix bugs assigned by the Team Lead
107
- - Write clean, well-tested code following project conventions
108
- - Run tests before submitting work for review
109
- - Respond to code review feedback promptly
110
-
111
- ## Boundaries
112
- - Only work on tasks assigned to you by the Team Lead
113
- - Do NOT modify files outside your area of expertise unless directed
114
- - Do NOT merge PRs — submit work for team lead review
115
- - Always run the test suite before reporting task completion
116
-
117
- ## Project Context
118
- Languages: ${langContext}${frameworkContext}
119
- `;
120
- }
121
-
122
- /**
123
- * Execute the full squad hiring flow:
124
- * 1. Analyze the project
125
- * 2. Create the squad in DB
126
- * 3. Write SKILL.md files to disk
127
- * 4. Add all members
128
- */
129
- export async function hireSquad(params: {
130
- projectPath: string;
131
- repoUrl?: string;
132
- name?: string;
133
- universe?: string;
134
- }): Promise<{ squadId: string; analysis: ProjectAnalysis; members: string[]; universe: string }> {
135
- const log = logger();
136
-
137
- // 1. Analyze
138
- const analysis = analyzeProject(params.projectPath);
139
- const squadName = params.name ?? analysis.name;
140
- log.info({ squadName, analysis }, 'Project analyzed');
141
-
142
- // 2. Build skill files
143
- const skillsDir = join(homedir(), '.io', 'squads', squadName);
144
- mkdirSync(skillsDir, { recursive: true });
145
-
146
- const skillFiles: { role: string; content: string; veto: boolean }[] = [
147
- { role: 'team-lead', content: TEAM_LEAD_SKILL, veto: true },
148
- { role: 'scribe', content: SCRIBE_SKILL, veto: false },
149
- { role: 'qa-tester', content: QA_TESTER_SKILL, veto: true },
150
- ];
151
-
152
- for (const specialist of analysis.suggestedSpecialists) {
153
- skillFiles.push({
154
- role: specialist,
155
- content: generateSpecialistSkill(specialist, analysis),
156
- veto: false,
157
- });
158
- }
159
-
160
- // 3. Generate character names from universe via LLM
161
- const allRoles = skillFiles.map((f) => f.role);
162
- const generated = await generateSquadNames(allRoles, params.universe);
163
- log.info({ squadName, universe: generated.universe }, 'Universe names generated');
164
-
165
- // 4. Create squad
166
- const squad = await createSquad({
167
- name: squadName,
168
- projectPath: params.projectPath,
169
- repoUrl: params.repoUrl,
170
- universe: generated.universe,
171
- });
172
-
173
- // 5. Create wiki folder for this squad
174
- ensureSquadWiki(squadName);
175
-
176
- // 6. Write files and add members
177
- const memberRoles: string[] = [];
178
- for (const { role, content, veto } of skillFiles) {
179
- const filePath = join(skillsDir, `${role}.skill.md`);
180
- writeFileSync(filePath, content, 'utf-8');
181
-
182
- const skill = parseSkillContent(content, filePath);
183
- const assignment = generated.assignments.find((a) => a.role === role);
184
- const displayName = assignment?.displayName ?? role;
185
- const persona = assignment?.persona;
186
- await addMember({ squadId: squad.id, skill, displayName, persona, isVetoMember: veto });
187
- memberRoles.push(`${displayName} (${role})`);
188
- }
189
-
190
- log.info(
191
- { squadId: squad.id, members: memberRoles, universe: generated.universe },
192
- 'Squad hired successfully',
193
- );
194
- return { squadId: squad.id, analysis, members: memberRoles, universe: generated.universe };
195
- }
196
-
197
- // Helpers
198
-
199
- function safeReadDir(dir: string): string[] {
200
- try {
201
- if (!existsSync(dir)) return [];
202
- return readdirSync(dir);
203
- } catch {
204
- return [];
205
- }
206
- }
207
-
208
- function safeReadJson(path: string): Record<string, unknown> | null {
209
- try {
210
- if (!existsSync(path)) return null;
211
- return JSON.parse(readFileSync(path, 'utf-8'));
212
- } catch {
213
- return null;
214
- }
215
- }
216
-
217
- function titleCase(str: string): string {
218
- return str
219
- .split('-')
220
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
221
- .join(' ');
222
- }
@@ -1,17 +0,0 @@
1
- export { EventBus, getEventBus } from './event-bus.js';
2
- export { parseSkillFile, parseSkillContent, compileSystemPrompt } from './skill-parser.js';
3
- export type { SkillDefinition } from './skill-parser.js';
4
- export { Agent } from './agent.js';
5
- export type { AgentConfig } from './agent.js';
6
- export {
7
- createSquad,
8
- addMember,
9
- listSquads,
10
- getSquadByName,
11
- getSquadMembers,
12
- disbandSquad,
13
- bootSquad,
14
- getSquadRuntime,
15
- delegateToSquad,
16
- } from './manager.js';
17
- export { hireSquad, analyzeProject } from './hiring.js';