mcpick 0.0.18 → 0.0.20

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 (79) hide show
  1. package/.github/workflows/ci.yml +26 -0
  2. package/.vscode/settings.json +5 -0
  3. package/CHANGELOG.md +15 -0
  4. package/LICENSE +21 -0
  5. package/dist/add-BDyaBew0.js +113 -0
  6. package/dist/add-json-BjgzdeG-.js +58 -0
  7. package/dist/atomic-write-BqEykHp9.js +26 -0
  8. package/dist/backup-DSDhHI5f.js +64 -0
  9. package/dist/cache-D6kd7qE8.js +226 -0
  10. package/dist/claude-cli-DnmBJrjg.js +445 -0
  11. package/dist/cli-CsFfnWBo.js +84 -0
  12. package/dist/clone-DYKPEsar.js +88 -0
  13. package/dist/config-DijVdEFn.js +176 -0
  14. package/dist/dev-DRJRNp7y.js +265 -0
  15. package/dist/disable-xJXZfUR_.js +39 -0
  16. package/dist/enable-RrpcN6la.js +40 -0
  17. package/dist/get-Bb1eOOIZ.js +41 -0
  18. package/dist/hook-state-Di8lUsPr.js +171 -0
  19. package/dist/hooks-Bmn7pUZa.js +280 -0
  20. package/dist/index.js +1232 -305
  21. package/dist/list-B8YeDWt6.js +64 -0
  22. package/dist/marketplace-DcKk5dc1.js +168 -0
  23. package/dist/output-BchYq0mR.js +15 -0
  24. package/dist/paths-BPISiJi4.js +124 -0
  25. package/dist/plugin-cache-Bby9Dxm9.js +405 -0
  26. package/dist/plugins-Dc7DN6R_.js +212 -0
  27. package/dist/profile-CX97sMGp.js +120 -0
  28. package/dist/profile-DkY_lBEm.js +70 -0
  29. package/dist/redact-O35tjnRD.js +26 -0
  30. package/dist/registry-CfUKT7_C.js +92 -0
  31. package/dist/reload-CYDhkCVZ.js +31 -0
  32. package/dist/remove-D1owHLhG.js +31 -0
  33. package/dist/reset-project-choices-BfRSNN3m.js +28 -0
  34. package/dist/restore-DdMfUljI.js +84 -0
  35. package/dist/rolldown-runtime-CiIaOW0V.js +13 -0
  36. package/dist/settings-DEcWtzLE.js +201 -0
  37. package/dist/validation-xMlbgGCF.js +44 -0
  38. package/package.json +23 -22
  39. package/dist/cli/commands/add-json.js +0 -60
  40. package/dist/cli/commands/add.js +0 -135
  41. package/dist/cli/commands/backup.js +0 -83
  42. package/dist/cli/commands/cache.js +0 -296
  43. package/dist/cli/commands/clone.js +0 -108
  44. package/dist/cli/commands/dev.js +0 -167
  45. package/dist/cli/commands/disable.js +0 -36
  46. package/dist/cli/commands/enable.js +0 -39
  47. package/dist/cli/commands/get.js +0 -45
  48. package/dist/cli/commands/hooks.js +0 -314
  49. package/dist/cli/commands/list.js +0 -64
  50. package/dist/cli/commands/marketplace.js +0 -211
  51. package/dist/cli/commands/plugins.js +0 -265
  52. package/dist/cli/commands/profile.js +0 -134
  53. package/dist/cli/commands/reload.js +0 -36
  54. package/dist/cli/commands/remove.js +0 -35
  55. package/dist/cli/commands/reset-project-choices.js +0 -32
  56. package/dist/cli/commands/restore.js +0 -105
  57. package/dist/cli/index.js +0 -29
  58. package/dist/cli/output.js +0 -21
  59. package/dist/commands/add-server.js +0 -310
  60. package/dist/commands/backup.js +0 -60
  61. package/dist/commands/edit-config.js +0 -109
  62. package/dist/commands/edit-plugins.js +0 -201
  63. package/dist/commands/manage-cache.js +0 -155
  64. package/dist/commands/manage-hooks.js +0 -99
  65. package/dist/commands/manage-marketplace.js +0 -293
  66. package/dist/commands/restore.js +0 -118
  67. package/dist/core/config.js +0 -200
  68. package/dist/core/dev-override.js +0 -155
  69. package/dist/core/hook-state.js +0 -220
  70. package/dist/core/plugin-cache.js +0 -506
  71. package/dist/core/profile.js +0 -94
  72. package/dist/core/registry.js +0 -121
  73. package/dist/core/settings.js +0 -243
  74. package/dist/core/validation.js +0 -49
  75. package/dist/types.js +0 -2
  76. package/dist/utils/atomic-write.js +0 -27
  77. package/dist/utils/claude-cli.js +0 -485
  78. package/dist/utils/paths.js +0 -114
  79. package/dist/utils/redact.js +0 -28
