chati-dev 4.3.0 → 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 (156) hide show
  1. package/README.md +18 -13
  2. package/bin/chati.js +32 -4
  3. package/framework/agents/build/dev.md +9 -5
  4. package/framework/agents/discover/brief.md +2 -0
  5. package/framework/agents/discover/brownfield-wu.md +2 -0
  6. package/framework/agents/discover/greenfield-wu.md +18 -1
  7. package/framework/agents/plan/detail.md +2 -0
  8. package/framework/agents/plan/tasks.md +31 -0
  9. package/framework/agents/plan/ux-brand-architect.md +21 -2
  10. package/framework/agents/plan/ux-component-engineer.md +10 -0
  11. package/framework/agents/quality/qa-implementation.md +1 -1
  12. package/framework/agents/quality/qa-planning.md +1 -1
  13. package/framework/agents/quality/qa-visual.md +33 -21
  14. package/framework/config.yaml +5 -4
  15. package/framework/constitution.md +58 -11
  16. package/framework/context/governance.md +12 -10
  17. package/framework/context/root.md +2 -2
  18. package/framework/data/entity-registry.yaml +12 -4
  19. package/framework/data/qa-rubrics.yaml +335 -0
  20. package/framework/domains/constitution.yaml +3 -3
  21. package/framework/executors/json-validate.js +80 -0
  22. package/framework/executors/npm-script.js +72 -0
  23. package/framework/executors/path-check.js +67 -0
  24. package/framework/executors/registry.yaml +36 -0
  25. package/framework/hooks/advance-trigger.js +47 -3
  26. package/framework/hooks/constitution-guard.js +9 -4
  27. package/framework/hooks/git-push-authority.js +113 -0
  28. package/framework/hooks/license-guard.js +59 -9
  29. package/framework/hooks/model-governance.js +40 -17
  30. package/framework/hooks/prism-engine.js +78 -11
  31. package/framework/hooks/session-digest.js +12 -3
  32. package/framework/hooks/settings.json +7 -3
  33. package/framework/hooks/style-guard.js +52 -3
  34. package/framework/hooks/team-quality-gate.js +43 -5
  35. package/framework/i18n/en.yaml +3 -3
  36. package/framework/i18n/es.yaml +3 -3
  37. package/framework/i18n/fr.yaml +3 -3
  38. package/framework/i18n/pt.yaml +3 -3
  39. package/framework/intelligence/confidence.yaml +85 -6
  40. package/framework/intelligence/context-engine.md +9 -5
  41. package/framework/intelligence/decision-engine.md +1 -1
  42. package/framework/orchestrator/chati-router.js +59 -5
  43. package/framework/orchestrator/chati-update.md +19 -3
  44. package/framework/orchestrator/chati.md +20 -1
  45. package/framework/quality-gates/planning-gate.md +2 -0
  46. package/framework/rules/human-writing-style.md +47 -0
  47. package/framework/scaffold/motion-premium/README.md +12 -2
  48. package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +1 -1
  49. package/framework/scaffold/motion-premium/scaffold.yaml +6 -1
  50. package/framework/scaffold/motion-premium-3d/README.md +10 -0
  51. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +1 -1
  52. package/framework/scaffold/motion-premium-3d/scaffold.yaml +3 -0
  53. package/framework/scaffold/saas-dashboard/README.md +58 -0
  54. package/framework/scaffold/saas-dashboard/app/dashboard/page.tsx.template +546 -0
  55. package/framework/scaffold/saas-dashboard/app/globals.css.template +746 -0
  56. package/framework/scaffold/saas-dashboard/app/layout.tsx.template +192 -0
  57. package/framework/scaffold/saas-dashboard/app/shell.css.template +1070 -0
  58. package/framework/scaffold/saas-dashboard/components/data/Chart.tsx.template +725 -0
  59. package/framework/scaffold/saas-dashboard/components/data/DataTable.tsx.template +623 -0
  60. package/framework/scaffold/saas-dashboard/components/data/DataTable.types.ts.template +104 -0
  61. package/framework/scaffold/saas-dashboard/components/data/DataTablePagination.tsx.template +234 -0
  62. package/framework/scaffold/saas-dashboard/components/data/SkeletonBlock.tsx.template +111 -0
  63. package/framework/scaffold/saas-dashboard/components/data/StatCard.tsx.template +393 -0
  64. package/framework/scaffold/saas-dashboard/components/data/StatCardGrid.tsx.template +121 -0
  65. package/framework/scaffold/saas-dashboard/components/feedback/EmptyState.tsx.template +281 -0
  66. package/framework/scaffold/saas-dashboard/components/interaction/CommandPalette.tsx.template +767 -0
  67. package/framework/scaffold/saas-dashboard/components/interaction/Field.tsx.template +351 -0
  68. package/framework/scaffold/saas-dashboard/components/interaction/Form.tsx.template +424 -0
  69. package/framework/scaffold/saas-dashboard/components/primitives/Badge.tsx.template +193 -0
  70. package/framework/scaffold/saas-dashboard/components/primitives/Button.tsx.template +261 -0
  71. package/framework/scaffold/saas-dashboard/components/primitives/Card.tsx.template +287 -0
  72. package/framework/scaffold/saas-dashboard/components/primitives/Input.tsx.template +310 -0
  73. package/framework/scaffold/saas-dashboard/components/primitives/Spinner.tsx.template +135 -0
  74. package/framework/scaffold/saas-dashboard/components/primitives/index.ts.template +60 -0
  75. package/framework/scaffold/saas-dashboard/components/shell/AppShell.tsx.template +174 -0
  76. package/framework/scaffold/saas-dashboard/components/shell/Breadcrumb.tsx.template +186 -0
  77. package/framework/scaffold/saas-dashboard/components/shell/Container.tsx.template +86 -0
  78. package/framework/scaffold/saas-dashboard/components/shell/NavItem.tsx.template +147 -0
  79. package/framework/scaffold/saas-dashboard/components/shell/Sidebar.tsx.template +378 -0
  80. package/framework/scaffold/saas-dashboard/components/shell/SidebarContext.tsx.template +169 -0
  81. package/framework/scaffold/saas-dashboard/components/shell/ThemeToggle.tsx.template +128 -0
  82. package/framework/scaffold/saas-dashboard/components/shell/Topbar.tsx.template +301 -0
  83. package/framework/scaffold/saas-dashboard/lib/brand.ts.template +324 -0
  84. package/framework/scaffold/saas-dashboard/scaffold.yaml +225 -0
  85. package/framework/schemas/session.schema.json +5 -0
  86. package/framework/schemas/task.schema.json +9 -0
  87. package/framework/scripts/visual-qa.js +101 -3
  88. package/framework/tasks/orchestrator-health.md +4 -4
  89. package/framework/tasks/qa-impl-verdict.md +11 -5
  90. package/framework/templates/brandbook-html-tmpl.md +1 -1
  91. package/framework/templates/qa-gate-tmpl.yaml +24 -11
  92. package/package.json +4 -3
  93. package/src/config/context-file-generator.js +0 -6
  94. package/src/dashboard/renderer.js +0 -36
  95. package/src/executors/runner.js +204 -0
  96. package/src/installer/core.js +13 -3
  97. package/src/installer/templates.js +5 -6
  98. package/src/intelligence/registry-manager.js +1 -1
  99. package/src/license/client.js +9 -3
  100. package/src/license/commands.js +12 -2
  101. package/src/license/machine-id.js +42 -1
  102. package/src/memory/gotchas.js +58 -40
  103. package/src/memory/magic-docs.js +1 -1
  104. package/src/memory/session-digest.js +9 -4
  105. package/src/orchestrator/cli.js +186 -15
  106. package/src/orchestrator/doctor.js +98 -16
  107. package/src/orchestrator/pipeline-manager.js +44 -22
  108. package/src/orchestrator/session-manager.js +64 -8
  109. package/src/telemetry/sender.js +7 -19
  110. package/src/terminal/run-team.js +3 -3
  111. package/src/upgrade/tracked-files-detector.js +34 -16
  112. package/src/utils/feature-flags.js +1 -1
  113. package/src/utils/flatten-entities.js +4 -40
  114. package/src/utils/schema-validator.js +0 -14
  115. package/src/wizard/i18n.js +3 -3
  116. package/src/api/index.js +0 -120
  117. package/src/autonomy/autonomous-gate.js +0 -294
  118. package/src/autonomy/build-loop.js +0 -281
  119. package/src/autonomy/build-state.js +0 -286
  120. package/src/autonomy/cause-analyzer.js +0 -177
  121. package/src/autonomy/escalation.js +0 -214
  122. package/src/autonomy/index.js +0 -51
  123. package/src/autonomy/mode-manager.js +0 -225
  124. package/src/autonomy/mode-suggester.js +0 -283
  125. package/src/autonomy/progress-reporter.js +0 -275
  126. package/src/autonomy/safety-net.js +0 -370
  127. package/src/config/agent-customizer.js +0 -231
  128. package/src/decision/analyzer.js +0 -291
  129. package/src/decision/engine.js +0 -250
  130. package/src/decision/index.js +0 -38
  131. package/src/decision/registry-healer.js +0 -468
  132. package/src/decision/registry-updater.js +0 -339
  133. package/src/extensions/loader.js +0 -145
  134. package/src/extensions/registry.js +0 -134
  135. package/src/gates/circuit-breaker.js +0 -151
  136. package/src/gates/g1-planning-complete.js +0 -154
  137. package/src/gates/g2-qa-planning.js +0 -156
  138. package/src/gates/g3-implementation.js +0 -215
  139. package/src/gates/g4-qa-implementation.js +0 -240
  140. package/src/gates/g5-deploy-ready.js +0 -181
  141. package/src/gates/gate-base.js +0 -185
  142. package/src/gates/index.js +0 -46
  143. package/src/health/auto-fix.js +0 -216
  144. package/src/health/engine.js +0 -246
  145. package/src/merger/semantic-merger.js +0 -292
  146. package/src/preview/detector.js +0 -238
  147. package/src/preview/index.js +0 -20
  148. package/src/preview/launcher.js +0 -235
  149. package/src/preview/log-buffer.js +0 -103
  150. package/src/quality/metrics-collector.js +0 -281
  151. package/src/quality/test-runner.js +0 -366
  152. package/src/tasks/executor.js +0 -195
  153. package/src/tasks/index.js +0 -4
  154. package/src/tasks/loader.js +0 -210
  155. package/src/tasks/router.js +0 -182
  156. package/src/utils/event-bus.js +0 -126
