@neyugn/agent-kits 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -40,14 +40,14 @@
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
 
47
47
  ## 🚀 Quick Start
48
48
 
49
49
  ```bash
50
- npx @neyugn/agent-kits
50
+ npx @neyugn/agent-kits@latest
51
51
  ```
52
52
 
53
53
  That's it! The interactive installer will guide you through:
@@ -64,7 +64,7 @@ That's it! The interactive installer will guide you through:
64
64
  ### 🎯 One Command, Any Tool
65
65
 
66
66
  ```bash
67
- npx @neyugn/agent-kits
67
+ npx @neyugn/agent-kits@latest
68
68
  ```
69
69
 
70
70
  ```
@@ -100,13 +100,14 @@ npx @neyugn/agent-kits
100
100
 
101
101
  **Global Paths by Tool:**
102
102
 
103
- | Tool | Global Path | Workspace Path |
104
- | ----------- | ------------ | -------------- |
105
- | Claude Code | `~/.claude/` | `.claude/` |
106
- | Gemini CLI | `~/.gemini/` | `.gemini/` |
107
- | Codex CLI | `~/.codex/` | `.codex/` |
108
- | Antigravity | `~/.agent/` | `.agent/` |
109
- | Cursor | `~/.cursor/` | `.cursor/` |
103
+ | Tool | Global Path | Workspace Path |
104
+ | ----------- | --------------------- | -------------- |
105
+ | Claude Code | `~/.claude/` | `.claude/` |
106
+ | Gemini CLI | `~/.gemini/` | `.gemini/` |
107
+ | Codex CLI | `~/.codex/` | `.codex/` |
108
+ | Antigravity | `~/.agent/` | `.agent/` |
109
+ | OpenCode | `~/.config/opencode/` | `.opencode/` |
110
+ | Cursor | `~/.cursor/` | `.cursor/` |
110
111
 
111
112
  > **Note:** On Windows, `~` is replaced with `C:\Users\<username>\`
112
113
 
@@ -121,14 +122,15 @@ If the installer detects an existing installation, you'll be prompted:
121
122
 
122
123
  ### 🔌 Universal Compatibility
123
124
 
124
- | Tool | Workspace Path | Global Path | Status |
125
- | ----------- | ----------------- | ------------ | ------------------ |
126
- | Antigravity | `.agent/skills/` | `~/.agent/` | ✅ Fully Supported |
127
- | Cursor | `.cursor/skills/` | `~/.cursor/` | ✅ Fully Supported |
128
- | Claude Code | `.claude/skills/` | `~/.claude/` | 🔜 Coming Soon |
129
- | Gemini CLI | `.gemini/skills/` | `~/.gemini/` | 🔜 Coming Soon |
130
- | Codex CLI | `.codex/skills/` | `~/.codex/` | 🔜 Coming Soon |
131
- | Custom | Configurable | `~/.ai/` | 🔜 Coming Soon |
125
+ | Tool | Workspace Path | Global Path | Status |
126
+ | ----------- | ----------------- | --------------------- | ------------------ |
127
+ | Antigravity | `.agent/skills/` | `~/.agent/` | ✅ Fully Supported |
128
+ | OpenCode | `.opencode/` | `~/.config/opencode/` | ✅ Fully Supported |
129
+ | Cursor | `.cursor/skills/` | `~/.cursor/` | Fully Supported |
130
+ | Claude Code | `.claude/skills/` | `~/.claude/` | 🔜 Coming Soon |
131
+ | Gemini CLI | `.gemini/skills/` | `~/.gemini/` | 🔜 Coming Soon |
132
+ | Codex CLI | `.codex/skills/` | `~/.codex/` | 🔜 Coming Soon |
133
+ | Custom | Configurable | `~/.ai/` | 🔜 Coming Soon |
132
134
 
133
135
  > **Note:** Tools marked as 🔜 Coming Soon are planned for future releases. The infrastructure is ready, but these tools require additional testing and configuration.
134
136
 
package/README.vi.md CHANGED
@@ -39,7 +39,7 @@ Hoạt động với **mọi công cụ AI** — Claude, Gemini, Codex, Cursor,
39
39
  ## 🚀 Bắt đầu nhanh
40
40
 
41
41
  ```bash
