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.
- package/package.json +3 -2
- package/rook-framework/PRD-INSTALL-COMMAND.md +379 -0
- package/rook-framework/PRD.md +1214 -0
- package/rook-framework/README.md +143 -0
- package/rook-framework/assets/rk-accordion.js +99 -0
- package/rook-framework/assets/rk-alert-dialog.js +132 -0
- package/rook-framework/assets/rk-bottom-app-bar.js +88 -0
- package/rook-framework/assets/rk-carousel.js +145 -0
- package/rook-framework/assets/rk-collapsible.js +151 -0
- package/rook-framework/assets/rk-dialog.js +161 -0
- package/rook-framework/assets/rk-drawer.js +214 -0
- package/rook-framework/assets/rk-framework-core.css +2554 -0
- package/rook-framework/assets/rk-framework-tokens.css +101 -0
- package/rook-framework/assets/rk-modal.js +91 -0
- package/rook-framework/assets/rk-popover.js +264 -0
- package/rook-framework/assets/rk-progress.js +81 -0
- package/rook-framework/assets/rk-quantity.js +91 -0
- package/rook-framework/assets/rk-scroll-area.js +286 -0
- package/rook-framework/assets/rk-sheet.js +157 -0
- package/rook-framework/assets/rk-tabs.js +179 -0
- package/rook-framework/assets/rk-toggle.js +153 -0
- package/rook-framework/blocks/rk-accordion.liquid +97 -0
- package/rook-framework/blocks/rk-badge.liquid +103 -0
- package/rook-framework/blocks/rk-button.liquid +166 -0
- package/rook-framework/blocks/rk-divider.liquid +100 -0
- package/rook-framework/blocks/rk-form-field.liquid +120 -0
- package/rook-framework/blocks/rk-icon.liquid +134 -0
- package/rook-framework/blocks/rk-image.liquid +198 -0
- package/rook-framework/blocks/rk-installments.liquid +99 -0
- package/rook-framework/blocks/rk-pix-discount.liquid +99 -0
- package/rook-framework/blocks/rk-price.liquid +128 -0
- package/rook-framework/blocks/rk-quantity.liquid +108 -0
- package/rook-framework/blocks/rk-quick-add.liquid +137 -0
- package/rook-framework/blocks/rk-skeleton.liquid +104 -0
- package/rook-framework/blocks/rk-typography.liquid +183 -0
- package/rook-framework/config/rk-color-scheme-group.json +138 -0
- package/rook-framework/config/rk-settings_schema.json +259 -0
- package/rook-framework/snippets/rk-accordion.liquid +31 -0
- package/rook-framework/snippets/rk-alert-dialog.liquid +83 -0
- package/rook-framework/snippets/rk-aspect-ratio.liquid +23 -0
- package/rook-framework/snippets/rk-badge.liquid +17 -0
- package/rook-framework/snippets/rk-bottom-app-bar.liquid +51 -0
- package/rook-framework/snippets/rk-button.liquid +49 -0
- package/rook-framework/snippets/rk-card.liquid +64 -0
- package/rook-framework/snippets/rk-carousel.liquid +74 -0
- package/rook-framework/snippets/rk-checkbox.liquid +34 -0
- package/rook-framework/snippets/rk-collapsible.liquid +52 -0
- package/rook-framework/snippets/rk-color-schemes-standalone.liquid +61 -0
- package/rook-framework/snippets/rk-color-schemes.liquid +43 -0
- package/rook-framework/snippets/rk-dialog.liquid +85 -0
- package/rook-framework/snippets/rk-divider.liquid +25 -0
- package/rook-framework/snippets/rk-drawer.liquid +81 -0
- package/rook-framework/snippets/rk-external-assets copy.liquid +33 -0
- package/rook-framework/snippets/rk-external-assets.liquid +68 -0
- package/rook-framework/snippets/rk-form-field.liquid +83 -0
- package/rook-framework/snippets/rk-gap-style.liquid +32 -0
- package/rook-framework/snippets/rk-icon.liquid +28 -0
- package/rook-framework/snippets/rk-image.liquid +60 -0
- package/rook-framework/snippets/rk-input.liquid +35 -0
- package/rook-framework/snippets/rk-installments.liquid +54 -0
- package/rook-framework/snippets/rk-item.liquid +69 -0
- package/rook-framework/snippets/rk-layout-style.liquid +37 -0
- package/rook-framework/snippets/rk-modal.liquid +31 -0
- package/rook-framework/snippets/rk-pix-discount.liquid +34 -0
- package/rook-framework/snippets/rk-popover.liquid +77 -0
- package/rook-framework/snippets/rk-price.liquid +48 -0
- package/rook-framework/snippets/rk-progress.liquid +38 -0
- package/rook-framework/snippets/rk-quantity.liquid +56 -0
- package/rook-framework/snippets/rk-quick-add.liquid +67 -0
- package/rook-framework/snippets/rk-scripts.liquid +17 -0
- package/rook-framework/snippets/rk-scroll-area.liquid +60 -0
- package/rook-framework/snippets/rk-sheet.liquid +86 -0
- package/rook-framework/snippets/rk-size-style.liquid +48 -0
- package/rook-framework/snippets/rk-skeleton.liquid +25 -0
- package/rook-framework/snippets/rk-spacing-padding.liquid +18 -0
- package/rook-framework/snippets/rk-spacing-style.liquid +54 -0
- package/rook-framework/snippets/rk-spinner.liquid +43 -0
- package/rook-framework/snippets/rk-swatch.liquid +33 -0
- package/rook-framework/snippets/rk-table.liquid +44 -0
- package/rook-framework/snippets/rk-tabs.liquid +52 -0
- package/rook-framework/snippets/rk-textarea.liquid +42 -0
- package/rook-framework/snippets/rk-toggle-group.liquid +27 -0
- package/rook-framework/snippets/rk-toggle.liquid +58 -0
- package/rook-framework/snippets/rk-typography.liquid +27 -0
- package/rook-framework/snippets/rk-variables.liquid +76 -0
- package/src/app.js +24 -0
- package/src/commands/InstallCommand.js +133 -0
- package/src/mcp/server.js +111 -1
- package/src/services/FrameworkInstaller.js +485 -0
- package/src/templates/block.liquid.txt +0 -15
- package/src/ui/PromptUI.js +15 -1
- 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
|