@codecademy/gamut 68.6.1-alpha.f6b2ce.0 → 68.6.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 (59) hide show
  1. package/dist/Modals/elements.d.ts +186 -1
  2. package/dist/Modals/elements.js +3 -3
  3. package/dist/{InternalFloatingCard/InternalFloatingCard.d.ts → PatternBackdrop/PatternBackdrop.d.ts} +8 -24
  4. package/dist/PatternBackdrop/PatternBackdrop.js +42 -0
  5. package/dist/Toast/Toast.js +4 -4
  6. package/package.json +8 -11
  7. package/agent-tools/.claude-plugin/marketplace.json +0 -16
  8. package/agent-tools/.claude-plugin/plugin.json +0 -7
  9. package/agent-tools/.cursor-plugin/plugin.json +0 -7
  10. package/agent-tools/DESIGN.Codecademy.md +0 -696
  11. package/agent-tools/DESIGN.LXStudio.md +0 -512
  12. package/agent-tools/DESIGN.Percipio.md +0 -513
  13. package/agent-tools/DESIGN.md +0 -1
  14. package/agent-tools/agents/.gitkeep +0 -0
  15. package/agent-tools/commands/gamut-review.md +0 -259
  16. package/agent-tools/guidelines/components/animations.md +0 -74
  17. package/agent-tools/guidelines/components/buttons.md +0 -95
  18. package/agent-tools/guidelines/components/card.md +0 -19
  19. package/agent-tools/guidelines/components/coachmark.md +0 -21
  20. package/agent-tools/guidelines/components/data-table.md +0 -79
  21. package/agent-tools/guidelines/components/forms.md +0 -106
  22. package/agent-tools/guidelines/components/loading-states.md +0 -17
  23. package/agent-tools/guidelines/components/menu.md +0 -58
  24. package/agent-tools/guidelines/components/overview.md +0 -124
  25. package/agent-tools/guidelines/components/radial-progress.md +0 -13
  26. package/agent-tools/guidelines/components/select.md +0 -23
  27. package/agent-tools/guidelines/components/tooltips.md +0 -22
  28. package/agent-tools/guidelines/components/video.md +0 -29
  29. package/agent-tools/guidelines/foundations/color.md +0 -168
  30. package/agent-tools/guidelines/foundations/modes.md +0 -69
  31. package/agent-tools/guidelines/foundations/spacing.md +0 -107
  32. package/agent-tools/guidelines/foundations/typography.md +0 -82
  33. package/agent-tools/guidelines/overview-icons.md +0 -19
  34. package/agent-tools/guidelines/overview-illustrations.md +0 -7
  35. package/agent-tools/guidelines/overview-patterns.md +0 -7
  36. package/agent-tools/guidelines/overview.md +0 -84
  37. package/agent-tools/guidelines/setup.md +0 -83
  38. package/agent-tools/rules/accessibility.mdc +0 -78
  39. package/agent-tools/skills/gamut-accessibility/SKILL.md +0 -224
  40. package/agent-tools/skills/gamut-color-mode/SKILL.md +0 -149
  41. package/agent-tools/skills/gamut-components/SKILL.md +0 -46
  42. package/agent-tools/skills/gamut-forms/SKILL.md +0 -101
  43. package/agent-tools/skills/gamut-style-utilities/SKILL.md +0 -111
  44. package/agent-tools/skills/gamut-system-props/SKILL.md +0 -225
  45. package/agent-tools/skills/gamut-testing/SKILL.md +0 -225
  46. package/agent-tools/skills/gamut-theming/SKILL.md +0 -63
  47. package/agent-tools/skills/gamut-typography/SKILL.md +0 -79
  48. package/bin/commands/plugin/install.mjs +0 -213
  49. package/bin/commands/plugin/list.mjs +0 -73
  50. package/bin/commands/plugin/remove.mjs +0 -108
  51. package/bin/commands/plugin/update.mjs +0 -59
  52. package/bin/gamut.mjs +0 -96
  53. package/bin/lib/claude.mjs +0 -52
  54. package/bin/lib/cursor.mjs +0 -40
  55. package/bin/lib/design.mjs +0 -71
  56. package/bin/lib/io.mjs +0 -14
  57. package/bin/lib/resolve-plugin-dir.mjs +0 -38
  58. package/bin/lib/run-command.mjs +0 -22
  59. package/dist/InternalFloatingCard/InternalFloatingCard.js +0 -98
