rook-cli 1.3.2 → 1.3.5

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.
Files changed (92) hide show
  1. package/package.json +3 -2
  2. package/rook-framework/PRD-INSTALL-COMMAND.md +379 -0
  3. package/rook-framework/PRD.md +1214 -0
  4. package/rook-framework/README.md +143 -0
  5. package/rook-framework/assets/rk-accordion.js +99 -0
  6. package/rook-framework/assets/rk-alert-dialog.js +132 -0
  7. package/rook-framework/assets/rk-bottom-app-bar.js +88 -0
  8. package/rook-framework/assets/rk-carousel.js +145 -0
  9. package/rook-framework/assets/rk-collapsible.js +151 -0
  10. package/rook-framework/assets/rk-dialog.js +161 -0
  11. package/rook-framework/assets/rk-drawer.js +214 -0
  12. package/rook-framework/assets/rk-framework-core.css +2554 -0
  13. package/rook-framework/assets/rk-framework-tokens.css +101 -0
  14. package/rook-framework/assets/rk-modal.js +91 -0
  15. package/rook-framework/assets/rk-popover.js +264 -0
  16. package/rook-framework/assets/rk-progress.js +81 -0
  17. package/rook-framework/assets/rk-quantity.js +91 -0
  18. package/rook-framework/assets/rk-scroll-area.js +286 -0
  19. package/rook-framework/assets/rk-sheet.js +157 -0
  20. package/rook-framework/assets/rk-tabs.js +179 -0
  21. package/rook-framework/assets/rk-toggle.js +153 -0
  22. package/rook-framework/blocks/rk-accordion.liquid +97 -0
  23. package/rook-framework/blocks/rk-badge.liquid +103 -0
  24. package/rook-framework/blocks/rk-button.liquid +166 -0
  25. package/rook-framework/blocks/rk-divider.liquid +100 -0
  26. package/rook-framework/blocks/rk-form-field.liquid +120 -0
  27. package/rook-framework/blocks/rk-icon.liquid +134 -0
  28. package/rook-framework/blocks/rk-image.liquid +198 -0
  29. package/rook-framework/blocks/rk-installments.liquid +99 -0
  30. package/rook-framework/blocks/rk-pix-discount.liquid +99 -0
  31. package/rook-framework/blocks/rk-price.liquid +128 -0
  32. package/rook-framework/blocks/rk-quantity.liquid +108 -0
  33. package/rook-framework/blocks/rk-quick-add.liquid +137 -0
  34. package/rook-framework/blocks/rk-skeleton.liquid +104 -0
  35. package/rook-framework/blocks/rk-typography.liquid +183 -0
  36. package/rook-framework/config/rk-color-scheme-group.json +138 -0
  37. package/rook-framework/config/rk-settings_schema.json +259 -0
  38. package/rook-framework/snippets/rk-accordion.liquid +31 -0
  39. package/rook-framework/snippets/rk-alert-dialog.liquid +83 -0
  40. package/rook-framework/snippets/rk-aspect-ratio.liquid +23 -0
  41. package/rook-framework/snippets/rk-badge.liquid +17 -0
  42. package/rook-framework/snippets/rk-bottom-app-bar.liquid +51 -0
  43. package/rook-framework/snippets/rk-button.liquid +49 -0
  44. package/rook-framework/snippets/rk-card.liquid +64 -0
  45. package/rook-framework/snippets/rk-carousel.liquid +74 -0
  46. package/rook-framework/snippets/rk-checkbox.liquid +34 -0
  47. package/rook-framework/snippets/rk-collapsible.liquid +52 -0
  48. package/rook-framework/snippets/rk-color-schemes-standalone.liquid +61 -0
  49. package/rook-framework/snippets/rk-color-schemes.liquid +43 -0
  50. package/rook-framework/snippets/rk-dialog.liquid +85 -0
  51. package/rook-framework/snippets/rk-divider.liquid +25 -0
  52. package/rook-framework/snippets/rk-drawer.liquid +81 -0
  53. package/rook-framework/snippets/rk-external-assets copy.liquid +33 -0
  54. package/rook-framework/snippets/rk-external-assets.liquid +68 -0
  55. package/rook-framework/snippets/rk-form-field.liquid +83 -0
  56. package/rook-framework/snippets/rk-gap-style.liquid +32 -0
  57. package/rook-framework/snippets/rk-icon.liquid +28 -0
  58. package/rook-framework/snippets/rk-image.liquid +60 -0
  59. package/rook-framework/snippets/rk-input.liquid +35 -0
  60. package/rook-framework/snippets/rk-installments.liquid +54 -0
  61. package/rook-framework/snippets/rk-item.liquid +69 -0
  62. package/rook-framework/snippets/rk-layout-style.liquid +37 -0
  63. package/rook-framework/snippets/rk-modal.liquid +31 -0
  64. package/rook-framework/snippets/rk-pix-discount.liquid +34 -0
  65. package/rook-framework/snippets/rk-popover.liquid +77 -0
  66. package/rook-framework/snippets/rk-price.liquid +48 -0
  67. package/rook-framework/snippets/rk-progress.liquid +38 -0
  68. package/rook-framework/snippets/rk-quantity.liquid +56 -0
  69. package/rook-framework/snippets/rk-quick-add.liquid +67 -0
  70. package/rook-framework/snippets/rk-scripts.liquid +17 -0
  71. package/rook-framework/snippets/rk-scroll-area.liquid +60 -0
  72. package/rook-framework/snippets/rk-sheet.liquid +86 -0
  73. package/rook-framework/snippets/rk-size-style.liquid +48 -0
  74. package/rook-framework/snippets/rk-skeleton.liquid +25 -0
  75. package/rook-framework/snippets/rk-spacing-padding.liquid +18 -0
  76. package/rook-framework/snippets/rk-spacing-style.liquid +54 -0
  77. package/rook-framework/snippets/rk-spinner.liquid +43 -0
  78. package/rook-framework/snippets/rk-swatch.liquid +33 -0
  79. package/rook-framework/snippets/rk-table.liquid +44 -0
  80. package/rook-framework/snippets/rk-tabs.liquid +52 -0
  81. package/rook-framework/snippets/rk-textarea.liquid +42 -0
  82. package/rook-framework/snippets/rk-toggle-group.liquid +27 -0
  83. package/rook-framework/snippets/rk-toggle.liquid +58 -0
  84. package/rook-framework/snippets/rk-typography.liquid +27 -0
  85. package/rook-framework/snippets/rk-variables.liquid +76 -0
  86. package/src/app.js +24 -0
  87. package/src/commands/InstallCommand.js +133 -0
  88. package/src/mcp/server.js +111 -1
  89. package/src/services/FrameworkInstaller.js +485 -0
  90. package/src/templates/block.liquid.txt +0 -15
  91. package/src/ui/PromptUI.js +15 -1
  92. package/src/utils/logger.js +1 -1
