@superdesign/cli 0.1.4 → 0.1.6

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
@@ -3,10 +3,11 @@ import { fileURLToPath } from "url";
3
3
  import { dirname, join, resolve as external_path_resolve } from "path";
4
4
  import { Command } from "commander";
5
5
  import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
6
- import { homedir, hostname, platform, release } from "os";
6
+ import os, { homedir, hostname, platform, release } from "os";
7
7
  import axios from "axios";
8
8
  import ora from "ora";
9
9
  import { writeFile } from "fs/promises";
10
+ import { PostHog } from "posthog-node";
10
11
  const CONFIG_DIR_NAME = '.superdesign';
11
12
  const CONFIG_FILE_NAME = 'config.json';
12
13
  const EXIT_CODES = {
@@ -20,6 +21,8 @@ const EXIT_CODES = {
20
21
  };
21
22
  const SKILLS_DIR = '.claude/skills/superdesign';
22
23
  const SKILL_FILE_NAME = 'SKILL.md';
24
+ const POSTHOG_KEY = process.env.POSTHOG_KEY || 'phc_oUcDklFBX3wy8eksSyEC0pataKCgSadur1sio5hBHg4';
25
+ const POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://eu.i.posthog.com';
23
26
  function getConfigDir() {
24
27
  return join(homedir(), CONFIG_DIR_NAME);
25
28
  }
@@ -223,7 +226,7 @@ async function runAuthFlow(options = {}) {
223
226
  try {
224
227
  startSpinner('Creating auth session...');
225
228
  const session = await createSession({
226
- cliVersion: "0.1.4",
229
+ cliVersion: "0.1.6",
227
230
  os: `${platform()} ${release()}`,
228
231
  hostname: hostname()
229
232
  });
@@ -422,269 +425,35 @@ function getSkillTemplate() {
422
425
  return `---
423
426
  name: superdesign
424
427
  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
428
+ metadata:
429
+ author: superdesign
430
+ version: "0.0.1"
425
431
  ---
426
432
 
427
- SuperDesign helps you (1) find design inspirations/styles and (2) optionally generate/iterate design drafts on an infinite canvas.
428
-
429
- Each SuperDesign canvas run creates a new node and returns a \`previewUrl\`.
433
+ SuperDesign helps you (1) find design inspirations/styles and (2) generate/iterate design drafts on an infinite canvas.
430
434
 
431
435
  ---
432
436
 
433
- ## Core scenarios (what this skill handles)
437
+ # Core scenarios (what this skill handles)
434
438
 
435
439
  1. **Help me design X** (feature/page/flow)
436
440
  2. **Set design system**
437
441
  3. **Help me improve design of X**
438
442
 
439
- ---
440
-
441
- ## Tooling overview
442
-
443
- ### A) Inspiration & Style Tools (generic, always available)
444
- Use these to discover style direction, references, and brand context:
445
-
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
- \`\`\`
452
-
453
- - **Get full prompt details**
454
- \`\`\`bash
455
- superdesign get-prompts --slugs "<slug1,slug2,...>" --json
456
- \`\`\`
457
-
458
- - **Extract brand guide from a URL**
459
- \`\`\`bash
460
- superdesign extract-brand-guide --url https://example.com --json
461
- \`\`\`
462
-
463
- ### B) Canvas Design Tools (specialised, optional)
464
-
465
- Use these when you want to explore multiple directions and show previews:
466
-
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
472
-
473
- > Pixel-perfect HTML playground is ONLY required for Canvas workflows.
474
-
475
- ---
476
-
477
- ## Always-on rules
478
-
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.
485
-
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.
494
-
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\`
502
-
503
- ### Example: Adding a "Book Demo" section to home page
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:
521
- \`\`\`bash
522
- --prompt "Add a Book Demo section between the hero and projects sections. Requirements: minimal card style, horizontal layout..."
523
- \`\`\`
524
-
525
- ---
526
-
527
- # 1) Design System Setup
528
-
529
- ### Step 0 — Ask user (one question)
530
-
531
- "Do you want to **create a new design system** or **extract from the current codebase**?"
532
-
533
- ### A) Extract from codebase
534
-
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
543
-
544
- ### B) Create a new design system (to improve current UI)
545
-
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)
554
-
555
- ---
556
-
557
- # 2) Designing X (feature/page/flow)
558
-
559
- ## A) If user wants references / direction only (no canvas)
560
-
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
603
-
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)
626
-
627
- \`\`\`bash
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 \\
633
- --json
443
+ # How it works
444
+ Fetch fresh guidelines below:
634
445
  \`\`\`
635
-
636
- ---
637
-
638
- # Advanced usage
639
-
640
- ### Plan + generate a full user journey
641
-
642
- Plan:
643
- \`\`\`bash
644
- superdesign plan-flow-pages \\
645
- --draft-id <draftId> \\
646
- --source-node-id <nodeId> \\
647
- --context "<flow context>" \\
648
- --json
446
+ https://raw.githubusercontent.com/superdesigndev/superdesign-skill/main/skills/superdesign/SUPERDESIGN.md
649
447
  \`\`\`
650
448
 
651
- Execute:
652
- \`\`\`bash
653
- superdesign execute-flow-pages \\
654
- --draft-id <draftId> \\
655
- --source-node-id <nodeId> \\
656
- --pages '[{"title":"Signup","prompt":"..."},{"title":"Payment","prompt":"..."}]' \\
657
- --json
658
- \`\`\`
449
+ Action accordingly based on instruction in the guidelines
659
450
 
660
- ### Get HTML reference from a draft
451
+ # Superdesign CLI
661
452
 
662
- \`\`\`bash
663
- superdesign get-design --draft-id <draftId> --output ./design.html
664
453
  \`\`\`
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
681
-
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
685
-
686
- superdesign fetch-design-nodes --project-id <id> --json
687
- superdesign get-design --draft-id <id> --json
454
+ npm install -g @superdesign/cli@latest
455
+ superdesign init
456
+ superdesign --help
688
457
  \`\`\`
689
458
  `;
690
459
  }
@@ -776,102 +545,6 @@ async function addDraft(projectId, data) {
776
545
  const response = await client.post(`/external/projects/${projectId}/drafts/add`, data);
777
546
  return response.data;
778
547
  }
779
- function createCreateProjectCommand() {
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)=>{
781
- if (options.json) setJsonMode(true);
782
- try {
783
- if (!manager_isAuthenticated()) {
784
- output_error('Not authenticated. Run `superdesign login` first.');
785
- process.exit(EXIT_CODES.AUTH_REQUIRED);
786
- }
787
- if (options.device && ![
788
- 'mobile',
789
- 'tablet',
790
- 'desktop'
791
- ].includes(options.device)) {
792
- output_error('Invalid device mode. Must be: mobile, tablet, or desktop');
793
- process.exit(EXIT_CODES.VALIDATION_ERROR);
794
- }
795
- let htmlContent;
796
- if (options.htmlFile) {
797
- if (!existsSync(options.htmlFile)) {
798
- output_error(`HTML file not found: ${options.htmlFile}`);
799
- process.exit(EXIT_CODES.VALIDATION_ERROR);
800
- }
801
- htmlContent = readFileSync(options.htmlFile, 'utf-8');
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;
811
- startSpinner('Creating project...');
812
- const project = await createProject({
813
- title: options.title,
814
- html: htmlContent,
815
- prompt: promptContent,
816
- deviceMode: options.device
817
- });
818
- succeedSpinner('Project created successfully!');
819
- if (options.open) {
820
- const urlWithLive = `${project.projectUrl}?live=1`;
821
- await openBrowser(urlWithLive);
822
- }
823
- if (isJsonMode()) output({
824
- projectId: project.projectId,
825
- title: project.title,
826
- projectUrl: project.projectUrl,
827
- shareToken: project.shareToken,
828
- ...project.draftId && {
829
- draftId: project.draftId,
830
- previewUrl: project.previewUrl
831
- }
832
- });
833
- else {
834
- success(`Project "${project.title}" created!`);
835
- info(`\nProject ID: ${project.projectId}`);
836
- info(`Project URL: ${project.projectUrl}`);
837
- if (project.draftId) {
838
- info(`\nDraft ID: ${project.draftId}`);
839
- info(`Preview URL: ${project.previewUrl}`);
840
- }
841
- }
842
- } catch (err) {
843
- failSpinner('Failed to create project');
844
- if (err instanceof ApiClientError) {
845
- output_error(`API Error: ${err.message}`);
846
- process.exit(EXIT_CODES.API_ERROR);
847
- }
848
- const message = err instanceof Error ? err.message : 'Unknown error';
849
- output_error(message);
850
- process.exit(EXIT_CODES.GENERAL_ERROR);
851
- }
852
- });
853
- return command;
854
- }
855
- async function createDraft(projectId, data) {
856
- const client = getApiClient();
857
- const response = await client.post(`/external/projects/${projectId}/drafts/create`, data);
858
- return response.data;
859
- }
860
- async function iterateDraft(draftId, data) {
861
- const client = getApiClient();
862
- const response = await client.post(`/external/drafts/${draftId}/iterate`, data);
863
- return response.data;
864
- }
865
- async function planFlowPages(draftId, data) {
866
- const client = getApiClient();
867
- const response = await client.post(`/external/drafts/${draftId}/flow/plan`, data);
868
- return response.data;
869
- }
870
- async function executeFlowPages(draftId, data) {
871
- const client = getApiClient();
872
- const response = await client.post(`/external/drafts/${draftId}/flow/execute`, data);
873
- return response.data;
874
- }
875
548
  async function getJobStatus(jobId) {
876
549
  const client = getApiClient();
877
550
  const response = await client.get(`/external/jobs/${jobId}`);
@@ -961,18 +634,98 @@ function handleApiError(err, failureMessage) {
961
634
  else output_error(`${failureMessage}: ${message}`);
962
635
  process.exit(EXIT_CODES.GENERAL_ERROR);
963
636
  }
964
- function createCreateDesignDraftCommand() {
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)=>{
637
+ const VALID_DEVICES = [
638
+ 'mobile',
639
+ 'tablet',
640
+ 'desktop'
641
+ ];
642
+ function validateDeviceMode(device) {
643
+ if (device && !VALID_DEVICES.includes(device)) {
644
+ output_error('Invalid device mode. Must be: mobile, tablet, or desktop');
645
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
646
+ }
647
+ }
648
+ function readFileContent(filePath, inlineContent, fileType) {
649
+ if (filePath) {
650
+ if (!existsSync(filePath)) {
651
+ output_error(`${fileType} file not found: ${filePath}`);
652
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
653
+ }
654
+ return readFileSync(filePath, 'utf-8');
655
+ }
656
+ return inlineContent;
657
+ }
658
+ function createCreateProjectCommand() {
659
+ 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('-s, --set-project-prompt <prompt>', 'System prompt for the AI agent').option('--set-project-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)=>{
966
660
  if (options.json) setJsonMode(true);
967
661
  job_runner_requireAuth(manager_isAuthenticated);
968
- if (options.device && ![
969
- 'mobile',
970
- 'tablet',
971
- 'desktop'
972
- ].includes(options.device)) {
973
- output_error('Invalid device mode. Must be: mobile, tablet, or desktop');
974
- process.exit(EXIT_CODES.VALIDATION_ERROR);
662
+ validateDeviceMode(options.device);
663
+ const htmlContent = readFileContent(options.htmlFile, options.html, 'HTML');
664
+ const promptContent = readFileContent(options.setProjectPromptFile, options.setProjectPrompt, 'Prompt');
665
+ startSpinner('Creating project...');
666
+ try {
667
+ const project = await createProject({
668
+ title: options.title,
669
+ html: htmlContent,
670
+ prompt: promptContent,
671
+ deviceMode: options.device
672
+ });
673
+ succeedSpinner('Project created successfully!');
674
+ if (options.open) {
675
+ const urlWithLive = `${project.projectUrl}?live=1`;
676
+ await openBrowser(urlWithLive);
677
+ }
678
+ if (isJsonMode()) output({
679
+ projectId: project.projectId,
680
+ title: project.title,
681
+ projectUrl: project.projectUrl,
682
+ shareToken: project.shareToken,
683
+ ...project.draftId && {
684
+ draftId: project.draftId,
685
+ previewUrl: project.previewUrl
686
+ }
687
+ });
688
+ else {
689
+ success(`Project "${project.title}" created!`);
690
+ info(`\nProject ID: ${project.projectId}`);
691
+ info(`Project URL: ${project.projectUrl}`);
692
+ if (project.draftId) {
693
+ info(`\nDraft ID: ${project.draftId}`);
694
+ info(`Preview URL: ${project.previewUrl}`);
695
+ }
696
+ }
697
+ } catch (err) {
698
+ failSpinner('Failed to create project');
699
+ handleApiError(err, 'Failed to create project');
975
700
  }
701
+ });
702
+ return command;
703
+ }
704
+ async function createDraft(projectId, data) {
705
+ const client = getApiClient();
706
+ const response = await client.post(`/external/projects/${projectId}/drafts/create`, data);
707
+ return response.data;
708
+ }
709
+ async function iterateDraft(draftId, data) {
710
+ const client = getApiClient();
711
+ const response = await client.post(`/external/drafts/${draftId}/iterate`, data);
712
+ return response.data;
713
+ }
714
+ async function planFlowPages(draftId, data) {
715
+ const client = getApiClient();
716
+ const response = await client.post(`/external/drafts/${draftId}/flow/plan`, data);
717
+ return response.data;
718
+ }
719
+ async function executeFlowPages(draftId, data) {
720
+ const client = getApiClient();
721
+ const response = await client.post(`/external/drafts/${draftId}/flow/execute`, data);
722
+ return response.data;
723
+ }
724
+ function createCreateDesignDraftCommand() {
725
+ 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('-p, --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)=>{
726
+ if (options.json) setJsonMode(true);
727
+ job_runner_requireAuth(manager_isAuthenticated);
728
+ validateDeviceMode(options.device);
976
729
  await runJob({
977
730
  startLabel: 'Creating design draft...',
978
731
  pollingLabel: 'Generating design with AI...',
@@ -1434,15 +1187,103 @@ function createGetDesignCommand() {
1434
1187
  });
1435
1188
  return command;
1436
1189
  }
1190
+ let posthogClient = null;
1191
+ function getPostHog() {
1192
+ if (!posthogClient && POSTHOG_KEY) posthogClient = new PostHog(POSTHOG_KEY, {
1193
+ host: POSTHOG_HOST
1194
+ });
1195
+ return posthogClient;
1196
+ }
1197
+ function sanitizeOptions(options) {
1198
+ const sensitiveKeys = [
1199
+ 'token',
1200
+ 'key',
1201
+ 'secret',
1202
+ 'password',
1203
+ 'apiKey',
1204
+ 'api_key',
1205
+ 'apikey',
1206
+ 'auth',
1207
+ 'authorization',
1208
+ 'credential'
1209
+ ];
1210
+ const sanitized = {};
1211
+ for (const [key, value] of Object.entries(options)){
1212
+ const lowerKey = key.toLowerCase();
1213
+ if (!sensitiveKeys.some((sensitive)=>lowerKey.includes(sensitive))) {
1214
+ if ('string' != typeof value || !(value.length > 100)) sanitized[key] = value;
1215
+ }
1216
+ }
1217
+ return sanitized;
1218
+ }
1219
+ async function trackCommand(opts) {
1220
+ const config = loadConfig();
1221
+ const teamId = config.teamId;
1222
+ const metadata = {
1223
+ command: opts.command,
1224
+ success: opts.success,
1225
+ durationMs: opts.durationMs,
1226
+ errorCode: opts.errorCode,
1227
+ options: opts.options ? sanitizeOptions(opts.options) : void 0,
1228
+ cliVersion: "0.1.6",
1229
+ os: `${os.platform()} ${os.release()}`
1230
+ };
1231
+ const posthog = getPostHog();
1232
+ if (posthog) {
1233
+ const distinctId = teamId || `anon_${os.hostname()}`;
1234
+ posthog.capture({
1235
+ distinctId,
1236
+ event: 'cli_command',
1237
+ properties: {
1238
+ team_id: teamId,
1239
+ ...metadata
1240
+ }
1241
+ });
1242
+ }
1243
+ if (manager_isAuthenticated() && teamId) try {
1244
+ const client = getApiClient();
1245
+ await client.post('/external/track', {
1246
+ activityType: 'cli_command',
1247
+ metadata
1248
+ });
1249
+ } catch {}
1250
+ }
1251
+ async function shutdownAnalytics() {
1252
+ if (posthogClient) {
1253
+ await posthogClient.shutdown();
1254
+ posthogClient = null;
1255
+ }
1256
+ }
1437
1257
  const src_filename = fileURLToPath(import.meta.url);
1438
1258
  const src_dirname = dirname(src_filename);
1439
1259
  external_dotenv_config({
1440
1260
  path: external_path_resolve(src_dirname, '../.env')
1441
1261
  });
1442
1262
  external_dotenv_config();
1263
+ let commandStartTime = 0;
1264
+ let currentCommand = '';
1265
+ function sanitizeCommandOptions(opts) {
1266
+ const sanitized = {};
1267
+ for (const [key, value] of Object.entries(opts))if (!('function' == typeof value || key.startsWith('_'))) sanitized[key] = value;
1268
+ return sanitized;
1269
+ }
1443
1270
  function createProgram() {
1444
1271
  const program = new Command();
1445
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.4");
1272
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.6");
1273
+ program.hook('preAction', ()=>{
1274
+ commandStartTime = Date.now();
1275
+ currentCommand = process.argv[2] || 'unknown';
1276
+ });
1277
+ program.hook('postAction', async (_thisCommand, actionCommand)=>{
1278
+ const durationMs = Date.now() - commandStartTime;
1279
+ await trackCommand({
1280
+ command: currentCommand,
1281
+ success: true,
1282
+ durationMs,
1283
+ options: sanitizeCommandOptions(actionCommand.opts())
1284
+ });
1285
+ await shutdownAnalytics();
1286
+ });
1446
1287
  program.addCommand(createLoginCommand());
1447
1288
  program.addCommand(createLogoutCommand());
1448
1289
  program.addCommand(createInitCommand());
@@ -0,0 +1,25 @@
1
+ /**
2
+ * CLI Analytics - Dual tracking with PostHog (real-time) and Backend (long-term storage)
3
+ */
4
+ export interface TrackCommandOptions {
5
+ command: string;
6
+ success: boolean;
7
+ durationMs: number;
8
+ errorCode?: string;
9
+ options?: Record<string, unknown>;
10
+ }
11
+ /**
12
+ * Sanitize command options to remove sensitive values
13
+ */
14
+ declare function sanitizeOptions(options: Record<string, unknown>): Record<string, unknown>;
15
+ /**
16
+ * Track a CLI command execution
17
+ * Sends to both PostHog (real-time dashboards) and backend (long-term storage)
18
+ */
19
+ export declare function trackCommand(opts: TrackCommandOptions): Promise<void>;
20
+ /**
21
+ * Shutdown analytics and flush any pending events
22
+ * Should be called before CLI exits
23
+ */
24
+ export declare function shutdownAnalytics(): Promise<void>;
25
+ export { sanitizeOptions };
@@ -44,3 +44,9 @@ export declare function requireAuth(isAuthenticated: () => boolean): void;
44
44
  * Exits the process with appropriate exit code
45
45
  */
46
46
  export declare function handleApiError(err: unknown, failureMessage: string): never;
47
+ export declare const VALID_DEVICES: readonly ["mobile", "tablet", "desktop"];
48
+ export type DeviceMode = (typeof VALID_DEVICES)[number];
49
+ /**
50
+ * Validate device mode option and exit with error if invalid
51
+ */
52
+ export declare function validateDeviceMode(device: string | undefined): void;