chati-dev 1.3.3 → 2.0.1

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 (215) hide show
  1. package/README.md +7 -6
  2. package/framework/agents/build/dev.md +343 -0
  3. package/framework/agents/clarity/architect.md +113 -0
  4. package/framework/agents/clarity/brief.md +183 -0
  5. package/framework/agents/clarity/brownfield-wu.md +182 -0
  6. package/framework/agents/clarity/detail.md +111 -0
  7. package/framework/agents/clarity/greenfield-wu.md +154 -0
  8. package/framework/agents/clarity/phases.md +1 -0
  9. package/framework/agents/clarity/tasks.md +1 -0
  10. package/framework/agents/clarity/ux.md +113 -0
  11. package/framework/agents/deploy/devops.md +1 -0
  12. package/framework/agents/quality/qa-implementation.md +1 -0
  13. package/framework/agents/quality/qa-planning.md +1 -0
  14. package/framework/config.yaml +3 -3
  15. package/framework/constitution.md +58 -1
  16. package/framework/context/governance.md +37 -0
  17. package/framework/context/protocols.md +34 -0
  18. package/framework/context/quality.md +27 -0
  19. package/framework/context/root.md +24 -0
  20. package/framework/data/entity-registry.yaml +1 -1
  21. package/framework/domains/agents/architect.yaml +51 -0
  22. package/framework/domains/agents/brief.yaml +47 -0
  23. package/framework/domains/agents/brownfield-wu.yaml +49 -0
  24. package/framework/domains/agents/detail.yaml +47 -0
  25. package/framework/domains/agents/dev.yaml +49 -0
  26. package/framework/domains/agents/devops.yaml +43 -0
  27. package/framework/domains/agents/greenfield-wu.yaml +47 -0
  28. package/framework/domains/agents/orchestrator.yaml +49 -0
  29. package/framework/domains/agents/phases.yaml +47 -0
  30. package/framework/domains/agents/qa-implementation.yaml +43 -0
  31. package/framework/domains/agents/qa-planning.yaml +44 -0
  32. package/framework/domains/agents/tasks.yaml +48 -0
  33. package/framework/domains/agents/ux.yaml +50 -0
  34. package/framework/domains/constitution.yaml +77 -0
  35. package/framework/domains/global.yaml +64 -0
  36. package/framework/domains/workflows/brownfield-discovery.yaml +16 -0
  37. package/framework/domains/workflows/brownfield-fullstack.yaml +26 -0
  38. package/framework/domains/workflows/brownfield-service.yaml +22 -0
  39. package/framework/domains/workflows/brownfield-ui.yaml +22 -0
  40. package/framework/domains/workflows/greenfield-fullstack.yaml +26 -0
  41. package/framework/hooks/constitution-guard.js +101 -0
  42. package/framework/hooks/mode-governance.js +92 -0
  43. package/framework/hooks/model-governance.js +76 -0
  44. package/framework/hooks/prism-engine.js +89 -0
  45. package/framework/hooks/session-digest.js +60 -0
  46. package/framework/hooks/settings.json +44 -0
  47. package/framework/i18n/en.yaml +3 -3
  48. package/framework/i18n/es.yaml +3 -3
  49. package/framework/i18n/fr.yaml +3 -3
  50. package/framework/i18n/pt.yaml +3 -3
  51. package/framework/intelligence/context-engine.md +2 -2
  52. package/framework/intelligence/decision-engine.md +1 -1
  53. package/framework/migrations/v1.4-to-v2.0.yaml +167 -0
  54. package/framework/migrations/v2.0-to-v2.0.1.yaml +132 -0
  55. package/framework/orchestrator/chati.md +350 -7
  56. package/framework/schemas/session.schema.json +15 -0
  57. package/framework/tasks/architect-api-design.md +63 -0
  58. package/framework/tasks/architect-consolidate.md +47 -0
  59. package/framework/tasks/architect-db-design.md +73 -0
  60. package/framework/tasks/architect-design.md +95 -0
  61. package/framework/tasks/architect-security-review.md +62 -0
  62. package/framework/tasks/architect-stack-selection.md +53 -0
  63. package/framework/tasks/brief-consolidate.md +249 -0
  64. package/framework/tasks/brief-constraint-identify.md +277 -0
  65. package/framework/tasks/brief-extract-requirements.md +339 -0
  66. package/framework/tasks/brief-stakeholder-map.md +176 -0
  67. package/framework/tasks/brief-validate-completeness.md +121 -0
  68. package/framework/tasks/brownfield-wu-architecture-map.md +394 -0
  69. package/framework/tasks/brownfield-wu-deep-discovery.md +312 -0
  70. package/framework/tasks/brownfield-wu-dependency-scan.md +359 -0
  71. package/framework/tasks/brownfield-wu-migration-plan.md +483 -0
  72. package/framework/tasks/brownfield-wu-report.md +325 -0
  73. package/framework/tasks/brownfield-wu-risk-assess.md +424 -0
  74. package/framework/tasks/detail-acceptance-criteria.md +372 -0
  75. package/framework/tasks/detail-consolidate.md +138 -0
  76. package/framework/tasks/detail-edge-case-analysis.md +300 -0
  77. package/framework/tasks/detail-expand-prd.md +389 -0
  78. package/framework/tasks/detail-nfr-extraction.md +223 -0
  79. package/framework/tasks/dev-code-review.md +404 -0
  80. package/framework/tasks/dev-consolidate.md +543 -0
  81. package/framework/tasks/dev-debug.md +322 -0
  82. package/framework/tasks/dev-implement.md +252 -0
  83. package/framework/tasks/dev-iterate.md +411 -0
  84. package/framework/tasks/dev-pr-prepare.md +497 -0
  85. package/framework/tasks/dev-refactor.md +342 -0
  86. package/framework/tasks/dev-test-write.md +306 -0
  87. package/framework/tasks/devops-ci-setup.md +412 -0
  88. package/framework/tasks/devops-consolidate.md +712 -0
  89. package/framework/tasks/devops-deploy-config.md +598 -0
  90. package/framework/tasks/devops-monitoring-setup.md +658 -0
  91. package/framework/tasks/devops-release-prepare.md +673 -0
  92. package/framework/tasks/greenfield-wu-analyze-empty.md +169 -0
  93. package/framework/tasks/greenfield-wu-report.md +266 -0
  94. package/framework/tasks/greenfield-wu-scaffold-detection.md +203 -0
  95. package/framework/tasks/greenfield-wu-tech-stack-assess.md +255 -0
  96. package/framework/tasks/orchestrator-deviation.md +260 -0
  97. package/framework/tasks/orchestrator-escalate.md +276 -0
  98. package/framework/tasks/orchestrator-handoff.md +243 -0
  99. package/framework/tasks/orchestrator-health.md +372 -0
  100. package/framework/tasks/orchestrator-mode-switch.md +262 -0
  101. package/framework/tasks/orchestrator-resume.md +189 -0
  102. package/framework/tasks/orchestrator-route.md +169 -0
  103. package/framework/tasks/orchestrator-spawn-terminal.md +358 -0
  104. package/framework/tasks/orchestrator-status.md +260 -0
  105. package/framework/tasks/orchestrator-suggest-mode.md +372 -0
  106. package/framework/tasks/phases-breakdown.md +91 -0
  107. package/framework/tasks/phases-dependency-mapping.md +67 -0
  108. package/framework/tasks/phases-mvp-scoping.md +94 -0
  109. package/framework/tasks/qa-impl-consolidate.md +522 -0
  110. package/framework/tasks/qa-impl-performance-test.md +487 -0
  111. package/framework/tasks/qa-impl-regression-check.md +413 -0
  112. package/framework/tasks/qa-impl-sast-scan.md +402 -0
  113. package/framework/tasks/qa-impl-test-execute.md +344 -0
  114. package/framework/tasks/qa-impl-verdict.md +339 -0
  115. package/framework/tasks/qa-planning-consolidate.md +309 -0
  116. package/framework/tasks/qa-planning-coverage-plan.md +338 -0
  117. package/framework/tasks/qa-planning-gate-define.md +339 -0
  118. package/framework/tasks/qa-planning-risk-matrix.md +631 -0
  119. package/framework/tasks/qa-planning-test-strategy.md +217 -0
  120. package/framework/tasks/tasks-acceptance-write.md +75 -0
  121. package/framework/tasks/tasks-consolidate.md +57 -0
  122. package/framework/tasks/tasks-decompose.md +80 -0
  123. package/framework/tasks/tasks-estimate.md +66 -0
  124. package/framework/tasks/ux-a11y-check.md +49 -0
  125. package/framework/tasks/ux-component-map.md +55 -0
  126. package/framework/tasks/ux-consolidate.md +46 -0
  127. package/framework/tasks/ux-user-flow.md +46 -0
  128. package/framework/tasks/ux-wireframe.md +76 -0
  129. package/package.json +1 -1
  130. package/scripts/bundle-framework.js +2 -0
  131. package/scripts/changelog-generator.js +222 -0
  132. package/scripts/codebase-mapper.js +728 -0
  133. package/scripts/commit-message-generator.js +167 -0
  134. package/scripts/coverage-analyzer.js +260 -0
  135. package/scripts/dependency-analyzer.js +280 -0
  136. package/scripts/framework-analyzer.js +308 -0
  137. package/scripts/generate-constitution-domain.js +253 -0
  138. package/scripts/health-check.js +481 -0
  139. package/scripts/ide-sync.js +327 -0
  140. package/scripts/performance-analyzer.js +325 -0
  141. package/scripts/plan-tracker.js +278 -0
  142. package/scripts/populate-entity-registry.js +481 -0
  143. package/scripts/pr-review.js +317 -0
  144. package/scripts/rollback-manager.js +310 -0
  145. package/scripts/stuck-detector.js +343 -0
  146. package/scripts/test-quality-assessment.js +257 -0
  147. package/scripts/validate-agents.js +367 -0
  148. package/scripts/validate-tasks.js +465 -0
  149. package/src/autonomy/autonomous-gate.js +293 -0
  150. package/src/autonomy/index.js +51 -0
  151. package/src/autonomy/mode-manager.js +225 -0
  152. package/src/autonomy/mode-suggester.js +283 -0
  153. package/src/autonomy/progress-reporter.js +268 -0
  154. package/src/autonomy/safety-net.js +320 -0
  155. package/src/context/bracket-tracker.js +79 -0
  156. package/src/context/domain-loader.js +107 -0
  157. package/src/context/engine.js +144 -0
  158. package/src/context/formatter.js +184 -0
  159. package/src/context/index.js +4 -0
  160. package/src/context/layers/l0-constitution.js +28 -0
  161. package/src/context/layers/l1-global.js +37 -0
  162. package/src/context/layers/l2-agent.js +39 -0
  163. package/src/context/layers/l3-workflow.js +42 -0
  164. package/src/context/layers/l4-task.js +24 -0
  165. package/src/decision/analyzer.js +167 -0
  166. package/src/decision/engine.js +270 -0
  167. package/src/decision/index.js +38 -0
  168. package/src/decision/registry-healer.js +450 -0
  169. package/src/decision/registry-updater.js +330 -0
  170. package/src/gates/circuit-breaker.js +119 -0
  171. package/src/gates/g1-planning-complete.js +153 -0
  172. package/src/gates/g2-qa-planning.js +153 -0
  173. package/src/gates/g3-implementation.js +188 -0
  174. package/src/gates/g4-qa-implementation.js +207 -0
  175. package/src/gates/g5-deploy-ready.js +180 -0
  176. package/src/gates/gate-base.js +144 -0
  177. package/src/gates/index.js +46 -0
  178. package/src/installer/brownfield-upgrader.js +249 -0
  179. package/src/installer/core.js +55 -3
  180. package/src/installer/file-hasher.js +51 -0
  181. package/src/installer/manifest.js +117 -0
  182. package/src/installer/templates.js +17 -15
  183. package/src/installer/transaction.js +229 -0
  184. package/src/installer/validator.js +18 -1
  185. package/src/intelligence/registry-manager.js +2 -2
  186. package/src/memory/agent-memory.js +255 -0
  187. package/src/memory/gotchas-injector.js +72 -0
  188. package/src/memory/gotchas.js +361 -0
  189. package/src/memory/index.js +35 -0
  190. package/src/memory/search.js +233 -0
  191. package/src/memory/session-digest.js +239 -0
  192. package/src/merger/env-merger.js +112 -0
  193. package/src/merger/index.js +56 -0
  194. package/src/merger/replace-merger.js +51 -0
  195. package/src/merger/yaml-merger.js +127 -0
  196. package/src/orchestrator/agent-selector.js +285 -0
  197. package/src/orchestrator/deviation-handler.js +350 -0
  198. package/src/orchestrator/handoff-engine.js +271 -0
  199. package/src/orchestrator/index.js +67 -0
  200. package/src/orchestrator/intent-classifier.js +264 -0
  201. package/src/orchestrator/pipeline-manager.js +492 -0
  202. package/src/orchestrator/pipeline-state.js +223 -0
  203. package/src/orchestrator/session-manager.js +409 -0
  204. package/src/tasks/executor.js +195 -0
  205. package/src/tasks/handoff.js +226 -0
  206. package/src/tasks/index.js +4 -0
  207. package/src/tasks/loader.js +210 -0
  208. package/src/tasks/router.js +182 -0
  209. package/src/terminal/collector.js +216 -0
  210. package/src/terminal/index.js +30 -0
  211. package/src/terminal/isolation.js +129 -0
  212. package/src/terminal/monitor.js +277 -0
  213. package/src/terminal/spawner.js +269 -0
  214. package/src/upgrade/checker.js +1 -1
  215. package/src/wizard/i18n.js +3 -3
