chati-dev 4.3.1 → 4.4.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.
Files changed (147) hide show
  1. package/README.md +18 -13
  2. package/bin/chati.js +12 -4
  3. package/framework/agents/build/dev.md +5 -1
  4. package/framework/agents/discover/greenfield-wu.md +16 -1
  5. package/framework/agents/plan/tasks.md +31 -0
  6. package/framework/agents/plan/ux-brand-architect.md +21 -2
  7. package/framework/agents/plan/ux-component-engineer.md +10 -0
  8. package/framework/agents/quality/qa-implementation.md +1 -1
  9. package/framework/agents/quality/qa-planning.md +1 -1
  10. package/framework/agents/quality/qa-visual.md +24 -21
  11. package/framework/config.yaml +5 -4
  12. package/framework/constitution.md +58 -11
  13. package/framework/context/governance.md +12 -10
  14. package/framework/context/root.md +2 -2
  15. package/framework/data/entity-registry.yaml +12 -4
  16. package/framework/data/qa-rubrics.yaml +335 -0
  17. package/framework/domains/constitution.yaml +3 -3
  18. package/framework/executors/json-validate.js +80 -0
  19. package/framework/executors/npm-script.js +72 -0
  20. package/framework/executors/path-check.js +67 -0
  21. package/framework/executors/registry.yaml +36 -0
  22. package/framework/hooks/advance-trigger.js +47 -3
  23. package/framework/hooks/constitution-guard.js +9 -4
  24. package/framework/hooks/git-push-authority.js +113 -0
  25. package/framework/hooks/license-guard.js +59 -9
  26. package/framework/hooks/model-governance.js +40 -17
  27. package/framework/hooks/prism-engine.js +78 -11
  28. package/framework/hooks/session-digest.js +12 -3
  29. package/framework/hooks/settings.json +7 -3
  30. package/framework/hooks/style-guard.js +52 -3
  31. package/framework/hooks/team-quality-gate.js +43 -5
  32. package/framework/i18n/en.yaml +3 -3
  33. package/framework/i18n/es.yaml +3 -3
  34. package/framework/i18n/fr.yaml +3 -3
  35. package/framework/i18n/pt.yaml +3 -3
  36. package/framework/intelligence/confidence.yaml +85 -6
  37. package/framework/intelligence/context-engine.md +9 -5
  38. package/framework/intelligence/decision-engine.md +1 -1
  39. package/framework/orchestrator/chati-router.js +1 -1
  40. package/framework/orchestrator/chati-update.md +19 -3
  41. package/framework/orchestrator/chati.md +19 -1
  42. package/framework/quality-gates/planning-gate.md +2 -0
  43. package/framework/scaffold/motion-premium/scaffold.yaml +2 -1
  44. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +1 -1
  45. package/framework/scaffold/saas-dashboard/README.md +58 -0
  46. package/framework/scaffold/saas-dashboard/app/dashboard/page.tsx.template +546 -0
  47. package/framework/scaffold/saas-dashboard/app/globals.css.template +746 -0
  48. package/framework/scaffold/saas-dashboard/app/layout.tsx.template +192 -0
  49. package/framework/scaffold/saas-dashboard/app/shell.css.template +1070 -0
  50. package/framework/scaffold/saas-dashboard/components/data/Chart.tsx.template +725 -0
  51. package/framework/scaffold/saas-dashboard/components/data/DataTable.tsx.template +623 -0
  52. package/framework/scaffold/saas-dashboard/components/data/DataTable.types.ts.template +104 -0
  53. package/framework/scaffold/saas-dashboard/components/data/DataTablePagination.tsx.template +234 -0
  54. package/framework/scaffold/saas-dashboard/components/data/SkeletonBlock.tsx.template +111 -0
  55. package/framework/scaffold/saas-dashboard/components/data/StatCard.tsx.template +393 -0
  56. package/framework/scaffold/saas-dashboard/components/data/StatCardGrid.tsx.template +121 -0
  57. package/framework/scaffold/saas-dashboard/components/feedback/EmptyState.tsx.template +281 -0
  58. package/framework/scaffold/saas-dashboard/components/interaction/CommandPalette.tsx.template +767 -0
  59. package/framework/scaffold/saas-dashboard/components/interaction/Field.tsx.template +351 -0
  60. package/framework/scaffold/saas-dashboard/components/interaction/Form.tsx.template +424 -0
  61. package/framework/scaffold/saas-dashboard/components/primitives/Badge.tsx.template +193 -0
  62. package/framework/scaffold/saas-dashboard/components/primitives/Button.tsx.template +261 -0
  63. package/framework/scaffold/saas-dashboard/components/primitives/Card.tsx.template +287 -0
  64. package/framework/scaffold/saas-dashboard/components/primitives/Input.tsx.template +310 -0
  65. package/framework/scaffold/saas-dashboard/components/primitives/Spinner.tsx.template +135 -0
  66. package/framework/scaffold/saas-dashboard/components/primitives/index.ts.template +60 -0
  67. package/framework/scaffold/saas-dashboard/components/shell/AppShell.tsx.template +174 -0
  68. package/framework/scaffold/saas-dashboard/components/shell/Breadcrumb.tsx.template +186 -0
  69. package/framework/scaffold/saas-dashboard/components/shell/Container.tsx.template +86 -0
  70. package/framework/scaffold/saas-dashboard/components/shell/NavItem.tsx.template +147 -0
  71. package/framework/scaffold/saas-dashboard/components/shell/Sidebar.tsx.template +378 -0
  72. package/framework/scaffold/saas-dashboard/components/shell/SidebarContext.tsx.template +169 -0
  73. package/framework/scaffold/saas-dashboard/components/shell/ThemeToggle.tsx.template +128 -0
  74. package/framework/scaffold/saas-dashboard/components/shell/Topbar.tsx.template +301 -0
  75. package/framework/scaffold/saas-dashboard/lib/brand.ts.template +324 -0
  76. package/framework/scaffold/saas-dashboard/scaffold.yaml +225 -0
  77. package/framework/schemas/session.schema.json +5 -0
  78. package/framework/schemas/task.schema.json +9 -0
  79. package/framework/scripts/visual-qa.js +87 -1
  80. package/framework/tasks/orchestrator-health.md +4 -4
  81. package/framework/tasks/qa-impl-verdict.md +11 -5
  82. package/framework/templates/qa-gate-tmpl.yaml +24 -11
  83. package/package.json +3 -3
  84. package/src/config/context-file-generator.js +0 -6
  85. package/src/dashboard/renderer.js +0 -36
  86. package/src/executors/runner.js +204 -0
  87. package/src/installer/core.js +2 -1
  88. package/src/installer/templates.js +5 -6
  89. package/src/intelligence/registry-manager.js +1 -1
  90. package/src/license/client.js +9 -3
  91. package/src/license/commands.js +12 -2
  92. package/src/license/machine-id.js +42 -1
  93. package/src/memory/gotchas.js +58 -40
  94. package/src/memory/magic-docs.js +1 -1
  95. package/src/memory/session-digest.js +9 -4
  96. package/src/orchestrator/cli.js +174 -14
  97. package/src/orchestrator/doctor.js +98 -16
  98. package/src/orchestrator/pipeline-manager.js +44 -22
  99. package/src/orchestrator/session-manager.js +56 -8
  100. package/src/telemetry/sender.js +7 -19
  101. package/src/terminal/run-team.js +3 -3
  102. package/src/upgrade/tracked-files-detector.js +34 -16
  103. package/src/utils/feature-flags.js +1 -1
  104. package/src/utils/flatten-entities.js +4 -40
  105. package/src/utils/schema-validator.js +0 -14
  106. package/src/wizard/i18n.js +3 -3
  107. package/src/api/index.js +0 -120
  108. package/src/autonomy/autonomous-gate.js +0 -294
  109. package/src/autonomy/build-loop.js +0 -281
  110. package/src/autonomy/build-state.js +0 -286
  111. package/src/autonomy/cause-analyzer.js +0 -177
  112. package/src/autonomy/escalation.js +0 -214
  113. package/src/autonomy/index.js +0 -51
  114. package/src/autonomy/mode-manager.js +0 -225
  115. package/src/autonomy/mode-suggester.js +0 -283
  116. package/src/autonomy/progress-reporter.js +0 -275
  117. package/src/autonomy/safety-net.js +0 -370
  118. package/src/config/agent-customizer.js +0 -231
  119. package/src/decision/analyzer.js +0 -291
  120. package/src/decision/engine.js +0 -250
  121. package/src/decision/index.js +0 -38
  122. package/src/decision/registry-healer.js +0 -468
  123. package/src/decision/registry-updater.js +0 -339
  124. package/src/extensions/loader.js +0 -145
  125. package/src/extensions/registry.js +0 -134
  126. package/src/gates/circuit-breaker.js +0 -151
  127. package/src/gates/g1-planning-complete.js +0 -154
  128. package/src/gates/g2-qa-planning.js +0 -156
  129. package/src/gates/g3-implementation.js +0 -215
  130. package/src/gates/g4-qa-implementation.js +0 -240
  131. package/src/gates/g5-deploy-ready.js +0 -181
  132. package/src/gates/gate-base.js +0 -185
  133. package/src/gates/index.js +0 -46
  134. package/src/health/auto-fix.js +0 -216
  135. package/src/health/engine.js +0 -246
  136. package/src/merger/semantic-merger.js +0 -292
  137. package/src/preview/detector.js +0 -238
  138. package/src/preview/index.js +0 -20
  139. package/src/preview/launcher.js +0 -235
  140. package/src/preview/log-buffer.js +0 -103
  141. package/src/quality/metrics-collector.js +0 -281
  142. package/src/quality/test-runner.js +0 -366
  143. package/src/tasks/executor.js +0 -195
  144. package/src/tasks/index.js +0 -4
  145. package/src/tasks/loader.js +0 -210
  146. package/src/tasks/router.js +0 -182
  147. package/src/utils/event-bus.js +0 -126
