magicpath-ai 1.3.0-beta.1 → 1.3.0-beta.11

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 (60) hide show
  1. package/README.md +164 -53
  2. package/dist/cli.js +28 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/add.d.ts +49 -0
  5. package/dist/commands/add.js +140 -27
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/auth.d.ts +2 -2
  8. package/dist/commands/auth.js +139 -38
  9. package/dist/commands/auth.js.map +1 -1
  10. package/dist/commands/clone.js +112 -86
  11. package/dist/commands/clone.js.map +1 -1
  12. package/dist/commands/info.d.ts +2 -0
  13. package/dist/commands/info.js +124 -0
  14. package/dist/commands/info.js.map +1 -0
  15. package/dist/commands/init.d.ts +2 -0
  16. package/dist/commands/init.js +113 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/integrate.js +108 -175
  19. package/dist/commands/integrate.js.map +1 -1
  20. package/dist/commands/list.d.ts +2 -0
  21. package/dist/commands/list.js +216 -0
  22. package/dist/commands/list.js.map +1 -0
  23. package/dist/commands/retheme.d.ts +2 -0
  24. package/dist/commands/retheme.js +327 -0
  25. package/dist/commands/retheme.js.map +1 -0
  26. package/dist/commands/schema.d.ts +2 -0
  27. package/dist/commands/schema.js +249 -0
  28. package/dist/commands/schema.js.map +1 -0
  29. package/dist/commands/search.d.ts +2 -0
  30. package/dist/commands/search.js +124 -0
  31. package/dist/commands/search.js.map +1 -0
  32. package/dist/commands/view.d.ts +2 -0
  33. package/dist/commands/view.js +27 -0
  34. package/dist/commands/view.js.map +1 -0
  35. package/dist/commands/whoami.d.ts +2 -0
  36. package/dist/commands/whoami.js +74 -0
  37. package/dist/commands/whoami.js.map +1 -0
  38. package/dist/util/api.d.ts +4 -0
  39. package/dist/util/api.js +12 -0
  40. package/dist/util/api.js.map +1 -1
  41. package/dist/util/auth.js +5 -0
  42. package/dist/util/auth.js.map +1 -1
  43. package/dist/util/component.d.ts +8 -8
  44. package/dist/util/component.js +8 -2
  45. package/dist/util/component.js.map +1 -1
  46. package/dist/util/diff.d.ts +4 -0
  47. package/dist/util/diff.js +11 -3
  48. package/dist/util/diff.js.map +1 -1
  49. package/dist/util/error.d.ts +7 -1
  50. package/dist/util/error.js +5 -1
  51. package/dist/util/error.js.map +1 -1
  52. package/dist/util/integrate.d.ts +13 -2
  53. package/dist/util/integrate.js +111 -9
  54. package/dist/util/integrate.js.map +1 -1
  55. package/dist/util/output.d.ts +15 -0
  56. package/dist/util/output.js +36 -0
  57. package/dist/util/output.js.map +1 -0
  58. package/package.json +3 -2
  59. package/skills/cli-reference.md +146 -0
  60. package/skills/guide.md +66 -0
package/README.md CHANGED
@@ -1,96 +1,207 @@
1
1
  # MagicPath CLI
2
2
 
