opencode-swarm-plugin 0.12.23 → 0.12.25

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/bin/swarm.ts CHANGED
@@ -623,6 +623,66 @@ async function doctor() {
623
623
  const requiredMissing = required.filter((r) => !r.available);
624
624
  const optionalMissing = optional.filter((r) => !r.available);
625
625
 
626
+ // Check skills
627
+ p.log.step("Skills:");
628
+ const configDir = join(homedir(), ".config", "opencode");
629
+ const globalSkillsPath = join(configDir, "skills");
630
+ const bundledSkillsPath = join(__dirname, "..", "global-skills");
631
+
632
+ // Global skills directory
633
+ if (existsSync(globalSkillsPath)) {
634
+ try {
635
+ const { readdirSync } = require("fs");
636
+ const skills = readdirSync(globalSkillsPath, { withFileTypes: true })
637
+ .filter((d: { isDirectory: () => boolean }) => d.isDirectory())
638
+ .map((d: { name: string }) => d.name);
639
+ if (skills.length > 0) {
640
+ p.log.success(`Global skills (${skills.length}): ${skills.join(", ")}`);
641
+ } else {
642
+ p.log.warn("Global skills directory exists but is empty");
643
+ }
644
+ } catch {
645
+ p.log.warn("Global skills directory: " + globalSkillsPath);
646
+ }
647
+ } else {
648
+ p.log.warn("No global skills directory (run 'swarm setup' to create)");
649
+ }
650
+
651
+ // Bundled skills
652
+ if (existsSync(bundledSkillsPath)) {
653
+ try {
654
+ const { readdirSync } = require("fs");
655
+ const bundled = readdirSync(bundledSkillsPath, { withFileTypes: true })
656
+ .filter((d: { isDirectory: () => boolean }) => d.isDirectory())
657
+ .map((d: { name: string }) => d.name);
658
+ p.log.success(
659
+ `Bundled skills (${bundled.length}): ${bundled.join(", ")}`,
660
+ );
661
+ } catch {
662
+ p.log.warn("Could not read bundled skills");
663
+ }
664
+ }
665
+
666
+ // Project skills (check current directory)
667
+ const projectSkillsDirs = [".opencode/skills", ".claude/skills", "skills"];
668
+ for (const dir of projectSkillsDirs) {
669
+ if (existsSync(dir)) {
670
+ try {
671
+ const { readdirSync } = require("fs");
672
+ const skills = readdirSync(dir, { withFileTypes: true })
673
+ .filter((d: { isDirectory: () => boolean }) => d.isDirectory())
674
+ .map((d: { name: string }) => d.name);
675
+ if (skills.length > 0) {
676
+ p.log.success(
677
+ `Project skills in ${dir}/ (${skills.length}): ${skills.join(", ")}`,
678
+ );
679
+ }
680
+ } catch {
681
+ // Ignore
682
+ }
683
+ }
684
+ }
685
+
626
686
  if (requiredMissing.length > 0) {
627
687
  p.outro(
628
688
  "Missing " +
@@ -977,7 +1037,8 @@ async function setup() {
977
1037
  p.log.step("Setting up OpenCode integration...");
978
1038
 
979
1039
  // Create directories if needed
980
- for (const dir of [pluginDir, commandDir, agentDir]) {
1040
+ const skillsDir = join(configDir, "skills");
1041
+ for (const dir of [pluginDir, commandDir, agentDir, skillsDir]) {
981
1042
  if (!existsSync(dir)) {
982
1043
  mkdirSync(dir, { recursive: true });
983
1044
  }
@@ -995,8 +1056,24 @@ async function setup() {
995
1056
  writeFileSync(workerAgentPath, getWorkerAgent(workerModel as string));
996
1057
  p.log.success("Worker agent: " + workerAgentPath);
997
1058
 
1059
+ p.log.success("Skills directory: " + skillsDir);
1060
+
1061
+ // Show bundled skills info
1062
+ const bundledSkillsPath = join(__dirname, "..", "global-skills");
1063
+ if (existsSync(bundledSkillsPath)) {
1064
+ try {
1065
+ const { readdirSync } = require("fs");
1066
+ const bundled = readdirSync(bundledSkillsPath, { withFileTypes: true })
1067
+ .filter((d: { isDirectory: () => boolean }) => d.isDirectory())
1068
+ .map((d: { name: string }) => d.name);
1069
+ p.log.message(dim(" Bundled skills: " + bundled.join(", ")));
1070
+ } catch {
1071
+ // Ignore
1072
+ }
1073
+ }
1074
+
998
1075
  p.note(
999
- 'cd your-project\nbd init\nopencode\n/swarm "your task"',
1076
+ 'cd your-project\nbd init\nopencode\n/swarm "your task"\n\nSkills: Use skills_list to see available skills',
1000
1077
  "Next steps",
1001
1078
  );
1002
1079
 
@@ -1078,6 +1155,25 @@ async function init() {
1078
1155
  }
1079
1156
  }
1080
1157
 
1158
+ // Offer to create project skills directory
1159
+ const createSkillsDir = await p.confirm({
1160
+ message: "Create project skills directory (.opencode/skills/)?",
1161
+ initialValue: false,
1162
+ });
1163
+
1164
+ if (!p.isCancel(createSkillsDir) && createSkillsDir) {
1165
+ const skillsPath = ".opencode/skills";
1166
+ if (!existsSync(skillsPath)) {
1167
+ mkdirSync(skillsPath, { recursive: true });
1168
+ p.log.success("Created " + skillsPath + "/");
1169
+ p.log.message(
1170
+ dim(" Add SKILL.md files here for project-specific skills"),
1171
+ );
1172
+ } else {
1173
+ p.log.warn(skillsPath + "/ already exists");
1174
+ }
1175
+ }
1176
+
1081
1177
  p.outro("Project initialized! Use '/swarm' in OpenCode to get started.");
1082
1178
  } else {
1083
1179
  s.stop("Failed to initialize beads");
@@ -666,6 +666,115 @@ const swarm_evaluation_prompt = tool({
666
666
  execute: (args, ctx) => execTool("swarm_evaluation_prompt", args, ctx),
667
667
  });
668
668
 
669
+ // =============================================================================
670
+ // Skills Tools
671
+ // =============================================================================
672
+
673
+ const skills_list = tool({
674
+ description:
675
+ "List all available skills from global, project, and bundled sources",
676
+ args: {
677
+ source: tool.schema
678
+ .enum(["all", "global", "project", "bundled"])
679
+ .optional()
680
+ .describe("Filter by source (default: all)"),
681
+ },
682
+ execute: (args, ctx) => execTool("skills_list", args, ctx),
683
+ });
684
+
685
+ const skills_read = tool({
686
+ description: "Read a skill's full content including SKILL.md and references",
687
+ args: {
688
+ name: tool.schema.string().describe("Skill name"),
689
+ },
690
+ execute: (args, ctx) => execTool("skills_read", args, ctx),
691
+ });
692
+
693
+ const skills_use = tool({
694
+ description:
695
+ "Get skill content formatted for injection into agent context. Use this when you need to apply a skill's knowledge to the current task.",
696
+ args: {
697
+ name: tool.schema.string().describe("Skill name"),
698
+ context: tool.schema
699
+ .string()
700
+ .optional()
701
+ .describe("Optional context about how the skill will be used"),
702
+ },
703
+ execute: (args, ctx) => execTool("skills_use", args, ctx),
704
+ });
705
+
706
+ const skills_create = tool({
707
+ description: "Create a new skill with SKILL.md template",
708
+ args: {
709
+ name: tool.schema.string().describe("Skill name (kebab-case)"),
710
+ description: tool.schema.string().describe("Brief skill description"),
711
+ scope: tool.schema
712
+ .enum(["global", "project"])
713
+ .optional()
714
+ .describe("Where to create (default: project)"),
715
+ tags: tool.schema
716
+ .array(tool.schema.string())
717
+ .optional()
718
+ .describe("Skill tags for discovery"),
719
+ },
720
+ execute: (args, ctx) => execTool("skills_create", args, ctx),
721
+ });
722
+
723
+ const skills_update = tool({
724
+ description: "Update an existing skill's SKILL.md content",
725
+ args: {
726
+ name: tool.schema.string().describe("Skill name"),
727
+ content: tool.schema.string().describe("New SKILL.md content"),
728
+ },
729
+ execute: (args, ctx) => execTool("skills_update", args, ctx),
730
+ });
731
+
732
+ const skills_delete = tool({
733
+ description: "Delete a skill (project skills only)",
734
+ args: {
735
+ name: tool.schema.string().describe("Skill name"),
736
+ },
737
+ execute: (args, ctx) => execTool("skills_delete", args, ctx),
738
+ });
739
+
740
+ const skills_init = tool({
741
+ description: "Initialize skills directory in current project",
742
+ args: {
743
+ path: tool.schema
744
+ .string()
745
+ .optional()
746
+ .describe("Custom path (default: .opencode/skills)"),
747
+ },
748
+ execute: (args, ctx) => execTool("skills_init", args, ctx),
749
+ });
750
+
751
+ const skills_add_script = tool({
752
+ description: "Add an executable script to a skill",
753
+ args: {
754
+ skill_name: tool.schema.string().describe("Skill name"),
755
+ script_name: tool.schema.string().describe("Script filename"),
756
+ content: tool.schema.string().describe("Script content"),
757
+ executable: tool.schema
758
+ .boolean()
759
+ .optional()
760
+ .describe("Make executable (default: true)"),
761
+ },
762
+ execute: (args, ctx) => execTool("skills_add_script", args, ctx),
763
+ });
764
+
765
+ const skills_execute = tool({
766
+ description: "Execute a skill's script",
767
+ args: {
768
+ skill_name: tool.schema.string().describe("Skill name"),
769
+ script_name: tool.schema.string().describe("Script to execute"),
770
+ args: tool.schema
771
+ .array(tool.schema.string())
772
+ .optional()
773
+ .describe("Script arguments"),
774
+ },
775
+ execute: (args, ctx) => execTool("skills_execute", args, ctx),
776
+ });
777
+
669
778
  // =============================================================================
670
779
  // Plugin Export
671
780
  // =============================================================================
@@ -716,6 +825,16 @@ export const SwarmPlugin: Plugin = async (
716
825
  swarm_spawn_subtask,
717
826
  swarm_complete_subtask,
718
827
  swarm_evaluation_prompt,
828
+ // Skills
829
+ skills_list,
830
+ skills_read,
831
+ skills_use,
832
+ skills_create,
833
+ skills_update,
834
+ skills_delete,
835
+ skills_init,
836
+ skills_add_script,
837
+ skills_execute,
719
838
  },
720
839
  };
721
840
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm-plugin",
3
- "version": "0.12.23",
3
+ "version": "0.12.25",
4
4
  "description": "Multi-agent swarm coordination for OpenCode with learning capabilities, beads integration, and Agent Mail",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",