@@ -1,213 +0,0 @@
1
- import { cp, mkdir, readdir, rm, symlink } from 'node:fs/promises';
2
- import { resolve } from 'node:path';
3
-
4
- import { claudePluginSpec, marketplaceName } from '../../lib/claude.mjs';
5
- import { cursorDestPath } from '../../lib/cursor.mjs';
6
- import {
7
- installDesignMd,
8
- listCanonicalThemes,
9
- resolveTheme,
10
- } from '../../lib/design.mjs';
11
- import { log, warn } from '../../lib/io.mjs';
12
- import { getFlag, resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
13
- import { runCommand } from '../../lib/run-command.mjs';
14
-
15
- export const TARGETS = ['cursor', 'claude'];
16
- export const SCOPES = ['all', 'skills', 'rules', 'commands', 'agents'];
17
-
18
- export function help() {
19
- log(`
20
- Usage:
21
- gamut plugin install [target] [options]
22
-
23
- Install the Gamut plugin into an AI tool.
24
-
25
- Arguments:
26
- target Tool to install into (default: cursor)
27
- cursor | claude
28
-
29
- Options:
30
- --scope <scope> Content to install (default: all)
31
- all | skills | rules | commands | agents
32
- --theme <theme> Copy DESIGN.*.md to ./DESIGN.md in the current directory
33
- core | admin | platform | percipio | lxstudio
34
- (admin/platform use Codecademy DESIGN; aliases: codecademy, cc, lx-studio)
35
- --force Overwrite existing DESIGN.md when using --theme
36
- --plugin-dir <path> Override the bundled agent-tools directory
37
- -h, --help Show this help message
38
-
39
- Examples:
40
- gamut plugin install
41
- gamut plugin install claude
42
- gamut plugin install cursor --theme core
43
- gamut plugin install cursor --theme percipio --force
44
- gamut plugin install cursor --scope skills
45
- gamut plugin install cursor --plugin-dir ./my-agent-tools
46
- `);
47
- }
48
-
49
- // ---------------------------------------------------------------------------
50
-
51
- /** Directories in the plugin source that should not be installed to Cursor. */
52
- const CURSOR_IGNORE = new Set([
53
- '.claude-plugin', // Claude Code manifest — not a Cursor concept
54
- ]);
55
-
56
- /** @param {string} sourceRoot @param {string} scope */
57
- async function installCursor(sourceRoot, scope) {
58
- const dest = await cursorDestPath(sourceRoot);
59
-
60
- if ((process.env.CURSOR_INSTALL_METHOD ?? 'copy') !== 'copy') {
61
- // Symlink the whole plugin dir (dev convenience)
62
- await rm(dest, { recursive: true, force: true });
63
- await symlink(resolve(sourceRoot), dest, 'dir');
64
- log(`Cursor: symlinked to ${dest}`);
65
- return;
66
- }
67
-
68
- // Selective copy: always include the cursor manifest, then scoped content dirs
69
- await rm(dest, { recursive: true, force: true });
70
- await mkdir(dest, { recursive: true });
71
-
72
- await cp(`${sourceRoot}/.cursor-plugin`, `${dest}/.cursor-plugin`, {
73
- recursive: true,
74
- });
75
-
76
- let dirs;
77
- if (scope === 'all') {
78
- const entries = await readdir(sourceRoot, { withFileTypes: true });
79
- dirs = entries
80
- .filter(
81
- (e) =>
82
- e.isDirectory() &&
83
- !e.name.startsWith('.') &&
84
- !CURSOR_IGNORE.has(e.name)
85
- )
86
- .map((e) => e.name);
87
- } else {
88
- dirs = [scope];
89
- }
90
-
91
- await Promise.all(
92
- dirs.map((dir) =>
93
- cp(`${sourceRoot}/${dir}`, `${dest}/${dir}`, { recursive: true }).catch(
94
- () => {
95
- // directory may be empty/missing — not an error
96
- }
97
- )
98
- )
99
- );
100
-
101
- const scopeLabel = scope === 'all' ? 'all content' : scope;
102
- log(`Cursor: installed (${scopeLabel}) → ${dest}`);
103
- }
104
-
105
- // Claude Code only loads from recognized plugin directories: skills/, commands/, agents/.
106
- // rules/ is Cursor-specific (.mdc format). guidelines/ is installed to Cursor and is
107
- // also source for the Figma Make kit; Claude marketplace registers the full sourceRoot.
108
-
109
- /** @param {string} sourceRoot */
110
- async function installClaude(sourceRoot) {
111
- const spec = await claudePluginSpec(sourceRoot);
112
- const mpName = marketplaceName(spec);
113
- const root = resolve(sourceRoot);
114
-
115
- let code = await runCommand('claude', [
116
- 'plugin',
117
- 'marketplace',
118
- 'add',
119
- root,
120
- '--scope',
121
- 'user',
122
- ]);
123
- if (code !== 0) {
124
- warn(
125
- `warning: "claude plugin marketplace add" exited ${code} — ` +
126
- `if it's already registered this is safe to ignore.`
127
- );
128
- code = await runCommand('claude', [
129
- 'plugin',
130
- 'marketplace',
131
- 'update',
132
- mpName,
133
- ]);
134
- if (code !== 0) {
135
- throw new Error(
136
- `claude plugin marketplace add/update failed (exit ${code}).\n` +
137
- `Try manually: claude plugin marketplace add ${root}`
138
- );
139
- }
140
- }
141
-
142
- code = await runCommand('claude', [
143
- 'plugin',
144
- 'install',
145
- spec,
146
- '--scope',
147
- 'user',
148
- ]);
149
- if (code !== 0) {
150
- throw new Error(
151
- `claude plugin install failed (exit ${code}).\n` +
152
- `Try manually: claude plugin install ${spec} --scope user`
153
- );
154
- }
155
-
156
- log(`Claude Code: installed ${spec} (user scope)`);
157
- log(
158
- ` Tip: run /reload-plugins in Claude Code if skills don't appear immediately.`
159
- );
160
- log(` One-off without install: claude --plugin-dir ${root}`);
161
- }
162
-
163
- // ---------------------------------------------------------------------------
164
-
165
- /**
166
- * gamut plugin install [cursor|claude] [--scope all|skills|rules|commands|agents]
167
- * [--plugin-dir <path>]
168
- *
169
- * @param {string[]} args
170
- */
171
- export default async function install(args) {
172
- const target = args.find((a) => !a.startsWith('-')) ?? 'cursor';
173
- const scope = getFlag(args, '--scope', 'all') ?? 'all';
174
-
175
- if (!TARGETS.includes(target)) {
176
- throw new Error(
177
- `Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`
178
- );
179
- }
180
- if (!SCOPES.includes(scope)) {
181
- throw new Error(
182
- `Unknown scope: "${scope}". Choose from: ${SCOPES.join(', ')}`
183
- );
184
- }
185
-
186
- const pluginDir = await resolvePluginDir(args);
187
- const theme = getFlag(args, '--theme');
188
- const force = args.includes('--force');
189
-
190
- if (theme) {
191
- resolveTheme(theme);
192
- }
193
-
194
- if (target === 'cursor') {
195
- await installCursor(pluginDir, scope);
196
- } else if (target === 'claude') {
197
- await installClaude(pluginDir);
198
- }
199
-
200
- if (theme) {
201
- const { dest, label } = await installDesignMd(
202
- pluginDir,
203
- process.cwd(),
204
- theme,
205
- {
206
- force,
207
- }
208
- );
209
- log(`DESIGN.md: installed (${label}) → ${dest}`);
210
- }
211
- }
212
-
213
- export { listCanonicalThemes };
@@ -1,73 +0,0 @@
1
- import { stat } from 'node:fs/promises';
2
-
3
- import { cursorDestPath } from '../../lib/cursor.mjs';
4
- import { log } from '../../lib/io.mjs';
5
- import { resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
6
-
7
- export function help() {
8
- log(`
9
- Usage:
10
- gamut plugin list [options]
11
-
12
- Show installation status for all supported targets.
13
-
14
- Options:
15
- --plugin-dir <path> Override the bundled agent-tools directory
16
- -h, --help Show this help message
17
-
18
- Examples:
19
- gamut plugin list
20
- `);
21
- }
22
-
23
- // ---------------------------------------------------------------------------
24
-
25
- /** @param {string} sourceRoot */
26
- async function cursorStatus(sourceRoot) {
27
- const dest = await cursorDestPath(sourceRoot);
28
- const installed = !!(await stat(dest).catch(() => null));
29
- return {
30
- target: 'cursor',
31
- status: installed ? '✓ installed' : '✗ not installed',
32
- notes: installed ? dest : 'run: gamut plugin install cursor',
33
- };
34
- }
35
-
36
- async function claudeStatus() {
37
- // Claude Code doesn't expose a stable filesystem path we can check.
38
- return {
39
- target: 'claude',
40
- status: '? unknown',
41
- notes: 'run: claude plugin list',
42
- };
43
- }
44
-
45
- // ---------------------------------------------------------------------------
46
-
47
- /**
48
- * gamut plugin list
49
- *
50
- * Shows installation status for each supported target.
51
- *
52
- * @param {string[]} args
53
- */
54
- export default async function list(args) {
55
- const pluginDir = await resolvePluginDir(args);
56
-
57
- const rows = await Promise.all([cursorStatus(pluginDir), claudeStatus()]);
58
-
59
- const col0 = Math.max(...rows.map((r) => r.target.length));
60
- const col1 = Math.max(...rows.map((r) => r.status.length));
61
-
62
- const header = `${'Target'.padEnd(col0)} ${'Status'.padEnd(
63
- col1
64
- )} Path / Notes`;
65
- const rule = '─'.repeat(header.length);
66
-
67
- log(`\n${header}`);
68
- log(rule);
69
- for (const row of rows) {
70
- log(`${row.target.padEnd(col0)} ${row.status.padEnd(col1)} ${row.notes}`);
71
- }
72
- log('');
73
- }
@@ -1,108 +0,0 @@
1
- import { rm, stat } from 'node:fs/promises';
2
-
3
- import { claudePluginSpec, marketplaceName } from '../../lib/claude.mjs';
4
- import { cursorDestPath } from '../../lib/cursor.mjs';
5
- import { log, warn } from '../../lib/io.mjs';
6
- import { resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
7
- import { runCommand } from '../../lib/run-command.mjs';
8
- import { TARGETS } from './install.mjs';
9
-
10
- export function help() {
11
- log(`
12
- Usage:
13
- gamut plugin remove [target] [options]
14
-
15
- Remove the installed Gamut plugin from an AI tool.
16
-
17
- Arguments:
18
- target Tool to remove from (default: cursor)
19
- cursor | claude
20
-
21
- Options:
22
- --plugin-dir <path> Override the bundled agent-tools directory
23
- -h, --help Show this help message
24
-
25
- Examples:
26
- gamut plugin remove
27
- gamut plugin remove claude
28
- `);
29
- }
30
-
31
- // ---------------------------------------------------------------------------
32
-
33
- /** @param {string} sourceRoot */
34
- async function removeCursor(sourceRoot) {
35
- const dest = await cursorDestPath(sourceRoot);
36
- const st = await stat(dest).catch(() => null);
37
-
38
- if (!st) {
39
- log(`Cursor: nothing to remove — ${dest} does not exist.`);
40
- return;
41
- }
42
-
43
- await rm(dest, { recursive: true, force: true });
44
- log(`Cursor: removed ${dest}`);
45
- }
46
-
47
- /** @param {string} sourceRoot */
48
- async function removeClaude(sourceRoot) {
49
- const spec = await claudePluginSpec(sourceRoot);
50
- const mpName = marketplaceName(spec);
51
- const pluginName = spec.split('@')[0];
52
-
53
- let code = await runCommand('claude', [
54
- 'plugin',
55
- 'remove',
56
- pluginName,
57
- '--scope',
58
- 'user',
59
- ]);
60
- if (code !== 0) {
61
- warn(
62
- `warning: "claude plugin remove" exited ${code} — the plugin may not have been installed.`
63
- );
64
- } else {
65
- log(`Claude Code: removed plugin "${pluginName}"`);
66
- }
67
-
68
- code = await runCommand('claude', [
69
- 'plugin',
70
- 'marketplace',
71
- 'remove',
72
- mpName,
73
- ]);
74
- if (code !== 0) {
75
- warn(
76
- `warning: "claude plugin marketplace remove" exited ${code} — ` +
77
- `the marketplace entry may not exist or the command syntax may differ. ` +
78
- `Run "claude plugin marketplace list" to check.`
79
- );
80
- } else {
81
- log(`Claude Code: removed marketplace "${mpName}"`);
82
- }
83
- }
84
-
85
- // ---------------------------------------------------------------------------
86
-
87
- /**
88
- * gamut plugin remove [cursor|claude] [--plugin-dir <path>]
89
- *
90
- * @param {string[]} args
91
- */
92
- export default async function remove(args) {
93
- const target = args.find((a) => !a.startsWith('-')) ?? 'cursor';
94
-
95
- if (!TARGETS.includes(target)) {
96
- throw new Error(
97
- `Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`
98
- );
99
- }
100
-
101
- const pluginDir = await resolvePluginDir(args);
102
-
103
- if (target === 'cursor') {
104
- await removeCursor(pluginDir);
105
- } else if (target === 'claude') {
106
- await removeClaude(pluginDir);
107
- }
108
- }
@@ -1,59 +0,0 @@
1
- import { log } from '../../lib/io.mjs';
2
- import { getFlag } from '../../lib/resolve-plugin-dir.mjs';
3
- import install, { TARGETS } from './install.mjs';
4
-
5
- export function help() {
6
- log(`
7
- Usage:
8
- gamut plugin update [target] [options]
9
-
10
- Update the Gamut plugin in an AI or design tool.
11
- Equivalent to re-running install — replaces the existing installation in place.
12
-
13
- Arguments:
14
- target Tool to update (default: cursor)
15
- cursor | claude
16
-
17
- Options:
18
- --scope <scope> Content to update (default: all)
19
- all | skills | rules | commands | agents
20
- --theme <theme> Refresh ./DESIGN.md (same themes as install)
21
- --force Overwrite existing DESIGN.md when using --theme
22
- --plugin-dir <path> Override the bundled agent-tools directory
23
- -h, --help Show this help message
24
-
25
- Examples:
26
- gamut plugin update
27
- gamut plugin update claude
28
- gamut plugin update cursor --theme core --force
29
- gamut plugin update cursor --scope skills
30
- `);
31
- }
32
-
33
- /**
34
- * gamut plugin update [cursor|claude] [--scope all|skills|rules|commands|agents]
35
- * [--plugin-dir <path>]
36
- *
37
- * Re-runs install with the same arguments. For Cursor this does an in-place
38
- * copy replacing any existing installation. For Claude Code it updates the
39
- * marketplace entry and re-installs.
40
- *
41
- * @param {string[]} args
42
- */
43
- export default async function update(args) {
44
- const target = args.find((a) => !a.startsWith('-')) ?? 'cursor';
45
- const scope = getFlag(args, '--scope', 'all') ?? 'all';
46
-
47
- if (!TARGETS.includes(target)) {
48
- throw new Error(
49
- `Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`
50
- );
51
- }
52
-
53
- log(
54
- `Updating Gamut plugin for ${target}${
55
- scope !== 'all' ? ` (scope: ${scope})` : ''
56
- }…`
57
- );
58
- await install(args);
59
- }
package/bin/gamut.mjs DELETED
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { error, log } from './lib/io.mjs';
4
-
5
- /**
6
- * Gamut CLI
7
- *
8
- * Usage:
9
- * gamut plugin install [cursor|claude] [--scope all|skills|rules|commands|agents]
10
- * gamut plugin remove [cursor|claude]
11
- * gamut plugin update [cursor|claude] [--scope all|skills|rules|commands|agents]
12
- * gamut plugin list
13
- */
14
-
15
- const args = process.argv.slice(2);
16
- const [noun, verb, ...rest] = args;
17
-
18
- if (!noun || noun === '--help' || noun === '-h') {
19
- printHelp();
20
- process.exit(noun ? 0 : 1);
21
- }
22
-
23
- if (noun !== 'plugin') {
24
- error(`Unknown command: "${noun}"`);
25
- printHelp();
26
- process.exit(1);
27
- }
28
-
29
- if (!verb || verb === '--help' || verb === '-h') {
30
- printPluginHelp();
31
- process.exit(verb ? 0 : 1);
32
- }
33
-
34
- let cmd;
35
- try {
36
- cmd = await import(`./commands/plugin/${verb}.mjs`);
37
- } catch {
38
- error(`Unknown plugin subcommand: "${verb}"`);
39
- printPluginHelp();
40
- process.exit(1);
41
- }
42
-
43
- if (rest.includes('--help') || rest.includes('-h')) {
44
- cmd.help();
45
- process.exit(0);
46
- }
47
-
48
- try {
49
- await cmd.default(rest);
50
- } catch (/** @type {any} */ err) {
51
- error(`Error: ${err.message}`);
52
- process.exit(1);
53
- }
54
-
55
- // ---------------------------------------------------------------------------
56
- // Help
57
- // ---------------------------------------------------------------------------
58
-
59
- function printHelp() {
60
- log(`
61
- gamut — Gamut design system CLI
62
-
63
- Usage:
64
- gamut <command> [subcommand] [options]
65
-
66
- Commands:
67
- plugin Manage the Gamut plugin in your AI/design tools
68
-
69
- Run "gamut plugin --help" for plugin subcommands.
70
- `);
71
- }
72
-
73
- function printPluginHelp() {
74
- log(`
75
- gamut plugin — Manage the Gamut plugin
76
-
77
- Subcommands:
78
- install [target] [--scope <scope>] [--theme <theme>] Install the plugin (+ optional DESIGN.md)
79
- remove [target] Remove an installed plugin
80
- update [target] [--scope <scope>] [--theme <theme>] Update an installed plugin
81
- list Show installation status for all targets
82
-
83
- Targets: cursor (default) | claude
84
- Scopes: all (default) | skills | rules | commands | agents
85
- Themes: core | admin | platform | percipio | lxstudio (--theme copies DESIGN.md to repo root)
86
-
87
- Examples:
88
- gamut plugin install
89
- gamut plugin install claude
90
- gamut plugin install cursor --theme percipio
91
- gamut plugin install cursor --scope skills
92
- gamut plugin remove claude
93
- gamut plugin update
94
- gamut plugin list
95
- `);
96
- }
@@ -1,52 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
-
4
- /**
5
- * Reads .claude-plugin/marketplace.json and returns a "name@marketplace" plugin spec.
6
- *
7
- * @param {string} sourceRoot
8
- * @returns {Promise<string>}
9
- */
10
- export async function claudePluginSpec(sourceRoot) {
11
- const mp = join(sourceRoot, '.claude-plugin', 'marketplace.json');
12
- let text;
13
- try {
14
- text = await readFile(mp, 'utf8');
15
- } catch {
16
- throw new Error(
17
- `Missing ${mp}.\n` +
18
- `A .claude-plugin/marketplace.json is required for Claude Code installation.`,
19
- );
20
- }
21
-
22
- const json =
23
- /** @type {{ name?: string; plugins?: Array<{ name?: string; source?: string }> }} */ (
24
- JSON.parse(text)
25
- );
26
- const { name: marketplaceName, plugins } = json;
27
-
28
- if (!marketplaceName || !Array.isArray(plugins) || plugins.length === 0) {
29
- throw new Error(`Invalid marketplace.json — needs "name" and "plugins[]": ${mp}`);
30
- }
31
-
32
- const entry =
33
- plugins.find((p) => p.source === './' || p.source === '.' || p.source == null) ?? plugins[0];
34
-
35
- if (!entry?.name) {
36
- throw new Error(`No plugin name found in marketplace.json plugins[]: ${mp}`);
37
- }
38
-
39
- return `${entry.name}@${marketplaceName}`;
40
- }
41
-
42
- /**
43
- * Returns just the marketplace name portion of a plugin spec ("name@marketplace").
44
- *
45
- * @param {string} spec
46
- * @returns {string}
47
- */
48
- export function marketplaceName(spec) {
49
- const name = spec.split('@')[1];
50
- if (!name) throw new Error(`Could not parse marketplace name from plugin spec: ${spec}`);
51
- return name;
52
- }
@@ -1,40 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- /** @returns {string} */
6
- export function cursorPluginsRoot() {
7
- return process.env.CURSOR_PLUGINS_LOCAL ?? join(homedir(), '.cursor', 'plugins', 'local');
8
- }
9
-
10
- /**
11
- * Reads the .cursor-plugin/plugin.json manifest and derives a folder name.
12
- * Falls back to "gamut-agent-tools" if no manifest is found.
13
- *
14
- * @param {string} sourceRoot
15
- * @returns {Promise<string>}
16
- */
17
- export async function cursorFolderName(sourceRoot) {
18
- const manifest = join(sourceRoot, '.cursor-plugin', 'plugin.json');
19
- try {
20
- const text = await readFile(manifest, 'utf8');
21
- const json = /** @type {{ name?: string }} */ (JSON.parse(text));
22
- if (json.name && typeof json.name === 'string') {
23
- return json.name.replace(/^@/, '').replace(/\//g, '-');
24
- }
25
- } catch {
26
- // no manifest — use default
27
- }
28
- return 'gamut-agent-tools';
29
- }
30
-
31
- /**
32
- * Returns the absolute path where the plugin is/should be installed for Cursor.
33
- *
34
- * @param {string} sourceRoot
35
- * @returns {Promise<string>}
36
- */
37
- export async function cursorDestPath(sourceRoot) {
38
- const folderName = await cursorFolderName(sourceRoot);
39
- return join(cursorPluginsRoot(), folderName);
40
- }