@polymorphism-tech/morph-spec 9.5.0 → 9.7.0
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/README.md +2 -2
- package/bin/morph-spec.js +13 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents.json +3 -3
- package/framework/docs/CLI.md +43 -11
- package/framework/evals/review-checklist.md +2 -2
- package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
- package/framework/hooks/claude-code/core/README.md +4 -0
- package/framework/hooks/claude-code/core/heavy-gate.js +5 -3
- package/framework/hooks/claude-code/core/test-budget.js +89 -0
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/feature-locator.js +7 -5
- package/framework/hooks/shared/heavy-command.js +72 -1
- package/framework/hooks/shared/heavy-config.js +28 -1
- package/framework/hooks/shared/heavy-slots.js +41 -5
- package/framework/hooks/shared/host-services.js +433 -0
- package/framework/schemas/config.schema.json +65 -2
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +3 -3
- package/framework/skills/core/morph-review/SKILL.md +3 -2
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +2 -1
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +15 -1
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/e2e/e2e.js +50 -24
- package/src/commands/observability/cost.js +12 -0
- package/src/commands/project/env.js +3 -3
- package/src/commands/project/heavy.js +10 -3
- package/src/commands/project/worktree.js +9 -4
- package/src/commands/state/advance.js +3 -0
- package/src/commands/state/create.js +3 -3
- package/src/commands/state/delete.js +1 -1
- package/src/commands/state/finish.js +20 -4
- package/src/commands/validation/mutate.js +2 -2
- package/src/commands/validation/plan-check.js +1 -0
- package/src/commands/validation/verify.js +3 -0
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/e2e/compose-env.js +26 -3
- package/src/lib/e2e/stack-teardown.js +19 -4
- package/src/lib/heavy/gate.js +23 -4
- package/src/lib/heavy/live-stacks.js +64 -0
- package/src/lib/review-reports.js +21 -10
- package/src/lib/risk-detector.js +9 -1
- package/src/lib/tasks/test-runner.js +3 -0
- package/src/lib/telemetry/metrics.js +47 -3
- package/src/lib/validators/packages/package-validator.js +2 -2
- package/src/lib/worktree/docker-audit.js +7 -3
- package/src/lib/worktree/env-allocator.js +27 -5
- package/src/lib/worktree/ignored-triage.js +8 -0
- package/src/utils/agents-installer.js +4 -1
- package/src/utils/file-copier.js +26 -4
- package/src/utils/hooks-installer.js +4 -4
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
|
@@ -1,226 +1,226 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Design System - {Project Name}
|
|
3
|
-
*
|
|
4
|
-
* CSS Variables generated from design-system.md
|
|
5
|
-
* Use estas variáveis em todo o projeto para manter consistência visual.
|
|
6
|
-
*
|
|
7
|
-
* Template MORPH-SPEC by Polymorphism Tech
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
:root {
|
|
11
|
-
/* ========================================
|
|
12
|
-
* CORES PRINCIPAIS
|
|
13
|
-
* ======================================== */
|
|
14
|
-
|
|
15
|
-
--color-primary: #{hex da cor primária};
|
|
16
|
-
--color-primary-hover: #{hex da cor primária mais escura};
|
|
17
|
-
--color-primary-active: #{hex da cor primária ainda mais escura};
|
|
18
|
-
|
|
19
|
-
--color-secondary: #{hex da cor secundária};
|
|
20
|
-
--color-secondary-hover: #{hex};
|
|
21
|
-
--color-secondary-active: #{hex};
|
|
22
|
-
|
|
23
|
-
/* ========================================
|
|
24
|
-
* CORES DE ESTADO
|
|
25
|
-
* ======================================== */
|
|
26
|
-
|
|
27
|
-
--color-success: #{hex};
|
|
28
|
-
--color-success-bg: #{hex com transparência ou shade mais claro};
|
|
29
|
-
--color-success-border: #{hex};
|
|
30
|
-
|
|
31
|
-
--color-error: #{hex};
|
|
32
|
-
--color-error-bg: #{hex};
|
|
33
|
-
--color-error-border: #{hex};
|
|
34
|
-
|
|
35
|
-
--color-warning: #{hex};
|
|
36
|
-
--color-warning-bg: #{hex};
|
|
37
|
-
--color-warning-border: #{hex};
|
|
38
|
-
|
|
39
|
-
--color-info: #{hex};
|
|
40
|
-
--color-info-bg: #{hex};
|
|
41
|
-
--color-info-border: #{hex};
|
|
42
|
-
|
|
43
|
-
/* ========================================
|
|
44
|
-
* ESCALA DE NEUTROS (Cinzas)
|
|
45
|
-
* ======================================== */
|
|
46
|
-
|
|
47
|
-
--color-gray-50: #{hex};
|
|
48
|
-
--color-gray-100: #{hex};
|
|
49
|
-
--color-gray-200: #{hex};
|
|
50
|
-
--color-gray-300: #{hex};
|
|
51
|
-
--color-gray-400: #{hex};
|
|
52
|
-
--color-gray-500: #{hex};
|
|
53
|
-
--color-gray-600: #{hex};
|
|
54
|
-
--color-gray-700: #{hex};
|
|
55
|
-
--color-gray-800: #{hex};
|
|
56
|
-
--color-gray-900: #{hex};
|
|
57
|
-
|
|
58
|
-
/* ========================================
|
|
59
|
-
* TIPOGRAFIA - FONTES
|
|
60
|
-
* ======================================== */
|
|
61
|
-
|
|
62
|
-
--font-sans: '{Font Name}', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
63
|
-
--font-serif: '{Font Name}', Georgia, 'Times New Roman', serif;
|
|
64
|
-
--font-mono: '{Font Name}', 'Fira Code', 'Courier New', Consolas, monospace;
|
|
65
|
-
|
|
66
|
-
/* ========================================
|
|
67
|
-
* TIPOGRAFIA - TAMANHOS
|
|
68
|
-
* ======================================== */
|
|
69
|
-
|
|
70
|
-
--text-xs: 0.75rem; /* 12px */
|
|
71
|
-
--text-sm: 0.875rem; /* 14px */
|
|
72
|
-
--text-base: 1rem; /* 16px */
|
|
73
|
-
--text-lg: 1.125rem; /* 18px */
|
|
74
|
-
--text-xl: 1.25rem; /* 20px */
|
|
75
|
-
--text-2xl: 1.5rem; /* 24px */
|
|
76
|
-
--text-3xl: 1.875rem; /* 30px */
|
|
77
|
-
--text-4xl: 2.25rem; /* 36px */
|
|
78
|
-
--text-5xl: 3rem; /* 48px */
|
|
79
|
-
|
|
80
|
-
/* ========================================
|
|
81
|
-
* TIPOGRAFIA - LINE HEIGHTS
|
|
82
|
-
* ======================================== */
|
|
83
|
-
|
|
84
|
-
--leading-none: 1;
|
|
85
|
-
--leading-tight: 1.25;
|
|
86
|
-
--leading-snug: 1.375;
|
|
87
|
-
--leading-normal: 1.5;
|
|
88
|
-
--leading-relaxed: 1.625;
|
|
89
|
-
--leading-loose: 2;
|
|
90
|
-
|
|
91
|
-
/* ========================================
|
|
92
|
-
* TIPOGRAFIA - FONT WEIGHTS
|
|
93
|
-
* ======================================== */
|
|
94
|
-
|
|
95
|
-
--font-light: 300;
|
|
96
|
-
--font-regular: 400;
|
|
97
|
-
--font-medium: 500;
|
|
98
|
-
--font-semibold: 600;
|
|
99
|
-
--font-bold: 700;
|
|
100
|
-
--font-extrabold: 800;
|
|
101
|
-
|
|
102
|
-
/* ========================================
|
|
103
|
-
* ESPAÇAMENTO
|
|
104
|
-
* Baseado em múltiplos de {base}px (ex: 4px)
|
|
105
|
-
* ======================================== */
|
|
106
|
-
|
|
107
|
-
--space-0: 0;
|
|
108
|
-
--space-1: 0.25rem; /* 4px */
|
|
109
|
-
--space-2: 0.5rem; /* 8px */
|
|
110
|
-
--space-3: 0.75rem; /* 12px */
|
|
111
|
-
--space-4: 1rem; /* 16px */
|
|
112
|
-
--space-5: 1.25rem; /* 20px */
|
|
113
|
-
--space-6: 1.5rem; /* 24px */
|
|
114
|
-
--space-7: 1.75rem; /* 28px */
|
|
115
|
-
--space-8: 2rem; /* 32px */
|
|
116
|
-
--space-10: 2.5rem; /* 40px */
|
|
117
|
-
--space-12: 3rem; /* 48px */
|
|
118
|
-
--space-16: 4rem; /* 64px */
|
|
119
|
-
--space-20: 5rem; /* 80px */
|
|
120
|
-
--space-24: 6rem; /* 96px */
|
|
121
|
-
|
|
122
|
-
/* ========================================
|
|
123
|
-
* BORDER RADIUS
|
|
124
|
-
* ======================================== */
|
|
125
|
-
|
|
126
|
-
--rounded-none: 0;
|
|
127
|
-
--rounded-sm: 0.125rem; /* 2px */
|
|
128
|
-
--rounded-md: 0.375rem; /* 6px */
|
|
129
|
-
--rounded-lg: 0.5rem; /* 8px */
|
|
130
|
-
--rounded-xl: 0.75rem; /* 12px */
|
|
131
|
-
--rounded-2xl: 1rem; /* 16px */
|
|
132
|
-
--rounded-full: 9999px;
|
|
133
|
-
|
|
134
|
-
/* ========================================
|
|
135
|
-
* SOMBRAS (Shadows)
|
|
136
|
-
* ======================================== */
|
|
137
|
-
|
|
138
|
-
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
139
|
-
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
140
|
-
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
141
|
-
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
142
|
-
--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
143
|
-
|
|
144
|
-
/* ========================================
|
|
145
|
-
* Z-INDEX LAYERS
|
|
146
|
-
* ======================================== */
|
|
147
|
-
|
|
148
|
-
--z-0: 0;
|
|
149
|
-
--z-10: 10;
|
|
150
|
-
--z-20: 20;
|
|
151
|
-
--z-30: 30;
|
|
152
|
-
--z-40: 40;
|
|
153
|
-
--z-50: 50; /* Modals, dropdowns */
|
|
154
|
-
--z-100: 100; /* Tooltips */
|
|
155
|
-
--z-max: 9999; /* Toasts, notifications */
|
|
156
|
-
|
|
157
|
-
/* ========================================
|
|
158
|
-
* TRANSITIONS / ANIMATIONS
|
|
159
|
-
* ======================================== */
|
|
160
|
-
|
|
161
|
-
--transition-fast: 150ms ease-in-out;
|
|
162
|
-
--transition-base: 250ms ease-in-out;
|
|
163
|
-
--transition-slow: 350ms ease-in-out;
|
|
164
|
-
|
|
165
|
-
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
166
|
-
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
167
|
-
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
168
|
-
|
|
169
|
-
/* ========================================
|
|
170
|
-
* BREAKPOINTS (informativo - usar em media queries)
|
|
171
|
-
* ======================================== */
|
|
172
|
-
|
|
173
|
-
/* @media (min-width: 640px) - sm */
|
|
174
|
-
/* @media (min-width: 768px) - md */
|
|
175
|
-
/* @media (min-width: 1024px) - lg */
|
|
176
|
-
/* @media (min-width: 1280px) - xl */
|
|
177
|
-
/* @media (min-width: 1536px) - 2xl */
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/* ========================================
|
|
181
|
-
* MODO ESCURO (Dark Mode)
|
|
182
|
-
* Opcional - ajuste as cores para dark theme
|
|
183
|
-
* ======================================== */
|
|
184
|
-
|
|
185
|
-
@media (prefers-color-scheme: dark) {
|
|
186
|
-
:root {
|
|
187
|
-
/* Inverter cores conforme necessário */
|
|
188
|
-
--color-gray-50: #{hex do gray-900};
|
|
189
|
-
--color-gray-900: #{hex do gray-50};
|
|
190
|
-
/* ... outras inversões */
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/* ========================================
|
|
195
|
-
* CLASSES UTILITÁRIAS (Exemplo)
|
|
196
|
-
* ======================================== */
|
|
197
|
-
|
|
198
|
-
/* Exemplo de uso das variáveis */
|
|
199
|
-
.btn-primary {
|
|
200
|
-
background-color: var(--color-primary);
|
|
201
|
-
color: white;
|
|
202
|
-
padding: var(--space-2) var(--space-4);
|
|
203
|
-
border-radius: var(--rounded-md);
|
|
204
|
-
font-weight: var(--font-semibold);
|
|
205
|
-
transition: background-color var(--transition-base);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.btn-primary:hover {
|
|
209
|
-
background-color: var(--color-primary-hover);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.card {
|
|
213
|
-
background-color: white;
|
|
214
|
-
padding: var(--space-6);
|
|
215
|
-
border-radius: var(--rounded-lg);
|
|
216
|
-
box-shadow: var(--shadow-md);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.text-muted {
|
|
220
|
-
color: var(--color-gray-600);
|
|
221
|
-
font-size: var(--text-sm);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/* ========================================
|
|
225
|
-
* FIM DO DESIGN SYSTEM
|
|
226
|
-
* ======================================== */
|
|
1
|
+
/**
|
|
2
|
+
* Design System - {Project Name}
|
|
3
|
+
*
|
|
4
|
+
* CSS Variables generated from design-system.md
|
|
5
|
+
* Use estas variáveis em todo o projeto para manter consistência visual.
|
|
6
|
+
*
|
|
7
|
+
* Template MORPH-SPEC by Polymorphism Tech
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
:root {
|
|
11
|
+
/* ========================================
|
|
12
|
+
* CORES PRINCIPAIS
|
|
13
|
+
* ======================================== */
|
|
14
|
+
|
|
15
|
+
--color-primary: #{hex da cor primária};
|
|
16
|
+
--color-primary-hover: #{hex da cor primária mais escura};
|
|
17
|
+
--color-primary-active: #{hex da cor primária ainda mais escura};
|
|
18
|
+
|
|
19
|
+
--color-secondary: #{hex da cor secundária};
|
|
20
|
+
--color-secondary-hover: #{hex};
|
|
21
|
+
--color-secondary-active: #{hex};
|
|
22
|
+
|
|
23
|
+
/* ========================================
|
|
24
|
+
* CORES DE ESTADO
|
|
25
|
+
* ======================================== */
|
|
26
|
+
|
|
27
|
+
--color-success: #{hex};
|
|
28
|
+
--color-success-bg: #{hex com transparência ou shade mais claro};
|
|
29
|
+
--color-success-border: #{hex};
|
|
30
|
+
|
|
31
|
+
--color-error: #{hex};
|
|
32
|
+
--color-error-bg: #{hex};
|
|
33
|
+
--color-error-border: #{hex};
|
|
34
|
+
|
|
35
|
+
--color-warning: #{hex};
|
|
36
|
+
--color-warning-bg: #{hex};
|
|
37
|
+
--color-warning-border: #{hex};
|
|
38
|
+
|
|
39
|
+
--color-info: #{hex};
|
|
40
|
+
--color-info-bg: #{hex};
|
|
41
|
+
--color-info-border: #{hex};
|
|
42
|
+
|
|
43
|
+
/* ========================================
|
|
44
|
+
* ESCALA DE NEUTROS (Cinzas)
|
|
45
|
+
* ======================================== */
|
|
46
|
+
|
|
47
|
+
--color-gray-50: #{hex};
|
|
48
|
+
--color-gray-100: #{hex};
|
|
49
|
+
--color-gray-200: #{hex};
|
|
50
|
+
--color-gray-300: #{hex};
|
|
51
|
+
--color-gray-400: #{hex};
|
|
52
|
+
--color-gray-500: #{hex};
|
|
53
|
+
--color-gray-600: #{hex};
|
|
54
|
+
--color-gray-700: #{hex};
|
|
55
|
+
--color-gray-800: #{hex};
|
|
56
|
+
--color-gray-900: #{hex};
|
|
57
|
+
|
|
58
|
+
/* ========================================
|
|
59
|
+
* TIPOGRAFIA - FONTES
|
|
60
|
+
* ======================================== */
|
|
61
|
+
|
|
62
|
+
--font-sans: '{Font Name}', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
63
|
+
--font-serif: '{Font Name}', Georgia, 'Times New Roman', serif;
|
|
64
|
+
--font-mono: '{Font Name}', 'Fira Code', 'Courier New', Consolas, monospace;
|
|
65
|
+
|
|
66
|
+
/* ========================================
|
|
67
|
+
* TIPOGRAFIA - TAMANHOS
|
|
68
|
+
* ======================================== */
|
|
69
|
+
|
|
70
|
+
--text-xs: 0.75rem; /* 12px */
|
|
71
|
+
--text-sm: 0.875rem; /* 14px */
|
|
72
|
+
--text-base: 1rem; /* 16px */
|
|
73
|
+
--text-lg: 1.125rem; /* 18px */
|
|
74
|
+
--text-xl: 1.25rem; /* 20px */
|
|
75
|
+
--text-2xl: 1.5rem; /* 24px */
|
|
76
|
+
--text-3xl: 1.875rem; /* 30px */
|
|
77
|
+
--text-4xl: 2.25rem; /* 36px */
|
|
78
|
+
--text-5xl: 3rem; /* 48px */
|
|
79
|
+
|
|
80
|
+
/* ========================================
|
|
81
|
+
* TIPOGRAFIA - LINE HEIGHTS
|
|
82
|
+
* ======================================== */
|
|
83
|
+
|
|
84
|
+
--leading-none: 1;
|
|
85
|
+
--leading-tight: 1.25;
|
|
86
|
+
--leading-snug: 1.375;
|
|
87
|
+
--leading-normal: 1.5;
|
|
88
|
+
--leading-relaxed: 1.625;
|
|
89
|
+
--leading-loose: 2;
|
|
90
|
+
|
|
91
|
+
/* ========================================
|
|
92
|
+
* TIPOGRAFIA - FONT WEIGHTS
|
|
93
|
+
* ======================================== */
|
|
94
|
+
|
|
95
|
+
--font-light: 300;
|
|
96
|
+
--font-regular: 400;
|
|
97
|
+
--font-medium: 500;
|
|
98
|
+
--font-semibold: 600;
|
|
99
|
+
--font-bold: 700;
|
|
100
|
+
--font-extrabold: 800;
|
|
101
|
+
|
|
102
|
+
/* ========================================
|
|
103
|
+
* ESPAÇAMENTO
|
|
104
|
+
* Baseado em múltiplos de {base}px (ex: 4px)
|
|
105
|
+
* ======================================== */
|
|
106
|
+
|
|
107
|
+
--space-0: 0;
|
|
108
|
+
--space-1: 0.25rem; /* 4px */
|
|
109
|
+
--space-2: 0.5rem; /* 8px */
|
|
110
|
+
--space-3: 0.75rem; /* 12px */
|
|
111
|
+
--space-4: 1rem; /* 16px */
|
|
112
|
+
--space-5: 1.25rem; /* 20px */
|
|
113
|
+
--space-6: 1.5rem; /* 24px */
|
|
114
|
+
--space-7: 1.75rem; /* 28px */
|
|
115
|
+
--space-8: 2rem; /* 32px */
|
|
116
|
+
--space-10: 2.5rem; /* 40px */
|
|
117
|
+
--space-12: 3rem; /* 48px */
|
|
118
|
+
--space-16: 4rem; /* 64px */
|
|
119
|
+
--space-20: 5rem; /* 80px */
|
|
120
|
+
--space-24: 6rem; /* 96px */
|
|
121
|
+
|
|
122
|
+
/* ========================================
|
|
123
|
+
* BORDER RADIUS
|
|
124
|
+
* ======================================== */
|
|
125
|
+
|
|
126
|
+
--rounded-none: 0;
|
|
127
|
+
--rounded-sm: 0.125rem; /* 2px */
|
|
128
|
+
--rounded-md: 0.375rem; /* 6px */
|
|
129
|
+
--rounded-lg: 0.5rem; /* 8px */
|
|
130
|
+
--rounded-xl: 0.75rem; /* 12px */
|
|
131
|
+
--rounded-2xl: 1rem; /* 16px */
|
|
132
|
+
--rounded-full: 9999px;
|
|
133
|
+
|
|
134
|
+
/* ========================================
|
|
135
|
+
* SOMBRAS (Shadows)
|
|
136
|
+
* ======================================== */
|
|
137
|
+
|
|
138
|
+
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
139
|
+
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
140
|
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
141
|
+
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
142
|
+
--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
143
|
+
|
|
144
|
+
/* ========================================
|
|
145
|
+
* Z-INDEX LAYERS
|
|
146
|
+
* ======================================== */
|
|
147
|
+
|
|
148
|
+
--z-0: 0;
|
|
149
|
+
--z-10: 10;
|
|
150
|
+
--z-20: 20;
|
|
151
|
+
--z-30: 30;
|
|
152
|
+
--z-40: 40;
|
|
153
|
+
--z-50: 50; /* Modals, dropdowns */
|
|
154
|
+
--z-100: 100; /* Tooltips */
|
|
155
|
+
--z-max: 9999; /* Toasts, notifications */
|
|
156
|
+
|
|
157
|
+
/* ========================================
|
|
158
|
+
* TRANSITIONS / ANIMATIONS
|
|
159
|
+
* ======================================== */
|
|
160
|
+
|
|
161
|
+
--transition-fast: 150ms ease-in-out;
|
|
162
|
+
--transition-base: 250ms ease-in-out;
|
|
163
|
+
--transition-slow: 350ms ease-in-out;
|
|
164
|
+
|
|
165
|
+
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
166
|
+
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
167
|
+
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
168
|
+
|
|
169
|
+
/* ========================================
|
|
170
|
+
* BREAKPOINTS (informativo - usar em media queries)
|
|
171
|
+
* ======================================== */
|
|
172
|
+
|
|
173
|
+
/* @media (min-width: 640px) - sm */
|
|
174
|
+
/* @media (min-width: 768px) - md */
|
|
175
|
+
/* @media (min-width: 1024px) - lg */
|
|
176
|
+
/* @media (min-width: 1280px) - xl */
|
|
177
|
+
/* @media (min-width: 1536px) - 2xl */
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ========================================
|
|
181
|
+
* MODO ESCURO (Dark Mode)
|
|
182
|
+
* Opcional - ajuste as cores para dark theme
|
|
183
|
+
* ======================================== */
|
|
184
|
+
|
|
185
|
+
@media (prefers-color-scheme: dark) {
|
|
186
|
+
:root {
|
|
187
|
+
/* Inverter cores conforme necessário */
|
|
188
|
+
--color-gray-50: #{hex do gray-900};
|
|
189
|
+
--color-gray-900: #{hex do gray-50};
|
|
190
|
+
/* ... outras inversões */
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* ========================================
|
|
195
|
+
* CLASSES UTILITÁRIAS (Exemplo)
|
|
196
|
+
* ======================================== */
|
|
197
|
+
|
|
198
|
+
/* Exemplo de uso das variáveis */
|
|
199
|
+
.btn-primary {
|
|
200
|
+
background-color: var(--color-primary);
|
|
201
|
+
color: white;
|
|
202
|
+
padding: var(--space-2) var(--space-4);
|
|
203
|
+
border-radius: var(--rounded-md);
|
|
204
|
+
font-weight: var(--font-semibold);
|
|
205
|
+
transition: background-color var(--transition-base);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.btn-primary:hover {
|
|
209
|
+
background-color: var(--color-primary-hover);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.card {
|
|
213
|
+
background-color: white;
|
|
214
|
+
padding: var(--space-6);
|
|
215
|
+
border-radius: var(--rounded-lg);
|
|
216
|
+
box-shadow: var(--shadow-md);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.text-muted {
|
|
220
|
+
color: var(--color-gray-600);
|
|
221
|
+
font-size: var(--text-sm);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* ========================================
|
|
225
|
+
* FIM DO DESIGN SYSTEM
|
|
226
|
+
* ======================================== */
|
package/package.json
CHANGED
package/src/commands/e2e/e2e.js
CHANGED
|
@@ -8,7 +8,7 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
8
8
|
import { join, resolve } from 'path';
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
bringUpE2e,
|
|
12
12
|
composeProjectName,
|
|
13
13
|
computeSourceStamp,
|
|
14
14
|
decideIsolation,
|
|
@@ -17,17 +17,18 @@ import {
|
|
|
17
17
|
getStackStatus,
|
|
18
18
|
isDockerAvailable,
|
|
19
19
|
lintComposeIsolation,
|
|
20
|
-
prepareAuth,
|
|
21
20
|
readStamp,
|
|
22
21
|
resolveComposeFile,
|
|
23
22
|
resolveSpecCommand,
|
|
24
23
|
tearDownStack,
|
|
25
24
|
} from '../../../framework/hooks/shared/e2e-check.js';
|
|
26
25
|
import { isSecondaryWorktree } from '../../../framework/hooks/shared/git-paths.js';
|
|
26
|
+
import { hostServicesStatus, stopHostServices } from '../../../framework/hooks/shared/host-services.js';
|
|
27
27
|
import { buildComposeEnv } from '../../lib/e2e/compose-env.js';
|
|
28
28
|
import { findWorktreeForFeature, resolvePrimaryRoot } from '../project/worktree.js';
|
|
29
29
|
import { createHeavyGate, HeavyBusyError } from '../../lib/heavy/gate.js';
|
|
30
30
|
import { afterHeavyRun } from '../../lib/heavy/cleanup.js';
|
|
31
|
+
import { memoryBusyHint } from '../../lib/heavy/live-stacks.js';
|
|
31
32
|
import { readHeavyConfig } from '../../../framework/hooks/shared/heavy-config.js';
|
|
32
33
|
|
|
33
34
|
const EXIT_USAGE = 2;
|
|
@@ -104,7 +105,8 @@ export async function e2eUpCommand(featureName, options = {}) {
|
|
|
104
105
|
console.log(chalk.yellow(`⚠️ ${isolation.reason}`));
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
|
|
108
|
+
const exec = options._exec ?? defaultExec;
|
|
109
|
+
if (!isDockerAvailable(ctx.cwd, exec)) {
|
|
108
110
|
console.error(chalk.red('\n❌ Docker is not available in this environment.\n'));
|
|
109
111
|
process.exit(EXIT_USAGE);
|
|
110
112
|
}
|
|
@@ -113,58 +115,63 @@ export async function e2eUpCommand(featureName, options = {}) {
|
|
|
113
115
|
let result;
|
|
114
116
|
try {
|
|
115
117
|
gate.enter('e2e');
|
|
116
|
-
result =
|
|
118
|
+
result = bringUpE2e(ctx.cwd, {
|
|
119
|
+
config: ctx.config,
|
|
117
120
|
composeFile: ctx.composeFile,
|
|
118
121
|
projectName: ctx.projectName,
|
|
119
122
|
gitCommonDir: ctx.gitCommonDir,
|
|
120
123
|
env: ctx.env,
|
|
121
124
|
forceFresh: Boolean(options.fresh),
|
|
122
|
-
});
|
|
125
|
+
}, exec);
|
|
123
126
|
} catch (err) {
|
|
124
127
|
if (!(err instanceof HeavyBusyError)) throw err;
|
|
125
128
|
if (options.json) {
|
|
126
129
|
console.log(JSON.stringify({ status: 'busy', feature: featureName, busy: { reason: err.reason, waitedMs: err.waitedMs } }, null, 2));
|
|
127
130
|
}
|
|
128
131
|
console.error(chalk.yellow(`\n⏳ e2e up não rodou: ${err.message}. Não é vermelho — a fila já esperou o prazo dela: não repita em laço — siga sem esta rodada e avise o usuário (\`morph-spec heavy status\`).\n`));
|
|
132
|
+
const hint = memoryBusyHint(err, ctx.cwd);
|
|
133
|
+
if (hint) console.error(chalk.yellow(`${hint}\n`));
|
|
129
134
|
process.exit(EXIT_BUSY);
|
|
130
135
|
} finally {
|
|
131
136
|
gate.release();
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
const auth =
|
|
135
|
-
result.status === 'pass'
|
|
136
|
-
? prepareAuth(ctx.cwd, { config: ctx.config, env: ctx.env, forceSeed: Boolean(options.fresh) })
|
|
137
|
-
: { status: 'skip', reason: null };
|
|
138
|
-
|
|
139
|
+
const { auth, hostServices } = result;
|
|
139
140
|
// A seedCommand that `mode` switches off is reported, never swallowed.
|
|
140
141
|
const warnings = [];
|
|
141
142
|
if (isolation.status === 'warn') warnings.push(isolation.reason);
|
|
142
|
-
|
|
143
|
+
warnings.push(...result.warnings);
|
|
143
144
|
|
|
144
|
-
const failed = result.status === 'fail'
|
|
145
|
+
const failed = result.status === 'fail';
|
|
145
146
|
const payload = {
|
|
146
147
|
feature: featureName,
|
|
147
148
|
project: ctx.projectName,
|
|
148
149
|
tree: ctx.tree,
|
|
149
150
|
composeFile: ctx.composeFile,
|
|
150
151
|
status: failed ? 'fail' : 'pass',
|
|
151
|
-
reused: result.reused,
|
|
152
|
+
reused: result.stack.reused,
|
|
152
153
|
reason: result.reason,
|
|
153
154
|
auth: { status: auth.status, reason: auth.reason },
|
|
155
|
+
...(hostServices.status === 'skip' ? {} : { hostServices: hostServices.services }),
|
|
154
156
|
...(warnings.length ? { warnings } : {}),
|
|
157
|
+
...(failed && result.output ? { output: result.output } : {}),
|
|
155
158
|
};
|
|
156
159
|
|
|
157
160
|
if (options.json) {
|
|
158
161
|
console.log(JSON.stringify(payload, null, 2));
|
|
159
162
|
} else if (failed) {
|
|
160
|
-
console.error(chalk.red(`\n✗ ${result.reason
|
|
163
|
+
console.error(chalk.red(`\n✗ ${result.reason}\n`));
|
|
161
164
|
if (result.output) console.error(result.output);
|
|
165
|
+
for (const w of result.warnings) console.error(chalk.yellow(`⚠️ ${w}`));
|
|
162
166
|
} else {
|
|
163
|
-
const how = result.reused ? chalk.gray(`(${result.reason})`) : chalk.gray('(built)');
|
|
167
|
+
const how = result.stack.reused ? chalk.gray(`(${result.stack.reason})`) : chalk.gray('(built)');
|
|
164
168
|
console.log(chalk.green(`\n✓ Stack up — ${ctx.projectName} ${how}`));
|
|
165
169
|
console.log(chalk.gray(` tree: ${ctx.tree}`));
|
|
170
|
+
for (const s of hostServices.services) {
|
|
171
|
+
console.log(chalk.gray(` host: ${s.name} pid ${s.pid} (${s.reused ? 'reused' : 'started'}) — log ${s.log}`));
|
|
172
|
+
}
|
|
166
173
|
if (auth.reason) console.log(chalk.gray(` auth: ${auth.reason}`));
|
|
167
|
-
|
|
174
|
+
for (const w of result.warnings) console.log(chalk.yellow(`⚠️ ${w}`));
|
|
168
175
|
console.log('');
|
|
169
176
|
}
|
|
170
177
|
|
|
@@ -182,17 +189,19 @@ export async function e2eStatusCommand(featureName, options = {}) {
|
|
|
182
189
|
const ctx = resolveContext(featureName, options);
|
|
183
190
|
if (!ctx) process.exit(EXIT_USAGE);
|
|
184
191
|
|
|
192
|
+
const exec = options._exec ?? defaultExec;
|
|
185
193
|
const status = getStackStatus(ctx.cwd, {
|
|
186
194
|
composeFile: ctx.composeFile,
|
|
187
195
|
projectName: ctx.projectName,
|
|
188
196
|
env: ctx.env,
|
|
189
|
-
});
|
|
197
|
+
}, exec);
|
|
190
198
|
|
|
191
|
-
const stamp = computeSourceStamp(ctx.cwd);
|
|
199
|
+
const stamp = computeSourceStamp(ctx.cwd, exec);
|
|
192
200
|
const previous = ctx.gitCommonDir ? readStamp(ctx.gitCommonDir, ctx.projectName) : null;
|
|
193
201
|
const fresh = Boolean(stamp && previous?.stamp === stamp);
|
|
194
202
|
|
|
195
203
|
const spec = resolveSpecCommand(ctx.cwd, ctx.config, ctx.config?.project?.frontendPath);
|
|
204
|
+
const hostServices = ctx.gitCommonDir ? hostServicesStatus(ctx.gitCommonDir, ctx.projectName) : [];
|
|
196
205
|
|
|
197
206
|
const payload = {
|
|
198
207
|
feature: featureName,
|
|
@@ -210,6 +219,7 @@ export async function e2eStatusCommand(featureName, options = {}) {
|
|
|
210
219
|
state: s.State ?? null,
|
|
211
220
|
health: s.Health || null,
|
|
212
221
|
})),
|
|
222
|
+
...(hostServices.length ? { hostServices } : {}),
|
|
213
223
|
};
|
|
214
224
|
|
|
215
225
|
if (options.json) {
|
|
@@ -230,6 +240,10 @@ export async function e2eStatusCommand(featureName, options = {}) {
|
|
|
230
240
|
for (const s of payload.services) {
|
|
231
241
|
console.log(chalk.gray(` · ${s.name}: ${s.state}${s.health ? ` / ${s.health}` : ''}`));
|
|
232
242
|
}
|
|
243
|
+
for (const h of hostServices) {
|
|
244
|
+
const health = h.health === null ? 'no health check' : h.health ? 'healthy' : 'unhealthy';
|
|
245
|
+
console.log(chalk.gray(` · host ${h.name}: pid ${h.pid} ${h.alive ? 'alive' : 'dead'} / ${health}`));
|
|
246
|
+
}
|
|
233
247
|
console.log('');
|
|
234
248
|
}
|
|
235
249
|
|
|
@@ -242,30 +256,42 @@ export async function e2eDownCommand(featureName, options = {}) {
|
|
|
242
256
|
const ctx = resolveContext(featureName, options);
|
|
243
257
|
if (!ctx) process.exit(EXIT_USAGE);
|
|
244
258
|
|
|
259
|
+
const hostServices = stopHostServices(ctx.gitCommonDir, ctx.projectName);
|
|
245
260
|
const result = tearDownStack(ctx.cwd, {
|
|
246
261
|
composeFile: ctx.composeFile,
|
|
247
262
|
projectName: ctx.projectName,
|
|
248
263
|
env: ctx.env,
|
|
249
264
|
volumes: Boolean(options.volumes),
|
|
250
|
-
});
|
|
265
|
+
}, options._exec ?? defaultExec);
|
|
251
266
|
const stampDeleted = deleteStamp(ctx.gitCommonDir, ctx.projectName);
|
|
252
267
|
const cleanup = afterHeavyRun({ projectPath: ctx.cwd, config: readHeavyConfig(ctx.cwd) });
|
|
253
268
|
|
|
254
269
|
if (options.json) {
|
|
255
270
|
console.log(JSON.stringify(
|
|
256
|
-
{
|
|
271
|
+
{
|
|
272
|
+
feature: featureName, project: ctx.projectName, tree: ctx.tree, status: result.status, stampDeleted, cleanup,
|
|
273
|
+
...(stoppedAnything(hostServices) ? { hostServices } : {}),
|
|
274
|
+
},
|
|
257
275
|
null, 2,
|
|
258
276
|
));
|
|
259
|
-
} else if (result.status === 'pass') {
|
|
260
|
-
console.log(chalk.green(`\n✓ Stack down — ${ctx.projectName}`));
|
|
261
|
-
console.log(chalk.gray(` tree: ${ctx.tree}\n`));
|
|
262
277
|
} else {
|
|
263
|
-
|
|
278
|
+
if (result.status === 'pass') {
|
|
279
|
+
console.log(chalk.green(`\n✓ Stack down — ${ctx.projectName}`));
|
|
280
|
+
console.log(chalk.gray(` tree: ${ctx.tree}\n`));
|
|
281
|
+
} else {
|
|
282
|
+
console.log(chalk.yellow(`\n⚠️ Teardown reported a problem (not fatal):\n${result.output}\n`));
|
|
283
|
+
}
|
|
284
|
+
if (hostServices.killed.length) console.log(chalk.gray(` host services stopped: ${hostServices.killed.join(', ')}`));
|
|
285
|
+
for (const w of hostServices.warnings) console.log(chalk.yellow(`⚠️ ${w}`));
|
|
264
286
|
}
|
|
265
287
|
|
|
266
288
|
process.exit(0);
|
|
267
289
|
}
|
|
268
290
|
|
|
291
|
+
function stoppedAnything({ killed, skipped, warnings }) {
|
|
292
|
+
return killed.length + skipped.length + warnings.length > 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
269
295
|
function readConfig(cwd) {
|
|
270
296
|
try {
|
|
271
297
|
const path = join(cwd, '.morph', 'config', 'config.json');
|