@@ -0,0 +1,481 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Health Check — Comprehensive framework integrity validation.
5
+ *
6
+ * Exports:
7
+ * runHealthCheck(frameworkDir) → HealthReport
8
+ * formatHealthReport(report) → human-readable string
9
+ *
10
+ * HealthReport:
11
+ * { overall: 'HEALTHY'|'DEGRADED'|'UNHEALTHY', checks: {...}, timestamp }
12
+ *
13
+ * Each check:
14
+ * { pass: boolean, details: string, severity: 'critical'|'warning'|'info' }
15
+ */
16
+
17
+ import { readFileSync, existsSync, readdirSync } from 'fs';
18
+ import { join } from 'path';
19
+ import yaml from 'js-yaml';
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Individual checks
23
+ // ---------------------------------------------------------------------------
24
+
25
+ /**
26
+ * Check entity-registry.yaml integrity.
27
+ */
28
+ function checkRegistry(frameworkDir) {
29
+ const regPath = join(frameworkDir, 'data', 'entity-registry.yaml');
30
+ if (!existsSync(regPath)) {
31
+ return { pass: false, details: 'entity-registry.yaml not found', severity: 'critical' };
32
+ }
33
+
34
+ try {
35
+ const content = readFileSync(regPath, 'utf8');
36
+ const registry = yaml.load(content);
37
+
38
+ if (!registry || !registry.entities) {
39
+ return { pass: false, details: 'entity-registry.yaml has no entities section', severity: 'critical' };
40
+ }
41
+
42
+ // Count all entities across all categories
43
+ let entityCount = 0;
44
+ for (const category of Object.values(registry.entities)) {
45
+ if (typeof category === 'object' && category !== null) {
46
+ entityCount += Object.keys(category).length;
47
+ }
48
+ }
49
+
50
+ const declaredCount = registry.metadata?.entity_count || 0;
51
+ if (declaredCount > 0 && entityCount !== declaredCount) {
52
+ return {
53
+ pass: false,
54
+ details: `Entity count mismatch: declared ${declaredCount}, actual ${entityCount}`,
55
+ severity: 'warning',
56
+ };
57
+ }
58
+
59
+ return { pass: true, details: `${entityCount} entities registered`, severity: 'info' };
60
+ } catch (err) {
61
+ return { pass: false, details: `Failed to parse: ${err.message}`, severity: 'critical' };
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Check that all schema files exist and are valid JSON.
67
+ */
68
+ function checkSchemas(frameworkDir) {
69
+ const schemasDir = join(frameworkDir, 'schemas');
70
+ if (!existsSync(schemasDir)) {
71
+ return { pass: false, details: 'schemas/ directory not found', severity: 'critical' };
72
+ }
73
+
74
+ const expectedSchemas = [
75
+ 'session.schema.json',
76
+ 'config.schema.json',
77
+ 'context.schema.json',
78
+ 'memory.schema.json',
79
+ 'task.schema.json',
80
+ ];
81
+
82
+ const missing = [];
83
+ const invalid = [];
84
+
85
+ for (const schema of expectedSchemas) {
86
+ const schemaPath = join(schemasDir, schema);
87
+ if (!existsSync(schemaPath)) {
88
+ missing.push(schema);
89
+ continue;
90
+ }
91
+ try {
92
+ JSON.parse(readFileSync(schemaPath, 'utf8'));
93
+ } catch {
94
+ invalid.push(schema);
95
+ }
96
+ }
97
+
98
+ if (missing.length > 0 || invalid.length > 0) {
99
+ const issues = [];
100
+ if (missing.length) issues.push(`missing: ${missing.join(', ')}`);
101
+ if (invalid.length) issues.push(`invalid JSON: ${invalid.join(', ')}`);
102
+ return {
103
+ pass: false,
104
+ details: issues.join('; '),
105
+ severity: missing.length > 0 ? 'critical' : 'warning',
106
+ };
107
+ }
108
+
109
+ const actualSchemas = readdirSync(schemasDir).filter(f => f.endsWith('.json'));
110
+ return { pass: true, details: `${actualSchemas.length} schemas valid`, severity: 'info' };
111
+ }
112
+
113
+ /**
114
+ * Check constitution.md exists and has the expected number of articles.
115
+ */
116
+ function checkConstitution(frameworkDir) {
117
+ const constPath = join(frameworkDir, 'constitution.md');
118
+ if (!existsSync(constPath)) {
119
+ return { pass: false, details: 'constitution.md not found', severity: 'critical' };
120
+ }
121
+
122
+ try {
123
+ const content = readFileSync(constPath, 'utf8');
124
+ const articleMatches = content.match(/^## Article [IVXLCDM]+/gm) || [];
125
+ const articleCount = articleMatches.length;
126
+
127
+ if (articleCount < 10) {
128
+ return {
129
+ pass: false,
130
+ details: `Only ${articleCount} articles found (expected 10+)`,
131
+ severity: 'warning',
132
+ };
133
+ }
134
+
135
+ return { pass: true, details: `${articleCount} articles found`, severity: 'info' };
136
+ } catch (err) {
137
+ return { pass: false, details: `Failed to read: ${err.message}`, severity: 'critical' };
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Check that all 13 agent definition files exist with valid structure.
143
+ */
144
+ function checkAgents(frameworkDir) {
145
+ const agentsDir = join(frameworkDir, 'agents');
146
+ const orchestratorDir = join(frameworkDir, 'orchestrator');
147
+
148
+ const expectedAgents = {
149
+ 'clarity/greenfield-wu.md': 'greenfield-wu',
150
+ 'clarity/brownfield-wu.md': 'brownfield-wu',
151
+ 'clarity/brief.md': 'brief',
152
+ 'clarity/detail.md': 'detail',
153
+ 'clarity/architect.md': 'architect',
154
+ 'clarity/ux.md': 'ux',
155
+ 'clarity/phases.md': 'phases',
156
+ 'clarity/tasks.md': 'tasks',
157
+ 'quality/qa-planning.md': 'qa-planning',
158
+ 'quality/qa-implementation.md': 'qa-implementation',
159
+ 'build/dev.md': 'dev',
160
+ 'deploy/devops.md': 'devops',
161
+ };
162
+
163
+ const missing = [];
164
+ const invalid = [];
165
+
166
+ // Check agent files
167
+ for (const [relPath, name] of Object.entries(expectedAgents)) {
168
+ const fullPath = join(agentsDir, relPath);
169
+ if (!existsSync(fullPath)) {
170
+ missing.push(name);
171
+ continue;
172
+ }
173
+ try {
174
+ const content = readFileSync(fullPath, 'utf8');
175
+ if (!content.includes('## Identity') && !content.includes('## Mission')) {
176
+ invalid.push(name);
177
+ }
178
+ } catch {
179
+ invalid.push(name);
180
+ }
181
+ }
182
+
183
+ // Check orchestrator
184
+ const orchPath = join(orchestratorDir, 'chati.md');
185
+ if (!existsSync(orchPath)) {
186
+ missing.push('orchestrator');
187
+ }
188
+
189
+ const total = Object.keys(expectedAgents).length + 1; // +1 for orchestrator
190
+ const found = total - missing.length;
191
+
192
+ if (missing.length > 0 || invalid.length > 0) {
193
+ const issues = [];
194
+ if (missing.length) issues.push(`missing: ${missing.join(', ')}`);
195
+ if (invalid.length) issues.push(`invalid: ${invalid.join(', ')}`);
196
+ return {
197
+ pass: false,
198
+ details: `${found}/${total} agents found; ${issues.join('; ')}`,
199
+ severity: missing.length > 0 ? 'critical' : 'warning',
200
+ };
201
+ }
202
+
203
+ return { pass: true, details: `${found}/${total} agents present`, severity: 'info' };
204
+ }
205
+
206
+ /**
207
+ * Check that workflow files exist and are valid YAML.
208
+ */
209
+ function checkWorkflows(frameworkDir) {
210
+ const workflowsDir = join(frameworkDir, 'workflows');
211
+ if (!existsSync(workflowsDir)) {
212
+ return { pass: false, details: 'workflows/ directory not found', severity: 'warning' };
213
+ }
214
+
215
+ const invalid = [];
216
+
217
+ let files;
218
+ try {
219
+ files = readdirSync(workflowsDir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
220
+ } catch (err) {
221
+ return { pass: false, details: `Cannot read workflows/: ${err.message}`, severity: 'warning' };
222
+ }
223
+
224
+ const total = files.length;
225
+ let valid = 0;
226
+
227
+ for (const file of files) {
228
+ try {
229
+ yaml.load(readFileSync(join(workflowsDir, file), 'utf8'));
230
+ valid++;
231
+ } catch {
232
+ invalid.push(file);
233
+ }
234
+ }
235
+
236
+ if (total === 0) {
237
+ return { pass: false, details: 'No workflow files found', severity: 'warning' };
238
+ }
239
+
240
+ if (invalid.length > 0) {
241
+ return { pass: false, details: `${valid}/${total} valid; invalid: ${invalid.join(', ')}`, severity: 'warning' };
242
+ }
243
+
244
+ return { pass: true, details: `${valid} workflows valid`, severity: 'info' };
245
+ }
246
+
247
+ /**
248
+ * Check templates directory.
249
+ */
250
+ function checkTemplates(frameworkDir) {
251
+ const templatesDir = join(frameworkDir, 'templates');
252
+ if (!existsSync(templatesDir)) {
253
+ return { pass: false, details: 'templates/ directory not found', severity: 'warning' };
254
+ }
255
+
256
+ let files;
257
+ try {
258
+ files = readdirSync(templatesDir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
259
+ } catch {
260
+ return { pass: false, details: 'Cannot read templates/', severity: 'warning' };
261
+ }
262
+
263
+ const total = files.length;
264
+ let valid = 0;
265
+
266
+ for (const file of files) {
267
+ try {
268
+ yaml.load(readFileSync(join(templatesDir, file), 'utf8'));
269
+ valid++;
270
+ } catch {
271
+ // Invalid YAML
272
+ }
273
+ }
274
+
275
+ if (total === 0) {
276
+ return { pass: false, details: 'No template files found', severity: 'warning' };
277
+ }
278
+
279
+ return { pass: valid === total, details: `${valid}/${total} templates valid`, severity: valid < total ? 'warning' : 'info' };
280
+ }
281
+
282
+ /**
283
+ * Check quality gates directory.
284
+ */
285
+ function checkQualityGates(frameworkDir) {
286
+ const gatesDir = join(frameworkDir, 'quality-gates');
287
+ if (!existsSync(gatesDir)) {
288
+ return { pass: false, details: 'quality-gates/ directory not found', severity: 'warning' };
289
+ }
290
+
291
+ const expected = ['planning-gate.md', 'implementation-gate.md'];
292
+ const missing = expected.filter(f => !existsSync(join(gatesDir, f)));
293
+
294
+ if (missing.length > 0) {
295
+ return { pass: false, details: `Missing: ${missing.join(', ')}`, severity: 'warning' };
296
+ }
297
+
298
+ return { pass: true, details: `${expected.length} quality gates present`, severity: 'info' };
299
+ }
300
+
301
+ /**
302
+ * Check config.yaml.
303
+ */
304
+ function checkConfig(frameworkDir) {
305
+ const configPath = join(frameworkDir, 'config.yaml');
306
+ if (!existsSync(configPath)) {
307
+ return { pass: false, details: 'config.yaml not found', severity: 'critical' };
308
+ }
309
+
310
+ try {
311
+ const content = readFileSync(configPath, 'utf8');
312
+ // config.yaml has a markdown header line, extract YAML portion
313
+ const lines = content.split('\n');
314
+ const yamlLines = lines.filter(l => !l.startsWith('#'));
315
+ const config = yaml.load(yamlLines.join('\n'));
316
+
317
+ if (!config) {
318
+ return { pass: false, details: 'config.yaml is empty', severity: 'critical' };
319
+ }
320
+
321
+ return { pass: true, details: `version: ${config.version || 'unknown'}`, severity: 'info' };
322
+ } catch (err) {
323
+ return { pass: false, details: `Failed to parse: ${err.message}`, severity: 'critical' };
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Check intelligence directory.
329
+ */
330
+ function checkIntelligence(frameworkDir) {
331
+ const intellDir = join(frameworkDir, 'intelligence');
332
+ if (!existsSync(intellDir)) {
333
+ return { pass: false, details: 'intelligence/ directory not found', severity: 'warning' };
334
+ }
335
+
336
+ const expectedFiles = [
337
+ 'context-engine.md',
338
+ 'memory-layer.md',
339
+ 'decision-engine.md',
340
+ 'confidence.yaml',
341
+ 'patterns.yaml',
342
+ 'gotchas.yaml',
343
+ ];
344
+
345
+ const found = expectedFiles.filter(f => existsSync(join(intellDir, f)));
346
+
347
+ if (found.length < expectedFiles.length) {
348
+ const missing = expectedFiles.filter(f => !found.includes(f));
349
+ return {
350
+ pass: false,
351
+ details: `${found.length}/${expectedFiles.length} present; missing: ${missing.join(', ')}`,
352
+ severity: 'warning',
353
+ };
354
+ }
355
+
356
+ return { pass: true, details: `${found.length} intelligence files present`, severity: 'info' };
357
+ }
358
+
359
+ // ---------------------------------------------------------------------------
360
+ // Public API
361
+ // ---------------------------------------------------------------------------
362
+
363
+ /**
364
+ * Run a comprehensive health check on the framework directory.
365
+ * @param {string} frameworkDir - Path to the framework directory (e.g., chati.dev/ or framework/).
366
+ * @returns {object} HealthReport.
367
+ */
368
+ export function runHealthCheck(frameworkDir) {
369
+ if (!existsSync(frameworkDir)) {
370
+ return {
371
+ overall: 'UNHEALTHY',
372
+ checks: {
373
+ frameworkDir: { pass: false, details: `Directory not found: ${frameworkDir}`, severity: 'critical' },
374
+ },
375
+ timestamp: new Date().toISOString(),
376
+ };
377
+ }
378
+
379
+ const checks = {
380
+ registry: checkRegistry(frameworkDir),
381
+ schemas: checkSchemas(frameworkDir),
382
+ constitution: checkConstitution(frameworkDir),
383
+ agents: checkAgents(frameworkDir),
384
+ workflows: checkWorkflows(frameworkDir),
385
+ templates: checkTemplates(frameworkDir),
386
+ qualityGates: checkQualityGates(frameworkDir),
387
+ config: checkConfig(frameworkDir),
388
+ intelligence: checkIntelligence(frameworkDir),
389
+ };
390
+
391
+ // Determine overall health
392
+ const criticalFailures = Object.values(checks).filter(c => !c.pass && c.severity === 'critical');
393
+ const warnings = Object.values(checks).filter(c => !c.pass && c.severity === 'warning');
394
+
395
+ let overall;
396
+ if (criticalFailures.length > 0) {
397
+ overall = 'UNHEALTHY';
398
+ } else if (warnings.length > 0) {
399
+ overall = 'DEGRADED';
400
+ } else {
401
+ overall = 'HEALTHY';
402
+ }
403
+
404
+ return {
405
+ overall,
406
+ checks,
407
+ timestamp: new Date().toISOString(),
408
+ summary: {
409
+ total: Object.keys(checks).length,
410
+ passed: Object.values(checks).filter(c => c.pass).length,
411
+ failed: Object.values(checks).filter(c => !c.pass).length,
412
+ criticalFailures: criticalFailures.length,
413
+ warnings: warnings.length,
414
+ },
415
+ };
416
+ }
417
+
418
+ /**
419
+ * Format a HealthReport into a human-readable string.
420
+ * @param {object} report - HealthReport from runHealthCheck.
421
+ * @returns {string} Formatted report string.
422
+ */
423
+ export function formatHealthReport(report) {
424
+ const statusSymbol = {
425
+ HEALTHY: '[OK]',
426
+ DEGRADED: '[WARN]',
427
+ UNHEALTHY: '[FAIL]',
428
+ };
429
+
430
+ const checkSymbol = (check) => check.pass ? '[PASS]' : '[FAIL]';
431
+
432
+ const lines = [
433
+ `=== Health Check Report ===`,
434
+ `Status: ${statusSymbol[report.overall] || '[ ? ]'} ${report.overall}`,
435
+ `Time: ${report.timestamp}`,
436
+ '',
437
+ ];
438
+
439
+ if (report.summary) {
440
+ lines.push(`Summary: ${report.summary.passed}/${report.summary.total} checks passed`);
441
+ if (report.summary.criticalFailures > 0) {
442
+ lines.push(` ${report.summary.criticalFailures} critical failure(s)`);
443
+ }
444
+ if (report.summary.warnings > 0) {
445
+ lines.push(` ${report.summary.warnings} warning(s)`);
446
+ }
447
+ lines.push('');
448
+ }
449
+
450
+ lines.push('Checks:');
451
+ for (const [name, check] of Object.entries(report.checks)) {
452
+ const symbol = checkSymbol(check);
453
+ const severity = check.severity !== 'info' ? ` (${check.severity})` : '';
454
+ lines.push(` ${symbol} ${name}: ${check.details}${severity}`);
455
+ }
456
+
457
+ lines.push('');
458
+ lines.push('=== End Report ===');
459
+
460
+ return lines.join('\n');
461
+ }
462
+
463
+ // ---------------------------------------------------------------------------
464
+ // CLI entrypoint
465
+ // ---------------------------------------------------------------------------
466
+
467
+ const isMainModule = process.argv[1] && (
468
+ process.argv[1].endsWith('health-check.js') ||
469
+ process.argv[1].endsWith('health-check')
470
+ );
471
+
472
+ if (isMainModule) {
473
+ const frameworkDir = process.argv[2] || join(process.cwd(), 'chati.dev');
474
+
475
+ const report = runHealthCheck(frameworkDir);
476
+ console.log(formatHealthReport(report));
477
+
478
+ if (report.overall === 'UNHEALTHY') {
479
+ process.exit(1);
480
+ }
481
+ }