@codecademy/gamut
68.6.068.6.1-alpha.5064c8.0
bin/lib/design.mjs+
bin/lib/design.mjsNew file+71
Index: package/bin/lib/design.mjs
===================================================================
--- package/bin/lib/design.mjs
+++ package/bin/lib/design.mjs
@@ -0,0 +1,71 @@
+import { copyFile, stat } from 'node:fs/promises';
+import { join } from 'node:path';
+
+/** @type {Record<string, { sourceFile: string, label: string }>} */
+const THEME_ALIASES = {
+ core: { sourceFile: 'DESIGN.Codecademy.md', label: 'Codecademy (Core)' },
+ codecademy: {
+ sourceFile: 'DESIGN.Codecademy.md',
+ label: 'Codecademy (Core)',
+ },
+ cc: { sourceFile: 'DESIGN.Codecademy.md', label: 'Codecademy (Core)' },
+ admin: { sourceFile: 'DESIGN.Codecademy.md', label: 'Codecademy (Admin)' },
+ platform: {
+ sourceFile: 'DESIGN.Codecademy.md',
+ label: 'Codecademy (Platform)',
+ },
+ percipio: { sourceFile: 'DESIGN.Percipio.md', label: 'Percipio' },
+ lxstudio: { sourceFile: 'DESIGN.LXStudio.md', label: 'LX Studio' },
+ 'lx-studio': { sourceFile: 'DESIGN.LXStudio.md', label: 'LX Studio' },
+};
+
+const CANONICAL_THEMES = ['core', 'admin', 'platform', 'percipio', 'lxstudio'];
+
+/**
+ * @param {string} name
+ * @returns {{ sourceFile: string, label: string, alias: string }}
+ */
+export function resolveTheme(name) {
+ const alias = name.trim().toLowerCase();
+ const entry = THEME_ALIASES[alias];
+ if (!entry) {
+ throw new Error(
+ `Unknown theme: "${name}". Choose from: ${CANONICAL_THEMES.join(', ')} ` +
+ `(aliases: codecademy, cc, lx-studio, …).`
+ );
+ }
+ return { ...entry, alias };
+}
+
+/** @returns {string[]} */
+export function listCanonicalThemes() {
+ return [...CANONICAL_THEMES];
+}
+
+/**
+ * @param {string} sourceRoot agent-tools directory
+ * @param {string} cwd destination directory (app repo root)
+ * @param {string} theme
+ * @param {{ force?: boolean }} [options]
+ * @returns {Promise<{ dest: string, label: string }>}
+ */
+export async function installDesignMd(sourceRoot, cwd, theme, options = {}) {
+ const { sourceFile, label } = resolveTheme(theme);
+ const src = join(sourceRoot, sourceFile);
+ const dest = join(cwd, 'DESIGN.md');
+
+ const srcStat = await stat(src).catch(() => null);
+ if (!srcStat?.isFile()) {
+ throw new Error(`DESIGN source not found: ${src}`);
+ }
+
+ const destStat = await stat(dest).catch(() => null);
+ if (destStat?.isFile() && !options.force) {
+ throw new Error(
+ `DESIGN.md already exists at ${dest}. Use --force to overwrite, or remove it first.`
+ );
+ }
+
+ await copyFile(src, dest);
+ return { dest, label };
+}