@polymorphism-tech/morph-spec 10.1.1 → 10.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +3 -7
  2. package/bin/morph-spec.js +10 -41
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -4
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/MORPH.md +1 -1
  7. package/framework/agents.json +1 -2
  8. package/framework/commands/morph-archive.md +1 -1
  9. package/framework/commands/morph-preflight.md +3 -4
  10. package/framework/docs/CLI.md +45 -22
  11. package/framework/evals/review-checklist.md +5 -5
  12. package/framework/hooks/README.md +2 -2
  13. package/framework/hooks/claude-code/core/heavy-gate.js +21 -2
  14. package/framework/hooks/claude-code/core/validate-completion.js +0 -35
  15. package/framework/hooks/shared/e2e-check.js +44 -7
  16. package/framework/hooks/shared/heavy-command.js +12 -1
  17. package/framework/hooks/shared/heavy-config.js +12 -0
  18. package/framework/hooks/shared/host-services.js +9 -6
  19. package/framework/hooks/shared/next-step.js +12 -8
  20. package/framework/phases.json +4 -4
  21. package/framework/rules/plan-grammar.md +4 -1
  22. package/framework/schemas/config.schema.json +59 -1
  23. package/framework/schemas/feature.schema.json +15 -0
  24. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  25. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  26. package/framework/skills/core/morph-apply/SKILL.md +8 -5
  27. package/framework/skills/core/morph-apply/references/hotfix.md +1 -1
  28. package/framework/skills/core/morph-apply/references/machine.md +3 -2
  29. package/framework/skills/core/morph-apply/references/review.md +9 -7
  30. package/framework/skills/core/morph-plan/SKILL.md +7 -6
  31. package/framework/skills/core/morph-plan/references/critique.md +1 -1
  32. package/framework/skills/core/morph-uiux/SKILL.md +2 -2
  33. package/framework/templates/IDE-SNIPPETS.md +262 -262
  34. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  35. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  36. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  37. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  38. package/framework/templates/docs/plan.md +2 -1
  39. package/framework/templates/examples/design-system-examples.md +0 -3
  40. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  41. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  42. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  43. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  44. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  45. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  46. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  47. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  48. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  49. package/framework/templates/state.template.json +17 -17
  50. package/framework/templates/ui/design-system.css +226 -226
  51. package/package.json +1 -1
  52. package/src/commands/project/doctor.js +0 -1
  53. package/src/commands/project/heavy.js +13 -0
  54. package/src/commands/state/approve.js +9 -109
  55. package/src/commands/state/report-check.js +44 -2
  56. package/src/commands/validation/verify.js +57 -7
  57. package/src/core/state/state-manager.js +0 -24
  58. package/src/dashboard/app/main.jsx +13 -13
  59. package/src/dashboard/vite.config.js +24 -24
  60. package/src/lib/heavy/cleanup.js +8 -2
  61. package/src/lib/heavy/gate.js +24 -1
  62. package/src/lib/heavy/reclaim.js +107 -0
  63. package/src/lib/heavy/watchdog.js +46 -0
  64. package/src/lib/next-step.js +1 -0
  65. package/src/lib/plan/checks.js +1 -1
  66. package/src/lib/plan/critique.js +23 -3
  67. package/src/lib/procs/prune.js +23 -1
  68. package/src/lib/review-confirmation.js +67 -0
  69. package/src/lib/review-reports.js +1 -0
  70. package/src/lib/risk-detector.js +2 -2
  71. package/src/lib/tasks/test-nodes.js +10 -0
  72. package/src/lib/tasks/test-runner.js +37 -43
  73. package/src/lib/telemetry/telemetry-reader.js +1 -2
  74. package/src/lib/telemetry/verify-event.js +7 -0
  75. package/src/lib/validators/verify-cache.js +63 -0
  76. package/src/lib/validators/verify-runner.js +131 -26
  77. package/src/lib/verify/affected.js +141 -0
  78. package/src/lib/verify/detached.js +154 -0
  79. package/src/utils/agents-installer.js +2 -2
  80. package/src/utils/hooks-installer.js +0 -20
  81. package/bin/validate.js +0 -212
  82. package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
  83. package/framework/templates/.idea/morph-templates.xml +0 -92
  84. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
  85. package/src/commands/observability/telemetry.js +0 -60
  86. package/src/commands/validation/validate-feature.js +0 -337
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polymorphism-tech/morph-spec",
3
- "version": "10.1.1",
3
+ "version": "10.5.2",
4
4
  "description": "MORPH-SPEC: NLH (Natural Language Harness) for spec-driven development with Claude Code",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -80,7 +80,6 @@ const REQUIRED_COMMAND_FILES = [
80
80
  'src/commands/project/status.js',
81
81
  'src/commands/project/worktree.js',
82
82
  'src/commands/state/approve.js',
83
- 'src/commands/validation/validate-feature.js',
84
83
  'src/commands/scaffold/scaffold.js',
85
84
  'src/commands/mcp/mcp-setup.js',
86
85
  'src/commands/context/context-refresh.js',
@@ -7,9 +7,22 @@ import { describeHolder, heavyDir, heavyStatus } from '../../../framework/hooks/
7
7
  import { resolveSpawn } from '../../../framework/hooks/shared/spawn-command.js';
8
8
  import { heavyClass, heavyKind } from '../../../framework/hooks/shared/heavy-command.js';
9
9
  import { memoryBusyHint } from '../../lib/heavy/live-stacks.js';
10
+ import { reclaimUnlessDisabled } from '../../lib/heavy/reclaim.js';
10
11
 
11
12
  const EXIT_BUSY = 3;
12
13
 
14
+ export function heavyReclaimCommand(options = {}) {
15
+ const projectPath = process.cwd();
16
+ const result = reclaimUnlessDisabled({ projectPath, config: readHeavyConfig(projectPath) });
17
+ if (options.json) {
18
+ console.log(JSON.stringify(result));
19
+ return result;
20
+ }
21
+ const gone = [...result.freed, ...result.containers];
22
+ console.log(gone.length ? `Derrubado: ${gone.join(', ')}` : 'Nada ocioso para derrubar.');
23
+ return result;
24
+ }
25
+
13
26
  export function heavyStatusCommand(options = {}) {
14
27
  const config = readHeavyConfig(process.cwd());
15
28
  const status = heavyStatus({ dir: heavyDir(), maxHoldMs: config.maxHoldMinutes * 60_000 });
@@ -1,9 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { execFileSync } from 'child_process';
4
- import { existsSync } from 'fs';
5
- import { join } from 'path';
6
- import { getFeature, setApprovalGate, getApprovalGate, getApprovalHistory, derivePhase } from '../../core/state/state-manager.js';
4
+ import { getFeature, setApprovalGate, getApprovalGate } from '../../core/state/state-manager.js';
7
5
  import { unmetPrereqs } from '../../core/state/gate-order.js';
8
6
  import { reviewReports, reviewFileName, REVIEW_ROLES, VERDICT_TOKEN, REVIEWED_SHA_TOKEN } from '../../lib/review-reports.js';
9
7
  import { shouldPauseGate } from '../../lib/risk-detector.js';
@@ -12,6 +10,7 @@ import { autoApprovalRefusal, printRefusal } from './auto-approval.js';
12
10
  import { readTrustFromConfig } from './gate-decision.js';
13
11
  import { assertOwnerTree, ownerTreeRefusalLines } from '../../lib/worktree/feature-truth.js';
14
12
  import { treeMovedSince } from '../../lib/git/evidence-staleness.js';
13
+ import { reviewConfirmation } from '../../lib/review-confirmation.js';
15
14
  import { seedDotnetBuildIgnores } from '../../lib/stack/dotnet-build-ignores.js';
16
15
  import { reportDotnetIgnores } from './advance.js';
17
16
 
@@ -31,6 +30,11 @@ function reviewStaleness(reports) {
31
30
  return { status: 'skip' };
32
31
  }
33
32
 
33
+ const confirmation = reviewConfirmation(cwd, reports, head);
34
+ const excused = new Set(
35
+ confirmation.confirmed ? confirmation.moved.filter((m) => m.skippable).map((m) => m.role) : [],
36
+ );
37
+
34
38
  let unstamped = null;
35
39
  for (const [role, report] of Object.entries(reports)) {
36
40
  if (report.status === 'missing') continue;
@@ -48,7 +52,7 @@ function reviewStaleness(reports) {
48
52
  unstamped ??= { status: 'unstamped', file, head };
49
53
  continue;
50
54
  }
51
- if (!treeMovedSince(cwd, reviewed, head)) continue;
55
+ if (excused.has(role) || !treeMovedSince(cwd, reviewed, head)) continue;
52
56
  let ahead = 0;
53
57
  try {
54
58
  ahead = Number(execFileSync('git', ['rev-list', '--count', `${reviewed}..${head}`], { cwd, stdio: 'pipe' }).toString().trim()) || 0;
@@ -218,7 +222,7 @@ export async function approveCommand(featureName, gateName, options = {}) {
218
222
  for (const role of unusable) {
219
223
  const { status } = reports[role];
220
224
  const what = status === 'missing' ? 'não existe'
221
- : status === 'partial' ? 'está PARCIAL — retome o mesmo agente por SendMessage'
225
+ : status === 'partial' ? 'está PARCIAL — despache o mesmo papel de novo dizendo o que já está no relatório'
222
226
  : `não tem \`${VERDICT_TOKEN} SHIP|FIX\``;
223
227
  console.error(chalk.gray(` ${reviewFileName(role)} ${what}.`));
224
228
  }
@@ -280,110 +284,6 @@ program
280
284
  .description('Approve a phase gate to allow workflow progression')
281
285
  .action(approveCommand);
282
286
 
283
- export async function approvalStatusCommand(featureName, options = {}) {
284
- try {
285
- const feature = getFeature(featureName);
286
- if (!feature) {
287
- console.error(chalk.red(`\n❌ Feature not found: ${featureName}\n`));
288
- process.exit(1);
289
- }
290
-
291
- // Phase derived from the filesystem + approved gates, never "unknown".
292
- const featurePath = join(process.cwd(), '.morph', 'features', featureName);
293
- const phase = feature.phase || derivePhase(featurePath);
294
-
295
- // The uiux branch is optional: a feature that never took it has no
296
- // 1-design/ folder and no uiux approval — that is "n/a", not "rejected".
297
- const uiuxGate = getApprovalGate(featureName, 'uiux');
298
- const uiuxTaken = uiuxGate?.approved === true || existsSync(join(featurePath, '1-design'));
299
-
300
- if (options.json) {
301
- const status = {
302
- feature: featureName,
303
- phase,
304
- status: feature.status,
305
- uiuxTaken,
306
- gates: {}
307
- };
308
-
309
- for (const gateName of VALID_GATES) {
310
- status.gates[gateName] = getApprovalGate(featureName, gateName) || { approved: false };
311
- }
312
-
313
- console.log(JSON.stringify(status, null, 2));
314
- return;
315
- }
316
-
317
- console.log(chalk.bold(`\n📋 Approval Status for: ${featureName}`));
318
- console.log('━'.repeat(60));
319
-
320
- console.log(chalk.cyan(`\nCurrent Phase: ${phase}`));
321
- console.log(chalk.gray(`Feature Status: ${feature.status || 'unknown'}\n`));
322
-
323
- console.log(chalk.bold('Approval Gates:'));
324
-
325
- for (const gateName of VALID_GATES) {
326
- const gateStatus = getApprovalGate(featureName, gateName);
327
-
328
- if (!gateStatus) {
329
- console.log(` ${chalk.gray('○')} ${gateName}: ${chalk.gray('Not yet evaluated')}`);
330
- continue;
331
- }
332
-
333
- if (gateName === 'uiux' && !uiuxTaken) {
334
- console.log(` ${chalk.gray('—')} ${gateName}: ${chalk.gray('n/a (ramo de design não tomado)')}`);
335
- continue;
336
- }
337
-
338
- if (gateStatus.approved) {
339
- console.log(` ${chalk.green('✓')} ${gateName}: ${chalk.green('Approved')}`);
340
- if (gateStatus.timestamp) {
341
- console.log(chalk.gray(` Approved at: ${gateStatus.timestamp}`));
342
- }
343
- if (gateStatus.approvedBy) {
344
- console.log(chalk.gray(` Approved by: ${gateStatus.approvedBy}`));
345
- }
346
- } else {
347
- // Distinguish "never evaluated" (initialized approved:false, no
348
- // timestamp) from an explicit rejection/revocation (has timestamp).
349
- const neverEvaluated = !gateStatus.timestamp && !gateStatus.rejectedAt && !gateStatus.revokedAt;
350
- if (neverEvaluated) {
351
- console.log(` ${chalk.gray('○')} ${gateName}: ${chalk.gray('Pending (not yet evaluated)')}`);
352
- continue;
353
- }
354
- console.log(` ${chalk.red('✗')} ${gateName}: ${chalk.red('Rejected / Revoked')}`);
355
- if (gateStatus.reason) {
356
- console.log(chalk.gray(` Reason: ${gateStatus.reason}`));
357
- }
358
- if (gateStatus.timestamp) {
359
- console.log(chalk.gray(` Rejected at: ${gateStatus.timestamp}`));
360
- }
361
- }
362
- }
363
-
364
- const history = getApprovalHistory(featureName);
365
- if (history && history.length > 0) {
366
- console.log(chalk.bold('\nApproval History:'));
367
- history.slice(-5).forEach(entry => {
368
- const status = entry.approved ? chalk.green('✓ Approved') : chalk.red('✗ Rejected');
369
- console.log(` ${status} ${entry.gate} - ${entry.timestamp}`);
370
- });
371
- }
372
-
373
- console.log('\n' + '━'.repeat(60) + '\n');
374
-
375
- } catch (error) {
376
- console.error(chalk.red(`\n❌ Error showing approval status: ${error.message}\n`));
377
- process.exit(1);
378
- }
379
- }
380
-
381
- program
382
- .command('approval-status <feature>')
383
- .alias('status')
384
- .description('Show approval status for all gates')
385
- .action(approvalStatusCommand);
386
-
387
287
  if (import.meta.url === `file://${process.argv[1]}`) {
388
288
  if (process.argv.length > 2) {
389
289
  program.parse(process.argv);