@@ -1,468 +0,0 @@
1
- /**
2
- * Registry Healer - Self-healing for entity-registry.yaml
3
- * Detects and fixes 6 types of registry issues
4
- *
5
- * @module decision/registry-healer
6
- */
7
-
8
- import { readFileSync, writeFileSync, existsSync, statSync, copyFileSync, mkdirSync } from 'fs';
9
- import { join, isAbsolute } from 'path';
10
- import yaml from 'js-yaml';
11
- import { resolveFrameworkDir } from '../utils/framework-dir.js';
12
- import { detectNewEntities, detectRemovedEntities, generateEntityMeta } from './registry-updater.js';
13
-
14
- /**
15
- * Load and parse the entity registry YAML once.
16
- * @param {string} projectDir
17
- * @returns {{ registry: object|null, registryPath: string }}
18
- */
19
- function loadRegistry(projectDir) {
20
- const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
21
- if (!existsSync(registryPath)) {
22
- return { registry: null, registryPath };
23
- }
24
- const registry = yaml.load(readFileSync(registryPath, 'utf8'));
25
- return { registry, registryPath };
26
- }
27
-
28
- /**
29
- * Run all healing rules and return diagnosis.
30
- * @param {string} projectDir
31
- * @param {object} [options] - { autoFix: boolean }
32
- * @returns {{ healthy: boolean, issues: object[], fixes: object[] }}
33
- */
34
- export function healRegistry(projectDir, options = {}) {
35
- const { autoFix = false } = options;
36
-
37
- const issues = [];
38
- const fixes = [];
39
-
40
- // Load registry once for rules 3-6
41
- const { registry } = loadRegistry(projectDir);
42
-
43
- // Rule 1: Missing entities
44
- const missingIssues = detectMissingEntities(projectDir);
45
- issues.push(...missingIssues);
46
- if (autoFix) {
47
- missingIssues.forEach(issue => {
48
- fixes.push({
49
- rule: 'missing_entity',
50
- action: 'add',
51
- path: issue.path,
52
- entity: issue.entity
53
- });
54
- });
55
- }
56
-
57
- // Rule 2: Orphaned entries
58
- const orphanedIssues = detectOrphanedEntries(projectDir);
59
- issues.push(...orphanedIssues);
60
- if (autoFix) {
61
- orphanedIssues.forEach(issue => {
62
- fixes.push({
63
- rule: 'orphaned_entry',
64
- action: 'remove',
65
- path: issue.path
66
- });
67
- });
68
- }
69
-
70
- // Rule 3: Stale metadata
71
- const staleIssues = detectStaleMetadata(projectDir, registry);
72
- issues.push(...staleIssues);
73
- if (autoFix) {
74
- staleIssues.forEach(issue => {
75
- fixes.push({
76
- rule: 'stale_metadata',
77
- action: 'update',
78
- path: issue.path,
79
- newMeta: issue.newMeta
80
- });
81
- });
82
- }
83
-
84
- // Rule 4: Duplicates
85
- const duplicateIssues = detectDuplicates(projectDir, registry);
86
- issues.push(...duplicateIssues);
87
- if (autoFix) {
88
- duplicateIssues.forEach(issue => {
89
- fixes.push({
90
- rule: 'duplicate_entry',
91
- action: 'deduplicate',
92
- path: issue.path,
93
- count: issue.count
94
- });
95
- });
96
- }
97
-
98
- // Rule 5: Invalid paths
99
- const invalidPathIssues = detectInvalidPaths(projectDir, registry);
100
- issues.push(...invalidPathIssues);
101
- if (autoFix) {
102
- invalidPathIssues.forEach(issue => {
103
- fixes.push({
104
- rule: 'invalid_path',
105
- action: 'remove',
106
- path: issue.path,
107
- reason: issue.reason
108
- });
109
- });
110
- }
111
-
112
- // Rule 6: Count mismatch
113
- const countIssues = detectCountMismatch(projectDir, registry);
114
- issues.push(...countIssues);
115
- if (autoFix) {
116
- countIssues.forEach(issue => {
117
- fixes.push({
118
- rule: 'count_mismatch',
119
- action: 'fix_count',
120
- expected: issue.actual,
121
- current: issue.recorded
122
- });
123
- });
124
- }
125
-
126
- // Apply fixes if requested
127
- if (autoFix && fixes.length > 0) {
128
- const result = applyFixes(projectDir, fixes);
129
- return {
130
- healthy: issues.length === 0,
131
- issues,
132
- fixes,
133
- applied: result.applied,
134
- backup: result.backup
135
- };
136
- }
137
-
138
- return {
139
- healthy: issues.length === 0,
140
- issues,
141
- fixes
142
- };
143
- }
144
-
145
- /**
146
- * Rule 1: Missing entities (file exists but not in registry).
147
- * @param {string} projectDir
148
- * @returns {object[]} Issues found
149
- */
150
- export function detectMissingEntities(projectDir) {
151
- const newEntities = detectNewEntities(projectDir);
152
- return newEntities.map(entity => ({
153
- rule: 'missing_entity',
154
- severity: 'warning',
155
- path: entity.path,
156
- message: `File exists but not in registry: ${entity.path}`,
157
- entity
158
- }));
159
- }
160
-
161
- /**
162
- * Rule 2: Orphaned entries (in registry but file doesn't exist).
163
- * @param {string} projectDir
164
- * @returns {object[]} Issues found
165
- */
166
- export function detectOrphanedEntries(projectDir) {
167
- const removedEntities = detectRemovedEntities(projectDir);
168
- return removedEntities.map(entity => ({
169
- rule: 'orphaned_entry',
170
- severity: 'error',
171
- path: entity.path,
172
- message: `Registry entry exists but file not found: ${entity.path}`
173
- }));
174
- }
175
-
176
- /**
177
- * Rule 3: Stale metadata (file modified after registry entry).
178
- * @param {string} projectDir
179
- * @param {object} [preloaded] - Pre-loaded registry object (avoids redundant file read)
180
- * @returns {object[]} Issues found
181
- */
182
- export function detectStaleMetadata(projectDir, preloaded) {
183
- const registry = preloaded || loadRegistry(projectDir).registry;
184
- if (!registry) {
185
- return [];
186
- }
187
-
188
- const lastUpdated = registry.metadata?.last_updated
189
- ? new Date(registry.metadata.last_updated)
190
- : new Date(0);
191
-
192
- const issues = [];
193
-
194
- Object.entries(registry.entities || {}).forEach(([_type, entities]) => {
195
- if (Array.isArray(entities)) {
196
- entities.forEach(entity => {
197
- if (entity.path) {
198
- const fullPath = join(projectDir, entity.path);
199
- if (existsSync(fullPath)) {
200
- const stats = statSync(fullPath);
201
- if (stats.mtime > lastUpdated) {
202
- const newMeta = generateEntityMeta(projectDir, entity.path);
203
- issues.push({
204
- rule: 'stale_metadata',
205
- severity: 'info',
206
- path: entity.path,
207
- message: `File modified after registry update: ${entity.path}`,
208
- newMeta
209
- });
210
- }
211
- }
212
- }
213
- });
214
- }
215
- });
216
-
217
- return issues;
218
- }
219
-
220
- /**
221
- * Rule 4: Duplicate entries (same path appears twice).
222
- * @param {string} projectDir
223
- * @param {object} [preloaded] - Pre-loaded registry object (avoids redundant file read)
224
- * @returns {object[]} Issues found
225
- */
226
- export function detectDuplicates(projectDir, preloaded) {
227
- const registry = preloaded || loadRegistry(projectDir).registry;
228
- if (!registry) {
229
- return [];
230
- }
231
-
232
- const pathCounts = new Map();
233
- const issues = [];
234
-
235
- Object.values(registry.entities || {}).forEach(entities => {
236
- if (Array.isArray(entities)) {
237
- entities.forEach(entity => {
238
- if (entity.path) {
239
- pathCounts.set(entity.path, (pathCounts.get(entity.path) || 0) + 1);
240
- }
241
- });
242
- }
243
- });
244
-
245
- pathCounts.forEach((count, path) => {
246
- if (count > 1) {
247
- issues.push({
248
- rule: 'duplicate_entry',
249
- severity: 'error',
250
- path,
251
- count,
252
- message: `Duplicate registry entries for path: ${path} (${count} times)`
253
- });
254
- }
255
- });
256
-
257
- return issues;
258
- }
259
-
260
- /**
261
- * Rule 5: Invalid paths (malformed or absolute paths).
262
- * @param {string} projectDir
263
- * @param {object} [preloaded] - Pre-loaded registry object (avoids redundant file read)
264
- * @returns {object[]} Issues found
265
- */
266
- export function detectInvalidPaths(projectDir, preloaded) {
267
- const registry = preloaded || loadRegistry(projectDir).registry;
268
- if (!registry) {
269
- return [];
270
- }
271
-
272
- const issues = [];
273
-
274
- Object.values(registry.entities || {}).forEach(entities => {
275
- if (Array.isArray(entities)) {
276
- entities.forEach(entity => {
277
- if (!entity.path) {
278
- issues.push({
279
- rule: 'invalid_path',
280
- severity: 'error',
281
- path: '<missing>',
282
- reason: 'Missing path field',
283
- message: 'Entity missing path field'
284
- });
285
- } else if (isAbsolute(entity.path)) {
286
- issues.push({
287
- rule: 'invalid_path',
288
- severity: 'error',
289
- path: entity.path,
290
- reason: 'Absolute path',
291
- message: `Path should be relative: ${entity.path}`
292
- });
293
- } else if (entity.path.includes('..')) {
294
- issues.push({
295
- rule: 'invalid_path',
296
- severity: 'error',
297
- path: entity.path,
298
- reason: 'Contains parent directory reference',
299
- message: `Path contains ..: ${entity.path}`
300
- });
301
- }
302
- });
303
- }
304
- });
305
-
306
- return issues;
307
- }
308
-
309
- /**
310
- * Rule 6: Entity count mismatch (metadata.entity_count != actual).
311
- * @param {string} projectDir
312
- * @param {object} [preloaded] - Pre-loaded registry object (avoids redundant file read)
313
- * @returns {object[]} Issues found
314
- */
315
- export function detectCountMismatch(projectDir, preloaded) {
316
- const registry = preloaded || loadRegistry(projectDir).registry;
317
- if (!registry) {
318
- return [];
319
- }
320
-
321
- let actualCount = 0;
322
-
323
- Object.values(registry.entities || {}).forEach(entities => {
324
- if (Array.isArray(entities)) {
325
- actualCount += entities.length;
326
- }
327
- });
328
-
329
- const recordedCount = registry.metadata?.entity_count || 0;
330
-
331
- if (actualCount !== recordedCount) {
332
- return [{
333
- rule: 'count_mismatch',
334
- severity: 'warning',
335
- actual: actualCount,
336
- recorded: recordedCount,
337
- message: `Entity count mismatch: metadata says ${recordedCount}, actual is ${actualCount}`
338
- }];
339
- }
340
-
341
- return [];
342
- }
343
-
344
- /**
345
- * Apply fixes and create backup before modifying.
346
- * @param {string} projectDir
347
- * @param {object[]} fixes
348
- * @returns {{ applied: number, backup: string }}
349
- */
350
- export function applyFixes(projectDir, fixes) {
351
- const registryPath = join(projectDir, resolveFrameworkDir(projectDir), 'entity-registry.yaml');
352
-
353
- if (!existsSync(registryPath)) {
354
- return { applied: 0, backup: null };
355
- }
356
-
357
- // Create backup
358
- const backupDir = join(projectDir, '.chati', 'backups');
359
- if (!existsSync(backupDir)) {
360
- mkdirSync(backupDir, { recursive: true });
361
- }
362
-
363
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
364
- const backupPath = join(backupDir, `entity-registry-${timestamp}.yaml`);
365
- copyFileSync(registryPath, backupPath);
366
-
367
- // Load registry (fresh read for modification — applyFixes is the write path)
368
- const registry = yaml.load(readFileSync(registryPath, 'utf8'));
369
-
370
- let applied = 0;
371
-
372
- fixes.forEach(fix => {
373
- try {
374
- switch (fix.action) {
375
- case 'add':
376
- // Add missing entity
377
- if (fix.entity) {
378
- const type = fix.entity.type;
379
- if (!registry.entities[type]) {
380
- registry.entities[type] = [];
381
- }
382
- registry.entities[type].push(fix.entity);
383
- applied++;
384
- }
385
- break;
386
-
387
- case 'remove':
388
- // Remove orphaned entry
389
- Object.keys(registry.entities).forEach(type => {
390
- if (Array.isArray(registry.entities[type])) {
391
- registry.entities[type] = registry.entities[type].filter(
392
- e => e.path !== fix.path
393
- );
394
- }
395
- });
396
- applied++;
397
- break;
398
-
399
- case 'update':
400
- // Update stale metadata
401
- Object.keys(registry.entities).forEach(type => {
402
- if (Array.isArray(registry.entities[type])) {
403
- const index = registry.entities[type].findIndex(
404
- e => e.path === fix.path
405
- );
406
- if (index !== -1) {
407
- registry.entities[type][index] = {
408
- ...registry.entities[type][index],
409
- ...fix.newMeta
410
- };
411
- }
412
- }
413
- });
414
- applied++;
415
- break;
416
-
417
- case 'deduplicate':
418
- // Remove duplicate entries (keep first occurrence)
419
- Object.keys(registry.entities).forEach(type => {
420
- if (Array.isArray(registry.entities[type])) {
421
- const seen = new Set();
422
- registry.entities[type] = registry.entities[type].filter(e => {
423
- if (e.path === fix.path) {
424
- if (seen.has(e.path)) {
425
- return false; // Remove duplicate
426
- }
427
- seen.add(e.path);
428
- }
429
- return true;
430
- });
431
- }
432
- });
433
- applied++;
434
- break;
435
-
436
- case 'fix_count':
437
- // Fix entity count
438
- registry.metadata = registry.metadata || {};
439
- registry.metadata.entity_count = fix.expected;
440
- applied++;
441
- break;
442
- }
443
- } catch { /* expected: operation may fail gracefully */
444
- // Skip failed fixes
445
- }
446
- });
447
-
448
- // Update metadata
449
- registry.metadata = registry.metadata || {};
450
- registry.metadata.last_updated = new Date().toISOString();
451
-
452
- // Recalculate entity count
453
- let totalCount = 0;
454
- Object.values(registry.entities).forEach(entities => {
455
- if (Array.isArray(entities)) {
456
- totalCount += entities.length;
457
- }
458
- });
459
- registry.metadata.entity_count = totalCount;
460
-
461
- // Write updated registry
462
- writeFileSync(registryPath, yaml.dump(registry), 'utf8');
463
-
464
- return {
465
- applied,
466
- backup: backupPath
467
- };
468
- }