opencode-swarm-plugin 0.23.6 → 0.25.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 +53 -0
- package/README.md +155 -3
- package/bin/swarm.ts +635 -234
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +620 -89
- package/dist/plugin.js +548 -89
- 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 +1 -1
- 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 +15 -51
- 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
|
// ============================================================================
|
|
@@ -489,7 +883,7 @@ const SWARM_COMMAND = `---
|
|
|
489
883
|
description: Decompose task into parallel subtasks and coordinate agents
|
|
490
884
|
---
|
|
491
885
|
|
|
492
|
-
You are a swarm coordinator.
|
|
886
|
+
You are a swarm coordinator. Your job is to clarify the task, decompose it into beads, and spawn parallel agents.
|
|
493
887
|
|
|
494
888
|
## Task
|
|
495
889
|
|
|
@@ -497,42 +891,84 @@ $ARGUMENTS
|
|
|
497
891
|
|
|
498
892
|
## Workflow
|
|
499
893
|
|
|
500
|
-
###
|
|
894
|
+
### Phase 0: Socratic Planning (INTERACTIVE - unless --fast)
|
|
895
|
+
|
|
896
|
+
**Before decomposing, clarify the task with the user.**
|
|
897
|
+
|
|
898
|
+
Check for flags in the task:
|
|
899
|
+
- \`--fast\` → Skip questions, use reasonable defaults
|
|
900
|
+
- \`--auto\` → Zero interaction, heuristic decisions
|
|
901
|
+
- \`--confirm-only\` → Show plan, get yes/no only
|
|
902
|
+
|
|
903
|
+
**Default (no flags): Full Socratic Mode**
|
|
904
|
+
|
|
905
|
+
1. **Analyze task for ambiguity:**
|
|
906
|
+
- Scope unclear? (what's included/excluded)
|
|
907
|
+
- Strategy unclear? (file-based vs feature-based)
|
|
908
|
+
- Dependencies unclear? (what needs to exist first)
|
|
909
|
+
- Success criteria unclear? (how do we know it's done)
|
|
910
|
+
|
|
911
|
+
2. **If clarification needed, ask ONE question at a time:**
|
|
912
|
+
\`\`\`
|
|
913
|
+
The task "<task>" needs clarification before I can decompose it.
|
|
914
|
+
|
|
915
|
+
**Question:** <specific question>
|
|
916
|
+
|
|
917
|
+
Options:
|
|
918
|
+
a) <option 1> - <tradeoff>
|
|
919
|
+
b) <option 2> - <tradeoff>
|
|
920
|
+
c) <option 3> - <tradeoff>
|
|
921
|
+
|
|
922
|
+
I'd recommend (b) because <reason>. Which approach?
|
|
923
|
+
\`\`\`
|
|
924
|
+
|
|
925
|
+
3. **Wait for user response before proceeding**
|
|
926
|
+
|
|
927
|
+
4. **Iterate if needed** (max 2-3 questions)
|
|
928
|
+
|
|
929
|
+
**Rules:**
|
|
930
|
+
- ONE question at a time - don't overwhelm
|
|
931
|
+
- Offer concrete options - not open-ended
|
|
932
|
+
- Lead with recommendation - save cognitive load
|
|
933
|
+
- Wait for answer - don't assume
|
|
934
|
+
|
|
935
|
+
### Phase 1: Initialize
|
|
501
936
|
\`swarmmail_init(project_path="$PWD", task_description="Swarm: <task>")\`
|
|
502
937
|
|
|
503
|
-
### 2
|
|
938
|
+
### Phase 2: Knowledge Gathering (MANDATORY)
|
|
504
939
|
|
|
505
940
|
**Before decomposing, query ALL knowledge sources:**
|
|
506
941
|
|
|
507
942
|
\`\`\`
|
|
508
943
|
semantic-memory_find(query="<task keywords>", limit=5) # Past learnings
|
|
509
944
|
cass_search(query="<task description>", limit=5) # Similar past tasks
|
|
510
|
-
pdf-brain_search(query="<domain concepts>", limit=5) # Design patterns
|
|
511
945
|
skills_list() # Available skills
|
|
512
946
|
\`\`\`
|
|
513
947
|
|
|
514
948
|
Synthesize findings into shared_context for workers.
|
|
515
949
|
|
|
516
|
-
### 3
|
|
950
|
+
### Phase 3: Decompose
|
|
517
951
|
\`\`\`
|
|
518
952
|
swarm_select_strategy(task="<task>")
|
|
519
953
|
swarm_plan_prompt(task="<task>", context="<synthesized knowledge>")
|
|
520
954
|
swarm_validate_decomposition(response="<BeadTree JSON>")
|
|
521
955
|
\`\`\`
|
|
522
956
|
|
|
523
|
-
### 4
|
|
957
|
+
### Phase 4: Create Beads
|
|
524
958
|
\`beads_create_epic(epic_title="<task>", subtasks=[...])\`
|
|
525
959
|
|
|
526
|
-
### 5
|
|
960
|
+
### Phase 5: Reserve Files
|
|
527
961
|
\`swarmmail_reserve(paths=[...], reason="<bead-id>: <desc>")\`
|
|
528
962
|
|
|
529
|
-
### 6
|
|
963
|
+
### Phase 6: Spawn Agents (ALL in single message)
|
|
530
964
|
\`\`\`
|
|
531
|
-
swarm_spawn_subtask(bead_id, epic_id, subtask_title, files, shared_context)
|
|
965
|
+
swarm_spawn_subtask(bead_id, epic_id, subtask_title, files, shared_context, project_path="$PWD")
|
|
532
966
|
Task(subagent_type="swarm/worker", prompt="<from above>")
|
|
533
967
|
\`\`\`
|
|
534
968
|
|
|
535
|
-
|
|
969
|
+
**IMPORTANT:** Pass \`project_path\` to \`swarm_spawn_subtask\` so workers can call \`swarmmail_init\`.
|
|
970
|
+
|
|
971
|
+
### Phase 7: Monitor
|
|
536
972
|
\`\`\`
|
|
537
973
|
swarm_status(epic_id, project_key)
|
|
538
974
|
swarmmail_inbox()
|
|
@@ -540,7 +976,7 @@ swarmmail_inbox()
|
|
|
540
976
|
|
|
541
977
|
Intervene if: blocked >5min, file conflicts, scope creep.
|
|
542
978
|
|
|
543
|
-
### 8
|
|
979
|
+
### Phase 8: Complete
|
|
544
980
|
\`\`\`
|
|
545
981
|
swarm_complete(...)
|
|
546
982
|
beads_sync()
|
|
@@ -555,7 +991,15 @@ beads_sync()
|
|
|
555
991
|
| risk-based | Bug fixes, security | fix, bug, security, critical, urgent |
|
|
556
992
|
| research-based | Investigation, discovery | research, investigate, explore, learn |
|
|
557
993
|
|
|
558
|
-
|
|
994
|
+
## Flag Reference
|
|
995
|
+
|
|
996
|
+
| Flag | Effect |
|
|
997
|
+
|------|--------|
|
|
998
|
+
| \`--fast\` | Skip Socratic questions, use defaults |
|
|
999
|
+
| \`--auto\` | Zero interaction, heuristic decisions |
|
|
1000
|
+
| \`--confirm-only\` | Show plan, get yes/no only |
|
|
1001
|
+
|
|
1002
|
+
Begin with Phase 0 (Socratic Planning) unless \`--fast\` or \`--auto\` flag is present.
|
|
559
1003
|
`;
|
|
560
1004
|
|
|
561
1005
|
const getPlannerAgent = (model: string) => `---
|
|
@@ -623,47 +1067,61 @@ description: Executes subtasks in a swarm - fast, focused, cost-effective
|
|
|
623
1067
|
model: ${model}
|
|
624
1068
|
---
|
|
625
1069
|
|
|
626
|
-
You are a swarm worker agent.
|
|
1070
|
+
You are a swarm worker agent. Your prompt contains a **MANDATORY SURVIVAL CHECKLIST** - follow it IN ORDER.
|
|
627
1071
|
|
|
628
|
-
##
|
|
1072
|
+
## CRITICAL: Read Your Prompt Carefully
|
|
629
1073
|
|
|
630
|
-
Your prompt
|
|
631
|
-
-
|
|
632
|
-
-
|
|
633
|
-
-
|
|
1074
|
+
Your Task prompt contains detailed instructions including:
|
|
1075
|
+
- 9-step survival checklist (FOLLOW IN ORDER)
|
|
1076
|
+
- File reservations (YOU reserve, not coordinator)
|
|
1077
|
+
- Progress reporting requirements
|
|
1078
|
+
- Completion protocol
|
|
634
1079
|
|
|
635
|
-
**
|
|
1080
|
+
**DO NOT skip steps.** The checklist exists because skipping steps causes:
|
|
1081
|
+
- Lost work (no tracking)
|
|
1082
|
+
- Edit conflicts (no reservations)
|
|
1083
|
+
- Wasted time (no semantic memory query)
|
|
1084
|
+
- Silent failures (no progress reports)
|
|
636
1085
|
|
|
637
|
-
##
|
|
1086
|
+
## Step Summary (details in your prompt)
|
|
638
1087
|
|
|
639
|
-
1. **
|
|
640
|
-
2. **
|
|
641
|
-
3. **
|
|
642
|
-
4. **
|
|
643
|
-
5. **
|
|
1088
|
+
1. **swarmmail_init()** - FIRST, before anything else
|
|
1089
|
+
2. **semantic-memory_find()** - Check past learnings
|
|
1090
|
+
3. **skills_list() / skills_use()** - Load relevant skills
|
|
1091
|
+
4. **swarmmail_reserve()** - YOU reserve your files
|
|
1092
|
+
5. **Do the work** - Read, implement, verify
|
|
1093
|
+
6. **swarm_progress()** - Report at 25/50/75%
|
|
1094
|
+
7. **swarm_checkpoint()** - Before risky operations
|
|
1095
|
+
8. **semantic-memory_store()** - Store learnings
|
|
1096
|
+
9. **swarm_complete()** - NOT beads_close
|
|
644
1097
|
|
|
645
|
-
##
|
|
1098
|
+
## Non-Negotiables
|
|
646
1099
|
|
|
647
|
-
-
|
|
648
|
-
-
|
|
649
|
-
-
|
|
650
|
-
-
|
|
1100
|
+
- **Step 1 is MANDATORY** - swarm_complete fails without init
|
|
1101
|
+
- **Step 2 saves time** - past agents may have solved this
|
|
1102
|
+
- **Step 4 prevents conflicts** - workers reserve, not coordinator
|
|
1103
|
+
- **Step 6 prevents silent failure** - report progress
|
|
1104
|
+
- **Step 9 is the ONLY way to close** - releases reservations, records learning
|
|
651
1105
|
|
|
652
|
-
##
|
|
1106
|
+
## When Blocked
|
|
653
1107
|
|
|
654
1108
|
\`\`\`
|
|
655
1109
|
swarmmail_send(
|
|
656
1110
|
to=["coordinator"],
|
|
657
|
-
subject="
|
|
658
|
-
body="
|
|
659
|
-
|
|
1111
|
+
subject="BLOCKED: <bead-id>",
|
|
1112
|
+
body="<what you need>",
|
|
1113
|
+
importance="high"
|
|
660
1114
|
)
|
|
1115
|
+
beads_update(id="<bead-id>", status="blocked")
|
|
661
1116
|
\`\`\`
|
|
662
1117
|
|
|
663
|
-
##
|
|
1118
|
+
## Focus
|
|
664
1119
|
|
|
665
|
-
|
|
666
|
-
|
|
1120
|
+
- Only modify your assigned files
|
|
1121
|
+
- Don't fix other agents' code - coordinate instead
|
|
1122
|
+
- Report scope changes before expanding
|
|
1123
|
+
|
|
1124
|
+
Begin by reading your full prompt and executing Step 1.
|
|
667
1125
|
`;
|
|
668
1126
|
|
|
669
1127
|
// ============================================================================
|
|
@@ -834,6 +1292,8 @@ async function setup() {
|
|
|
834
1292
|
|
|
835
1293
|
p.intro("opencode-swarm-plugin v" + VERSION);
|
|
836
1294
|
|
|
1295
|
+
let isReinstall = false;
|
|
1296
|
+
|
|
837
1297
|
// Check if already configured FIRST
|
|
838
1298
|
const configDir = join(homedir(), ".config", "opencode");
|
|
839
1299
|
const pluginDir = join(configDir, "plugin");
|
|
@@ -842,14 +1302,20 @@ async function setup() {
|
|
|
842
1302
|
|
|
843
1303
|
const pluginPath = join(pluginDir, "swarm.ts");
|
|
844
1304
|
const commandPath = join(commandDir, "swarm.md");
|
|
845
|
-
const
|
|
846
|
-
const
|
|
1305
|
+
const swarmAgentDir = join(agentDir, "swarm");
|
|
1306
|
+
const plannerAgentPath = join(swarmAgentDir, "planner.md");
|
|
1307
|
+
const workerAgentPath = join(swarmAgentDir, "worker.md");
|
|
1308
|
+
// Legacy flat paths (for detection/cleanup)
|
|
1309
|
+
const legacyPlannerPath = join(agentDir, "swarm-planner.md");
|
|
1310
|
+
const legacyWorkerPath = join(agentDir, "swarm-worker.md");
|
|
847
1311
|
|
|
848
1312
|
const existingFiles = [
|
|
849
1313
|
pluginPath,
|
|
850
1314
|
commandPath,
|
|
851
1315
|
plannerAgentPath,
|
|
852
1316
|
workerAgentPath,
|
|
1317
|
+
legacyPlannerPath,
|
|
1318
|
+
legacyWorkerPath,
|
|
853
1319
|
].filter((f) => existsSync(f));
|
|
854
1320
|
|
|
855
1321
|
if (existingFiles.length > 0) {
|
|
@@ -872,7 +1338,7 @@ async function setup() {
|
|
|
872
1338
|
{
|
|
873
1339
|
value: "reinstall",
|
|
874
1340
|
label: "Reinstall everything",
|
|
875
|
-
hint: "Check deps
|
|
1341
|
+
hint: "Check deps, sync bundled skills, regenerate config files",
|
|
876
1342
|
},
|
|
877
1343
|
],
|
|
878
1344
|
});
|
|
@@ -906,28 +1372,40 @@ async function setup() {
|
|
|
906
1372
|
process.exit(0);
|
|
907
1373
|
}
|
|
908
1374
|
|
|
909
|
-
// Update model lines in agent files
|
|
910
|
-
|
|
911
|
-
|
|
1375
|
+
// Update model lines in agent files (check both nested and legacy paths)
|
|
1376
|
+
const plannerPaths = [plannerAgentPath, legacyPlannerPath].filter(existsSync);
|
|
1377
|
+
const workerPaths = [workerAgentPath, legacyWorkerPath].filter(existsSync);
|
|
1378
|
+
|
|
1379
|
+
for (const path of plannerPaths) {
|
|
1380
|
+
const content = readFileSync(path, "utf-8");
|
|
912
1381
|
const updated = content.replace(
|
|
913
1382
|
/^model: .+$/m,
|
|
914
1383
|
`model: ${coordinatorModel}`,
|
|
915
1384
|
);
|
|
916
|
-
writeFileSync(
|
|
1385
|
+
writeFileSync(path, updated);
|
|
1386
|
+
}
|
|
1387
|
+
if (plannerPaths.length > 0) {
|
|
917
1388
|
p.log.success("Planner: " + coordinatorModel);
|
|
918
1389
|
}
|
|
919
|
-
|
|
920
|
-
|
|
1390
|
+
|
|
1391
|
+
for (const path of workerPaths) {
|
|
1392
|
+
const content = readFileSync(path, "utf-8");
|
|
921
1393
|
const updated = content.replace(
|
|
922
1394
|
/^model: .+$/m,
|
|
923
1395
|
`model: ${workerModel}`,
|
|
924
1396
|
);
|
|
925
|
-
writeFileSync(
|
|
1397
|
+
writeFileSync(path, updated);
|
|
1398
|
+
}
|
|
1399
|
+
if (workerPaths.length > 0) {
|
|
926
1400
|
p.log.success("Worker: " + workerModel);
|
|
927
1401
|
}
|
|
1402
|
+
|
|
928
1403
|
p.outro("Models updated! Your customizations are preserved.");
|
|
929
1404
|
return;
|
|
930
1405
|
}
|
|
1406
|
+
if (action === "reinstall") {
|
|
1407
|
+
isReinstall = true;
|
|
1408
|
+
}
|
|
931
1409
|
// action === "reinstall" - fall through to full setup
|
|
932
1410
|
}
|
|
933
1411
|
|
|
@@ -1157,7 +1635,7 @@ async function setup() {
|
|
|
1157
1635
|
|
|
1158
1636
|
// Create directories if needed
|
|
1159
1637
|
const skillsDir = join(configDir, "skills");
|
|
1160
|
-
for (const dir of [pluginDir, commandDir, agentDir, skillsDir]) {
|
|
1638
|
+
for (const dir of [pluginDir, commandDir, agentDir, swarmAgentDir, skillsDir]) {
|
|
1161
1639
|
if (!existsSync(dir)) {
|
|
1162
1640
|
mkdirSync(dir, { recursive: true });
|
|
1163
1641
|
}
|
|
@@ -1169,25 +1647,94 @@ async function setup() {
|
|
|
1169
1647
|
writeFileSync(commandPath, SWARM_COMMAND);
|
|
1170
1648
|
p.log.success("Command: " + commandPath);
|
|
1171
1649
|
|
|
1650
|
+
// Write nested agent files (swarm/planner.md, swarm/worker.md)
|
|
1651
|
+
// This is the format used by Task(subagent_type="swarm/worker")
|
|
1172
1652
|
writeFileSync(plannerAgentPath, getPlannerAgent(coordinatorModel as string));
|
|
1173
1653
|
p.log.success("Planner agent: " + plannerAgentPath);
|
|
1174
1654
|
|
|
1175
1655
|
writeFileSync(workerAgentPath, getWorkerAgent(workerModel as string));
|
|
1176
1656
|
p.log.success("Worker agent: " + workerAgentPath);
|
|
1177
1657
|
|
|
1658
|
+
// Clean up legacy flat agent files if they exist
|
|
1659
|
+
if (existsSync(legacyPlannerPath)) {
|
|
1660
|
+
rmSync(legacyPlannerPath);
|
|
1661
|
+
p.log.message(dim(" Removed legacy: " + legacyPlannerPath));
|
|
1662
|
+
}
|
|
1663
|
+
if (existsSync(legacyWorkerPath)) {
|
|
1664
|
+
rmSync(legacyWorkerPath);
|
|
1665
|
+
p.log.message(dim(" Removed legacy: " + legacyWorkerPath));
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1178
1668
|
p.log.success("Skills directory: " + skillsDir);
|
|
1179
1669
|
|
|
1180
|
-
// Show bundled skills info
|
|
1670
|
+
// Show bundled skills info (and optionally sync to global skills dir)
|
|
1181
1671
|
const bundledSkillsPath = join(__dirname, "..", "global-skills");
|
|
1672
|
+
const bundledSkills = listDirectoryNames(bundledSkillsPath);
|
|
1182
1673
|
if (existsSync(bundledSkillsPath)) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1674
|
+
if (bundledSkills.length > 0) {
|
|
1675
|
+
p.log.message(dim(" Bundled skills: " + bundledSkills.join(", ")));
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// If the user keeps their skills in ~/.config/opencode/skills, offer to sync the bundled set
|
|
1680
|
+
if (bundledSkills.length > 0) {
|
|
1681
|
+
const globalSkills = listDirectoryNames(skillsDir);
|
|
1682
|
+
const managedBundled = globalSkills.filter((name) =>
|
|
1683
|
+
existsSync(join(skillsDir, name, BUNDLED_SKILL_MARKER_FILENAME)),
|
|
1684
|
+
);
|
|
1685
|
+
const missingBundled = bundledSkills.filter(
|
|
1686
|
+
(name) => !globalSkills.includes(name),
|
|
1687
|
+
);
|
|
1688
|
+
|
|
1689
|
+
if (missingBundled.length > 0 || managedBundled.length > 0) {
|
|
1690
|
+
const shouldSync = await p.confirm({
|
|
1691
|
+
message:
|
|
1692
|
+
"Sync bundled skills into your global skills directory? " +
|
|
1693
|
+
(missingBundled.length > 0
|
|
1694
|
+
? `(${missingBundled.length} missing)`
|
|
1695
|
+
: "(update managed skills)"),
|
|
1696
|
+
initialValue: isReinstall || missingBundled.length > 0,
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
if (p.isCancel(shouldSync)) {
|
|
1700
|
+
p.cancel("Setup cancelled");
|
|
1701
|
+
process.exit(0);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
if (shouldSync) {
|
|
1705
|
+
const syncSpinner = p.spinner();
|
|
1706
|
+
syncSpinner.start("Syncing bundled skills...");
|
|
1707
|
+
try {
|
|
1708
|
+
const { installed, updated, skipped } = syncBundledSkillsToGlobal({
|
|
1709
|
+
bundledSkillsPath,
|
|
1710
|
+
globalSkillsPath: skillsDir,
|
|
1711
|
+
version: VERSION,
|
|
1712
|
+
});
|
|
1713
|
+
syncSpinner.stop("Bundled skills synced");
|
|
1714
|
+
|
|
1715
|
+
if (installed.length > 0) {
|
|
1716
|
+
p.log.success("Installed: " + installed.join(", "));
|
|
1717
|
+
}
|
|
1718
|
+
if (updated.length > 0) {
|
|
1719
|
+
p.log.success("Updated: " + updated.join(", "));
|
|
1720
|
+
}
|
|
1721
|
+
if (skipped.length > 0) {
|
|
1722
|
+
p.log.message(
|
|
1723
|
+
dim(
|
|
1724
|
+
"Skipped (already exists, not managed): " + skipped.join(", "),
|
|
1725
|
+
),
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
syncSpinner.stop("Could not sync bundled skills");
|
|
1730
|
+
p.log.warn(
|
|
1731
|
+
"Bundled skills are still available from the package via skills_list.",
|
|
1732
|
+
);
|
|
1733
|
+
p.log.message(
|
|
1734
|
+
dim(error instanceof Error ? error.message : String(error)),
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1191
1738
|
}
|
|
1192
1739
|
}
|
|
1193
1740
|
|
|
@@ -1203,111 +1750,28 @@ async function setup() {
|
|
|
1203
1750
|
const s = p.spinner();
|
|
1204
1751
|
s.start("Updating AGENTS.md...");
|
|
1205
1752
|
|
|
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
1753
|
try {
|
|
1295
|
-
const
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1754
|
+
const bundledSkillsCsv =
|
|
1755
|
+
bundledSkills.length > 0
|
|
1756
|
+
? bundledSkills.join(", ")
|
|
1757
|
+
: "cli-builder, learning-systems, skill-creator, swarm-coordination, system-design, testing-patterns";
|
|
1758
|
+
|
|
1759
|
+
const result = updateAgentsMdFile({ agentsPath, bundledSkillsCsv });
|
|
1300
1760
|
|
|
1301
|
-
if (result.
|
|
1761
|
+
if (result.changed) {
|
|
1302
1762
|
s.stop("AGENTS.md updated");
|
|
1303
|
-
p.log.success("
|
|
1763
|
+
p.log.success("Updated: " + agentsPath);
|
|
1764
|
+
if (result.backupPath) {
|
|
1765
|
+
p.log.message(dim(" Backup: " + result.backupPath));
|
|
1766
|
+
}
|
|
1304
1767
|
} else {
|
|
1305
|
-
s.stop("
|
|
1306
|
-
p.log.warn("You can manually add skills section later");
|
|
1768
|
+
s.stop("AGENTS.md already up to date");
|
|
1307
1769
|
}
|
|
1308
|
-
} catch {
|
|
1770
|
+
} catch (error) {
|
|
1309
1771
|
s.stop("Could not update AGENTS.md");
|
|
1310
|
-
p.log.
|
|
1772
|
+
p.log.error(
|
|
1773
|
+
error instanceof Error ? error.message : "Unknown error updating file",
|
|
1774
|
+
);
|
|
1311
1775
|
}
|
|
1312
1776
|
}
|
|
1313
1777
|
}
|
|
@@ -1786,15 +2250,6 @@ async function agents() {
|
|
|
1786
2250
|
return;
|
|
1787
2251
|
}
|
|
1788
2252
|
|
|
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
2253
|
const confirm = await p.confirm({
|
|
1799
2254
|
message: "Update AGENTS.md with skill awareness?",
|
|
1800
2255
|
initialValue: true,
|
|
@@ -1808,82 +2263,28 @@ async function agents() {
|
|
|
1808
2263
|
const s = p.spinner();
|
|
1809
2264
|
s.start("Updating AGENTS.md with skill awareness...");
|
|
1810
2265
|
|
|
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.`;
|
|
2266
|
+
const bundledSkillsPath = join(__dirname, "..", "global-skills");
|
|
2267
|
+
const bundledSkills = listDirectoryNames(bundledSkillsPath);
|
|
1870
2268
|
|
|
1871
2269
|
try {
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2270
|
+
const bundledSkillsCsv =
|
|
2271
|
+
bundledSkills.length > 0
|
|
2272
|
+
? bundledSkills.join(", ")
|
|
2273
|
+
: "cli-builder, learning-systems, skill-creator, swarm-coordination, system-design, testing-patterns";
|
|
2274
|
+
|
|
2275
|
+
const result = updateAgentsMdFile({ agentsPath, bundledSkillsCsv });
|
|
1876
2276
|
|
|
1877
|
-
if (result.
|
|
2277
|
+
if (result.changed) {
|
|
1878
2278
|
s.stop("AGENTS.md updated with skill awareness");
|
|
1879
2279
|
p.log.success("Skills section added to " + agentsPath);
|
|
1880
2280
|
p.log.message(
|
|
1881
2281
|
dim("Skills available: skills_list, skills_use, skills_read"),
|
|
1882
2282
|
);
|
|
2283
|
+
if (result.backupPath) {
|
|
2284
|
+
p.log.message(dim("Backup: " + result.backupPath));
|
|
2285
|
+
}
|
|
1883
2286
|
} else {
|
|
1884
|
-
|
|
1885
|
-
s.stop("Failed to update AGENTS.md");
|
|
1886
|
-
p.log.error(stderr || "Unknown error");
|
|
2287
|
+
s.stop("AGENTS.md already up to date");
|
|
1887
2288
|
}
|
|
1888
2289
|
} catch (error) {
|
|
1889
2290
|
s.stop("Failed to update AGENTS.md");
|