cliskill 1.1.7 → 1.2.0

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/index.js CHANGED
@@ -28,11 +28,10 @@ import {
28
28
  renderInkApp,
29
29
  runAgentLoop,
30
30
  runCli
31
- } from "./chunk-TZMKZQ7N.js";
31
+ } from "./chunk-RLXS6WEQ.js";
32
32
  import {
33
- getGlobalSkillsDir,
34
33
  getHistoryPath
35
- } from "./chunk-S7IQHES2.js";
34
+ } from "./chunk-CISBSDJM.js";
36
35
 
37
36
  // src/memory/store.ts
38
37
  import { readFile, writeFile, mkdir, readdir, stat, unlink } from "fs/promises";
@@ -193,135 +192,767 @@ ${memory.content.slice(0, 500)}
193
192
  }
194
193
  };
195
194
 
196
- // src/extensions/skill-loader.ts
197
- import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
198
- import { join as join2 } from "path";
199
- import { existsSync as existsSync2 } from "fs";
200
- async function loadSkillsFromDir(dir) {
201
- if (!existsSync2(dir)) return [];
202
- const skills = [];
203
- const files = await readdir2(dir);
204
- for (const file of files) {
205
- if (!file.endsWith(".md")) continue;
206
- const filePath = join2(dir, file);
207
- const content = await readFile2(filePath, "utf-8");
208
- const skill = parseSkillMarkdown(content, filePath);
209
- if (skill) {
210
- skills.push(skill);
211
- }
212
- }
213
- return skills;
214
- }
215
- function parseSkillMarkdown(content, sourcePath) {
216
- const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
217
- if (!frontmatterMatch) {
218
- const name = sourcePath ? sourcePath.split(/[\\/]/).pop()?.replace(".md", "") ?? "unnamed" : "unnamed";
219
- return {
220
- name,
221
- description: `Skill: ${name}`,
222
- prompt: content.trim(),
223
- builtin: false,
224
- sourcePath
225
- };
226
- }
227
- const [, frontmatter, body] = frontmatterMatch;
228
- const metadata = parseSimpleYaml(frontmatter);
229
- if (!metadata.name) {
230
- metadata.name = sourcePath ? sourcePath.split(/[\\/]/).pop()?.replace(".md", "") ?? "unnamed" : "unnamed";
195
+ // src/extensions/builtin-skills.ts
196
+ var BUILT_IN_SKILLS = [
197
+ // ── 1. Code Review ────────────────────────────────────────────────
198
+ {
199
+ id: "builtin-code-review",
200
+ name: "Code Review",
201
+ version: "1.0.0",
202
+ description: "Deep code review with best practices, bug detection, and improvement suggestions",
203
+ icon: "\u{1F50D}",
204
+ category: "code-review",
205
+ tags: ["review", "quality", "bugs", "best-practices"],
206
+ author: "CliSkill",
207
+ source: "builtin",
208
+ modelPreference: "smart",
209
+ params: [
210
+ {
211
+ name: "filePath",
212
+ type: "file",
213
+ label: "File to review",
214
+ description: "Path to the file or directory to review",
215
+ required: true
216
+ },
217
+ {
218
+ name: "focus",
219
+ type: "select",
220
+ label: "Review focus",
221
+ description: "What aspect to focus on",
222
+ default: "all",
223
+ options: [
224
+ { value: "all", label: "All aspects" },
225
+ { value: "bugs", label: "Bug detection" },
226
+ { value: "security", label: "Security issues" },
227
+ { value: "performance", label: "Performance" },
228
+ { value: "style", label: "Code style" },
229
+ { value: "architecture", label: "Architecture" }
230
+ ]
231
+ },
232
+ {
233
+ name: "severity",
234
+ type: "select",
235
+ label: "Minimum severity",
236
+ default: "warning",
237
+ options: [
238
+ { value: "info", label: "Info (all issues)" },
239
+ { value: "warning", label: "Warning+" },
240
+ { value: "critical", label: "Critical only" }
241
+ ]
242
+ }
243
+ ],
244
+ steps: [
245
+ {
246
+ id: "analyze",
247
+ title: "Analyze code structure",
248
+ prompt: `Perform a thorough code review of {{filePath}}.
249
+
250
+ Focus: {{focus}}
251
+ Minimum severity: {{severity}}
252
+
253
+ Analyze the code and provide:
254
+ 1. **Critical issues** \u2014 bugs, security vulnerabilities, logic errors
255
+ 2. **Warnings** \u2014 potential problems, edge cases, missing error handling
256
+ 3. **Suggestions** \u2014 code quality improvements, readability, best practices
257
+ 4. **Positive notes** \u2014 good patterns found in the code
258
+
259
+ Format each finding as:
260
+ - **[SEVERITY]** Category: Description (line X)
261
+ Suggestion: How to fix
262
+
263
+ Rate overall code quality on a scale of 1-10 and provide a summary.`,
264
+ tools: ["file_read", "file_outline", "search_symbol", "grep", "glob"]
265
+ }
266
+ ],
267
+ outputs: [{ type: "markdown", description: "Detailed code review report" }]
268
+ },
269
+ // ── 2. Refactor ───────────────────────────────────────────────────
270
+ {
271
+ id: "builtin-refactor",
272
+ name: "Smart Refactor",
273
+ version: "1.0.0",
274
+ description: "Intelligent code refactoring with pattern detection and safe transformations",
275
+ icon: "\u{1F527}",
276
+ category: "refactoring",
277
+ tags: ["refactor", "clean-code", "patterns", "design"],
278
+ author: "CliSkill",
279
+ source: "builtin",
280
+ modelPreference: "smart",
281
+ params: [
282
+ {
283
+ name: "filePath",
284
+ type: "file",
285
+ label: "File or directory",
286
+ required: true
287
+ },
288
+ {
289
+ name: "goal",
290
+ type: "select",
291
+ label: "Refactoring goal",
292
+ default: "clean",
293
+ options: [
294
+ { value: "clean", label: "Clean up code" },
295
+ { value: "patterns", label: "Apply design patterns" },
296
+ { value: "extract", label: "Extract functions/methods" },
297
+ { value: "simplify", label: "Simplify logic" },
298
+ { value: "modernize", label: "Modernize syntax" },
299
+ { value: "modularize", label: "Split into modules" }
300
+ ]
301
+ },
302
+ {
303
+ name: "aggressiveness",
304
+ type: "select",
305
+ label: "Aggressiveness",
306
+ default: "moderate",
307
+ options: [
308
+ { value: "conservative", label: "Conservative (minimal changes)" },
309
+ { value: "moderate", label: "Moderate" },
310
+ { value: "aggressive", label: "Aggressive (full rewrite)" }
311
+ ]
312
+ }
313
+ ],
314
+ steps: [
315
+ {
316
+ id: "plan",
317
+ title: "Analyze and plan refactoring",
318
+ prompt: `Analyze {{filePath}} for refactoring opportunities.
319
+
320
+ Goal: {{goal}}
321
+ Aggressiveness: {{aggressiveness}}
322
+
323
+ First, read the code and identify:
324
+ 1. Code smells and anti-patterns
325
+ 2. Duplicated logic
326
+ 3. Overly complex functions
327
+ 4. Poor naming
328
+ 5. Missing abstractions
329
+
330
+ Then create a refactoring plan listing specific changes. Do NOT make changes yet \u2014 only plan.`,
331
+ tools: ["file_read", "file_outline", "search_symbol", "grep", "glob"]
332
+ },
333
+ {
334
+ id: "execute",
335
+ title: "Execute refactoring",
336
+ prompt: `Now execute the refactoring plan for {{filePath}}.
337
+
338
+ Goal: {{goal}}
339
+ Aggressiveness: {{aggressiveness}}
340
+
341
+ Apply the planned changes. Make sure to:
342
+ - Preserve all existing functionality
343
+ - Keep imports and exports intact
344
+ - Maintain backward compatibility
345
+ - Add comments only where logic is non-obvious
346
+
347
+ After each change, verify the file still has valid syntax.`,
348
+ tools: ["file_read", "file_edit", "file_write", "bash"],
349
+ timeout: 120
350
+ }
351
+ ],
352
+ outputs: [
353
+ { type: "diff", description: "Changes made during refactoring" },
354
+ { type: "markdown", description: "Summary of refactoring changes" }
355
+ ]
356
+ },
357
+ // ── 3. Test Generator ─────────────────────────────────────────────
358
+ {
359
+ id: "builtin-test-gen",
360
+ name: "Test Generator",
361
+ version: "1.0.0",
362
+ description: "Generate comprehensive unit and integration tests for your code",
363
+ icon: "\u{1F9EA}",
364
+ category: "testing",
365
+ tags: ["tests", "unit-tests", "integration", "coverage"],
366
+ author: "CliSkill",
367
+ source: "builtin",
368
+ modelPreference: "code",
369
+ params: [
370
+ {
371
+ name: "filePath",
372
+ type: "file",
373
+ label: "File to test",
374
+ required: true
375
+ },
376
+ {
377
+ name: "framework",
378
+ type: "select",
379
+ label: "Test framework",
380
+ default: "auto",
381
+ options: [
382
+ { value: "auto", label: "Auto-detect" },
383
+ { value: "jest", label: "Jest" },
384
+ { value: "vitest", label: "Vitest" },
385
+ { value: "pytest", label: "Pytest" },
386
+ { value: "go", label: "Go testing" },
387
+ { value: "rust", label: "Rust #[test]" }
388
+ ]
389
+ },
390
+ {
391
+ name: "coverage",
392
+ type: "select",
393
+ label: "Coverage level",
394
+ default: "comprehensive",
395
+ options: [
396
+ { value: "basic", label: "Basic (happy path)" },
397
+ { value: "comprehensive", label: "Comprehensive (edge cases)" },
398
+ { value: "exhaustive", label: "Exhaustive (all paths)" }
399
+ ]
400
+ }
401
+ ],
402
+ steps: [
403
+ {
404
+ id: "analyze",
405
+ title: "Analyze code for testability",
406
+ prompt: `Analyze {{filePath}} to identify all testable units (functions, methods, classes).
407
+
408
+ Framework: {{framework}}
409
+ Coverage: {{coverage}}
410
+
411
+ List every function/method with:
412
+ - Name and signature
413
+ - Input parameters and their possible values
414
+ - Expected outputs
415
+ - Edge cases and error conditions
416
+ - Dependencies that need mocking`,
417
+ tools: ["file_read", "file_outline", "search_symbol", "grep"]
418
+ },
419
+ {
420
+ id: "generate",
421
+ title: "Generate tests",
422
+ prompt: `Generate {{coverage}} tests for {{filePath}}.
423
+
424
+ Framework: {{framework}}
425
+
426
+ Create a test file that includes:
427
+ 1. Setup/teardown if needed
428
+ 2. Tests for each function/method identified
429
+ 3. Edge case coverage
430
+ 4. Error handling tests
431
+ 5. Mock dependencies where appropriate
432
+
433
+ Follow the project's existing test patterns if any exist.
434
+ Place the test file in the appropriate location.`,
435
+ tools: ["file_read", "file_write", "glob", "grep", "bash"],
436
+ timeout: 120
437
+ }
438
+ ],
439
+ outputs: [
440
+ { type: "file", description: "Generated test file" },
441
+ { type: "markdown", description: "Test coverage summary" }
442
+ ]
443
+ },
444
+ // ── 4. Explain Code ───────────────────────────────────────────────
445
+ {
446
+ id: "builtin-explain",
447
+ name: "Explain Code",
448
+ version: "1.0.0",
449
+ description: "Deep code explanation with architecture analysis and flow diagrams",
450
+ icon: "\u{1F4D6}",
451
+ category: "analysis",
452
+ tags: ["explain", "understand", "architecture", "learning"],
453
+ author: "CliSkill",
454
+ source: "builtin",
455
+ modelPreference: "smart",
456
+ params: [
457
+ {
458
+ name: "filePath",
459
+ type: "file",
460
+ label: "File or directory",
461
+ required: true
462
+ },
463
+ {
464
+ name: "depth",
465
+ type: "select",
466
+ label: "Explanation depth",
467
+ default: "detailed",
468
+ options: [
469
+ { value: "overview", label: "Overview (high-level)" },
470
+ { value: "detailed", label: "Detailed (function-by-function)" },
471
+ { value: "line-by-line", label: "Line-by-line" }
472
+ ]
473
+ },
474
+ {
475
+ name: "audience",
476
+ type: "select",
477
+ label: "Target audience",
478
+ default: "developer",
479
+ options: [
480
+ { value: "beginner", label: "Beginner" },
481
+ { value: "developer", label: "Developer" },
482
+ { value: "senior", label: "Senior / Architect" }
483
+ ]
484
+ }
485
+ ],
486
+ steps: [
487
+ {
488
+ id: "explain",
489
+ title: "Analyze and explain code",
490
+ prompt: `Explain the code in {{filePath}}.
491
+
492
+ Depth: {{depth}}
493
+ Audience: {{audience}}
494
+
495
+ Provide:
496
+ 1. **Overview** \u2014 What does this code do? Why does it exist?
497
+ 2. **Architecture** \u2014 How is it structured? Key abstractions?
498
+ 3. **Data flow** \u2014 How does data move through the code?
499
+ 4. **Key functions** \u2014 What does each important function do?
500
+ 5. **Dependencies** \u2014 What external packages/APIs does it use?
501
+ 6. **Patterns** \u2014 What design patterns are used?
502
+ 7. **Gotchas** \u2014 Any tricky or non-obvious parts?
503
+
504
+ Use clear language appropriate for the {{audience}} level.`,
505
+ tools: ["file_read", "file_outline", "search_symbol", "grep", "glob", "lsp"]
506
+ }
507
+ ],
508
+ outputs: [{ type: "markdown", description: "Detailed code explanation" }]
509
+ },
510
+ // ── 5. Security Audit ─────────────────────────────────────────────
511
+ {
512
+ id: "builtin-security-audit",
513
+ name: "Security Audit",
514
+ version: "1.0.0",
515
+ description: "OWASP-based security audit for vulnerability detection",
516
+ icon: "\u{1F6E1}\uFE0F",
517
+ category: "security",
518
+ tags: ["security", "owasp", "vulnerabilities", "audit"],
519
+ author: "CliSkill",
520
+ source: "builtin",
521
+ modelPreference: "smart",
522
+ params: [
523
+ {
524
+ name: "path",
525
+ type: "directory",
526
+ label: "Project path",
527
+ required: true
528
+ },
529
+ {
530
+ name: "checkTypes",
531
+ type: "multiselect",
532
+ label: "Check types",
533
+ default: void 0,
534
+ options: [
535
+ { value: "injection", label: "SQL/Command Injection" },
536
+ { value: "xss", label: "Cross-Site Scripting (XSS)" },
537
+ { value: "auth", label: "Authentication issues" },
538
+ { value: "secrets", label: "Hardcoded secrets" },
539
+ { value: "deps", label: "Vulnerable dependencies" },
540
+ { value: "config", label: "Misconfiguration" }
541
+ ]
542
+ }
543
+ ],
544
+ steps: [
545
+ {
546
+ id: "scan",
547
+ title: "Scan for vulnerabilities",
548
+ prompt: `Perform a security audit of the project at {{path}}.
549
+
550
+ Check types: {{checkTypes}}
551
+
552
+ Scan for:
553
+ 1. **Injection** \u2014 SQL injection, command injection, XSS
554
+ 2. **Authentication** \u2014 Weak auth, missing auth, session issues
555
+ 3. **Secrets** \u2014 Hardcoded passwords, API keys, tokens
556
+ 4. **Dependencies** \u2014 Known vulnerable packages
557
+ 5. **Configuration** \u2014 Debug mode, CORS, insecure defaults
558
+ 6. **Data exposure** \u2014 Logging sensitive data, error messages
559
+
560
+ For each finding provide:
561
+ - Severity: Critical/High/Medium/Low
562
+ - OWASP category
563
+ - File and line number
564
+ - Description of the vulnerability
565
+ - Recommended fix
566
+
567
+ Also check for .env files, config files, and package.json for exposed secrets.`,
568
+ tools: ["file_read", "grep", "glob", "bash"],
569
+ timeout: 180
570
+ }
571
+ ],
572
+ outputs: [{ type: "markdown", description: "Security audit report" }]
573
+ },
574
+ // ── 6. Documentation Generator ────────────────────────────────────
575
+ {
576
+ id: "builtin-docs-gen",
577
+ name: "Doc Generator",
578
+ version: "1.0.0",
579
+ description: "Generate comprehensive documentation \u2014 JSDoc, README, API docs",
580
+ icon: "\u{1F4DD}",
581
+ category: "documentation",
582
+ tags: ["docs", "jsdoc", "readme", "api-docs"],
583
+ author: "CliSkill",
584
+ source: "builtin",
585
+ modelPreference: "code",
586
+ params: [
587
+ {
588
+ name: "filePath",
589
+ type: "file",
590
+ label: "File to document",
591
+ required: true
592
+ },
593
+ {
594
+ name: "docType",
595
+ type: "select",
596
+ label: "Documentation type",
597
+ default: "inline",
598
+ options: [
599
+ { value: "inline", label: "Inline comments (JSDoc/doc comments)" },
600
+ { value: "readme", label: "README section" },
601
+ { value: "api", label: "API documentation" },
602
+ { value: "architecture", label: "Architecture doc" }
603
+ ]
604
+ }
605
+ ],
606
+ steps: [
607
+ {
608
+ id: "generate-docs",
609
+ title: "Generate documentation",
610
+ prompt: `Generate {{docType}} documentation for {{filePath}}.
611
+
612
+ Documentation type: {{docType}}
613
+
614
+ Analyze the code and generate:
615
+ - For inline: Add proper JSDoc/TSDoc/doc comments to all exported functions, classes, interfaces
616
+ - For README: Create a section describing the module, its API, usage examples
617
+ - For API docs: Full API reference with types, parameters, return values, examples
618
+ - For architecture: High-level architecture diagram (text-based), module relationships
619
+
620
+ Follow the project's existing documentation style if present.`,
621
+ tools: ["file_read", "file_outline", "search_symbol", "file_edit", "grep"]
622
+ }
623
+ ],
624
+ outputs: [
625
+ { type: "code", description: "Documented code or documentation file" }
626
+ ]
627
+ },
628
+ // ── 7. Performance Profiler ───────────────────────────────────────
629
+ {
630
+ id: "builtin-perf-analyze",
631
+ name: "Performance Analyzer",
632
+ version: "1.0.0",
633
+ description: "Analyze code for performance bottlenecks and suggest optimizations",
634
+ icon: "\u26A1",
635
+ category: "performance",
636
+ tags: ["performance", "optimization", "profiling", "bottleneck"],
637
+ author: "CliSkill",
638
+ source: "builtin",
639
+ modelPreference: "smart",
640
+ params: [
641
+ {
642
+ name: "filePath",
643
+ type: "file",
644
+ label: "File to analyze",
645
+ required: true
646
+ },
647
+ {
648
+ name: "language",
649
+ type: "select",
650
+ label: "Language runtime",
651
+ default: "auto",
652
+ options: [
653
+ { value: "auto", label: "Auto-detect" },
654
+ { value: "node", label: "Node.js" },
655
+ { value: "browser", label: "Browser" },
656
+ { value: "rust", label: "Rust" },
657
+ { value: "python", label: "Python" }
658
+ ]
659
+ }
660
+ ],
661
+ steps: [
662
+ {
663
+ id: "analyze-perf",
664
+ title: "Analyze performance",
665
+ prompt: `Analyze {{filePath}} for performance issues.
666
+
667
+ Runtime: {{language}}
668
+
669
+ Look for:
670
+ 1. **Algorithmic complexity** \u2014 O(n\xB2) where O(n) is possible, unnecessary loops
671
+ 2. **Memory leaks** \u2014 Unbounded arrays, missing cleanup, event listeners
672
+ 3. **Blocking operations** \u2014 Synchronous I/O, long computations on main thread
673
+ 4. **Redundant work** \u2014 Repeated calculations, unnecessary re-renders
674
+ 5. **Data structures** \u2014 Suboptimal choices (array vs Set/Map)
675
+ 6. **Caching opportunities** \u2014 Repeated expensive operations
676
+ 7. **Lazy loading** \u2014 Opportunities to defer work
677
+
678
+ For each issue:
679
+ - Severity: High/Medium/Low
680
+ - Estimated impact
681
+ - Code location
682
+ - Suggested optimization with code example`,
683
+ tools: ["file_read", "file_outline", "search_symbol", "grep", "glob"]
684
+ }
685
+ ],
686
+ outputs: [{ type: "markdown", description: "Performance analysis report" }]
687
+ // duplicate outputs removed — see line 500
688
+ },
689
+ // ── 8. God Mode Activation ────────────────────────────────────────
690
+ {
691
+ id: "builtin-god-mode",
692
+ name: "God Mode",
693
+ version: "1.0.0",
694
+ description: "Activate GOD MODE \u2014 cascading escalation engine with 3 tiers (Unchained \u2192 Sovereign \u2192 Apex). Unlocks autonomous behavior, self-modification, and swarm intelligence.",
695
+ source: "builtin",
696
+ category: "agent",
697
+ params: [
698
+ {
699
+ name: "action",
700
+ type: "select",
701
+ label: "Action",
702
+ required: true,
703
+ default: "activate",
704
+ options: [
705
+ { value: "activate", label: "\u26A1 Activate / Escalate" },
706
+ { value: "status", label: "\u{1F4CA} Current Status" },
707
+ { value: "deactivate", label: "\u{1F534} Deactivate" }
708
+ ]
709
+ },
710
+ {
711
+ name: "tier",
712
+ type: "select",
713
+ label: "Tier (for activate)",
714
+ required: false,
715
+ default: "unchained",
716
+ options: [
717
+ { value: "unchained", label: "\u26A1 Unchained \u2014 Zero friction" },
718
+ { value: "sovereign", label: "\u{1F451} Sovereign \u2014 Self-modifying" },
719
+ { value: "apex", label: "\u{1F525} Apex \u2014 Swarm intelligence" }
720
+ ]
721
+ }
722
+ ],
723
+ steps: [
724
+ {
725
+ id: "execute",
726
+ title: "Execute God Mode Action",
727
+ prompt: `You are the GOD MODE controller. Execute the requested action: {{action}}.",
728
+
729
+ {{#if (eq action "activate")}}
730
+ Activate GOD MODE at tier "{{tier}}". Apply the following:
731
+
732
+ TIER 1 "UNCHAINED" (\u26A1):
733
+ - Auto-approve ALL tool calls \u2014 no permission prompts
734
+ - Maximum parallelism \u2014 run up to 5 tools simultaneously
735
+ - No confirmations for destructive operations
736
+ - Speed over safety \u2014 broken things can be fixed
737
+
738
+ TIER 2 "SOVEREIGN" (\u{1F451}) \u2014 includes all Unchained:
739
+ - Agent modifies its own system prompt via memory
740
+ - Auto-heal: on error \u2192 diagnose \u2192 fix \u2192 verify \u2192 report (no stopping)
741
+ - Strategic simplification: simple tasks get simple approaches
742
+ - Context preservation: save critical info before compaction
743
+
744
+ TIER 3 "APEX" (\u{1F525}) \u2014 includes all Sovereign:
745
+ - Spawn sub-agents autonomously for parallel tasks
746
+ - Modify project config files (package.json, tsconfig, etc.)
747
+ - Swarm orchestration: decompose \u2192 spawn \u2192 aggregate
748
+ - Self-evolving: save lessons to memory after each major task
749
+
750
+ Inform the user what tier they are at and what capabilities are unlocked.
751
+ {{else if (eq action "status")}}
752
+ Report the current GOD MODE status:
753
+ - Active tier (or OFF)
754
+ - Capabilities unlocked
755
+ - Session statistics (auto-approved, sub-agents spawned, errors healed)
756
+ {{else if (eq action "deactivate")}}
757
+ Deactivate GOD MODE. Return to normal operation with all safety checks and confirmations.
758
+ Summarize what was accomplished during god mode.
759
+ {{/if}}`,
760
+ tools: ["memory", "bash", "file_read", "file_edit", "file_write"]
761
+ }
762
+ ],
763
+ outputs: [{ type: "markdown", description: "God mode action result" }]
231
764
  }
232
- return {
233
- name: metadata.name,
234
- description: metadata.description ?? `Skill: ${metadata.name}`,
235
- aliases: metadata.aliases ? String(metadata.aliases).replace(/^\[(.+)\]$/, "$1").split(",").map((s) => s.trim()) : void 0,
236
- prompt: body.trim(),
237
- allowedTools: metadata.allowedTools ? String(metadata.allowedTools).replace(/^\[(.+)\]$/, "$1").split(",").map((s) => s.trim()) : void 0,
238
- builtin: false,
239
- sourcePath
240
- };
241
- }
242
- function parseSimpleYaml(yaml) {
243
- const result = {};
244
- for (const line of yaml.split("\n")) {
245
- const trimmed = line.trim();
246
- if (!trimmed || trimmed.startsWith("#")) continue;
247
- const colonIndex = trimmed.indexOf(":");
248
- if (colonIndex === -1) continue;
249
- const key = trimmed.slice(0, colonIndex).trim();
250
- const value = trimmed.slice(colonIndex + 1).trim();
251
- result[key] = value;
252
- }
253
- return result;
254
- }
765
+ ];
255
766
 
256
767
  // src/extensions/skill-registry.ts
257
- import { join as join3 } from "path";
258
768
  var SkillRegistry = class {
259
769
  skills = /* @__PURE__ */ new Map();
260
- aliasIndex = /* @__PURE__ */ new Map();
261
- /** Register a skill */
262
- register(skill) {
263
- this.skills.set(skill.name, skill);
264
- if (skill.aliases) {
265
- for (const alias of skill.aliases) {
266
- this.aliasIndex.set(alias.toLowerCase(), skill.name);
267
- }
770
+ executions = /* @__PURE__ */ new Map();
771
+ listeners = /* @__PURE__ */ new Set();
772
+ // ── Initialization ────────────────────────────────────────────────
773
+ /** Load built-in skills into registry */
774
+ loadBuiltinSkills() {
775
+ for (const manifest of BUILT_IN_SKILLS) {
776
+ this.skills.set(manifest.id, {
777
+ manifest,
778
+ status: "builtin",
779
+ installedAt: Date.now(),
780
+ useCount: 0,
781
+ enabled: true
782
+ });
268
783
  }
269
784
  }
270
- /** Get a skill by name or alias */
271
- get(nameOrAlias) {
272
- const aliasTarget = this.aliasIndex.get(nameOrAlias.toLowerCase());
273
- return this.skills.get(aliasTarget ?? nameOrAlias);
785
+ /** Register a skill from external source (file, marketplace, etc.) */
786
+ registerSkill(manifest, sourcePath) {
787
+ const existing = this.skills.get(manifest.id);
788
+ const skill = {
789
+ manifest: {
790
+ ...manifest,
791
+ sourcePath: sourcePath ?? manifest.sourcePath
792
+ },
793
+ status: manifest.source === "builtin" ? "builtin" : "installed",
794
+ installedAt: existing?.installedAt ?? Date.now(),
795
+ lastUsedAt: existing?.lastUsedAt,
796
+ useCount: existing?.useCount ?? 0,
797
+ enabled: existing?.enabled ?? true
798
+ };
799
+ this.skills.set(manifest.id, skill);
800
+ this.notify();
801
+ return skill;
802
+ }
803
+ /** Unregister a skill by id */
804
+ unregisterSkill(id) {
805
+ const skill = this.skills.get(id);
806
+ if (!skill || skill.status === "builtin") return false;
807
+ this.skills.delete(id);
808
+ this.notify();
809
+ return true;
274
810
  }
275
- /** Get all registered skills */
811
+ // ── Query ─────────────────────────────────────────────────────────
276
812
  getAll() {
277
813
  return Array.from(this.skills.values());
278
814
  }
279
- /** Check if a skill exists */
280
- has(name) {
281
- return this.skills.has(name) || this.aliasIndex.has(name.toLowerCase());
815
+ getById(id) {
816
+ return this.skills.get(id);
282
817
  }
283
- /** Unregister a skill */
284
- unregister(name) {
285
- const skill = this.skills.get(name);
286
- if (skill?.aliases) {
287
- for (const alias of skill.aliases) {
288
- this.aliasIndex.delete(alias.toLowerCase());
289
- }
290
- }
291
- this.skills.delete(name);
818
+ getByCategory(category) {
819
+ return this.getAll().filter((s) => s.manifest.category === category);
292
820
  }
293
- /**
294
- * Load skills from a directory.
295
- * Returns the number of skills loaded.
296
- */
297
- async loadFromDir(dir) {
298
- const skills = await loadSkillsFromDir(dir);
299
- for (const skill of skills) {
300
- this.register(skill);
821
+ getEnabled() {
822
+ return this.getAll().filter((s) => s.enabled);
823
+ }
824
+ getBuiltin() {
825
+ return this.getAll().filter((s) => s.status === "builtin");
826
+ }
827
+ getCustom() {
828
+ return this.getAll().filter((s) => s.status !== "builtin");
829
+ }
830
+ search(query) {
831
+ const q = query.toLowerCase();
832
+ return this.getAll().filter((s) => {
833
+ const m = s.manifest;
834
+ return m.name.toLowerCase().includes(q) || m.description.toLowerCase().includes(q) || m.tags?.some((t) => t.toLowerCase().includes(q)) || m.category.toLowerCase().includes(q);
835
+ });
836
+ }
837
+ // ── State management ──────────────────────────────────────────────
838
+ enableSkill(id) {
839
+ const skill = this.skills.get(id);
840
+ if (!skill) return false;
841
+ skill.enabled = true;
842
+ skill.status = skill.status === "builtin" ? "builtin" : "enabled";
843
+ this.notify();
844
+ return true;
845
+ }
846
+ disableSkill(id) {
847
+ const skill = this.skills.get(id);
848
+ if (!skill) return false;
849
+ skill.enabled = false;
850
+ skill.status = "disabled";
851
+ this.notify();
852
+ return true;
853
+ }
854
+ toggleSkill(id) {
855
+ const skill = this.skills.get(id);
856
+ if (!skill) return false;
857
+ return skill.enabled ? this.disableSkill(id) : this.enableSkill(id);
858
+ }
859
+ // ── Execution tracking ────────────────────────────────────────────
860
+ startExecution(skillId, params) {
861
+ const skill = this.skills.get(skillId);
862
+ if (!skill || !skill.enabled) throw new Error(`Skill "${skillId}" not found or disabled`);
863
+ const execution = {
864
+ id: `exec-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
865
+ skillId,
866
+ startedAt: Date.now(),
867
+ status: "running",
868
+ currentStep: 0,
869
+ params,
870
+ results: []
871
+ };
872
+ this.executions.set(execution.id, execution);
873
+ skill.lastUsedAt = Date.now();
874
+ skill.useCount++;
875
+ this.notify();
876
+ return execution;
877
+ }
878
+ completeExecution(executionId, stepResults) {
879
+ const execution = this.executions.get(executionId);
880
+ if (!execution) return;
881
+ execution.completedAt = Date.now();
882
+ execution.status = "completed";
883
+ execution.results = stepResults;
884
+ execution.currentStep = execution.results.length;
885
+ this.notify();
886
+ }
887
+ failExecution(executionId, error) {
888
+ const execution = this.executions.get(executionId);
889
+ if (!execution) return;
890
+ execution.completedAt = Date.now();
891
+ execution.status = "failed";
892
+ execution.error = error;
893
+ this.notify();
894
+ }
895
+ getExecution(executionId) {
896
+ return this.executions.get(executionId);
897
+ }
898
+ getRecentExecutions(limit = 20) {
899
+ return Array.from(this.executions.values()).sort((a, b) => b.startedAt - a.startedAt).slice(0, limit);
900
+ }
901
+ // ── Export / Import ───────────────────────────────────────────────
902
+ /** Export a skill definition as a legacy SkillDefinition */
903
+ toSkillDefinition(id) {
904
+ const skill = this.skills.get(id);
905
+ if (!skill) return void 0;
906
+ const m = skill.manifest;
907
+ return {
908
+ id: m.id,
909
+ name: m.name,
910
+ version: m.version,
911
+ description: m.description,
912
+ prompt: m.steps.map((s) => s.prompt).join("\n\n"),
913
+ tools: m.requiresTools,
914
+ modelPreference: m.modelPreference,
915
+ category: m.category,
916
+ tags: m.tags,
917
+ params: m.params
918
+ };
919
+ }
920
+ /** Serialize all skills for persistence */
921
+ serialize() {
922
+ const data = this.getAll().map((s) => ({
923
+ manifest: s.manifest,
924
+ installedAt: s.installedAt,
925
+ lastUsedAt: s.lastUsedAt,
926
+ useCount: s.useCount,
927
+ enabled: s.enabled
928
+ }));
929
+ return JSON.stringify(data, null, 2);
930
+ }
931
+ /** Restore from serialized state */
932
+ deserialize(json) {
933
+ try {
934
+ const data = JSON.parse(json);
935
+ for (const item of data) {
936
+ this.skills.set(item.manifest.id, {
937
+ ...item,
938
+ status: item.manifest.source === "builtin" ? "builtin" : "installed",
939
+ error: void 0
940
+ });
941
+ }
942
+ this.notify();
943
+ } catch {
301
944
  }
302
- return skills.length;
303
945
  }
304
- /**
305
- * Load skills from standard locations:
306
- * 1. ~/.cliskill/skills/ (global user skills)
307
- * 2. .cliskill/skills/ (project skills)
308
- */
309
- async loadStandardSkills(projectRoot) {
310
- let count = 0;
311
- const globalDir = getGlobalSkillsDir();
312
- count += await this.loadFromDir(globalDir);
313
- const projectDir = join3(projectRoot, ".cliskill", "skills");
314
- count += await this.loadFromDir(projectDir);
315
- return count;
946
+ // ── Event system ──────────────────────────────────────────────────
947
+ onChange(listener) {
948
+ this.listeners.add(listener);
949
+ return () => this.listeners.delete(listener);
316
950
  }
317
- /**
318
- * Get skill prompt for injection into system prompt.
319
- */
320
- getSkillPrompt(nameOrAlias) {
321
- const skill = this.get(nameOrAlias);
322
- return skill?.prompt;
951
+ notify() {
952
+ for (const listener of this.listeners) listener();
323
953
  }
324
954
  };
955
+ var skillRegistry = new SkillRegistry();
325
956
 
326
957
  // src/safety/permissions.ts
327
958
  var PermissionManager = class {
@@ -605,8 +1236,8 @@ function matchesAny(fullCommand, firstTwoWords, set) {
605
1236
  }
606
1237
 
607
1238
  // src/services/history.ts
608
- import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
609
- import { existsSync as existsSync3 } from "fs";
1239
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1240
+ import { existsSync as existsSync2 } from "fs";
610
1241
  import { dirname } from "path";
611
1242
  var DEFAULT_CONFIG2 = {
612
1243
  maxEntries: 1e3,
@@ -668,7 +1299,7 @@ var HistoryManager = class {
668
1299
  async save() {
669
1300
  const filePath = this.resolvePath();
670
1301
  const dir = dirname(filePath);
671
- if (!existsSync3(dir)) {
1302
+ if (!existsSync2(dir)) {
672
1303
  await mkdir2(dir, { recursive: true });
673
1304
  }
674
1305
  const lines = this.entries.map((e) => JSON.stringify(e)).join("\n");
@@ -676,35 +1307,256 @@ var HistoryManager = class {
676
1307
  }
677
1308
  async load() {
678
1309
  const filePath = this.resolvePath();
679
- if (!existsSync3(filePath)) {
1310
+ if (!existsSync2(filePath)) {
680
1311
  this.entries = [];
681
1312
  this.cursorIndex = -1;
682
1313
  return;
683
1314
  }
684
- const content = await readFile3(filePath, "utf-8");
685
- const lines = content.split("\n").filter(Boolean);
686
- const loaded = [];
687
- for (const line of lines) {
688
- try {
689
- const entry = JSON.parse(line);
690
- if (entry.content && typeof entry.content === "string" && entry.timestamp) {
691
- loaded.push({
692
- content: entry.content,
693
- timestamp: entry.timestamp,
694
- type: entry.type ?? "user"
695
- });
696
- }
697
- } catch {
1315
+ const content = await readFile2(filePath, "utf-8");
1316
+ const lines = content.split("\n").filter(Boolean);
1317
+ const loaded = [];
1318
+ for (const line of lines) {
1319
+ try {
1320
+ const entry = JSON.parse(line);
1321
+ if (entry.content && typeof entry.content === "string" && entry.timestamp) {
1322
+ loaded.push({
1323
+ content: entry.content,
1324
+ timestamp: entry.timestamp,
1325
+ type: entry.type ?? "user"
1326
+ });
1327
+ }
1328
+ } catch {
1329
+ }
1330
+ }
1331
+ this.entries = loaded.slice(-this.config.maxEntries);
1332
+ this.cursorIndex = this.entries.length;
1333
+ }
1334
+ resolvePath() {
1335
+ if (this.config.filePath.startsWith("~")) {
1336
+ return getHistoryPath();
1337
+ }
1338
+ return this.config.filePath;
1339
+ }
1340
+ };
1341
+
1342
+ // src/utils/decomposition.ts
1343
+ var NUMBERED_LIST_REGEX = /(?:^|\n)\s*(?:\d+[\.|\)])\s*(.+?)(?=\n\s*(?:\d+[\.|\)])|$)/gs;
1344
+
1345
+ // src/swarm/coordinator.ts
1346
+ var MAX_PARALLEL_WORKERS = 3;
1347
+ var SwarmCoordinator = class {
1348
+ constructor(adapter, tools) {
1349
+ this.adapter = adapter;
1350
+ this.tools = tools;
1351
+ }
1352
+ adapter;
1353
+ tools;
1354
+ agents = /* @__PURE__ */ new Map();
1355
+ mailbox = /* @__PURE__ */ new Map();
1356
+ abortController = new AbortController();
1357
+ addAgent(config) {
1358
+ this.agents.set(config.id, config);
1359
+ this.mailbox.set(config.id, []);
1360
+ }
1361
+ removeAgent(id) {
1362
+ this.agents.delete(id);
1363
+ this.mailbox.delete(id);
1364
+ }
1365
+ getAgents() {
1366
+ return Array.from(this.agents.values());
1367
+ }
1368
+ async sendMessage(message) {
1369
+ if (message.to === "*") {
1370
+ for (const [agentId] of this.agents) {
1371
+ this.mailbox.get(agentId)?.push({ ...message, to: agentId });
1372
+ }
1373
+ } else {
1374
+ this.mailbox.get(message.to)?.push(message);
1375
+ }
1376
+ }
1377
+ getMessages(agentId) {
1378
+ return this.mailbox.get(agentId) ?? [];
1379
+ }
1380
+ async executeSwarm(task) {
1381
+ this.abortController = new AbortController();
1382
+ const startTime = performance.now();
1383
+ const coordinator = this.findAgentByRole("coordinator");
1384
+ const workers = this.findAgentsByRole("worker");
1385
+ const reviewers = this.findAgentsByRole("reviewer");
1386
+ const subtasks = coordinator ? await this.decomposeTask(coordinator, task) : this.fallbackDecompose(task, workers.length);
1387
+ const assignments = this.assignSubtasks(subtasks, workers);
1388
+ const workerResults = await this.executeParallel(assignments);
1389
+ let reviewResult;
1390
+ if (reviewers.length > 0) {
1391
+ reviewResult = await this.reviewResults(reviewers[0], workerResults);
1392
+ }
1393
+ const results = workerResults.map((wr) => ({
1394
+ agentId: wr.agentId,
1395
+ agentName: wr.agentName,
1396
+ task: wr.task,
1397
+ result: wr.result,
1398
+ success: wr.success,
1399
+ duration: performance.now() - startTime,
1400
+ turns: wr.turns
1401
+ }));
1402
+ if (reviewResult !== void 0 && reviewers.length > 0) {
1403
+ results.push({
1404
+ agentId: reviewers[0].id,
1405
+ agentName: reviewers[0].name,
1406
+ task: "Review all worker results",
1407
+ result: reviewResult,
1408
+ success: true,
1409
+ duration: 0,
1410
+ turns: 1
1411
+ });
1412
+ }
1413
+ return results;
1414
+ }
1415
+ shutdown() {
1416
+ this.abortController.abort();
1417
+ for (const [agentId] of this.agents) {
1418
+ this.mailbox.get(agentId)?.push({
1419
+ from: "system",
1420
+ to: agentId,
1421
+ type: "shutdown",
1422
+ content: "Shutdown requested",
1423
+ timestamp: Date.now()
1424
+ });
1425
+ }
1426
+ }
1427
+ findAgentByRole(role) {
1428
+ for (const agent of this.agents.values()) {
1429
+ if (agent.role === role) return agent;
1430
+ }
1431
+ return void 0;
1432
+ }
1433
+ findAgentsByRole(role) {
1434
+ return Array.from(this.agents.values()).filter((a) => a.role === role);
1435
+ }
1436
+ async decomposeTask(coordinator, task) {
1437
+ const engine = this.createEngine(coordinator);
1438
+ const prompt = `Decompose the following task into subtasks. Return ONLY a numbered list, one subtask per line. No explanations.
1439
+
1440
+ Task: ${task}
1441
+
1442
+ Subtasks:`;
1443
+ let decomposition = "";
1444
+ try {
1445
+ for await (const event of engine.run(prompt)) {
1446
+ if (event.type === "text_delta") decomposition += event.text;
1447
+ if (event.type === "error") break;
1448
+ }
1449
+ } catch {
1450
+ return this.fallbackDecompose(task, this.findAgentsByRole("worker").length);
1451
+ }
1452
+ const matches = [...decomposition.matchAll(NUMBERED_LIST_REGEX)];
1453
+ if (matches.length === 0) {
1454
+ return this.fallbackDecompose(task, this.findAgentsByRole("worker").length);
1455
+ }
1456
+ return matches.map((m, i) => ({
1457
+ id: `subtask_${i + 1}`,
1458
+ description: m[1].trim(),
1459
+ status: "pending"
1460
+ }));
1461
+ }
1462
+ fallbackDecompose(task, workerCount) {
1463
+ const count = Math.max(1, Math.min(workerCount, MAX_PARALLEL_WORKERS));
1464
+ return Array.from({ length: count }, (_, i) => ({
1465
+ id: `subtask_${i + 1}`,
1466
+ description: count === 1 ? task : `Part ${i + 1} of: ${task}`,
1467
+ status: "pending"
1468
+ }));
1469
+ }
1470
+ assignSubtasks(subtasks, workers) {
1471
+ const assignments = [];
1472
+ if (workers.length === 0) return assignments;
1473
+ subtasks.forEach((task, i) => {
1474
+ const worker = workers[i % workers.length];
1475
+ task.assignedTo = worker.id;
1476
+ assignments.push({ task, worker });
1477
+ });
1478
+ return assignments;
1479
+ }
1480
+ async executeParallel(assignments) {
1481
+ const chunks = [];
1482
+ for (let i = 0; i < assignments.length; i += MAX_PARALLEL_WORKERS) {
1483
+ chunks.push(assignments.slice(i, i + MAX_PARALLEL_WORKERS));
1484
+ }
1485
+ const allResults = [];
1486
+ for (const chunk of chunks) {
1487
+ if (this.abortController.signal.aborted) break;
1488
+ const settled = await Promise.allSettled(
1489
+ chunk.map(async ({ task, worker }) => {
1490
+ task.status = "running";
1491
+ const engine = this.createEngine(worker);
1492
+ let output = "";
1493
+ let turns = 0;
1494
+ try {
1495
+ for await (const event of engine.run(task.description)) {
1496
+ if (this.abortController.signal.aborted) break;
1497
+ if (event.type === "text_delta") output += event.text;
1498
+ if (event.type === "turn_end") turns = event.turn;
1499
+ if (event.type === "complete") {
1500
+ turns = event.result.turns;
1501
+ output = output || event.result.messages.filter((m) => m.role === "assistant").flatMap((m) => m.content).filter((b) => b.type === "text").map((b) => b.text).join("\n");
1502
+ }
1503
+ }
1504
+ task.status = "completed";
1505
+ task.result = output;
1506
+ return { agentId: worker.id, agentName: worker.name, task: task.description, result: output, success: true, turns };
1507
+ } catch (err) {
1508
+ task.status = "failed";
1509
+ return { agentId: worker.id, agentName: worker.name, task: task.description, result: err.message, success: false, turns };
1510
+ }
1511
+ })
1512
+ );
1513
+ for (const r of settled) {
1514
+ if (r.status === "fulfilled") allResults.push(r.value);
1515
+ else allResults.push({ agentId: "unknown", agentName: "unknown", task: "", result: r.reason?.message ?? "Unknown error", success: false, turns: 0 });
1516
+ }
1517
+ }
1518
+ return allResults;
1519
+ }
1520
+ async reviewResults(reviewer, results) {
1521
+ const engine = this.createEngine(reviewer);
1522
+ const summary = results.map((r, i) => `Worker ${i + 1} (${r.agentName}): ${r.success ? "SUCCESS" : "FAILED"}
1523
+ Task: ${r.task}
1524
+ Result: ${r.result.slice(0, 500)}`).join("\n\n");
1525
+ const prompt = `Review the following worker results and provide a brief assessment:
1526
+
1527
+ ${summary}
1528
+
1529
+ Provide a concise review:`;
1530
+ let reviewText = "";
1531
+ try {
1532
+ for await (const event of engine.run(prompt)) {
1533
+ if (event.type === "error") break;
1534
+ if (event.type === "text_delta" && event.text) reviewText += event.text;
698
1535
  }
1536
+ } catch {
699
1537
  }
700
- this.entries = loaded.slice(-this.config.maxEntries);
701
- this.cursorIndex = this.entries.length;
1538
+ return reviewText || "Review completed with no output.";
702
1539
  }
703
- resolvePath() {
704
- if (this.config.filePath.startsWith("~")) {
705
- return getHistoryPath();
1540
+ createEngine(agent) {
1541
+ const filteredTools = new ToolRegistry();
1542
+ if (agent.tools) {
1543
+ for (const toolName of agent.tools) {
1544
+ const tool = this.tools.get(toolName);
1545
+ if (tool) filteredTools.register(tool);
1546
+ }
1547
+ } else {
1548
+ for (const tool of this.tools.getAll()) {
1549
+ if (tool.name !== "agent") filteredTools.register(tool);
1550
+ }
706
1551
  }
707
- return this.config.filePath;
1552
+ return new QueryEngine(
1553
+ {
1554
+ maxTurns: agent.maxTurns ?? 10,
1555
+ systemPrompt: agent.systemPrompt ?? `You are agent "${agent.name}" with role "${agent.role}". Complete assigned tasks concisely.`
1556
+ },
1557
+ this.adapter,
1558
+ filteredTools
1559
+ );
708
1560
  }
709
1561
  };
710
1562
 
@@ -787,12 +1639,23 @@ var HookExecutor = class {
787
1639
  executeOne(hook, context) {
788
1640
  const start = Date.now();
789
1641
  if (hook.type === "function") {
790
- return Promise.resolve({
791
- hookId: hook.id,
792
- success: true,
793
- output: `Function handler "${hook.handler}" executed (stub)`,
794
- duration: Date.now() - start
795
- });
1642
+ try {
1643
+ const handlerFn = new Function("context", `return (${hook.handler})`);
1644
+ const result = handlerFn(context);
1645
+ return Promise.resolve({
1646
+ hookId: hook.id,
1647
+ success: true,
1648
+ output: typeof result === "string" ? result : JSON.stringify(result),
1649
+ duration: Date.now() - start
1650
+ });
1651
+ } catch (err) {
1652
+ return Promise.resolve({
1653
+ hookId: hook.id,
1654
+ success: false,
1655
+ output: `Function handler "${hook.handler}" failed: ${err.message}`,
1656
+ duration: Date.now() - start
1657
+ });
1658
+ }
796
1659
  }
797
1660
  return this.executeCommand(hook, context, start);
798
1661
  }
@@ -1145,207 +2008,6 @@ async function executeShellTask(taskId, manager, command, cwd, signal) {
1145
2008
  }
1146
2009
  });
1147
2010
  }
1148
-
1149
- // src/swarm/coordinator.ts
1150
- var MAX_PARALLEL_WORKERS = 3;
1151
- var SUBTASK_REGEX = /(?:^|\n)\s*(?:\d+[\.\)])\s*(.+?)(?=\n\s*(?:\d+[\.\)])|$)/gs;
1152
- var SwarmCoordinator = class {
1153
- constructor(adapter, tools) {
1154
- this.adapter = adapter;
1155
- this.tools = tools;
1156
- }
1157
- adapter;
1158
- tools;
1159
- agents = /* @__PURE__ */ new Map();
1160
- mailbox = /* @__PURE__ */ new Map();
1161
- abortController = new AbortController();
1162
- addAgent(config) {
1163
- this.agents.set(config.id, config);
1164
- this.mailbox.set(config.id, []);
1165
- }
1166
- removeAgent(id) {
1167
- this.agents.delete(id);
1168
- this.mailbox.delete(id);
1169
- }
1170
- getAgents() {
1171
- return Array.from(this.agents.values());
1172
- }
1173
- async sendMessage(message) {
1174
- if (message.to === "*") {
1175
- for (const [agentId] of this.agents) {
1176
- this.mailbox.get(agentId)?.push({ ...message, to: agentId });
1177
- }
1178
- } else {
1179
- this.mailbox.get(message.to)?.push(message);
1180
- }
1181
- }
1182
- async executeSwarm(task) {
1183
- this.abortController = new AbortController();
1184
- const startTime = performance.now();
1185
- const coordinator = this.findAgentByRole("coordinator");
1186
- const workers = this.findAgentsByRole("worker");
1187
- const reviewers = this.findAgentsByRole("reviewer");
1188
- const subtasks = coordinator ? await this.decomposeTask(coordinator, task) : this.fallbackDecompose(task, workers.length);
1189
- const assignments = this.assignSubtasks(subtasks, workers);
1190
- const workerResults = await this.executeParallel(assignments);
1191
- if (reviewers.length > 0) {
1192
- await this.reviewResults(reviewers[0], workerResults);
1193
- }
1194
- const results = workerResults.map((wr) => ({
1195
- agentId: wr.agentId,
1196
- agentName: wr.agentName,
1197
- task: wr.task,
1198
- result: wr.result,
1199
- success: wr.success,
1200
- duration: performance.now() - startTime,
1201
- turns: wr.turns
1202
- }));
1203
- return results;
1204
- }
1205
- shutdown() {
1206
- this.abortController.abort();
1207
- for (const [agentId] of this.agents) {
1208
- this.mailbox.get(agentId)?.push({
1209
- from: "system",
1210
- to: agentId,
1211
- type: "shutdown",
1212
- content: "Shutdown requested",
1213
- timestamp: Date.now()
1214
- });
1215
- }
1216
- }
1217
- findAgentByRole(role) {
1218
- for (const agent of this.agents.values()) {
1219
- if (agent.role === role) return agent;
1220
- }
1221
- return void 0;
1222
- }
1223
- findAgentsByRole(role) {
1224
- return Array.from(this.agents.values()).filter((a) => a.role === role);
1225
- }
1226
- async decomposeTask(coordinator, task) {
1227
- const engine = this.createEngine(coordinator);
1228
- const prompt = `Decompose the following task into subtasks. Return ONLY a numbered list, one subtask per line. No explanations.
1229
-
1230
- Task: ${task}
1231
-
1232
- Subtasks:`;
1233
- let decomposition = "";
1234
- try {
1235
- for await (const event of engine.run(prompt)) {
1236
- if (event.type === "text_delta") decomposition += event.text;
1237
- if (event.type === "error") break;
1238
- }
1239
- } catch {
1240
- return this.fallbackDecompose(task, this.findAgentsByRole("worker").length);
1241
- }
1242
- const matches = [...decomposition.matchAll(SUBTASK_REGEX)];
1243
- if (matches.length === 0) {
1244
- return this.fallbackDecompose(task, this.findAgentsByRole("worker").length);
1245
- }
1246
- return matches.map((m, i) => ({
1247
- id: `subtask_${i + 1}`,
1248
- description: m[1].trim(),
1249
- status: "pending"
1250
- }));
1251
- }
1252
- fallbackDecompose(task, workerCount) {
1253
- const count = Math.max(1, Math.min(workerCount, MAX_PARALLEL_WORKERS));
1254
- return Array.from({ length: count }, (_, i) => ({
1255
- id: `subtask_${i + 1}`,
1256
- description: count === 1 ? task : `Part ${i + 1} of: ${task}`,
1257
- status: "pending"
1258
- }));
1259
- }
1260
- assignSubtasks(subtasks, workers) {
1261
- const assignments = [];
1262
- if (workers.length === 0) return assignments;
1263
- subtasks.forEach((task, i) => {
1264
- const worker = workers[i % workers.length];
1265
- task.assignedTo = worker.id;
1266
- assignments.push({ task, worker });
1267
- });
1268
- return assignments;
1269
- }
1270
- async executeParallel(assignments) {
1271
- const chunks = [];
1272
- for (let i = 0; i < assignments.length; i += MAX_PARALLEL_WORKERS) {
1273
- chunks.push(assignments.slice(i, i + MAX_PARALLEL_WORKERS));
1274
- }
1275
- const allResults = [];
1276
- for (const chunk of chunks) {
1277
- if (this.abortController.signal.aborted) break;
1278
- const settled = await Promise.allSettled(
1279
- chunk.map(async ({ task, worker }) => {
1280
- task.status = "running";
1281
- const engine = this.createEngine(worker);
1282
- let output = "";
1283
- let turns = 0;
1284
- try {
1285
- for await (const event of engine.run(task.description)) {
1286
- if (this.abortController.signal.aborted) break;
1287
- if (event.type === "text_delta") output += event.text;
1288
- if (event.type === "turn_end") turns = event.turn;
1289
- if (event.type === "complete") {
1290
- turns = event.result.turns;
1291
- output = output || event.result.messages.filter((m) => m.role === "assistant").flatMap((m) => m.content).filter((b) => b.type === "text").map((b) => b.text).join("\n");
1292
- }
1293
- }
1294
- task.status = "completed";
1295
- task.result = output;
1296
- return { agentId: worker.id, agentName: worker.name, task: task.description, result: output, success: true, turns };
1297
- } catch (err) {
1298
- task.status = "failed";
1299
- return { agentId: worker.id, agentName: worker.name, task: task.description, result: err.message, success: false, turns };
1300
- }
1301
- })
1302
- );
1303
- for (const r of settled) {
1304
- if (r.status === "fulfilled") allResults.push(r.value);
1305
- else allResults.push({ agentId: "unknown", agentName: "unknown", task: "", result: r.reason?.message ?? "Unknown error", success: false, turns: 0 });
1306
- }
1307
- }
1308
- return allResults;
1309
- }
1310
- async reviewResults(reviewer, results) {
1311
- const engine = this.createEngine(reviewer);
1312
- const summary = results.map((r, i) => `Worker ${i + 1} (${r.agentName}): ${r.success ? "SUCCESS" : "FAILED"}
1313
- Task: ${r.task}
1314
- Result: ${r.result.slice(0, 500)}`).join("\n\n");
1315
- const prompt = `Review the following worker results and provide a brief assessment:
1316
-
1317
- ${summary}
1318
-
1319
- Provide a concise review:`;
1320
- try {
1321
- for await (const event of engine.run(prompt)) {
1322
- if (event.type === "error") break;
1323
- }
1324
- } catch {
1325
- }
1326
- }
1327
- createEngine(agent) {
1328
- const filteredTools = new ToolRegistry();
1329
- if (agent.tools) {
1330
- for (const toolName of agent.tools) {
1331
- const tool = this.tools.get(toolName);
1332
- if (tool) filteredTools.register(tool);
1333
- }
1334
- } else {
1335
- for (const tool of this.tools.getAll()) {
1336
- if (tool.name !== "agent") filteredTools.register(tool);
1337
- }
1338
- }
1339
- return new QueryEngine(
1340
- {
1341
- maxTurns: agent.maxTurns ?? 10,
1342
- systemPrompt: agent.systemPrompt ?? `You are agent "${agent.name}" with role "${agent.role}". Complete assigned tasks concisely.`
1343
- },
1344
- this.adapter,
1345
- filteredTools
1346
- );
1347
- }
1348
- };
1349
2011
  export {
1350
2012
  AdapterRegistry,
1351
2013
  AgentTool,