@superdesign/cli 0.1.6 → 0.1.7

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,264 @@
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
+ --source-node-id <nodeId> \
222
+ --pages '[{"title":"Signup","prompt":"..."},{"title":"Payment","prompt":"..."}]' \
223
+ --json
224
+ ```
225
+
226
+ #### Get HTML reference from a draft
227
+
228
+ ```bash
229
+ superdesign get-design --draft-id <draftId> --output ./design.html
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Quick reference (key commands)
235
+
236
+ ```bash
237
+ # Inspirations
238
+ superdesign search-prompts --keyword "<keyword>" --json
239
+ superdesign search-prompts --tags "style" --json
240
+ superdesign get-prompts --slugs "<slug1,slug2>" --json
241
+ superdesign extract-brand-guide --url https://example.com --json
242
+
243
+ # Canvas - Create project
244
+ # Options: -s/--set-project-prompt (inline), --set-project-prompt-file (from file)
245
+ superdesign create-project --title "X" --set-project-prompt "..." --json
246
+ superdesign create-project --title "X" --set-project-prompt-file .superdesign/design-system.md --json
247
+ superdesign create-project --title "X" --html-file ./index.html --set-project-prompt-file .superdesign/design-system.md --json
248
+
249
+ # Iterate: replace mode (single variation, updates in place)
250
+ superdesign iterate-design-draft --draft-id <id> -p "..." --mode replace --json
251
+
252
+ # 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)
253
+ superdesign iterate-design-draft --draft-id <id> -p "dark theme" -p "minimal" -p "bold" --mode branch --json
254
+
255
+ # Iterate: Auto explore (only give exploration direction, and let Superdesign fill in details, e.g. explore different styles; Default do not use this)
256
+ superdesign iterate-design-draft --draft-id <id> -p "..." --mode branch --count 3 --json
257
+
258
+ # Fetch & get designs
259
+ superdesign fetch-design-nodes --project-id <id> --json
260
+ superdesign get-design --draft-id <id> --json
261
+
262
+ # Create new design from scracth without any reference - ONLY use this for creating brand new design, default NEVER use this
263
+ superdesign create-design-draft --project-id <id> --title "X" -p "..." --json
264
+ ```
@@ -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.7";
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.7",
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()) {
@@ -1286,38 +1275,36 @@ superdesign --help
1286
1275
  });
1287
1276
  return posthogClient;
1288
1277
  }
1278
+ const SENSITIVE_KEYS = [
1279
+ 'token',
1280
+ 'key',
1281
+ 'secret',
1282
+ 'password',
1283
+ 'apikey',
1284
+ 'auth',
1285
+ 'authorization',
1286
+ 'credential'
1287
+ ];
1289
1288
  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
1289
  const sanitized = {};
1303
1290
  for (const [key, value] of Object.entries(options)){
1291
+ if ('function' == typeof value || key.startsWith('_')) continue;
1304
1292
  const lowerKey = key.toLowerCase();
1305
- if (!sensitiveKeys.some((sensitive)=>lowerKey.includes(sensitive))) {
1293
+ if (!SENSITIVE_KEYS.some((sensitive)=>lowerKey.includes(sensitive))) {
1306
1294
  if ('string' != typeof value || !(value.length > 100)) sanitized[key] = value;
1307
1295
  }
1308
1296
  }
1309
1297
  return sanitized;
1310
1298
  }
1311
1299
  async function trackCommand(opts) {
1312
- const config = loadConfig();
1313
- const teamId = config.teamId;
1300
+ const { teamId } = loadConfig();
1314
1301
  const metadata = {
1315
1302
  command: opts.command,
1316
1303
  success: opts.success,
1317
1304
  durationMs: opts.durationMs,
1318
1305
  errorCode: opts.errorCode,
1319
- options: opts.options ? sanitizeOptions(opts.options) : void 0,
1320
- cliVersion: "0.1.6",
1306
+ options: opts.options,
1307
+ cliVersion: "0.1.7",
1321
1308
  os: `${external_os_default().platform()} ${external_os_default().release()}`
1322
1309
  };
1323
1310
  const posthog = getPostHog();
@@ -1352,27 +1339,19 @@ superdesign --help
1352
1339
  path: (0, external_path_namespaceObject.resolve)(src_dirname, '../.env')
1353
1340
  });
