@codecademy/gamut 68.6.0 → 68.6.1-alpha.edab62.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/agent-tools/.claude-plugin/marketplace.json +16 -0
- package/agent-tools/.claude-plugin/plugin.json +7 -0
- package/agent-tools/.cursor-plugin/plugin.json +7 -0
- package/agent-tools/DESIGN.Codecademy.md +643 -0
- package/agent-tools/DESIGN.LXStudio.md +444 -0
- package/agent-tools/DESIGN.Percipio.md +435 -0
- package/agent-tools/DESIGN.md +1 -0
- package/agent-tools/agents/.gitkeep +0 -0
- package/agent-tools/commands/gamut-review.md +170 -0
- package/agent-tools/guidelines/components/buttons.md +44 -0
- package/agent-tools/guidelines/components/overview.md +44 -0
- package/agent-tools/guidelines/foundations/color.md +86 -0
- package/agent-tools/guidelines/foundations/modes.md +45 -0
- package/agent-tools/guidelines/foundations/spacing.md +66 -0
- package/agent-tools/guidelines/foundations/typography.md +50 -0
- package/agent-tools/guidelines/overview.md +35 -0
- package/agent-tools/guidelines/setup.md +42 -0
- package/agent-tools/rules/accessibility.mdc +69 -0
- package/agent-tools/skills/gamut-accessibility/SKILL.md +239 -0
- package/agent-tools/skills/gamut-color-mode/SKILL.md +99 -0
- package/agent-tools/skills/gamut-system-props/SKILL.md +173 -0
- package/agent-tools/skills/gamut-testing/SKILL.md +181 -0
- package/agent-tools/skills/gamut-theming/SKILL.md +113 -0
- package/agent-tools/skills/gamut-typography/SKILL.md +123 -0
- package/bin/commands/plugin/install.mjs +173 -0
- package/bin/commands/plugin/list.mjs +105 -0
- package/bin/commands/plugin/remove.mjs +116 -0
- package/bin/commands/plugin/update.mjs +49 -0
- package/bin/gamut.mjs +92 -0
- package/bin/lib/claude.mjs +52 -0
- package/bin/lib/cursor.mjs +40 -0
- package/bin/lib/figma.mjs +49 -0
- package/bin/lib/resolve-plugin-dir.mjs +38 -0
- package/bin/lib/run-command.mjs +22 -0
- package/package.json +11 -8
package/bin/gamut.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gamut CLI
|
|
5
|
+
*
|
|
6
|
+
* 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]
|
|
10
|
+
* gamut plugin list
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const [noun, verb, ...rest] = args;
|
|
15
|
+
|
|
16
|
+
if (!noun || noun === '--help' || noun === '-h') {
|
|
17
|
+
printHelp();
|
|
18
|
+
process.exit(noun ? 0 : 1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (noun !== 'plugin') {
|
|
22
|
+
console.error(`Unknown command: "${noun}"`);
|
|
23
|
+
printHelp();
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!verb || verb === '--help' || verb === '-h') {
|
|
28
|
+
printPluginHelp();
|
|
29
|
+
process.exit(verb ? 0 : 1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let cmd;
|
|
33
|
+
try {
|
|
34
|
+
cmd = await import(`./commands/plugin/${verb}.mjs`);
|
|
35
|
+
} catch {
|
|
36
|
+
console.error(`Unknown plugin subcommand: "${verb}"`);
|
|
37
|
+
printPluginHelp();
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (rest.includes('--help') || rest.includes('-h')) {
|
|
42
|
+
cmd.help();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await cmd.default(rest);
|
|
48
|
+
} catch (/** @type {any} */ err) {
|
|
49
|
+
console.error(`Error: ${err.message}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Help
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
function printHelp() {
|
|
58
|
+
console.log(`
|
|
59
|
+
gamut — Gamut design system CLI
|
|
60
|
+
|
|
61
|
+
Usage:
|
|
62
|
+
gamut <command> [subcommand] [options]
|
|
63
|
+
|
|
64
|
+
Commands:
|
|
65
|
+
plugin Manage the Gamut plugin in your AI/design tools
|
|
66
|
+
|
|
67
|
+
Run "gamut plugin --help" for plugin subcommands.
|
|
68
|
+
`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function printPluginHelp() {
|
|
72
|
+
console.log(`
|
|
73
|
+
gamut plugin — Manage the Gamut plugin
|
|
74
|
+
|
|
75
|
+
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
|
|
80
|
+
|
|
81
|
+
Targets: cursor (default) | claude | figma
|
|
82
|
+
Scopes: all (default) | skills | rules | commands | agents
|
|
83
|
+
|
|
84
|
+
Examples:
|
|
85
|
+
gamut plugin install
|
|
86
|
+
gamut plugin install claude
|
|
87
|
+
gamut plugin install cursor --scope skills
|
|
88
|
+
gamut plugin remove claude
|
|
89
|
+
gamut plugin update
|
|
90
|
+
gamut plugin list
|
|
91
|
+
`);
|
|
92
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Walk up from `startDir` looking for a `figma.config.json` file.
|
|
6
|
+
* Returns the directory containing it, or null if not found before the filesystem root.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} startDir
|
|
9
|
+
* @returns {Promise<string | null>}
|
|
10
|
+
*/
|
|
11
|
+
export async function findFigmaConfigDir(startDir) {
|
|
12
|
+
let dir = startDir;
|
|
13
|
+
while (true) {
|
|
14
|
+
const st = await stat(join(dir, 'figma.config.json')).catch(() => null);
|
|
15
|
+
if (st?.isFile()) return dir;
|
|
16
|
+
const parent = dirname(dir);
|
|
17
|
+
if (parent === dir) return null;
|
|
18
|
+
dir = parent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the destination directory for the guidelines/ folder.
|
|
24
|
+
*
|
|
25
|
+
* Priority:
|
|
26
|
+
* 1. --output <path> if provided (treated as the parent directory)
|
|
27
|
+
* 2. Directory containing the nearest figma.config.json (walking up from cwd)
|
|
28
|
+
*
|
|
29
|
+
* Throws with actionable guidance if neither resolves.
|
|
30
|
+
*
|
|
31
|
+
* @param {string | undefined} outputArg
|
|
32
|
+
* @returns {Promise<{ path: string; discovered: boolean }>}
|
|
33
|
+
*/
|
|
34
|
+
export async function resolveFigmaOutput(outputArg) {
|
|
35
|
+
if (outputArg) {
|
|
36
|
+
return { path: resolve(outputArg), discovered: false };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const dir = await findFigmaConfigDir(process.cwd());
|
|
40
|
+
if (dir) {
|
|
41
|
+
return { path: join(dir, 'guidelines'), discovered: true };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Could not find figma.config.json in ${process.cwd()} or any parent directory.\n` +
|
|
46
|
+
`Provide the destination explicitly with --output:\n` +
|
|
47
|
+
` gamut plugin install figma --output /path/to/your/project/guidelines`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns the absolute path to the bundled agent-tools directory, or the
|
|
9
|
+
* value of --plugin-dir if provided.
|
|
10
|
+
*
|
|
11
|
+
* @param {string[]} args
|
|
12
|
+
* @returns {Promise<string>}
|
|
13
|
+
*/
|
|
14
|
+
export async function resolvePluginDir(args) {
|
|
15
|
+
const override = getFlag(args, '--plugin-dir');
|
|
16
|
+
if (override) {
|
|
17
|
+
const abs = resolve(override);
|
|
18
|
+
const st = await stat(abs).catch(() => null);
|
|
19
|
+
if (!st?.isDirectory()) {
|
|
20
|
+
throw new Error(`--plugin-dir path not found: ${abs}`);
|
|
21
|
+
}
|
|
22
|
+
return abs;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// agent-tools/ is bundled at <package-root>/agent-tools/ relative to bin/lib/
|
|
26
|
+
return resolve(__dirname, '..', '..', 'agent-tools');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string[]} argv
|
|
31
|
+
* @param {string} flag
|
|
32
|
+
* @param {string} [fallback]
|
|
33
|
+
* @returns {string | undefined}
|
|
34
|
+
*/
|
|
35
|
+
export function getFlag(argv, flag, fallback) {
|
|
36
|
+
const idx = argv.indexOf(flag);
|
|
37
|
+
return idx !== -1 ? argv[idx + 1] : fallback;
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Spawns an external command and returns its exit code.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} command
|
|
7
|
+
* @param {string[]} args
|
|
8
|
+
* @returns {Promise<number>}
|
|
9
|
+
*/
|
|
10
|
+
export function runCommand(command, args) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const child = spawn(command, args, { stdio: 'inherit', shell: false });
|
|
13
|
+
child.on('error', (/** @type {NodeJS.ErrnoException} */ err) => {
|
|
14
|
+
if (err.code === 'ENOENT') {
|
|
15
|
+
reject(new Error(`"${command}" not found on PATH. Is it installed?`));
|
|
16
|
+
} else {
|
|
17
|
+
reject(err);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
21
|
+
});
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codecademy/gamut",
|
|
3
3
|
"description": "Styleguide & Component library for Codecademy",
|
|
4
|
-
"version": "68.6.0",
|
|
4
|
+
"version": "68.6.1-alpha.edab62.0",
|
|
5
5
|
"author": "Codecademy Engineering <dev@codecademy.com>",
|
|
6
|
+
"bin": "./bin/gamut.mjs",
|
|
6
7
|
"dependencies": {
|
|
7
|
-
"@codecademy/gamut-icons": "9.57.
|
|
8
|
-
"@codecademy/gamut-illustrations": "0.58.
|
|
9
|
-
"@codecademy/gamut-patterns": "0.10.
|
|
10
|
-
"@codecademy/gamut-styles": "18.0.0",
|
|
11
|
-
"@codecademy/variance": "0.26.
|
|
8
|
+
"@codecademy/gamut-icons": "9.57.6-alpha.edab62.0",
|
|
9
|
+
"@codecademy/gamut-illustrations": "0.58.12-alpha.edab62.0",
|
|
10
|
+
"@codecademy/gamut-patterns": "0.10.31-alpha.edab62.0",
|
|
11
|
+
"@codecademy/gamut-styles": "18.0.1-alpha.edab62.0",
|
|
12
|
+
"@codecademy/variance": "0.26.2-alpha.edab62.0",
|
|
12
13
|
"@formatjs/intl-locale": "5.3.1",
|
|
13
14
|
"@react-aria/interactions": "3.25.0",
|
|
14
15
|
"@types/marked": "^4.0.8",
|
|
@@ -30,7 +31,9 @@
|
|
|
30
31
|
"sanitize-markdown": "^2.6.7"
|
|
31
32
|
},
|
|
32
33
|
"files": [
|
|
33
|
-
"dist"
|
|
34
|
+
"dist",
|
|
35
|
+
"bin",
|
|
36
|
+
"agent-tools"
|
|
34
37
|
],
|
|
35
38
|
"license": "MIT",
|
|
36
39
|
"main": "./dist/index.js",
|
|
@@ -52,7 +55,7 @@
|
|
|
52
55
|
"build": "nx build @codecademy/gamut",
|
|
53
56
|
"build:watch": "yarn build && onchange ./src -- yarn build",
|
|
54
57
|
"compile": "babel ./src --out-dir ./dist --extensions \".ts,.tsx\"",
|
|
55
|
-
"verify": "tsc --noEmit"
|
|
58
|
+
"verify": "tsc --noEmit && tsc --project tsconfig.bin.json"
|
|
56
59
|
},
|
|
57
60
|
"sideEffects": [
|
|
58
61
|
"**/*.css",
|