@@ -0,0 +1,27 @@
1
+ {% doc %}
2
+ Snippet: RkToggleGroup
3
+ Renders a Toggle Group that manages multiple <rk-toggle-element>.
4
+ Uses <rk-toggle-group> Web Component.
5
+
6
+ @param {string} type - 'single' or 'multiple' (default: 'single')
7
+ @param {string} toggles - The rendered HTML of the <rk-toggle-element>s
8
+ @param {string} [custom_class] - Extra CSS classes
9
+
10
+ @example
11
+ {% capture group_toggles %}
12
+ {% render 'rk-toggle', value: 'b', text: 'B', variant: 'outline', pressed: true %}
13
+ {% render 'rk-toggle', value: 'i', text: 'I', variant: 'outline' %}
14
+ {% render 'rk-toggle', value: 'u', text: 'U', variant: 'outline' %}
15
+ {% endcapture %}
16
+
17
+ {% render 'rk-toggle-group', type: 'multiple', toggles: group_toggles %}
18
+ {% enddoc %}
19
+
20
+ <rk-toggle-group
21
+ data-type="{{ type | default: 'single' }}"
22
+ role="group"
23
+ dir="ltr"
24
+ class="rk-toggle-group{% if custom_class %} {{ custom_class }}{% endif %}"
25
+ >
26
+ {{ toggles }}
27
+ </rk-toggle-group>
@@ -0,0 +1,58 @@
1
+ {% doc %}
2
+ Snippet: RkToggle
3
+ Renders a Toggle button (two-state switch).
4
+ Uses <rk-toggle-element> Web Component.
5
+
6
+ @param {string} value - The value when pressed
7
+ @param {string} [id] - Unique id
8
+ @param {string} [text] - Text label
9
+ @param {string} [icon] - HTML for the icon (usually an SVG)
10
+ @param {string} [variant] - 'default' or 'outline' (default: 'default')
11
+ @param {string} [size] - 'sm', 'md', 'lg' (default: 'md')
12
+ @param {boolean} [pressed] - Set true if it should start pressed
13
+ @param {boolean} [disabled] - Set
14
+ @param {string} [aria_label] - Required if only icon is used
15
+ @param {string} [custom_class] - Extra CSS classes
16
+
17
+ @example
18
+ {% render 'rk-toggle', icon: my_bold_svg, aria_label: 'Bold', pressed: true %}
19
+ {% enddoc %}
20
+
21
+ {%- liquid
22
+ assign t_variant = variant | default: 'default'
23
+ assign t_size = size | default: 'md'
24
+ assign t_pressed = pressed | default: false
25
+
26
+ capture class_list
27
+ echo 'rk-toggle'
28
+ echo ' rk-toggle--' | append: t_variant
29
+ echo ' rk-toggle--' | append: t_size
30
+ if t_pressed
31
+ echo ' rk-toggle--on'
32
+ else
33
+ echo ' rk-toggle--off'
34
+ endif
35
+ if custom_class
36
+ echo ' ' | append: custom_class
37
+ endif
38
+ endcapture
39
+ -%}
40
+
41
+ <rk-toggle-element data-value="{{ value | escape }}">
42
+ <button
43
+ type="button"
44
+ class="{{ class_list | strip_newlines }}"
45
+ {% if id %}id="{{ id }}"{% endif %}
46
+ {% if disabled %}disabled aria-disabled="true"{% endif %}
47
+ aria-pressed="{{ t_pressed | downcase }}"
48
+ {% if aria_label %}aria-label="{{ aria_label }}"{% endif %}
49
+ >
50
+ {%- if icon -%}
51
+ <span class="rk-toggle__icon">{{ icon }}</span>
52
+ {%- endif -%}
53
+
54
+ {%- if text -%}
55
+ <span class="rk-toggle__text">{{ text }}</span>
56
+ {%- endif -%}
57
+ </button>
58
+ </rk-toggle-element>
@@ -0,0 +1,27 @@
1
+ {% doc %}
2
+ Snippet: RkTypography
3
+ Renders a text element with configurable scale, weight and alignment.
4
+
5
+ @param {string} text - The text content (HTML safe)
6
+ @param {string} [tag] - HTML tag (default: 'p')
7
+ @param {string} [size] - 'xs'|'sm'|'md'|'lg'|'xl'|'2xl'|'3xl'|'4xl' (default: 'md')
8
+ @param {string} [align] - 'center' | 'right'
9
+ @param {boolean} [muted] - Muted color
10
+ @param {boolean} [bold] - Bold weight
11
+ @param {boolean} [uppercase] - Uppercase text
12
+ @param {string} [custom_class] - Extra CSS classes
13
+
14
+ @example
15
+ {% render 'rk-typography', text: 'Hello', tag: 'h2', size: '2xl', bold: true %}
16
+ {% enddoc %}
17
+
18
+ {%- liquid
19
+ assign el = tag | default: 'p'
20
+ assign t_size = size | default: 'md'
21
+ -%}
22
+
23
+ <{{ el }}
24
+ class="rk-typography rk-typography--{{ t_size }}{% if align %} rk-typography--{{ align }}{% endif %}{% if muted %} rk-typography--muted{% endif %}{% if bold %} rk-typography--bold{% endif %}{% if uppercase %} rk-typography--uppercase{% endif %}{% if custom_class %} {{ custom_class }}{% endif %}"
25
+ >
26
+ {{ text }}
27
+ </{{ el }}>
@@ -0,0 +1,76 @@
1
+ {% comment %}
2
+ Rook UI Core Framework — CSS Variables (Liquid → CSS)
3
+ =====================================================
4
+ Este snippet gera as CSS custom properties globais do framework:
5
+ - Cores globais padrão (:root) — usadas quando nenhum rk-scheme é aplicado
6
+ - Border-radius dinâmico
7
+ - Velocidade de transição
8
+
9
+ Para esquemas de cores por seção, veja rk-color-schemes.liquid.
10
+
11
+ Deve ser renderizado no <head> do layout/theme.liquid:
12
+ {% render 'rk-color-schemes' %} ← Esquemas de cores (por seção)
13
+ {% render 'rk-variables' %} ← Cores globais + forma + transição
14
+ {% endcomment %}
15
+
16
+ {% style %}
17
+ :root {
18
+ /* ================================================================
19
+ CORES GLOBAIS — Fallback quando nenhum rk-scheme é aplicado
20
+ ================================================================ */
21
+
22
+ /* Primárias */
23
+ --rk-color-primary: {{ settings.rk_color_primary }};
24
+ --rk-color-primary-hover: {{ settings.rk_color_primary_hover }};
25
+ --rk-color-primary-foreground: {{ settings.rk_color_primary_foreground }};
26
+
27
+ /* Secundárias */
28
+ --rk-color-secondary: {{ settings.rk_color_secondary }};
29
+ --rk-color-secondary-hover: {{ settings.rk_color_secondary_hover }};
30
+
31
+ /* Fundos */
32
+ --rk-color-bg: {{ settings.rk_color_background }};
33
+ --rk-color-bg-secondary: {{ settings.rk_color_background_secondary }};
34
+
35
+ /* Semânticas */
36
+ --rk-color-success: {{ settings.rk_color_success }};
37
+ --rk-color-danger: {{ settings.rk_color_danger }};
38
+ --rk-color-destructive: {{ settings.rk_color_danger }};
39
+
40
+ /* Texto */
41
+ --rk-color-text: {{ settings.rk_color_text }};
42
+ --rk-color-text-muted: {{ settings.rk_color_text_muted }};
43
+
44
+ /* Bordas e Foco */
45
+ --rk-color-border: {{ settings.rk_color_border }};
46
+ --rk-color-ring: {{ settings.rk_color_ring }};
47
+
48
+ {%- comment -%}
49
+ Valores RGB derivados para uso com opacity.
50
+ Ex: rgb(var(--rk-color-border-rgb) / 0.5)
51
+ {%- endcomment -%}
52
+ {%- assign rk_border_color = settings.rk_color_border -%}
53
+ --rk-color-border-rgb: {{ rk_border_color.red }} {{ rk_border_color.green }} {{ rk_border_color.blue }};
54
+
55
+ {%- assign rk_text_color = settings.rk_color_text -%}
56
+ --rk-color-text-rgb: {{ rk_text_color.red }} {{ rk_text_color.green }} {{ rk_text_color.blue }};
57
+
58
+ /* Hover de borda (derivado) */
59
+ --rk-color-border-hover: rgb({{ rk_text_color.red }} {{ rk_text_color.green }} {{ rk_text_color.blue }} / 0.3);
60
+
61
+ /* ================================================================
62
+ FORMA — Border Radius dinâmico
63
+ ================================================================ */
64
+ --rk-radius: {{ settings.rk_border_radius }}px;
65
+ --rk-radius-sm: {{ settings.rk_border_radius_sm }}px;
66
+ --rk-radius-md: {{ settings.rk_border_radius }}px;
67
+ --rk-radius-lg: {{ settings.rk_border_radius_lg }}px;
68
+ --rk-radius-pill: {{ settings.rk_border_radius_pill }}px;
69
+
70
+ /* ================================================================
71
+ TRANSIÇÃO — Velocidade dinâmica
72
+ ================================================================ */
73
+ --rk-transition-speed: {{ settings.rk_transition_speed }}ms;
74
+ --rk-transition: var(--rk-transition-speed) cubic-bezier(0.4, 0, 0.2, 1);
75
+ }
76
+ {% endstyle %}
package/src/app.js CHANGED
@@ -22,6 +22,8 @@ import { AddCommand } from './commands/AddCommand.js';
22
22
  import { ConfigCommand } from './commands/ConfigCommand.js';
