@superdesign/cli 0.1.6 → 0.1.8

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/README.md ADDED
@@ -0,0 +1,263 @@
1
+ SuperDesign helps you (1) find design inspirations/styles and (2) generate/iterate design drafts on an infinite canvas.
2
+
3
+ ---
4
+
5
+ # Core scenarios (what this skill handles)
6
+
7
+ 1. **Help me design X** (feature/page/flow)
8
+ 2. **Set design system**
9
+ 3. **Help me improve design of X**
10
+
11
+ # Quickstart
12
+
13
+ Install CLI
14
+
15
+ ```
16
+ npm install -g @superdesign/cli@latest
17
+ ```
18
+
19
+ Install skills for any coding agent
20
+
21
+ ```
22
+ npx skills add superdesigndev/superdesign-skill
23
+ ```
24
+
25
+ Prompt in any agent
26
+
27
+ ```
28
+ /superdesign help me design X
29
+ ```
30
+
31
+ --
32
+
33
+ ## Tooling overview
34
+
35
+ ### A) Inspiration & Style Tools (generic, always available)
36
+
37
+ Use these to discover style direction, references, and brand context:
38
+
39
+ - **Search prompt library** (style/components/pages)
40
+
41
+ ```bash
42
+ superdesign search-prompts --keyword "<keyword>" --json
43
+ superdesign search-prompts --tags "style" --json
44
+ superdesign search-prompts --tags "style" --keyword "<style keyword>" --json
45
+ ```
46
+
47
+ - **Get full prompt details**
48
+
49
+ ```bash
50
+ superdesign get-prompts --slugs "<slug1,slug2,...>" --json
51
+ ```
52
+
53
+ - **Extract brand guide from a URL**
54
+ ```bash
55
+ superdesign extract-brand-guide --url https://example.com --json
56
+ ```
57
+
58
+ ### B) Canvas Design Tools
59
+
60
+ Use design agent to generate high quality design drafts:
61
+
62
+ - Create project (supports prompt / prompt file / HTML)
63
+ - Create design draft
64
+ - Iterate design draft (replace / branch)
65
+ - Plan flow pages → execute flow pages
66
+ - Fetch specific design draft
67
+
68
+ ---
69
+
70
+ ## Overall SOP for designing features on top of existing app:
71
+
72
+ 1. Investigate existing UI, workflow
73
+ 2. Setup design system file if not exist yet
74
+ 3. Requirements gathering: use askQuestion tool to clarify requirements with users (Optionally use Inspiration tool to find inspiration when needed)
75
+ 4. Ask user whether ready to design in superdesign OR implement UI directly
76
+ 5. If yes to superdesign
77
+ 5.1 Create/update a pixel perfect html replica of current UI of page that we will design on top of in `.superdesign/replica_html_template/<name>.html` (html should only contain & reflect how UI look now, the actual design should be handled by superdesign agent)
78
+ 5.2 Create project with this replica html + design system guide
79
+ 5.3 Start desigining by iterating & branching design draft based on designDraft ID returned from project
80
+
81
+ ## Always-on rules
82
+
83
+ - Design system should live at: `.superdesign/design-system.md`
84
+ - If `.superdesign/design-system.md` is missing, run **Design System Setup** first.
85
+ - Use `askQuestion` to ask high-signal questions (constraints, taste, tradeoffs).
86
+ - Always use `--json` for machine parsing.
87
+
88
+ ---
89
+
90
+ ## replica_html_template rules (Canvas only)
91
+
92
+ The purpose of replica html template is creating a lightweight version of existing UI so design agent can iterate on top of it (Since superdesign doesn't have access to your codebase directly, this is important context)
93
+
94
+ Overall process for designing features on top of existing app:
95
+
96
+ 1. Identify & understand existing UI of page related
97
+ 2. Create/update a pixel perfect replica html in `.superdesign/replica_html_template/<name>.html` (Only replicate how UI look now, do NOT design)
98
+
99
+ - If design task is redesign profile page, then replicate current profile page UI pixel perfectly
100
+ - If design task is add new button to side panel, identify which page side panel is using, then replicate that page UI pixel perfectly
101
+
102
+ **replica_html_template = BEFORE state (what exists now).** It provides context for SuperDesign agent.
103
+ Actual design will be done via superdesign agent, by passing the prompt
104
+
105
+ The replica_html_template must contain **ONLY UI that currently exists in the codebase**.
106
+
107
+ - **DO NOT** design or improve anything in the replica_html_template
108
+ - **DO NOT** add placeholder sections like `<!-- NEW FEATURE - DESIGN THIS -->`
109
+ - **DO** create pixel-perfect replica of current UI state
110
+ - Save to: `.superdesign/replica_html_template/<name>.html`
111
+
112
+ ### Naming & Reuse
113
+
114
+ **Naming convention**
115
+ Name replica_html_template for reusability: Use the page route (e.g., `home.html`, `settings-profile.html`, `dashboard.html`)
116
+ This makes it easy to identify if a page_template already exists.
117
+
118
+ **Before creating a replica_html_template:**
119
+
120
+ 1. Check if `.superdesign/replica_html_template/` already contains a matching file
121
+ 2. If exists: reuse it or update to reflect the latest existing UI
122
+ 3. If not exists: create the neww file
123
+
124
+ ### Example: Adding a "Book Demo" section to home page
125
+
126
+ **BAD approach:**
127
+
128
+ ```html
129
+ <!-- replica_html_template includes a sketched Book Demo section -->
130
+ <section class="book-demo">
131
+ <!-- DESIGN THIS - Add CTA here -->
132
+ <h3>Book a Demo</h3>
133
+ <button>Schedule</button>
134
+ </section>
135
+ ```
136
+
137
+ **GOOD approach:**
138
+
139
+ ```html
140
+ <!-- replica_html_template is pure replica of existing home page (hero + projects) -->
141
+ ```
142
+
143
+ Then in the iterate command:
144
+ 1/ create project passing this replica html
145
+ 2/ create design draft based on design draft id
146
+
147
+ ---
148
+
149
+ # 1) Design System Setup
150
+
151
+ ### Step 0 — Ask user (one question)
152
+
153
+ "Do you want to **create a new design system** or **extract from the current codebase**?"
154
+
155
+ ### A) Extract from codebase
156
+
157
+ 1. Investigate codebase:
158
+ - Product context: what is being built, target users, core value proposition, key user journeys and page structure
159
+ - design tokens, typography, colors, spacing, radius, shadows
160
+ - motion/animation patterns
161
+ - example components usage + implementation patterns
162
+ 2. Write standalone design system to:
163
+ - `.superdesign/design-system.md`
164
+ - Must be implementable without the codebase
165
+
166
+ ### B) Create a new design system (to improve current UI)
167
+
168
+ 1. Investigate codebase to understand:
169
+ - Product context: what is being built, target users, core value proposition, key user journeys and page structure
170
+ - needed pages/components
171
+ 2. Gather inspirations (generic tools):
172
+ - `superdesign search-prompts --tags "style" --json`
173
+ - `superdesign get-prompts --slugs ... --json`
174
+ - optional: `superdesign extract-brand-guide --url ... --json`
175
+ 3. Interview user (`askQuestion`) to choose direction
176
+ 4. Write:
177
+ - `.superdesign/design-system.md` (product context + UX flows + visual design, adapted to references)
178
+
179
+ ---
180
+
181
+ # 2) Designing X (feature/page/flow)
182
+
183
+ ### Example workflow - Add feature to existing page
184
+
185
+ 1. Investigate existing design and Ask targeted questions (`askQuestion`) about requirements + taste
186
+ 2. After clarifying, Ask user whether ready to design in superdesign OR implement UI directly
187
+ 3. If design in superdesign
188
+ 3.1 Ensure `.superdesign/design-system.md` exists (setup if missing)
189
+ 3.2 Identify page most relevant, and build a pixel-perfect replica in replica_html_template:
190
+ - `.superdesign/replica_html_template/<page>-<feature>.html`
191
+ 3.3 Create project with replica_html_template (returns `draftId`):
192
+ ```bash
193
+ superdesign create-project \
194
+ --title "<feature>" \
195
+ --html-file .superdesign/replica_html_template/<file>.html \
196
+ --set-project-prompt-file .superdesign/design-system.md \
197
+ --json
198
+ ```
199
+ → Note: `draftId` in response is the baseline draft
200
+ 3.4 Branch designs from baseline (use `draftId` from step 3.3)
201
+ ```bash
202
+ superdesign iterate-design-draft \
203
+ --draft-id <draftId> \
204
+ -p "Dark theme with neon accents" \
205
+ -p "Minimal with more whitespace" \
206
+ -p "Bold gradients and shadows" \
207
+ --mode branch \
208
+ --json
209
+ ```
210
+ 3.5 Share design title & preview URL → collect feedback → iterate
211
+
212
+ ### Advanced usage
213
+
214
+ #### Design multiple page OR a full user journey
215
+
216
+ Execute:
217
+
218
+ ```bash
219
+ superdesign execute-flow-pages \
220
+ --draft-id <draftId> \
221
+ --pages '[{"title":"Signup","prompt":"..."},{"title":"Payment","prompt":"..."}]' \
222
+ --json
223
+ ```
224
+
225
+ #### Get HTML reference from a draft
226
+
227
+ ```bash
228
+ superdesign get-design --draft-id <draftId> --output ./design.html
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Quick reference (key commands)
234
+
235
+ ```bash
236
+ # Inspirations
237
+ superdesign search-prompts --keyword "<keyword>" --json
238
+ superdesign search-prompts --tags "style" --json
239
+ superdesign get-prompts --slugs "<slug1,slug2>" --json
240
+ superdesign extract-brand-guide --url https://example.com --json
241
+
242
+ # Canvas - Create project
243
+ # Options: -s/--set-project-prompt (inline), --set-project-prompt-file (from file)
244
+ superdesign create-project --title "X" --set-project-prompt "..." --json
245
+ superdesign create-project --title "X" --set-project-prompt-file .superdesign/design-system.md --json
246
+ superdesign create-project --title "X" --html-file ./index.html --set-project-prompt-file .superdesign/design-system.md --json
247
+
248
+ # Iterate: replace mode (single variation, updates in place)
249
+ superdesign iterate-design-draft --draft-id <id> -p "..." --mode replace --json
250
+
251
+ # Iterate: Explore multiple versions & variations (each prompt = one variation, prompt should be just directional, do not specify color, style, let superdesign design expert fill in details, you just give direction)
252
+ superdesign iterate-design-draft --draft-id <id> -p "dark theme" -p "minimal" -p "bold" --mode branch --json
253
+
254
+ # Iterate: Auto explore (only give exploration direction, and let Superdesign fill in details, e.g. explore different styles; Default do not use this)
255
+ superdesign iterate-design-draft --draft-id <id> -p "..." --mode branch --count 3 --json
256
+
257
+ # Fetch & get designs
258
+ superdesign fetch-design-nodes --project-id <id> --json
259
+ superdesign get-design --draft-id <id> --json
260
+
261
+ # Create new design from scracth without any reference - ONLY use this for creating brand new design, default NEVER use this
262
+ superdesign create-design-draft --project-id <id> --title "X" -p "..." --json
263
+ ```
@@ -12,7 +12,10 @@ export declare const POLL_TIMEOUT_MS: number;
12
12
  export declare const AUTH_POLL_INTERVAL_MS = 2000;
13
13
  export declare const AUTH_POLL_TIMEOUT_MS: number;
14
14
  /** CLI version - should match package.json */
15
- export declare const CLI_VERSION = "0.1.6";
15
+ export declare const CLI_VERSION = "0.1.8";
16
+ /** PostHog analytics configuration */
17
+ export declare const POSTHOG_KEY: string;
18
+ export declare const POSTHOG_HOST: string;
16
19
  /** Config directory name */
17
20
  export declare const CONFIG_DIR_NAME = ".superdesign";
18
21
  /** Config file name */
@@ -31,6 +34,3 @@ export declare const EXIT_CODES: {
31
34
  export declare const SKILLS_DIR = ".claude/skills/superdesign";
32
35
  /** SKILL.md file name */
33
36
  export declare const SKILL_FILE_NAME = "SKILL.md";
34
- /** PostHog analytics configuration */
35
- export declare const POSTHOG_KEY: string;
36
- export declare const POSTHOG_HOST: string;
package/dist/index.cjs CHANGED
@@ -91,6 +91,8 @@ var __webpack_exports__ = {};
91
91
  const external_url_namespaceObject = require("url");
92
92
  const external_path_namespaceObject = require("path");
93
93
  const external_commander_namespaceObject = require("commander");
94
+ const POSTHOG_KEY = process.env.POSTHOG_KEY || 'phc_oUcDklFBX3wy8eksSyEC0pataKCgSadur1sio5hBHg4';
95
+ const POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://eu.i.posthog.com';
94
96
  const CONFIG_DIR_NAME = '.superdesign';
95
97
  const CONFIG_FILE_NAME = 'config.json';
96
98
  const EXIT_CODES = {
@@ -104,8 +106,6 @@ var __webpack_exports__ = {};
104
106
  };
105
107
  const SKILLS_DIR = '.claude/skills/superdesign';
106
108
  const SKILL_FILE_NAME = 'SKILL.md';
107
- const POSTHOG_KEY = process.env.POSTHOG_KEY || 'phc_oUcDklFBX3wy8eksSyEC0pataKCgSadur1sio5hBHg4';
108
- const POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://eu.i.posthog.com';
109
109
  const external_fs_namespaceObject = require("fs");
110
110
  const external_os_namespaceObject = require("os");
111
111
  var external_os_default = /*#__PURE__*/ __webpack_require__.n(external_os_namespaceObject);
@@ -316,7 +316,7 @@ var __webpack_exports__ = {};
316
316
  try {
317
317
  startSpinner('Creating auth session...');
318
318
  const session = await createSession({
319
- cliVersion: "0.1.6",
319
+ cliVersion: "0.1.8",
320
320
  os: `${external_os_namespaceObject.platform()} ${external_os_namespaceObject.release()}`,
321
321
  hostname: external_os_namespaceObject.hostname()
322
322
  });
@@ -542,7 +542,7 @@ Action accordingly based on instruction in the guidelines
542
542
 
543
543
  \`\`\`
544
544
  npm install -g @superdesign/cli@latest
545
- superdesign init
545
+ superdesign login
546
546
  superdesign --help
547
547
  \`\`\`
548
548
  `;
@@ -651,56 +651,45 @@ superdesign --help
651
651
  }