1354
1341
  (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
1342
  function createProgram() {
1363
1343
  const program = new external_commander_namespaceObject.Command();
1364
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.6");
1344
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.7");
1345
+ let startTime = 0;
1365
1346
  program.hook('preAction', ()=>{
1366
- commandStartTime = Date.now();
1367
- currentCommand = process.argv[2] || 'unknown';
1347
+ startTime = Date.now();
1368
1348
  });
1369
1349
  program.hook('postAction', async (_thisCommand, actionCommand)=>{
1370
- const durationMs = Date.now() - commandStartTime;
1371
1350
  await trackCommand({
1372
- command: currentCommand,
1351
+ command: actionCommand.name(),
1373
1352
  success: true,
1374
- durationMs,
1375
- options: sanitizeCommandOptions(actionCommand.opts())
1353
+ durationMs: Date.now() - startTime,
1354
+ options: sanitizeOptions(actionCommand.opts())
1376
1355
  });
1377
1356
  await shutdownAnalytics();
1378
1357
  });
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.7",
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()) {
@@ -1194,38 +1183,36 @@ function getPostHog() {
1194
1183
  });
1195
1184
  return posthogClient;
1196
1185
  }
1186
+ const SENSITIVE_KEYS = [
1187
+ 'token',
1188
+ 'key',
1189
+ 'secret',
1190
+ 'password',
1191
+ 'apikey',
1192
+ 'auth',
1193
+ 'authorization',
1194
+ 'credential'
1195
+ ];
1197
1196
  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
1197
  const sanitized = {};
1211
1198
  for (const [key, value] of Object.entries(options)){
1199
+ if ('function' == typeof value || key.startsWith('_')) continue;
1212
1200
  const lowerKey = key.toLowerCase();
1213
- if (!sensitiveKeys.some((sensitive)=>lowerKey.includes(sensitive))) {
1201
+ if (!SENSITIVE_KEYS.some((sensitive)=>lowerKey.includes(sensitive))) {
1214
1202
  if ('string' != typeof value || !(value.length > 100)) sanitized[key] = value;
1215
1203
  }
1216
1204
  }
1217
1205
  return sanitized;
1218
1206
  }
1219
1207
  async function trackCommand(opts) {
1220
- const config = loadConfig();
1221
- const teamId = config.teamId;
1208
+ const { teamId } = loadConfig();
1222
1209
  const metadata = {
1223
1210
  command: opts.command,
1224
1211
  success: opts.success,
1225
1212
  durationMs: opts.durationMs,
1226
1213
  errorCode: opts.errorCode,
1227
- options: opts.options ? sanitizeOptions(opts.options) : void 0,
1228
- cliVersion: "0.1.6",
1214
+ options: opts.options,
1215
+ cliVersion: "0.1.7",
1229
1216
  os: `${os.platform()} ${os.release()}`
1230
1217
  };
1231
1218
  const posthog = getPostHog();
@@ -1260,27 +1247,19 @@ external_dotenv_config({
1260
1247
  path: external_path_resolve(src_dirname, '../.env')
1261
1248
  });
1262
1249
  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
1250
  function createProgram() {
1271
1251
  const program = new Command();
1272
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.6");
1252
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.7");
1253
+ let startTime = 0;
1273
1254
  program.hook('preAction', ()=>{
1274
- commandStartTime = Date.now();
1275
- currentCommand = process.argv[2] || 'unknown';
1255
+ startTime = Date.now();
1276
1256
  });
1277
1257
  program.hook('postAction', async (_thisCommand, actionCommand)=>{
1278
- const durationMs = Date.now() - commandStartTime;
1279
1258
  await trackCommand({
1280
- command: currentCommand,
1259
+ command: actionCommand.name(),
1281
1260
  success: true,
1282
- durationMs,
1283
- options: sanitizeCommandOptions(actionCommand.opts())
1261
+ durationMs: Date.now() - startTime,
1262
+ options: sanitizeOptions(actionCommand.opts())
1284
1263
  });
1285
1264
  await shutdownAnalytics();
1286
1265
  });
@@ -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.7",
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.