23
23
  import { GenerateCommand } from './commands/GenerateCommand.js';
24
24
  import { ScaffoldService } from './services/ScaffoldService.js';
25
+ import { FrameworkInstaller } from './services/FrameworkInstaller.js';
26
+ import { InstallCommand } from './commands/InstallCommand.js';
25
27
  import { CLI_NAME } from './config/constants.js';
26
28
 
27
29
  export class App {
@@ -61,6 +63,14 @@ export class App {
61
63
  this.scaffoldService,
62
64
  this.conflictResolver
63
65
  );
66
+
67
+ this.frameworkInstaller = new FrameworkInstaller(this.logger);
68
+
69
+ this.installCommand = new InstallCommand(
70
+ this.logger,
71
+ this.promptUI,
72
+ this.frameworkInstaller
73
+ );
64
74
  }
65
75
 
66
76
  /**
@@ -108,6 +118,20 @@ export class App {
108
118
  await this.generateCommand.executar(nome, opcoes);
109
119
  });
110
120
 
121
+ // Comando: rook install
122
+ this.programa
123
+ .command('install')
124
+ .alias('i')
125
+ .description('Instala o Rook UI Core Framework no tema Shopify')
126
+ .option('--path <dir>', 'Diretório do tema Shopify (padrão: diretório atual)')
127
+ .option('--force', 'Sobrescreve arquivos sem perguntar')
128
+ .option('--skip-layout', 'Não modifica o theme.liquid')
129
+ .option('--skip-settings', 'Não modifica o settings_schema.json')
130
+ .action(async (opcoes) => {
131
+ this.logger.banner();
132
+ await this.installCommand.executar(opcoes);
133
+ });
134
+
111
135
  // Comando padrão (sem argumentos) — abre o menu interativo
112
136
  this.programa
113
137
  .action(async () => {
@@ -0,0 +1,133 @@
1
+ /**
2
+ * InstallCommand — Comando "install" do CLI.
3
+ *
4
+ * Orquestra o fluxo de instalação do Rook UI Core Framework:
5
+ * 1. Valida que o diretório é um tema Shopify
6
+ * 2. Detecta instalação prévia
7
+ * 3. Confirma com o usuário (modo interativo)
8
+ * 4. Delega ao FrameworkInstaller
9
+ *
10
+ * Princípio: Inversão de Dependência (DIP) — dependências injetadas
11
+ * Princípio: Responsabilidade Única (SRP) — orquestra apenas o fluxo do comando "install"
12
+ */
13
+
14
+ import path from 'path';
15
+
16
+ export class InstallCommand {
17
+
18
+ /**
19
+ * @param {import('../utils/logger.js').Logger} logger
20
+ * @param {import('../ui/PromptUI.js').PromptUI} promptUI
21
+ * @param {import('../services/FrameworkInstaller.js').FrameworkInstaller} frameworkInstaller
22
+ */
23
+ constructor(logger, promptUI, frameworkInstaller) {
24
+ this.logger = logger;
25
+ this.promptUI = promptUI;
26
+ this.frameworkInstaller = frameworkInstaller;
27
+ }
28
+
29
+ /**
30
+ * Executa o fluxo principal do comando "install".
31
+ *
32
+ * @param {Object} [opcoes={}]
33
+ * @param {string} [opcoes.path] - Diretório do tema Shopify (padrão: cwd)
34
+ * @param {boolean} [opcoes.force] - Sobrescreve sem perguntar
35
+ * @param {boolean} [opcoes.skipLayout] - Não modifica theme.liquid
36
+ * @param {boolean} [opcoes.skipSettings] - Não modifica settings_schema.json
37
+ * @returns {Promise<void>}
38
+ */
39
+ async executar(opcoes = {}) {
40
+ try {
41
+ const themePath = path.resolve(opcoes.path || process.cwd());
42
+ const isHeadless = opcoes.force || opcoes.path;
43
+
44
+ // 1. Validação: é um tema Shopify?
45
+ const isValid = await this.frameworkInstaller.validate(themePath);
46
+
47
+ if (!isValid) {
48
+ if (isHeadless) {
49
+ this.logger.erro(`O diretório "${themePath}" não parece ser um tema Shopify.`);
50
+ process.exitCode = 1;
51
+ return;
52
+ }
53
+
54
+ const continuar = await this.promptUI.confirmar(
55
+ 'Este diretório não parece ser um tema Shopify. Deseja continuar mesmo assim?',
56
+ false
57
+ );
58
+
59
+ if (!continuar) {
60
+ this.logger.aviso('Instalação cancelada.');
61
+ return;
62
+ }
63
+ }
64
+
65
+ // Detectar informações do tema
66
+ const themeInfo = await this.frameworkInstaller.detectThemeInfo(themePath);
67
+ if (themeInfo) {
68
+ this.logger.info(`Tema detectado: ${themeInfo.name} v${themeInfo.version}`);
69
+ }
70
+
71
+ // 2. Detectar instalação prévia
72
+ const jaInstalado = await this.frameworkInstaller.isAlreadyInstalled(themePath);
73
+
74
+ if (jaInstalado) {
75
+ if (isHeadless) {
76
+ this.logger.aviso('Rook UI já instalado. Reinstalando (--force)...');
77
+ } else {
78
+ const reinstalar = await this.promptUI.confirmar(
79
+ 'Rook UI já está instalado neste tema. Deseja reinstalar/atualizar?',
80
+ false
81
+ );
82
+
83
+ if (!reinstalar) {
84
+ this.logger.aviso('Instalação cancelada.');
85
+ return;
86
+ }
87
+ }
88
+ }
89
+
90
+ // 3. Confirmação (modo interativo)
91
+ if (!isHeadless) {
92
+ const confirmar = await this.promptUI.confirmar(
93
+ 'Instalar Rook UI Core Framework neste tema?',
94
+ true
95
+ );
96
+
97
+ if (!confirmar) {
98
+ this.logger.aviso('Instalação cancelada.');
99
+ return;
100
+ }
101
+ }
102
+
103
+ // 4. Execução
104
+ this.logger.destaque('\n ⚡ Instalando Rook UI Core Framework...\n');
105
+
106
+ const resultado = await this.frameworkInstaller.install(themePath, {
107
+ force: opcoes.force || false,
108
+ skipLayout: opcoes.skipLayout || false,
109
+ skipSettings: opcoes.skipSettings || false,
110
+ });
111
+
112
+ // 5. Resumo final
113
+ console.log('');
114
+ this.logger.destaque('═══════════════════════════════════════════');
115
+ this.logger.sucesso('🎉 Rook UI Core Framework instalado com sucesso!');
116
+ this.logger.sutil(` Assets: ${resultado.assets} arquivo(s)`);
117
+ this.logger.sutil(` Snippets: ${resultado.snippets} arquivo(s)`);
118
+ this.logger.sutil(` Blocks: ${resultado.blocks} arquivo(s)`);
119
+ this.logger.sutil(` Settings: ${resultado.settingsSections} seção(ões)`);
120
+ this.logger.sutil(` Layout: ${resultado.layoutPatched ? 'atualizado' : 'sem alteração'}`);
121
+ this.logger.destaque('═══════════════════════════════════════════');
122
+ console.log('');
123
+
124
+ } catch (erro) {
125
+ if (erro.name === 'ExitPromptError') {
126
+ this.logger.aviso('Operação cancelada pelo usuário.');
127
+ return;
128
+ }
129
+ this.logger.erro(`Erro durante a instalação: ${erro.message}`);
130
+ process.exitCode = 1;
131
+ }
132
+ }
133
+ }
package/src/mcp/server.js CHANGED
@@ -43,6 +43,7 @@ import { DownloadService } from '../services/DownloadService.js';
43
43
  import { ScaffoldService } from '../services/ScaffoldService.js';
