@codecademy/gamut 68.6.1-alpha.edab62.0 → 68.6.1-alpha.f6b2ce.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.
Files changed (46) hide show
  1. package/agent-tools/.cursor-plugin/plugin.json +1 -1
  2. package/agent-tools/DESIGN.Codecademy.md +466 -413
  3. package/agent-tools/DESIGN.LXStudio.md +350 -282
  4. package/agent-tools/DESIGN.Percipio.md +357 -279
  5. package/agent-tools/commands/gamut-review.md +176 -87
  6. package/agent-tools/guidelines/components/animations.md +74 -0
  7. package/agent-tools/guidelines/components/buttons.md +74 -23
  8. package/agent-tools/guidelines/components/card.md +19 -0
  9. package/agent-tools/guidelines/components/coachmark.md +21 -0
  10. package/agent-tools/guidelines/components/data-table.md +79 -0
  11. package/agent-tools/guidelines/components/forms.md +106 -0
  12. package/agent-tools/guidelines/components/loading-states.md +17 -0
  13. package/agent-tools/guidelines/components/menu.md +58 -0
  14. package/agent-tools/guidelines/components/overview.md +97 -17
  15. package/agent-tools/guidelines/components/radial-progress.md +13 -0
  16. package/agent-tools/guidelines/components/select.md +23 -0
  17. package/agent-tools/guidelines/components/tooltips.md +22 -0
  18. package/agent-tools/guidelines/components/video.md +29 -0
  19. package/agent-tools/guidelines/foundations/color.md +140 -58
  20. package/agent-tools/guidelines/foundations/modes.md +41 -17
  21. package/agent-tools/guidelines/foundations/spacing.md +78 -37
  22. package/agent-tools/guidelines/foundations/typography.md +69 -37
  23. package/agent-tools/guidelines/overview-icons.md +19 -0
  24. package/agent-tools/guidelines/overview-illustrations.md +7 -0
  25. package/agent-tools/guidelines/overview-patterns.md +7 -0
  26. package/agent-tools/guidelines/overview.md +71 -22
  27. package/agent-tools/guidelines/setup.md +59 -18
  28. package/agent-tools/rules/accessibility.mdc +22 -13
  29. package/agent-tools/skills/gamut-accessibility/SKILL.md +97 -112
  30. package/agent-tools/skills/gamut-color-mode/SKILL.md +91 -41
  31. package/agent-tools/skills/gamut-components/SKILL.md +46 -0
  32. package/agent-tools/skills/gamut-forms/SKILL.md +101 -0
  33. package/agent-tools/skills/gamut-style-utilities/SKILL.md +111 -0
  34. package/agent-tools/skills/gamut-system-props/SKILL.md +81 -29
  35. package/agent-tools/skills/gamut-testing/SKILL.md +106 -62
  36. package/agent-tools/skills/gamut-theming/SKILL.md +36 -86
  37. package/agent-tools/skills/gamut-typography/SKILL.md +36 -80
  38. package/bin/commands/plugin/install.mjs +96 -56
  39. package/bin/commands/plugin/list.mjs +11 -43
  40. package/bin/commands/plugin/remove.mjs +30 -38
  41. package/bin/commands/plugin/update.mjs +15 -5
  42. package/bin/gamut.mjs +17 -13
  43. package/bin/lib/design.mjs +71 -0
  44. package/bin/lib/io.mjs +14 -0
  45. package/package.json +6 -6
  46. package/bin/lib/figma.mjs +0 -49
@@ -1,39 +1,46 @@
1
1
  import { cp, mkdir, readdir, rm, symlink } from 'node:fs/promises';
2
- import { join, resolve } from 'node:path';
2
+ import { resolve } from 'node:path';
3
3
 
4
4
  import { claudePluginSpec, marketplaceName } from '../../lib/claude.mjs';
5
5
  import { cursorDestPath } from '../../lib/cursor.mjs';
