@superdesign/cli 0.1.0 → 0.1.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.
package/dist/index.js CHANGED
@@ -2,10 +2,11 @@ import { config as external_dotenv_config } from "dotenv";
2
2
  import { fileURLToPath } from "url";
3
3
  import { dirname, join, resolve as external_path_resolve } from "path";
4
4
  import { Command } from "commander";
5
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
5
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
6
6
  import { homedir, hostname, platform, release } from "os";
7
7
  import axios from "axios";
8
8
  import ora from "ora";
9
+ import { writeFile } from "fs/promises";
9
10
  const CONFIG_DIR_NAME = '.superdesign';
10
11
  const CONFIG_FILE_NAME = 'config.json';
11
12
  const EXIT_CODES = {
@@ -222,7 +223,7 @@ async function runAuthFlow(options = {}) {
222
223
  try {
223
224
  startSpinner('Creating auth session...');
224
225
  const session = await createSession({
225
- cliVersion: "0.1.0",
226
+ cliVersion: "0.1.2",
226
227
  os: `${platform()} ${release()}`,
227
228
  hostname: hostname()
228
229
  });
@@ -423,241 +424,282 @@ name: superdesign
423
424
  description: Superdesign is a design agent, where it specialised in frontend UI/UX design; Use this skill before you implement any UI that require some design thinking
424
425
  ---
425
426
 
426
- # SuperDesign Integration
427
- This skill provides integration with the SuperDesign platform for AI-powered design generation.
427
+ SuperDesign helps you (1) find design inspirations/styles and (2) optionally generate/iterate design drafts on an infinite canvas.
428
428
 
429
- ## Available Commands
429
+ Each SuperDesign canvas run creates a new node and returns a \`previewUrl\`.
430
430
 
431
- ### Create Project
431
+ ---
432
432
 
433
- Create a new new project, can pass optional HTML as initial starting point for design iteration:
433
+ ## Core scenarios (what this skill handles)
434
434
 
435
- \`\`\`bash
436
- # Basic project
437
- superdesign create-project --title "My Project" --json
435
+ 1. **Help me design X** (feature/page/flow)
436
+ 2. **Set design system**
437
+ 3. **Help me improve design of X**
438
438
 
439
- # Project with initial HTML
440
- superdesign create-project --title "My Project" --html "<html>...</html>" --json
439
+ ---
441
440
 
442
- # Project with HTML from file
443
- superdesign create-project --title "My Project" --html-file ./index.html --json
444
- \`\`\`
441
+ ## Tooling overview
445
442
 
446
- **Output (JSON):**
447
- \`\`\`json
448
- {
449
- "projectId": "uuid",
450
- "title": "My Project",
451
- "projectUrl": "https://app.superdesign.ai/...",
452
- "shareToken": "token"
453
- }
454
- \`\`\`
443
+ ### A) Inspiration & Style Tools (generic, always available)
444
+ Use these to discover style direction, references, and brand context:
455
445
 
456
- ### Create Design Draft
446
+ - **Search prompt library** (style/components/pages)
447
+ \`\`\`bash
448
+ superdesign search-prompts --keyword "<keyword>" --json
449
+ superdesign search-prompts --tags "style" --json
450
+ superdesign search-prompts --tags "style" --keyword "<style keyword>" --json
451
+ \`\`\`
457
452
 
458
- Generate a design draft using AI designer:
453
+ - **Get full prompt details**
454
+ \`\`\`bash
455
+ superdesign get-prompts --slugs "<slug1,slug2,...>" --json
456
+ \`\`\`
459
457
 
460
- \`\`\`bash
461
- superdesign create-design-draft \\
462
- --project-id <project-id> \\
463
- --title "Hero Section" \\
464
- --prompt "Create a modern hero section with gradient background" \\
465
- --device desktop \\
466
- --json
467
- \`\`\`
458
+ - **Extract brand guide from a URL**
459
+ \`\`\`bash
460
+ superdesign extract-brand-guide --url https://example.com --json
461
+ \`\`\`
468
462
 
469
- **Options:**
470
- - \`--project-id\` (required): Project to add draft to
471
- - \`--title\` (required): Draft title
472
- - \`--prompt\` (required): AI generation prompt
473
- - \`--device\`: Device mode (mobile, tablet, desktop)
463
+ ### B) Canvas Design Tools (specialised, optional)
474
464
 
475
- **Output (JSON):**
476
- \`\`\`json
477
- {
478
- "draftId": "uuid",
479
- "nodeId": "uuid",
480
- "title": "Hero Section",
481
- "projectUrl": "https://app.superdesign.ai/...",
482
- "nodeUrl": "https://app.superdesign.ai/...?node=...",
483
- "previewUrl": "https://preview.superdesign.ai/...",
484
- "creditsConsumed": 1
485
- }
486
- \`\`\`
465
+ Use these when you want to explore multiple directions and show previews:
487
466
 
488
- ### Iterate Design Draft
467
+ - Create project (supports prompt / prompt file / HTML)
468
+ - Create design draft
469
+ - Iterate design draft (replace / branch)
470
+ - Plan flow pages → execute flow pages
471
+ - Fetch nodes, export HTML
489
472
 
490
- Create variations or improvements of an existing draft:
473
+ > Pixel-perfect HTML playground is ONLY required for Canvas workflows.
491
474
 
492
- \`\`\`bash
493
- # Replace mode - updates the existing draft
494
- superdesign iterate-design-draft \\
495
- --draft-id <draft-id> \\
496
- --prompt "Make the colors more vibrant" \\
497
- --mode replace \\
498
- --json
475
+ ---
499
476
 
500
- # Branch mode - creates new variations
501
- superdesign iterate-design-draft \\
502
- --draft-id <draft-id> \\
503
- --prompt "Try different color schemes" \\
504
- --mode branch \\
505
- --count 3 \\
506
- --json
507
- \`\`\`
477
+ ## Always-on rules
508
478
 
509
- **Options:**
510
- - \`--draft-id\` (required): Draft to iterate on
511
- - \`--prompt\` (required): Iteration instructions
512
- - \`--mode\` (required): "replace" or "branch"
513
- - \`--count\`: Number of variations (1-4, branch mode only)
479
+ - Design system should live at: \`.superdesign/design-system.md\`
480
+ - If \`.superdesign/design-system.md\` is missing, run **Design System Setup** first.
481
+ - Use \`askQuestion\` to ask high-signal questions (constraints, taste, tradeoffs).
482
+ - For Canvas workflows: build a simplified/pixel-perfect prototype HTML in:
483
+ - \`.superdesign/playgrounds/<name>.html\`
484
+ - Always use \`--json\` for machine parsing.
514
485
 
515
- **Output (JSON):**
516
- \`\`\`json
517
- {
518
- "drafts": [
519
- {
520
- "draftId": "uuid",
521
- "nodeId": "uuid",
522
- "title": "Variation 1",
523
- "nodeUrl": "https://...",
524
- "previewUrl": "https://..."
525
- }
526
- ],
527
- "projectUrl": "https://...",
528
- "creditsConsumed": 3
529
- }
530
- \`\`\`
486
+ ---
487
+
488
+ ## Playground rules (Canvas only)
489
+
490
+ **Playground = BEFORE state (what exists now).** It provides context for SuperDesign agent.
491
+ **Prompt = DESIRED CHANGE (what to add/modify).**
492
+
493
+ The playground HTML must contain **ONLY UI that currently exists in the codebase**. For NEW features/sections, describe them entirely in the \`--prompt\` flag — do NOT add wireframes, placeholders, or sketches to the playground HTML.
531
494
 
532
- ### Plan Flow Pages
495
+ - **DO NOT** design or improve anything in the playground
496
+ - **DO NOT** add placeholder sections like \`<!-- NEW FEATURE - DESIGN THIS -->\`
497
+ - **DO** create pixel-perfect replica of current UI state
498
+ - Valid formats:
499
+ - **Component-only**: isolated component with enough markup to show core UX/states
500
+ - **Full page**: replica of page where component operates (shows surrounding context)
501
+ - Save to: \`.superdesign/playgrounds/<name>.html\`
533
502
 
534
- Get AI suggestions for related pages in a user flow:
503
+ ### Example: Adding a "Book Demo" section to home page
535
504
 
505
+ **BAD approach:**
506
+ \`\`\`html
507
+ <!-- playground includes a sketched Book Demo section -->
508
+ <section class="book-demo">
509
+ <!-- DESIGN THIS - Add CTA here -->
510
+ <h3>Book a Demo</h3>
511
+ <button>Schedule</button>
512
+ </section>
513
+ \`\`\`
514
+
515
+ **GOOD approach:**
516
+ \`\`\`html
517
+ <!-- playground is pure replica of existing home page (hero + projects) -->
518
+ <!-- NO book demo section in HTML -->
519
+ \`\`\`
520
+ Then in the iterate command:
536
521
  \`\`\`bash
537
- superdesign plan-flow-pages \\
538
- --draft-id <draft-id> \\
539
- --source-node-id <node-id> \\
540
- --context "E-commerce checkout flow" \\
541
- --json
522
+ --prompt "Add a Book Demo section between the hero and projects sections. Requirements: minimal card style, horizontal layout..."
542
523
  \`\`\`
543
524
 
544
- **Options:**
545
- - \`--draft-id\` (required): Source draft
546
- - \`--source-node-id\` (required): Starting node in flow
547
- - \`--context\`: Additional context for planning
525
+ ---
548
526
 
549
- **Output (JSON):**
550
- \`\`\`json
551
- {
552
- "pages": [
553
- {
554
- "title": "Cart Review",
555
- "prompt": "Create a cart review page showing..."
556
- },
557
- {
558
- "title": "Payment",
559
- "prompt": "Create a payment form with..."
560
- }
561
- ],
562
- "creditsConsumed": 1
563
- }
564
- \`\`\`
527
+ # 1) Design System Setup
565
528
 
566
- ### Execute Flow Pages
529
+ ### Step 0 — Ask user (one question)
567
530
 
568
- Generate the planned flow pages:
531
+ "Do you want to **create a new design system** or **extract from the current codebase**?"
569
532
 
570
- \`\`\`bash
571
- superdesign execute-flow-pages \\
572
- --draft-id <draft-id> \\
573
- --source-node-id <node-id> \\
574
- --pages '[{"title":"Cart","prompt":"Create cart page"},{"title":"Payment","prompt":"Create payment form"}]' \\
575
- --json
576
- \`\`\`
533
+ ### A) Extract from codebase
577
534
 
578
- **Options:**
579
- - \`--draft-id\` (required): Source draft
580
- - \`--source-node-id\` (required): Starting node
581
- - \`--pages\` (required): JSON array of pages to generate
582
- - \`--context\`: Additional context
535
+ 1. Investigate codebase:
536
+ - design tokens, typography, colors, spacing, radius, shadows
537
+ - motion/animation patterns
538
+ - example components usage + implementation patterns
539
+ - core product functions + key pages
540
+ 2. Write standalone design system to:
541
+ - \`.superdesign/design-system.md\`
542
+ - Must be implementable without the codebase
583
543
 
584
- **Output (JSON):**
585
- \`\`\`json
586
- {
587
- "drafts": [
588
- {
589
- "index": 0,
590
- "draftId": "uuid",
591
- "nodeId": "uuid",
592
- "title": "Cart",
593
- "nodeUrl": "https://...",
594
- "previewUrl": "https://..."
595
- }
596
- ],
597
- "projectUrl": "https://...",
598
- "creditsConsumed": 2
599
- }
600
- \`\`\`
544
+ ### B) Create a new design system (to improve current UI)
601
545
 
602
- ## Workflow Examples
546
+ 1. Investigate codebase to understand product + needed pages/components
547
+ 2. Gather inspirations (generic tools):
548
+ - \`superdesign search-prompts --tags "style" --json\`
549
+ - \`superdesign get-prompts --slugs ... --json\`
550
+ - optional: \`superdesign extract-brand-guide --url ... --json\`
551
+ 3. Interview user (\`askQuestion\`) to choose direction
552
+ 4. Write:
553
+ - \`.superdesign/design-system.md\` (adapted to product + references)
603
554
 
604
- ### Create a new design from scratch
555
+ ---
605
556
 
606
- \`\`\`bash
607
- # 1. Create project
608
- PROJECT=$(superdesign create-project --title "Landing Page" --json)
609
- PROJECT_ID=$(echo $PROJECT | jq -r '.projectId')
557
+ # 2) Designing X (feature/page/flow)
610
558
 
611
- # 2. Generate initial design
612
- DRAFT=$(superdesign create-design-draft \\
613
- --project-id $PROJECT_ID \\
614
- --title "Hero" \\
615
- --prompt "Modern SaaS landing page hero with gradient, CTA button" \\
616
- --json)
559
+ ## A) If user wants references / direction only (no canvas)
617
560
 
618
- echo "Preview: $(echo $DRAFT | jq -r '.previewUrl')"
619
- \`\`\`
561
+ 1. Ensure \`.superdesign/design-system.md\` exists (setup if missing)
562
+ 2. Use inspiration tools to collect references:
563
+ - search prompts (style + component/page)
564
+ - get prompts (full details)
565
+ - extract brand guide if relevant
566
+ 3. Provide a concrete design spec:
567
+ - layout + hierarchy
568
+ - component set + states
569
+ - token guidance (typography/color/spacing/radius/motion)
570
+ - interaction notes + edge cases
571
+
572
+ ## B) If user wants visual exploration (canvas optional)
573
+
574
+ Use canvas when direction is uncertain or multiple options are needed.
575
+
576
+ ### Canvas workflow — Add feature to existing page
577
+
578
+ 1. Ensure \`.superdesign/design-system.md\` exists (setup if missing)
579
+ 2. Build prototype HTML playground (simplified + pixel-perfect enough):
580
+ - \`.superdesign/playgrounds/<page>-<feature>.html\`
581
+ 3. Ask targeted questions (\`askQuestion\`) about requirements + taste
582
+ 4. Create project with playground HTML (returns \`draftId\`):
583
+ \`\`\`bash
584
+ superdesign create-project \\
585
+ --title "<feature>" \\
586
+ --html-file .superdesign/playgrounds/<file>.html \\
587
+ --prompt-file .superdesign/design-system.md \\
588
+ --json
589
+ \`\`\`
590
+ → Note: \`draftId\` in response is the baseline draft
591
+ 5. Branch designs from baseline (use \`draftId\` from step 4):
592
+ \`\`\`bash
593
+ superdesign iterate-design-draft \\
594
+ --draft-id <draftId> \\
595
+ --prompt "<design requirements>" \\
596
+ --mode branch \\
597
+ --count 3 \\
598
+ --json
599
+ \`\`\`
600
+ 6. Share \`previewUrl\` → collect feedback → iterate
601
+
602
+ ### Canvas workflow — Add new page
620
603
 
621
- ### Iterate on existing design
604
+ Same as above, but playground should include shared UI shell:
605
+ - nav, layout container, spacing rhythm, shared components
606
+
607
+ ### Canvas workflow — Improve current UI
608
+
609
+ 1. Investigate product + current UI states
610
+ 2. Ensure design system exists
611
+ 3. Propose improvement options:
612
+ - big restructure
613
+ - style uplift
614
+ - low-hanging polish
615
+ 4. Pick one → build playground → create project (prompt-file design system) → branch drafts
616
+ 5. Share \`previewUrl\` → iterate
617
+
618
+ ### When to use create-design-draft
619
+
620
+ Only use \`create-design-draft\` when creating from scratch (no HTML baseline).
621
+
622
+ **Required in prompt:**
623
+ - Full page structure (header, sidebar, main content areas)
624
+ - Component environment/surroundings
625
+ - Layout context (what's above/below/beside)
622
626
 
623
627
  \`\`\`bash
624
- # Create variations
625
- superdesign iterate-design-draft \\
626
- --draft-id <draft-id> \\
627
- --prompt "Try a dark theme version" \\
628
- --mode branch \\
629
- --count 2 \\
628
+ superdesign create-design-draft \\
629
+ --project-id <id> \\
630
+ --title "Feature X" \\
631
+ --prompt "Page has: fixed header 64px, left sidebar 240px, main content area. Design a settings panel in the main content area with..." \\
632
+ --device desktop \\
630
633
  --json
631
634
  \`\`\`
632
635
 
633
- ### Build a complete user flow
636
+ ---
637
+
638
+ # Advanced usage
634
639
 
640
+ ### Plan + generate a full user journey
641
+
642
+ Plan:
635
643
  \`\`\`bash
636
- # 1. Plan the flow
637
- PLAN=$(superdesign plan-flow-pages \\
638
- --draft-id <draft-id> \\
639
- --source-node-id <node-id> \\
640
- --context "User onboarding flow" \\
641
- --json)
644
+ superdesign plan-flow-pages \\
645
+ --draft-id <draftId> \\
646
+ --source-node-id <nodeId> \\
647
+ --context "<flow context>" \\
648
+ --json
649
+ \`\`\`
642
650
 
643
- # 2. Execute the plan
644
- PAGES=$(echo $PLAN | jq -c '.pages')
651
+ Execute:
652
+ \`\`\`bash
645
653
  superdesign execute-flow-pages \\
646
- --draft-id <draft-id> \\
647
- --source-node-id <node-id> \\
648
- --pages "$PAGES" \\
654
+ --draft-id <draftId> \\
655
+ --source-node-id <nodeId> \\
656
+ --pages '[{"title":"Signup","prompt":"..."},{"title":"Payment","prompt":"..."}]' \\
649
657
  --json
650
658
  \`\`\`
651
659
 
660
+ ### Get HTML reference from a draft
661
+
662
+ \`\`\`bash
663
+ superdesign get-design --draft-id <draftId> --output ./design.html
664
+ \`\`\`
665
+
666
+ ---
667
+
668
+ ## Quick reference (key commands)
669
+
670
+ \`\`\`bash
671
+ # Inspirations
672
+ superdesign search-prompts --keyword "<keyword>" --json
673
+ superdesign search-prompts --tags "style" --json
674
+ superdesign get-prompts --slugs "<slug1,slug2>" --json
675
+ superdesign extract-brand-guide --url https://example.com --json
676
+
677
+ # Canvas
678
+ superdesign create-project --title "X" --prompt "..." --json
679
+ superdesign create-project --title "X" --prompt-file .superdesign/design-system.md --json
680
+ superdesign create-project --title "X" --html-file ./index.html --prompt-file .superdesign/design-system.md --json
652
681
 
653
- ## Tips
682
+ superdesign create-design-draft --project-id <id> --title "X" --prompt "..." --device desktop --json
683
+ superdesign iterate-design-draft --draft-id <id> --prompt "..." --mode replace --json
684
+ superdesign iterate-design-draft --draft-id <id> --prompt "..." --mode branch --count 3 --json
654
685
 
655
- - Always use \`--json\` flag when parsing output programmatically
656
- - Store project and draft IDs for subsequent operations
657
- - Use \`plan-flow-pages\` before \`execute-flow-pages\` for best results
658
- - The \`previewUrl\` can be used to view designs without authentication
686
+ superdesign fetch-design-nodes --project-id <id> --json
687
+ superdesign get-design --draft-id <id> --json
688
+ \`\`\`
659
689
  `;
660
690
  }
691
+ function ensureGitignoreEntry(gitignorePath, entry) {
692
+ if (existsSync(gitignorePath)) {
693
+ const content = readFileSync(gitignorePath, 'utf-8');
694
+ const hasEntry = content.split('\n').some((line)=>line.trim() === entry);
695
+ if (hasEntry) return false;
696
+ const prefix = content.endsWith('\n') ? '' : '\n';
697
+ appendFileSync(gitignorePath, `${prefix}${entry}\n`, 'utf-8');
698
+ return true;
699
+ }
700
+ writeFileSync(gitignorePath, `${entry}\n`, 'utf-8');
701
+ return true;
702
+ }
661
703
  function createInitCommand() {
662
704
  const command = new Command('init').description('Install SuperDesign skill files for Claude Code (auto-login if needed)').option('--json', 'Output in JSON format').option('--force', 'Overwrite existing skill files').action(async (options)=>{
663
705
  if (options.json) setJsonMode(true);
@@ -694,14 +736,19 @@ function createInitCommand() {
694
736
  });
695
737
  const skillContent = getSkillTemplate();
696
738
  writeFileSync(skillFilePath, skillContent, 'utf-8');
739
+ const gitignorePath = join(cwd, '.gitignore');
740
+ const gitignoreEntry = '.superdesign';
741
+ const gitignoreUpdated = ensureGitignoreEntry(gitignorePath, gitignoreEntry);
697
742
  if (isJsonMode()) output({
698
743
  success: true,
699
744
  path: skillFilePath,
745
+ gitignoreUpdated,
700
746
  message: 'Skill files installed successfully'
701
747
  });
702
748
  else {
703
749
  success('Skill files installed successfully!');
704
750
  info(`\nSkill file created at: ${skillFilePath}`);
751
+ if (gitignoreUpdated) info(".gitignore updated to exclude .superdesign directory");
705
752
  info('\nClaude Code will now have access to SuperDesign commands.');
706
753
  info('Available commands:');
707
754
  info(' - superdesign create-project');
@@ -709,6 +756,7 @@ function createInitCommand() {
709
756
  info(' - superdesign iterate-design-draft');
710
757
  info(' - superdesign plan-flow-pages');
711
758
  info(' - superdesign execute-flow-pages');
759
+ info(' - superdesign extract-brand-guide');
712
760
  }
713
761
  } catch (err) {
714
762
  const message = err instanceof Error ? err.message : 'Unknown error';
@@ -729,7 +777,7 @@ async function addDraft(projectId, data) {
729
777
  return response.data;
730
778
  }
731
779
  function createCreateProjectCommand() {
732
- const command = new Command('create-project').description('Create a new SuperDesign project').requiredOption('--title <title>', 'Project title').option('--html <html>', 'Initial HTML content for first draft').option('--html-file <path>', 'Path to HTML file for first draft').option('--device <mode>', 'Device mode for draft (mobile, tablet, desktop)', 'desktop').option('--json', 'Output in JSON format').action(async (options)=>{
780
+ const command = new Command('create-project').description('Create a new SuperDesign project').requiredOption('--title <title>', 'Project title').option('--html <html>', 'Initial HTML content for first draft').option('--html-file <path>', 'Path to HTML file for first draft').option('--prompt <prompt>', 'System prompt for the AI agent').option('--prompt-file <path>', 'Path to markdown file containing system prompt').option('--device <mode>', 'Device mode for draft (mobile, tablet, desktop)', 'desktop').option('--json', 'Output in JSON format').option('--no-open', 'Do not open project in browser after creation').action(async (options)=>{
733
781
  if (options.json) setJsonMode(true);
734
782
  try {
735
783
  if (!manager_isAuthenticated()) {
@@ -752,40 +800,43 @@ function createCreateProjectCommand() {
752
800
  }
753
801
  htmlContent = readFileSync(options.htmlFile, 'utf-8');
754
802
  } else if (options.html) htmlContent = options.html;
803
+ let promptContent;
804
+ if (options.promptFile) {
805
+ if (!existsSync(options.promptFile)) {
806
+ output_error(`Prompt file not found: ${options.promptFile}`);
807
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
808
+ }
809
+ promptContent = readFileSync(options.promptFile, 'utf-8');
810
+ } else if (options.prompt) promptContent = options.prompt;
755
811
  startSpinner('Creating project...');
756
812
  const project = await createProject({
757
- title: options.title
758
- });
759
- let draftResult = null;
760
- if (htmlContent) draftResult = await addDraft(project.projectId, {
761
- title: 'Initial Draft',
813
+ title: options.title,
762
814
  html: htmlContent,
815
+ prompt: promptContent,
763
816
  deviceMode: options.device
764
817
  });
765
818
  succeedSpinner('Project created successfully!');
766
- const result = {
819
+ if (options.open) {
820
+ const urlWithLive = `${project.projectUrl}?live=1`;
821
+ await openBrowser(urlWithLive);
822
+ }
823
+ if (isJsonMode()) output({
767
824
  projectId: project.projectId,
768
825
  title: project.title,
769
826
  projectUrl: project.projectUrl,
770
827
  shareToken: project.shareToken,
771
- ...draftResult && {
772
- draft: {
773
- draftId: draftResult.draftId,
774
- nodeId: draftResult.nodeId,
775
- nodeUrl: draftResult.nodeUrl,
776
- previewUrl: draftResult.previewUrl
777
- }
828
+ ...project.draftId && {
829
+ draftId: project.draftId,
830
+ previewUrl: project.previewUrl
778
831
  }
779
- };
780
- if (isJsonMode()) output(result);
832
+ });
781
833
  else {
782
834
  success(`Project "${project.title}" created!`);
783
835
  info(`\nProject ID: ${project.projectId}`);
784
836
  info(`Project URL: ${project.projectUrl}`);
785
- if (draftResult) {
786
- info(`\nDraft ID: ${draftResult.draftId}`);
787
- info(`Draft URL: ${draftResult.nodeUrl}`);
788
- info(`Preview URL: ${draftResult.previewUrl}`);
837
+ if (project.draftId) {
838
+ info(`\nDraft ID: ${project.draftId}`);
839
+ info(`Preview URL: ${project.previewUrl}`);
789
840
  }
790
841
  }
791
842
  } catch (err) {
@@ -894,6 +945,22 @@ function job_runner_requireAuth(isAuthenticated) {
894
945
  process.exit(EXIT_CODES.AUTH_REQUIRED);
895
946
  }
896
947
  }
948
+ function handleApiError(err, failureMessage) {
949
+ if (err instanceof ApiClientError) {
950
+ if (isJsonMode()) output({
951
+ error: err.message,
952
+ code: err.code
953
+ });
954
+ else output_error(`API Error: ${err.message}`);
955
+ process.exit(EXIT_CODES.API_ERROR);
956
+ }
957
+ const message = err instanceof Error ? err.message : 'Unknown error';
958
+ if (isJsonMode()) output({
959
+ error: message
960
+ });
961
+ else output_error(`${failureMessage}: ${message}`);
962
+ process.exit(EXIT_CODES.GENERAL_ERROR);
963
+ }
897
964
  function createCreateDesignDraftCommand() {
898
965
  const command = new Command('create-design-draft').description('Create a design draft using AI generation').requiredOption('--project-id <id>', 'Project ID').requiredOption('--title <title>', 'Draft title').requiredOption('--prompt <prompt>', 'Design prompt for AI generation').option('--device <mode>', 'Device mode (mobile, tablet, desktop)', 'desktop').option('--json', 'Output in JSON format').action(async (options)=>{
899
966
  if (options.json) setJsonMode(true);
@@ -1081,6 +1148,260 @@ function createExecuteFlowPagesCommand() {
1081
1148
  });
1082
1149
  return command;
1083
1150
  }
1151
+ async function extractBrandGuide(url) {
1152
+ const client = getApiClient();
1153
+ const response = await client.post('/external/brand/extract', {
1154
+ url
1155
+ });
1156
+ return response.data;
1157
+ }
1158
+ function extractDomain(url) {
1159
+ try {
1160
+ const parsed = new URL(url);
1161
+ return parsed.hostname.replace(/^www\./, '');
1162
+ } catch {
1163
+ return url.replace(/[^a-zA-Z0-9.-]/g, '_');
1164
+ }
1165
+ }
1166
+ async function downloadFile(url, outputPath) {
1167
+ try {
1168
+ const response = await fetch(url);
1169
+ if (!response.ok) return false;
1170
+ const buffer = Buffer.from(await response.arrayBuffer());
1171
+ writeFileSync(outputPath, buffer);
1172
+ return true;
1173
+ } catch {
1174
+ return false;
1175
+ }
1176
+ }
1177
+ function createExtractBrandGuideCommand() {
1178
+ const command = new Command('extract-brand-guide').description('Extract brand guidelines from a website URL').requiredOption('--url <url>', 'Website URL to extract brand guide from').option('--output-dir <path>', 'Output directory (default: .superdesign/brand_styles/<domain>)').option('--json', 'Output in JSON format').action(async (options)=>{
1179
+ if (options.json) setJsonMode(true);
1180
+ job_runner_requireAuth(manager_isAuthenticated);
1181
+ const spinner = isJsonMode() ? null : ora('Extracting brand guide...').start();
1182
+ try {
1183
+ const result = await extractBrandGuide(options.url);
1184
+ if (!result.success) {
1185
+ if (spinner) spinner.fail('Failed to extract brand guide');
1186
+ if (isJsonMode()) output({
1187
+ success: false,
1188
+ error: result.error || 'Failed to extract brand guide'
1189
+ });
1190
+ else output_error(result.error || 'Failed to extract brand guide');
1191
+ process.exit(EXIT_CODES.API_ERROR);
1192
+ }
1193
+ if (spinner) spinner.succeed('Brand guide extracted successfully');
1194
+ const domain = extractDomain(options.url);
1195
+ const cwd = process.cwd();
1196
+ const outputDir = options.outputDir || join(cwd, '.superdesign', 'brand_styles', domain);
1197
+ mkdirSync(outputDir, {
1198
+ recursive: true
1199
+ });
1200
+ const jsonPath = join(outputDir, 'brand-guide.json');
1201
+ writeFileSync(jsonPath, JSON.stringify(result, null, 2), 'utf-8');
1202
+ let screenshotSaved = false;
1203
+ if (result.data?.screenshot?.url) {
1204
+ const screenshotPath = join(outputDir, 'screenshot.png');
1205
+ const downloadSpinner = isJsonMode() ? null : ora('Downloading screenshot...').start();
1206
+ screenshotSaved = await downloadFile(result.data.screenshot.url, screenshotPath);
1207
+ if (downloadSpinner) if (screenshotSaved) downloadSpinner.succeed('Screenshot downloaded');
1208
+ else downloadSpinner.warn('Could not download screenshot');
1209
+ }
1210
+ if (isJsonMode()) output({
1211
+ success: true,
1212
+ outputDir,
1213
+ files: {
1214
+ brandGuide: jsonPath,
1215
+ screenshot: screenshotSaved ? join(outputDir, 'screenshot.png') : null
1216
+ },
1217
+ data: result.data
1218
+ });
1219
+ else {
1220
+ success('Brand guide saved!');
1221
+ if (screenshotSaved) info(`\nScreenshot: ${join(outputDir, 'screenshot.png')}`);
1222
+ info(`Extracted brand style JSON: ${jsonPath}`);
1223
+ }
1224
+ } catch (err) {
1225
+ if (spinner) spinner.fail('Failed to extract brand guide');
1226
+ const message = err instanceof Error ? err.message : 'Unknown error';
1227
+ if (isJsonMode()) output({
1228
+ success: false,
1229
+ error: message
1230
+ });
1231
+ else output_error(`Failed to extract brand guide: ${message}`);
1232
+ process.exit(EXIT_CODES.API_ERROR);
1233
+ }
1234
+ });
1235
+ return command;
1236
+ }
1237
+ async function searchPrompts(data) {
1238
+ const client = getApiClient();
1239
+ const response = await client.post('/external/prompts/search', data);
1240
+ return response.data;
1241
+ }
1242
+ async function getPrompts(data) {
1243
+ const client = getApiClient();
1244
+ const response = await client.post('/external/prompts/get', data);
1245
+ return response.data;
1246
+ }
1247
+ function createSearchPromptsCommand() {
1248
+ const command = new Command('search-prompts').description('Search public prompts in the library').option('--keyword <text>', "Search keyword (searches title and description)").option('--tags <tags>', 'Comma-separated tags to filter by').option('--limit <n>', 'Maximum number of results (default: 20, max: 100)').option('--offset <n>', 'Number of results to skip (for pagination)').option('--json', 'Output in JSON format').action(async (options)=>{
1249
+ if (options.json) setJsonMode(true);
1250
+ job_runner_requireAuth(manager_isAuthenticated);
1251
+ const spinner = isJsonMode() ? null : ora('Searching prompts...').start();
1252
+ try {
1253
+ const tags = options.tags ? options.tags.split(',').map((t)=>t.trim()) : void 0;
1254
+ const limit = options.limit ? parseInt(options.limit, 10) : 20;
1255
+ const offset = options.offset ? parseInt(options.offset, 10) : 0;
1256
+ const result = await searchPrompts({
1257
+ keyword: options.keyword,
1258
+ tags,
1259
+ limit,
1260
+ offset
1261
+ });
1262
+ if (spinner) spinner.succeed(`Found ${result.prompts.length} prompts (total: ${result.total})`);
1263
+ if (isJsonMode()) output(result);
1264
+ else if (0 === result.prompts.length) info('\nNo prompts found matching your criteria.');
1265
+ else {
1266
+ success(`\nFound ${result.prompts.length} prompts:\n`);
1267
+ result.prompts.forEach((prompt, index)=>{
1268
+ const tagsStr = prompt.tags.length > 0 ? ` [${prompt.tags.join(', ')}]` : '';
1269
+ console.log(`${index + 1}. "${prompt.title}"${tagsStr}`);
1270
+ if (prompt.description) {
1271
+ const desc = prompt.description.length > 100 ? prompt.description.substring(0, 97) + '...' : prompt.description;
1272
+ console.log(` ${desc}`);
1273
+ }
1274
+ console.log(` Views: ${prompt.viewCount} | Remixes: ${prompt.remixCount}`);
1275
+ if (prompt.previewUrl) console.log(` Preview: ${prompt.previewUrl}`);
1276
+ console.log(` Slug: ${prompt.slug}`);
1277
+ console.log('');
1278
+ });
1279
+ if (result.total > result.prompts.length) info(`Showing ${result.prompts.length} of ${result.total} results. Use --offset to paginate.`);
1280
+ }
1281
+ } catch (err) {
1282
+ if (spinner) spinner.fail('Failed to search prompts');
1283
+ handleApiError(err, 'Failed to search prompts');
1284
+ }
1285
+ });
1286
+ return command;
1287
+ }
1288
+ function createGetPromptsCommand() {
1289
+ const command = new Command('get-prompts').description('Get prompts by their slugs').requiredOption('--slugs <slugs>', 'Comma-separated list of prompt slugs').option('--json', 'Output in JSON format').action(async (options)=>{
1290
+ if (options.json) setJsonMode(true);
1291
+ job_runner_requireAuth(manager_isAuthenticated);
1292
+ const slugs = options.slugs.split(',').map((s)=>s.trim());
1293
+ if (0 === slugs.length) {
1294
+ output_error('At least one slug is required');
1295
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
1296
+ }
1297
+ if (slugs.length > 20) {
1298
+ output_error('Maximum 20 slugs allowed per request');
1299
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
1300
+ }
1301
+ const spinner = isJsonMode() ? null : ora(`Fetching ${slugs.length} prompt(s)...`).start();
1302
+ try {
1303
+ const result = await getPrompts({
1304
+ slugs
1305
+ });
1306
+ if (spinner) spinner.succeed(`Retrieved ${result.prompts.length} of ${slugs.length} prompts`);
1307
+ if (isJsonMode()) output(result);
1308
+ else if (0 === result.prompts.length) info('\nNo prompts found for the provided slugs.');
1309
+ else {
1310
+ result.prompts.forEach((prompt, index)=>{
1311
+ if (index > 0) console.log('\n' + '='.repeat(60) + '\n');
1312
+ success(`Prompt: "${prompt.title}"`);
1313
+ info(`Slug: ${prompt.slug}`);
1314
+ if (prompt.tags.length > 0) info(`Tags: ${prompt.tags.join(', ')}`);
1315
+ if (prompt.description) console.log(`\nDescription: ${prompt.description}`);
1316
+ console.log('\n---');
1317
+ console.log(prompt.prompt);
1318
+ console.log('---\n');
1319
+ console.log(`Views: ${prompt.viewCount} | Remixes: ${prompt.remixCount}`);
1320
+ if (prompt.previewUrl) info(`Preview: ${prompt.previewUrl}`);
1321
+ if (prompt.images && prompt.images.length > 0) info(`Images: ${prompt.images.length} attached`);
1322
+ });
1323
+ const foundSlugs = result.prompts.map((p)=>p.slug);
1324
+ const missingSlugs = slugs.filter((s)=>!foundSlugs.includes(s));
1325
+ if (missingSlugs.length > 0) {
1326
+ console.log('');
1327
+ info(`Note: ${missingSlugs.length} slug(s) not found: ${missingSlugs.join(', ')}`);
1328
+ }
1329
+ }
1330
+ } catch (err) {
1331
+ if (spinner) spinner.fail('Failed to get prompts');
1332
+ handleApiError(err, 'Failed to get prompts');
1333
+ }
1334
+ });
1335
+ return command;
1336
+ }
1337
+ async function fetchDesignNodes(projectId) {
1338
+ const client = getApiClient();
1339
+ const response = await client.get(`/external/projects/${projectId}/design-nodes`);
1340
+ return response.data;
1341
+ }
1342
+ async function getDesignHtml(draftId) {
1343
+ const client = getApiClient();
1344
+ const response = await client.get(`/external/drafts/${draftId}/html`);
1345
+ return response.data;
1346
+ }
1347
+ function displayNode(node, index) {
1348
+ console.log(`${index + 1}. "${node.title}" (depth: ${node.iterationDepth})`);
1349
+ info(node.parentDraftId ? ` Parent: ${node.parentDraftId}` : ' Root draft');
1350
+ if (node.description) console.log(` ${node.description}`);
1351
+ info(` Preview: ${node.previewUrl}`);
1352
+ if (node.screenshot) info(` Screenshot: ${node.screenshot.url}`);
1353
+ console.log(` Created: ${node.createdAt}\n`);
1354
+ }
1355
+ function createFetchDesignNodesCommand() {
1356
+ const command = new Command('fetch-design-nodes').description('Get all design draft nodes for a project').requiredOption('--project-id <projectId>', 'Project ID to fetch nodes from').option('--json', 'Output in JSON format').action(async (options)=>{
1357
+ if (options.json) setJsonMode(true);
1358
+ job_runner_requireAuth(manager_isAuthenticated);
1359
+ const spinner = isJsonMode() ? null : ora('Fetching design nodes...').start();
1360
+ try {
1361
+ const result = await fetchDesignNodes(options.projectId);
1362
+ if (spinner) spinner.succeed(`Found ${result.nodes.length} design node(s)`);
1363
+ if (isJsonMode()) return void output(result);
1364
+ if (0 === result.nodes.length) return void info('\nNo design nodes found in this project.');
1365
+ console.log(`\nFound ${result.nodes.length} design node(s):\n`);
1366
+ result.nodes.forEach(displayNode);
1367
+ } catch (err) {
1368
+ if (spinner) spinner.fail('Failed to fetch design nodes');
1369
+ handleApiError(err, 'Failed to fetch design nodes');
1370
+ }
1371
+ });
1372
+ return command;
1373
+ }
1374
+ async function writeHtmlToFile(result, outputPath) {
1375
+ const resolvedPath = external_path_resolve(process.cwd(), outputPath);
1376
+ await writeFile(resolvedPath, result.htmlContent, 'utf-8');
1377
+ if (isJsonMode()) output({
1378
+ ...result,
1379
+ savedTo: resolvedPath
1380
+ });
1381
+ else {
1382
+ success(`HTML written to: ${resolvedPath}`);
1383
+ info(`Title: ${result.title}`);
1384
+ info(`Draft ID: ${result.draftId}`);
1385
+ }
1386
+ }
1387
+ function createGetDesignCommand() {
1388
+ const command = new Command('get-design').description('Get HTML content for a specific draft').requiredOption('--draft-id <draftId>', 'Draft ID to fetch').option('--json', 'Output in JSON format').option('--output <path>', 'Write HTML to file instead of stdout').action(async (options)=>{
1389
+ if (options.json) setJsonMode(true);
1390
+ job_runner_requireAuth(manager_isAuthenticated);
1391
+ const spinner = isJsonMode() ? null : ora('Fetching design HTML...').start();
1392
+ try {
1393
+ const result = await getDesignHtml(options.draftId);
1394
+ if (spinner) spinner.succeed(`Retrieved design: "${result.title}"`);
1395
+ if (options.output) return void await writeHtmlToFile(result, options.output);
1396
+ if (isJsonMode()) return void output(result);
1397
+ console.log(result.htmlContent);
1398
+ } catch (err) {
1399
+ if (spinner) spinner.fail('Failed to get design HTML');
1400
+ handleApiError(err, 'Failed to get design HTML');
1401
+ }
1402
+ });
1403
+ return command;
1404
+ }
1084
1405
  const src_filename = fileURLToPath(import.meta.url);
1085
1406
  const src_dirname = dirname(src_filename);
1086
1407
  external_dotenv_config({
@@ -1089,7 +1410,7 @@ external_dotenv_config({
1089
1410
  external_dotenv_config();
1090
1411
  function createProgram() {
1091
1412
  const program = new Command();
1092
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.0");
1413
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.2");
1093
1414
  program.addCommand(createLoginCommand());
1094
1415
  program.addCommand(createLogoutCommand());
1095
1416
  program.addCommand(createInitCommand());
@@ -1098,10 +1419,15 @@ function createProgram() {
1098
1419
  program.addCommand(createIterateDesignDraftCommand());
1099
1420
  program.addCommand(createPlanFlowPagesCommand());
1100
1421
  program.addCommand(createExecuteFlowPagesCommand());
1422
+ program.addCommand(createExtractBrandGuideCommand());
1423
+ program.addCommand(createSearchPromptsCommand());
1424
+ program.addCommand(createGetPromptsCommand());
1425
+ program.addCommand(createFetchDesignNodesCommand());
1426
+ program.addCommand(createGetDesignCommand());
1101
1427
  return program;
1102
1428
  }
1103
1429
  async function run() {
1104
1430
  const program = createProgram();
1105
1431
  await program.parseAsync(process.argv);
1106
1432
  }
1107
- export { ApiClientError, addDraft, claimSession, clearConfig, createApiClient, createDraft, createProgram, createProject, createPublicApiClient, createSession, executeFlowPages, getApiClient, getApiKey, getApiUrl, getConfigDir, getConfigPath, getJobStatus, manager_isAuthenticated as isAuthenticated, isJobCompleted, isJobDone, isJobFailed, iterateDraft, loadConfig, planFlowPages, pollSession, run, saveConfig, updateConfig };
1433
+ export { ApiClientError, addDraft, claimSession, clearConfig, createApiClient, createDraft, createProgram, createProject, createPublicApiClient, createSession, executeFlowPages, extractBrandGuide, fetchDesignNodes, getApiClient, getApiKey, getApiUrl, getConfigDir, getConfigPath, getDesignHtml, getJobStatus, getPrompts, manager_isAuthenticated as isAuthenticated, isJobCompleted, isJobDone, isJobFailed, iterateDraft, loadConfig, planFlowPages, pollSession, run, saveConfig, searchPrompts, updateConfig };