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,707 +0,0 @@
1
- import { defineTool } from '@github/copilot-sdk';
2
- import { z } from 'zod';
3
- import {
4
- activateSkill,
5
- deactivateSkill,
6
- getActiveSkills,
7
- installSkillFromUrl,
8
- listInstalledSkills,
9
- removeSkill,
10
- } from '../skills/index.js';
11
- import { runInstance } from '../squad/execution/runner.js';
12
- import { hireSquad } from '../squad/hiring.js';
13
- import {
14
- bootSquad,
15
- delegateToSquad,
16
- getSquadByName,
17
- getSquadMembers,
18
- getSquadRuntime,
19
- listSquads,
20
- rethemeSquad,
21
- } from '../squad/manager.js';
22
- import { listInboxEntries, resolveInboxEntry } from '../store/inbox.js';
23
- import { createSchedule, deleteSchedule, listSchedules } from '../store/schedules.js';
24
- import {
25
- getOrchestratorScopes,
26
- getPageListing,
27
- listWikiPages,
28
- readWikiPage,
29
- searchWiki,
30
- writeWikiPage,
31
- } from '../wiki/index.js';
32
-
33
- export function createOrchestratorTools() {
34
- return [
35
- defineTool('list_squads', {
36
- description:
37
- 'List all active squads and their current status. Use this when the user asks about their teams or projects.',
38
- parameters: z.object({}).strict(),
39
- handler: async () => {
40
- const squads = await listSquads();
41
- if (squads.length === 0) {
42
- return {
43
- textResultForLlm: JSON.stringify({
44
- squads: [],
45
- message: 'No squads currently active.',
46
- }),
47
- resultType: 'success' as const,
48
- };
49
- }
50
- const summary = await Promise.all(
51
- squads.map(async (s) => {
52
- const members = await getSquadMembers(s.id);
53
- return {
54
- name: s.name,
55
- project: s.projectPath,
56
- universe: s.universe,
57
- autonomy: s.autonomyTier,
58
- members: members.map((m) => `${m.displayName} (${m.roleName})`),
59
- status: s.status,
60
- };
61
- }),
62
- );
63
- return {
64
- textResultForLlm: JSON.stringify({ squads: summary }),
65
- resultType: 'success' as const,
66
- };
67
- },
68
- }),
69
-
70
- defineTool('get_squad_status', {
71
- description:
72
- 'Get the detailed status of a specific squad including active instances, team members, and recent activity.',
73
- parameters: z.object({
74
- squadName: z.string().describe('The name of the squad to check'),
75
- }),
76
- handler: async (args: { squadName: string }) => {
77
- const squad = await getSquadByName(args.squadName);
78
- if (!squad) {
79
- return {
80
- textResultForLlm: JSON.stringify({ error: `Squad '${args.squadName}' not found.` }),
81
- resultType: 'success' as const,
82
- };
83
- }
84
- const members = await getSquadMembers(squad.id);
85
- return {
86
- textResultForLlm: JSON.stringify({
87
- squad: {
88
- name: squad.name,
89
- project: squad.projectPath,
90
- repo: squad.repoUrl,
91
- universe: squad.universe,
92
- autonomy: squad.autonomyTier,
93
- status: squad.status,
94
- createdAt: squad.createdAt.toISOString(),
95
- },
96
- members: members.map((m) => ({
97
- name: m.displayName,
98
- role: m.roleName,
99
- veto: m.isVetoMember,
100
- tools: m.toolsAllowed,
101
- })),
102
- }),
103
- resultType: 'success' as const,
104
- };
105
- },
106
- }),
107
-
108
- defineTool('hire_squad', {
109
- description:
110
- 'Create a new squad for a project. Provide a GitHub repo URL and the project will be cloned automatically to ~/.io/source/{owner}/{repo}. Analyzes the project and recommends team composition. Each squad member gets a character name from a pop-culture universe.',
111
- parameters: z.object({
112
- repoUrl: z.string().describe('GitHub repository URL (e.g. https://github.com/owner/repo)'),
113
- name: z.string().optional().describe('Name for the squad (auto-generated if omitted)'),
114
- universe: z
115
- .string()
116
- .optional()
117
- .describe(
118
- 'Pop-culture universe for member names (a-team, marvel, star-wars, lord-of-the-rings, star-trek, firefly). Random if omitted.',
119
- ),
120
- }),
121
- handler: async (args: { repoUrl: string; name?: string; universe?: string }) => {
122
- try {
123
- const { ensureCloned } = await import('../squad/source-resolver.js');
124
- const projectPath = ensureCloned(args.repoUrl);
125
- const result = await hireSquad({
126
- projectPath,
127
- repoUrl: args.repoUrl,
128
- name: args.name,
129
- universe: args.universe,
130
- });
131
- return {
132
- textResultForLlm: JSON.stringify({
133
- message: `Squad '${args.name ?? result.analysis.name}' hired successfully!`,
134
- squadId: result.squadId,
135
- projectPath,
136
- universe: result.universe,
137
- analysis: result.analysis,
138
- members: result.members,
139
- }),
140
- resultType: 'success' as const,
141
- };
142
- } catch (err) {
143
- return {
144
- textResultForLlm: JSON.stringify({
145
- error: `Failed to hire squad: ${err instanceof Error ? err.message : String(err)}`,
146
- }),
147
- resultType: 'success' as const,
148
- };
149
- }
150
- },
151
- }),
152
-
153
- defineTool('retheme_squad', {
154
- description:
155
- "Change a squad's pop-culture universe. Generates new character names and personas for all members from the specified universe.",
156
- parameters: z.object({
157
- squadName: z.string().describe('Name of the squad to retheme'),
158
- universe: z
159
- .string()
160
- .describe('New pop-culture universe (e.g. "The Office", "Star Wars", "Breaking Bad")'),
161
- }),
162
- handler: async (args: { squadName: string; universe: string }) => {
163
- try {
164
- const squad = await getSquadByName(args.squadName);
165
- if (!squad) {
166
- return {
167
- textResultForLlm: JSON.stringify({ error: `Squad '${args.squadName}' not found.` }),
168
- resultType: 'success' as const,
169
- };
170
- }
171
- const members = await getSquadMembers(squad.id);
172
- const roles = members.map((m) => m.roleName);
173
-
174
- const { generateSquadNames } = await import('../squad/name-generator.js');
175
- const generated = await generateSquadNames(roles, args.universe);
176
-
177
- await rethemeSquad(squad.id, generated.universe, generated.assignments);
178
-
179
- return {
180
- textResultForLlm: JSON.stringify({
181
- message: `Squad '${args.squadName}' rethemed to ${generated.universe}!`,
182
- members: generated.assignments.map((a) => `${a.displayName} (${a.role})`),
183
- }),
184
- resultType: 'success' as const,
185
- };
186
- } catch (err) {
187
- return {
188
- textResultForLlm: JSON.stringify({
189
- error: `Failed to retheme squad: ${err instanceof Error ? err.message : String(err)}`,
190
- }),
191
- resultType: 'success' as const,
192
- };
193
- }
194
- },
195
- }),
196
-
197
- defineTool('delegate_to_squad', {
198
- description:
199
- "Delegate a message or task to a specific squad's team lead. Use this when the user's message relates to a project that has an assigned squad.",
200
- parameters: z.object({
201
- squadName: z.string().describe('Name of the squad to delegate to'),
202
- message: z.string().describe('The full message or task to delegate'),
203
- }),
204
- handler: async (args: { squadName: string; message: string }) => {
205
- const squad = await getSquadByName(args.squadName);
206
- if (!squad) {
207
- return {
208
- textResultForLlm: JSON.stringify({
209
- error: `Squad '${args.squadName}' not found.`,
210
- }),
211
- resultType: 'success' as const,
212
- };
213
- }
214
-
215
- try {
216
- // Boot squad if not already running
217
- if (!getSquadRuntime(squad.id)) {
218
- await bootSquad(squad);
219
- }
220
-
221
- const response = await delegateToSquad(squad.id, args.message);
222
- return {
223
- textResultForLlm: JSON.stringify({
224
- delegatedTo: args.squadName,
225
- teamLeadResponse: response,
226
- }),
227
- resultType: 'success' as const,
228
- };
229
- } catch (err) {
230
- return {
231
- textResultForLlm: JSON.stringify({
232
- error: `Failed to delegate: ${err instanceof Error ? err.message : String(err)}`,
233
- }),
234
- resultType: 'success' as const,
235
- };
236
- }
237
- },
238
- }),
239
-
240
- defineTool('run_squad_instance', {
241
- description:
242
- 'Start a new work instance for a squad. This kicks off the full lifecycle: meeting → task execution → PR creation. Use when the user asks a squad to work on something specific.',
243
- parameters: z.object({
244
- squadName: z.string().describe('Name of the squad'),
245
- objective: z.string().describe('What the squad should accomplish'),
246
- issueRef: z.string().optional().describe('GitHub issue reference (e.g., #42)'),
247
- }),
248
- handler: async (args: { squadName: string; objective: string; issueRef?: string }) => {
249
- const squad = await getSquadByName(args.squadName);
250
- if (!squad) {
251
- return {
252
- textResultForLlm: JSON.stringify({ error: `Squad '${args.squadName}' not found.` }),
253
- resultType: 'success' as const,
254
- };
255
- }
256
-
257
- try {
258
- const result = await runInstance({
259
- squad,
260
- objective: args.objective,
261
- issueRef: args.issueRef,
262
- });
263
-
264
- return {
265
- textResultForLlm: JSON.stringify({
266
- instanceId: result.instanceId,
267
- success: result.success,
268
- pr: result.pr ? { url: result.pr.url, number: result.pr.number } : null,
269
- error: result.error,
270
- }),
271
- resultType: 'success' as const,
272
- };
273
- } catch (err) {
274
- return {
275
- textResultForLlm: JSON.stringify({
276
- error: `Failed to run instance: ${err instanceof Error ? err.message : String(err)}`,
277
- }),
278
- resultType: 'success' as const,
279
- };
280
- }
281
- },
282
- }),
283
-
284
- defineTool('list_inbox', {
285
- description:
286
- "List unread inbox entries from squads. Shows deliverables and pending questions that need the user's attention.",
287
- parameters: z.object({
288
- status: z
289
- .enum(['unread', 'read', 'resolved'])
290
- .optional()
291
- .describe('Filter by status (default: unread)'),
292
- }),
293
- handler: async (args: { status?: 'unread' | 'read' | 'resolved' }) => {
294
- const entries = await listInboxEntries({
295
- status: args.status ?? 'unread',
296
- limit: 20,
297
- });
298
-
299
- if (entries.length === 0) {
300
- return {
301
- textResultForLlm: JSON.stringify({ entries: [], message: 'No inbox entries.' }),
302
- resultType: 'success' as const,
303
- };
304
- }
305
-
306
- const summary = entries.map((e) => ({
307
- id: e.id,
308
- squad: e.squadId,
309
- kind: e.kind,
310
- title: e.title,
311
- content: e.content.slice(0, 500),
312
- status: e.status,
313
- createdAt: e.createdAt,
314
- }));
315
-
316
- return {
317
- textResultForLlm: JSON.stringify({ entries: summary }),
318
- resultType: 'success' as const,
319
- };
320
- },
321
- }),
322
-
323
- defineTool('respond_to_inbox', {
324
- description:
325
- "Respond to an inbox question from a squad. Use this when the user provides an answer to a squad's pending question. This unblocks the squad so it can continue working.",
326
- parameters: z.object({
327
- entryId: z.string().describe('The inbox entry ID to respond to'),
328
- response: z.string().describe("The user's response to the squad's question"),
329
- }),
330
- handler: async (args: { entryId: string; response: string }) => {
331
- try {
332
- const unblocked = await resolveInboxEntry(args.entryId, args.response);
333
- return {
334
- textResultForLlm: JSON.stringify({
335
- resolved: true,
336
- squadUnblocked: unblocked,
337
- message: unblocked
338
- ? 'Response delivered — squad has been unblocked and will continue working.'
339
- : 'Response recorded (squad was not actively waiting).',
340
- }),
341
- resultType: 'success' as const,
342
- };
343
- } catch (err) {
344
- return {
345
- textResultForLlm: JSON.stringify({
346
- error: `Failed to respond: ${err instanceof Error ? err.message : String(err)}`,
347
- }),
348
- resultType: 'success' as const,
349
- };
350
- }
351
- },
352
- }),
353
-
354
- defineTool('create_schedule', {
355
- description:
356
- 'Create a cron-based schedule that triggers a squad or the orchestrator with a predefined prompt at specified times. Use standard cron syntax (e.g., "0 9 * * 1-5" for weekdays at 9am).',
357
- parameters: z.object({
358
- name: z.string().describe('Human-readable name for the schedule (e.g., "Daily Standup")'),
359
- targetType: z
360
- .enum(['squad', 'orchestrator'])
361
- .describe('Whether to target a squad or the orchestrator'),
362
- targetId: z.string().optional().describe('Squad name (required if targetType is "squad")'),
363
- cron: z.string().describe('Cron expression (e.g., "0 9 * * 1-5" for weekdays at 9am)'),
364
- prompt: z.string().describe('The prompt/message to send when the schedule fires'),
365
- }),
366
- handler: async (args: {
367
- name: string;
368
- targetType: 'squad' | 'orchestrator';
369
- targetId?: string;
370
- cron: string;
371
- prompt: string;
372
- }) => {
373
- try {
374
- // Validate squad exists if targeting a squad
375
- if (args.targetType === 'squad') {
376
- if (!args.targetId) {
377
- return {
378
- textResultForLlm: JSON.stringify({
379
- error: 'targetId (squad name) is required for squad schedules',
380
- }),
381
- resultType: 'success' as const,
382
- };
383
- }
384
- const squad = await getSquadByName(args.targetId);
385
- if (!squad) {
386
- return {
387
- textResultForLlm: JSON.stringify({ error: `Squad '${args.targetId}' not found` }),
388
- resultType: 'success' as const,
389
- };
390
- }
391
- args.targetId = squad.id;
392
- }
393
-
394
- const schedule = await createSchedule({
395
- name: args.name,
396
- targetType: args.targetType,
397
- targetId: args.targetId,
398
- cron: args.cron,
399
- prompt: args.prompt,
400
- });
401
-
402
- return {
403
- textResultForLlm: JSON.stringify({
404
- created: true,
405
- schedule: {
406
- id: schedule.id,
407
- name: schedule.name,
408
- cron: schedule.cron,
409
- nextRun: schedule.nextRun,
410
- },
411
- }),
412
- resultType: 'success' as const,
413
- };
414
- } catch (err) {
415
- return {
416
- textResultForLlm: JSON.stringify({
417
- error: `Failed to create schedule: ${err instanceof Error ? err.message : String(err)}`,
418
- }),
419
- resultType: 'success' as const,
420
- };
421
- }
422
- },
423
- }),
424
-
425
- defineTool('list_schedules', {
426
- description: 'List all configured schedules (cron-based automations).',
427
- parameters: z.object({}).strict(),
428
- handler: async () => {
429
- const schedules = await listSchedules();
430
- if (schedules.length === 0) {
431
- return {
432
- textResultForLlm: JSON.stringify({
433
- schedules: [],
434
- message: 'No schedules configured.',
435
- }),
436
- resultType: 'success' as const,
437
- };
438
- }
439
-
440
- const summary = schedules.map((s) => ({
441
- id: s.id,
442
- name: s.name,
443
- targetType: s.targetType,
444
- targetId: s.targetId,
445
- cron: s.cron,
446
- prompt: s.prompt.slice(0, 100),
447
- enabled: s.enabled,
448
- nextRun: s.nextRun,
449
- lastRun: s.lastRun,
450
- }));
451
-
452
- return {
453
- textResultForLlm: JSON.stringify({ schedules: summary }),
454
- resultType: 'success' as const,
455
- };
456
- },
457
- }),
458
-
459
- defineTool('delete_schedule', {
460
- description: 'Delete a schedule by ID. Use list_schedules first to find the ID.',
461
- parameters: z.object({
462
- scheduleId: z.string().describe('The ID of the schedule to delete'),
463
- }),
464
- handler: async (args: { scheduleId: string }) => {
465
- try {
466
- await deleteSchedule(args.scheduleId);
467
- return {
468
- textResultForLlm: JSON.stringify({ deleted: true, scheduleId: args.scheduleId }),
469
- resultType: 'success' as const,
470
- };
471
- } catch (err) {
472
- return {
473
- textResultForLlm: JSON.stringify({
474
- error: `Failed to delete: ${err instanceof Error ? err.message : String(err)}`,
475
- }),
476
- resultType: 'success' as const,
477
- };
478
- }
479
- },
480
- }),
481
-
482
- defineTool('read_wiki', {
483
- description:
484
- 'Read from the wiki knowledge base. Call with no pageName to list available pages, or with a pageName to read its content. You have access to IO-level and Shared wiki scopes.',
485
- parameters: z.object({
486
- scope: z.enum(['io', 'shared']).describe('Which wiki scope to read from'),
487
- pageName: z
488
- .string()
489
- .optional()
490
- .describe('Page name to read (omit to list all pages in scope)'),
491
- }),
492
- handler: async (args: { scope: 'io' | 'shared'; pageName?: string }) => {
493
- if (!args.pageName) {
494
- const pages = listWikiPages(args.scope);
495
- return {
496
- textResultForLlm: JSON.stringify({
497
- scope: args.scope,
498
- pages: pages.length > 0 ? pages : '(empty)',
499
- }),
500
- resultType: 'success' as const,
501
- };
502
- }
503
- const page = readWikiPage(args.scope, args.pageName);
504
- if (!page) {
505
- return {
506
- textResultForLlm: JSON.stringify({
507
- error: `Page '${args.pageName}' not found in ${args.scope} wiki`,
508
- }),
509
- resultType: 'success' as const,
510
- };
511
- }
512
- return {
513
- textResultForLlm: JSON.stringify({
514
- scope: page.scope,
515
- name: page.name,
516
- content: page.content,
517
- }),
518
- resultType: 'success' as const,
519
- };
520
- },
521
- }),
522
-
523
- defineTool('write_wiki', {
524
- description:
525
- 'Write a page to the wiki knowledge base. Provide the full page content (read existing first and merge if updating). You can write to IO-level and Shared scopes.',
526
- parameters: z.object({
527
- scope: z.enum(['io', 'shared']).describe('Which wiki scope to write to'),
528
- pageName: z
529
- .string()
530
- .describe('Page name (no .md extension, e.g., "preferences" or "routing-conventions")'),
531
- content: z.string().describe('Full markdown content for the page'),
532
- }),
533
- handler: async (args: { scope: 'io' | 'shared'; pageName: string; content: string }) => {
534
- writeWikiPage(args.scope, args.pageName, args.content);
535
- return {
536
- textResultForLlm: JSON.stringify({
537
- written: true,
538
- scope: args.scope,
539
- pageName: args.pageName,
540
- }),
541
- resultType: 'success' as const,
542
- };
543
- },
544
- }),
545
-
546
- defineTool('search_wiki', {
547
- description: 'Search across wiki pages by keyword. Searches IO-level and Shared scopes.',
548
- parameters: z.object({
549
- keyword: z.string().describe('Keyword or phrase to search for'),
550
- }),
551
- handler: async (args: { keyword: string }) => {
552
- const results = searchWiki(args.keyword, getOrchestratorScopes());
553
- if (results.length === 0) {
554
- return {
555
- textResultForLlm: JSON.stringify({ results: [], message: 'No matches found.' }),
556
- resultType: 'success' as const,
557
- };
558
- }
559
- return {
560
- textResultForLlm: JSON.stringify({ results }),
561
- resultType: 'success' as const,
562
- };
563
- },
564
- }),
565
-
566
- defineTool('install_skill', {
567
- description:
568
- 'Install a skill from a URL (raw GitHub URL to a SKILL.md file). Skills extend IO or squad capabilities with additional instructions and behaviors.',
569
- parameters: z.object({
570
- name: z
571
- .string()
572
- .describe('Name for the skill (kebab-case, e.g., "tdd-workflow" or "code-review")'),
573
- url: z
574
- .string()
575
- .describe(
576
- 'URL to the raw SKILL.md content (e.g., raw.githubusercontent.com/.../SKILL.md)',
577
- ),
578
- }),
579
- handler: async (args: { name: string; url: string }) => {
580
- try {
581
- const skill = await installSkillFromUrl(args.name, args.url);
582
- return {
583
- textResultForLlm: JSON.stringify({
584
- installed: true,
585
- name: skill.name,
586
- preview: skill.content.slice(0, 200),
587
- }),
588
- resultType: 'success' as const,
589
- };
590
- } catch (err) {
591
- return {
592
- textResultForLlm: JSON.stringify({
593
- error: `Failed to install: ${err instanceof Error ? err.message : String(err)}`,
594
- }),
595
- resultType: 'success' as const,
596
- };
597
- }
598
- },
599
- }),
600
-
601
- defineTool('list_skills', {
602
- description: 'List all installed skills and their activation status.',
603
- parameters: z.object({}).strict(),
604
- handler: async () => {
605
- const installed = listInstalledSkills();
606
- const orchestratorActivations = await getActiveSkills('orchestrator');
607
-
608
- const summary = installed.map((s) => ({
609
- name: s.name,
610
- activatedForOrchestrator: orchestratorActivations.some((a) => a.skillName === s.name),
611
- preview: s.content.slice(0, 100),
612
- }));
613
-
614
- return {
615
- textResultForLlm: JSON.stringify({
616
- skills: summary.length > 0 ? summary : '(no skills installed)',
617
- }),
618
- resultType: 'success' as const,
619
- };
620
- },
621
- }),
622
-
623
- defineTool('activate_skill', {
624
- description:
625
- 'Activate an installed skill for the orchestrator or a specific squad. Active skills are injected into the system prompt.',
626
- parameters: z.object({
627
- skillName: z.string().describe('Name of the installed skill'),
628
- targetType: z
629
- .enum(['orchestrator', 'squad'])
630
- .describe('Activate for orchestrator or a specific squad'),
631
- targetId: z.string().optional().describe('Squad name (required if targetType is "squad")'),
632
- }),
633
- handler: async (args: {
634
- skillName: string;
635
- targetType: 'orchestrator' | 'squad';
636
- targetId?: string;
637
- }) => {
638
- try {
639
- let resolvedTargetId = args.targetId ?? null;
640
- if (args.targetType === 'squad' && args.targetId) {
641
- const squad = await getSquadByName(args.targetId);
642
- if (!squad) {
643
- return {
644
- textResultForLlm: JSON.stringify({ error: `Squad '${args.targetId}' not found` }),
645
- resultType: 'success' as const,
646
- };
647
- }
648
- resolvedTargetId = squad.id;
649
- }
650
-
651
- await activateSkill(args.skillName, args.targetType, resolvedTargetId ?? undefined);
652
- return {
653
- textResultForLlm: JSON.stringify({
654
- activated: true,
655
- skillName: args.skillName,
656
- target: args.targetType === 'orchestrator' ? 'orchestrator' : args.targetId,
657
- }),
658
- resultType: 'success' as const,
659
- };
660
- } catch (err) {
661
- return {
662
- textResultForLlm: JSON.stringify({
663
- error: `Failed to activate: ${err instanceof Error ? err.message : String(err)}`,
664
- }),
665
- resultType: 'success' as const,
666
- };
667
- }
668
- },
669
- }),
670
-
671
- defineTool('deactivate_skill', {
672
- description: 'Deactivate a skill (stop injecting it into the system prompt).',
673
- parameters: z.object({
674
- skillName: z.string().describe('Name of the skill to deactivate'),
675
- targetType: z
676
- .enum(['orchestrator', 'squad'])
677
- .describe('Deactivate from orchestrator or a specific squad'),
678
- targetId: z.string().optional().describe('Squad name (required if targetType is "squad")'),
679
- }),
680
- handler: async (args: {
681
- skillName: string;
682
- targetType: 'orchestrator' | 'squad';
683
- targetId?: string;
684
- }) => {
685
- await deactivateSkill(args.skillName, args.targetType, args.targetId ?? undefined);
686
- return {
687
- textResultForLlm: JSON.stringify({ deactivated: true, skillName: args.skillName }),
688
- resultType: 'success' as const,
689
- };
690
- },
691
- }),
692
-
693
- defineTool('remove_skill', {
694
- description: 'Uninstall a skill completely (removes files and all activations).',
695
- parameters: z.object({
696
- skillName: z.string().describe('Name of the skill to remove'),
697
- }),
698
- handler: async (args: { skillName: string }) => {
699
- removeSkill(args.skillName);
700
- return {
701
- textResultForLlm: JSON.stringify({ removed: true, skillName: args.skillName }),
702
- resultType: 'success' as const,
703
- };
704
- },
705
- }),
706
- ];
707
- }