@polymorphism-tech/morph-spec 9.6.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 +12 -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 +35 -7
- 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/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/heavy-command.js +60 -1
- package/framework/hooks/shared/host-services.js +433 -0
- package/framework/schemas/config.schema.json +53 -1
- 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 +2 -2
- 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 +14 -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 +47 -24
- package/src/commands/observability/cost.js +12 -0
- package/src/commands/project/worktree.js +7 -3
- 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 +3 -1
- package/src/commands/validation/plan-check.js +1 -0
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/e2e/stack-teardown.js +19 -4
- package/src/lib/review-reports.js +21 -10
- 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/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,13 +17,13 @@ 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';
|
|
@@ -105,7 +105,8 @@ export async function e2eUpCommand(featureName, options = {}) {
|
|
|
105
105
|
console.log(chalk.yellow(`⚠️ ${isolation.reason}`));
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
const exec = options._exec ?? defaultExec;
|
|
109
|
+
if (!isDockerAvailable(ctx.cwd, exec)) {
|
|
109
110
|
console.error(chalk.red('\n❌ Docker is not available in this environment.\n'));
|
|
110
111
|
process.exit(EXIT_USAGE);
|
|
111
112
|
}
|
|
@@ -114,13 +115,14 @@ export async function e2eUpCommand(featureName, options = {}) {
|
|
|
114
115
|
let result;
|
|
115
116
|
try {
|
|
116
117
|
gate.enter('e2e');
|
|
117
|
-
result =
|
|
118
|
+
result = bringUpE2e(ctx.cwd, {
|
|
119
|
+
config: ctx.config,
|
|
118
120
|
composeFile: ctx.composeFile,
|
|
119
121
|
projectName: ctx.projectName,
|
|
120
122
|
gitCommonDir: ctx.gitCommonDir,
|
|
121
123
|
env: ctx.env,
|
|
122
124
|
forceFresh: Boolean(options.fresh),
|
|
123
|
-
});
|
|
125
|
+
}, exec);
|
|
124
126
|
} catch (err) {
|
|
125
127
|
if (!(err instanceof HeavyBusyError)) throw err;
|
|
126
128
|
if (options.json) {
|
|
@@ -134,40 +136,42 @@ export async function e2eUpCommand(featureName, options = {}) {
|
|
|
134
136
|
gate.release();
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
const auth =
|
|
138
|
-
result.status === 'pass'
|
|
139
|
-
? prepareAuth(ctx.cwd, { config: ctx.config, env: ctx.env, forceSeed: Boolean(options.fresh) })
|
|
140
|
-
: { status: 'skip', reason: null };
|
|
141
|
-
|
|
139
|
+
const { auth, hostServices } = result;
|
|
142
140
|
// A seedCommand that `mode` switches off is reported, never swallowed.
|
|
143
141
|
const warnings = [];
|
|
144
142
|
if (isolation.status === 'warn') warnings.push(isolation.reason);
|
|
145
|
-
|
|
143
|
+
warnings.push(...result.warnings);
|
|
146
144
|
|
|
147
|
-
const failed = result.status === 'fail'
|
|
145
|
+
const failed = result.status === 'fail';
|
|
148
146
|
const payload = {
|
|
149
147
|
feature: featureName,
|
|
150
148
|
project: ctx.projectName,
|
|
151
149
|
tree: ctx.tree,
|
|
152
150
|
composeFile: ctx.composeFile,
|
|
153
151
|
status: failed ? 'fail' : 'pass',
|
|
154
|
-
reused: result.reused,
|
|
152
|
+
reused: result.stack.reused,
|
|
155
153
|
reason: result.reason,
|
|
156
154
|
auth: { status: auth.status, reason: auth.reason },
|
|
155
|
+
...(hostServices.status === 'skip' ? {} : { hostServices: hostServices.services }),
|
|
157
156
|
...(warnings.length ? { warnings } : {}),
|
|
157
|
+
...(failed && result.output ? { output: result.output } : {}),
|
|
158
158
|
};
|
|
159
159
|
|
|
160
160
|
if (options.json) {
|
|
161
161
|
console.log(JSON.stringify(payload, null, 2));
|
|
162
162
|
} else if (failed) {
|
|
163
|
-
console.error(chalk.red(`\n✗ ${result.reason
|
|
163
|
+
console.error(chalk.red(`\n✗ ${result.reason}\n`));
|
|
164
164
|
if (result.output) console.error(result.output);
|
|
165
|
+
for (const w of result.warnings) console.error(chalk.yellow(`⚠️ ${w}`));
|
|
165
166
|
} else {
|
|
166
|
-
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)');
|
|
167
168
|
console.log(chalk.green(`\n✓ Stack up — ${ctx.projectName} ${how}`));
|
|
168
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
|
+
}
|
|
169
173
|
if (auth.reason) console.log(chalk.gray(` auth: ${auth.reason}`));
|
|
170
|
-
|
|
174
|
+
for (const w of result.warnings) console.log(chalk.yellow(`⚠️ ${w}`));
|
|
171
175
|
console.log('');
|
|
172
176
|
}
|
|
173
177
|
|
|
@@ -185,17 +189,19 @@ export async function e2eStatusCommand(featureName, options = {}) {
|
|
|
185
189
|
const ctx = resolveContext(featureName, options);
|
|
186
190
|
if (!ctx) process.exit(EXIT_USAGE);
|
|
187
191
|
|
|
192
|
+
const exec = options._exec ?? defaultExec;
|
|
188
193
|
const status = getStackStatus(ctx.cwd, {
|
|
189
194
|
composeFile: ctx.composeFile,
|
|
190
195
|
projectName: ctx.projectName,
|
|
191
196
|
env: ctx.env,
|
|
192
|
-
});
|
|
197
|
+
}, exec);
|
|
193
198
|
|
|
194
|
-
const stamp = computeSourceStamp(ctx.cwd);
|
|
199
|
+
const stamp = computeSourceStamp(ctx.cwd, exec);
|
|
195
200
|
const previous = ctx.gitCommonDir ? readStamp(ctx.gitCommonDir, ctx.projectName) : null;
|
|
196
201
|
const fresh = Boolean(stamp && previous?.stamp === stamp);
|
|
197
202
|
|
|
198
203
|
const spec = resolveSpecCommand(ctx.cwd, ctx.config, ctx.config?.project?.frontendPath);
|
|
204
|
+
const hostServices = ctx.gitCommonDir ? hostServicesStatus(ctx.gitCommonDir, ctx.projectName) : [];
|
|
199
205
|
|
|
200
206
|
const payload = {
|
|
201
207
|
feature: featureName,
|
|
@@ -213,6 +219,7 @@ export async function e2eStatusCommand(featureName, options = {}) {
|
|
|
213
219
|
state: s.State ?? null,
|
|
214
220
|
health: s.Health || null,
|
|
215
221
|
})),
|
|
222
|
+
...(hostServices.length ? { hostServices } : {}),
|
|
216
223
|
};
|
|
217
224
|
|
|
218
225
|
if (options.json) {
|
|
@@ -233,6 +240,10 @@ export async function e2eStatusCommand(featureName, options = {}) {
|
|
|
233
240
|
for (const s of payload.services) {
|
|
234
241
|
console.log(chalk.gray(` · ${s.name}: ${s.state}${s.health ? ` / ${s.health}` : ''}`));
|
|
235
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
|
+
}
|
|
236
247
|
console.log('');
|
|
237
248
|
}
|
|
238
249
|
|
|
@@ -245,30 +256,42 @@ export async function e2eDownCommand(featureName, options = {}) {
|
|
|
245
256
|
const ctx = resolveContext(featureName, options);
|
|
246
257
|
if (!ctx) process.exit(EXIT_USAGE);
|
|
247
258
|
|
|
259
|
+
const hostServices = stopHostServices(ctx.gitCommonDir, ctx.projectName);
|
|
248
260
|
const result = tearDownStack(ctx.cwd, {
|
|
249
261
|
composeFile: ctx.composeFile,
|
|
250
262
|
projectName: ctx.projectName,
|
|
251
263
|
env: ctx.env,
|
|
252
264
|
volumes: Boolean(options.volumes),
|
|
253
|
-
});
|
|
265
|
+
}, options._exec ?? defaultExec);
|
|
254
266
|
const stampDeleted = deleteStamp(ctx.gitCommonDir, ctx.projectName);
|
|
255
267
|
const cleanup = afterHeavyRun({ projectPath: ctx.cwd, config: readHeavyConfig(ctx.cwd) });
|
|
256
268
|
|
|
257
269
|
if (options.json) {
|
|
258
270
|
console.log(JSON.stringify(
|
|
259
|
-
{
|
|
271
|
+
{
|
|
272
|
+
feature: featureName, project: ctx.projectName, tree: ctx.tree, status: result.status, stampDeleted, cleanup,
|
|
273
|
+
...(stoppedAnything(hostServices) ? { hostServices } : {}),
|
|
274
|
+
},
|
|
260
275
|
null, 2,
|
|
261
276
|
));
|
|
262
|
-
} else if (result.status === 'pass') {
|
|
263
|
-
console.log(chalk.green(`\n✓ Stack down — ${ctx.projectName}`));
|
|
264
|
-
console.log(chalk.gray(` tree: ${ctx.tree}\n`));
|
|
265
277
|
} else {
|
|
266
|
-
|
|
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}`));
|
|
267
286
|
}
|
|
268
287
|
|
|
269
288
|
process.exit(0);
|
|
270
289
|
}
|
|
271
290
|
|
|
291
|
+
function stoppedAnything({ killed, skipped, warnings }) {
|
|
292
|
+
return killed.length + skipped.length + warnings.length > 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
272
295
|
function readConfig(cwd) {
|
|
273
296
|
try {
|
|
274
297
|
const path = join(cwd, '.morph', 'config', 'config.json');
|
|
@@ -108,6 +108,18 @@ function printMetricsBody(m) {
|
|
|
108
108
|
console.log(` ${klass.padEnd(12)} ${fmtDuration(t.measuredMs / 1000).padStart(8)} ${t.runs} execução(ões)${unmeasured}`);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
const tov = m.testOutsideVerify;
|
|
112
|
+
const timed = (t) => `${String(t.runs).padStart(4)}× ${fmtDuration(t.measuredMs / 1000).padStart(8)}`
|
|
113
|
+
+ (t.unmeasured ? chalk.gray(` (${t.unmeasured} sem duração)`) : '');
|
|
114
|
+
console.log('\n' + chalk.white.bold('Teste fora do verify (sub-agentes)')
|
|
115
|
+
+ chalk.gray(` · de ${tov.total.runs} testes+verifies, ${fmtDuration(tov.total.measuredMs / 1000)} cronometrados`
|
|
116
|
+
+ (tov.total.unmeasured ? `, ${tov.total.unmeasured} sem duração` : '')));
|
|
117
|
+
console.log(` sub-agente rodando verify cheio ${timed(tov.subagentVerify)}`);
|
|
118
|
+
console.log(` implementador, 3ª rodada em diante ${timed(tov.implementerRound3Plus)}`);
|
|
119
|
+
console.log(` agente de review rodando teste ${timed(tov.reviewTests)}`);
|
|
120
|
+
console.log(chalk.gray(` ${tov.reviewTests.byPersona.runs}× pela persona morph-* (o que o hook test-budget reconhece);`
|
|
121
|
+
+ ' o resto veio de um nome de dispatch (ver-, rev-, gate3-…)'));
|
|
122
|
+
|
|
111
123
|
console.log('\n' + chalk.white.bold('Rerun do mesmo comando em ≤10 min (mesma feature; o filtro de saída não conta)'));
|
|
112
124
|
console.log(` total: ${pct(m.reruns.rate)} repetidas (${m.reruns.reruns}/${m.reruns.runs}) · ${pct(m.reruns.burstRate)} em rajada — testes, builds e verifies`);
|
|
113
125
|
for (const [kind, r] of Object.entries(m.rerunsByKind).sort((a, b) => b[1].runs - a[1].runs)) {
|