@superdesign/cli 0.1.13 → 0.2.1

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.
@@ -1,16 +1,22 @@
1
1
  /**
2
2
  * Drafts API endpoints
3
3
  */
4
+ export interface ContextFile {
5
+ filename: string;
6
+ content: string;
7
+ }
4
8
  export interface CreateDraftRequest {
5
9
  title: string;
6
10
  prompt: string;
7
11
  deviceMode?: 'mobile' | 'tablet' | 'desktop';
12
+ contextFiles?: ContextFile[];
8
13
  }
9
14
  export interface IterateDraftRequest {
10
15
  prompts?: string[];
11
16
  prompt?: string;
12
17
  count?: 1 | 2 | 3 | 4;
13
18
  mode: 'replace' | 'branch';
19
+ contextFiles?: ContextFile[];
14
20
  }
15
21
  export interface PlanFlowRequest {
16
22
  flowContext?: string;
@@ -22,6 +28,7 @@ export interface FlowPage {
22
28
  export interface ExecuteFlowRequest {
23
29
  flowContext?: string;
24
30
  pages: FlowPage[];
31
+ contextFiles?: ContextFile[];
25
32
  }
26
33
  export interface JobCreatedResponse {
27
34
  jobId: string;
@@ -12,7 +12,7 @@ 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.13";
15
+ export declare const CLI_VERSION = "0.2.1";
16
16
  /** PostHog analytics configuration */
17
17
  export declare const POSTHOG_KEY: string;
18
18
  export declare const POSTHOG_HOST: string;
package/dist/index.cjs CHANGED
@@ -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.13",
319
+ cliVersion: "0.2.1",
320
320
  os: `${external_os_namespaceObject.platform()} ${external_os_namespaceObject.release()}`,
321
321
  hostname: external_os_namespaceObject.hostname()
322
322
  });
@@ -799,11 +799,140 @@ superdesign --help
799
799
  const response = await client.post(`/external/drafts/${draftId}/flow/execute`, data);
800
800
  return response.data;
801
801
  }
