opencode-marketplace 0.4.1 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-marketplace",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "CLI marketplace for OpenCode plugins",
5
5
  "type": "module",
6
6
  "author": "nikiforovall",
package/src/cli.ts CHANGED
@@ -6,6 +6,7 @@ import { list } from "./commands/list";
6
6
  import { scan } from "./commands/scan";
7
7
  import { uninstall } from "./commands/uninstall";
8
8
  import { update } from "./commands/update";
9
+ import { setAgentsOverride } from "./config";
9
10
 
10
11
  export function run(argv = process.argv) {
11
12
  const cli = cac("opencode-marketplace");
@@ -19,11 +20,15 @@ export function run(argv = process.argv) {
19
20
  )
20
21
  .option("--force", "Overwrite existing components", { default: false })
21
22
  .option("-i, --interactive", "Interactively select components to install", { default: false })
23
+ .option("--agents", "Install skills to ~/.agents/skills/ (overrides config)")
22
24
  .action(async (path, options) => {
23
25
  if (options.scope !== "user" && options.scope !== "project") {
24
26
  console.error(`Invalid scope: ${options.scope}. Must be 'user' or 'project'.`);
25
27
  process.exit(1);
26
28
  }
29
+ if (options.agents) {
30
+ setAgentsOverride();
31
+ }
27
32
 
28
33
  try {
29
34
  await install(path, options);
@@ -41,18 +46,26 @@ export function run(argv = process.argv) {
41
46
  .command("import [config-path]", "Install plugins from import config file")
42
47
  .option("--target-dir <dir>", "Custom installation directory (overrides ~/.config/opencode)")
43
48
  .option("--force", "Overwrite existing components", { default: false })
49
+ .option("--agents", "Install skills to ~/.agents/skills/ (overrides config)")
44
50
  .action((configPath, options) => {
51
+ if (options.agents) {
52
+ setAgentsOverride();
53
+ }
45
54
  return importPlugins(configPath, options);
46
55
  });
47
56
 
48
57
  cli
49
58
  .command("uninstall <name>", "Uninstall a plugin")
50
59
  .option("--scope <scope>", "Installation scope (user/project)", { default: "user" })
60
+ .option("--agents", "Install skills to ~/.agents/skills/ (overrides config)")
51
61
  .action((name, options) => {
52
62
  if (options.scope !== "user" && options.scope !== "project") {
53
63
  console.error(`Invalid scope: ${options.scope}. Must be 'user' or 'project'.`);
54
64
  process.exit(1);
55
65
  }
66
+ if (options.agents) {
67
+ setAgentsOverride();
68
+ }
56
69
  return uninstall(name, options);
57
70
  });
58
71
 
@@ -76,11 +89,15 @@ export function run(argv = process.argv) {
76
89
  cli
77
90
  .command("update <name>", "Update a plugin from its remote source")
78
91
  .option("--scope <scope>", "Installation scope (user/project)", { default: "user" })
92
+ .option("--agents", "Install skills to ~/.agents/skills/ (overrides config)")
79
93
  .action((name, options) => {
80
94
  if (options.scope !== "user" && options.scope !== "project") {
81
95
  console.error(`Invalid scope: ${options.scope}. Must be 'user' or 'project'.`);
82
96
  process.exit(1);
83
97
  }
98
+ if (options.agents) {
99
+ setAgentsOverride();
100
+ }
84
101
  return update(name, options);
85
102
  });
86
103
 
@@ -96,9 +96,9 @@ export async function scan(path: string, options: ScanOptions): Promise<void> {
96
96
  console.log("No components found.");
97
97
  console.log();
98
98
  console.log("Expected directories:");
99
- console.log(" - .opencode/command/, .claude/commands/, command/, or commands/");
100
- console.log(" - .opencode/agent/, .claude/agents/, agent/, or agents/");
101
- console.log(" - .opencode/skill/, .claude/skills/, skill/, or skills/");
99
+ console.log(" - .opencode/commands/, .claude/commands/, commands/, or command/");
100
+ console.log(" - .opencode/agents/, .claude/agents/, agents/, or agent/");
101
+ console.log(" - .opencode/skills/, .claude/skills/, skills/, or skill/");
102
102
  return;
103
103
  }
104
104
 
package/src/config.ts ADDED
@@ -0,0 +1,65 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join, normalize } from "node:path";
4
+ import type { OcmConfig } from "./types";
5
+
6
+ function getDefaultSkillsBase(): string {
7
+ return join(homedir(), ".agents");
8
+ }
9
+
10
+ let cachedConfig: OcmConfig | null = null;
11
+ let agentsOverride = false;
12
+
13
+ export function setAgentsOverride(): void {
14
+ agentsOverride = true;
15
+ }
16
+
17
+ export function getConfigPath(): string {
18
+ return join(homedir(), ".config", "opencode", "ocm-config.json");
19
+ }
20
+
21
+ export function loadConfig(): OcmConfig {
22
+ if (cachedConfig !== null) {
23
+ return cachedConfig;
24
+ }
25
+
26
+ const configPath = getConfigPath();
27
+
28
+ if (!existsSync(configPath)) {
29
+ cachedConfig = {};
30
+ return cachedConfig;
31
+ }
32
+
33
+ try {
34
+ const content = readFileSync(configPath, "utf-8");
35
+ const parsed = JSON.parse(content);
36
+ cachedConfig = {};
37
+
38
+ if (parsed && typeof parsed === "object" && typeof parsed.skillsPath === "string") {
39
+ let skillsPath = parsed.skillsPath;
40
+ if (skillsPath.startsWith("~/") || skillsPath === "~") {
41
+ skillsPath = join(homedir(), skillsPath.slice(2));
42
+ }
43
+ cachedConfig.skillsPath = normalize(skillsPath);
44
+ }
45
+
46
+ return cachedConfig;
47
+ } catch {
48
+ cachedConfig = {};
49
+ return cachedConfig;
50
+ }
51
+ }
52
+
53
+ export function resolveUserSkillsPath(): string {
54
+ if (agentsOverride) {
55
+ return join(getDefaultSkillsBase(), "skills");
56
+ }
57
+ const config = loadConfig();
58
+ const base = config.skillsPath || getDefaultSkillsBase();
59
+ return join(base, "skills");
60
+ }
61
+
62
+ export function resetConfigCache(): void {
63
+ cachedConfig = null;
64
+ agentsOverride = false;
65
+ }
package/src/discovery.ts CHANGED
@@ -5,9 +5,9 @@ import type { ComponentType, DiscoveredComponent } from "./types";
5
5
  import { getComponentTargetName } from "./types";
6
6
 
7
7
  const SEARCH_PATHS: Record<ComponentType, string[]> = {
8
- command: [".opencode/command", ".claude/commands", "command", "commands"],
9
- agent: [".opencode/agent", ".claude/agents", "agent", "agents"],
10
- skill: [".opencode/skill", ".claude/skills", "skill", "skills"],
8
+ command: [".opencode/commands", ".claude/commands", "commands", "command"],
9
+ agent: [".opencode/agents", ".claude/agents", "agents", "agent"],
10
+ skill: [".opencode/skills", ".claude/skills", "skills", "skill"],
11
11
  };
12
12
 
13
13
  /**
package/src/paths.ts CHANGED
@@ -1,22 +1,32 @@
1
1
  import { mkdir } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { join, normalize } from "node:path";
4
+ import { resolveUserSkillsPath } from "./config";
4
5
  import type { ComponentType, Scope } from "./types";
5
6
  import { getComponentTargetName } from "./types";
6
7
 
8
+ function pluralizeType(type: ComponentType): string {
9
+ return `${type}s`;
10
+ }
11
+
7
12
  /**
8
13
  * Returns the base directory for a component type with trailing slash.
9
14
  * Examples:
10
- * - User scope: "~/.config/opencode/command/"
11
- * - Project scope: ".opencode/command/"
15
+ * - User scope: "~/.config/opencode/commands/", "~/.agents/skills/"
16
+ * - Project scope: ".opencode/commands/"
12
17
  */
13
18
  export function getComponentDir(type: ComponentType, scope: Scope, targetDir?: string): string {
14
- const basePath =
15
- scope === "user"
16
- ? join(targetDir || join(homedir(), ".config", "opencode"), type)
17
- : join(process.cwd(), ".opencode", type);
19
+ if (scope === "user") {
20
+ if (targetDir) {
21
+ return `${normalize(join(targetDir, pluralizeType(type)))}/`;
22
+ }
23
+ if (type === "skill") {
24
+ return `${normalize(resolveUserSkillsPath())}/`;
25
+ }
26
+ return `${normalize(join(homedir(), ".config", "opencode", pluralizeType(type)))}/`;
27
+ }
18
28
 
19
- return `${normalize(basePath)}/`;
29
+ return `${normalize(join(process.cwd(), ".opencode", pluralizeType(type)))}/`;
20
30
  }
21
31
 
22
32
  /**
@@ -24,8 +34,8 @@ export function getComponentDir(type: ComponentType, scope: Scope, targetDir?: s
24
34
  * Handles both files (commands/agents) and directories (skills).
25
35
  *
26
36
  * Examples:
27
- * - Command: "~/.config/opencode/command/myplugin--reflect.md"
28
- * - Skill: "~/.config/opencode/skill/myplugin--code-review/"
37
+ * - Command: "~/.config/opencode/commands/myplugin--reflect.md"
38
+ * - Skill: "~/.agents/skills/myplugin--code-review/"
29
39
  */
30
40
  export function getComponentTargetPath(
31
41
  pluginName: string,
@@ -47,7 +57,7 @@ export function getComponentTargetPath(
47
57
  }
48
58
 
49
59
  /**
50
- * Ensures all component directories (command, agent, skill) exist for the given scope.
60
+ * Ensures all component directories (commands, agents, skills) exist for the given scope.
51
61
  * Idempotent - safe to call multiple times.
52
62
  */
53
63
  export async function ensureComponentDirsExist(scope: Scope, targetDir?: string): Promise<void> {
package/src/types.ts CHANGED
@@ -48,6 +48,10 @@ export interface ImportConfig {
48
48
  plugins: string[];
49
49
  }
50
50
 
51
+ export interface OcmConfig {
52
+ skillsPath?: string;
53
+ }
54
+
51
55
  // Validation & Helpers
52
56
 
53
57
  /**