@slicemachine/manager 0.25.7-alpha.jp-figma-to-slice-1.2 → 0.25.7-alpha.jp-figma-to-slice-1.4

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.
@@ -39,9 +39,17 @@ import { CustomTypeFormat } from "./types";
39
39
  import { existsSync } from "node:fs";
40
40
  import { tmpdir } from "node:os";
41
41
  import { randomUUID } from "node:crypto";
42
- import { join as joinPath } from "node:path";
43
- import { mkdtemp, rename, rm, writeFile, readFile } from "node:fs/promises";
42
+ import { join as joinPath, relative as relativePath } from "node:path";
43
+ import {
44
+ mkdtemp,
45
+ rename,
46
+ rm,
47
+ writeFile,
48
+ readFile,
49
+ readdir,
50
+ } from "node:fs/promises";
44
51
  import { query as queryClaude } from "@anthropic-ai/claude-agent-sdk";
52
+ import { APPLICATION_MODE } from "../../constants/APPLICATION_MODE";
45
53
 
46
54
  type SliceMachineManagerReadCustomTypeLibraryReturnType = {
47
55
  ids: string[];
@@ -567,6 +575,8 @@ export class CustomTypesManager extends BaseManager {
567
575
  .object({ llmProxyUrl: z.string().url() })
568
576
  .parse(exp.payload);
569
577
 
578
+ console.info({ llmProxyUrl });
579
+
570
580
  let tmpDir: string | undefined;
571
581
  try {
572
582
  const config = await this.project.getSliceMachineConfig();
@@ -591,28 +601,14 @@ export class CustomTypesManager extends BaseManager {
591
601
  );
592
602
  }
593
603
 
594
- let frameworkFileExtension: string | undefined;
595
- if (framework.type === "nextjs") {
596
- frameworkFileExtension = "tsx";
597
- } else if (framework.type === "nuxt") {
598
- frameworkFileExtension = "vue";
599
- } else if (framework.type === "sveltekit") {
600
- frameworkFileExtension = "svelte";
601
- }
602
-
603
- if (!frameworkFileExtension) {
604
- throw new Error(
605
- "Could not determine framework from Slice Machine config.",
606
- );
607
- }
608
-
609
604
  const projectRoot = await this.project.getRoot();
610
605
  const libraryAbsPath = joinPath(projectRoot, libraryID);
606
+ const cwd = libraryAbsPath;
611
607
 
612
608
  tmpDir = await mkdtemp(
613
609
  joinPath(tmpdir(), "slice-machine-infer-slice-tmp-"),
614
610
  );
615
- const tmpImagePath = joinPath(tmpDir, `${randomUUID()}.png`);
611
+ const tmpImageAbsPath = joinPath(tmpDir, `${randomUUID()}.png`);
616
612
  const response = await fetch(imageUrl);
617
613
  if (!response.ok) {
618
614
  throw new Error(
@@ -620,163 +616,207 @@ export class CustomTypesManager extends BaseManager {
620
616
  );
621
617
  }
622
618
  await writeFile(
623
- tmpImagePath,
619
+ tmpImageAbsPath,
624
620
  Buffer.from(await response.arrayBuffer()),
625
621
  );
626
622
 
623
+ const otherSlices = (
624
+ await Promise.all(
625
+ (await readdir(libraryAbsPath, { withFileTypes: true })).flatMap(
626
+ async (path) => {
627
+ try {
628
+ if (!path.isDirectory()) {
629
+ throw new Error("Not a directory");
630
+ }
631
+
632
+ const absPath = joinPath(libraryAbsPath, path.name);
633
+ const modelAbsPath = joinPath(absPath, "model.json");
634
+ if (!existsSync(modelAbsPath)) {
635
+ throw new Error("Model file not found");
636
+ }
637
+
638
+ const decoded = SharedSlice.decode(
639
+ JSON.parse(await readFile(modelAbsPath, "utf-8")),
640
+ );
641
+ if (decoded._tag === "Left") {
642
+ throw new Error("Invalid model file");
643
+ }
644
+
645
+ return [
646
+ {
647
+ absPath,
648
+ relPath: relativePath(cwd, absPath),
649
+ name: decoded.right.name,
650
+ },
651
+ ];
652
+ } catch {
653
+ return [];
654
+ }
655
+ },
656
+ ),
657
+ )
658
+ ).flat();
659
+
660
+ const prompt = `CRITICAL INSTRUCTIONS - READ FIRST:
661
+ - You MUST start immediately with Step 1.1. DO NOT read, analyze, or explore any project files first.
662
+ - Work step-by-step through the numbered tasks below.
663
+ - DO NOT present any summary, explanation, or completion message after finishing.
664
+ - DO NOT create TODO lists while performing tasks.
665
+ - Keep responses minimal - only show necessary tool calls and brief progress notes.
666
+
667
+ # CONTEXT
668
+
669
+ The user wants to build a new Prismic Slice based on a design image they provided.
670
+ Your goal is to analyze the design image and generate the JSON model data and boilerplate code for the slice following Prismic requirements.
671
+
672
+ You will work under the slice library at <slice_library_directory_path>, where all the slices are stored.
673
+
674
+ # AVAILABLE RESOURCES
675
+
676
+ <framework>
677
+ ${framework.label}
678
+ </framework>
679
+
680
+ <design_image_path>
681
+ ${tmpImageAbsPath}
682
+ </design_image_path>
683
+
684
+ <slice_library_directory_path>
685
+ ${libraryAbsPath}
686
+ </slice_library_directory_path>
687
+
688
+ <disallowed_slice_names>
689
+ ${otherSlices.map((slice) => `- ${slice.name}`).join("\n")}
690
+ </disallowed_slice_names>
691
+
692
+ # AVAILABLE TOOLS
693
+
694
+ You have access to specialized Prismic MCP tools for this task:
695
+
696
+ <tool name="mcp__prismic__how_to_model_slice">
697
+ <description>
698
+ Provides detailed guidance on creating Prismic slice models, including field types, naming conventions, and best practices.
699
+ </description>
700
+ <when_to_use>
701
+ Call this tool in Step 2.1 to learn how to structure the slice model data for the design you analysed.
702
+ </when_to_use>
703
+ </tool>
704
+
705
+ <tool name="mcp__prismic__how_to_code_slice">
706
+ <description>
707
+ Provides guidance on implementing Prismic slice components, including how to use Prismic field components, props structure, and best practices.
708
+ </description>
709
+ <when_to_use>
710
+ Call this tool in Step 2.1 to learn how to properly structure the slice component with Prismic fields.
711
+ </when_to_use>
712
+ </tool>
713
+
714
+ <tool name="mcp__prismic__save_slice_data">
715
+ <description>
716
+ Validates and saves the slice model data to model.json. This is the ONLY way to create the model file.
717
+ </description>
718
+ <when_to_use>
719
+ Call this tool in Step 2.3 after you have built the complete slice model structure in memory.
720
+ </when_to_use>
721
+ </tool>
722
+
723
+ # TASK REQUIREMENTS
724
+
725
+ ## Step 1: Gather information from the design image
726
+ 1.1. Analyse the design image at <design_image_path>.
727
+ 1.2. Identify all elements in the image that should be dynamically editable (e.g., headings, paragraphs, images, links, buttons, etc.).
728
+ 1.3. Come up with a UNIQUE name for the new slice based on the content of the image, DO NOT use any of the names in <disallowed_slice_names>.
729
+
730
+ ## Step 2: Model the Prismic slice
731
+ 2.1. Call mcp__prismic__how_to_model_slice to learn how to structure the model for this design.
732
+ 2.2. Build the complete slice JSON model data in memory based on the guidance received and the information extracted from the image.
733
+ 2.3. Call mcp__prismic__save_slice_data to save the model (DO NOT manually write model.json) in the slice library at <slice_library_directory_path>.
734
+
735
+ ## Step 3: Code a boilerplate slice component based on the model
736
+ 3.1. Call mcp__prismic__how_to_code_slice to learn how to properly structure the slice component with Prismic fields.
737
+ 3.2. Update the slice component code at <slice_library_directory_path>/index.*, replacing the placeholder code with boilerplate code with the following requirements:
738
+ - Must NOT be based on existing slices or components from the codebase.
739
+ - Must render all the Prismic components to display the fields of the slice model created at <slice_model_path>.
740
+ - Must be a valid ${framework.label} component.
741
+ - Must NOT have any styling/CSS. No inlines styles or classNames. Just the skeleton component structure.
742
+ - Must NOT use any other custom component or functions from the user's codebase.
743
+ - Avoid creating unnecessary wrapper elements, like if they only wrap a single component (e.g., <div><PrismicRichText /></div>).
744
+
745
+ ## Step 4: Present the newly created slice path
746
+ 4.1. Present the path to the newly created slice in the following format: <new_slice_path>${libraryAbsPath}/MyNewSlice</new_slice_path>.
747
+ - "MyNewSlice" must be the name of the directory of the newly created slice.
748
+
749
+ # EXAMPLE OF CORRECT EXECUTION
750
+
751
+ <example>
752
+ Assistant: Step 1.1: Analysing design image...
753
+ [reads <design_image_path>]
754
+
755
+ Step 1.2: Identifying editable content elements...
756
+ [identifies: title field, description field, buttonText field, buttonLink field, backgroundImage field]
757
+
758
+ Step 1.3: Listing slice directories under <slice_library_directory_path>...
759
+ [lists slice directories: Hero, Hero2, Hero3]
760
+
761
+ Step 1.4: Coming up with a unique name for the new slice...
762
+ [comes up with a unique name for the new slice: Hero4]
763
+
764
+ Step 2.1: Getting Prismic modeling guidance...
765
+ [calls mcp__prismic__how_to_model_slice]
766
+
767
+ Step 2.2: Building slice model based on guidance and the information extracted...
768
+ [creates model with title field, description field, buttonText field, buttonLink field, backgroundImage field]
769
+
770
+ Step 2.3: Saving slice model...
771
+ [calls mcp__prismic__save_slice_data]
772
+
773
+ Step 3.1: Learning Prismic slice coding requirements...
774
+ [calls mcp__prismic__how_to_code_slice]
775
+
776
+ Step 3.2: Coding boilerplate slice component based on the model...
777
+ [updates component with Prismic field components, no styling, no other components]
778
+
779
+ Step 4.1: Presenting the path to the newly created slice...
780
+ [presents <new_slice_path>${joinPath(
781
+ libraryAbsPath,
782
+ "MyNewSlice",
783
+ )}</new_slice_path>]
784
+
785
+ # DELIVERABLES
786
+ - Slice model saved to <slice_library_directory_path>/model.json using mcp__prismic__save_slice_data
787
+ - Slice component at <slice_library_directory_path>/index.* updated with boilerplate code
788
+ - New slice path presented in the format mentioned in Step 3.1
789
+
790
+ YOU ARE NOT FINISHED UNTIL YOU HAVE THESE DELIVERABLES.
791
+
792
+ ---
793
+
794
+ FINAL REMINDERS:
795
+ - You MUST use mcp__prismic__save_slice_data to save the model
796
+ - You MUST call mcp__prismic__how_to_code_slice in Step 3.1
797
+ - DO NOT ATTEMPT TO BUILD THE APPLICATION
798
+ - START IMMEDIATELY WITH STEP 1.1 - NO PRELIMINARY ANALYSIS`;
799
+
627
800
  const queries = queryClaude({
628
- prompt: `CRITICAL INSTRUCTIONS - READ FIRST:
629
- - You MUST start immediately with Step 1.1. DO NOT read, analyze, or explore any project files first.
630
- - Work step-by-step through the numbered tasks below.
631
- - DO NOT present any summary, explanation, or completion message after finishing.
632
- - DO NOT create TODO lists while performing tasks.
633
- - Keep responses minimal - only show necessary tool calls and brief progress notes.
634
-
635
- # CONTEXT
636
-
637
- The user wants to build a new Prismic Slice based on a design image they provided.
638
- Your goal is to analyze the design image and generate the JSON model data and boilerplate code for the slice following Prismic requirements.
639
-
640
- You will work under the slice library at <slice_library_path>, where all the slices are stored.
641
-
642
- # AVAILABLE RESOURCES
643
-
644
- <design_image_path>
645
- ${tmpImagePath}
646
- </design_image_path>
647
-
648
- <slice_library_path>
649
- ${libraryAbsPath}
650
- </slice_library_path>
651
-
652
- <framework>
653
- ${framework.label}
654
- </framework>
655
-
656
- # AVAILABLE TOOLS
657
-
658
- You have access to specialized Prismic MCP tools for this task:
659
-
660
- <tool name="mcp__prismic__how_to_model_slice">
661
- <description>
662
- Provides detailed guidance on creating Prismic slice models, including field types, naming conventions, and best practices.
663
- </description>
664
- <when_to_use>
665
- Call this tool in Step 2.1 to learn how to structure the slice model data for the design you analysed.
666
- </when_to_use>
667
- </tool>
668
-
669
- <tool name="mcp__prismic__how_to_code_slice">
670
- <description>
671
- Provides guidance on implementing Prismic slice components, including how to use Prismic field components, props structure, and best practices.
672
- </description>
673
- <when_to_use>
674
- Call this tool in Step 2.1 to learn how to properly structure the slice component with Prismic fields.
675
- </when_to_use>
676
- </tool>
677
-
678
- <tool name="mcp__prismic__save_slice_data">
679
- <description>
680
- Validates and saves the slice model data to model.json. This is the ONLY way to create the model file.
681
- </description>
682
- <when_to_use>
683
- Call this tool in Step 2.3 after you have built the complete slice model structure in memory.
684
- </when_to_use>
685
- </tool>
686
-
687
- # TASK REQUIREMENTS
688
-
689
- ## Step 1: Gather information from the design image
690
- 1.1. Analyse the design image at <design_image_path>.
691
- 1.2. Identify all elements in the image that should be dynamically editable (e.g., headings, paragraphs, images, links, buttons, etc.).
692
- 1.3. List the slice directories under <slice_library_path>.
693
- 1.4. Come up with a unique name for the new slice based on the content of the image and the slice directories.
694
-
695
- ## Step 2: Model the Prismic slice
696
- 2.1. Call mcp__prismic__how_to_model_slice to learn how to structure the model for this design.
697
- - Make sure the name you use for the new slice does not yet exist in the slice library at <slice_library_path>. If it does, use a different name.
698
- 2.2. Build the complete slice JSON model data in memory based on the guidance received and the information extracted from the image.
699
- 2.3. Call mcp__prismic__save_slice_data to save the model (DO NOT manually write model.json) in the slice library at <slice_library_path>.
700
-
701
- ## Step 3: Code a boilerplate slice component based on the model
702
- 3.1. Call mcp__prismic__how_to_code_slice to learn how to properly structure the slice component with Prismic fields.
703
- 3.2. Update the slice component code at <slice_library_path>/index.${frameworkFileExtension}, replacing the placeholder code with boilerplate code with the following requirements:
704
- - Must NOT be based on existing slices or components from the codebase.
705
- - Must render all the Prismic components to display the fields of the slice model created at <slice_model_path>.
706
- - Must be a valid ${framework.label} component.
707
- - Must NOT have any styling/CSS. No inlines styles or classNames. Just the skeleton component structure.
708
- - Must NOT use any other custom component or functions from the user's codebase.
709
- - Avoid creating unnecessary wrapper elements, like if they only wrap a single component (e.g., <div><PrismicRichText /></div>).
710
-
711
- ## Step 4: Present the newly created slice path
712
- 4.1. Present the path to the newly created slice in the following format: <new_slice_path>${libraryAbsPath}/MyNewSlice</new_slice_path>.
713
- - "MyNewSlice" must be the name of the directory of the newly created slice.
714
-
715
- # EXAMPLE OF CORRECT EXECUTION
716
-
717
- <example>
718
- Assistant: Step 1.1: Analysing design image...
719
- [reads <design_image_path>]
720
-
721
- Step 1.2: Identifying editable content elements...
722
- [identifies: title field, description field, buttonText field, buttonLink field, backgroundImage field]
723
-
724
- Step 1.3: Listing slice directories under <slice_library_path>...
725
- [lists slice directories: Hero, Hero2, Hero3]
726
-
727
- Step 1.4: Coming up with a unique name for the new slice...
728
- [comes up with a unique name for the new slice: Hero4]
729
-
730
- Step 2.1: Getting Prismic modeling guidance...
731
- [calls mcp__prismic__how_to_model_slice]
732
-
733
- Step 2.2: Building slice model based on guidance and the information extracted...
734
- [creates model with title field, description field, buttonText field, buttonLink field, backgroundImage field]
735
-
736
- Step 2.3: Saving slice model...
737
- [calls mcp__prismic__save_slice_data]
738
-
739
- Step 3.1: Learning Prismic slice coding requirements...
740
- [calls mcp__prismic__how_to_code_slice]
741
-
742
- Step 3.2: Coding boilerplate slice component based on the model...
743
- [updates component with Prismic field components, no styling, no other components]
744
-
745
- Step 4.1: Presenting the path to the newly created slice...
746
- [presents <new_slice_path>${joinPath(
747
- libraryAbsPath,
748
- "MyNewSlice",
749
- )}</new_slice_path>]
750
-
751
- # DELIVERABLES
752
- - Slice model saved to <slice_library_path>/model.json using mcp__prismic__save_slice_data
753
- - Slice component at <slice_library_path>/index.${frameworkFileExtension} updated with boilerplate code
754
- - New slice path presented in the format mentioned in Step 3.1
755
-
756
- YOU ARE NOT FINISHED UNTIL YOU HAVE THESE DELIVERABLES.
757
-
758
- ---
759
-
760
- FINAL REMINDERS:
761
- - You MUST use mcp__prismic__save_slice_data to save the model
762
- - You MUST call mcp__prismic__how_to_code_slice in Step 3.1
763
- - DO NOT ATTEMPT TO BUILD THE APPLICATION
764
- - START IMMEDIATELY WITH STEP 1.1 - NO PRELIMINARY ANALYSIS;`,
801
+ prompt,
765
802
  options: {
766
- cwd: libraryAbsPath,
767
- stderr: (data) => console.error("inferSlice error:" + data),
803
+ cwd,
804
+ stderr: (data) => {
805
+ if (!data.startsWith("Spawning Claude Code process")) {
806
+ console.error("inferSlice error:" + data);
807
+ }
808
+ },
768
809
  model: "claude-haiku-4-5",
769
- permissionMode: "bypassPermissions",
810
+ permissionMode: "acceptEdits",
770
811
  allowedTools: [
771
- "Bash",
812
+ `Bash(${cwd})`,
772
813
  "Read",
773
- "FileSearch",
774
814
  "Grep",
775
815
  "Glob",
776
- "Task",
777
- "Edit",
778
816
  "Write",
817
+ "Edit",
779
818
  "MultiEdit",
819
+ "FileSearch",
780
820
  "mcp__prismic__how_to_model_slice",
781
821
  "mcp__prismic__how_to_code_slice",
782
822
  "mcp__prismic__save_slice_data",
@@ -786,6 +826,11 @@ export class CustomTypesManager extends BaseManager {
786
826
  `Write(**/model.json)`,
787
827
  "Edit(**/mocks.json)",
788
828
  "Write(**/mocks.json)",
829
+ ...otherSlices.flatMap((slice) => [
830
+ `Write(${slice.relPath})`,
831
+ `Edit(${slice.relPath})`,
832
+ `MultiEdit(${slice.relPath})`,
833
+ ]),
789
834
  ],
790
835
  env: {
791
836
  ...process.env,
@@ -807,6 +852,9 @@ export class CustomTypesManager extends BaseManager {
807
852
  let newSliceAbsPath: string | undefined;
808
853
 
809
854
  for await (const query of queries) {
855
+ if (process.env.SM_ENV !== APPLICATION_MODE.Production) {
856
+ console.info(JSON.stringify(query, null, 2));
857
+ }
810
858
  switch (query.type) {
811
859
  case "result":
812
860
  if (query.subtype === "success") {
@@ -835,7 +883,7 @@ export class CustomTypesManager extends BaseManager {
835
883
 
836
884
  // move the screenshot image to the new slice directory
837
885
  await rename(
838
- tmpImagePath,
886
+ tmpImageAbsPath,
839
887
  joinPath(newSliceAbsPath, "screenshot-default.png"),
840
888
  );
841
889