44
44
  import { FileMapper } from '../filesystem/FileMapper.js';
45
45
  import { ConflictResolver } from '../filesystem/ConflictResolver.js';
46
+ import { FrameworkInstaller } from '../services/FrameworkInstaller.js';
46
47
  import { generateNames } from '../utils/stringUtils.js';
47
48
 
48
49
  import path from 'path';
@@ -69,6 +70,7 @@ const downloadService = new DownloadService(logger, tokenManager);
69
70
  const conflictResolver = new ConflictResolver(logger);
70
71
  const fileMapper = new FileMapper(logger, conflictResolver);
71
72
  const scaffoldService = new ScaffoldService(logger);
73
+ const frameworkInstaller = new FrameworkInstaller(logger);
72
74
 
73
75
  // No modo MCP, o ConflictResolver opera em modo headless (sobrescreve tudo)
74
76
  conflictResolver.definirDecisaoGlobal(true);
@@ -99,6 +101,28 @@ const InstallComponentSchema = z.object({
99
101
  .describe('Obrigatório no Cursor/Claude Desktop: Caminho absoluto para a raiz do projeto (ex: /Users/nome/projeto).'),
100
102
  });
101
103
 
104
+ const InstallFrameworkSchema = z.object({
105
+ projectPath: z
106
+ .string()
107
+ .min(1)
108
+ .describe('Obrigatório: Caminho absoluto para a raiz do tema Shopify (ex: /Users/nome/meu-tema).'),
109
+ force: z
110
+ .boolean()
111
+ .optional()
112
+ .default(true)
113
+ .describe('Sobrescreve arquivos existentes sem perguntar (padrão: true no MCP).'),
114
+ skipLayout: z
115
+ .boolean()
116
+ .optional()
117
+ .default(false)
118
+ .describe('Se true, não modifica o layout/theme.liquid.'),
119
+ skipSettings: z
120
+ .boolean()
121
+ .optional()
122
+ .default(false)
123
+ .describe('Se true, não modifica o config/settings_schema.json.'),
124
+ });
125
+
102
126
  const GenerateScaffoldSchema = z.object({
103
127
  name: z
104
128
  .string()
@@ -267,6 +291,58 @@ async function handleGenerateScaffold(args) {
267
291
  };
268
292
  }
269
293
 
294
+ /**
295
+ * Handler: install_framework
296
+ * Instala o Rook UI Core Framework em um tema Shopify.
297
+ */
298
+ async function handleInstallFramework(args) {
299
+ const { projectPath, force, skipLayout, skipSettings } = InstallFrameworkSchema.parse(args);
300
+
301
+ // Validar tema
302
+ const isValid = await frameworkInstaller.validate(projectPath);
303
+ if (!isValid) {
304
+ return {
305
+ content: [{
306
+ type: 'text',
307
+ text: JSON.stringify({
308
+ success: false,
309
+ message: `O diretório "${projectPath}" não parece ser um tema Shopify válido.`,
310
+ }, null, 2),
311
+ }],
312
+ };
313
+ }
314
+
315
+ // Detectar tema
316
+ const themeInfo = await frameworkInstaller.detectThemeInfo(projectPath);
317
+ const jaInstalado = await frameworkInstaller.isAlreadyInstalled(projectPath);
318
+
319
+ // Executar instalação
320
+ const resultado = await frameworkInstaller.install(projectPath, {
321
+ force,
322
+ skipLayout,
323
+ skipSettings,
324
+ });
325
+
326
+ return {
327
+ content: [{
328
+ type: 'text',
329
+ text: JSON.stringify({
330
+ success: true,
331
+ message: 'Rook UI Core Framework instalado com sucesso.',
332
+ theme: themeInfo || undefined,
333
+ wasReinstall: jaInstalado,
334
+ result: {
335
+ assets: resultado.assets,
336
+ snippets: resultado.snippets,
337
+ blocks: resultado.blocks,
338
+ settingsSections: resultado.settingsSections,
339
+ layoutPatched: resultado.layoutPatched,
340
+ },
341
+ }, null, 2),
342
+ }],
343
+ };
344
+ }
345
+
270
346
  // ═══════════════════════════════════════════════════════════════
271
347
  // Função auxiliar: Download + Distribuição
272
348
  // ═══════════════════════════════════════════════════════════════
@@ -354,6 +430,37 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
354
430
  required: ['name', 'type', 'projectPath'],
355
431
  },