6
- import { resolveFigmaOutput } from '../../lib/figma.mjs';
6
+ import {
7
+ installDesignMd,
8
+ listCanonicalThemes,
9
+ resolveTheme,
10
+ } from '../../lib/design.mjs';
11
+ import { log, warn } from '../../lib/io.mjs';
7
12
  import { getFlag, resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
8
13
  import { runCommand } from '../../lib/run-command.mjs';
9
14
 
10
- export const TARGETS = ['cursor', 'claude', 'figma'];
15
+ export const TARGETS = ['cursor', 'claude'];
11
16
  export const SCOPES = ['all', 'skills', 'rules', 'commands', 'agents'];
12
17
 
13
18
  export function help() {
14
- console.log(`
19
+ log(`
15
20
  Usage:
16
21
  gamut plugin install [target] [options]
17
22
 
18
- Install the Gamut plugin into an AI or design tool.
23
+ Install the Gamut plugin into an AI tool.
19
24
 
20
25
  Arguments:
21
26
  target Tool to install into (default: cursor)
22
- cursor | claude | figma
27
+ cursor | claude
23
28
 
24
29
  Options:
25
30
  --scope <scope> Content to install (default: all)
26
31
  all | skills | rules | commands | agents
27
- --output <path> [figma] Explicit destination directory for guidelines/.
28
- If omitted, walks up from cwd to find figma.config.json.
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
29
36
  --plugin-dir <path> Override the bundled agent-tools directory
30
37
  -h, --help Show this help message
31
38
 
32
39
  Examples:
33
40
  gamut plugin install
34
41
  gamut plugin install claude
35
- gamut plugin install figma
36
- gamut plugin install figma --output /path/to/project/guidelines
42
+ gamut plugin install cursor --theme core
43
+ gamut plugin install cursor --theme percipio --force
37
44
  gamut plugin install cursor --scope skills
38
45
  gamut plugin install cursor --plugin-dir ./my-agent-tools
39
46
  `);
@@ -44,7 +51,6 @@ Examples:
44
51
  /** Directories in the plugin source that should not be installed to Cursor. */
45
52
  const CURSOR_IGNORE = new Set([
46
53
  '.claude-plugin', // Claude Code manifest — not a Cursor concept
47
- 'guidelines', // Figma Make only
48
54
  ]);
49
55
 
50
56
  /** @param {string} sourceRoot @param {string} scope */
@@ -55,7 +61,7 @@ async function installCursor(sourceRoot, scope) {
55
61
  // Symlink the whole plugin dir (dev convenience)
56
62
  await rm(dest, { recursive: true, force: true });
57
63
  await symlink(resolve(sourceRoot), dest, 'dir');
58
- console.log(`Cursor: symlinked to ${dest}`);
64
+ log(`Cursor: symlinked to ${dest}`);
59
65
  return;
60
66
  }
61
67
 
@@ -63,31 +69,42 @@ async function installCursor(sourceRoot, scope) {
63
69
  await rm(dest, { recursive: true, force: true });
64
70
  await mkdir(dest, { recursive: true });
65
71
 
66
- await cp(`${sourceRoot}/.cursor-plugin`, `${dest}/.cursor-plugin`, { recursive: true });
72
+ await cp(`${sourceRoot}/.cursor-plugin`, `${dest}/.cursor-plugin`, {
73
+ recursive: true,
74
+ });
67
75
 
68
76
  let dirs;
69
77
  if (scope === 'all') {
70
78
  const entries = await readdir(sourceRoot, { withFileTypes: true });
71
79
  dirs = entries
72
- .filter((e) => e.isDirectory() && !e.name.startsWith('.') && !CURSOR_IGNORE.has(e.name))
80
+ .filter(
81
+ (e) =>
82
+ e.isDirectory() &&
83
+ !e.name.startsWith('.') &&
84
+ !CURSOR_IGNORE.has(e.name)
85
+ )
73
86
  .map((e) => e.name);
74
87
  } else {
75
88
  dirs = [scope];
76
89
  }
77
90
 
78
- for (const dir of dirs) {
79
- await cp(`${sourceRoot}/${dir}`, `${dest}/${dir}`, { recursive: true }).catch(() => {
80
- // directory may be empty/missing — not an error
81
- });
82
- }
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
+ );
83
100
 
84
101
  const scopeLabel = scope === 'all' ? 'all content' : scope;
85
- console.log(`Cursor: installed (${scopeLabel}) → ${dest}`);
102
+ log(`Cursor: installed (${scopeLabel}) → ${dest}`);
86
103
  }
87
104
 
88
105
  // Claude Code only loads from recognized plugin directories: skills/, commands/, agents/.
89
- // rules/ is Cursor-specific (.mdc format); .cursor-plugin/ and guidelines/ are also
90
- // present in sourceRoot but ignored by Claude Code.
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.
91
108
 
92
109
  /** @param {string} sourceRoot */
93
110
  async function installClaude(sourceRoot) {
@@ -95,56 +112,58 @@ async function installClaude(sourceRoot) {
95
112
  const mpName = marketplaceName(spec);
96
113
  const root = resolve(sourceRoot);
97
114
 
98
- let code = await runCommand('claude', ['plugin', 'marketplace', 'add', root, '--scope', 'user']);
115
+ let code = await runCommand('claude', [
116
+ 'plugin',
117
+ 'marketplace',
118
+ 'add',
119
+ root,
120
+ '--scope',
121
+ 'user',
122
+ ]);
99
123
  if (code !== 0) {
100
- console.warn(
124
+ warn(
101
125
  `warning: "claude plugin marketplace add" exited ${code} — ` +
102
- `if it's already registered this is safe to ignore.`,
126
+ `if it's already registered this is safe to ignore.`
103
127
  );
104
- code = await runCommand('claude', ['plugin', 'marketplace', 'update', mpName]);
128
+ code = await runCommand('claude', [
129
+ 'plugin',
130
+ 'marketplace',
131
+ 'update',
132
+ mpName,
133
+ ]);
105
134
  if (code !== 0) {
106
135
  throw new Error(
107
136
  `claude plugin marketplace add/update failed (exit ${code}).\n` +
108
- `Try manually: claude plugin marketplace add ${root}`,
137
+ `Try manually: claude plugin marketplace add ${root}`
109
138
  );
110
139
  }
111
140
  }
112
141
 
113
- code = await runCommand('claude', ['plugin', 'install', spec, '--scope', 'user']);
142
+ code = await runCommand('claude', [
143
+ 'plugin',
144
+ 'install',
145
+ spec,
146
+ '--scope',
147
+ 'user',
148
+ ]);
114
149
  if (code !== 0) {
115
150
  throw new Error(
116
151
  `claude plugin install failed (exit ${code}).\n` +
117
- `Try manually: claude plugin install ${spec} --scope user`,
152
+ `Try manually: claude plugin install ${spec} --scope user`
118
153
  );
119
154
  }
120
155
 
121
- console.log(`Claude Code: installed ${spec} (user scope)`);
122
- console.log(` Tip: run /reload-plugins in Claude Code if skills don't appear immediately.`);
123
- console.log(` One-off without install: claude --plugin-dir ${root}`);
124
- }
125
-
126
- /**
127
- * @param {string} sourceRoot
128
- * @param {string | undefined} outputArg
129
- */
130
- async function installFigma(sourceRoot, outputArg) {
131
- const src = join(sourceRoot, 'guidelines');
132
- const { path: dest, discovered } = await resolveFigmaOutput(outputArg);
133
-
134
- if (discovered) {
135
- console.log(`Figma: found figma.config.json — installing to ${dest}`);
136
- }
137
-
138
- await rm(dest, { recursive: true, force: true });
139
- await cp(src, dest, { recursive: true });
140
- console.log(`Figma: installed guidelines/ → ${dest}`);
141
- console.log(` In Figma Make, point your kit at this guidelines/ directory for design system context.`);
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}`);
142
161
  }
143
162
 
144
163
  // ---------------------------------------------------------------------------
145
164
 
146
165
  /**
147
- * gamut plugin install [cursor|claude|figma] [--scope all|skills|rules|commands|agents]
166
+ * gamut plugin install [cursor|claude] [--scope all|skills|rules|commands|agents]
148
167
  * [--plugin-dir <path>]
149
168
  *
150
169
  * @param {string[]} args
@@ -154,20 +173,41 @@ export default async function install(args) {
154
173
  const scope = getFlag(args, '--scope', 'all') ?? 'all';
155
174
 
156
175
  if (!TARGETS.includes(target)) {
157
- throw new Error(`Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`);
176
+ throw new Error(
177
+ `Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`
178
+ );
158
179
  }
159
180
  if (!SCOPES.includes(scope)) {
160
- throw new Error(`Unknown scope: "${scope}". Choose from: ${SCOPES.join(', ')}`);
181
+ throw new Error(
182
+ `Unknown scope: "${scope}". Choose from: ${SCOPES.join(', ')}`
183
+ );
161
184
  }
162
185
 
163
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
+ }
164
193
 
165
194
  if (target === 'cursor') {
166
195
  await installCursor(pluginDir, scope);
167
196
  } else if (target === 'claude') {
168
197
  await installClaude(pluginDir);
169
- } else if (target === 'figma') {
170
- const output = getFlag(args, '--output', undefined);
171
- await installFigma(pluginDir, output);
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}`);
172
210
  }
