@superdesign/cli 0.2.0 → 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.
@@ -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.2.0";
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.2.0",
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,23 +799,133 @@ 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
+ }
802
874
  function readContextFiles(filePaths) {
803
875
  const cwd = process.cwd();
804
- return filePaths.map((filePath)=>{
805
- const absolutePath = (0, external_path_namespaceObject.resolve)(cwd, filePath);
806
- try {
807
- const content = (0, external_fs_namespaceObject.readFileSync)(absolutePath, 'utf-8');
808
- const filename = (0, external_path_namespaceObject.relative)(cwd, absolutePath);
809
- return {
810
- filename,
811
- content
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: []
812
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');
813
901
  } catch (err) {
814
902
  const msg = err instanceof Error ? err.message : 'Unknown error';
815
- output_error(`Failed to read context file "${filePath}": ${msg}`);
903
+ output_error(`Failed to read context file "${group.relPath}": ${msg}`);
816
904
  process.exit(EXIT_CODES.VALIDATION_ERROR);
817
905
  }
818
- });
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;
819
929
  }
820
930
  function createCreateDesignDraftCommand() {
821
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)=>{
@@ -1336,7 +1446,7 @@ Usage Examples:
1336
1446
  durationMs: opts.durationMs,
1337
1447
  errorCode: opts.errorCode,
1338
1448
  options: opts.options,
1339
- cliVersion: "0.2.0",
1449
+ cliVersion: "0.2.1",
1340
1450
  os: `${external_os_default().platform()} ${external_os_default().release()}`
1341
1451
  };
1342
1452
  const posthog = getPostHog();
@@ -1404,7 +1514,7 @@ Usage Examples:
1404
1514
  }
1405
1515
  function createProgram() {
1406
1516
  const program = new external_commander_namespaceObject.Command();
1407
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.2.0");
1517
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.2.1");
1408
1518
  program.configureOutput({
1409
1519
  writeErr: (str)=>{
1410
1520
  if (!process.argv.includes('--json')) process.stderr.write(str);
package/dist/index.js CHANGED
@@ -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.2.0",
229
+ cliVersion: "0.2.1",
230
230
  os: `${platform()} ${release()}`,
231
231
  hostname: hostname()
232
232
  });
@@ -709,23 +709,133 @@ 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
+ }
712
784
  function readContextFiles(filePaths) {
713
785
  const cwd = process.cwd();
714
- return filePaths.map((filePath)=>{
715
- const absolutePath = external_path_resolve(cwd, filePath);
716
- try {
717
- const content = readFileSync(absolutePath, 'utf-8');
718
- const filename = relative(cwd, absolutePath);
719
- return {
720
- filename,
721
- content
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: []
722
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');
723
811
  } catch (err) {
724
812
  const msg = err instanceof Error ? err.message : 'Unknown error';
725
- output_error(`Failed to read context file "${filePath}": ${msg}`);
813
+ output_error(`Failed to read context file "${group.relPath}": ${msg}`);
726
814
  process.exit(EXIT_CODES.VALIDATION_ERROR);
727
815
  }
728
- });
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;
729
839
  }
730
840
  function createCreateDesignDraftCommand() {
731
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)=>{
@@ -1244,7 +1354,7 @@ async function trackCommand(opts) {
1244
1354
  durationMs: opts.durationMs,
1245
1355
  errorCode: opts.errorCode,
1246
1356
  options: opts.options,
1247
- cliVersion: "0.2.0",
1357
+ cliVersion: "0.2.1",
1248
1358
  os: `${os.platform()} ${os.release()}`
1249
1359
  };
1250
1360
  const posthog = getPostHog();
@@ -1312,7 +1422,7 @@ function findFailedCommand(program) {
1312
1422
  }
1313
1423
  function createProgram() {
1314
1424
  const program = new Command();
1315
- program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.2.0");
1425
+ program.name('superdesign').description('SuperDesign CLI - AI product designer for coding agents').version("0.2.1");
1316
1426
  program.configureOutput({
1317
1427
  writeErr: (str)=>{
1318
1428
  if (!process.argv.includes('--json')) process.stderr.write(str);
@@ -1,9 +1,70 @@
1
1
  /**
2
- * Utilities for reading context files from disk
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.
3
11
  */
4
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;
5
58
  /**
6
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
+ *
7
67
  * Exits the process if any file cannot be read.
8
68
  */
9
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.2.0",
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",
@@ -53,4 +53,4 @@
53
53
  "directory": "packages/cli"
54
54
  },
55
55
  "license": "MIT"
56
- }
56
+ }