3
- ( Instantly bring your MagicPath AI projects to your local development environment. Edit, remix, and create in your own vibe.
3
+ A component platform for AI agents. Search, add, and integrate UI components from MagicPath directly into your projects works with Claude Code, Cursor, and GitHub Copilot.
4
4
 
5
5
  ## Quick Start
6
6
 
7
7
  ```bash
8
- npx magicpath-ai@latest clone -k <your-access-key>
8
+ npm install -g magicpath-ai@beta
9
+ magicpath-ai login
10
+ magicpath-ai init # run in your project directory
9
11
  ```
10
12
 
11
- ## Installation
13
+ After `init`, Claude Code, Cursor, and GitHub Copilot automatically pick up MagicPath skills — just mention MagicPath in conversation and your agent knows what to do.
12
14
 
13
- ### Using npx (Recommended)
15
+ ## Commands
14
16
 
15
- No installation required! Just run:
17
+ ### Auth
18
+
19
+ #### `login`
20
+
21
+ Log in to your MagicPath account. Opens your browser for one-click authentication.
16
22
 
17
23
  ```bash
18
- npx magicpath-ai@latest clone -k <access-key>
24
+ magicpath-ai login
25
+ magicpath-ai login --code <code> # headless fallback
19
26
  ```
20
27
 
21
- ### Global Installation
28
+ #### `logout`
22
29
 
23
30
  ```bash
24
- npm install -g magicpath-ai
25
- magicpath-ai clone -k <access-key>
31
+ magicpath-ai logout
26
32
  ```
27
33
 
28
- ### Local Installation
34
+ #### `whoami`
35
+
36
+ Show the currently authenticated user.
29
37
 
30
38
  ```bash
31
- npm install magicpath-ai
32
- npx magicpath-ai clone -k <access-key>
39
+ magicpath-ai whoami
33
40
  ```
34
41
 
35
- ## Usage
42
+ ### Discovery
36
43
 
37
- ### Clone Command
44
+ #### `search`
38
45
 
39
- Download and set up a MagicPath project locally:
46
+ Search component names across all your projects.
40
47
 
41
48
  ```bash
42
- npx magicpath-ai@latest clone -k <access-key> [options]
49
+ magicpath-ai search "button"
50
+ magicpath-ai search "nav" --limit 5
43
51
  ```
44
52
 
45
- **Options:**
46
- - `-k, --key <accessKey>` (required): = One-time magic access key to unlock and download your project
47
- - `-d, --debug`: Get verbose debug logs in the CLI
53
+ | Option | Description |
54
+ |--------|-------------|
55
+ | `--limit <n>` | Max results (default: 20) |
56
+
57
+ #### `list-projects`
58
+
59
+ List all projects for the current user.
48
60
 
49
- **Example:**
50
61
  ```bash
51
- npx magicpath-ai@latest clone -k mp_1234567890abcdef
62
+ magicpath-ai list-projects
63
+ magicpath-ai list-projects --limit 10 --offset 0
52
64
  ```
53
65
 
54
- The CLI will:
55
- 1. =� Download your project using the access key
56
- 2. =� Prompt you to name your project (default: `magicpath-project`)
57
- 3. =� Unpack the project to a local directory
58
- 4. <� Your project is ready to go!
66
+ | Option | Description |
67
+ |--------|-------------|
68
+ | `--limit <n>` | Max results |
69
+ | `--offset <n>` | Skip first N results (default: 0) |
59
70
 
60
- ## What You Get
71
+ #### `list-components`
61
72
 
62
- When you clone a MagicPath project, you'll get a complete local copy with:
63
- - All project files and assets
64
- - Configuration files
65
- - Dependencies and setup instructions
66
- - Ready-to-run codebase
73
+ List components in a project. Supports cursor-based pagination.
67
74
 
68
- ## Requirements
75
+ ```bash
76
+ magicpath-ai list-components <projectId>
77
+ magicpath-ai list-components <projectId> --limit 20
78
+ magicpath-ai list-components <projectId> --after <lastId>
79
+ ```
69
80
 
70
- - Node.js >=16.0.0
71
- - npm or yarn
81
+ | Option | Description |
82
+ |--------|-------------|
83
+ | `--limit <n>` | Max results per page (default: 100) |
84
+ | `--after <id>` | Fetch components after this ID |
85
+ | `--sort-by <field>` | Sort by `name` or `createdAt` (default: name) |
86
+ | `--order <dir>` | Sort direction: `asc` or `desc` (default: asc) |
87
+
88
+ #### `view`
89
+
90
+ Open a component preview in the browser.
91
+
92
+ ```bash
93
+ magicpath-ai view <generatedName>
94
+ ```
95
+
96
+ ### Components
97
+
98
+ #### `add`
99
+
100
+ Add a MagicPath component to your project. Fetches the component files, writes them to your project, and installs any required dependencies.
101
+
102
+ ```bash
103
+ magicpath-ai add <generatedName>
104
+ magicpath-ai add wispy-river-5234 --path src/components/ui
105
+ magicpath-ai add wispy-river-5234 --inspect # view source without installing
106
+ magicpath-ai add wispy-river-5234 --dry-run # preview file list only
107
+ ```
108
+
109
+ | Option | Description |
110
+ |--------|-------------|
111
+ | `-p, --path <path>` | Custom component path (default: `src/components/magicpath`) |
112
+ | `--inspect` | Show full source code without installing |
113
+ | `--dry-run` | Preview file list without writing |
114
+ | `-y, --yes` | Skip confirmation prompts |
115
+ | `--overwrite` | Overwrite existing files |
116
+ | `-d, --debug` | Enable debug logging |
117
+
118
+ #### `integrate`
119
+
120
+ Integrate a MagicPath component into your project using AI. Offers two modes: integrate into a specific file, or retheme your entire project to match the component.
121
+
122
+ ```bash
123
+ magicpath-ai integrate <generatedName>
124
+ magicpath-ai integrate wispy-river-5234 --target src/app/page.tsx
125
+ magicpath-ai integrate wispy-river-5234 --dry-run
126
+ ```
127
+
128
+ | Option | Description |
129
+ |--------|-------------|
130
+ | `-t, --target <file>` | Target file to integrate into (skips interactive prompt) |
131
+ | `--no-review` | Apply changes without review |
132
+ | `--dry-run` | Show what would happen without writing |
133
+ | `-d, --debug` | Enable debug logging |
134
+
135
+ ### AI Agent Setup
72
136
 
73
- ## How It Works
137
+ #### `init`
74
138
 
75
- 1. **Access Key**: Each MagicPath project comes with a unique, one-time access key
76
- 2. **Secure Download**: The CLI securely downloads your project from MagicPath servers
77
- 3. **Local Setup**: Projects are extracted and set up in your chosen directory
78
- 4. **Ready to Code**: Start editing, remixing, and building immediately
139
+ Set up MagicPath skills for AI agents. Creates rule/skill files for Claude Code, Cursor, and GitHub Copilot in your project.
79
140
 
80
- ## Troubleshooting
141
+ ```bash
142
+ magicpath-ai init
143
+ ```
144
+
145
+ #### `info`
146
+
147
+ Show project and auth context. Useful for agent context injection.
148
+
149
+ ```bash
150
+ magicpath-ai info
151
+ ```
152
+
153
+ #### `schema`
154
+
155
+ Show JSON Schema for a command's input/output.
156
+
157
+ ```bash
158
+ magicpath-ai schema add
159
+ magicpath-ai schema --all
160
+ ```
161
+
162
+ #### `clone`
163
+
164
+ Download and unpack a full MagicPath project using a one-time access key.
81
165
 
82
- ### Invalid Access Key
83
- - Ensure your access key is correct and hasn't been used before
84
- - Access keys are single-use for security
166
+ ```bash
167
+ magicpath-ai clone -k <accessKey>
168
+ ```
169
+
170
+ | Option | Description |
171
+ |--------|-------------|
172
+ | `-k, --key <accessKey>` | Access key (required) |
173
+ | `-i, --ide <ide>` | Open in IDE (cursor, antigravity, vscode, webstorm) |
174
+ | `--dir <directory>` | Target directory (non-interactive) |
175
+ | `--name <projectName>` | Project name (non-interactive) |
176
+
177
+ ## Global Options
178
+
179
+ | Option | Description |
180
+ |--------|-------------|
181
+ | `-o json` | Structured JSON output (also skips interactive prompts) |
182
+
183
+ The `-d, --debug` flag is available on `add`, `clone`, and `integrate` for verbose logging.
184
+
185
+ ## Environment Variables
85
186
 
86
- ### Download Issues
87
- - Check your internet connection
88
- - Verify the access key hasn't expired
89
- - Use `--debug` flag for detailed error information
187
+ | Variable | Description |
188
+ |----------|-------------|
189
+ | `MAGICPATH_TOKEN` | Auth token (bypasses login flow) |
90
190
 
91
- ### Permission Errors
92
- - Ensure you have write permissions in the current directory
93
- - Try running from a different directory
191
+ ## AI Agent Integration
192
+
193
+ Running `magicpath-ai init` in your project creates skill files that teach AI agents how to use MagicPath:
194
+
195
+ - **Claude Code** — `.claude/skills/magicpath/SKILL.md`
196
+ - **Cursor** — `.cursor/rules/magicpath.mdc`
197
+ - **GitHub Copilot** — `.github/instructions/magicpath.instructions.md`
198
+
199
+ Once set up, agents automatically know how to search for components, add them to your project, and integrate them into specific files.
200
+
201
+ ## Requirements
202
+
203
+ - Node.js >= 16.0.0
204
+ - npm or yarn
94
205
 
95
206
  ## Support
96
207
 
@@ -100,8 +211,8 @@ When you clone a MagicPath project, you'll get a complete local copy with:
100
211
 
101
212
  ## License
102
213
 
103
- MIT Jack Beoris
214
+ MIT - Jack Beoris
104
215
 
105
216
  ---
106
217
 
107
- Made with ( by the MagicPath team
218
+ Made with love by the MagicPath team
package/dist/cli.js CHANGED
@@ -1,13 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from 'commander';
3
+ import { setJsonMode } from './util/output.js';
3
4
  import { registerCloneCommand } from './commands/clone.js';
4
5
  import { registerAddCommand } from './commands/add.js';
5
6
  import { registerIntegrateCommand } from './commands/integrate.js';
7
+ // import { registerRethemeCommand } from './commands/retheme.js';
6
8
  import { registerAuthCommands } from './commands/auth.js';
7
- program.name('magicpath').description('CLI for MagicPath AI').version('1.2.0');
9
+ import { registerWhoamiCommand } from './commands/whoami.js';
10
+ import { registerListCommands } from './commands/list.js';
11
+ import { registerSchemaCommand } from './commands/schema.js';
12
+ import { registerInitCommand } from './commands/init.js';
13
+ import { registerViewCommand } from './commands/view.js';
14
+ import { registerInfoCommand } from './commands/info.js';
15
+ import { registerSearchCommand } from './commands/search.js';
16
+ program
17
+ .name('magicpath')
18
+ .description('CLI for MagicPath AI')
19
+ .version('1.3.0')
20
+ .option('-o, --output <format>', 'Output format: json')
21
+ .hook('preAction', (thisCommand) => {
22
+ const opts = thisCommand.optsWithGlobals();
23
+ if (opts.output === 'json') {
24
+ setJsonMode(true);
25
+ }
26
+ });
8
27
  registerCloneCommand(program);
9
28
  registerAddCommand(program);
10
29
  registerIntegrateCommand(program);
30
+ // registerRethemeCommand(program);
11
31
  registerAuthCommands(program);
32
+ registerWhoamiCommand(program);
33
+ registerListCommands(program);
34
+ registerSchemaCommand(program);
35
+ registerInitCommand(program);
36
+ registerViewCommand(program);
37
+ registerInfoCommand(program);
38
+ registerSearchCommand(program);
12
39
  program.parse();
13
40
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/E,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAClC,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAE9B,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,kEAAkE;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,sBAAsB,CAAC;KACnC,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,uBAAuB,EAAE,qBAAqB,CAAC;KACtD,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,EAAE;IACjC,MAAM,IAAI,GAAG,WAAW,CAAC,eAAe,EAAE,CAAC;IAC3C,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC3B,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAClC,mCAAmC;AACnC,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/B,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -1,2 +1,51 @@
1
1
  import type { Command } from 'commander';
2
+ import { z } from 'zod';
3
+ export declare const addOptionsSchema: z.ZodObject<{
4
+ yes: z.ZodDefault<z.ZodBoolean>;
5
+ overwrite: z.ZodDefault<z.ZodBoolean>;
6
+ path: z.ZodOptional<z.ZodString>;
7
+ debug: z.ZodDefault<z.ZodBoolean>;
8
+ dryRun: z.ZodDefault<z.ZodBoolean>;
9
+ inspect: z.ZodDefault<z.ZodBoolean>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ debug: boolean;
12
+ yes: boolean;
13
+ overwrite: boolean;
14
+ dryRun: boolean;
15
+ inspect: boolean;
16
+ path?: string | undefined;
17
+ }, {
18
+ debug?: boolean | undefined;
19
+ path?: string | undefined;
20
+ yes?: boolean | undefined;
21
+ overwrite?: boolean | undefined;
22
+ dryRun?: boolean | undefined;
23
+ inspect?: boolean | undefined;
24
+ }>;
25
+ export type AddOptions = z.infer<typeof addOptionsSchema>;
26
+ export declare const addOutputSchema: z.ZodObject<{
27
+ component: z.ZodString;
28
+ generatedName: z.ZodString;
29
+ filesWritten: z.ZodArray<z.ZodString, "many">;
30
+ dependenciesInstalled: z.ZodArray<z.ZodString, "many">;
31
+ importStatement: z.ZodOptional<z.ZodString>;
32
+ usage: z.ZodOptional<z.ZodString>;
33
+ dryRun: z.ZodBoolean;
34
+ }, "strip", z.ZodTypeAny, {
35
+ generatedName: string;
36
+ component: string;
37
+ dryRun: boolean;
38
+ filesWritten: string[];
39
+ dependenciesInstalled: string[];
40
+ importStatement?: string | undefined;
41
+ usage?: string | undefined;
42
+ }, {
43
+ generatedName: string;
44
+ component: string;
45
+ dryRun: boolean;
46
+ filesWritten: string[];
47
+ dependenciesInstalled: string[];
48
+ importStatement?: string | undefined;
49
+ usage?: string | undefined;
50
+ }>;
2
51
  export declare function registerAddCommand(program: Command): void;
@@ -6,14 +6,26 @@ import { enableDebugLogger, logger } from '../util/logger.js';
6
6
  import { MagicPathError } from '../util/error.js';
7
7
  import { AuthRequiredError } from '../util/authError.js';
8
8
  import { brandSpinner } from '../util/ui.js';
9
+ import { isJsonMode, jsonResult, jsonError } from '../util/output.js';
9
10
  import { fetchComponent, checkExistingFiles, writeComponentFiles, checkUtilsExists, writeUtilsFile, resolveComponentsPath, resolveUtilsPath, sanitizeComponentFolderName, extractComponentExports, generateImportStatement, generateUsageExample, writeUsageFile, DEFAULT_COMPONENTS_PATH, } from '../util/component.js';
10
11
  import { installPackages } from '../util/dependencies.js';
11
12
  import { runLoginFlow } from './auth.js';
12
- const addOptionsSchema = z.object({
13
+ export const addOptionsSchema = z.object({
13
14
  yes: z.boolean().default(false),
14
15
  overwrite: z.boolean().default(false),
15
16
  path: z.string().optional(),
16
17
  debug: z.boolean().default(false),
18
+ dryRun: z.boolean().default(false),
19
+ inspect: z.boolean().default(false),
20
+ });
21
+ export const addOutputSchema = z.object({
22
+ component: z.string(),
23
+ generatedName: z.string(),
24
+ filesWritten: z.array(z.string()),
25
+ dependenciesInstalled: z.array(z.string()),
26
+ importStatement: z.string().optional(),
27
+ usage: z.string().optional(),
28
+ dryRun: z.boolean(),
17
29
  });
18
30
  export function registerAddCommand(program) {
19
31
  program
@@ -21,9 +33,11 @@ export function registerAddCommand(program) {
21
33
  .description('Add a MagicPath component to your project')
22
34
  .argument('<generatedName>', 'The generated name of the component (e.g., wispy-river-5234)')
23
35
  .option('-y, --yes', 'Skip confirmation prompts', false)
24
- .option('-o, --overwrite', 'Overwrite existing files', false)
36
+ .option('--overwrite', 'Overwrite existing files', false)
25
37
  .option('-p, --path <path>', `Custom path for components (default: ${DEFAULT_COMPONENTS_PATH})`)
26
38
  .option('-d, --debug', 'Enable debug logging', false)
39
+ .option('--dry-run', 'Show what would happen without writing files', false)
40
+ .option('--inspect', 'Show component source code without installing (implies --dry-run)', false)
27
41
  .action(async (generatedName, options) => {
28
42
  try {
29
43
  let addOptions;
@@ -35,23 +49,43 @@ export function registerAddCommand(program) {
35
49
  }
36
50
  if (addOptions.debug)
37
51
  enableDebugLogger();
52
+ // --inspect implies --dry-run and --yes (no prompts, no writing)
53
+ if (addOptions.inspect) {
54
+ addOptions.dryRun = true;
55
+ addOptions.yes = true;
56
+ }
57
+ // In JSON mode, imply --yes (skip prompts)
58
+ const json = isJsonMode();
59
+ if (json)
60
+ addOptions.yes = true;
38
61
  logger.debug({ generatedName, options: addOptions });
39
62
  const cwd = process.cwd();
40
63
  // Check if we're in a valid project directory
41
64
  const packageJsonPath = path.join(cwd, 'package.json');
42
65
  if (!fs.existsSync(packageJsonPath)) {
43
- throw new MagicPathError('No package.json found. Please run this command from the root of your project.');
66
+ throw new MagicPathError('No package.json found. Please run this command from the root of your React project.', {
67
+ code: 'MISSING_PACKAGE_JSON',
68
+ suggestion: 'Run this command from the root of your project where package.json is located.',
69
+ });
44
70
  }
45
71
  // Step 1: Fetch component from registry
46
- const fetchSpinner = brandSpinner(`Fetching component "${generatedName}"...`).start();
72
+ const fetchSpinner = json
73
+ ? null
74
+ : brandSpinner(`Fetching component "${generatedName}"...`).start();
47
75
  let componentData;
48
76
  try {
49
77
  componentData = await fetchComponent(generatedName);
50
- fetchSpinner.succeed(`Found component: ${componentData.name}`);
78
+ fetchSpinner?.succeed(`Found component: ${componentData.name}`);
51
79
  }
52
80
  catch (err) {
53
81
  if (err instanceof AuthRequiredError) {
54
- fetchSpinner.fail('Not logged in');
82
+ fetchSpinner?.fail('Not logged in');
83
+ if (json) {
84
+ throw new MagicPathError('Not authenticated. Set MAGICPATH_TOKEN or run `magicpath-ai login`.', {
85
+ code: 'NOT_AUTHENTICATED',
86
+ suggestion: 'Run `magicpath-ai login` or set the MAGICPATH_TOKEN environment variable.',
87
+ });
88
+ }
55
89
  const { shouldLogin } = await prompts({
56
90
  type: 'confirm',
57
91
  name: 'shouldLogin',
@@ -82,7 +116,7 @@ export function registerAddCommand(program) {
82
116
  }
83
117
  }
84
118
  else {
85
- fetchSpinner.fail('Failed to fetch component');
119
+ fetchSpinner?.fail('Failed to fetch component');
86
120
  if (err instanceof MagicPathError) {
87
121
  throw err;
88
122
  }
@@ -103,8 +137,10 @@ export function registerAddCommand(program) {
103
137
  // Step 2: Check for existing files
104
138
  const existingFiles = checkExistingFiles(componentData.files, componentsPath);
105
139
  if (existingFiles.length > 0 && !addOptions.overwrite) {
106
- console.log('\nThe following files already exist:');
107
- existingFiles.forEach((file) => console.log(` - ${file}`));
140
+ if (!json) {
141
+ console.log('\nThe following files already exist:');
142
+ existingFiles.forEach((file) => console.log(` - ${file}`));
143
+ }
108
144
  if (!addOptions.yes) {
109
145
  const { shouldOverwrite } = await prompts({
110
146
  type: 'confirm',
@@ -118,6 +154,12 @@ export function registerAddCommand(program) {
118
154
  }
119
155
  }
120
156
  else {
157
+ if (json) {
158
+ throw new MagicPathError(`Files already exist: ${existingFiles.join(', ')}. Use --overwrite to replace.`, {
159
+ code: 'FILES_EXIST',
160
+ suggestion: 'Pass `--overwrite` to replace existing files.',
161
+ });
162
+ }
121
163
  console.log('Use --overwrite to replace existing files.');
122
164
  return;
123
165
  }
@@ -139,35 +181,100 @@ export function registerAddCommand(program) {
139
181
  return;
140
182
  }
141
183
  }
184
+ // Build result data for JSON output
185
+ const filesWritten = componentData.files.map((f) => path.relative(cwd, path.join(componentsPath, f.path)));
186
+ // Extract import info
187
+ const mainFile = componentData.files[0];
188
+ let importStatement;
189
+ let usageStr;
190
+ if (mainFile) {
191
+ const fileName = mainFile.name.replace(/\.tsx?$/, '');
192
+ const importPath = addOptions.path
193
+ ? `@/${addOptions.path}/${componentFolderName}/${fileName}`
194
+ : `@/components/magicpath/${componentFolderName}/${fileName}`;
195
+ const exportInfo = extractComponentExports(mainFile.content);
196
+ if (exportInfo) {
197
+ importStatement = generateImportStatement(exportInfo, importPath);
198
+ usageStr = generateUsageExample(exportInfo);
199
+ }
200
+ else {
201
+ importStatement = `import { ${fileName} } from '${importPath}';`;
202
+ }
203
+ }
204
+ // Dry run: return what would happen without writing
205
+ if (addOptions.dryRun) {
206
+ const fileContents = addOptions.inspect || json
207
+ ? componentData.files.map((f) => ({
208
+ path: f.path,
209
+ name: f.name,
210
+ content: f.content,
211
+ }))
212
+ : undefined;
213
+ if (json) {
214
+ jsonResult({
215
+ component: componentData.name,
216
+ generatedName: componentData.generatedName,
217
+ filesWritten,
218
+ files: fileContents,
219
+ dependenciesInstalled: componentData.dependencies,
220
+ importStatement,
221
+ usage: usageStr,
222
+ dryRun: true,
223
+ });
224
+ }
225
+ console.log('\n[Dry run] Would install:');
226
+ console.log(` Component: ${componentData.name}`);
227
+ console.log(` Files: ${filesWritten.join(', ')}`);
228
+ console.log(` Dependencies: ${componentData.dependencies.join(', ') || 'none'}`);
229
+ if (importStatement)
230
+ console.log(` Import: ${importStatement}`);
231
+ // --inspect: show file contents
232
+ if (addOptions.inspect) {
233
+ for (const f of componentData.files) {
234
+ console.log(`\n${'─'.repeat(60)}`);
235
+ console.log(` ${f.path}`);
236
+ console.log(`${'─'.repeat(60)}`);
237
+ console.log(f.content);
238
+ }
239
+ }
240
+ return;
241
+ }
142
242
  // Step 4: Write component files
143
- const writeSpinner = brandSpinner('Writing component files...').start();
243
+ const writeSpinner = json
244
+ ? null
245
+ : brandSpinner('Writing component files...').start();
144
246
  try {
145
247
  writeComponentFiles(componentData.files, componentsPath, true);
146
- writeSpinner.succeed(`Wrote ${componentData.files.length} file(s) to ${path.relative(cwd, componentsPath) || '.'}`);
248
+ writeSpinner?.succeed(`Wrote ${componentData.files.length} file(s) to ${path.relative(cwd, componentsPath) || '.'}`);
147
249
  }
148
250
  catch (err) {
149
- writeSpinner.fail('Failed to write component files');
251
+ writeSpinner?.fail('Failed to write component files');
150
252
  throw new MagicPathError('Failed to write component files. Please check permissions and try again.');
151
253
  }
152
254
  // Step 5: Install utils if needed
153
255
  const utilsExists = checkUtilsExists(utilsPath);
154
256
  if (!utilsExists && componentData.utils.content) {
155
- const utilsSpinner = brandSpinner('Installing utilities...').start();
257
+ const utilsSpinner = json
258
+ ? null
259
+ : brandSpinner('Installing utilities...').start();
156
260
  try {
157
261
  writeUtilsFile(componentData.utils.content, utilsPath);
158
- utilsSpinner.succeed(`Wrote utils.ts to ${path.relative(cwd, utilsPath) || '.'}`);
262
+ utilsSpinner?.succeed(`Wrote utils.ts to ${path.relative(cwd, utilsPath) || '.'}`);
159
263
  }
160
264
  catch (err) {
161
- utilsSpinner.fail('Failed to write utils file');
265
+ utilsSpinner?.fail('Failed to write utils file');
162
266
  logger.debug({ err });
163
- // Non-fatal - continue with installation
164
- console.log(' Warning: Could not write utils.ts. You may need to create it manually.');
267
+ if (!json) {
268
+ console.log(' Warning: Could not write utils.ts. You may need to create it manually.');
269
+ }
165
270
  }
166
271
  }
167
272
  // Step 6: Install dependencies
273
+ const installedDeps = [];
168
274
  if (componentData.dependencies.length > 0) {
169
275
  try {
170
276
  const { installed, skipped } = installPackages(componentData.dependencies, cwd);
277
+ installedDeps.push(...installed);
171
278
  if (skipped.length > 0) {
172
279
  logger.debug({
173
280
  message: 'Skipped already installed packages',
@@ -177,39 +284,44 @@ export function registerAddCommand(program) {
177
284
  }
178
285
  catch (err) {
179
286
  if (err instanceof MagicPathError) {
180
- // Non-fatal - show warning but don't fail
181
- console.log(`\n⚠️ ${err.message}`);
287
+ if (!json)
288
+ console.log(`\n⚠️ ${err.message}`);
182
289
  }
183
290
  }
184
291
  }
292
+ // JSON output
293
+ if (json) {
294
+ jsonResult({
295
+ component: componentData.name,
296
+ generatedName: componentData.generatedName,
297
+ filesWritten,
298
+ dependenciesInstalled: installedDeps,
299
+ importStatement,
300
+ usage: usageStr,
301
+ dryRun: false,
302
+ });
303
+ }
185
304
  // Step 7: Show success message and write usage file
186
305
  console.log(`\n✅ Successfully added "${componentData.name}" to your project!`);
187
306
  console.log(`\nUsage:`);
188
- // Show import and usage example
189
- const mainFile = componentData.files[0];
190
307
  if (mainFile) {
191
308
  const fileName = mainFile.name.replace(/\.tsx?$/, '');
192
309
  const importPath = addOptions.path
193
310
  ? `@/${addOptions.path}/${componentFolderName}/${fileName}`
194
311
  : `@/components/magicpath/${componentFolderName}/${fileName}`;
195
- // Try to extract actual export info from the component source
196
312
  const exportInfo = extractComponentExports(mainFile.content);
197
313
  if (exportInfo) {
198
- // Show accurate import statement based on actual exports
199
314
  console.log(` ${generateImportStatement(exportInfo, importPath)}`);
200
315
  console.log('');
201
316
  console.log(` ${generateUsageExample(exportInfo)}`);
202
- // Show required props hint if there are any
203
317
  if (exportInfo.requiredProps.length > 0) {
204
318
  console.log('');
205
319
  console.log(` Required props: ${exportInfo.requiredProps.join(', ')}`);
206
320
  }
207
321
  }
208
322
  else {
209
- // Fallback to simple named export assumption
210
323
  console.log(` import { ${fileName} } from '${importPath}';`);
211
324
  }
212
- // Write usage.md file to the component folder
213
325
  try {
214
326
  writeUsageFile(componentsPath, {
215
327
  componentName: componentData.name,
@@ -221,11 +333,12 @@ export function registerAddCommand(program) {
221
333
  }
222
334
  catch (err) {
223
335
  logger.debug({ err });
224
- // Non-fatal - just skip writing the usage file
225
336
  }
226
337
  }
227
338
  }
228
339
  catch (err) {
340
+ if (isJsonMode())
341
+ jsonError(err);
229
342
  if (err instanceof MagicPathError) {
230
343
  console.error(`\n${err.message}`);
231
344
  process.exit(1);