@@ -0,0 +1,351 @@
1
+ /**
2
+ * components/interaction/Field.tsx
3
+ * SaaS Dashboard Preset -- D_interaction phase
4
+ *
5
+ * WHY THIS EXISTS
6
+ * ---------------
7
+ * The Field primitive is the accessibility contract boundary for every input
8
+ * in the dashboard. Two failure modes plague dashboard forms:
9
+ *
10
+ * 1. Placeholder-only labels: placeholder disappears on focus, leaving
11
+ * users with short-term memory limitations (or filling multiple fields
12
+ * rapidly) unable to recall what a field is for. WCAG 1.3.1 requires
13
+ * programmatic labels. This component makes it structurally impossible
14
+ * to render an input without a <label>.
15
+ *
16
+ * 2. Error announced too late: error text that appears only near the
17
+ * submit button is not announced to screen reader users focused inside
18
+ * the form. The `role="alert"` on the error message here fires
19
+ * immediately when error text appears, regardless of focus position.
20
+ *
21
+ * ARCHITECTURE DECISIONS
22
+ * ----------------------
23
+ * 1. Field is the composition root; it renders the <label>, hint, and error.
24
+ * The <Input> sub-component handles only the input element itself.
25
+ * This separation means the Field wrapper can be reused with <textarea>
26
+ * or <select> by passing `as="textarea"` / `as="select"`, without
27
+ * duplicating label/hint/error logic.
28
+ *
29
+ * 2. `labelHidden` prop renders the label text inside a `.sr-only` span
30
+ * so visually minimal forms (e.g., inline search) still have SR labels.
31
+ * The label element is NEVER removed from the DOM.
32
+ *
33
+ * 3. Icon slots use absolute positioning within a `position:relative` wrapper.
34
+ * The input's padding-left / padding-right adjusts via inline style when
35
+ * icons are present. This avoids flexbox-inside-input hacks and keeps
36
+ * the input text baseline aligned regardless of icon presence.
37
+ *
38
+ * 4. The `required` prop adds an asterisk in the label AND sets
39
+ * `aria-required="true"` on the input. Both are needed: the asterisk
40
+ * is a visual convention; aria-required communicates to SR users who
41
+ * may not see the asterisk.
42
+ *
43
+ * 5. Focus ring: implemented globally via `:focus-visible` in globals.css.
44
+ * The input itself does NOT set outline -- it relies on the global rule,
45
+ * which applies the --shadow-focus token that includes a 2px ring with
46
+ * 2px offset.
47
+ *
48
+ * STATE TRANSITIONS
49
+ * -----------------
50
+ * resting -> border: var(--color-border-default)
51
+ * focus -> border: var(--color-border-emphasis); transition duration-normal
52
+ * error -> border: var(--color-danger); bg: var(--color-danger-subtle) tint
53
+ * disabled -> opacity 0.5; cursor: not-allowed
54
+ *
55
+ * TOKEN USAGE
56
+ * -----------
57
+ * All properties reference CSS custom properties from globals.css.
58
+ * No raw hex values, no raw pixel literals.
59
+ */
60
+
61
+ 'use client';
62
+
63
+ import React, {
64
+ forwardRef,
65
+ useId,
66
+ type InputHTMLAttributes,
67
+ type TextareaHTMLAttributes,
68
+ type SelectHTMLAttributes,
69
+ type ReactNode,
70
+ } from 'react';
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Types
74
+ // ---------------------------------------------------------------------------
75
+
76
+ type InputAs = 'input' | 'textarea' | 'select';
77
+
78
+ export interface FieldProps {
79
+ /** Always rendered as a real <label>. Never omitted. */
80
+ label: string;
81
+ /** Hint text below the input; linked via aria-describedby. */
82
+ hint?: string;
83
+ /** Error message. Overrides hint. Sets aria-invalid and role=alert. */
84
+ error?: string;
85
+ /** Visually hides the label but keeps it in the DOM for screen readers. */
86
+ labelHidden?: boolean;
87
+ /** Lucide icon rendered inside the input on the left. */
88
+ iconLeft?: ReactNode;
89
+ /** Lucide icon or button rendered inside the input on the right. */
90
+ iconRight?: ReactNode;
91
+ /** Adds asterisk and aria-required. */
92
+ required?: boolean;
93
+ /** Element to render. Default: 'input'. */
94
+ as?: InputAs;
95
+ /** Passed to underlying input element. */
96
+ inputProps?: InputHTMLAttributes<HTMLInputElement>;
97
+ /** Passed when as='textarea'. */
98
+ textareaProps?: TextareaHTMLAttributes<HTMLTextAreaElement>;
99
+ /** Passed when as='select'. */
100
+ selectProps?: SelectHTMLAttributes<HTMLSelectElement>;
101
+ /** Children for select options when as='select'. */
102
+ children?: ReactNode;
103
+ /** Additional class on the field wrapper. */
104
+ className?: string;
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // Shared input styles (CSS-in-JS-style object used across all variants)
109
+ // ---------------------------------------------------------------------------
110
+
111
+ function buildInputStyle({
112
+ hasError,
113
+ isDisabled,
114
+ iconLeftPresent,
115
+ iconRightPresent,
116
+ }: {
117
+ hasError: boolean;
118
+ isDisabled: boolean;
119
+ iconLeftPresent: boolean;
120
+ iconRightPresent: boolean;
121
+ }): React.CSSProperties {
122
+ return {
123
+ display: 'block',
124
+ width: '100%',
125
+ minHeight: '40px',
126
+ paddingTop: 'var(--spacing-input-pad-v)',
127
+ paddingBottom: 'var(--spacing-input-pad-v)',
128
+ paddingLeft: iconLeftPresent ? '36px' : 'var(--spacing-input-pad-h)',
129
+ paddingRight: iconRightPresent ? '36px' : 'var(--spacing-input-pad-h)',
130
+ backgroundColor: 'var(--color-bg-sunken)',
131
+ color: hasError ? 'var(--color-text-primary)' : 'var(--color-text-primary)',
132
+ fontSize: 'var(--type-body)',
133
+ lineHeight: 'var(--type-body-lh)',
134
+ fontFamily: 'var(--font-sans)',
135
+ borderRadius: 'var(--radius-default)',
136
+ border: hasError
137
+ ? '1px solid var(--color-danger)'
138
+ : '1px solid var(--color-border-default)',
139
+ outline: 'none',
140
+ opacity: isDisabled ? 0.5 : 1,
141
+ cursor: isDisabled ? 'not-allowed' : 'text',
142
+ transition: `border-color var(--duration-normal) var(--ease-in-out)`,
143
+ // Focus border via CSS :focus-visible in globals.css (:focus-visible + border-color)
144
+ };
145
+ }
146
+
147
+ // ---------------------------------------------------------------------------
148
+ // Icon slot helper
149
+ // ---------------------------------------------------------------------------
150
+
151
+ function IconSlot({
152
+ side,
153
+ children,
154
+ }: {
155
+ side: 'left' | 'right';
156
+ children: ReactNode;
157
+ }) {
158
+ return (
159
+ <span
160
+ aria-hidden="true"
161
+ style={{
162
+ position: 'absolute',
163
+ top: '50%',
164
+ transform: 'translateY(-50%)',
165
+ [side === 'left' ? 'left' : 'right']: '10px',
166
+ display: 'flex',
167
+ alignItems: 'center',
168
+ justifyContent: 'center',
169
+ color: 'var(--color-text-muted)',
170
+ pointerEvents: 'none',
171
+ zIndex: 1,
172
+ }}
173
+ >
174
+ {children}
175
+ </span>
176
+ );
177
+ }
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // Field component
181
+ // ---------------------------------------------------------------------------
182
+
183
+ export const Field = forwardRef<HTMLDivElement, FieldProps>(
184
+ function Field(
185
+ {
186
+ label,
187
+ hint,
188
+ error,
189
+ labelHidden = false,
190
+ iconLeft,
191
+ iconRight,
192
+ required = false,
193
+ as: As = 'input',
194
+ inputProps,
195
+ textareaProps,
196
+ selectProps,
197
+ children,
198
+ className,
199
+ },
200
+ ref,
201
+ ) {
202
+ const inputId = useId();
203
+ const hintId = useId();
204
+ const errorId = useId();
205
+
206
+ const hasError = Boolean(error);
207
+ const isDisabled =
208
+ As === 'input'
209
+ ? Boolean(inputProps?.disabled)
210
+ : As === 'textarea'
211
+ ? Boolean(textareaProps?.disabled)
212
+ : Boolean(selectProps?.disabled);
213
+
214
+ // aria-describedby links input to hint or error message
215
+ const describedBy = hasError ? errorId : hint ? hintId : undefined;
216
+
217
+ const inputStyle = buildInputStyle({
218
+ hasError,
219
+ isDisabled,
220
+ iconLeftPresent: Boolean(iconLeft),
221
+ iconRightPresent: Boolean(iconRight),
222
+ });
223
+
224
+ return (
225
+ <div
226
+ ref={ref}
227
+ className={className}
228
+ style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-2)' }}
229
+ >
230
+ {/* Label */}
231
+ <label
232
+ htmlFor={inputId}
233
+ style={{
234
+ display: 'flex',
235
+ alignItems: 'center',
236
+ gap: 'var(--space-1)',
237
+ fontSize: 'var(--type-small)',
238
+ lineHeight: 'var(--type-small-lh)',
239
+ fontWeight: 500,
240
+ color: 'var(--color-text-primary)',
241
+ }}
242
+ >
243
+ {labelHidden ? (
244
+ <span className="sr-only">{label}</span>
245
+ ) : (
246
+ <span>{label}</span>
247
+ )}
248
+ {required && (
249
+ <span
250
+ aria-hidden="true"
251
+ style={{ color: 'var(--color-danger)', marginLeft: 'var(--space-px)' }}
252
+ >
253
+ *
254
+ </span>
255
+ )}
256
+ </label>
257
+
258
+ {/* Input wrapper (relative for icon slots) */}
259
+ <div style={{ position: 'relative' }}>
260
+ {iconLeft && <IconSlot side="left">{iconLeft}</IconSlot>}
261
+
262
+ {As === 'textarea' ? (
263
+ <textarea
264
+ id={inputId}
265
+ aria-required={required || undefined}
266
+ aria-invalid={hasError || undefined}
267
+ aria-describedby={describedBy}
268
+ disabled={isDisabled}
269
+ {...textareaProps}
270
+ style={{
271
+ ...inputStyle,
272
+ minHeight: '80px',
273
+ resize: 'vertical',
274
+ cursor: isDisabled ? 'not-allowed' : 'auto',
275
+ ...textareaProps?.style,
276
+ }}
277
+ />
278
+ ) : As === 'select' ? (
279
+ <select
280
+ id={inputId}
281
+ aria-required={required || undefined}
282
+ aria-invalid={hasError || undefined}
283
+ aria-describedby={describedBy}
284
+ disabled={isDisabled}
285
+ {...selectProps}
286
+ style={{
287
+ ...inputStyle,
288
+ appearance: 'none',
289
+ cursor: isDisabled ? 'not-allowed' : 'pointer',
290
+ paddingRight: '36px',
291
+ ...selectProps?.style,
292
+ }}
293
+ >
294
+ {children}
295
+ </select>
296
+ ) : (
297
+ <input
298
+ id={inputId}
299
+ aria-required={required || undefined}
300
+ aria-invalid={hasError || undefined}
301
+ aria-describedby={describedBy}
302
+ disabled={isDisabled}
303
+ {...inputProps}
304
+ style={{
305
+ ...inputStyle,
306
+ ...inputProps?.style,
307
+ }}
308
+ />
309
+ )}
310
+
311
+ {iconRight && <IconSlot side="right">{iconRight}</IconSlot>}
312
+ </div>
313
+
314
+ {/* Error message (role=alert for immediate SR announcement) */}
315
+ {hasError && (
316
+ <p
317
+ id={errorId}
318
+ role="alert"
319
+ style={{
320
+ fontSize: 'var(--type-caption)',
321
+ lineHeight: 'var(--type-caption-lh)',
322
+ color: 'var(--color-danger)',
323
+ margin: 0,
324
+ }}
325
+ >
326
+ {error}
327
+ </p>
328
+ )}
329
+
330
+ {/* Hint (shown when no error) */}
331
+ {!hasError && hint && (
332
+ <p
333
+ id={hintId}
334
+ style={{
335
+ fontSize: 'var(--type-caption)',
336
+ lineHeight: 'var(--type-caption-lh)',
337
+ color: 'var(--color-text-muted)',
338
+ margin: 0,
339
+ }}
340
+ >
341
+ {hint}
342
+ </p>
343
+ )}
344
+ </div>
345
+ );
346
+ },
347
+ );
348
+
349
+ Field.displayName = 'Field';
350
+
351
+ export default Field;