voidui-cli 0.1.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 (65) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/add.d.ts +28 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +104 -0
  5. package/dist/commands/augment.d.ts +2 -0
  6. package/dist/commands/augment.d.ts.map +1 -0
  7. package/dist/commands/augment.js +97 -0
  8. package/dist/commands/diff.d.ts +19 -0
  9. package/dist/commands/diff.d.ts.map +1 -0
  10. package/dist/commands/diff.js +233 -0
  11. package/dist/commands/index.d.ts +3 -0
  12. package/dist/commands/index.d.ts.map +1 -0
  13. package/dist/commands/index.js +2 -0
  14. package/dist/commands/snapshot.d.ts +2 -0
  15. package/dist/commands/snapshot.d.ts.map +1 -0
  16. package/dist/commands/snapshot.js +99 -0
  17. package/dist/commands/update.d.ts +28 -0
  18. package/dist/commands/update.d.ts.map +1 -0
  19. package/dist/commands/update.js +203 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +89 -0
  23. package/dist/types/changelog.d.ts +17 -0
  24. package/dist/types/changelog.d.ts.map +1 -0
  25. package/dist/types/changelog.js +1 -0
  26. package/dist/types/lock-file.d.ts +41 -0
  27. package/dist/types/lock-file.d.ts.map +1 -0
  28. package/dist/types/lock-file.js +4 -0
  29. package/dist/types/registry.d.ts +84 -0
  30. package/dist/types/registry.d.ts.map +1 -0
  31. package/dist/types/registry.js +4 -0
  32. package/dist/utils/checksum.d.ts +27 -0
  33. package/dist/utils/checksum.d.ts.map +1 -0
  34. package/dist/utils/checksum.js +50 -0
  35. package/dist/utils/component-locator.d.ts +23 -0
  36. package/dist/utils/component-locator.d.ts.map +1 -0
  37. package/dist/utils/component-locator.js +59 -0
  38. package/dist/utils/diff-formatter.d.ts +47 -0
  39. package/dist/utils/diff-formatter.d.ts.map +1 -0
  40. package/dist/utils/diff-formatter.js +186 -0
  41. package/dist/utils/file-operations.d.ts +7 -0
  42. package/dist/utils/file-operations.d.ts.map +1 -0
  43. package/dist/utils/file-operations.js +37 -0
  44. package/dist/utils/lock-file.d.ts +60 -0
  45. package/dist/utils/lock-file.d.ts.map +1 -0
  46. package/dist/utils/lock-file.js +110 -0
  47. package/dist/utils/merge.d.ts +54 -0
  48. package/dist/utils/merge.d.ts.map +1 -0
  49. package/dist/utils/merge.js +84 -0
  50. package/dist/utils/prompts.d.ts +8 -0
  51. package/dist/utils/prompts.d.ts.map +1 -0
  52. package/dist/utils/prompts.js +49 -0
  53. package/dist/utils/registry.d.ts +32 -0
  54. package/dist/utils/registry.d.ts.map +1 -0
  55. package/dist/utils/registry.js +139 -0
  56. package/dist/utils/shadcn.d.ts +30 -0
  57. package/dist/utils/shadcn.d.ts.map +1 -0
  58. package/dist/utils/shadcn.js +67 -0
  59. package/dist/validators/changelog.d.ts +50 -0
  60. package/dist/validators/changelog.d.ts.map +1 -0
  61. package/dist/validators/changelog.js +31 -0
  62. package/dist/validators/lock-file.d.ts +32 -0
  63. package/dist/validators/lock-file.d.ts.map +1 -0
  64. package/dist/validators/lock-file.js +39 -0
  65. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # voidui CLI
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Add command implementation
3
+ * Installs components with version tracking
4
+ */
5
+ interface AddCommandOptions {
6
+ /**
7
+ * Add tracking to existing component without reinstalling
8
+ */
9
+ scan?: boolean;
10
+ /**
11
+ * Force reinstall even if component exists
12
+ */
13
+ force?: boolean;
14
+ /**
15
+ * Registry URL
16
+ */
17
+ registry?: string;
18
+ }
19
+ /**
20
+ * Main add command handler
21
+ * Installs a component and tracks it in voidui.lock.json
22
+ *
23
+ * @param component - Component name
24
+ * @param options - Command options
25
+ */
26
+ export declare function addCommand(component: string | undefined, options: AddCommandOptions): Promise<void>;
27
+ export {};
28
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH,UAAU,iBAAiB;IACzB;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CA8If"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Add command implementation
3
+ * Installs components with version tracking
4
+ */
5
+ import chalk from "chalk";
6
+ import { locateComponent } from "../utils/component-locator.js";
7
+ import { computeChecksum } from "../utils/checksum.js";
8
+ import { fetchRegistryItem } from "../utils/registry.js";
9
+ import { execShadcnAdd } from "../utils/shadcn.js";
10
+ import { readOrCreateLockFile, writeLockFile, updateComponentEntry, isComponentTracked, } from "../utils/lock-file.js";
11
+ const DEFAULT_REGISTRY_URL = "https://voidui.dev/r";
12
+ /**
13
+ * Main add command handler
14
+ * Installs a component and tracks it in voidui.lock.json
15
+ *
16
+ * @param component - Component name
17
+ * @param options - Command options
18
+ */
19
+ export async function addCommand(component, options) {
20
+ const registryUrl = options.registry || DEFAULT_REGISTRY_URL;
21
+ const cwd = process.cwd();
22
+ // Validate component name
23
+ if (!component) {
24
+ console.error(chalk.red("āŒ Component name is required"));
25
+ console.error(chalk.gray("\nUsage:"));
26
+ console.error(chalk.gray(" voidui add <component>"));
27
+ console.error(chalk.gray(" voidui add <component> --scan"));
28
+ console.error(chalk.gray(" voidui add <component> --force"));
29
+ process.exit(1);
30
+ }
31
+ console.log(chalk.blue(`\nšŸ“¦ Adding ${component}...\n`));
32
+ // 1. Check if component already exists locally
33
+ const location = await locateComponent(component, cwd);
34
+ if (location.exists && !options.scan && !options.force) {
35
+ console.error(chalk.red(`āŒ Component "${component}" already exists in your project`));
36
+ console.error(chalk.gray(` Location: ${location.path}`));
37
+ console.error(chalk.gray("\n Use --scan to add version tracking without reinstalling"));
38
+ console.error(chalk.gray(" Use --force to reinstall and update tracking"));
39
+ process.exit(1);
40
+ }
41
+ // 2. Fetch registry metadata to get current version
42
+ console.log(chalk.gray("Fetching registry metadata..."));
43
+ const registryItem = await fetchRegistryItem(component, registryUrl);
44
+ if (!registryItem) {
45
+ console.error(chalk.red(`āŒ Component "${component}" not found in registry`));
46
+ console.error(chalk.gray(` Registry: ${registryUrl}`));
47
+ console.error(chalk.gray(` Available at: ${registryUrl.replace("/r", "/components")}`));
48
+ process.exit(1);
49
+ }
50
+ const versioning = registryItem.meta?.versioning;
51
+ if (!versioning) {
52
+ console.error(chalk.red(`āŒ Component "${component}" does not have version tracking enabled`));
53
+ process.exit(1);
54
+ }
55
+ const currentVersion = versioning.currentVersion;
56
+ // 3. Install component via shadcn if needed
57
+ if (!options.scan) {
58
+ try {
59
+ await execShadcnAdd(component, { registryUrl });
60
+ }
61
+ catch (error) {
62
+ console.error(chalk.red("\nāŒ Installation failed:"));
63
+ console.error(error instanceof Error ? error.message : String(error));
64
+ process.exit(1);
65
+ }
66
+ }
67
+ // 4. Locate the installed component
68
+ const componentLocation = await locateComponent(component, cwd);
69
+ if (!componentLocation.exists) {
70
+ console.error(chalk.red(`āŒ Component was not installed at expected location`));
71
+ console.error(chalk.gray(` Expected: ${componentLocation.path}`));
72
+ console.error(chalk.gray("\n This might be a configuration issue."));
73
+ console.error(chalk.gray(" Check your components.json or project structure."));
74
+ process.exit(1);
75
+ }
76
+ // 5. Compute checksum of installed file
77
+ console.log(chalk.gray("Computing checksum..."));
78
+ const checksum = await computeChecksum(componentLocation.path);
79
+ // 6. Read or create lock file
80
+ const lockFile = await readOrCreateLockFile(cwd);
81
+ // Check if already tracked
82
+ if (isComponentTracked(lockFile, component) && !options.force) {
83
+ console.log(chalk.yellow(`āš ļø Component "${component}" is already tracked in lock file`));
84
+ console.log(chalk.gray(" Use --force to update the tracking information"));
85
+ return;
86
+ }
87
+ // 7. Update lock file
88
+ const updatedLockFile = updateComponentEntry(lockFile, component, {
89
+ installedVersion: currentVersion,
90
+ installedAt: new Date().toISOString(),
91
+ checksum: checksum,
92
+ registryUrl: registryUrl,
93
+ });
94
+ await writeLockFile(cwd, updatedLockFile);
95
+ // 8. Success message
96
+ console.log(chalk.green(`\nāœ“ Added ${component}@${currentVersion} with version tracking`));
97
+ console.log(chalk.gray(` Checksum: ${checksum.substring(0, 20)}...`));
98
+ console.log(chalk.gray(` Location: ${componentLocation.path}`));
99
+ if (options.scan) {
100
+ console.log(chalk.yellow("\n Note: Existing component was scanned. Checksum reflects current state."));
101
+ }
102
+ console.log(chalk.gray(`\nRun \`voidui diff ${component}\` to check for updates`));
103
+ console.log("");
104
+ }
@@ -0,0 +1,2 @@
1
+ export declare function augmentCommand(): Promise<void>;
2
+ //# sourceMappingURL=augment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"augment.d.ts","sourceRoot":"","sources":["../../src/commands/augment.ts"],"names":[],"mappings":"AA2CA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA4HpD"}
@@ -0,0 +1,97 @@
1
+ import path from "path";
2
+ import chalk from "chalk";
3
+ import { glob } from "glob";
4
+ import { fileExists, readJsonFile, writeJsonFile, readDir, } from "../utils/file-operations.js";
5
+ export async function augmentCommand() {
6
+ console.log(chalk.blue("\nšŸ”§ Augmenting registry with versioning metadata...\n"));
7
+ const cwd = process.cwd();
8
+ // Detect if running from monorepo root or www-docs
9
+ let registryPath;
10
+ let outputPath;
11
+ if (await fileExists(path.join(cwd, "registry/components/ui"))) {
12
+ // Running from www-docs
13
+ registryPath = path.join(cwd, "registry/components/ui");
14
+ outputPath = path.join(cwd, "public/r");
15
+ }
16
+ else {
17
+ // Running from monorepo root
18
+ registryPath = path.join(cwd, "apps/www-docs/registry/components/ui");
19
+ outputPath = path.join(cwd, "apps/www-docs/public/r");
20
+ }
21
+ // Check if output directory exists
22
+ if (!(await fileExists(outputPath))) {
23
+ console.error(chalk.red('āŒ Registry output not found. Run "pnpm registry:build:raw" first.'));
24
+ process.exit(1);
25
+ }
26
+ let augmentedCount = 0;
27
+ // Find all changelog files
28
+ const changelogPattern = path.join(registryPath, "*.changelog.json");
29
+ const changelogFiles = await glob(changelogPattern.replace(/\\/g, "/"));
30
+ for (const changelogFilePath of changelogFiles) {
31
+ const componentName = path
32
+ .basename(changelogFilePath)
33
+ .replace(".changelog.json", "");
34
+ // Read changelog
35
+ const changelog = await readJsonFile(changelogFilePath);
36
+ if (!changelog) {
37
+ console.warn(chalk.yellow(`āš ļø Could not read changelog for ${componentName}`));
38
+ continue;
39
+ }
40
+ // Find available versions
41
+ const versionsDir = path.join(registryPath, `${componentName}.versions`);
42
+ let availableVersions = [];
43
+ if (await fileExists(versionsDir)) {
44
+ const files = await readDir(versionsDir);
45
+ availableVersions = files
46
+ .filter((f) => f.endsWith(".tsx"))
47
+ .map((f) => f.replace(".tsx", ""))
48
+ .sort((a, b) => {
49
+ // Semantic version sort (descending)
50
+ const aParts = a.split(".").map(Number);
51
+ const bParts = b.split(".").map(Number);
52
+ for (let i = 0; i < 3; i++) {
53
+ const aVal = aParts[i] ?? 0;
54
+ const bVal = bParts[i] ?? 0;
55
+ if (bVal !== aVal) {
56
+ return bVal - aVal;
57
+ }
58
+ }
59
+ return 0;
60
+ });
61
+ }
62
+ // Read generated registry item
63
+ const registryItemPath = path.join(outputPath, `${componentName}.json`);
64
+ const registryItem = await readJsonFile(registryItemPath);
65
+ if (!registryItem) {
66
+ console.warn(chalk.yellow(`āš ļø No registry output found for ${componentName}`));
67
+ continue;
68
+ }
69
+ // Augment with meta field
70
+ registryItem.meta = {
71
+ versioning: {
72
+ currentVersion: changelog.currentVersion,
73
+ changelog,
74
+ availableVersions,
75
+ },
76
+ };
77
+ // Write back
78
+ await writeJsonFile(registryItemPath, registryItem);
79
+ console.log(chalk.green(`āœ“ Augmented: ${componentName}.json`));
80
+ augmentedCount++;
81
+ }
82
+ // Update main registry.json
83
+ const mainRegistryPath = path.join(outputPath, "registry.json");
84
+ const mainRegistry = await readJsonFile(mainRegistryPath);
85
+ if (mainRegistry && mainRegistry.items) {
86
+ for (const item of mainRegistry.items) {
87
+ const itemPath = path.join(outputPath, `${item.name}.json`);
88
+ const itemData = await readJsonFile(itemPath);
89
+ if (itemData?.meta) {
90
+ item.meta = itemData.meta;
91
+ }
92
+ }
93
+ await writeJsonFile(mainRegistryPath, mainRegistry);
94
+ console.log(chalk.green("āœ“ Augmented: registry.json"));
95
+ }
96
+ console.log(chalk.blue(`\n✨ Augmentation complete! ${augmentedCount} component(s) updated.\n`));
97
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Diff command implementation
3
+ * Compares local components against registry versions
4
+ */
5
+ interface DiffCommandOptions {
6
+ code?: boolean;
7
+ registry?: string;
8
+ }
9
+ /**
10
+ * Main diff command handler
11
+ *
12
+ * Supports three modes:
13
+ * 1. `voidui diff separator` - Check local vs latest
14
+ * 2. `voidui diff separator --code` - Show code diff
15
+ * 3. `voidui diff separator 1.0.0 1.2.0` - Compare registry versions
16
+ */
17
+ export declare function diffCommand(component: string | undefined, fromVersion: string | undefined, toVersion: string | undefined, options: DiffCommandOptions): Promise<void>;
18
+ export {};
19
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAsBH,UAAU,kBAAkB;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAsCf"}
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Diff command implementation
3
+ * Compares local components against registry versions
4
+ */
5
+ import path from "path";
6
+ import chalk from "chalk";
7
+ import { readFile } from "fs/promises";
8
+ import { fileExists, readJsonFile } from "../utils/file-operations.js";
9
+ import { locateComponent } from "../utils/component-locator.js";
10
+ import { computeChecksum, compareChecksums } from "../utils/checksum.js";
11
+ import { fetchRegistryItem, extractComponentCode, fetchComponentVersion, } from "../utils/registry.js";
12
+ import { formatDiff, formatChangelog, formatChangelogSummary, } from "../utils/diff-formatter.js";
13
+ import { lockFileSchema } from "../validators/lock-file.js";
14
+ const DEFAULT_REGISTRY_URL = "https://voidui.dev/r";
15
+ /**
16
+ * Main diff command handler
17
+ *
18
+ * Supports three modes:
19
+ * 1. `voidui diff separator` - Check local vs latest
20
+ * 2. `voidui diff separator --code` - Show code diff
21
+ * 3. `voidui diff separator 1.0.0 1.2.0` - Compare registry versions
22
+ */
23
+ export async function diffCommand(component, fromVersion, toVersion, options) {
24
+ const registryUrl = options.registry || DEFAULT_REGISTRY_URL;
25
+ // Validate component name is provided
26
+ if (!component) {
27
+ console.error(chalk.red("āŒ Component name is required"));
28
+ console.error(chalk.gray("\nUsage:"));
29
+ console.error(chalk.gray(" voidui diff <component>"));
30
+ console.error(chalk.gray(" voidui diff <component> --code"));
31
+ console.error(chalk.gray(" voidui diff <component> <from-version> <to-version>"));
32
+ process.exit(1);
33
+ }
34
+ // Determine which mode to run
35
+ if (fromVersion && toVersion) {
36
+ // Mode 3: Compare two registry versions
37
+ await compareRegistryVersions(component, fromVersion, toVersion, registryUrl);
38
+ }
39
+ else if (fromVersion && !toVersion) {
40
+ // Invalid: only one version specified
41
+ console.error(chalk.red("āŒ Both from-version and to-version must be specified"));
42
+ console.error(chalk.gray("\nUsage:"));
43
+ console.error(chalk.gray(" voidui diff <component> <from-version> <to-version>"));
44
+ process.exit(1);
45
+ }
46
+ else {
47
+ // Mode 1 or 2: Check local vs registry
48
+ await compareLocalVsRegistry(component, registryUrl, options);
49
+ }
50
+ }
51
+ /**
52
+ * Mode 1 & 2: Compare local component against registry
53
+ */
54
+ async function compareLocalVsRegistry(component, registryUrl, options) {
55
+ const cwd = process.cwd();
56
+ console.log(chalk.blue(`\nšŸ“Š Checking ${component}...\n`));
57
+ // 1. Locate component in user's project
58
+ const location = await locateComponent(component, cwd);
59
+ if (!location.exists) {
60
+ console.error(chalk.red(`āŒ Component "${component}" not found in your project`));
61
+ console.error(chalk.gray(` Expected at: ${location.path}`));
62
+ process.exit(1);
63
+ }
64
+ // 2. Read lock file if exists
65
+ const lockFilePath = path.join(cwd, "voidui.lock.json");
66
+ const lockFileExists = await fileExists(lockFilePath);
67
+ let lockFile = null;
68
+ let installedVersion = null;
69
+ let expectedChecksum = null;
70
+ if (lockFileExists) {
71
+ const rawLockFile = await readJsonFile(lockFilePath);
72
+ if (rawLockFile) {
73
+ try {
74
+ const parsedLockFile = lockFileSchema.parse(rawLockFile);
75
+ lockFile = parsedLockFile;
76
+ const entry = parsedLockFile.components[component];
77
+ if (entry) {
78
+ installedVersion = entry.installedVersion;
79
+ expectedChecksum = entry.checksum;
80
+ }
81
+ }
82
+ catch (error) {
83
+ console.error(chalk.yellow("āš ļø Lock file is corrupted. Continuing without version tracking."));
84
+ }
85
+ }
86
+ }
87
+ // 3. Compute local file checksum
88
+ const actualChecksum = await computeChecksum(location.path);
89
+ const isModified = expectedChecksum && !compareChecksums(expectedChecksum, actualChecksum);
90
+ // 4. Fetch registry metadata
91
+ const registryItem = await fetchRegistryItem(component, registryUrl);
92
+ if (!registryItem) {
93
+ console.error(chalk.red(`āŒ Component "${component}" not found in registry`));
94
+ console.error(chalk.gray(` Registry: ${registryUrl}`));
95
+ console.error(chalk.gray(` Available at: ${registryUrl.replace("/r", "/components")}`));
96
+ process.exit(1);
97
+ }
98
+ const versioning = registryItem.meta?.versioning;
99
+ if (!versioning) {
100
+ console.error(chalk.red(`āŒ Component "${component}" does not have version tracking enabled`));
101
+ process.exit(1);
102
+ }
103
+ const latestVersion = versioning.currentVersion;
104
+ // 5. Display version information
105
+ if (installedVersion) {
106
+ const modifiedText = isModified ? chalk.yellow(" (modified āš ļø )") : "";
107
+ console.log(`Your version: ${chalk.bold(installedVersion)}${modifiedText}`);
108
+ }
109
+ else {
110
+ if (!lockFileExists) {
111
+ console.log(chalk.yellow("āš ļø No lock file found. Lock file will be created when you run:"));
112
+ console.log(chalk.gray(` voidui add ${component}\n`));
113
+ }
114
+ else {
115
+ console.log(chalk.yellow(`āš ļø Component "${component}" not found in lock file. Install with:`));
116
+ console.log(chalk.gray(` voidui add ${component}\n`));
117
+ }
118
+ }
119
+ console.log(`Latest: ${chalk.bold(latestVersion)}`);
120
+ // 6. Show code diff if requested
121
+ if (options.code) {
122
+ console.log(chalk.blue(`\nšŸ“ Comparing ${installedVersion || "local"} → ${latestVersion}\n`));
123
+ // Read local file content
124
+ const localContent = await readFile(location.path, "utf-8");
125
+ // Get latest version content from registry
126
+ const latestContent = extractComponentCode(registryItem);
127
+ // Generate and display diff
128
+ const diff = formatDiff(localContent, latestContent, `${component}@${installedVersion || "local"}.tsx`, `${component}@${latestVersion}.tsx`);
129
+ console.log(diff);
130
+ console.log(""); // Empty line
131
+ return;
132
+ }
133
+ // 7. Show changelog summary if versions differ
134
+ if (installedVersion && installedVersion !== latestVersion) {
135
+ console.log(chalk.blue(`\nChanges from ${installedVersion} to ${latestVersion}:\n`));
136
+ const relevantEntries = getEntriesBetweenVersions(versioning.changelog.entries, installedVersion, latestVersion);
137
+ if (relevantEntries.length > 0) {
138
+ for (const entry of relevantEntries) {
139
+ console.log(formatChangelogSummary(entry));
140
+ }
141
+ }
142
+ else {
143
+ console.log(chalk.gray("No changelog entries found."));
144
+ }
145
+ console.log(chalk.gray(`\nRun \`voidui diff ${component} --code\` to see code changes`));
146
+ console.log(chalk.gray(`Run \`voidui update ${component}\` to update to latest`));
147
+ }
148
+ else if (!installedVersion) {
149
+ // Show latest version info
150
+ console.log(chalk.blue("\nLatest changes:\n"));
151
+ const latestEntry = versioning.changelog.entries[0];
152
+ if (latestEntry) {
153
+ console.log(formatChangelogSummary(latestEntry));
154
+ }
155
+ console.log(chalk.gray(`\nRun \`voidui diff ${component} --code\` to see code`));
156
+ }
157
+ else {
158
+ // Versions are the same
159
+ console.log(chalk.green("\nāœ“ You're on the latest version!"));
160
+ if (isModified) {
161
+ console.log(chalk.yellow("\nNote: Your local file has been modified from the installed version."));
162
+ }
163
+ }
164
+ console.log(""); // Empty line
165
+ }
166
+ /**
167
+ * Mode 3: Compare two registry versions
168
+ */
169
+ async function compareRegistryVersions(component, fromVersion, toVersion, registryUrl) {
170
+ console.log(chalk.blue(`\nšŸ“Š Comparing ${component} ${fromVersion} → ${toVersion}\n`));
171
+ // Fetch registry metadata
172
+ const registryItem = await fetchRegistryItem(component, registryUrl);
173
+ if (!registryItem) {
174
+ console.error(chalk.red(`āŒ Component "${component}" not found in registry`));
175
+ console.error(chalk.gray(` Registry: ${registryUrl}`));
176
+ process.exit(1);
177
+ }
178
+ const versioning = registryItem.meta?.versioning;
179
+ if (!versioning) {
180
+ console.error(chalk.red(`āŒ Component "${component}" does not have version tracking enabled`));
181
+ process.exit(1);
182
+ }
183
+ // Validate versions exist
184
+ const availableVersions = versioning.availableVersions;
185
+ if (!availableVersions.includes(fromVersion)) {
186
+ console.error(chalk.red(`āŒ Version "${fromVersion}" not found for ${component}`));
187
+ console.error(chalk.gray(` Available: ${availableVersions.join(", ")}`));
188
+ process.exit(1);
189
+ }
190
+ if (!availableVersions.includes(toVersion)) {
191
+ console.error(chalk.red(`āŒ Version "${toVersion}" not found for ${component}`));
192
+ console.error(chalk.gray(` Available: ${availableVersions.join(", ")}`));
193
+ process.exit(1);
194
+ }
195
+ // Show changelog between versions
196
+ console.log(chalk.blue(`Changes from ${fromVersion} to ${toVersion}:\n`));
197
+ const changelog = formatChangelog(versioning.changelog.entries, fromVersion, toVersion);
198
+ console.log(changelog);
199
+ // Try to fetch code for both versions and show diff
200
+ const fromContent = await fetchComponentVersion(component, fromVersion, registryUrl);
201
+ const toContent = await fetchComponentVersion(component, toVersion, registryUrl);
202
+ if (fromContent && toContent) {
203
+ console.log(chalk.blue("\nšŸ“ Code changes:\n"));
204
+ const diff = formatDiff(fromContent, toContent, `${component}@${fromVersion}.tsx`, `${component}@${toVersion}.tsx`);
205
+ console.log(diff);
206
+ }
207
+ else {
208
+ // Show note about why we can't show code diff
209
+ console.log(chalk.gray("\nNote: Code diff for historical versions is not yet supported in production."));
210
+ console.log(chalk.gray(" This feature requires registry infrastructure for version endpoints."));
211
+ if (registryUrl.includes("localhost")) {
212
+ console.log(chalk.yellow("\n For local testing, make sure version files exist at:"));
213
+ console.log(chalk.gray(` registry/components/ui/${component}.versions/${fromVersion}.tsx`));
214
+ console.log(chalk.gray(` registry/components/ui/${component}.versions/${toVersion}.tsx`));
215
+ }
216
+ }
217
+ console.log(""); // Empty line
218
+ }
219
+ /**
220
+ * Get changelog entries between two versions
221
+ */
222
+ function getEntriesBetweenVersions(entries, fromVersion, toVersion) {
223
+ const fromIndex = entries.findIndex((e) => e.version === fromVersion);
224
+ const toIndex = entries.findIndex((e) => e.version === toVersion);
225
+ if (fromIndex === -1 || toIndex === -1) {
226
+ return [];
227
+ }
228
+ // Entries are sorted newest first, extract range between versions
229
+ const start = Math.min(fromIndex, toIndex);
230
+ const end = Math.max(fromIndex, toIndex);
231
+ // Return entries in chronological order (oldest first)
232
+ return entries.slice(start, end + 1).reverse();
233
+ }
@@ -0,0 +1,3 @@
1
+ export { snapshotCommand } from "./snapshot.js";
2
+ export { augmentCommand } from "./augment.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { snapshotCommand } from "./snapshot.js";
2
+ export { augmentCommand } from "./augment.js";
@@ -0,0 +1,2 @@
1
+ export declare function snapshotCommand(component: string, version: string): Promise<void>;
2
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"AAqBA,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAqHf"}
@@ -0,0 +1,99 @@
1
+ import path from "path";
2
+ import chalk from "chalk";
3
+ import { componentChangelogSchema } from "../validators/changelog.js";
4
+ import { fileExists, readJsonFile, writeJsonFile, copyFile, ensureDir, } from "../utils/file-operations.js";
5
+ import { promptForChange, promptForMore, promptForBreaking, } from "../utils/prompts.js";
6
+ export async function snapshotCommand(component, version) {
7
+ console.log(chalk.blue(`\nšŸ“ø Creating snapshot for ${component}@${version}\n`));
8
+ // 1. Validate paths
9
+ const cwd = process.cwd();
10
+ // Detect if running from monorepo root or www-docs
11
+ let registryPath;
12
+ if (await fileExists(path.join(cwd, "registry/components/ui"))) {
13
+ // Running from www-docs
14
+ registryPath = path.join(cwd, "registry/components/ui");
15
+ }
16
+ else {
17
+ // Running from monorepo root
18
+ registryPath = path.join(cwd, "apps/www-docs/registry/components/ui");
19
+ }
20
+ const componentFile = path.join(registryPath, `${component}.tsx`);
21
+ const changelogFile = path.join(registryPath, `${component}.changelog.json`);
22
+ const versionsDir = path.join(registryPath, `${component}.versions`);
23
+ // 2. Check component exists
24
+ if (!(await fileExists(componentFile))) {
25
+ console.error(chalk.red(`āŒ Component "${component}" not found`));
26
+ console.error(chalk.gray(` Expected: ${componentFile}`));
27
+ process.exit(1);
28
+ }
29
+ // 3. Validate version format
30
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
31
+ console.error(chalk.red("āŒ Version must be in semver format (e.g., 1.0.0)"));
32
+ process.exit(1);
33
+ }
34
+ // 4. Create versions directory if needed
35
+ await ensureDir(versionsDir);
36
+ // 5. Check if version already exists
37
+ const versionFile = path.join(versionsDir, `${version}.tsx`);
38
+ if (await fileExists(versionFile)) {
39
+ console.error(chalk.red(`āŒ Version ${version} already exists`));
40
+ console.error(chalk.gray(` File: ${versionFile}`));
41
+ process.exit(1);
42
+ }
43
+ // 6. Copy component file to versions folder
44
+ await copyFile(componentFile, versionFile);
45
+ console.log(chalk.green(`āœ“ Created snapshot: ${versionFile}`));
46
+ // 7. Interactive changelog entry
47
+ console.log(chalk.blue("\nšŸ“ Creating changelog entry...\n"));
48
+ const changeEntries = [];
49
+ let addMore = true;
50
+ while (addMore) {
51
+ const change = await promptForChange();
52
+ if (!change) {
53
+ console.error(chalk.red("\nāŒ Cancelled"));
54
+ process.exit(1);
55
+ }
56
+ changeEntries.push(change);
57
+ if (changeEntries.length > 0) {
58
+ addMore = await promptForMore("Add another change?");
59
+ }
60
+ }
61
+ const breaking = await promptForBreaking();
62
+ // 8. Update or create changelog file
63
+ let changelog = await readJsonFile(changelogFile);
64
+ if (!changelog) {
65
+ // Create new changelog
66
+ changelog = {
67
+ component,
68
+ currentVersion: version,
69
+ entries: [],
70
+ };
71
+ }
72
+ // 9. Add new entry to the beginning
73
+ const newEntry = {
74
+ version,
75
+ date: new Date().toISOString(),
76
+ changes: changeEntries,
77
+ ...(breaking && { breaking }),
78
+ };
79
+ changelog.entries.unshift(newEntry);
80
+ changelog.currentVersion = version;
81
+ // 10. Validate with Zod schema
82
+ try {
83
+ componentChangelogSchema.parse(changelog);
84
+ }
85
+ catch (error) {
86
+ console.error(chalk.red("\nāŒ Changelog validation failed:"));
87
+ console.error(error);
88
+ process.exit(1);
89
+ }
90
+ // 11. Write changelog
91
+ await writeJsonFile(changelogFile, changelog);
92
+ console.log(chalk.green(`\nāœ“ Updated changelog: ${changelogFile}`));
93
+ // 12. Success message with next steps
94
+ console.log(chalk.blue("\n✨ Snapshot created successfully!\n"));
95
+ console.log(chalk.gray("Next steps:"));
96
+ console.log(chalk.gray(" 1. Review the changes"));
97
+ console.log(chalk.gray(" 2. Run: pnpm registry:build"));
98
+ console.log(chalk.gray(" 3. Commit the changes\n"));
99
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Update command implementation
3
+ * Updates components with smart merge support
4
+ */
5
+ interface UpdateCommandOptions {
6
+ /**
7
+ * Force overwrite (lose local changes)
8
+ */
9
+ force?: boolean;
10
+ /**
11
+ * Automatically attempt 3-way merge
12
+ */
13
+ merge?: boolean;
14
+ /**
15
+ * Registry URL
16
+ */
17
+ registry?: string;
18
+ }
19
+ /**
20
+ * Main update command handler
21
+ * Updates a component to latest version with merge support
22
+ *
23
+ * @param component - Component name
24
+ * @param options - Command options
25
+ */
26
+ export declare function updateCommand(component: string | undefined, options: UpdateCommandOptions): Promise<void>;
27
+ export {};
28
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAsBH,UAAU,oBAAoB;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Qf"}