@neyugn/agent-kits 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/cli.js +164 -10
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -40,7 +40,7 @@
40
40
  - 📜 **Workflows** — Slash commands for common tasks
41
41
  - 🔍 **Smart Filtering** — Auto-detect techstack and optimize loaded skills
42
42
 
43
- Works with **any AI tool** — Claude, Gemini, Codex, Cursor, and more.
43
+ Works with **any AI tool** — Claude, Gemini, Codex, Cursor, OpenCode, and more.
44
44
 
45
45
  <br/>
46
46
 
@@ -106,6 +106,7 @@ npx @neyugn/agent-kits
106
106
  | Gemini CLI | `~/.gemini/` | `.gemini/` |
107
107
  | Codex CLI | `~/.codex/` | `.codex/` |
108
108
  | Antigravity | `~/.agent/` | `.agent/` |
109
+ | OpenCode | `~/.config/opencode/` | `.opencode/` |
109
110
  | Cursor | `~/.cursor/` | `.cursor/` |
110
111
 
111
112
  > **Note:** On Windows, `~` is replaced with `C:\Users\<username>\`
@@ -124,6 +125,7 @@ If the installer detects an existing installation, you'll be prompted:
124
125
  | Tool | Workspace Path | Global Path | Status |
125
126
  | ----------- | ----------------- | ------------ | ------------------ |
126
127
  | Antigravity | `.agent/skills/` | `~/.agent/` | ✅ Fully Supported |
128
+ | OpenCode | `.opencode/` | `~/.config/opencode/` | ✅ Fully Supported |
127
129
  | Cursor | `.cursor/skills/` | `~/.cursor/` | ✅ Fully Supported |
128
130
  | Claude Code | `.claude/skills/` | `~/.claude/` | 🔜 Coming Soon |
129
131
  | Gemini CLI | `.gemini/skills/` | `~/.gemini/` | 🔜 Coming Soon |
package/dist/cli.js CHANGED
@@ -4,10 +4,10 @@
4
4
  import * as p from "@clack/prompts";
5
5
  import boxen from "boxen";
6
6
  import figlet from "figlet";
7
- import fs4 from "fs";
7
+ import fs5 from "fs";
8
8
  import gradient from "gradient-string";
9
9
  import os2 from "os";
10
- import path5 from "path";
10
+ import path6 from "path";
11
11
  import pc from "picocolors";
12
12
 
13
13
  // src/config.ts
@@ -92,6 +92,20 @@ var AI_TOOLS = [
92
92
  // Cursor calls workflows "commands" in .cursor/commands/
93
93
  available: true
94
94
  },
95
+ {
96
+ id: "opencode",
97
+ name: "OpenCode",
98
+ icon: "\u232C",
99
+ path: ".opencode",
100
+ globalPathPattern: "~/.config/opencode",
101
+ rulesFile: "AGENTS.md",
102
+ kitRulesFile: "AGENTS.md",
103
+ rulesInsideKit: false,
104
+ // OpenCode reads AGENTS.md from project root
105
+ workflowsAs: "commands",
106
+ // OpenCode calls workflows "commands" in .opencode/commands/
107
+ available: true
108
+ },
95
109
  {
96
110
  id: "custom",
97
111
  name: "Custom",
@@ -169,7 +183,7 @@ function getKitSource(kitId) {
169
183
  }
170
184
  function replaceToolPaths(content, targetPath) {
171
185
  return content.replace(
172
- /\.(agent|claude|gemini|cursor|codex)\//g,
186
+ /\.(agent|claude|gemini|cursor|codex|opencode)\//g,
173
187
  `${targetPath}/`
174
188
  );
175
189
  }
@@ -465,6 +479,49 @@ function createCursorWorkflowTransformer() {
465
479
  return new CursorWorkflowTransformer();
466
480
  }
467
481
 
482
+ // src/transformers/opencode-workflow.ts
483
+ var OpenCodeWorkflowTransformer = class {
484
+ /**
485
+ * Transform workflow content from Agent-Kits format to OpenCode command format
486
+ */
487
+ transform(content, context) {
488
+ const parsed = parseFrontmatter(content);
489
+ const originalData = parsed.data;
490
+ const commandFrontmatter = {
491
+ description: String(originalData.description || "")
492
+ };
493
+ const newFrontmatter = serializeFrontmatter(
494
+ commandFrontmatter
495
+ );
496
+ let bodyContent = parsed.content;
497
+ bodyContent = this.transformPaths(bodyContent, context);
498
+ bodyContent = this.transformTerminology(bodyContent);
499
+ return combineMarkdown(newFrontmatter, bodyContent);
500
+ }
501
+ /**
502
+ * Replace .agent/ paths with .opencode/
503
+ */
504
+ transformPaths(content, context) {
505
+ const toolPath = context.aiTool?.path || ".opencode";
506
+ return content.replace(/\.agent\//g, `${toolPath}/`);
507
+ }
508
+ /**
509
+ * Transform terminology: workflow → command
510
+ */
511
+ transformTerminology(content) {
512
+ let transformed = content;
513
+ transformed = transformed.replace(/workflows?\//gi, "commands/");
514
+ transformed = transformed.replace(/\/workflow/gi, "/command");
515
+ transformed = transformed.replace(/workflow/gi, "command");
516
+ transformed = transformed.replace(/Workflow/g, "Command");
517
+ transformed = transformed.replace(/WORKFLOW/g, "COMMAND");
518
+ return transformed;
519
+ }
520
+ };
521
+ function createOpenCodeWorkflowTransformer() {
522
+ return new OpenCodeWorkflowTransformer();
523
+ }
524
+
468
525
  // src/installers/cursor.ts
469
526
  var CursorInstaller = class {
470
527
  // Cursor uses "commands" instead of "workflows"
@@ -650,6 +707,101 @@ var CursorInstaller = class {
650
707
  }
651
708
  };
652
709
 
710
+ // src/installers/opencode.ts
711
+ import fs4 from "fs/promises";
712
+ import path5 from "path";
713
+ var OpenCodeInstaller = class {
714
+ // OpenCode uses "commands" instead of "workflows"
715
+ COMMANDS_FOLDER = "commands";
716
+ // Transformer for OpenCode command format
717
+ workflowTransformer = createOpenCodeWorkflowTransformer();
718
+ async install(options) {
719
+ const { aiTool, kits, targetPath } = options;
720
+ const results = [];
721
+ for (const kitId of kits) {
722
+ const { kitSourcePath, kit } = getKitSource(kitId);
723
+ const kitTargetPath = path5.join(targetPath, aiTool.path);
724
+ await fs4.mkdir(kitTargetPath, { recursive: true });
725
+ await copyDirectory(
726
+ kitSourcePath,
727
+ kitTargetPath,
728
+ ["rules", "workflows"],
729
+ aiTool.path
730
+ );
731
+ await this.copyWorkflowsWithTransform(
732
+ kitSourcePath,
733
+ kitTargetPath,
734
+ aiTool.path
735
+ );
736
+ await copyRulesFile(
737
+ kitSourcePath,
738
+ kitTargetPath,
739
+ targetPath,
740
+ aiTool,
741
+ options.scope,
742
+ this.COMMANDS_FOLDER
743
+ // Replace workflows/ with commands/
744
+ );
745
+ try {
746
+ await copyCommonAssets(kitTargetPath, aiTool, this.COMMANDS_FOLDER);
747
+ } catch {
748
+ }
749
+ const agents = await countItems(path5.join(kitTargetPath, "agents"));
750
+ const skills = await countItems(path5.join(kitTargetPath, "skills"));
751
+ const commands = await countItems(
752
+ path5.join(kitTargetPath, this.COMMANDS_FOLDER)
753
+ );
754
+ results.push({
755
+ kit: kit.name,
756
+ agents,
757
+ skills,
758
+ workflows: commands
759
+ // Still called "workflows" in result for consistency
760
+ });
761
+ }
762
+ return results;
763
+ }
764
+ /**
765
+ * Copy workflows with transformation to OpenCode command format
766
+ *
767
+ * This method:
768
+ * 1. Reads each workflow file from the kit
769
+ * 2. Transforms the frontmatter to OpenCode command format
770
+ * 3. Replaces path references (.agent/ → .opencode/)
771
+ * 4. Replaces terminology (workflow → command)
772
+ * 5. Writes the transformed command to the commands directory
773
+ */
774
+ async copyWorkflowsWithTransform(kitSourcePath, kitTargetPath, toolPath) {
775
+ const workflowsSource = path5.join(kitSourcePath, "workflows");
776
+ const commandsTarget = path5.join(kitTargetPath, this.COMMANDS_FOLDER);
777
+ try {
778
+ await fs4.access(workflowsSource);
779
+ } catch {
780
+ return;
781
+ }
782
+ await fs4.mkdir(commandsTarget, { recursive: true });
783
+ const entries = await fs4.readdir(workflowsSource, { withFileTypes: true });
784
+ for (const entry of entries) {
785
+ if (!entry.isFile() || !entry.name.endsWith(".md")) {
786
+ continue;
787
+ }
788
+ const sourcePath = path5.join(workflowsSource, entry.name);
789
+ const targetPath = path5.join(commandsTarget, entry.name);
790
+ const content = await fs4.readFile(sourcePath, "utf-8");
791
+ const context = {
792
+ aiTool: { path: toolPath },
793
+ sourcePath,
794
+ targetPath
795
+ };
796
+ const transformedContent = this.workflowTransformer.transform(
797
+ content,
798
+ context
799
+ );
800
+ await fs4.writeFile(targetPath, transformedContent);
801
+ }
802
+ }
803
+ };
804
+
653
805
  // src/installers/index.ts
654
806
  var installerRegistry = {
655
807
  // Antigravity is the base/default installer
@@ -660,7 +812,9 @@ var installerRegistry = {
660
812
  codex: new AntigravityInstaller(),
661
813
  custom: new AntigravityInstaller(),
662
814
  // Cursor has special handling (workflows -> commands)
663
- cursor: new CursorInstaller()
815
+ cursor: new CursorInstaller(),
816
+ // OpenCode has special handling (workflows -> commands, AGENTS.md at root)
817
+ opencode: new OpenCodeInstaller()
664
818
  };
665
819
  function getInstaller(toolId) {
666
820
  const installer = installerRegistry[toolId];
@@ -677,13 +831,13 @@ async function installKit(options) {
677
831
  // src/cli.ts
678
832
  function expandPath(inputPath) {
679
833
  if (inputPath.startsWith("~")) {
680
- return path5.join(os2.homedir(), inputPath.slice(1));
834
+ return path6.join(os2.homedir(), inputPath.slice(1));
681
835
  }
682
836
  return inputPath;
683
837
  }
684
838
  function directoryExists(dirPath) {
685
839
  try {
686
- return fs4.existsSync(dirPath) && fs4.statSync(dirPath).isDirectory();
840
+ return fs5.existsSync(dirPath) && fs5.statSync(dirPath).isDirectory();
687
841
  } catch {
688
842
  return false;
689
843
  }
@@ -692,7 +846,7 @@ function getInstallPath(aiTool, scope, workspacePath) {
692
846
  if (scope === "global") {
693
847
  return getGlobalPath(aiTool);
694
848
  }
695
- return path5.join(workspacePath, aiTool.path);
849
+ return path6.join(workspacePath, aiTool.path);
696
850
  }
697
851
  function getDisplayPath(absolutePath) {
698
852
  const home = os2.homedir();
@@ -747,7 +901,7 @@ async function main() {
747
901
  {
748
902
  value: "workspace",
749
903
  label: "Workspace (Project)",
750
- hint: `Best for sharing with team (${path5.join(process.cwd(), aiTool.path)})`
904
+ hint: `Best for sharing with team (${path6.join(process.cwd(), aiTool.path)})`
751
905
  },
752
906
  {
753
907
  value: "global",
@@ -782,7 +936,7 @@ async function main() {
782
936
  workspacePath = expandPath(pathResult);
783
937
  }
784
938
  const finalInstallPath = getInstallPath(aiTool, scope, workspacePath);
785
- const rulesFilePath = scope === "global" || aiTool.rulesInsideKit ? path5.join(finalInstallPath, aiTool.rulesFile) : path5.join(workspacePath, aiTool.rulesFile);
939
+ const rulesFilePath = scope === "global" || aiTool.rulesInsideKit ? path6.join(finalInstallPath, aiTool.rulesFile) : path6.join(workspacePath, aiTool.rulesFile);
786
940
  if (directoryExists(finalInstallPath)) {
787
941
  p.log.warn(
788
942
  `${pc.yellow("\u26A0")} Existing toolkit found at: ${pc.cyan(getDisplayPath(finalInstallPath))}`
@@ -823,7 +977,7 @@ async function main() {
823
977
  if (replaceResult === "replace") {
824
978
  const s_rm = p.spinner();
825
979
  s_rm.start("Cleaning up old files...");
826
- fs4.rmSync(finalInstallPath, { recursive: true, force: true });
980
+ fs5.rmSync(finalInstallPath, { recursive: true, force: true });
827
981
  s_rm.stop("Cleanup complete.");
828
982
  }
829
983
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neyugn/agent-kits",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Universal AI Agent Toolkit - Skills, Agents, and Workflows for any AI coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,6 +20,7 @@
20
20
  "codex",
21
21
  "cursor",
22
22
  "copilot",
23
+ "opencode",
23
24
  "workflow",
24
25
  "prompt",
25
26
  "toolkit"