356
432
  },
433
+ {
434
+ name: 'install_framework',
435
+ description:
436
+ 'Instala o Rook UI Core Framework em um tema Shopify. ' +
437
+ 'Copia assets, snippets e blocks, injeta settings no settings_schema.json e configura o layout/theme.liquid.',
438
+ inputSchema: {
439
+ type: 'object',
440
+ properties: {
441
+ projectPath: {
442
+ type: 'string',
443
+ description: 'Obrigatório: Caminho absoluto para a raiz do tema Shopify.',
444
+ },
445
+ force: {
446
+ type: 'boolean',
447
+ description: 'Sobrescreve arquivos existentes sem perguntar (padrão: true).',
448
+ default: true,
449
+ },
450
+ skipLayout: {
451
+ type: 'boolean',
452
+ description: 'Se true, não modifica o layout/theme.liquid.',
453
+ default: false,
454
+ },
455
+ skipSettings: {
456
+ type: 'boolean',
457
+ description: 'Se true, não modifica o config/settings_schema.json.',
458
+ default: false,
459
+ },
460
+ },
461
+ required: ['projectPath'],
462
+ },
463
+ },
357
464
  {
358
465
  name: 'generate_scaffold',
359
466
  description:
@@ -395,11 +502,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
395
502
  case 'install_component':
396
503
  return await handleInstallComponent(args);
397
504
 
505
+ case 'install_framework':
506
+ return await handleInstallFramework(args);
507
+
398
508
  case 'generate_scaffold':
399
509
  return await handleGenerateScaffold(args);
400
510
 
401
511
  default:
402
- throw new Error(`Tool desconhecida: "${name}". Tools disponíveis: list_components, install_component, generate_scaffold`);
512
+ throw new Error(`Tool desconhecida: "${name}". Tools disponíveis: list_components, install_component, install_framework, generate_scaffold`);
403
513
  }
404
514
  } catch (erro) {
405
515
  // Retorna erro estruturado para a IA