@roboticela/devkit 1.0.2

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.
@@ -0,0 +1,6 @@
1
+ interface AddOptions {
2
+ variant?: string;
3
+ dryRun?: boolean;
4
+ }
5
+ export declare function addCommand(names: string[], opts: AddOptions, cwd?: string): Promise<void>;
6
+ export {};
@@ -0,0 +1,27 @@
1
+ import ora from "ora";
2
+ import chalk from "chalk";
3
+ import { readConfig, isComponentInstalled } from "../lib/config.js";
4
+ import { installComponent } from "../lib/installer.js";
5
+ import { log } from "../lib/logger.js";
6
+ export async function addCommand(names, opts, cwd = process.cwd()) {
7
+ const config = readConfig(cwd);
8
+ for (const nameAtVersion of names) {
9
+ const [name, version = "latest"] = nameAtVersion.split("@");
10
+ if (isComponentInstalled(name, cwd)) {
11
+ log.warn(`${name} is already installed. Run 'devkit update ${name}' to update.`);
12
+ continue;
13
+ }
14
+ if (opts.dryRun) {
15
+ log.info(`[dry-run] Would install: ${chalk.cyan(name)}@${version} (${config.template})`);
16
+ continue;
17
+ }
18
+ const spinner = ora({ text: `Installing ${chalk.cyan(name)}…`, color: "cyan" }).start();
19
+ try {
20
+ await installComponent(name, config.template, version, opts.variant, cwd);
21
+ spinner.succeed(chalk.green(`${name}@${version} installed`));
22
+ }
23
+ catch (e) {
24
+ spinner.fail(chalk.red(`Failed to install ${name}: ${e.message}`));
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,11 @@
1
+ interface CreateOptions {
2
+ template?: string;
3
+ preset?: string;
4
+ primary?: string;
5
+ git?: boolean;
6
+ install?: boolean;
7
+ add?: string;
8
+ yes?: boolean;
9
+ }
10
+ export declare function createCommand(projectName: string | undefined, opts: CreateOptions): Promise<void>;
11
+ export {};
@@ -0,0 +1,181 @@
1
+ import { intro, outro, text, select, multiselect, confirm, spinner } from "@clack/prompts";
2
+ import chalk from "chalk";
3
+ import { execSync } from "child_process";
4
+ import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs";
5
+ import { join } from "path";
6
+ import { downloadTemplate } from "../lib/template.js";
7
+ import { writeConfig, writeLock, defaultConfig } from "../lib/config.js";
8
+ import { generateThemeCSS } from "../lib/theme.js";
9
+ import { installComponent } from "../lib/installer.js";
10
+ import { log } from "../lib/logger.js";
11
+ const TEMPLATES = [
12
+ {
13
+ value: "nextjs-compact",
14
+ label: "NextJS Compact",
15
+ hint: "Next.js 16 · React 19 · TailwindCSS 4 — Web only, SSR/RSC",
16
+ },
17
+ {
18
+ value: "vite-express-tauri",
19
+ label: "Wide Express (Vite + Express + Tauri)",
20
+ hint: "Vite 7 · React 19 · Express 5 · Tauri 2 — Web + Desktop",
21
+ },
22
+ ];
23
+ const PRESETS = [
24
+ { value: "default", label: "Default — Indigo + Amber — Modern SaaS" },
25
+ { value: "minimal", label: "Minimal — Slate + Sky — Clean, editorial" },
26
+ { value: "bold", label: "Bold — Violet + Pink — Vibrant, expressive" },
27
+ { value: "playful", label: "Playful — Emerald + Orange — Friendly, rounded" },
28
+ { value: "corporate", label: "Corporate — Blue + Gray — Formal, enterprise" },
29
+ ];
30
+ const COMPONENTS = [
31
+ { value: "auth", label: "auth — Full authentication system" },
32
+ { value: "hero-section", label: "hero-section — Landing page hero banner" },
33
+ { value: "profile", label: "profile — User profile pages" },
34
+ { value: "pricing", label: "pricing — Pricing table" },
35
+ { value: "dashboard", label: "dashboard — App dashboard layout" },
36
+ { value: "contact-form", label: "contact-form — Contact / support form" },
37
+ ];
38
+ export async function createCommand(projectName, opts) {
39
+ console.log();
40
+ console.log(chalk.bold.white(" ┌─────────────────────────────────────────────────────┐"));
41
+ console.log(chalk.bold.white(" │ Roboticela DevKit — Create New Project │"));
42
+ console.log(chalk.bold.white(" │ v1.0.0 │"));
43
+ console.log(chalk.bold.white(" └─────────────────────────────────────────────────────┘"));
44
+ console.log();
45
+ intro(chalk.cyan("Let's scaffold your project"));
46
+ // ── Collect answers ──────────────────────────────────────────────────────
47
+ const name = projectName ?? (opts.yes ? "my-app" : await text({
48
+ message: "Project name",
49
+ placeholder: "my-app",
50
+ validate: (v) => v.length < 1 ? "Name is required" : undefined,
51
+ }));
52
+ if (!name || typeof name !== "string") {
53
+ process.exit(0);
54
+ }
55
+ const template = (opts.template ?? (opts.yes ? "nextjs-compact" : await select({
56
+ message: "Select a template",
57
+ options: TEMPLATES,
58
+ })));
59
+ const siteName = opts.yes ? name : await text({
60
+ message: "Site display name",
61
+ initialValue: name,
62
+ });
63
+ const siteUrl = opts.yes ? `https://${name}.com` : await text({
64
+ message: "Site URL",
65
+ placeholder: "https://myapp.com",
66
+ });
67
+ const preset = opts.preset ?? (opts.yes ? "default" : await select({
68
+ message: "Theme preset",
69
+ options: PRESETS,
70
+ }));
71
+ const primaryColor = opts.primary ?? (opts.yes ? undefined : await text({
72
+ message: "Primary brand color (hex, leave blank to use preset)",
73
+ placeholder: "(leave blank for preset default)",
74
+ validate: (v) => v && !/^#[0-9a-fA-F]{6}$/.test(v) ? "Enter a valid hex like #6366f1 or leave blank" : undefined,
75
+ }));
76
+ const doGit = opts.git ?? (opts.yes ? true : await confirm({ message: "Initialize a git repository?" }));
77
+ const doInstall = opts.install ?? (opts.yes ? true : await confirm({ message: "Install npm dependencies now?" }));
78
+ const selectedComponents = opts.add
79
+ ? opts.add.split(",").map((s) => s.trim())
80
+ : (opts.yes ? [] : await multiselect({
81
+ message: "Add components now? (space to select, enter to confirm — you can always run 'devkit add' later)",
82
+ options: COMPONENTS,
83
+ required: false,
84
+ }));
85
+ // ── Build ────────────────────────────────────────────────────────────────
86
+ console.log();
87
+ log.header("Creating project…");
88
+ const destDir = join(process.cwd(), name);
89
+ if (existsSync(destDir)) {
90
+ log.error(`Directory '${name}' already exists.`);
91
+ process.exit(1);
92
+ }
93
+ mkdirSync(destDir, { recursive: true });
94
+ // 1. Download template
95
+ const s = spinner();
96
+ s.start(`Fetching template: ${chalk.cyan(template)}`);
97
+ try {
98
+ await downloadTemplate(template, destDir);
99
+ s.stop(chalk.green(`✓ Template fetched: ${template}`));
100
+ }
101
+ catch (e) {
102
+ s.stop(chalk.red(`✗ Failed to fetch template`));
103
+ log.error(e.message);
104
+ process.exit(1);
105
+ }
106
+ // 2. Write devkit.config.json
107
+ const config = defaultConfig(template, siteName, siteUrl);
108
+ config.theme.preset = preset;
109
+ if (primaryColor)
110
+ config.theme.colors = { ...config.theme.colors, primary: primaryColor };
111
+ writeConfig(config, destDir);
112
+ log.success("Created devkit.config.json");
113
+ // 3. Write devkit.lock.json
114
+ writeLock({ lockVersion: 1, template, components: {} }, destDir);
115
+ log.success("Created devkit.lock.json");
116
+ // 4. Generate theme CSS
117
+ const themeCss = generateThemeCSS(config);
118
+ const cssPath = template === "nextjs-compact"
119
+ ? join(destDir, "app", "globals.css")
120
+ : join(destDir, "src", "index.css");
121
+ if (existsSync(cssPath)) {
122
+ const existing = readFileSync(cssPath, "utf-8");
123
+ const withoutOldBlock = existing.replace(/\/\* ── DevKit Managed Theme Block[\s\S]*?── End DevKit Managed Theme Block ── \*\//, "").trimStart();
124
+ writeFileSync(cssPath, themeCss + withoutOldBlock);
125
+ }
126
+ else {
127
+ mkdirSync(join(destDir, template === "nextjs-compact" ? "app" : "src"), { recursive: true });
128
+ writeFileSync(cssPath, themeCss);
129
+ }
130
+ log.success(`Applied theme preset: ${preset}`);
131
+ // 5. npm install
132
+ if (doInstall) {
133
+ s.start("Installing npm dependencies…");
134
+ try {
135
+ execSync("npm install", { cwd: destDir, stdio: "pipe" });
136
+ s.stop(chalk.green("✓ npm install complete"));
137
+ }
138
+ catch {
139
+ s.stop(chalk.yellow("⚠ npm install failed — run it manually"));
140
+ }
141
+ }
142
+ // 6. git init
143
+ if (doGit) {
144
+ try {
145
+ execSync("git init", { cwd: destDir, stdio: "pipe" });
146
+ execSync('git add . && git commit -m "chore: init project with DevKit"', { cwd: destDir, stdio: "pipe" });
147
+ log.success("Initialized git repository");
148
+ }
149
+ catch {
150
+ log.warn("git init failed — git may not be installed");
151
+ }
152
+ }
153
+ // 7. Add selected components
154
+ for (const comp of selectedComponents) {
155
+ s.start(`Adding component: ${chalk.cyan(comp)}`);
156
+ try {
157
+ await installComponent(comp, template, "latest", undefined, destDir);
158
+ s.stop(chalk.green(`✓ Added: ${comp}`));
159
+ }
160
+ catch (e) {
161
+ s.stop(chalk.yellow(`⚠ Skipped ${comp}: ${e.message}`));
162
+ }
163
+ }
164
+ // ── Done ─────────────────────────────────────────────────────────────────
165
+ console.log();
166
+ outro(chalk.green("Project ready!"));
167
+ console.log();
168
+ console.log(` ${chalk.bold("Location")} ${name}/`);
169
+ console.log(` ${chalk.bold("Template")} ${template}`);
170
+ console.log(` ${chalk.bold("Theme")} ${preset}`);
171
+ if (selectedComponents.length > 0) {
172
+ console.log(` ${chalk.bold("Components")} ${selectedComponents.join(", ")}`);
173
+ }
174
+ console.log();
175
+ console.log(chalk.bold(" Next steps:"));
176
+ console.log(` ${chalk.cyan("cd")} ${name}`);
177
+ console.log(` ${chalk.cyan("cp")} .env.example .env ${chalk.gray("# Fill in your secrets")}`);
178
+ console.log(` ${chalk.cyan("devkit doctor")} ${chalk.gray("# Check for missing config")}`);
179
+ console.log(` ${chalk.cyan("npm run dev")} ${chalk.gray("# Start the dev server")}`);
180
+ console.log();
181
+ }
@@ -0,0 +1 @@
1
+ export declare function doctorCommand(cwd?: string): Promise<void>;
@@ -0,0 +1,76 @@
1
+ import chalk from "chalk";
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { readConfig, readLock } from "../lib/config.js";
5
+ import { log } from "../lib/logger.js";
6
+ import { healthCheck } from "../lib/registry.js";
7
+ export async function doctorCommand(cwd = process.cwd()) {
8
+ let ok = true;
9
+ log.blank();
10
+ log.header("DevKit Doctor");
11
+ // Config check
12
+ const configPath = join(cwd, "devkit.config.json");
13
+ if (existsSync(configPath)) {
14
+ log.success("devkit.config.json found");
15
+ }
16
+ else {
17
+ log.error("devkit.config.json not found — run 'devkit init'");
18
+ ok = false;
19
+ }
20
+ // Template check
21
+ try {
22
+ const config = readConfig(cwd);
23
+ log.success(`Template: ${config.template}`);
24
+ // Site config
25
+ if (!config.site.name) {
26
+ log.warn("site.name is empty");
27
+ ok = false;
28
+ }
29
+ if (!config.site.url) {
30
+ log.warn("site.url is empty");
31
+ ok = false;
32
+ }
33
+ // Installed components env vars
34
+ const lock = readLock(cwd);
35
+ for (const [name, entry] of Object.entries(lock.components)) {
36
+ const manifestPath = join(cwd, ".devkit", `${name}.manifest.json`);
37
+ if (!existsSync(manifestPath)) {
38
+ log.warn(`${name}@${entry.version} — manifest missing (reinstall with 'devkit add ${name}')`);
39
+ continue;
40
+ }
41
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
42
+ const envVars = manifest.envVars ?? [];
43
+ let compOk = true;
44
+ for (const envVar of envVars) {
45
+ if (!process.env[envVar]) {
46
+ log.warn(`${name} — Missing env var: ${chalk.yellow(envVar)}`);
47
+ compOk = false;
48
+ ok = false;
49
+ }
50
+ }
51
+ if (compOk) {
52
+ log.success(`${name}@${entry.version} — OK`);
53
+ }
54
+ }
55
+ }
56
+ catch (e) {
57
+ log.error(e.message);
58
+ ok = false;
59
+ }
60
+ // Registry reachability
61
+ const registryOk = await healthCheck();
62
+ if (registryOk) {
63
+ log.success("Registry server reachable");
64
+ }
65
+ else {
66
+ log.warn("Registry server not reachable (offline mode)");
67
+ }
68
+ log.blank();
69
+ if (ok) {
70
+ log.success("Everything looks good!");
71
+ }
72
+ else {
73
+ log.warn("Some issues found — see above.");
74
+ }
75
+ log.blank();
76
+ }
@@ -0,0 +1 @@
1
+ export declare function infoCommand(name: string): Promise<void>;
@@ -0,0 +1,53 @@
1
+ import chalk from "chalk";
2
+ import { getComponentInfo } from "../lib/registry.js";
3
+ import { log } from "../lib/logger.js";
4
+ export async function infoCommand(name) {
5
+ try {
6
+ const info = await getComponentInfo(name);
7
+ const versions = info["versions"];
8
+ const variants = info["variants"];
9
+ log.blank();
10
+ console.log(` ${chalk.bold.white(info["displayName"])} ${chalk.dim(`(${name})`)}`);
11
+ console.log(` ${chalk.dim(info["description"])}`);
12
+ log.blank();
13
+ console.log(` ${chalk.bold("Version")} ${versions.latest}`);
14
+ console.log(` ${chalk.bold("Category")} ${info["category"]}`);
15
+ console.log(` ${chalk.bold("Templates")} ${info["templates"].join(", ")}`);
16
+ console.log(` ${chalk.bold("Platforms")} ${info["platforms"].join(", ")}`);
17
+ if (variants?.length) {
18
+ log.blank();
19
+ console.log(` ${chalk.bold("Variants:")}`);
20
+ for (const v of variants) {
21
+ console.log(` ${chalk.cyan(v.id.padEnd(18))} ${chalk.dim(v.description)}`);
22
+ }
23
+ }
24
+ const req = info["requiredConfig"];
25
+ const opt = info["optionalConfig"];
26
+ if (req?.length) {
27
+ log.blank();
28
+ console.log(` ${chalk.bold("Required config:")}`);
29
+ for (const k of req)
30
+ console.log(` ${chalk.yellow(k)}`);
31
+ }
32
+ if (opt?.length) {
33
+ console.log(` ${chalk.bold("Optional config:")}`);
34
+ for (const k of opt)
35
+ console.log(` ${chalk.dim(k)}`);
36
+ }
37
+ log.blank();
38
+ console.log(` ${chalk.bold("Version history:")}`);
39
+ for (const v of versions.history) {
40
+ const tag = v.breaking ? chalk.red(" [breaking]") : "";
41
+ console.log(` ${chalk.cyan(v.version)} ${chalk.dim(v.releaseDate)}${tag}`);
42
+ }
43
+ log.blank();
44
+ console.log(` ${chalk.dim("Install:")} devkit add ${name}`);
45
+ if (variants?.length) {
46
+ console.log(` ${chalk.dim("With variant:")} devkit add ${name} --variant=${variants[0].id}`);
47
+ }
48
+ log.blank();
49
+ }
50
+ catch (e) {
51
+ log.error(`Component '${name}' not found: ${e.message}`);
52
+ }
53
+ }
@@ -0,0 +1 @@
1
+ export declare function initCommand(cwd?: string): Promise<void>;
@@ -0,0 +1,65 @@
1
+ import { intro, outro, text, select } from "@clack/prompts";
2
+ import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { detectTemplate } from "../lib/detector.js";
5
+ import { writeConfig, writeLock, defaultConfig, configExists } from "../lib/config.js";
6
+ import { generateThemeCSS } from "../lib/theme.js";
7
+ import { log } from "../lib/logger.js";
8
+ export async function initCommand(cwd = process.cwd()) {
9
+ if (configExists(cwd)) {
10
+ log.warn("DevKit is already initialized in this project.");
11
+ log.info("Run 'devkit doctor' to check configuration.");
12
+ return;
13
+ }
14
+ intro("DevKit Init");
15
+ const detected = detectTemplate(cwd);
16
+ const template = detected !== "unknown"
17
+ ? detected
18
+ : await select({
19
+ message: "Could not auto-detect template. Select manually:",
20
+ options: [
21
+ { value: "nextjs-compact", label: "NextJS Compact" },
22
+ { value: "vite-express-tauri", label: "Wide Express (Vite + Express + Tauri)" },
23
+ ],
24
+ });
25
+ if (detected !== "unknown") {
26
+ log.success(`Detected template: ${template}`);
27
+ }
28
+ const siteName = await text({ message: "Site name", placeholder: "My App" });
29
+ const siteUrl = await text({ message: "Site URL", placeholder: "https://myapp.com" });
30
+ const preset = await select({
31
+ message: "Theme preset",
32
+ options: [
33
+ { value: "default", label: "Default — Indigo" },
34
+ { value: "minimal", label: "Minimal — Slate" },
35
+ { value: "bold", label: "Bold — Violet" },
36
+ { value: "playful", label: "Playful — Emerald" },
37
+ { value: "corporate", label: "Corporate — Blue" },
38
+ ],
39
+ });
40
+ // Write config
41
+ const config = defaultConfig(template, siteName, siteUrl);
42
+ config.theme.preset = preset;
43
+ writeConfig(config, cwd);
44
+ log.success("Created devkit.config.json");
45
+ // Write lock
46
+ writeLock({ lockVersion: 1, template, components: {} }, cwd);
47
+ log.success("Created devkit.lock.json");
48
+ // Generate theme CSS
49
+ const themeCss = generateThemeCSS(config);
50
+ const cssFile = template === "nextjs-compact" ? "app/globals.css" : "src/index.css";
51
+ const cssPath = join(cwd, cssFile);
52
+ if (existsSync(cssPath)) {
53
+ const old = readFileSync(cssPath, "utf-8");
54
+ const clean = old.replace(/\/\* ── DevKit Managed Theme Block[\s\S]*?── End DevKit Managed Theme Block ── \*\//, "").trimStart();
55
+ writeFileSync(cssPath, themeCss + clean);
56
+ }
57
+ else {
58
+ mkdirSync(join(cwd, template === "nextjs-compact" ? "app" : "src"), { recursive: true });
59
+ writeFileSync(cssPath, themeCss);
60
+ }
61
+ log.success(`Applied theme to ${cssFile}`);
62
+ // .devkit dir
63
+ mkdirSync(join(cwd, ".devkit"), { recursive: true });
64
+ outro("DevKit initialized! Run 'devkit list' to browse components.");
65
+ }
@@ -0,0 +1,7 @@
1
+ interface ListOptions {
2
+ template?: string;
3
+ installed?: boolean;
4
+ category?: string;
5
+ }
6
+ export declare function listCommand(opts: ListOptions, cwd?: string): Promise<void>;
7
+ export {};
@@ -0,0 +1,52 @@
1
+ import chalk from "chalk";
2
+ import { listComponents } from "../lib/registry.js";
3
+ import { readConfig, readLock } from "../lib/config.js";
4
+ import { log } from "../lib/logger.js";
5
+ export async function listCommand(opts, cwd = process.cwd()) {
6
+ let template = opts.template;
7
+ if (!template) {
8
+ try {
9
+ template = readConfig(cwd).template;
10
+ }
11
+ catch { /* no config */ }
12
+ }
13
+ try {
14
+ const components = await listComponents(template);
15
+ const lock = (() => { try {
16
+ return readLock(cwd);
17
+ }
18
+ catch {
19
+ return { lockVersion: 1, template: "", components: {} };
20
+ } })();
21
+ const filtered = opts.installed
22
+ ? components.filter((c) => c.name in lock.components)
23
+ : opts.category
24
+ ? components.filter((c) => c.category === opts.category)
25
+ : components;
26
+ if (filtered.length === 0) {
27
+ log.info("No components found.");
28
+ return;
29
+ }
30
+ log.blank();
31
+ const col = (s, w) => s.padEnd(w).slice(0, w);
32
+ console.log(chalk.gray(" " + col("Component", 20) + col("Category", 16) + col("Templates", 36) + "Installed"));
33
+ console.log(chalk.gray(" " + "─".repeat(80)));
34
+ for (const comp of filtered) {
35
+ const installed = lock.components[comp.name];
36
+ const installedStr = installed ? chalk.green(`v${installed.version}`) : chalk.gray("-");
37
+ const templateStr = comp.templates.join(", ");
38
+ console.log(" " +
39
+ chalk.bold(col(comp.name, 20)) +
40
+ chalk.dim(col(comp.category, 16)) +
41
+ col(templateStr, 36) +
42
+ installedStr);
43
+ }
44
+ log.blank();
45
+ log.info(`${filtered.length} component(s). Run ${chalk.cyan("devkit info <name>")} for details.`);
46
+ log.blank();
47
+ }
48
+ catch (e) {
49
+ log.error(`Could not reach registry: ${e.message}`);
50
+ log.info("Is the registry server running? Start it with: cd registry && npm run dev");
51
+ }
52
+ }
@@ -0,0 +1,3 @@
1
+ export declare function removeCommand(name: string, opts: {
2
+ keepFiles?: boolean;
3
+ }, cwd?: string): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { confirm } from "@clack/prompts";
2
+ import { isComponentInstalled } from "../lib/config.js";
3
+ import { removeComponent } from "../lib/installer.js";
4
+ import { log } from "../lib/logger.js";
5
+ export async function removeCommand(name, opts, cwd = process.cwd()) {
6
+ if (!isComponentInstalled(name, cwd)) {
7
+ log.error(`'${name}' is not installed.`);
8
+ process.exit(1);
9
+ }
10
+ const ok = await confirm({ message: `Remove ${name} and all its managed files?` });
11
+ if (!ok) {
12
+ log.info("Aborted.");
13
+ return;
14
+ }
15
+ try {
16
+ removeComponent(name, cwd);
17
+ log.success(`${name} removed.`);
18
+ }
19
+ catch (e) {
20
+ log.error(e.message);
21
+ }
22
+ }
@@ -0,0 +1,6 @@
1
+ export declare function themeApplyCommand(cwd?: string): void;
2
+ export declare function themePreviewCommand(cwd?: string): void;
3
+ export declare function themePresetCommand(preset: string, cwd?: string): void;
4
+ export declare function themeSetCommand(key: string, value: string, cwd?: string): void;
5
+ export declare function themeListCommand(cwd?: string): void;
6
+ export declare function themeAuditCommand(_cwd?: string): void;
@@ -0,0 +1,82 @@
1
+ import { writeFileSync, readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ import chalk from "chalk";
4
+ import { readConfig, writeConfig } from "../lib/config.js";
5
+ import { generateThemeCSS } from "../lib/theme.js";
6
+ import { log } from "../lib/logger.js";
7
+ function getCssPath(template, cwd) {
8
+ return template === "nextjs-compact"
9
+ ? join(cwd, "app", "globals.css")
10
+ : join(cwd, "src", "index.css");
11
+ }
12
+ function patchCss(cssPath, newBlock) {
13
+ const existing = existsSync(cssPath) ? readFileSync(cssPath, "utf-8") : "";
14
+ const userSection = existing
15
+ .replace(/\/\* ── DevKit Managed Theme Block[\s\S]*?── End DevKit Managed Theme Block ── \*\//, "")
16
+ .trimStart();
17
+ writeFileSync(cssPath, newBlock + userSection);
18
+ }
19
+ export function themeApplyCommand(cwd = process.cwd()) {
20
+ const config = readConfig(cwd);
21
+ const css = generateThemeCSS(config);
22
+ patchCss(getCssPath(config.template, cwd), css);
23
+ log.success("Theme applied to CSS.");
24
+ }
25
+ export function themePreviewCommand(cwd = process.cwd()) {
26
+ const config = readConfig(cwd);
27
+ const css = generateThemeCSS(config);
28
+ log.blank();
29
+ console.log(chalk.dim(css.split("\n").map((l) => " " + l).join("\n")));
30
+ }
31
+ export function themePresetCommand(preset, cwd = process.cwd()) {
32
+ const VALID = ["default", "minimal", "bold", "playful", "corporate"];
33
+ if (!VALID.includes(preset)) {
34
+ log.error(`Unknown preset '${preset}'. Valid: ${VALID.join(", ")}`);
35
+ process.exit(1);
36
+ }
37
+ const config = readConfig(cwd);
38
+ config.theme = { ...config.theme, preset };
39
+ writeConfig(config, cwd);
40
+ themeApplyCommand(cwd);
41
+ log.success(`Switched to preset: ${preset}`);
42
+ }
43
+ export function themeSetCommand(key, value, cwd = process.cwd()) {
44
+ const config = readConfig(cwd);
45
+ // Only support: colors.primary, colors.secondary, fonts.sans, fonts.mono, radius, darkMode
46
+ if (key === "colors.primary")
47
+ config.theme.colors = { ...config.theme.colors, primary: value };
48
+ else if (key === "colors.secondary")
49
+ config.theme.colors = { ...config.theme.colors, secondary: value };
50
+ else if (key === "fonts.sans")
51
+ config.theme.fonts = { ...config.theme.fonts, sans: value };
52
+ else if (key === "fonts.mono")
53
+ config.theme.fonts = { ...config.theme.fonts, mono: value };
54
+ else if (key === "radius")
55
+ config.theme.radius = value;
56
+ else if (key === "darkMode")
57
+ config.theme.darkMode = value === "true";
58
+ else {
59
+ log.error(`Unknown theme key: ${key}`);
60
+ process.exit(1);
61
+ }
62
+ writeConfig(config, cwd);
63
+ themeApplyCommand(cwd);
64
+ log.success(`Set ${key} = ${chalk.cyan(value)}`);
65
+ }
66
+ export function themeListCommand(cwd = process.cwd()) {
67
+ const config = readConfig(cwd);
68
+ const t = config.theme ?? {};
69
+ log.blank();
70
+ console.log(` ${chalk.bold("Preset")} ${t.preset ?? "default"}`);
71
+ console.log(` ${chalk.bold("Primary")} ${chalk.hex(t.colors?.primary ?? "#6366f1")(t.colors?.primary ?? "#6366f1 (preset)")}`);
72
+ console.log(` ${chalk.bold("Secondary")} ${t.colors?.secondary ?? "(preset)"}`);
73
+ console.log(` ${chalk.bold("Font sans")} ${t.fonts?.sans ?? "Inter"}`);
74
+ console.log(` ${chalk.bold("Font mono")} ${t.fonts?.mono ?? "JetBrains Mono"}`);
75
+ console.log(` ${chalk.bold("Radius")} ${t.radius ?? "md"}`);
76
+ console.log(` ${chalk.bold("Dark mode")} ${t.darkMode !== false ? "enabled" : "disabled"}`);
77
+ log.blank();
78
+ }
79
+ export function themeAuditCommand(_cwd = process.cwd()) {
80
+ log.warn("Theme audit checks component files for hardcoded colors (not yet implemented in CLI).");
81
+ log.info("This runs automatically in registry CI when submitting a component.");
82
+ }
@@ -0,0 +1,2 @@
1
+ export declare function updateCommand(nameAtVersion: string, cwd?: string): Promise<void>;
2
+ export declare function upgradeAllCommand(cwd?: string): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import ora from "ora";
2
+ import chalk from "chalk";
3
+ import { readConfig, readLock, isComponentInstalled } from "../lib/config.js";
4
+ import { installComponent } from "../lib/installer.js";
5
+ import { log } from "../lib/logger.js";
6
+ export async function updateCommand(nameAtVersion, cwd = process.cwd()) {
7
+ const [name, version = "latest"] = nameAtVersion.split("@");
8
+ const config = readConfig(cwd);
9
+ if (!isComponentInstalled(name, cwd)) {
10
+ log.error(`'${name}' is not installed. Use 'devkit add ${name}' to install.`);
11
+ process.exit(1);
12
+ }
13
+ const lock = readLock(cwd);
14
+ const current = lock.components[name];
15
+ log.info(`Updating ${chalk.cyan(name)} from v${current.version} → ${version}`);
16
+ const spinner = ora({ text: `Updating ${name}…`, color: "cyan" }).start();
17
+ try {
18
+ await installComponent(name, config.template, version, current.variant ?? undefined, cwd);
19
+ spinner.succeed(chalk.green(`${name} updated`));
20
+ }
21
+ catch (e) {
22
+ spinner.fail(chalk.red(`Update failed: ${e.message}`));
23
+ }
24
+ }
25
+ export async function upgradeAllCommand(cwd = process.cwd()) {
26
+ const config = readConfig(cwd);
27
+ const lock = readLock(cwd);
28
+ const names = Object.keys(lock.components);
29
+ if (names.length === 0) {
30
+ log.info("No components installed.");
31
+ return;
32
+ }
33
+ log.info(`Upgrading ${names.length} component(s)…`);
34
+ for (const name of names) {
35
+ const spinner = ora({ text: `Upgrading ${chalk.cyan(name)}…`, color: "cyan" }).start();
36
+ try {
37
+ const variant = lock.components[name].variant ?? undefined;
38
+ await installComponent(name, config.template, "latest", variant, cwd);
39
+ spinner.succeed(chalk.green(`${name} upgraded`));
40
+ }
41
+ catch (e) {
42
+ spinner.fail(chalk.yellow(`${name}: ${e.message}`));
43
+ }
44
+ }
45
+ }