opencode-swarm-plugin 0.23.5 → 0.24.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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +34 -0
- package/README.md +155 -3
- package/bin/swarm.ts +497 -187
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +668 -91
- package/dist/plugin.js +596 -91
- package/dist/schemas/bead-events.d.ts +698 -0
- package/dist/schemas/bead-events.d.ts.map +1 -0
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/swarm-decompose.d.ts +74 -0
- package/dist/swarm-decompose.d.ts.map +1 -1
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm.d.ts +27 -0
- package/dist/swarm.d.ts.map +1 -1
- package/docs/testing/context-recovery-test.md +470 -0
- package/examples/commands/swarm.md +92 -20
- package/global-skills/swarm-coordination/SKILL.md +380 -10
- package/package.json +2 -2
- package/src/schemas/bead-events.test.ts +341 -0
- package/src/schemas/bead-events.ts +583 -0
- package/src/schemas/index.ts +51 -0
- package/src/skills.ts +10 -3
- package/src/swarm-decompose.ts +337 -0
- package/src/swarm-orchestrate.ts +72 -55
- package/src/swarm-prompts.ts +144 -42
- package/src/swarm.integration.test.ts +581 -31
package/bin/swarm.ts
CHANGED
|
@@ -13,9 +13,19 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import * as p from "@clack/prompts";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
chmodSync,
|
|
18
|
+
copyFileSync,
|
|
19
|
+
existsSync,
|
|
20
|
+
mkdirSync,
|
|
21
|
+
readFileSync,
|
|
22
|
+
readdirSync,
|
|
23
|
+
rmSync,
|
|
24
|
+
statSync,
|
|
25
|
+
writeFileSync,
|
|
26
|
+
} from "fs";
|
|
17
27
|
import { homedir } from "os";
|
|
18
|
-
import { dirname, join } from "path";
|
|
28
|
+
import { basename, dirname, join } from "path";
|
|
19
29
|
import { fileURLToPath } from "url";
|
|
20
30
|
|
|
21
31
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -454,6 +464,390 @@ async function checkAllDependencies(): Promise<CheckResult[]> {
|
|
|
454
464
|
return results;
|
|
455
465
|
}
|
|
456
466
|
|
|
467
|
+
// ============================================================================
|
|
468
|
+
// Skills Sync Utilities
|
|
469
|
+
// ============================================================================
|
|
470
|
+
|
|
471
|
+
const BUNDLED_SKILL_MARKER_FILENAME = ".swarm-bundled-skill.json";
|
|
472
|
+
|
|
473
|
+
function listDirectoryNames(dirPath: string): string[] {
|
|
474
|
+
if (!existsSync(dirPath)) return [];
|
|
475
|
+
try {
|
|
476
|
+
return readdirSync(dirPath, { withFileTypes: true })
|
|
477
|
+
.filter((d) => d.isDirectory())
|
|
478
|
+
.map((d) => d.name)
|
|
479
|
+
.sort();
|
|
480
|
+
} catch {
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function copyDirRecursiveSync(srcDir: string, destDir: string): void {
|
|
486
|
+
mkdirSync(destDir, { recursive: true });
|
|
487
|
+
const entries = readdirSync(srcDir, { withFileTypes: true });
|
|
488
|
+
for (const entry of entries) {
|
|
489
|
+
const srcPath = join(srcDir, entry.name);
|
|
490
|
+
const destPath = join(destDir, entry.name);
|
|
491
|
+
|
|
492
|
+
if (entry.isDirectory()) {
|
|
493
|
+
copyDirRecursiveSync(srcPath, destPath);
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (entry.isFile()) {
|
|
498
|
+
copyFileSync(srcPath, destPath);
|
|
499
|
+
try {
|
|
500
|
+
chmodSync(destPath, statSync(srcPath).mode);
|
|
501
|
+
} catch {
|
|
502
|
+
// Best effort
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function writeBundledSkillMarker(
|
|
509
|
+
skillDir: string,
|
|
510
|
+
info: { version: string },
|
|
511
|
+
): void {
|
|
512
|
+
const markerPath = join(skillDir, BUNDLED_SKILL_MARKER_FILENAME);
|
|
513
|
+
writeFileSync(
|
|
514
|
+
markerPath,
|
|
515
|
+
JSON.stringify(
|
|
516
|
+
{
|
|
517
|
+
managed_by: "opencode-swarm-plugin",
|
|
518
|
+
version: info.version,
|
|
519
|
+
synced_at: new Date().toISOString(),
|
|
520
|
+
},
|
|
521
|
+
null,
|
|
522
|
+
2,
|
|
523
|
+
),
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function syncBundledSkillsToGlobal({
|
|
528
|
+
bundledSkillsPath,
|
|
529
|
+
globalSkillsPath,
|
|
530
|
+
version,
|
|
531
|
+
}: {
|
|
532
|
+
bundledSkillsPath: string;
|
|
533
|
+
globalSkillsPath: string;
|
|
534
|
+
version: string;
|
|
535
|
+
}): { installed: string[]; updated: string[]; skipped: string[] } {
|
|
536
|
+
const bundledSkills = listDirectoryNames(bundledSkillsPath);
|
|
537
|
+
|
|
538
|
+
const installed: string[] = [];
|
|
539
|
+
const updated: string[] = [];
|
|
540
|
+
const skipped: string[] = [];
|
|
541
|
+
|
|
542
|
+
for (const name of bundledSkills) {
|
|
543
|
+
const srcSkillDir = join(bundledSkillsPath, name);
|
|
544
|
+
const destSkillDir = join(globalSkillsPath, name);
|
|
545
|
+
const markerPath = join(destSkillDir, BUNDLED_SKILL_MARKER_FILENAME);
|
|
546
|
+
|
|
547
|
+
if (!existsSync(destSkillDir)) {
|
|
548
|
+
copyDirRecursiveSync(srcSkillDir, destSkillDir);
|
|
549
|
+
writeBundledSkillMarker(destSkillDir, { version });
|
|
550
|
+
installed.push(name);
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Only overwrite skills that we previously installed/managed
|
|
555
|
+
if (existsSync(markerPath)) {
|
|
556
|
+
rmSync(destSkillDir, { recursive: true, force: true });
|
|
557
|
+
copyDirRecursiveSync(srcSkillDir, destSkillDir);
|
|
558
|
+
writeBundledSkillMarker(destSkillDir, { version });
|
|
559
|
+
updated.push(name);
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
skipped.push(name);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return { installed, updated, skipped };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// ============================================================================
|
|
570
|
+
// AGENTS.md Update Utilities
|
|
571
|
+
// ============================================================================
|
|
572
|
+
|
|
573
|
+
function detectNewline(content: string): "\r\n" | "\n" {
|
|
574
|
+
return content.includes("\r\n") ? "\r\n" : "\n";
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function backupFileWithTimestamp(filePath: string): string | null {
|
|
578
|
+
try {
|
|
579
|
+
const dir = dirname(filePath);
|
|
580
|
+
const base = basename(filePath);
|
|
581
|
+
const timestamp = new Date()
|
|
582
|
+
.toISOString()
|
|
583
|
+
.replace(/[:.]/g, "")
|
|
584
|
+
.replace(/Z$/, "Z");
|
|
585
|
+
const backupPath = join(dir, `${base}.swarm-backup-${timestamp}`);
|
|
586
|
+
copyFileSync(filePath, backupPath);
|
|
587
|
+
return backupPath;
|
|
588
|
+
} catch {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function buildAgentsSkillsSection(
|
|
594
|
+
bundledSkillsCsv: string,
|
|
595
|
+
newline: string,
|
|
596
|
+
): string {
|
|
597
|
+
return [
|
|
598
|
+
"## Skills - Knowledge Injection",
|
|
599
|
+
"",
|
|
600
|
+
"Skills are reusable knowledge packages. Load them on-demand for specialized tasks.",
|
|
601
|
+
"",
|
|
602
|
+
"### When to Use",
|
|
603
|
+
"",
|
|
604
|
+
"- Before unfamiliar work - check if a skill exists",
|
|
605
|
+
"- When you need domain-specific patterns",
|
|
606
|
+
"- For complex workflows that benefit from guidance",
|
|
607
|
+
"",
|
|
608
|
+
"### Usage",
|
|
609
|
+
"",
|
|
610
|
+
"```bash",
|
|
611
|
+
"skills_list() # See available skills",
|
|
612
|
+
'skills_use(name="swarm-coordination") # Load a skill',
|
|
613
|
+
'skills_use(name="cli-builder", context="building a new CLI") # With context',
|
|
614
|
+
"```",
|
|
615
|
+
"",
|
|
616
|
+
`**Bundled Skills:** ${bundledSkillsCsv}`,
|
|
617
|
+
].join(newline);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function buildAgentsCassSection(newline: string): string {
|
|
621
|
+
return [
|
|
622
|
+
"## CASS - Cross-Agent Session Search",
|
|
623
|
+
"",
|
|
624
|
+
"Search across ALL your AI coding agent histories before solving problems from scratch.",
|
|
625
|
+
"",
|
|
626
|
+
"### When to Use",
|
|
627
|
+
"",
|
|
628
|
+
'- **BEFORE implementing anything**: check if any agent solved it before',
|
|
629
|
+
'- **Debugging**: "what did I try last time this error happened?"',
|
|
630
|
+
'- **Learning patterns**: "how did Cursor handle this API?"',
|
|
631
|
+
"",
|
|
632
|
+
"### Usage",
|
|
633
|
+
"",
|
|
634
|
+
"```bash",
|
|
635
|
+
"# Search all agents",
|
|
636
|
+
'cass_search(query="authentication token refresh", limit=5)',
|
|
637
|
+
"",
|
|
638
|
+
"# Filter by agent/time",
|
|
639
|
+
'cass_search(query="useEffect cleanup", agent="claude", days=7)',
|
|
640
|
+
"",
|
|
641
|
+
"# View specific result",
|
|
642
|
+
'cass_view(path="/path/from/search", line=42)',
|
|
643
|
+
"",
|
|
644
|
+
"# Expand context around match",
|
|
645
|
+
'cass_expand(path="/path", line=42, context=10)',
|
|
646
|
+
"```",
|
|
647
|
+
"",
|
|
648
|
+
"**Pro tip:** Query CASS at the START of complex tasks. Past solutions save time.",
|
|
649
|
+
].join(newline);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function buildAgentsSemanticMemorySection(newline: string): string {
|
|
653
|
+
return [
|
|
654
|
+
"## Semantic Memory - Persistent Learning",
|
|
655
|
+
"",
|
|
656
|
+
"Store and retrieve learnings across sessions. Memories persist and are searchable.",
|
|
657
|
+
"",
|
|
658
|
+
"### When to Use",
|
|
659
|
+
"",
|
|
660
|
+
"- After solving a tricky problem - store the solution",
|
|
661
|
+
"- After making architectural decisions - store the reasoning",
|
|
662
|
+
"- Before starting work - search for relevant past learnings",
|
|
663
|
+
"- When you discover project-specific patterns",
|
|
664
|
+
"",
|
|
665
|
+
"### Usage",
|
|
666
|
+
"",
|
|
667
|
+
"```bash",
|
|
668
|
+
"# Store a learning",
|
|
669
|
+
'semantic-memory_store(information="OAuth refresh tokens need 5min buffer before expiry", metadata="auth, tokens")',
|
|
670
|
+
"",
|
|
671
|
+
"# Search for relevant memories",
|
|
672
|
+
'semantic-memory_find(query="token refresh", limit=5)',
|
|
673
|
+
"",
|
|
674
|
+
"# Validate a memory is still accurate (resets decay timer)",
|
|
675
|
+
'semantic-memory_validate(id="mem_123")',
|
|
676
|
+
"```",
|
|
677
|
+
"",
|
|
678
|
+
"**Pro tip:** Store the WHY, not just the WHAT. Future you needs context.",
|
|
679
|
+
].join(newline);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function updateAgentsToolPreferencesBlock(
|
|
683
|
+
content: string,
|
|
684
|
+
newline: string,
|
|
685
|
+
): { content: string; changed: boolean } {
|
|
686
|
+
const lower = content.toLowerCase();
|
|
687
|
+
const openTag = "<tool_preferences>";
|
|
688
|
+
const closeTag = "</tool_preferences>";
|
|
689
|
+
const openIdx = lower.indexOf(openTag);
|
|
690
|
+
const closeIdx = lower.indexOf(closeTag);
|
|
691
|
+
|
|
692
|
+
if (openIdx === -1 || closeIdx === -1 || closeIdx <= openIdx) {
|
|
693
|
+
return { content, changed: false };
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const blockStart = openIdx;
|
|
697
|
+
const blockEnd = closeIdx + closeTag.length;
|
|
698
|
+
const before = content.slice(0, blockStart);
|
|
699
|
+
const block = content.slice(blockStart, blockEnd);
|
|
700
|
+
const after = content.slice(blockEnd);
|
|
701
|
+
|
|
702
|
+
const hasSkillsTools =
|
|
703
|
+
/skills_list/i.test(block) &&
|
|
704
|
+
/skills_use/i.test(block) &&
|
|
705
|
+
/skills_read/i.test(block);
|
|
706
|
+
const hasCassTools =
|
|
707
|
+
/cass_search/i.test(block) &&
|
|
708
|
+
/cass_view/i.test(block) &&
|
|
709
|
+
/cass_expand/i.test(block);
|
|
710
|
+
const hasSemanticTools =
|
|
711
|
+
/semantic-memory_find/i.test(block) &&
|
|
712
|
+
/semantic-memory_store/i.test(block);
|
|
713
|
+
|
|
714
|
+
const linesToAdd: string[] = [];
|
|
715
|
+
if (!hasSkillsTools) {
|
|
716
|
+
linesToAdd.push(
|
|
717
|
+
"- **skills_list, skills_use, skills_read** - Knowledge injection (load reusable skills)",
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
if (!hasCassTools) {
|
|
721
|
+
linesToAdd.push(
|
|
722
|
+
"- **cass_search, cass_view, cass_expand** - Search past agent sessions",
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
if (!hasSemanticTools) {
|
|
726
|
+
linesToAdd.push(
|
|
727
|
+
"- **semantic-memory_find, semantic-memory_store, semantic-memory_validate** - Persistent learning across sessions",
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (linesToAdd.length === 0) {
|
|
732
|
+
return { content, changed: false };
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const headingRe = /^###\s+Other Custom Tools.*$/m;
|
|
736
|
+
const headingMatch = headingRe.exec(block);
|
|
737
|
+
|
|
738
|
+
let updatedBlock: string;
|
|
739
|
+
const insertion = newline + newline + linesToAdd.join(newline) + newline;
|
|
740
|
+
|
|
741
|
+
if (headingMatch) {
|
|
742
|
+
const insertAt = headingMatch.index + headingMatch[0].length;
|
|
743
|
+
updatedBlock = block.slice(0, insertAt) + insertion + block.slice(insertAt);
|
|
744
|
+
} else {
|
|
745
|
+
const closeInBlock = block.toLowerCase().lastIndexOf(closeTag);
|
|
746
|
+
updatedBlock =
|
|
747
|
+
block.slice(0, closeInBlock) + insertion + block.slice(closeInBlock);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
return { content: before + updatedBlock + after, changed: true };
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function updateAgentsMdContent({
|
|
754
|
+
content,
|
|
755
|
+
bundledSkillsCsv,
|
|
756
|
+
}: {
|
|
757
|
+
content: string;
|
|
758
|
+
bundledSkillsCsv: string;
|
|
759
|
+
}): { updated: string; changed: boolean; changes: string[] } {
|
|
760
|
+
const newline = detectNewline(content);
|
|
761
|
+
const changes: string[] = [];
|
|
762
|
+
let updated = content;
|
|
763
|
+
|
|
764
|
+
// Update bundled skills line (common formats)
|
|
765
|
+
const beforeBundled = updated;
|
|
766
|
+
updated = updated.replace(
|
|
767
|
+
/^\*\*Bundled Skills:\*\*.*$/gm,
|
|
768
|
+
`**Bundled Skills:** ${bundledSkillsCsv}`,
|
|
769
|
+
);
|
|
770
|
+
updated = updated.replace(
|
|
771
|
+
/^\*\*Bundled:\*\*.*$/gm,
|
|
772
|
+
`**Bundled:** ${bundledSkillsCsv}`,
|
|
773
|
+
);
|
|
774
|
+
if (updated !== beforeBundled) {
|
|
775
|
+
changes.push("Updated bundled skills list");
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Update tool preferences block if present
|
|
779
|
+
const toolPrefsResult = updateAgentsToolPreferencesBlock(updated, newline);
|
|
780
|
+
if (toolPrefsResult.changed) {
|
|
781
|
+
updated = toolPrefsResult.content;
|
|
782
|
+
changes.push("Updated tool_preferences tool list");
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Add missing sections (append at end)
|
|
786
|
+
const hasSkillsSection =
|
|
787
|
+
/^#{1,6}\s+Skills\b/im.test(updated) || /skills_list\(\)/.test(updated);
|
|
788
|
+
const hasCassSection =
|
|
789
|
+
/^#{1,6}\s+.*CASS\b/im.test(updated) || /cass_search\(/.test(updated);
|
|
790
|
+
const hasSemanticMemorySection =
|
|
791
|
+
/^#{1,6}\s+Semantic Memory\b/im.test(updated) ||
|
|
792
|
+
/semantic-memory_store\(/.test(updated);
|
|
793
|
+
|
|
794
|
+
const sectionsToAppend: string[] = [];
|
|
795
|
+
if (!hasSkillsSection) {
|
|
796
|
+
sectionsToAppend.push(
|
|
797
|
+
buildAgentsSkillsSection(bundledSkillsCsv, newline),
|
|
798
|
+
);
|
|
799
|
+
changes.push("Added Skills section");
|
|
800
|
+
}
|
|
801
|
+
if (!hasCassSection) {
|
|
802
|
+
sectionsToAppend.push(buildAgentsCassSection(newline));
|
|
803
|
+
changes.push("Added CASS section");
|
|
804
|
+
}
|
|
805
|
+
if (!hasSemanticMemorySection) {
|
|
806
|
+
sectionsToAppend.push(buildAgentsSemanticMemorySection(newline));
|
|
807
|
+
changes.push("Added Semantic Memory section");
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (sectionsToAppend.length > 0) {
|
|
811
|
+
const trimmed = updated.replace(/\s+$/g, "");
|
|
812
|
+
const needsRule = !/^\s*---\s*$/m.test(trimmed.slice(-3000));
|
|
813
|
+
updated =
|
|
814
|
+
trimmed +
|
|
815
|
+
newline +
|
|
816
|
+
newline +
|
|
817
|
+
(needsRule ? `---${newline}${newline}` : "") +
|
|
818
|
+
sectionsToAppend.join(newline + newline);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Ensure trailing newline
|
|
822
|
+
if (!updated.endsWith(newline)) {
|
|
823
|
+
updated += newline;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return { updated, changed: updated !== content, changes };
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function updateAgentsMdFile({
|
|
830
|
+
agentsPath,
|
|
831
|
+
bundledSkillsCsv,
|
|
832
|
+
}: {
|
|
833
|
+
agentsPath: string;
|
|
834
|
+
bundledSkillsCsv: string;
|
|
835
|
+
}): { changed: boolean; backupPath?: string; changes: string[] } {
|
|
836
|
+
const original = readFileSync(agentsPath, "utf-8");
|
|
837
|
+
const { updated, changed, changes } = updateAgentsMdContent({
|
|
838
|
+
content: original,
|
|
839
|
+
bundledSkillsCsv,
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
if (!changed) {
|
|
843
|
+
return { changed: false, changes: ["No changes needed"] };
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const backupPath = backupFileWithTimestamp(agentsPath) || undefined;
|
|
847
|
+
writeFileSync(agentsPath, updated, "utf-8");
|
|
848
|
+
return { changed: true, backupPath, changes };
|
|
849
|
+
}
|
|
850
|
+
|
|
457
851
|
// ============================================================================
|
|
458
852
|
// File Templates
|
|
459
853
|
// ============================================================================
|
|
@@ -834,6 +1228,8 @@ async function setup() {
|
|
|
834
1228
|
|
|
835
1229
|
p.intro("opencode-swarm-plugin v" + VERSION);
|
|
836
1230
|
|
|
1231
|
+
let isReinstall = false;
|
|
1232
|
+
|
|
837
1233
|
// Check if already configured FIRST
|
|
838
1234
|
const configDir = join(homedir(), ".config", "opencode");
|
|
839
1235
|
const pluginDir = join(configDir, "plugin");
|
|
@@ -872,7 +1268,7 @@ async function setup() {
|
|
|
872
1268
|
{
|
|
873
1269
|
value: "reinstall",
|
|
874
1270
|
label: "Reinstall everything",
|
|
875
|
-
hint: "Check deps
|
|
1271
|
+
hint: "Check deps, sync bundled skills, regenerate config files",
|
|
876
1272
|
},
|
|
877
1273
|
],
|
|
878
1274
|
});
|
|
@@ -928,6 +1324,9 @@ async function setup() {
|
|
|
928
1324
|
p.outro("Models updated! Your customizations are preserved.");
|
|
929
1325
|
return;
|
|
930
1326
|
}
|
|
1327
|
+
if (action === "reinstall") {
|
|
1328
|
+
isReinstall = true;
|
|
1329
|
+
}
|
|
931
1330
|
// action === "reinstall" - fall through to full setup
|
|
932
1331
|
}
|
|
933
1332
|
|
|
@@ -1177,17 +1576,74 @@ async function setup() {
|
|
|
1177
1576
|
|
|
1178
1577
|
p.log.success("Skills directory: " + skillsDir);
|
|
1179
1578
|
|
|
1180
|
-
// Show bundled skills info
|
|
1579
|
+
// Show bundled skills info (and optionally sync to global skills dir)
|
|
1181
1580
|
const bundledSkillsPath = join(__dirname, "..", "global-skills");
|
|
1581
|
+
const bundledSkills = listDirectoryNames(bundledSkillsPath);
|
|
1182
1582
|
if (existsSync(bundledSkillsPath)) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1583
|
+
if (bundledSkills.length > 0) {
|
|
1584
|
+
p.log.message(dim(" Bundled skills: " + bundledSkills.join(", ")));
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// If the user keeps their skills in ~/.config/opencode/skills, offer to sync the bundled set
|
|
1589
|
+
if (bundledSkills.length > 0) {
|
|
1590
|
+
const globalSkills = listDirectoryNames(skillsDir);
|
|
1591
|
+
const managedBundled = globalSkills.filter((name) =>
|
|
1592
|
+
existsSync(join(skillsDir, name, BUNDLED_SKILL_MARKER_FILENAME)),
|
|
1593
|
+
);
|
|
1594
|
+
const missingBundled = bundledSkills.filter(
|
|
1595
|
+
(name) => !globalSkills.includes(name),
|
|
1596
|
+
);
|
|
1597
|
+
|
|
1598
|
+
if (missingBundled.length > 0 || managedBundled.length > 0) {
|
|
1599
|
+
const shouldSync = await p.confirm({
|
|
1600
|
+
message:
|
|
1601
|
+
"Sync bundled skills into your global skills directory? " +
|
|
1602
|
+
(missingBundled.length > 0
|
|
1603
|
+
? `(${missingBundled.length} missing)`
|
|
1604
|
+
: "(update managed skills)"),
|
|
1605
|
+
initialValue: isReinstall || missingBundled.length > 0,
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
if (p.isCancel(shouldSync)) {
|
|
1609
|
+
p.cancel("Setup cancelled");
|
|
1610
|
+
process.exit(0);
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
if (shouldSync) {
|
|
1614
|
+
const syncSpinner = p.spinner();
|
|
1615
|
+
syncSpinner.start("Syncing bundled skills...");
|
|
1616
|
+
try {
|
|
1617
|
+
const { installed, updated, skipped } = syncBundledSkillsToGlobal({
|
|
1618
|
+
bundledSkillsPath,
|
|
1619
|
+
globalSkillsPath: skillsDir,
|
|
1620
|
+
version: VERSION,
|
|
1621
|
+
});
|
|
1622
|
+
syncSpinner.stop("Bundled skills synced");
|
|
1623
|
+
|
|
1624
|
+
if (installed.length > 0) {
|
|
1625
|
+
p.log.success("Installed: " + installed.join(", "));
|
|
1626
|
+
}
|
|
1627
|
+
if (updated.length > 0) {
|
|
1628
|
+
p.log.success("Updated: " + updated.join(", "));
|
|
1629
|
+
}
|
|
1630
|
+
if (skipped.length > 0) {
|
|
1631
|
+
p.log.message(
|
|
1632
|
+
dim(
|
|
1633
|
+
"Skipped (already exists, not managed): " + skipped.join(", "),
|
|
1634
|
+
),
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
syncSpinner.stop("Could not sync bundled skills");
|
|
1639
|
+
p.log.warn(
|
|
1640
|
+
"Bundled skills are still available from the package via skills_list.",
|
|
1641
|
+
);
|
|
1642
|
+
p.log.message(
|
|
1643
|
+
dim(error instanceof Error ? error.message : String(error)),
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1191
1647
|
}
|
|
1192
1648
|
}
|
|
1193
1649
|
|
|
@@ -1203,111 +1659,28 @@ async function setup() {
|
|
|
1203
1659
|
const s = p.spinner();
|
|
1204
1660
|
s.start("Updating AGENTS.md...");
|
|
1205
1661
|
|
|
1206
|
-
const agentsPrompt = `You are updating the user's AGENTS.md file to add swarm plugin awareness.
|
|
1207
|
-
|
|
1208
|
-
## Task
|
|
1209
|
-
Read ${agentsPath} and add sections for Skills, CASS, and Semantic Memory if they don't exist. Update existing sections if present.
|
|
1210
|
-
|
|
1211
|
-
## What to Add
|
|
1212
|
-
|
|
1213
|
-
1. **Tool Preferences** - If there's a tool_preferences section, add these tools:
|
|
1214
|
-
- skills_list, skills_use, skills_read - knowledge injection
|
|
1215
|
-
- cass_search, cass_view, cass_expand - search past agent sessions
|
|
1216
|
-
- semantic-memory_find, semantic-memory_store - persistent learning
|
|
1217
|
-
|
|
1218
|
-
2. **Skills Section** - Add this (adapt style to match the file):
|
|
1219
|
-
|
|
1220
|
-
### Skills (Knowledge Injection)
|
|
1221
|
-
|
|
1222
|
-
Skills are reusable knowledge packages. Load them on-demand for specialized tasks.
|
|
1223
|
-
|
|
1224
|
-
**When to Use:**
|
|
1225
|
-
- Before unfamiliar work - check if a skill exists
|
|
1226
|
-
- When you need domain-specific patterns
|
|
1227
|
-
- For complex workflows that benefit from guidance
|
|
1228
|
-
|
|
1229
|
-
**Usage:**
|
|
1230
|
-
\`\`\`
|
|
1231
|
-
skills_list() # See available skills
|
|
1232
|
-
skills_use(name="swarm-coordination") # Load a skill
|
|
1233
|
-
skills_use(name="cli-builder", context="building a new CLI") # With context
|
|
1234
|
-
\`\`\`
|
|
1235
|
-
|
|
1236
|
-
**Bundled Skills:** cli-builder, learning-systems, mcp-tool-authoring, skill-creator, swarm-coordination
|
|
1237
|
-
|
|
1238
|
-
3. **CASS Section** - Add this:
|
|
1239
|
-
|
|
1240
|
-
### CASS (Cross-Agent Session Search)
|
|
1241
|
-
|
|
1242
|
-
Search across ALL your AI coding agent histories before solving problems from scratch.
|
|
1243
|
-
|
|
1244
|
-
**When to Use:**
|
|
1245
|
-
- BEFORE implementing anything - check if any agent solved it before
|
|
1246
|
-
- When debugging - "what did I try last time this error happened?"
|
|
1247
|
-
- When learning patterns - "how did Cursor handle this API?"
|
|
1248
|
-
|
|
1249
|
-
**Usage:**
|
|
1250
|
-
\`\`\`
|
|
1251
|
-
cass_search(query="authentication token refresh", limit=5) # Search all agents
|
|
1252
|
-
cass_search(query="useEffect cleanup", agent="claude", days=7) # Filter by agent/time
|
|
1253
|
-
cass_view(path="/path/from/search", line=42) # View specific result
|
|
1254
|
-
cass_expand(path="/path", line=42, context=10) # Expand context around match
|
|
1255
|
-
\`\`\`
|
|
1256
|
-
|
|
1257
|
-
**Pro tip:** Query CASS at the START of complex tasks. Past solutions save time.
|
|
1258
|
-
|
|
1259
|
-
4. **Semantic Memory Section** - Add this:
|
|
1260
|
-
|
|
1261
|
-
### Semantic Memory (Persistent Learning)
|
|
1262
|
-
|
|
1263
|
-
Store and retrieve learnings across sessions. Memories persist and are searchable.
|
|
1264
|
-
|
|
1265
|
-
**When to Use:**
|
|
1266
|
-
- After solving a tricky problem - store the solution
|
|
1267
|
-
- After making architectural decisions - store the reasoning
|
|
1268
|
-
- Before starting work - search for relevant past learnings
|
|
1269
|
-
- When you discover project-specific patterns
|
|
1270
|
-
|
|
1271
|
-
**Usage:**
|
|
1272
|
-
\`\`\`
|
|
1273
|
-
# Store a learning
|
|
1274
|
-
semantic-memory_store(information="OAuth refresh tokens need 5min buffer before expiry", metadata="auth, tokens")
|
|
1275
|
-
|
|
1276
|
-
# Search for relevant memories
|
|
1277
|
-
semantic-memory_find(query="token refresh", limit=5)
|
|
1278
|
-
|
|
1279
|
-
# Validate a memory is still accurate (resets decay timer)
|
|
1280
|
-
semantic-memory_validate(id="mem_123")
|
|
1281
|
-
\`\`\`
|
|
1282
|
-
|
|
1283
|
-
**Pro tip:** Store the WHY, not just the WHAT. Future you needs context.
|
|
1284
|
-
|
|
1285
|
-
## Rules
|
|
1286
|
-
- Preserve existing content and style
|
|
1287
|
-
- Don't duplicate - update existing sections if present
|
|
1288
|
-
- Keep tone consistent with the rest of the file
|
|
1289
|
-
- Place sections in logical order (Skills, CASS, Semantic Memory)
|
|
1290
|
-
- If there's a tool_preferences section, add the tools there too
|
|
1291
|
-
|
|
1292
|
-
Edit the file now.`;
|
|
1293
|
-
|
|
1294
1662
|
try {
|
|
1295
|
-
const
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1663
|
+
const bundledSkillsCsv =
|
|
1664
|
+
bundledSkills.length > 0
|
|
1665
|
+
? bundledSkills.join(", ")
|
|
1666
|
+
: "cli-builder, learning-systems, skill-creator, swarm-coordination, system-design, testing-patterns";
|
|
1667
|
+
|
|
1668
|
+
const result = updateAgentsMdFile({ agentsPath, bundledSkillsCsv });
|
|
1300
1669
|
|
|
1301
|
-
if (result.
|
|
1670
|
+
if (result.changed) {
|
|
1302
1671
|
s.stop("AGENTS.md updated");
|
|
1303
|
-
p.log.success("
|
|
1672
|
+
p.log.success("Updated: " + agentsPath);
|
|
1673
|
+
if (result.backupPath) {
|
|
1674
|
+
p.log.message(dim(" Backup: " + result.backupPath));
|
|
1675
|
+
}
|
|
1304
1676
|
} else {
|
|
1305
|
-
s.stop("
|
|
1306
|
-
p.log.warn("You can manually add skills section later");
|
|
1677
|
+
s.stop("AGENTS.md already up to date");
|
|
1307
1678
|
}
|
|
1308
|
-
} catch {
|
|
1679
|
+
} catch (error) {
|
|
1309
1680
|
s.stop("Could not update AGENTS.md");
|
|
1310
|
-
p.log.
|
|
1681
|
+
p.log.error(
|
|
1682
|
+
error instanceof Error ? error.message : "Unknown error updating file",
|
|
1683
|
+
);
|
|
1311
1684
|
}
|
|
1312
1685
|
}
|
|
1313
1686
|
}
|
|
@@ -1786,15 +2159,6 @@ async function agents() {
|
|
|
1786
2159
|
return;
|
|
1787
2160
|
}
|
|
1788
2161
|
|
|
1789
|
-
// Check if opencode is available
|
|
1790
|
-
const opencode = await checkCommand("opencode", ["--version"]);
|
|
1791
|
-
if (!opencode.available) {
|
|
1792
|
-
p.log.error("OpenCode not found");
|
|
1793
|
-
p.log.message(dim("Install: npm install -g opencode"));
|
|
1794
|
-
p.outro("Aborted");
|
|
1795
|
-
return;
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
2162
|
const confirm = await p.confirm({
|
|
1799
2163
|
message: "Update AGENTS.md with skill awareness?",
|
|
1800
2164
|
initialValue: true,
|
|
@@ -1808,82 +2172,28 @@ async function agents() {
|
|
|
1808
2172
|
const s = p.spinner();
|
|
1809
2173
|
s.start("Updating AGENTS.md with skill awareness...");
|
|
1810
2174
|
|
|
1811
|
-
const
|
|
1812
|
-
|
|
1813
|
-
## Task
|
|
1814
|
-
Read ~/.config/opencode/AGENTS.md and add sections for Skills, CASS, and Semantic Memory if they don't exist.
|
|
1815
|
-
|
|
1816
|
-
## What to Add
|
|
1817
|
-
|
|
1818
|
-
1. **Tool Preferences** - Add these tools to any tool_preferences section:
|
|
1819
|
-
- skills_list, skills_use, skills_read - knowledge injection
|
|
1820
|
-
- cass_search, cass_view, cass_expand - search past agent sessions
|
|
1821
|
-
- semantic-memory_find, semantic-memory_store - persistent learning
|
|
1822
|
-
|
|
1823
|
-
2. **Skills Section**:
|
|
1824
|
-
|
|
1825
|
-
### Skills (Knowledge Injection)
|
|
1826
|
-
|
|
1827
|
-
Skills are reusable knowledge packages. Load on-demand for specialized tasks.
|
|
1828
|
-
|
|
1829
|
-
**When to Use:** Before unfamiliar work, when you need domain patterns, for complex workflows.
|
|
1830
|
-
|
|
1831
|
-
\`\`\`
|
|
1832
|
-
skills_list() # See available skills
|
|
1833
|
-
skills_use(name="swarm-coordination") # Load a skill
|
|
1834
|
-
\`\`\`
|
|
1835
|
-
|
|
1836
|
-
**Bundled:** cli-builder, learning-systems, mcp-tool-authoring, skill-creator, swarm-coordination
|
|
1837
|
-
|
|
1838
|
-
3. **CASS Section**:
|
|
1839
|
-
|
|
1840
|
-
### CASS (Cross-Agent Session Search)
|
|
1841
|
-
|
|
1842
|
-
Search ALL your AI coding agent histories before solving from scratch.
|
|
1843
|
-
|
|
1844
|
-
**When to Use:** BEFORE implementing - check if solved before. When debugging - what worked last time?
|
|
1845
|
-
|
|
1846
|
-
\`\`\`
|
|
1847
|
-
cass_search(query="auth token refresh", limit=5) # Search all agents
|
|
1848
|
-
cass_view(path="/path/from/search", line=42) # View result
|
|
1849
|
-
\`\`\`
|
|
1850
|
-
|
|
1851
|
-
4. **Semantic Memory Section**:
|
|
1852
|
-
|
|
1853
|
-
### Semantic Memory (Persistent Learning)
|
|
1854
|
-
|
|
1855
|
-
Store and retrieve learnings across sessions.
|
|
1856
|
-
|
|
1857
|
-
**When to Use:** After solving tricky problems, after architectural decisions, before starting work.
|
|
1858
|
-
|
|
1859
|
-
\`\`\`
|
|
1860
|
-
semantic-memory_store(information="OAuth needs 5min buffer", metadata="auth")
|
|
1861
|
-
semantic-memory_find(query="token refresh", limit=5)
|
|
1862
|
-
\`\`\`
|
|
1863
|
-
|
|
1864
|
-
## Rules
|
|
1865
|
-
- Preserve existing content and style
|
|
1866
|
-
- Don't duplicate - update if sections exist
|
|
1867
|
-
- Keep tone consistent
|
|
1868
|
-
|
|
1869
|
-
Edit the file now.`;
|
|
2175
|
+
const bundledSkillsPath = join(__dirname, "..", "global-skills");
|
|
2176
|
+
const bundledSkills = listDirectoryNames(bundledSkillsPath);
|
|
1870
2177
|
|
|
1871
2178
|
try {
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2179
|
+
const bundledSkillsCsv =
|
|
2180
|
+
bundledSkills.length > 0
|
|
2181
|
+
? bundledSkills.join(", ")
|
|
2182
|
+
: "cli-builder, learning-systems, skill-creator, swarm-coordination, system-design, testing-patterns";
|
|
2183
|
+
|
|
2184
|
+
const result = updateAgentsMdFile({ agentsPath, bundledSkillsCsv });
|
|
1876
2185
|
|
|
1877
|
-
if (result.
|
|
2186
|
+
if (result.changed) {
|
|
1878
2187
|
s.stop("AGENTS.md updated with skill awareness");
|
|
1879
2188
|
p.log.success("Skills section added to " + agentsPath);
|
|
1880
2189
|
p.log.message(
|
|
1881
2190
|
dim("Skills available: skills_list, skills_use, skills_read"),
|
|
1882
2191
|
);
|
|
2192
|
+
if (result.backupPath) {
|
|
2193
|
+
p.log.message(dim("Backup: " + result.backupPath));
|
|
2194
|
+
}
|
|
1883
2195
|
} else {
|
|
1884
|
-
|
|
1885
|
-
s.stop("Failed to update AGENTS.md");
|
|
1886
|
-
p.log.error(stderr || "Unknown error");
|
|
2196
|
+
s.stop("AGENTS.md already up to date");
|
|
1887
2197
|
}
|
|
1888
2198
|
} catch (error) {
|
|
1889
2199
|
s.stop("Failed to update AGENTS.md");
|