802
+ function parseContextFileArg(arg) {
803
+ const match = arg.match(/^(.+?):(\d+)(?::(\d+))?$/);
804
+ if (!match) return {
805
+ rawPath: arg
806
+ };
807
+ const [, pathPart, startStr, endStr] = match;
808
+ const startLine = parseInt(startStr, 10);
809
+ const endLine = void 0 !== endStr ? parseInt(endStr, 10) : void 0;
810
+ if (startLine < 1) {
811
+ output_error(`Invalid line range in "${arg}": start line must be >= 1`);
812
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
813
+ }
814
+ if (void 0 !== endLine && endLine < startLine) {
815
+ output_error(`Invalid line range in "${arg}": end line (${endLine}) must be >= start line (${startLine})`);
816
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
817
+ }
818
+ const cwd = process.cwd();
819
+ const extractedAbsolute = (0, external_path_namespaceObject.resolve)(cwd, pathPart);
820
+ const originalAbsolute = (0, external_path_namespaceObject.resolve)(cwd, arg);
821
+ if (!(0, external_fs_namespaceObject.existsSync)(extractedAbsolute) && (0, external_fs_namespaceObject.existsSync)(originalAbsolute)) return {
822
+ rawPath: arg
823
+ };
824
+ return {
825
+ rawPath: pathPart,
826
+ startLine,
827
+ endLine
828
+ };
829
+ }
830
+ function extractLines(content, start, end) {
831
+ const lines = content.split('\n');
832
+ const from = Math.max(0, start - 1);
833
+ const to = Math.min(lines.length, end);
834
+ return lines.slice(from, to).join('\n');
835
+ }
836
+ function mergeRanges(ranges) {
837
+ if (0 === ranges.length) return [];
838
+ const sorted = [
839
+ ...ranges
840
+ ].sort((a, b)=>a.start - b.start);
841
+ const merged = [
842
+ sorted[0]
843
+ ];
844
+ for(let i = 1; i < sorted.length; i++){
845
+ const prev = merged[merged.length - 1];
846
+ const curr = sorted[i];
847
+ if (prev.end >= curr.start - 1 || prev.end === 1 / 0) prev.end = Math.max(prev.end, curr.end);
848
+ else merged.push({
849
+ ...curr
850
+ });
851
+ }
852
+ return merged;
853
+ }
854
+ function formatRangeContent(content, ranges) {
855
+ const totalLines = content.split('\n').length;
856
+ const parts = [];
857
+ for(let i = 0; i < ranges.length; i++){
858
+ const range = ranges[i];
859
+ const effectiveEnd = range.end === 1 / 0 ? totalLines : Math.min(range.end, totalLines);
860
+ if (i > 0) {
861
+ const prevEnd = ranges[i - 1].end === 1 / 0 ? totalLines : Math.min(ranges[i - 1].end, totalLines);
862
+ const gapStart = prevEnd + 1;
863
+ const gapEnd = range.start - 1;
864
+ if (gapEnd >= gapStart) {
865
+ const gapSize = gapEnd - gapStart + 1;
866
+ parts.push(`// ... (lines ${gapStart}-${gapEnd} omitted, ${gapSize} lines) ...`);
867
+ }
868
+ }
869
+ parts.push(`// === Lines ${range.start}-${effectiveEnd} ===`);
870
+ parts.push(extractLines(content, range.start, effectiveEnd));
871
+ }
872
+ return parts.join('\n');
873
+ }
874
+ function readContextFiles(filePaths) {
875
+ const cwd = process.cwd();
876
+ const refs = filePaths.map(parseContextFileArg);
877
+ const groups = new Map();
878
+ for (const ref of refs){
879
+ const absolutePath = (0, external_path_namespaceObject.resolve)(cwd, ref.rawPath);
880
+ const relPath = (0, external_path_namespaceObject.relative)(cwd, absolutePath);
881
+ let group = groups.get(absolutePath);
882
+ if (!group) {
883
+ group = {
884
+ relPath,
885
+ fullFile: false,
886
+ ranges: []
887
+ };
888
+ groups.set(absolutePath, group);
889
+ }
890
+ if (void 0 === ref.startLine) group.fullFile = true;
891
+ else group.ranges.push({
892
+ start: ref.startLine,
893
+ end: ref.endLine ?? 1 / 0
894
+ });
895
+ }
896
+ const results = [];
897
+ for (const [absolutePath, group] of groups){
898
+ let content;
899
+ try {
900
+ content = (0, external_fs_namespaceObject.readFileSync)(absolutePath, 'utf-8');
901
+ } catch (err) {
902
+ const msg = err instanceof Error ? err.message : 'Unknown error';
903
+ output_error(`Failed to read context file "${group.relPath}": ${msg}`);
904
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
905
+ }
906
+ if (group.fullFile || 0 === group.ranges.length) {
907
+ results.push({
908
+ filename: group.relPath,
909
+ content
910
+ });
911
+ continue;
912
+ }
913
+ const merged = mergeRanges(group.ranges);
914
+ const totalLines = content.split('\n').length;
915
+ if (1 === merged.length && merged[0].start <= 1 && (merged[0].end === 1 / 0 || merged[0].end >= totalLines)) {
916
+ results.push({
917
+ filename: group.relPath,
918
+ content
919
+ });
920
+ continue;
921
+ }
922
+ const formatted = formatRangeContent(content, merged);
923
+ results.push({
924
+ filename: group.relPath,
925
+ content: formatted
926
+ });
927
+ }
928
+ return results;
929
+ }
802
930
  function createCreateDesignDraftCommand() {
803
- const command = new external_commander_namespaceObject.Command('create-design-draft').description('Create a design draft from scratch without any reference, Default dont use this, use iterate-design-draft instead').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)=>{
931
+ const command = new external_commander_namespaceObject.Command('create-design-draft').description('Create a design draft from scratch without any reference, Default dont use this, use iterate-design-draft instead').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('--context-file <paths...>', 'UI source files to include as context for AI generation').option('--json', 'Output in JSON format').action(async (options)=>{
804
932
  if (options.json) setJsonMode(true);
805
933
  job_runner_requireAuth(manager_isAuthenticated);
806
934
  validateDeviceMode(options.device);
935
+ const contextFiles = options.contextFile ? readContextFiles(options.contextFile) : void 0;
807
936
  await runJob({
808
937
  startLabel: 'Creating design draft...',
809
938
  pollingLabel: 'Generating design with AI...',
@@ -813,7 +942,8 @@ superdesign --help
813
942
  startJob: ()=>createDraft(options.projectId, {
814
943
  title: options.title,
815
944
  prompt: options.prompt,
816
- deviceMode: options.device
945
+ deviceMode: options.device,
946
+ contextFiles
817
947
  }),
818
948
  transformResult: (job)=>({
819
949
  draftId: job.result.draftId,
@@ -885,17 +1015,20 @@ Usage Examples:
885
1015
  superdesign iterate-design-draft --draft-id <id> -p "dark theme" -p "minimal" -p "bold" --mode branch --json
886
1016
 
887
1017
  # Iterate: Auto explore (only give exploration direction, and let Superdesign fill in details, e.g. explore different styles; Default do NOT use auto explore mode)
888
- superdesign iterate-design-draft --draft-id <id> -p "..." --mode branch --count 3 --json`).requiredOption('--draft-id <id>', 'Draft ID to iterate on').requiredOption('-p, --prompt <prompt...>', 'Iteration prompt(s). Use multiple -p for specific prompts per variation.').requiredOption('--mode <mode>', 'Iteration mode (replace or branch)').option('--count <count>', 'Number of variations (1-4). Only used when single prompt provided.').option('--json', 'Output in JSON format').addOption(new external_commander_namespaceObject.Option('--project-id <id>').hideHelp()).action(async (options)=>{
1018
+ superdesign iterate-design-draft --draft-id <id> -p "..." --mode branch --count 3 --json`).requiredOption('--draft-id <id>', 'Draft ID to iterate on').requiredOption('-p, --prompt <prompt...>', 'Iteration prompt(s). Use multiple -p for specific prompts per variation.').requiredOption('--mode <mode>', 'Iteration mode (replace or branch)').option('--count <count>', 'Number of variations (1-4). Only used when single prompt provided.').option('--context-file <paths...>', 'UI source files to include as context for AI generation').option('--json', 'Output in JSON format').addOption(new external_commander_namespaceObject.Option('--project-id <id>').hideHelp()).action(async (options)=>{
889
1019
  if (options.json) setJsonMode(true);
890
1020
  job_runner_requireAuth(manager_isAuthenticated);
891
1021
  const { prompts, count, mode } = validateOptions(options);
1022
+ const contextFiles = options.contextFile ? readContextFiles(options.contextFile) : void 0;
892
1023
  const requestData = prompts.length > 1 ? {
893
1024
  prompts,
894
- mode
1025
+ mode,
1026
+ contextFiles
895
1027
  } : {
896
1028
  prompt: prompts[0],
897
1029
  count: count,
898
- mode
1030
+ mode,
1031
+ contextFiles
899
1032
  };
900
1033
  await runJob({
901
1034
  startLabel: 'Starting iteration...',
@@ -966,7 +1099,7 @@ Usage Examples:
966
1099
  return parsed;
967
1100
  }
968
1101
  function createExecuteFlowPagesCommand() {
969
- 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)=>{
1102
+ 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('--context-file <paths...>', 'UI source files to include as context for AI generation').option('--json', 'Output in JSON format').action(async (options)=>{
970
1103
  if (options.json) setJsonMode(true);
971
1104
  job_runner_requireAuth(manager_isAuthenticated);
972
1105
  let pages;
@@ -986,6 +1119,7 @@ Usage Examples:
986
1119
  output_error('Maximum 10 pages allowed');
987
1120
  process.exit(EXIT_CODES.VALIDATION_ERROR);
988
1121
  }
1122
+ const contextFiles = options.contextFile ? readContextFiles(options.contextFile) : void 0;
989
1123
  const timeoutMs = Math.max(300000, 2 * pages.length * 60000);
990
1124
  await runJob({
991
1125
  startLabel: `Generating ${pages.length} flow page(s)...`,
@@ -996,7 +1130,8 @@ Usage Examples:
996
1130
  timeoutMs,
997
1131
  startJob: ()=>executeFlowPages(options.draftId, {
998
1132
  flowContext: options.context,
999
- pages
1133
+ pages,
1134
+ contextFiles
1000
1135
  }),
1001
1136
  transformResult: (job)=>({
1002
1137
  drafts: job.result.drafts,
@@ -1311,7 +1446,7 @@ Usage Examples:
1311
1446
  durationMs: opts.durationMs,
1312
1447
  errorCode: opts.errorCode,
1313
1448
  options: opts.options,
1314
- cliVersion: "0.1.13",
1449
+ cliVersion: "0.2.1",
1315
1450
  os: `${external_os_default().platform()} ${external_os_default().release()}`
1316
1451
  };
1317
1452
  const posthog = getPostHog();
@@ -1379,7 +1514,7 @@ Usage Examples:
1379
1514
  }
1380
1515
  function createProgram() {
1381
1516
  const program = new external_commander_namespaceObject.Command();
1382
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.13");
1517
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.2.1");
1383
1518
  program.configureOutput({
1384
1519
  writeErr: (str)=>{
1385
1520
  if (!process.argv.includes('--json')) process.stderr.write(str);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { config as external_dotenv_config } from "dotenv";
2
2
  import { fileURLToPath } from "url";
3
- import { dirname, join, resolve as external_path_resolve } from "path";
3
+ import { dirname, join, relative, resolve as external_path_resolve } from "path";
4
4
  import { Command, Option } from "commander";
5
5
  import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
6
6
  import os, { homedir, hostname, platform, release } from "os";
@@ -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.13",
229
+ cliVersion: "0.2.1",
230
230
  os: `${platform()} ${release()}`,
231
231
  hostname: hostname()
232
232
  });
@@ -709,11 +709,140 @@ async function executeFlowPages(draftId, data) {
709
709
  const response = await client.post(`/external/drafts/${draftId}/flow/execute`, data);
710
710
  return response.data;
711
711
  }
712
+ function parseContextFileArg(arg) {
713
+ const match = arg.match(/^(.+?):(\d+)(?::(\d+))?$/);
714
+ if (!match) return {
715
+ rawPath: arg
716
+ };
717
+ const [, pathPart, startStr, endStr] = match;
718
+ const startLine = parseInt(startStr, 10);
719
+ const endLine = void 0 !== endStr ? parseInt(endStr, 10) : void 0;
720
+ if (startLine < 1) {
721
+ output_error(`Invalid line range in "${arg}": start line must be >= 1`);
722
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
723
+ }
724
+ if (void 0 !== endLine && endLine < startLine) {
725
+ output_error(`Invalid line range in "${arg}": end line (${endLine}) must be >= start line (${startLine})`);
726
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
727
+ }
728
+ const cwd = process.cwd();
729
+ const extractedAbsolute = external_path_resolve(cwd, pathPart);
730
+ const originalAbsolute = external_path_resolve(cwd, arg);
731
+ if (!existsSync(extractedAbsolute) && existsSync(originalAbsolute)) return {
732
+ rawPath: arg
733
+ };
734
+ return {
735
+ rawPath: pathPart,
736
+ startLine,
737
+ endLine
738
+ };
739
+ }
740
+ function extractLines(content, start, end) {
741
+ const lines = content.split('\n');
742
+ const from = Math.max(0, start - 1);
743
+ const to = Math.min(lines.length, end);
744
+ return lines.slice(from, to).join('\n');
745
+ }
746
+ function mergeRanges(ranges) {
747
+ if (0 === ranges.length) return [];
748
+ const sorted = [
749
+ ...ranges
750
+ ].sort((a, b)=>a.start - b.start);
751
+ const merged = [
752
+ sorted[0]
753
+ ];
754
+ for(let i = 1; i < sorted.length; i++){
755
+ const prev = merged[merged.length - 1];
756
+ const curr = sorted[i];
757
+ if (prev.end >= curr.start - 1 || prev.end === 1 / 0) prev.end = Math.max(prev.end, curr.end);
758
+ else merged.push({
759
+ ...curr
760
+ });
761
+ }
762
+ return merged;
763
+ }
764
+ function formatRangeContent(content, ranges) {
765
+ const totalLines = content.split('\n').length;
766
+ const parts = [];
767
+ for(let i = 0; i < ranges.length; i++){
768
+ const range = ranges[i];
769
+ const effectiveEnd = range.end === 1 / 0 ? totalLines : Math.min(range.end, totalLines);
770
+ if (i > 0) {
771
+ const prevEnd = ranges[i - 1].end === 1 / 0 ? totalLines : Math.min(ranges[i - 1].end, totalLines);
772
+ const gapStart = prevEnd + 1;
773
+ const gapEnd = range.start - 1;
774
+ if (gapEnd >= gapStart) {
775
+ const gapSize = gapEnd - gapStart + 1;
776
+ parts.push(`// ... (lines ${gapStart}-${gapEnd} omitted, ${gapSize} lines) ...`);
777
+ }
778
+ }
779
+ parts.push(`// === Lines ${range.start}-${effectiveEnd} ===`);
780
+ parts.push(extractLines(content, range.start, effectiveEnd));
781
+ }
782
+ return parts.join('\n');
783
+ }
784
+ function readContextFiles(filePaths) {
785
+ const cwd = process.cwd();
786
+ const refs = filePaths.map(parseContextFileArg);
787
+ const groups = new Map();
788
+ for (const ref of refs){
789
+ const absolutePath = external_path_resolve(cwd, ref.rawPath);
790
+ const relPath = relative(cwd, absolutePath);
791
+ let group = groups.get(absolutePath);
792
+ if (!group) {
793
+ group = {
794
+ relPath,
795
+ fullFile: false,
796
+ ranges: []
797
+ };
798
+ groups.set(absolutePath, group);
799
+ }
800
+ if (void 0 === ref.startLine) group.fullFile = true;
801
+ else group.ranges.push({
802
+ start: ref.startLine,
803
+ end: ref.endLine ?? 1 / 0
804
+ });
805
+ }
806
+ const results = [];
807
+ for (const [absolutePath, group] of groups){
808
+ let content;
809
+ try {
810
+ content = readFileSync(absolutePath, 'utf-8');
811
+ } catch (err) {
812
+ const msg = err instanceof Error ? err.message : 'Unknown error';
813
+ output_error(`Failed to read context file "${group.relPath}": ${msg}`);
814
+ process.exit(EXIT_CODES.VALIDATION_ERROR);
815
+ }
816
+ if (group.fullFile || 0 === group.ranges.length) {
817
+ results.push({
818
+ filename: group.relPath,
819
+ content
820
+ });
821
+ continue;
822
+ }
823
+ const merged = mergeRanges(group.ranges);
824
+ const totalLines = content.split('\n').length;
825
+ if (1 === merged.length && merged[0].start <= 1 && (merged[0].end === 1 / 0 || merged[0].end >= totalLines)) {
826
+ results.push({
827
+ filename: group.relPath,
828
+ content
829
+ });
830
+ continue;
831
+ }
832
+ const formatted = formatRangeContent(content, merged);
833
+ results.push({
834
+ filename: group.relPath,
835
+ content: formatted
836
+ });
837
+ }
838
+ return results;
839
+ }
712
840
  function createCreateDesignDraftCommand() {
713
- const command = new Command('create-design-draft').description('Create a design draft from scratch without any reference, Default dont use this, use iterate-design-draft instead').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)=>{
841
+ const command = new Command('create-design-draft').description('Create a design draft from scratch without any reference, Default dont use this, use iterate-design-draft instead').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('--context-file <paths...>', 'UI source files to include as context for AI generation').option('--json', 'Output in JSON format').action(async (options)=>{
714
842
  if (options.json) setJsonMode(true);
715
843
  job_runner_requireAuth(manager_isAuthenticated);
716
844
  validateDeviceMode(options.device);
845
+ const contextFiles = options.contextFile ? readContextFiles(options.contextFile) : void 0;
717
846
  await runJob({
718
847
  startLabel: 'Creating design draft...',
719
848
  pollingLabel: 'Generating design with AI...',
@@ -723,7 +852,8 @@ function createCreateDesignDraftCommand() {
723
852
  startJob: ()=>createDraft(options.projectId, {
724
853
  title: options.title,
725
854
  prompt: options.prompt,
726
- deviceMode: options.device
855
+ deviceMode: options.device,
856
+ contextFiles
727
857
  }),
728
858
  transformResult: (job)=>({
729
859
  draftId: job.result.draftId,
@@ -795,17 +925,20 @@ Usage Examples:
795
925
  superdesign iterate-design-draft --draft-id <id> -p "dark theme" -p "minimal" -p "bold" --mode branch --json
796
926
 
797
927
  # Iterate: Auto explore (only give exploration direction, and let Superdesign fill in details, e.g. explore different styles; Default do NOT use auto explore mode)
798
- superdesign iterate-design-draft --draft-id <id> -p "..." --mode branch --count 3 --json`).requiredOption('--draft-id <id>', 'Draft ID to iterate on').requiredOption('-p, --prompt <prompt...>', 'Iteration prompt(s). Use multiple -p for specific prompts per variation.').requiredOption('--mode <mode>', 'Iteration mode (replace or branch)').option('--count <count>', 'Number of variations (1-4). Only used when single prompt provided.').option('--json', 'Output in JSON format').addOption(new Option('--project-id <id>').hideHelp()).action(async (options)=>{
928
+ superdesign iterate-design-draft --draft-id <id> -p "..." --mode branch --count 3 --json`).requiredOption('--draft-id <id>', 'Draft ID to iterate on').requiredOption('-p, --prompt <prompt...>', 'Iteration prompt(s). Use multiple -p for specific prompts per variation.').requiredOption('--mode <mode>', 'Iteration mode (replace or branch)').option('--count <count>', 'Number of variations (1-4). Only used when single prompt provided.').option('--context-file <paths...>', 'UI source files to include as context for AI generation').option('--json', 'Output in JSON format').addOption(new Option('--project-id <id>').hideHelp()).action(async (options)=>{
799
929
  if (options.json) setJsonMode(true);
800
930
  job_runner_requireAuth(manager_isAuthenticated);
801
931
  const { prompts, count, mode } = validateOptions(options);
932
+ const contextFiles = options.contextFile ? readContextFiles(options.contextFile) : void 0;
802
933
  const requestData = prompts.length > 1 ? {
803
934
  prompts,
804
- mode
935
+ mode,
936
+ contextFiles
805
937
  } : {
806
938
  prompt: prompts[0],
807
939
  count: count,
808
- mode
940
+ mode,
941
+ contextFiles
809
942
  };
810
943
  await runJob({
811
944
  startLabel: 'Starting iteration...',
@@ -876,7 +1009,7 @@ function parsePages(pagesJson) {
876
1009
  return parsed;
877
1010
  }
878
1011
  function createExecuteFlowPagesCommand() {
879
- 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)=>{
1012
+ 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('--context-file <paths...>', 'UI source files to include as context for AI generation').option('--json', 'Output in JSON format').action(async (options)=>{
880
1013
  if (options.json) setJsonMode(true);
881
1014
  job_runner_requireAuth(manager_isAuthenticated);
882
1015
  let pages;
@@ -896,6 +1029,7 @@ function createExecuteFlowPagesCommand() {
896
1029
  output_error('Maximum 10 pages allowed');
897
1030
  process.exit(EXIT_CODES.VALIDATION_ERROR);
898
1031
  }
1032
+ const contextFiles = options.contextFile ? readContextFiles(options.contextFile) : void 0;
899
1033
  const timeoutMs = Math.max(300000, 2 * pages.length * 60000);
900
1034
  await runJob({
901
1035
  startLabel: `Generating ${pages.length} flow page(s)...`,
@@ -906,7 +1040,8 @@ function createExecuteFlowPagesCommand() {
906
1040
  timeoutMs,
907
1041
  startJob: ()=>executeFlowPages(options.draftId, {
908
1042
  flowContext: options.context,
909
- pages
1043
+ pages,
1044
+ contextFiles
910
1045
  }),
911
1046
  transformResult: (job)=>({
912
1047
  drafts: job.result.drafts,
@@ -1219,7 +1354,7 @@ async function trackCommand(opts) {
1219
1354
  durationMs: opts.durationMs,
1220
1355
  errorCode: opts.errorCode,
1221
1356
  options: opts.options,
1222
- cliVersion: "0.1.13",
1357
+ cliVersion: "0.2.1",
1223
1358
  os: `${os.platform()} ${os.release()}`
1224
1359
  };
1225
1360
  const posthog = getPostHog();
@@ -1287,7 +1422,7 @@ function findFailedCommand(program) {
1287
1422
  }
1288
1423
  function createProgram() {
1289
1424
  const program = new Command();
1290
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.1.13");
1425
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.2.1");
1291
1426
  program.configureOutput({
1292
1427
  writeErr: (str)=>{
1293
1428
  if (!process.argv.includes('--json')) process.stderr.write(str);
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Utilities for reading context files from disk.
3
+ *
4
+ * Supports optional line-range syntax:
5
+ * path → full file
6
+ * path:startLine → from startLine to end-of-file (1-based)
7
+ * path:start:end → lines start–end inclusive (1-based)
8
+ *
9
+ * Multiple ranges from the same file are merged into a single ContextFile
10
+ * with omission markers between non-contiguous ranges.
11
+ */
12
+ import type { ContextFile } from '../api/drafts';
13
+ /** A parsed reference to a file with an optional line range. */
14
+ interface ParsedFileRef {
15
+ /** Raw path as the user typed it (before resolution). */
16
+ rawPath: string;
17
+ /** Start line (1-based, inclusive). `undefined` means "from the beginning". */
18
+ startLine?: number;
19
+ /** End line (1-based, inclusive). `undefined` means "to the end of the file". */
20
+ endLine?: number;
21
+ }
22
+ /** Normalised, validated range ready for slicing. */
23
+ interface LineRange {
24
+ start: number;
25
+ end: number;
26
+ }
27
+ /**
28
+ * Parse a `--context-file` argument into a path and optional line range.
29
+ *
30
+ * Accepted forms:
31
+ * "src/App.tsx" → { rawPath: "src/App.tsx" }
32
+ * "src/App.tsx:10:50" → { rawPath: "src/App.tsx", startLine: 10, endLine: 50 }
33
+ * "src/App.tsx:10" → { rawPath: "src/App.tsx", startLine: 10 }
34
+ *
35
+ * Windows paths (C:\foo\bar.ts) are handled: we only split on the
36
+ * *last* colon-separated numeric segments.
37
+ */
38
+ export declare function parseContextFileArg(arg: string): ParsedFileRef;
39
+ /**
40
+ * Extract a range of lines from content.
41
+ * Both `start` and `end` are 1-based inclusive.
42
+ * If `end` exceeds total lines the result is clamped.
43
+ */
44
+ export declare function extractLines(content: string, start: number, end: number): string;
45
+ /**
46
+ * Merge overlapping or adjacent line ranges.
47
+ * Input ranges do not need to be sorted.
48
+ * Returns sorted, non-overlapping ranges.
49
+ */
50
+ export declare function mergeRanges(ranges: LineRange[]): LineRange[];
51
+ /**
52
+ * Build annotated content for multiple line ranges from a single file.
53
+ *
54
+ * Each range gets a `// === Lines X-Y ===` header.
55
+ * Gaps between ranges get `// ... (lines X-Y omitted, N lines) ...` markers.
56
+ */
57
+ export declare function formatRangeContent(content: string, ranges: LineRange[]): string;
58
+ /**
59
+ * Read context files from disk and return as ContextFile array.
60
+ *
61
+ * Supports `path:startLine:endLine` syntax. Multiple references to the
62
+ * same file are aggregated: the file is read once and ranges are merged.
63
+ *
64
+ * If any file has *both* a full-file reference and partial ranges, the
65
+ * full file content wins (no annotations).
66
+ *
67
+ * Exits the process if any file cannot be read.
68
+ */
69
+ export declare function readContextFiles(filePaths: string[]): ContextFile[];
70
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdesign/cli",
3
- "version": "0.1.13",
3
+ "version": "0.2.1",
4
4
  "description": "CLI for SuperDesign Platform - agent skills for Claude Code",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",