652
652
  async function runJob(config) {
653
653
  const { startLabel, pollingLabel, successLabel, timeoutLabel, failureLabel, timeoutMs = 300000, startJob, transformResult, displayResult } = config;
654
- try {
655
- startSpinner(startLabel);
656
- const job = await startJob();
657
- updateSpinner(pollingLabel);
658
- const pollResult = await poll(()=>getJobStatus(job.jobId), isJobDone, {
659
- intervalMs: 2000,
660
- timeoutMs,
661
- onPoll: (attempt)=>{
662
- if (attempt % 5 === 0) updateSpinner(`${pollingLabel} (${Math.floor(2000 * attempt / 1000)}s)`);
663
- }
664
- });
665
- if (pollResult.timedOut) {
666
- failSpinner(timeoutLabel);
667
- output_error('Job timed out. The operation may still be processing in the background.');
668
- if (isJsonMode()) output({
669
- error: 'timeout',
670
- jobId: job.jobId
671
- });
672
- process.exit(EXIT_CODES.TIMEOUT);
654
+ startSpinner(startLabel);
655
+ const job = await startJob();
656
+ updateSpinner(pollingLabel);
657
+ const pollResult = await poll(()=>getJobStatus(job.jobId), isJobDone, {
658
+ intervalMs: 2000,
659
+ timeoutMs,
660
+ onPoll: (attempt)=>{
661
+ if (attempt % 5 === 0) updateSpinner(`${pollingLabel} (${Math.floor(2000 * attempt / 1000)}s)`);
673
662
  }
674
- if (!pollResult.success || !pollResult.data) {
675
- failSpinner(failureLabel);
676
- output_error(pollResult.error || 'Failed to get job status');
677
- process.exit(EXIT_CODES.API_ERROR);
678
- }
679
- const jobResult = pollResult.data;
680
- if (isJobFailed(jobResult)) {
681
- failSpinner(failureLabel);
682
- output_error(`${jobResult.error.code}: ${jobResult.error.message}`);
683
- process.exit(EXIT_CODES.API_ERROR);
684
- }
685
- if (!isJobCompleted(jobResult)) {
686
- failSpinner('Unexpected job status');
687
- output_error('Job ended in unexpected state');
688
- process.exit(EXIT_CODES.API_ERROR);
689
- }
690
- succeedSpinner(successLabel);
691
- if (isJsonMode()) output(transformResult(jobResult));
692
- else displayResult(jobResult);
693
- process.exit(EXIT_CODES.SUCCESS);
694
- } catch (err) {
663
+ });
664
+ if (pollResult.timedOut) {
665
+ failSpinner(timeoutLabel);
666
+ if (isJsonMode()) output({
667
+ error: 'timeout',
668
+ jobId: job.jobId
669
+ });
670
+ else output_error('Job timed out. The operation may still be processing in the background.');
671
+ throw new ApiClientError('Job timed out', 'timeout');
672
+ }
673
+ if (!pollResult.success || !pollResult.data) {
695
674
  failSpinner(failureLabel);
696
- if (err instanceof ApiClientError) {
697
- output_error(`API Error: ${err.message}`);
698
- process.exit(EXIT_CODES.API_ERROR);
699
- }
700
- const message = err instanceof Error ? err.message : 'Unknown error';
701
- output_error(message);
702
- process.exit(EXIT_CODES.GENERAL_ERROR);
675
+ const errMsg = pollResult.error || 'Failed to get job status';
676
+ if (!isJsonMode()) output_error(errMsg);
677
+ throw new ApiClientError(errMsg, 'api_error');
678
+ }
679
+ const jobResult = pollResult.data;
680
+ if (isJobFailed(jobResult)) {
681
+ failSpinner(failureLabel);
682
+ if (!isJsonMode()) output_error(`${jobResult.error.code}: ${jobResult.error.message}`);
683
+ throw new ApiClientError(jobResult.error.message, jobResult.error.code);
703
684
  }
685
+ if (!isJobCompleted(jobResult)) {
686
+ failSpinner('Unexpected job status');
687
+ if (!isJsonMode()) output_error('Job ended in unexpected state');
688
+ throw new ApiClientError('Job ended in unexpected state', 'unexpected_status');
689
+ }
690
+ succeedSpinner(successLabel);
691
+ if (isJsonMode()) output(transformResult(jobResult));
692
+ else displayResult(jobResult);
704
693
  }
705
694
  function job_runner_requireAuth(isAuthenticated) {
706
695
  if (!isAuthenticated()) {
@@ -927,9 +916,10 @@ superdesign --help
927
916
  return command;
928
917
  }
929
918
  function createPlanFlowPagesCommand() {
930
- const command = new external_commander_namespaceObject.Command('plan-flow-pages').description('Plan flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').requiredOption('--source-node-id <id>', 'Source node ID in the flow').option('--context <context>', 'Additional context for flow planning').option('--json', 'Output in JSON format').action(async (options)=>{
919
+ const command = new external_commander_namespaceObject.Command('plan-flow-pages').description('Plan flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').option('--context <context>', 'Additional context for flow planning').option('--json', 'Output in JSON format').action(async (options)=>{
931
920
  if (options.json) setJsonMode(true);
932
921
  job_runner_requireAuth(manager_isAuthenticated);
922
+ const sourceNodeId = `draft-variant-${options.draftId}`;
933
923
  await runJob({
934
924
  startLabel: 'Planning flow pages...',
935
925
  pollingLabel: 'AI is analyzing and planning pages...',
@@ -937,7 +927,7 @@ superdesign --help
937
927
  timeoutLabel: 'Planning timed out',
938
928
  failureLabel: 'Failed to plan flow pages',
939
929
  startJob: ()=>planFlowPages(options.draftId, {
940
- sourceNodeId: options.sourceNodeId,
930
+ sourceNodeId,
941
931
  flowContext: options.context
942
932
  }),
943
933
  transformResult: (job)=>({
@@ -969,7 +959,7 @@ superdesign --help
969
959
  return parsed;
970
960
  }
971
961
  function createExecuteFlowPagesCommand() {
972
- const command = new external_commander_namespaceObject.Command('execute-flow-pages').description('Generate flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').requiredOption('--source-node-id <id>', 'Source node ID in the flow').requiredOption('--pages <json>', 'JSON array of pages to generate [{title, prompt}]').option('--context <context>', 'Additional context for flow generation').option('--json', 'Output in JSON format').action(async (options)=>{
962
+ const command = new external_commander_namespaceObject.Command('execute-flow-pages').description('Generate flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').requiredOption('--pages <json>', 'JSON array of pages to generate [{title, prompt}]').option('--context <context>', 'Additional context for flow generation').option('--json', 'Output in JSON format').action(async (options)=>{
973
963
  if (options.json) setJsonMode(true);
974
964
  job_runner_requireAuth(manager_isAuthenticated);
975
965
  let pages;
@@ -989,6 +979,7 @@ superdesign --help
989
979
  output_error('Maximum 10 pages allowed');
990
980
  process.exit(EXIT_CODES.VALIDATION_ERROR);
991
981
  }
982
+ const sourceNodeId = `draft-variant-${options.draftId}`;
992
983
  const timeoutMs = Math.max(300000, 2 * pages.length * 60000);
993
984
  await runJob({
994
985
  startLabel: `Generating ${pages.length} flow page(s)...`,
@@ -998,7 +989,7 @@ superdesign --help
998
989
  failureLabel: 'Failed to execute flow pages',
999
990
  timeoutMs,
1000
991
  startJob: ()=>executeFlowPages(options.draftId, {
1001
- sourceNodeId: options.sourceNodeId,
992
+ sourceNodeId,
1002
993
  flowContext: options.context,
1003
994
  pages
1004
995
  }),
@@ -1286,38 +1277,36 @@ superdesign --help
1286
1277
  });
1287
1278
  return posthogClient;
1288
1279
  }
1280
+ const SENSITIVE_KEYS = [
1281
+ 'token',
1282
+ 'key',
1283
+ 'secret',
1284
+ 'password',
1285
+ 'apikey',
1286
+ 'auth',
1287
+ 'authorization',
1288
+ 'credential'
1289
+ ];
1289
1290
  function sanitizeOptions(options) {
1290
- const sensitiveKeys = [
1291
- 'token',
1292
- 'key',
1293
- 'secret',
1294
- 'password',
1295
- 'apiKey',
1296
- 'api_key',
1297
- 'apikey',
1298
- 'auth',
1299
- 'authorization',
1300
- 'credential'
1301
- ];
1302
1291
  const sanitized = {};
1303
1292
  for (const [key, value] of Object.entries(options)){
1293
+ if ('function' == typeof value || key.startsWith('_')) continue;
1304
1294
  const lowerKey = key.toLowerCase();
1305
- if (!sensitiveKeys.some((sensitive)=>lowerKey.includes(sensitive))) {
1295
+ if (!SENSITIVE_KEYS.some((sensitive)=>lowerKey.includes(sensitive))) {
1306
1296
  if ('string' != typeof value || !(value.length > 100)) sanitized[key] = value;
1307
1297
  }
1308
1298
  }
1309
1299
  return sanitized;
1310
1300
  }
1311
1301
  async function trackCommand(opts) {
1312
- const config = loadConfig();
1313
- const teamId = config.teamId;
1302
+ const { teamId } = loadConfig();
1314
1303
  const metadata = {
1315
1304
  command: opts.command,
1316
1305
  success: opts.success,
1317
1306
  durationMs: opts.durationMs,
1318
1307
  errorCode: opts.errorCode,
1319
- options: opts.options ? sanitizeOptions(opts.options) : void 0,
1320
- cliVersion: "0.1.6",
1308
+ options: opts.options,
1309
+ cliVersion: "0.1.8",
1321
1310
  os: `${external_os_default().platform()} ${external_os_default().release()}`
1322
1311
  };
1323
1312
  const posthog = getPostHog();
@@ -1352,27 +1341,19 @@ superdesign --help
1352
1341
  path: (0, external_path_namespaceObject.resolve)(src_dirname, '../.env')
1353
1342
  });
1354
1343
  (0, external_dotenv_namespaceObject.config)();
1355
- let commandStartTime = 0;
1356
- let currentCommand = '';
1357
- function sanitizeCommandOptions(opts) {
1358
- const sanitized = {};
1359
- for (const [key, value] of Object.entries(opts))if (!('function' == typeof value || key.startsWith('_'))) sanitized[key] = value;
1360
- return sanitized;
1361
- }
1362
1344
  function createProgram() {
1363
1345
  const program = new external_commander_namespaceObject.Command();
1364
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.6");
1346
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.8");
1347
+ let startTime = 0;
1365
1348
  program.hook('preAction', ()=>{
1366
- commandStartTime = Date.now();
1367
- currentCommand = process.argv[2] || 'unknown';
1349
+ startTime = Date.now();
1368
1350
  });
1369
1351
  program.hook('postAction', async (_thisCommand, actionCommand)=>{
1370
- const durationMs = Date.now() - commandStartTime;
1371
1352
  await trackCommand({
1372
- command: currentCommand,
1353
+ command: actionCommand.name(),
1373
1354
  success: true,
1374
- durationMs,
1375
- options: sanitizeCommandOptions(actionCommand.opts())
1355
+ durationMs: Date.now() - startTime,
1356
+ options: sanitizeOptions(actionCommand.opts())
1376
1357
  });
1377
1358
  await shutdownAnalytics();
1378
1359
  });
package/dist/index.js CHANGED
@@ -8,6 +8,8 @@ import axios from "axios";
8
8
  import ora from "ora";
9
9
  import { writeFile } from "fs/promises";
10
10
  import { PostHog } from "posthog-node";
11
+ const POSTHOG_KEY = process.env.POSTHOG_KEY || 'phc_oUcDklFBX3wy8eksSyEC0pataKCgSadur1sio5hBHg4';
12
+ const POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://eu.i.posthog.com';
11
13
  const CONFIG_DIR_NAME = '.superdesign';
12
14
  const CONFIG_FILE_NAME = 'config.json';
13
15
  const EXIT_CODES = {
@@ -21,8 +23,6 @@ const EXIT_CODES = {
21
23
  };
22
24
  const SKILLS_DIR = '.claude/skills/superdesign';
23
25
  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';
26
26
  function getConfigDir() {
27
27
  return join(homedir(), CONFIG_DIR_NAME);
28
28
  }
@@ -226,7 +226,7 @@ async function runAuthFlow(options = {}) {
226
226
  try {
227
227
  startSpinner('Creating auth session...');
228
228
  const session = await createSession({
229
- cliVersion: "0.1.6",
229
+ cliVersion: "0.1.8",
230
230
  os: `${platform()} ${release()}`,
231
231
  hostname: hostname()
232
232
  });
@@ -452,7 +452,7 @@ Action accordingly based on instruction in the guidelines
452
452
 
453
453
  \`\`\`
454
454
  npm install -g @superdesign/cli@latest
455
- superdesign init
455
+ superdesign login
456
456
  superdesign --help
457
457
  \`\`\`
458
458
  `;
@@ -561,56 +561,45 @@ function isJobFailed(response) {
561
561
  }
562
562
  async function runJob(config) {
563
563
  const { startLabel, pollingLabel, successLabel, timeoutLabel, failureLabel, timeoutMs = 300000, startJob, transformResult, displayResult } = config;
564
- try {
565
- startSpinner(startLabel);
566
- const job = await startJob();
567
- updateSpinner(pollingLabel);
568
- const pollResult = await poll(()=>getJobStatus(job.jobId), isJobDone, {
569
- intervalMs: 2000,
570
- timeoutMs,
571
- onPoll: (attempt)=>{
572
- if (attempt % 5 === 0) updateSpinner(`${pollingLabel} (${Math.floor(2000 * attempt / 1000)}s)`);
573
- }
574
- });
575
- if (pollResult.timedOut) {
576
- failSpinner(timeoutLabel);
577
- output_error('Job timed out. The operation may still be processing in the background.');
578
- if (isJsonMode()) output({
579
- error: 'timeout',
580
- jobId: job.jobId
581
- });
582
- process.exit(EXIT_CODES.TIMEOUT);
564
+ startSpinner(startLabel);
565
+ const job = await startJob();
566
+ updateSpinner(pollingLabel);
567
+ const pollResult = await poll(()=>getJobStatus(job.jobId), isJobDone, {
568
+ intervalMs: 2000,
569
+ timeoutMs,
570
+ onPoll: (attempt)=>{
571
+ if (attempt % 5 === 0) updateSpinner(`${pollingLabel} (${Math.floor(2000 * attempt / 1000)}s)`);
583
572
  }
584
- if (!pollResult.success || !pollResult.data) {
585
- failSpinner(failureLabel);
586
- output_error(pollResult.error || 'Failed to get job status');
587
- process.exit(EXIT_CODES.API_ERROR);
588
- }
589
- const jobResult = pollResult.data;
590
- if (isJobFailed(jobResult)) {
591
- failSpinner(failureLabel);
592
- output_error(`${jobResult.error.code}: ${jobResult.error.message}`);
593
- process.exit(EXIT_CODES.API_ERROR);
594
- }
595
- if (!isJobCompleted(jobResult)) {
596
- failSpinner('Unexpected job status');
597
- output_error('Job ended in unexpected state');
598
- process.exit(EXIT_CODES.API_ERROR);
599
- }
600
- succeedSpinner(successLabel);
601
- if (isJsonMode()) output(transformResult(jobResult));
602
- else displayResult(jobResult);
603
- process.exit(EXIT_CODES.SUCCESS);
604
- } catch (err) {
573
+ });
574
+ if (pollResult.timedOut) {
575
+ failSpinner(timeoutLabel);
576
+ if (isJsonMode()) output({
577
+ error: 'timeout',
578
+ jobId: job.jobId
579
+ });
580
+ else output_error('Job timed out. The operation may still be processing in the background.');
581
+ throw new ApiClientError('Job timed out', 'timeout');
582
+ }
583
+ if (!pollResult.success || !pollResult.data) {
605
584
  failSpinner(failureLabel);
606
- if (err instanceof ApiClientError) {
607
- output_error(`API Error: ${err.message}`);
608
- process.exit(EXIT_CODES.API_ERROR);
609
- }
610
- const message = err instanceof Error ? err.message : 'Unknown error';
611
- output_error(message);
612
- process.exit(EXIT_CODES.GENERAL_ERROR);
585
+ const errMsg = pollResult.error || 'Failed to get job status';
586
+ if (!isJsonMode()) output_error(errMsg);
587
+ throw new ApiClientError(errMsg, 'api_error');
588
+ }
589
+ const jobResult = pollResult.data;
590
+ if (isJobFailed(jobResult)) {
591
+ failSpinner(failureLabel);
592
+ if (!isJsonMode()) output_error(`${jobResult.error.code}: ${jobResult.error.message}`);
593
+ throw new ApiClientError(jobResult.error.message, jobResult.error.code);
613
594
  }
595
+ if (!isJobCompleted(jobResult)) {
596
+ failSpinner('Unexpected job status');
597
+ if (!isJsonMode()) output_error('Job ended in unexpected state');
598
+ throw new ApiClientError('Job ended in unexpected state', 'unexpected_status');
599
+ }
600
+ succeedSpinner(successLabel);
601
+ if (isJsonMode()) output(transformResult(jobResult));
602
+ else displayResult(jobResult);
614
603
  }
615
604
  function job_runner_requireAuth(isAuthenticated) {
616
605
  if (!isAuthenticated()) {
@@ -837,9 +826,10 @@ function createIterateDesignDraftCommand() {
837
826
  return command;
838
827
  }
839
828
  function createPlanFlowPagesCommand() {
840
- const command = new Command('plan-flow-pages').description('Plan flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').requiredOption('--source-node-id <id>', 'Source node ID in the flow').option('--context <context>', 'Additional context for flow planning').option('--json', 'Output in JSON format').action(async (options)=>{
829
+ const command = new Command('plan-flow-pages').description('Plan flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').option('--context <context>', 'Additional context for flow planning').option('--json', 'Output in JSON format').action(async (options)=>{
841
830
  if (options.json) setJsonMode(true);
842
831
  job_runner_requireAuth(manager_isAuthenticated);
832
+ const sourceNodeId = `draft-variant-${options.draftId}`;
843
833
  await runJob({
844
834
  startLabel: 'Planning flow pages...',
845
835
  pollingLabel: 'AI is analyzing and planning pages...',
@@ -847,7 +837,7 @@ function createPlanFlowPagesCommand() {
847
837
  timeoutLabel: 'Planning timed out',
848
838
  failureLabel: 'Failed to plan flow pages',
849
839
  startJob: ()=>planFlowPages(options.draftId, {
850
- sourceNodeId: options.sourceNodeId,
840
+ sourceNodeId,
851
841
  flowContext: options.context
852
842
  }),
853
843
  transformResult: (job)=>({
@@ -879,7 +869,7 @@ function parsePages(pagesJson) {
879
869
  return parsed;
880
870
  }
881
871
  function createExecuteFlowPagesCommand() {
882
- const command = new Command('execute-flow-pages').description('Generate flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').requiredOption('--source-node-id <id>', 'Source node ID in the flow').requiredOption('--pages <json>', 'JSON array of pages to generate [{title, prompt}]').option('--context <context>', 'Additional context for flow generation').option('--json', 'Output in JSON format').action(async (options)=>{
872
+ const command = new Command('execute-flow-pages').description('Generate flow pages using AI').requiredOption('--draft-id <id>', 'Source draft ID').requiredOption('--pages <json>', 'JSON array of pages to generate [{title, prompt}]').option('--context <context>', 'Additional context for flow generation').option('--json', 'Output in JSON format').action(async (options)=>{
883
873
  if (options.json) setJsonMode(true);
884
874
  job_runner_requireAuth(manager_isAuthenticated);
885
875
  let pages;
@@ -899,6 +889,7 @@ function createExecuteFlowPagesCommand() {
899
889
  output_error('Maximum 10 pages allowed');
900
890
  process.exit(EXIT_CODES.VALIDATION_ERROR);
901
891
  }
892
+ const sourceNodeId = `draft-variant-${options.draftId}`;
902
893
  const timeoutMs = Math.max(300000, 2 * pages.length * 60000);
903
894
  await runJob({
904
895
  startLabel: `Generating ${pages.length} flow page(s)...`,
@@ -908,7 +899,7 @@ function createExecuteFlowPagesCommand() {
908
899
  failureLabel: 'Failed to execute flow pages',
909
900
  timeoutMs,
910
901
  startJob: ()=>executeFlowPages(options.draftId, {
911
- sourceNodeId: options.sourceNodeId,
902
+ sourceNodeId,
912
903
  flowContext: options.context,
913
904
  pages
914
905
  }),
@@ -1194,38 +1185,36 @@ function getPostHog() {
1194
1185
  });
1195
1186
  return posthogClient;
1196
1187
  }
1188
+ const SENSITIVE_KEYS = [
1189
+ 'token',
1190
+ 'key',
1191
+ 'secret',
1192
+ 'password',
1193
+ 'apikey',
1194
+ 'auth',
1195
+ 'authorization',
1196
+ 'credential'
1197
+ ];
1197
1198
  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
1199
  const sanitized = {};
1211
1200
  for (const [key, value] of Object.entries(options)){
1201
+ if ('function' == typeof value || key.startsWith('_')) continue;
1212
1202
  const lowerKey = key.toLowerCase();
1213
- if (!sensitiveKeys.some((sensitive)=>lowerKey.includes(sensitive))) {
1203
+ if (!SENSITIVE_KEYS.some((sensitive)=>lowerKey.includes(sensitive))) {
1214
1204
  if ('string' != typeof value || !(value.length > 100)) sanitized[key] = value;
1215
1205
  }
1216
1206
  }
1217
1207
  return sanitized;
1218
1208
  }
1219
1209
  async function trackCommand(opts) {
1220
- const config = loadConfig();
1221
- const teamId = config.teamId;
1210
+ const { teamId } = loadConfig();
1222
1211
  const metadata = {
1223
1212
  command: opts.command,
1224
1213
  success: opts.success,
1225
1214
  durationMs: opts.durationMs,
1226
1215
  errorCode: opts.errorCode,
1227
- options: opts.options ? sanitizeOptions(opts.options) : void 0,
1228
- cliVersion: "0.1.6",
1216
+ options: opts.options,
1217
+ cliVersion: "0.1.8",
1229
1218
  os: `${os.platform()} ${os.release()}`
1230
1219
  };
1231
1220
  const posthog = getPostHog();
@@ -1260,27 +1249,19 @@ external_dotenv_config({
1260
1249
  path: external_path_resolve(src_dirname, '../.env')
1261
1250
  });
1262
1251
  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
- }
1270
1252
  function createProgram() {
1271
1253
  const program = new Command();
1272
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.6");
1254
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.8");
1255
+ let startTime = 0;
1273
1256
  program.hook('preAction', ()=>{
1274
- commandStartTime = Date.now();
1275
- currentCommand = process.argv[2] || 'unknown';
1257
+ startTime = Date.now();
1276
1258
  });
1277
1259
  program.hook('postAction', async (_thisCommand, actionCommand)=>{
1278
- const durationMs = Date.now() - commandStartTime;
1279
1260
  await trackCommand({
1280
- command: currentCommand,
1261
+ command: actionCommand.name(),
1281
1262
  success: true,
1282
- durationMs,
1283
- options: sanitizeCommandOptions(actionCommand.opts())
1263
+ durationMs: Date.now() - startTime,
1264
+ options: sanitizeOptions(actionCommand.opts())
1284
1265
  });
1285
1266
  await shutdownAnalytics();
1286
1267
  });
@@ -9,9 +9,9 @@ export interface TrackCommandOptions {
9
9
  options?: Record<string, unknown>;
10
10
  }
11
11
  /**
12
- * Sanitize command options to remove sensitive values
12
+ * Sanitize command options by removing sensitive values, functions, and internal properties
13
13
  */
14
- declare function sanitizeOptions(options: Record<string, unknown>): Record<string, unknown>;
14
+ export declare function sanitizeOptions(options: Record<string, unknown>): Record<string, unknown>;
15
15
  /**
16
16
  * Track a CLI command execution
17
17
  * Sends to both PostHog (real-time dashboards) and backend (long-term storage)
@@ -22,4 +22,3 @@ export declare function trackCommand(opts: TrackCommandOptions): Promise<void>;
22
22
  * Should be called before CLI exits
23
23
  */
24
24
  export declare function shutdownAnalytics(): Promise<void>;
25
- export { sanitizeOptions };
@@ -33,8 +33,9 @@ export interface JobRunnerResult<TResult> {
33
33
  /**
34
34
  * Run a job with polling and standard error handling
35
35
  * Handles spinner updates, timeout, failure states, and output formatting
36
+ * Returns normally on success, throws on failure (to allow postAction hooks to run)
36
37
  */
37
- export declare function runJob<TResult>(config: JobRunnerConfig<TResult>): Promise<never>;
38
+ export declare function runJob<TResult>(config: JobRunnerConfig<TResult>): Promise<void>;
38
39
  /**
39
40
  * Check authentication and exit with appropriate error if not authenticated
40
41
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdesign/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "CLI for SuperDesign Platform - agent skills for Claude Code",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -20,13 +20,17 @@
20
20
  "dist",
21
21
  "bin"
22
22
  ],
23
+ "scripts": {
24
+ "build": "rslib build && chmod +x bin/superdesign.js",
25
+ "dev": "rslib build --watch"
26
+ },
23
27
  "dependencies": {
24
28
  "axios": "^1.6.0",
25
29
  "commander": "^11.1.0",
26
30
  "dotenv": "^16.3.0",
27
31
  "open": "^10.0.0",
28
32
  "ora": "^8.0.0",
29
- "posthog-node": "^4.0.0"
33
+ "posthog-node": "^5.24.1"
30
34
  },
31
35
  "devDependencies": {
32
36
  "@rslib/core": "0.15.1",
@@ -48,9 +52,5 @@
48
52
  "url": "https://github.com/superdesigndev/superdesign-platform.git",
49
53
  "directory": "packages/cli"
50
54
  },
51
- "license": "MIT",
52
- "scripts": {
53
- "build": "rslib build && chmod +x bin/superdesign.js",
54
- "dev": "rslib build --watch"
55
- }
56
- }
55
+ "license": "MIT"
56
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Super Design
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.