173
211
  }
212
+
213
+ export { listCanonicalThemes };
@@ -1,25 +1,22 @@
1
1
  import { stat } from 'node:fs/promises';
2
2
 
3
3
  import { cursorDestPath } from '../../lib/cursor.mjs';
4
- import { findFigmaConfigDir } from '../../lib/figma.mjs';
5
- import { getFlag, resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
4
+ import { log } from '../../lib/io.mjs';
5
+ import { resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
6
6
 
7
7
  export function help() {
8
- console.log(`
8
+ log(`
9
9
  Usage:
10
10
  gamut plugin list [options]
11
11
 
12
12
  Show installation status for all supported targets.
13
13
 
14
14
  Options:
15
- --output <path> [figma] Explicit path to DESIGN.md.
16
- If omitted, walks up from cwd to find figma.config.json.
17
15
  --plugin-dir <path> Override the bundled agent-tools directory
18
16
  -h, --help Show this help message
19
17
 
20
18
  Examples:
21
19
  gamut plugin list
22
- gamut plugin list --output ./docs/DESIGN.md
23
20
  `);
24
21
  }
25
22
 
@@ -45,32 +42,6 @@ async function claudeStatus() {
45
42
  };
46
43
  }
47
44
 
48
- /** @param {string | undefined} outputArg */
49
- async function figmaStatus(outputArg) {
50
- let dest;
51
- if (outputArg) {
52
- dest = outputArg;
53
- } else {
54
- const dir = await findFigmaConfigDir(process.cwd());
55
- dest = dir ? `${dir}/DESIGN.md` : null;
56
- }
57
-
58
- if (!dest) {
59
- return {
60
- target: 'figma',
61
- status: '? unknown',
62
- notes: 'figma.config.json not found — run from your project root or use --output',
63
- };
64
- }
65
-
66
- const installed = !!(await stat(dest).catch(() => null));
67
- return {
68
- target: 'figma',
69
- status: installed ? '✓ installed' : '✗ not installed',
70
- notes: installed ? dest : `run: gamut plugin install figma`,
71
- };
72
- }
73
-
74
45
  // ---------------------------------------------------------------------------
75
46
 
76
47
  /**
@@ -82,24 +53,21 @@ async function figmaStatus(outputArg) {
82
53
  */
83
54
  export default async function list(args) {
84
55
  const pluginDir = await resolvePluginDir(args);
85
- const output = getFlag(args, '--output', undefined);
86
56
 
87
- const rows = await Promise.all([
88
- cursorStatus(pluginDir),
89
- claudeStatus(),
90
- figmaStatus(output),
91
- ]);
57
+ const rows = await Promise.all([cursorStatus(pluginDir), claudeStatus()]);
92
58
 
93
59
  const col0 = Math.max(...rows.map((r) => r.target.length));
94
60
  const col1 = Math.max(...rows.map((r) => r.status.length));
95
61
 
96
- const header = `${'Target'.padEnd(col0)} ${'Status'.padEnd(col1)} Path / Notes`;
62
+ const header = `${'Target'.padEnd(col0)} ${'Status'.padEnd(
63
+ col1
64
+ )} Path / Notes`;
97
65
  const rule = '─'.repeat(header.length);
98
66
 
99
- console.log(`\n${header}`);
100
- console.log(rule);
67
+ log(`\n${header}`);
68
+ log(rule);
101
69
  for (const row of rows) {
102
- console.log(`${row.target.padEnd(col0)} ${row.status.padEnd(col1)} ${row.notes}`);
70
+ log(`${row.target.padEnd(col0)} ${row.status.padEnd(col1)} ${row.notes}`);
103
71
  }
104
- console.log();
72
+ log('');
105
73
  }
@@ -2,33 +2,29 @@ import { rm, stat } from 'node:fs/promises';
2
2
 
3
3
  import { claudePluginSpec, marketplaceName } from '../../lib/claude.mjs';
4
4
  import { cursorDestPath } from '../../lib/cursor.mjs';
5
- import { resolveFigmaOutput } from '../../lib/figma.mjs';
6
- import { getFlag, resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
5
+ import { log, warn } from '../../lib/io.mjs';
6
+ import { resolvePluginDir } from '../../lib/resolve-plugin-dir.mjs';
7
7
  import { runCommand } from '../../lib/run-command.mjs';
8
8
  import { TARGETS } from './install.mjs';
9
9
 
10
10
  export function help() {
11
- console.log(`
11
+ log(`
12
12
  Usage:
13
13
  gamut plugin remove [target] [options]
14
14
 
15
- Remove the installed Gamut plugin from an AI or design tool.
15
+ Remove the installed Gamut plugin from an AI tool.
16
16
 
17
17
  Arguments:
18
18
  target Tool to remove from (default: cursor)
19
- cursor | claude | figma
19
+ cursor | claude
20
20
 
21
21
  Options:
22
- --output <path> [figma] Path to the DESIGN.md that was installed.
23
- If omitted, walks up from cwd to find figma.config.json.
24
22
  --plugin-dir <path> Override the bundled agent-tools directory
25
23
  -h, --help Show this help message
26
24
 
27
25
  Examples:
28
26
  gamut plugin remove
29
27
  gamut plugin remove claude
30
- gamut plugin remove figma
31
- gamut plugin remove figma --output ./docs/DESIGN.md
32
28
  `);
33
29
  }
34
30
 
@@ -40,12 +36,12 @@ async function removeCursor(sourceRoot) {
40
36
  const st = await stat(dest).catch(() => null);
41
37
 
42
38
  if (!st) {
43
- console.log(`Cursor: nothing to remove — ${dest} does not exist.`);
39
+ log(`Cursor: nothing to remove — ${dest} does not exist.`);
44
40
  return;
45
41
  }
46
42
 
47
43
  await rm(dest, { recursive: true, force: true });
48
- console.log(`Cursor: removed ${dest}`);
44
+ log(`Cursor: removed ${dest}`);
49
45
  }
50
46
 
51
47
  /** @param {string} sourceRoot */
@@ -54,45 +50,42 @@ async function removeClaude(sourceRoot) {
54
50
  const mpName = marketplaceName(spec);
55
51
  const pluginName = spec.split('@')[0];
56
52
 
57
- let code = await runCommand('claude', ['plugin', 'remove', pluginName, '--scope', 'user']);
53
+ let code = await runCommand('claude', [
54
+ 'plugin',
55
+ 'remove',
56
+ pluginName,
57
+ '--scope',
58
+ 'user',
59
+ ]);
58
60
  if (code !== 0) {
59
- console.warn(
60
- `warning: "claude plugin remove" exited ${code} — the plugin may not have been installed.`,
61
+ warn(
62
+ `warning: "claude plugin remove" exited ${code} — the plugin may not have been installed.`
61
63
  );
62
64
  } else {
63
- console.log(`Claude Code: removed plugin "${pluginName}"`);
65
+ log(`Claude Code: removed plugin "${pluginName}"`);
64
66
  }
65
67
 
66
- code = await runCommand('claude', ['plugin', 'marketplace', 'remove', mpName]);
68
+ code = await runCommand('claude', [
69
+ 'plugin',
70
+ 'marketplace',
71
+ 'remove',
72
+ mpName,
73
+ ]);
67
74
  if (code !== 0) {
68
- console.warn(
75
+ warn(
69
76
  `warning: "claude plugin marketplace remove" exited ${code} — ` +
70
77
  `the marketplace entry may not exist or the command syntax may differ. ` +
71
- `Run "claude plugin marketplace list" to check.`,
78
+ `Run "claude plugin marketplace list" to check.`
72
79
  );
73
80
  } else {
74
- console.log(`Claude Code: removed marketplace "${mpName}"`);
81
+ log(`Claude Code: removed marketplace "${mpName}"`);
75
82
  }
76
83
  }
77
84
 
78
- /** @param {string | undefined} outputArg */
79
- async function removeFigma(outputArg) {
80
- const { path: dest } = await resolveFigmaOutput(outputArg);
81
- const st = await stat(dest).catch(() => null);
82
-
83
- if (!st) {
84
- console.log(`Figma: nothing to remove — ${dest} does not exist.`);
85
- return;
86
- }
87
-
88
- await rm(dest, { force: true });
89
- console.log(`Figma: removed ${dest}`);
90
- }
91
-
92
85
  // ---------------------------------------------------------------------------
93
86
 
94
87
  /**
95
- * gamut plugin remove [cursor|claude|figma] [--plugin-dir <path>]
88
+ * gamut plugin remove [cursor|claude] [--plugin-dir <path>]
96
89
  *
97
90
  * @param {string[]} args
98
91
  */
@@ -100,7 +93,9 @@ export default async function remove(args) {
100
93
  const target = args.find((a) => !a.startsWith('-')) ?? 'cursor';
101
94
 
102
95
  if (!TARGETS.includes(target)) {
103
- throw new Error(`Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`);
96
+ throw new Error(
97
+ `Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`
98
+ );
104
99
  }
105
100
 
106
101
  const pluginDir = await resolvePluginDir(args);
@@ -109,8 +104,5 @@ export default async function remove(args) {
109
104
  await removeCursor(pluginDir);
110
105
  } else if (target === 'claude') {
111
106
  await removeClaude(pluginDir);
112
- } else if (target === 'figma') {
113
- const output = getFlag(args, '--output', undefined);
114
- await removeFigma(output);
115
107
  }
116
108
  }
@@ -1,8 +1,9 @@
1
+ import { log } from '../../lib/io.mjs';
1
2
  import { getFlag } from '../../lib/resolve-plugin-dir.mjs';
2
3
  import install, { TARGETS } from './install.mjs';
3
4
 
4
5
  export function help() {
5
- console.log(`
6
+ log(`
6
7
  Usage:
7
8
  gamut plugin update [target] [options]
8
9
 
@@ -11,23 +12,26 @@ Equivalent to re-running install — replaces the existing installation in place
11
12
 
12
13
  Arguments:
13
14
  target Tool to update (default: cursor)
14
- cursor | claude | figma
15
+ cursor | claude
15
16
 
16
17
  Options:
17
18
  --scope <scope> Content to update (default: all)
18
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
19
22
  --plugin-dir <path> Override the bundled agent-tools directory
20
23
  -h, --help Show this help message
21
24
 
22
25
  Examples:
23
26
  gamut plugin update
24
27
  gamut plugin update claude
28
+ gamut plugin update cursor --theme core --force
25
29
  gamut plugin update cursor --scope skills
26
30
  `);
27
31
  }
28
32
 
29
33
  /**
30
- * gamut plugin update [cursor|claude|figma] [--scope all|skills|rules|commands|agents]
34
+ * gamut plugin update [cursor|claude] [--scope all|skills|rules|commands|agents]
31
35
  * [--plugin-dir <path>]
32
36
  *
33
37
  * Re-runs install with the same arguments. For Cursor this does an in-place
@@ -41,9 +45,15 @@ export default async function update(args) {
41
45
  const scope = getFlag(args, '--scope', 'all') ?? 'all';
42
46
 
43
47
  if (!TARGETS.includes(target)) {
44
- throw new Error(`Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`);
48
+ throw new Error(
49
+ `Unknown target: "${target}". Choose from: ${TARGETS.join(', ')}`
50
+ );
45
51
  }
46
52
 
47
- console.log(`Updating Gamut plugin for ${target}${scope !== 'all' ? ` (scope: ${scope})` : ''}…`);
53
+ log(
54
+ `Updating Gamut plugin for ${target}${
55
+ scope !== 'all' ? ` (scope: ${scope})` : ''
56
+ }…`
57
+ );
48
58
  await install(args);
49
59
  }
package/bin/gamut.mjs CHANGED
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { error, log } from './lib/io.mjs';
4
+
3
5
  /**
4
6
  * Gamut CLI
5
7
  *
6
8
  * Usage:
7
- * gamut plugin install [cursor|claude|figma] [--scope all|skills|rules|commands|agents]
8
- * gamut plugin remove [cursor|claude|figma]
9
- * gamut plugin update [cursor|claude|figma] [--scope all|skills|rules|commands|agents]
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]
10
12
  * gamut plugin list
11
13
  */
12
14
 
@@ -19,7 +21,7 @@ if (!noun || noun === '--help' || noun === '-h') {
19
21
  }
20
22
 
21
23
  if (noun !== 'plugin') {
22
- console.error(`Unknown command: "${noun}"`);
24
+ error(`Unknown command: "${noun}"`);
23
25
  printHelp();
24
26
  process.exit(1);
25
27
  }
@@ -33,7 +35,7 @@ let cmd;
33
35
  try {
34
36
  cmd = await import(`./commands/plugin/${verb}.mjs`);
35
37
  } catch {
36
- console.error(`Unknown plugin subcommand: "${verb}"`);
38
+ error(`Unknown plugin subcommand: "${verb}"`);
37
39
  printPluginHelp();
38
40
  process.exit(1);
39
41
  }
@@ -46,7 +48,7 @@ if (rest.includes('--help') || rest.includes('-h')) {
46
48
  try {
47
49
  await cmd.default(rest);
48
50
  } catch (/** @type {any} */ err) {
49
- console.error(`Error: ${err.message}`);
51
+ error(`Error: ${err.message}`);
50
52
  process.exit(1);
51
53
  }
52
54
 
@@ -55,7 +57,7 @@ try {
55
57
  // ---------------------------------------------------------------------------
56
58
 
57
59
  function printHelp() {
58
- console.log(`
60
+ log(`
59
61
  gamut — Gamut design system CLI
60
62
 
61
63
  Usage:
@@ -69,21 +71,23 @@ Run "gamut plugin --help" for plugin subcommands.
69
71
  }
70
72
 
71
73
  function printPluginHelp() {
72
- console.log(`
74
+ log(`
73
75
  gamut plugin — Manage the Gamut plugin
74
76
 
75
77
  Subcommands:
76
- install [target] [--scope <scope>] Install the plugin into a tool
77
- remove [target] Remove an installed plugin
78
- update [target] [--scope <scope>] Update an already-installed plugin
79
- list Show installation status for all targets
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
80
82
 
81
- Targets: cursor (default) | claude | figma
83
+ Targets: cursor (default) | claude
82
84
  Scopes: all (default) | skills | rules | commands | agents
85
+ Themes: core | admin | platform | percipio | lxstudio (--theme copies DESIGN.md to repo root)
83
86
 
84
87
  Examples:
85
88
  gamut plugin install
86
89
  gamut plugin install claude
90
+ gamut plugin install cursor --theme percipio
87
91
  gamut plugin install cursor --scope skills
88
92
  gamut plugin remove claude
89
93
  gamut plugin update