@@ -0,0 +1,26 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+
15
+ - uses: pnpm/action-setup@v6
16
+
17
+ - uses: actions/setup-node@v6
18
+ with:
19
+ node-version-file: package.json
20
+ cache: pnpm
21
+
22
+ - run: pnpm install --frozen-lockfile
23
+
24
+ - run: pnpm run build
25
+
26
+ - run: pnpm run test
@@ -0,0 +1,5 @@
1
+ {
2
+ "editor.defaultFormatter": "oxc.oxc-vscode",
3
+ "editor.formatOnSave": true,
4
+ "oxc.fmt.configPath": "./vite.config.ts"
5
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # mcpick
2
2
 
3
+ ## 0.0.20
4
+
5
+ ### Patch Changes
6
+
7
+ - 00ea930: chore: add unit tests and CI workflow with GitHub Actions
8
+ - 37a62e1: feat: auto-show help instead of TUI in non-TTY environments for LLM agents
9
+ - fc1db54: fix: replace exec with execFile to eliminate shell injection on all platforms
10
+
11
+ ## 0.0.19
12
+
13
+ ### Patch Changes
14
+
15
+ - 5ed618e: Migrate build tooling from tsc/prettier to vite-plus, fix all lint warnings
16
+ - 08997dc: feat: rewrite --help for LLM agents with workflow, concepts, and examples sections
17
+
3
18
  ## 0.0.18
4
19
 
5
20
  ### Patch Changes
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Scott Spence
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,113 @@
1
+ import { n as validate_mcp_server } from "./validation-xMlbgGCF.js";
2
+ import { t as add_server_to_registry } from "./registry-CfUKT7_C.js";
3
+ import { t as add_mcp_via_cli } from "./claude-cli-DnmBJrjg.js";
4
+ import { n as output, t as error } from "./output-BchYq0mR.js";
5
+ import { defineCommand } from "citty";
6
+ //#region src/cli/commands/add.ts
7
+ var add_default = defineCommand({
8
+ meta: {
9
+ name: "add",
10
+ description: "Add a new MCP server to the registry and enable it"
11
+ },
12
+ args: {
13
+ name: {
14
+ type: "string",
15
+ description: "Server name",
16
+ required: true
17
+ },
18
+ command: {
19
+ type: "string",
20
+ description: "Command to run (for stdio transport)"
21
+ },
22
+ args: {
23
+ type: "string",
24
+ description: "Comma-separated arguments (e.g. \"npx,-y,mcp-sqlite\")"
25
+ },
26
+ url: {
27
+ type: "string",
28
+ description: "URL (for sse or http transport)"
29
+ },
30
+ type: {
31
+ type: "string",
32
+ description: "Transport type: stdio, sse, or http (default: stdio)",
33
+ default: "stdio"
34
+ },
35
+ env: {
36
+ type: "string",
37
+ description: "Environment variables as KEY=val,KEY=val"
38
+ },
39
+ headers: {
40
+ type: "string",
41
+ description: "HTTP headers as KEY=val,KEY=val"
42
+ },
43
+ description: {
44
+ type: "string",
45
+ description: "Server description"
46
+ },
47
+ scope: {
48
+ type: "string",
49
+ description: "Scope: local, project, or user (default: local)",
50
+ default: "local"
51
+ },
52
+ json: {
53
+ type: "boolean",
54
+ description: "Output as JSON",
55
+ default: false
56
+ }
57
+ },
58
+ async run({ args }) {
59
+ const scope = args.scope;
60
+ if (![
61
+ "local",
62
+ "project",
63
+ "user"
64
+ ].includes(scope)) error(`Invalid scope: ${scope}. Use local, project, or user.`);
65
+ const transport = args.type;
66
+ if (![
67
+ "stdio",
68
+ "sse",
69
+ "http"
70
+ ].includes(transport)) error(`Invalid type: ${transport}. Use stdio, sse, or http.`);
71
+ const server_data = { name: args.name };
72
+ if (transport === "stdio") {
73
+ if (!args.command) error("--command is required for stdio transport");
74
+ server_data.command = args.command;
75
+ if (args.args) server_data.args = args.args.split(",");
76
+ } else {
77
+ if (!args.url) error(`--url is required for ${transport} transport`);
78
+ server_data.type = transport;
79
+ server_data.url = args.url;
80
+ if (args.headers) server_data.headers = parse_key_value_pairs(args.headers);
81
+ }
82
+ if (args.env) server_data.env = parse_key_value_pairs(args.env);
83
+ if (args.description) server_data.description = args.description;
84
+ let server;
85
+ try {
86
+ server = validate_mcp_server(server_data);
87
+ } catch (err) {
88
+ error(`Invalid server config: ${err instanceof Error ? err.message : "validation failed"}`);
89
+ }
90
+ await add_server_to_registry(server);
91
+ const result = await add_mcp_via_cli(server, scope);
92
+ if (args.json) output({
93
+ added: server.name,
94
+ scope,
95
+ cli: result.success,
96
+ error: result.error
97
+ }, true);
98
+ else if (result.success) console.log(`Added '${server.name}' and enabled (scope: ${scope})`);
99
+ else console.log(`Added '${server.name}' to registry but CLI failed: ${result.error}`);
100
+ }
101
+ });
102
+ function parse_key_value_pairs(input) {
103
+ const result = {};
104
+ for (const pair of input.split(",")) {
105
+ const eq = pair.indexOf("=");
106
+ if (eq > 0) result[pair.substring(0, eq)] = pair.substring(eq + 1);
107
+ }
108
+ return result;
109
+ }
110
+ //#endregion
111
+ export { add_default as default };
112
+
113
+ //# sourceMappingURL=add-BDyaBew0.js.map
@@ -0,0 +1,58 @@
1
+ import { u as mcp_add_json_via_cli } from "./claude-cli-DnmBJrjg.js";
2
+ import { n as output, t as error } from "./output-BchYq0mR.js";
3
+ import { defineCommand } from "citty";
4
+ //#region src/cli/commands/add-json.ts
5
+ var add_json_default = defineCommand({
6
+ meta: {
7
+ name: "add-json",
8
+ description: "Add an MCP server from a JSON configuration string"
9
+ },
10
+ args: {
11
+ name: {
12
+ type: "positional",
13
+ description: "Server name",
14
+ required: true
15
+ },
16
+ config: {
17
+ type: "positional",
18
+ description: "JSON configuration string",
19
+ required: true
20
+ },
21
+ scope: {
22
+ type: "string",
23
+ description: "Scope: local, project, or user (default: local)",
24
+ default: "local"
25
+ },
26
+ json: {
27
+ type: "boolean",
28
+ description: "Output as JSON",
29
+ default: false
30
+ }
31
+ },
32
+ async run({ args }) {
33
+ const scope = args.scope;
34
+ if (![
35
+ "local",
36
+ "project",
37
+ "user"
38
+ ].includes(scope)) error(`Invalid scope: ${scope}. Use local, project, or user.`);
39
+ try {
40
+ JSON.parse(args.config);
41
+ } catch {
42
+ error("Invalid JSON configuration. Provide a valid JSON string.");
43
+ }
44
+ const result = await mcp_add_json_via_cli(args.name, args.config, scope);
45
+ if (args.json) output({
46
+ added: args.name,
47
+ scope,
48
+ success: result.success,
49
+ error: result.error
50
+ }, true);
51
+ else if (result.success) console.log(`Added '${args.name}' from JSON (scope: ${scope})`);
52
+ else error(result.error || "Unknown error");
53
+ }
54
+ });
55
+ //#endregion
56
+ export { add_json_default as default };
57
+
58
+ //# sourceMappingURL=add-json-BjgzdeG-.js.map
@@ -0,0 +1,26 @@
1
+ import { readFile, rename, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ //#region src/utils/atomic-write.ts
4
+ /**
5
+ * Atomically write a JSON file with fresh-read merging.
6
+ *
7
+ * 1. Re-reads the file right before writing to pick up concurrent changes
8
+ * 2. Applies the merge function to the freshest data
9
+ * 3. Writes to a temp file, then renames (atomic on same filesystem)
10
+ */
11
+ async function atomic_json_write(file_path, merge) {
12
+ let existing = {};
13
+ try {
14
+ const content = await readFile(file_path, "utf-8");
15
+ existing = JSON.parse(content);
16
+ } catch {}
17
+ const merged = merge(existing);
18
+ const content = JSON.stringify(merged, null, 2);
19
+ const tmp_path = join(dirname(file_path), `.${Date.now()}.tmp`);
20
+ await writeFile(tmp_path, content, "utf-8");
21
+ await rename(tmp_path, file_path);
22
+ }
23
+ //#endregion
24
+ export { atomic_json_write as t };
25
+
26
+ //# sourceMappingURL=atomic-write-BqEykHp9.js.map
@@ -0,0 +1,64 @@
1
+ import { m as get_plugin_backup_filename, n as get_backup_filename, r as get_backups_dir, t as ensure_directory_exists } from "./paths-BPISiJi4.js";
2
+ import { s as read_claude_config } from "./config-DijVdEFn.js";
3
+ import { a as read_claude_settings } from "./settings-DEcWtzLE.js";
4
+ import { n as output } from "./output-BchYq0mR.js";
5
+ import { readdir, unlink, writeFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ import { defineCommand } from "citty";
8
+ //#region src/cli/commands/backup.ts
9
+ const MAX_BACKUPS = 10;
10
+ async function cleanup_old_backups(prefix) {
11
+ try {
12
+ const backups_dir = get_backups_dir();
13
+ const backup_files = (await readdir(backups_dir)).filter((f) => f.startsWith(prefix) && f.endsWith(".json")).sort().reverse();
14
+ if (backup_files.length > MAX_BACKUPS) for (const file of backup_files.slice(MAX_BACKUPS)) await unlink(join(backups_dir, file));
15
+ } catch {}
16
+ }
17
+ var backup_default = defineCommand({
18
+ meta: {
19
+ name: "backup",
20
+ description: "Create a timestamped backup of MCP servers and plugins"
21
+ },
22
+ args: { json: {
23
+ type: "boolean",
24
+ description: "Output as JSON",
25
+ default: false
26
+ } },
27
+ async run({ args }) {
28
+ const current_config = await read_claude_config();
29
+ const current_settings = await read_claude_settings();
30
+ const backups_dir = get_backups_dir();
31
+ await ensure_directory_exists(backups_dir);
32
+ const mcp_path = join(backups_dir, get_backup_filename());
33
+ const mcp_backup = { mcpServers: current_config.mcpServers || {} };
34
+ await writeFile(mcp_path, JSON.stringify(mcp_backup, null, 2), "utf-8");
35
+ const plugins = current_settings.enabledPlugins || {};
36
+ const plugin_count = Object.keys(plugins).length;
37
+ let plugin_path = null;
38
+ if (plugin_count > 0) {
39
+ plugin_path = join(backups_dir, get_plugin_backup_filename());
40
+ await writeFile(plugin_path, JSON.stringify({ enabledPlugins: plugins }, null, 2), "utf-8");
41
+ }
42
+ await cleanup_old_backups("mcp-servers-");
43
+ await cleanup_old_backups("plugins-");
44
+ const server_count = Object.keys(current_config.mcpServers || {}).length;
45
+ if (args.json) output({
46
+ mcp: {
47
+ path: mcp_path,
48
+ servers: server_count
49
+ },
50
+ plugins: plugin_path ? {
51
+ path: plugin_path,
52
+ plugins: plugin_count
53
+ } : null
54
+ }, true);
55
+ else {
56
+ console.log(`Backup created: ${mcp_path} (${server_count} servers)`);
57
+ if (plugin_path) console.log(`Plugin backup: ${plugin_path} (${plugin_count} plugins)`);
58
+ }
59
+ }
60
+ });
61
+ //#endregion
62
+ export { backup_default as default };
63
+
64
+ //# sourceMappingURL=backup-DSDhHI5f.js.map
@@ -0,0 +1,226 @@
1
+ import { a as list_linked_plugins, c as read_installed_plugins, d as refresh_all_marketplaces, f as scan_all_cache_keys, i as link_local_plugin, n as clear_plugin_caches, p as unlink_local_plugin, r as get_cached_plugins_info, t as clean_orphaned_versions } from "./plugin-cache-Bby9Dxm9.js";
2
+ import { n as output, t as error } from "./output-BchYq0mR.js";
3
+ import { defineCommand } from "citty";
4
+ var cache_default = defineCommand({
5
+ meta: {
6
+ name: "cache",
7
+ description: "Manage plugin cache"
8
+ },
9
+ subCommands: {
10
+ status: defineCommand({
11
+ meta: {
12
+ name: "status",
13
+ description: "Show cached plugins with staleness info"
14
+ },
15
+ args: { json: {
16
+ type: "boolean",
17
+ description: "Output as JSON",
18
+ default: false
19
+ } },
20
+ async run({ args }) {
21
+ const plugins = await get_cached_plugins_info();
22
+ if (args.json) {
23
+ output(plugins, true);
24
+ return;
25
+ }
26
+ if (plugins.length === 0) {
27
+ console.log("No cached plugins found.");
28
+ return;
29
+ }
30
+ for (const p of plugins) {
31
+ const stale_markers = [];
32
+ if (p.isVersionStale) stale_markers.push(`version: ${p.installedVersion} → ${p.latestVersion}`);
33
+ if (p.isShaStale) stale_markers.push("commits behind");
34
+ if (p.orphanedVersions.length > 0) stale_markers.push(`${p.orphanedVersions.length} orphaned`);
35
+ const status_str = stale_markers.length > 0 ? ` [stale: ${stale_markers.join(", ")}]` : " [up to date]";
36
+ console.log(`${p.name}@${p.marketplace} v${p.installedVersion}${status_str}`);
37
+ }
38
+ }
39
+ }),
40
+ clear: defineCommand({
41
+ meta: {
42
+ name: "clear",
43
+ description: "Clear plugin caches (refreshes marketplace first)"
44
+ },
45
+ args: {
46
+ plugin: {
47
+ type: "positional",
48
+ description: "Plugin key (name@marketplace) — omit for all",
49
+ required: false
50
+ },
51
+ all: {
52
+ type: "boolean",
53
+ description: "Clear all plugin caches",
54
+ default: false
55
+ },
56
+ json: {
57
+ type: "boolean",
58
+ description: "Output as JSON",
59
+ default: false
60
+ }
61
+ },
62
+ async run({ args }) {
63
+ const installed = await read_installed_plugins();
64
+ const installed_keys = Object.keys(installed.plugins);
65
+ const scanned_keys = args.all ? await scan_all_cache_keys() : [];
66
+ const all_keys = [...new Set([...installed_keys, ...scanned_keys])];
67
+ if (all_keys.length === 0) {
68
+ if (args.json) output({
69
+ cleared: [],
70
+ errors: []
71
+ }, true);
72
+ else console.log("No cached plugins to clear.");
73
+ return;
74
+ }
75
+ let keys_to_clear;
76
+ if (args.plugin) {
77
+ if (!installed.plugins[args.plugin] && !scanned_keys.includes(args.plugin)) error(`Plugin '${args.plugin}' not found in cache. Run 'mcpick cache status' to see cached plugins.`);
78
+ keys_to_clear = [args.plugin];
79
+ } else if (args.all) keys_to_clear = all_keys;
80
+ else error("Specify a plugin key or use --all. Run \"mcpick cache status\" to see cached plugins.");
81
+ const result = await clear_plugin_caches(keys_to_clear);
82
+ if (args.json) output(result, true);
83
+ else {
84
+ for (const key of result.cleared) console.log(`Cleared: ${key}`);
85
+ for (const err of result.errors) console.error(`Error: ${err}`);
86
+ if (result.cleared.length > 0) console.log("\nRun /reload-plugins in Claude Code or restart your session.");
87
+ }
88
+ }
89
+ }),
90
+ "clean-orphaned": defineCommand({
91
+ meta: {
92
+ name: "clean-orphaned",
93
+ description: "Remove orphaned plugin version directories"
94
+ },
95
+ args: { json: {
96
+ type: "boolean",
97
+ description: "Output as JSON",
98
+ default: false
99
+ } },
100
+ async run({ args }) {
101
+ const result = await clean_orphaned_versions();
102
+ if (args.json) output(result, true);
103
+ else if (result.cleaned === 0) console.log("No orphaned versions found.");
104
+ else {
105
+ for (const p of result.paths) console.log(`Removed: ${p}`);
106
+ console.log(`\nCleaned ${result.cleaned} orphaned version(s).`);
107
+ }
108
+ }
109
+ }),
110
+ refresh: defineCommand({
111
+ meta: {
112
+ name: "refresh",
113
+ description: "Refresh all marketplace clones (git pull)"
114
+ },
115
+ args: { json: {
116
+ type: "boolean",
117
+ description: "Output as JSON",
118
+ default: false
119
+ } },
120
+ async run({ args }) {
121
+ const results = await refresh_all_marketplaces();
122
+ if (args.json) {
123
+ output(Object.fromEntries(results), true);
124
+ return;
125
+ }
126
+ if (results.size === 0) {
127
+ console.log("No marketplaces configured.");
128
+ return;
129
+ }
130
+ for (const [name, result] of results) if (result.success) console.log(`${name} refreshed`);
131
+ else console.error(`${name} failed: ${result.error}`);
132
+ }
133
+ }),
134
+ link: defineCommand({
135
+ meta: {
136
+ name: "link",
137
+ description: "Symlink a local directory into the plugin cache for dev"
138
+ },
139
+ args: {
140
+ path: {
141
+ type: "positional",
142
+ description: "Local path to the plugin/marketplace directory",
143
+ required: true
144
+ },
145
+ as: {
146
+ type: "string",
147
+ description: "Plugin key (name@marketplace) for the cache entry",
148
+ required: true
149
+ },
150
+ json: {
151
+ type: "boolean",
152
+ description: "Output as JSON",
153
+ default: false
154
+ }
155
+ },
156
+ async run({ args }) {
157
+ if (!args.as) error("--as is required. Specify plugin key as name@marketplace.");
158
+ if (!args.as.includes("@")) error("Plugin key must be in name@marketplace format (e.g. my-plugin@my-marketplace)");
159
+ const result = await link_local_plugin(args.path, args.as);
160
+ if (args.json) output(result, true);
161
+ else if (result.success) {
162
+ console.log(`Linked: ${result.key}`);
163
+ console.log(` ${result.symlinkPath} → ${result.targetPath}`);
164
+ console.log("\nRun /reload-plugins in Claude Code or restart your session.");
165
+ } else error(result.error || "Unknown error");
166
+ }
167
+ }),
168
+ unlink: defineCommand({
169
+ meta: {
170
+ name: "unlink",
171
+ description: "Remove a symlink from the plugin cache"
172
+ },
173
+ args: {
174
+ key: {
175
+ type: "positional",
176
+ description: "Plugin key (name@marketplace)",
177
+ required: true
178
+ },
179
+ json: {
180
+ type: "boolean",
181
+ description: "Output as JSON",
182
+ default: false
183
+ }
184
+ },
185
+ async run({ args }) {
186
+ const result = await unlink_local_plugin(args.key);
187
+ if (args.json) output(result, true);
188
+ else if (result.success) {
189
+ console.log(`Unlinked: ${args.key}`);
190
+ if (result.restored) console.log(" Original cache directory restored from backup.");
191
+ console.log("\nRun /reload-plugins in Claude Code or restart your session.");
192
+ } else error(result.error || "Unknown error");
193
+ }
194
+ }),
195
+ links: defineCommand({
196
+ meta: {
197
+ name: "links",
198
+ description: "List all symlinked plugin cache entries"
199
+ },
200
+ args: { json: {
201
+ type: "boolean",
202
+ description: "Output as JSON",
203
+ default: false
204
+ } },
205
+ async run({ args }) {
206
+ const linked = await list_linked_plugins();
207
+ if (args.json) {
208
+ output(linked, true);
209
+ return;
210
+ }
211
+ if (linked.length === 0) {
212
+ console.log("No linked plugins.");
213
+ return;
214
+ }
215
+ for (const l of linked) {
216
+ console.log(`${l.key}`);
217
+ console.log(` ${l.symlinkPath} → ${l.targetPath}`);
218
+ }
219
+ }
220
+ })
221
+ }
222
+ });
223
+ //#endregion
224
+ export { cache_default as default };
225
+
226
+ //# sourceMappingURL=cache-D6kd7qE8.js.map