42
- npx @neyugn/agent-kits
42
+ npx @neyugn/agent-kits@latest
43
43
  ```
44
44
 
45
45
  Đó là tất cả! Installer tương tác sẽ hướng dẫn bạn:
@@ -56,7 +56,7 @@ npx @neyugn/agent-kits
56
56
  ### 🎯 Một lệnh, mọi công cụ
57
57
 
58
58
  ```bash
59
- npx @neyugn/agent-kits
59
+ npx @neyugn/agent-kits@latest
60
60
  ```
61
61
 
62
62
  ```
package/README.zh.md CHANGED
@@ -39,7 +39,7 @@
39
39
  ## 🚀 快速开始
40
40
 
41
41
  ```bash
42
- npx @neyugn/agent-kits
42
+ npx @neyugn/agent-kits@latest
43
43
  ```
44
44
 
45
45
  就这样!交互式安装程序将引导您:
@@ -56,7 +56,7 @@ npx @neyugn/agent-kits
56
56
  ### 🎯 一条命令,任何工具
57
57
 
58
58
  ```bash
59
- npx @neyugn/agent-kits
59
+ npx @neyugn/agent-kits@latest
60
60
  ```
61
61
 
62
62
  ```
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,139 @@ function createCursorWorkflowTransformer() {
465
479
  return new CursorWorkflowTransformer();
466
480
  }
467
481
 
482
+ // src/transformers/opencode-agent.ts
483
+ var TOOL_NAME_MAP = {
484
+ read: "read",
485
+ write: "write",
486
+ edit: "edit",
487
+ bash: "bash",
488
+ grep: "grep",
489
+ glob: "glob",
490
+ agent: "agent"
491
+ };
492
+ var OpenCodeAgentTransformer = class {
493
+ /**
494
+ * Transform agent content from Agent-Kits format to OpenCode agent format
495
+ */
496
+ transform(content, context) {
497
+ const parsed = parseFrontmatter(content);
498
+ const originalData = parsed.data;
499
+ const toolsRecord = this.parseToolsToRecord(originalData.tools);
500
+ const opencodeFrontmatter = {
501
+ description: originalData.description,
502
+ ...originalData.model && { model: originalData.model },
503
+ ...Object.keys(toolsRecord).length > 0 && { tools: toolsRecord }
504
+ };
505
+ const newFrontmatter = this.serializeOpenCodeFrontmatter(opencodeFrontmatter);
506
+ let bodyContent = parsed.content;
507
+ bodyContent = this.transformPaths(bodyContent, context);
508
+ return combineMarkdown(newFrontmatter, bodyContent);
509
+ }
510
+ /**
511
+ * Parse comma-separated tools string into a record of { toolName: boolean }
512
+ *
513
+ * @example
514
+ * "Read, Grep, Glob, Bash, Edit, Write" →
515
+ * { read: true, grep: true, glob: true, bash: true, edit: true, write: true }
516
+ */
517
+ parseToolsToRecord(toolsString) {
518
+ if (!toolsString) return {};
519
+ const tools = {};
520
+ const toolNames = toolsString.split(",").map((t) => t.trim().toLowerCase());
521
+ for (const toolName of toolNames) {
522
+ const mappedName = TOOL_NAME_MAP[toolName] || toolName;
523
+ tools[mappedName] = true;
524
+ }
525
+ return tools;
526
+ }
527
+ /**
528
+ * Custom serializer for OpenCode frontmatter
529
+ *
530
+ * OpenCode expects tools as a nested YAML map:
531
+ * ```yaml
532
+ * tools:
533
+ * read: true
534
+ * write: true
535
+ * ```
536
+ *
537
+ * The standard serializeFrontmatter outputs flat key-value pairs,
538
+ * so we need custom handling for the `tools` field.
539
+ */
540
+ serializeOpenCodeFrontmatter(data) {
541
+ const lines = ["---"];
542
+ if (data.description) {
543
+ lines.push(`description: ${data.description}`);
544
+ }
545
+ if (data.model) {
546
+ lines.push(`model: ${data.model}`);
547
+ }
548
+ if (data.mode) {
549
+ lines.push(`mode: ${data.mode}`);
550
+ }
551
+ if (data.tools && Object.keys(data.tools).length > 0) {
552
+ lines.push("tools:");
553
+ for (const [tool, enabled] of Object.entries(data.tools)) {
554
+ lines.push(` ${tool}: ${enabled}`);
555
+ }
556
+ }
557
+ lines.push("---");
558
+ return lines.join("\n");
559
+ }
560
+ /**
561
+ * Replace .agent/ paths with .opencode/
562
+ */
563
+ transformPaths(content, context) {
564
+ const toolPath = context.aiTool?.path || ".opencode";
565
+ return content.replace(/\.agent\//g, `${toolPath}/`);
566
+ }
567
+ };
568
+ function createOpenCodeAgentTransformer() {
569
+ return new OpenCodeAgentTransformer();
570
+ }
571
+
572
+ // src/transformers/opencode-workflow.ts
573
+ var OpenCodeWorkflowTransformer = class {
574
+ /**
575
+ * Transform workflow content from Agent-Kits format to OpenCode command format
576
+ */
577
+ transform(content, context) {
578
+ const parsed = parseFrontmatter(content);
579
+ const originalData = parsed.data;
580
+ const commandFrontmatter = {
581
+ description: String(originalData.description || "")
582
+ };
583
+ const newFrontmatter = serializeFrontmatter(
584
+ commandFrontmatter
585
+ );
586
+ let bodyContent = parsed.content;
587
+ bodyContent = this.transformPaths(bodyContent, context);
588
+ bodyContent = this.transformTerminology(bodyContent);
589
+ return combineMarkdown(newFrontmatter, bodyContent);
590
+ }
591
+ /**
592
+ * Replace .agent/ paths with .opencode/
593
+ */
594
+ transformPaths(content, context) {
595
+ const toolPath = context.aiTool?.path || ".opencode";
596
+ return content.replace(/\.agent\//g, `${toolPath}/`);
597
+ }
598
+ /**
599
+ * Transform terminology: workflow → command
600
+ */
601
+ transformTerminology(content) {
602
+ let transformed = content;
603
+ transformed = transformed.replace(/workflows?\//gi, "commands/");
604
+ transformed = transformed.replace(/\/workflow/gi, "/command");
605
+ transformed = transformed.replace(/workflow/gi, "command");
606
+ transformed = transformed.replace(/Workflow/g, "Command");
607
+ transformed = transformed.replace(/WORKFLOW/g, "COMMAND");
608
+ return transformed;
609
+ }
610
+ };
611
+ function createOpenCodeWorkflowTransformer() {
612
+ return new OpenCodeWorkflowTransformer();
613
+ }
614
+
468
615
  // src/installers/cursor.ts
469
616
  var CursorInstaller = class {
470
617
  // Cursor uses "commands" instead of "workflows"
@@ -650,6 +797,147 @@ var CursorInstaller = class {
650
797
  }
651
798
  };
652
799
 
800
+ // src/installers/opencode.ts
801
+ import fs4 from "fs/promises";
802
+ import path5 from "path";
803
+ var OpenCodeInstaller = class {
804
+ // OpenCode uses "commands" instead of "workflows"
805
+ COMMANDS_FOLDER = "commands";
806
+ // Transformers for OpenCode-specific formats
807
+ agentTransformer = createOpenCodeAgentTransformer();
808
+ workflowTransformer = createOpenCodeWorkflowTransformer();
809
+ async install(options) {
810
+ const { aiTool, kits, targetPath } = options;
811
+ const results = [];
812
+ for (const kitId of kits) {
813
+ const { kitSourcePath, kit } = getKitSource(kitId);
814
+ const kitTargetPath = path5.join(targetPath, aiTool.path);
815
+ await fs4.mkdir(kitTargetPath, { recursive: true });
816
+ await copyDirectory(
817
+ kitSourcePath,
818
+ kitTargetPath,
819
+ ["rules", "workflows", "agents"],
820
+ aiTool.path
821
+ );
822
+ await this.copyAgentsWithTransform(
823
+ kitSourcePath,
824
+ kitTargetPath,
825
+ aiTool.path
826
+ );
827
+ await this.copyWorkflowsWithTransform(
828
+ kitSourcePath,
829
+ kitTargetPath,
830
+ aiTool.path
831
+ );
832
+ await copyRulesFile(
833
+ kitSourcePath,
834
+ kitTargetPath,
835
+ targetPath,
836
+ aiTool,
837
+ options.scope,
838
+ this.COMMANDS_FOLDER
839
+ // Replace workflows/ with commands/
840
+ );
841
+ try {
842
+ await copyCommonAssets(kitTargetPath, aiTool, this.COMMANDS_FOLDER);
843
+ } catch {
844
+ }
845
+ const agents = await countItems(path5.join(kitTargetPath, "agents"));
846
+ const skills = await countItems(path5.join(kitTargetPath, "skills"));
847
+ const commands = await countItems(
848
+ path5.join(kitTargetPath, this.COMMANDS_FOLDER)
849
+ );
850
+ results.push({
851
+ kit: kit.name,
852
+ agents,
853
+ skills,
854
+ workflows: commands
855
+ // Still called "workflows" in result for consistency
856
+ });
857
+ }
858
+ return results;
859
+ }
860
+ /**
861
+ * Copy agents with transformation to OpenCode format
862
+ *
863
+ * This method:
864
+ * 1. Reads each agent file from the kit
865
+ * 2. Replaces tool path references (.agent/ → .opencode/)
866
+ * 3. Transforms the frontmatter `tools` field from string to record
867
+ * 4. Removes unnecessary fields (name, skills, tier)
868
+ * 5. Writes the transformed agent to the target directory
869
+ */
870
+ async copyAgentsWithTransform(kitSourcePath, kitTargetPath, toolPath) {
871
+ const agentsSource = path5.join(kitSourcePath, "agents");
872
+ const agentsTarget = path5.join(kitTargetPath, "agents");
873
+ try {
874
+ await fs4.access(agentsSource);
875
+ } catch {
876
+ return;
877
+ }
878
+ await fs4.mkdir(agentsTarget, { recursive: true });
879
+ const entries = await fs4.readdir(agentsSource, { withFileTypes: true });
880
+ for (const entry of entries) {
881
+ if (!entry.isFile() || !entry.name.endsWith(".md")) {
882
+ continue;
883
+ }
884
+ const sourcePath = path5.join(agentsSource, entry.name);
885
+ const targetPath = path5.join(agentsTarget, entry.name);
886
+ let content = await fs4.readFile(sourcePath, "utf-8");
887
+ content = replaceToolPaths(content, toolPath);
888
+ const context = {
889
+ aiTool: { path: toolPath },
890
+ sourcePath,
891
+ targetPath
892
+ };
893
+ const transformedContent = this.agentTransformer.transform(
894
+ content,
895
+ context
896
+ );
897
+ await fs4.writeFile(targetPath, transformedContent);
898
+ }
899
+ }
900
+ /**
901
+ * Copy workflows with transformation to OpenCode command format
902
+ *
903
+ * This method:
904
+ * 1. Reads each workflow file from the kit
905
+ * 2. Transforms the frontmatter to OpenCode command format
906
+ * 3. Replaces path references (.agent/ → .opencode/)
907
+ * 4. Replaces terminology (workflow → command)
908
+ * 5. Writes the transformed command to the commands directory
909
+ */
910
+ async copyWorkflowsWithTransform(kitSourcePath, kitTargetPath, toolPath) {
911
+ const workflowsSource = path5.join(kitSourcePath, "workflows");
912
+ const commandsTarget = path5.join(kitTargetPath, this.COMMANDS_FOLDER);
913
+ try {
914
+ await fs4.access(workflowsSource);
915
+ } catch {
916
+ return;
917
+ }
918
+ await fs4.mkdir(commandsTarget, { recursive: true });
919
+ const entries = await fs4.readdir(workflowsSource, { withFileTypes: true });
920
+ for (const entry of entries) {
921
+ if (!entry.isFile() || !entry.name.endsWith(".md")) {
922
+ continue;
923
+ }
924
+ const sourcePath = path5.join(workflowsSource, entry.name);
925
+ const targetPath = path5.join(commandsTarget, entry.name);
926
+ const content = await fs4.readFile(sourcePath, "utf-8");
927
+ const context = {
928
+ aiTool: { path: toolPath },
929
+ sourcePath,
930
+ targetPath
931
+ };
932
+ const transformedContent = this.workflowTransformer.transform(
933
+ content,
934
+ context
935
+ );
936
+ await fs4.writeFile(targetPath, transformedContent);
937
+ }
938
+ }
939
+ };
940
+
653
941
  // src/installers/index.ts
654
942
  var installerRegistry = {
655
943
  // Antigravity is the base/default installer
@@ -660,7 +948,9 @@ var installerRegistry = {
660
948
  codex: new AntigravityInstaller(),
661
949
  custom: new AntigravityInstaller(),
662
950
  // Cursor has special handling (workflows -> commands)
663
- cursor: new CursorInstaller()
951
+ cursor: new CursorInstaller(),
952
+ // OpenCode has special handling (workflows -> commands, AGENTS.md at root)
953
+ opencode: new OpenCodeInstaller()
664
954
  };
665
955
  function getInstaller(toolId) {
666
956
  const installer = installerRegistry[toolId];
@@ -677,13 +967,13 @@ async function installKit(options) {
677
967
  // src/cli.ts
678
968
  function expandPath(inputPath) {
679
969
  if (inputPath.startsWith("~")) {
680
- return path5.join(os2.homedir(), inputPath.slice(1));
970
+ return path6.join(os2.homedir(), inputPath.slice(1));
681
971
  }
682
972
  return inputPath;
683
973
  }
684
974
  function directoryExists(dirPath) {
685
975
  try {
686
- return fs4.existsSync(dirPath) && fs4.statSync(dirPath).isDirectory();
976
+ return fs5.existsSync(dirPath) && fs5.statSync(dirPath).isDirectory();
687
977
  } catch {
688
978
  return false;
689
979
  }
@@ -692,7 +982,7 @@ function getInstallPath(aiTool, scope, workspacePath) {
692
982
  if (scope === "global") {
693
983
  return getGlobalPath(aiTool);
694
984
  }
695
- return path5.join(workspacePath, aiTool.path);
985
+ return path6.join(workspacePath, aiTool.path);
696
986
  }
697
987
  function getDisplayPath(absolutePath) {
698
988
  const home = os2.homedir();
@@ -747,7 +1037,7 @@ async function main() {
747
1037
  {
748
1038
  value: "workspace",
749
1039
  label: "Workspace (Project)",
750
- hint: `Best for sharing with team (${path5.join(process.cwd(), aiTool.path)})`
1040
+ hint: `Best for sharing with team (${path6.join(process.cwd(), aiTool.path)})`
751
1041
  },
752
1042
  {
753
1043
  value: "global",
@@ -782,7 +1072,7 @@ async function main() {
782
1072
  workspacePath = expandPath(pathResult);
783
1073
  }
784
1074
  const finalInstallPath = getInstallPath(aiTool, scope, workspacePath);
785
- const rulesFilePath = scope === "global" || aiTool.rulesInsideKit ? path5.join(finalInstallPath, aiTool.rulesFile) : path5.join(workspacePath, aiTool.rulesFile);
1075
+ const rulesFilePath = scope === "global" || aiTool.rulesInsideKit ? path6.join(finalInstallPath, aiTool.rulesFile) : path6.join(workspacePath, aiTool.rulesFile);
786
1076
  if (directoryExists(finalInstallPath)) {
787
1077
  p.log.warn(
788
1078
  `${pc.yellow("\u26A0")} Existing toolkit found at: ${pc.cyan(getDisplayPath(finalInstallPath))}`
@@ -823,7 +1113,7 @@ async function main() {
823
1113
  if (replaceResult === "replace") {
824
1114
  const s_rm = p.spinner();
825
1115
  s_rm.start("Cleaning up old files...");
826
- fs4.rmSync(finalInstallPath, { recursive: true, force: true });
1116
+ fs5.rmSync(finalInstallPath, { recursive: true, force: true });
827
1117
  s_rm.stop("Cleanup complete.");
828
1118
  }
829
1119
  }
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.2",
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"