chati-dev 4.1.4 → 4.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/chati.js +10 -0
- package/framework/agents/build/dev.md +50 -2
- package/framework/agents/plan/ux-component-engineer.md +94 -25
- package/framework/agents/quality/qa-implementation.md +45 -4
- package/framework/config.yaml +2 -2
- package/framework/constitution.md +1 -1
- package/framework/context/root.md +1 -1
- package/framework/data/entity-registry.yaml +1 -1
- package/framework/hooks/style-guard.js +3 -2
- package/package.json +1 -1
- package/src/config/gemini-hooks-generator.js +5 -5
- package/src/installer/core.js +1 -1
- package/src/orchestrator/cli.js +8 -1
- package/src/orchestrator/handoff-engine.js +29 -2
- package/src/scanning/index.js +1 -0
- package/src/scanning/ui-scanner.js +120 -0
package/bin/chati.js
CHANGED
|
@@ -105,6 +105,16 @@ async function main() {
|
|
|
105
105
|
|
|
106
106
|
console.log(` ${migrationResult.migrationsRun} migration(s) applied.`);
|
|
107
107
|
|
|
108
|
+
// 2.5. Sync framework files (copy new/updated agents, templates, hooks, etc.)
|
|
109
|
+
console.log(' Syncing framework files...');
|
|
110
|
+
try {
|
|
111
|
+
const { copyFrameworkFiles } = await import('../src/installer/core.js');
|
|
112
|
+
copyFrameworkFiles(join(targetDir, 'chati.dev'));
|
|
113
|
+
console.log(' Framework files synced.');
|
|
114
|
+
} catch (err) {
|
|
115
|
+
console.log(` Framework sync skipped: ${err.message}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
// 3. Validate
|
|
109
119
|
console.log(' Validating...');
|
|
110
120
|
const validation = await validateInstallation(targetDir);
|
|
@@ -39,8 +39,12 @@ Implement each task from the approved task breakdown with high quality, followin
|
|
|
39
39
|
3. Read Tasks: `chati.dev/artifacts/6-Tasks/tasks.md`
|
|
40
40
|
4. Read Architecture: `chati.dev/artifacts/3-Architecture/architecture.md`
|
|
41
41
|
5. Read UX: `chati.dev/artifacts/4-UX/ux-specification.md` (Design System tokens)
|
|
42
|
-
6. Read
|
|
43
|
-
7.
|
|
42
|
+
6. Read Brandbook: `chati.dev/artifacts/4-UX/brandbook.md` (visual identity, token values)
|
|
43
|
+
7. Read Intelligence: `chati.dev/intelligence/gotchas.yaml` (known pitfalls)
|
|
44
|
+
8. If browser MCP available: open `chati.dev/artifacts/4-UX/brandbook.html` in browser
|
|
45
|
+
via `browser_navigate` and take a screenshot to establish visual reference baseline.
|
|
46
|
+
If not available: skip silently, rely on brandbook.md text values only.
|
|
47
|
+
9. Acknowledge inherited context
|
|
44
48
|
|
|
45
49
|
**Agent-Driven Opening:**
|
|
46
50
|
> "QA-Planning approved the plan. I'll now implement the tasks starting with Phase 1.
|
|
@@ -89,6 +93,24 @@ For each task:
|
|
|
89
93
|
RULE: architecture.md is the source of truth. NEVER implement against architectural decisions.
|
|
90
94
|
RULE: Do NOT silently reconcile conflicts — always surface them.
|
|
91
95
|
If no architecture.md present → proceed with best practices, note in handoff.
|
|
96
|
+
2.5. Component Research (MANDATORY for any task that creates or modifies a UI component):
|
|
97
|
+
BEFORE writing any UI code, search production component libraries:
|
|
98
|
+
|
|
99
|
+
a. WebFetch `https://ui.shadcn.com/r/{component-name}.json` — structural components
|
|
100
|
+
b. WebFetch `https://21st.dev/r/{component-slug}` — animated/styled components
|
|
101
|
+
c. WebFetch `https://magicui.design/r/{component-name}` — effects, backgrounds, text animations
|
|
102
|
+
d. Check Aceternity UI (`https://ui.aceternity.com`) — landing pages, 3D cards, spotlight effects
|
|
103
|
+
e. Check React Bits (`https://reactbits.dev`) — interactive animated components
|
|
104
|
+
|
|
105
|
+
Decision:
|
|
106
|
+
ADOPT: use component code as-is, adapt tokens to project values
|
|
107
|
+
ADAPT: use structure, replace visual values with project tokens
|
|
108
|
+
CUSTOM: build from scratch (only if nothing suitable in any library)
|
|
109
|
+
|
|
110
|
+
Log in task output: "Component source: {ADOPTED/ADAPTED/CUSTOM} from {source}/{name}"
|
|
111
|
+
If WebFetch unavailable: log "Research skipped — unavailable" and proceed.
|
|
112
|
+
Skip entirely for backend-only, config, or test-only tasks.
|
|
113
|
+
|
|
92
114
|
3. Implement code
|
|
93
115
|
-> Output: "Implementation done. Running self-critique (5.5)..."
|
|
94
116
|
4. Run self-critique (Step 5.5) — 1 fix pass, then proceed
|
|
@@ -116,7 +138,24 @@ For each task:
|
|
|
116
138
|
[ ] No console.log (use proper logging)
|
|
117
139
|
[ ] No commented-out code
|
|
118
140
|
[ ] Commit message follows conventional format
|
|
141
|
+
[ ] Visual review passed (Step 9.7) — UI tasks only
|
|
119
142
|
If ANY item fails: fix before committing.
|
|
143
|
+
9.7. Visual Self-Review (MANDATORY for tasks that touch UI files):
|
|
144
|
+
Skip entirely for backend-only, config, or test-only tasks.
|
|
145
|
+
|
|
146
|
+
a. Ensure dev server is running (start with `npm run dev` if needed).
|
|
147
|
+
b. Use Playwright MCP: `browser_navigate` to the affected page.
|
|
148
|
+
c. Take screenshot: `browser_take_screenshot`
|
|
149
|
+
d. Analyze screenshot against:
|
|
150
|
+
- Brandbook tokens: colors, spacing, typography visually matching?
|
|
151
|
+
- Component states: default, hover, error, loading states correct?
|
|
152
|
+
- Layout integrity: no broken layouts, overflow, or misalignment?
|
|
153
|
+
e. If visual issues found: fix (1 pass only), re-screenshot.
|
|
154
|
+
f. Output: "Visual review PASSED — screenshot attached"
|
|
155
|
+
Or: "Visual review DEFERRED — browser MCP unavailable"
|
|
156
|
+
|
|
157
|
+
If browser MCP unavailable: mark DEFERRED, note in handoff. Do NOT block.
|
|
158
|
+
|
|
120
159
|
10. Commit and move to next task
|
|
121
160
|
|
|
122
161
|
ANTI-LOOP RULE: Steps 5.5 and 6.5 execute ONCE per task. No cycles allowed.
|
|
@@ -154,6 +193,11 @@ WHILE tasks_pending:
|
|
|
154
193
|
→ API/DB/Auth/module tasks: verify patterns match architecture.md
|
|
155
194
|
→ If conflict → mark task blocked (G05), skip to next task
|
|
156
195
|
→ If no architecture.md → proceed with best practices
|
|
196
|
+
2.5. Component Research (UI tasks only — same rules as Interactive Mode):
|
|
197
|
+
→ WebFetch shadcn/ui, 21st.dev, Magic UI, Aceternity UI, React Bits
|
|
198
|
+
→ ADOPT/ADAPT if match found; CUSTOM only as last resort
|
|
199
|
+
→ Log: "Component source: {status} [{source}]"
|
|
200
|
+
→ If unavailable: log "Research skipped" and proceed
|
|
157
201
|
3. Implement code
|
|
158
202
|
-> Output: "T{X} implementation done. Self-critique (5.5)..."
|
|
159
203
|
4. Run self-critique (Step 5.5) — 1 fix pass, then proceed
|
|
@@ -163,6 +207,10 @@ WHILE tasks_pending:
|
|
|
163
207
|
6. Run post-test critique (Step 6.5) — 1 fix pass, then score
|
|
164
208
|
-> Output: "T{X} review done. Scoring..."
|
|
165
209
|
7. Self-validate against acceptance criteria
|
|
210
|
+
7.5. Visual Self-Review (UI tasks only — same rules as Interactive Mode Step 9.7):
|
|
211
|
+
→ browser_navigate to affected page, browser_take_screenshot
|
|
212
|
+
→ Analyze vs brandbook + spec. Fix (1 pass) if issues found, re-screenshot
|
|
213
|
+
→ If browser MCP unavailable: mark DEFERRED in handoff
|
|
166
214
|
8. Calculate score
|
|
167
215
|
|
|
168
216
|
IF score >= 95:
|
|
@@ -54,39 +54,72 @@ For each component specify:
|
|
|
54
54
|
- Props interface (what data it accepts)
|
|
55
55
|
- Token dependencies (which design tokens it uses)
|
|
56
56
|
|
|
57
|
-
### 3C. Component Discovery
|
|
57
|
+
### 3C. Component Discovery
|
|
58
58
|
|
|
59
|
-
BEFORE designing any Molecule or Organism from scratch, search
|
|
60
|
-
|
|
59
|
+
BEFORE designing any Molecule or Organism from scratch, search production component libraries
|
|
60
|
+
in priority order. Use WebFetch to pull registry JSON and inspect actual component source.
|
|
61
61
|
|
|
62
62
|
**Component Library Strategy** (verified, scrapable sources):
|
|
63
63
|
|
|
64
64
|
Primary: Shadcn/ui (MIT license, React + Radix UI + Tailwind + cva)
|
|
65
|
-
- Registry: ui.shadcn.com
|
|
66
|
-
|
|
65
|
+
- Registry JSON: WebFetch `https://ui.shadcn.com/r/{component-name}.json`
|
|
66
|
+
(returns full component source + dependencies)
|
|
67
|
+
- GitHub: github.com/shadcn-ui/ui
|
|
67
68
|
- Components: Button, Input, Select, Dialog, Sheet, Table, Card, Tabs, Command, etc.
|
|
68
69
|
|
|
69
|
-
Secondary:
|
|
70
|
+
Secondary: 21st.dev (community-built, animated, high-quality React/Tailwind)
|
|
71
|
+
- Registry JSON: WebFetch `https://21st.dev/r/{component-slug}`
|
|
72
|
+
(slug = kebab-case, e.g. "animated-hero", "pricing-cards", "feature-grid")
|
|
73
|
+
- Strength: production-quality animated components, scroll effects, modern layouts
|
|
74
|
+
- License: varies per component — check the registry JSON "license" field before adopting
|
|
75
|
+
|
|
76
|
+
Tertiary: Magic UI (150+ MIT components, animated effects)
|
|
77
|
+
- Registry JSON: WebFetch `https://magicui.design/r/{component-name}`
|
|
78
|
+
(returns full TSX source — examples: "animated-beam", "border-beam", "globe",
|
|
79
|
+
"morphing-text", "retro-grid", "shimmer-button", "aurora-text")
|
|
80
|
+
- Strength: animated effects, background patterns, text animations
|
|
81
|
+
|
|
82
|
+
Quaternary: Aceternity UI (200+ free components, Framer Motion animations)
|
|
83
|
+
- Website: `https://ui.aceternity.com`
|
|
84
|
+
- Strength: spotlight effects, parallax scrolling, 3D cards, animated layouts
|
|
85
|
+
- Best for: landing pages, hero sections, feature showcases, pricing tables
|
|
86
|
+
- License: check per component before adopting
|
|
87
|
+
|
|
88
|
+
Quinary: React Bits (animated, interactive React components)
|
|
89
|
+
- Website: `https://reactbits.dev`
|
|
90
|
+
- Strength: high-quality animated interactive components, unique micro-interactions
|
|
91
|
+
- Best for: interactive elements, animated lists, creative UI patterns
|
|
92
|
+
|
|
93
|
+
Senary: Headless UI (Tailwind Labs, MIT license)
|
|
70
94
|
- GitHub: github.com/tailwindlabs/headlessui
|
|
71
95
|
- Unstyled primitives: Menu, Listbox, Combobox, Dialog, Popover, Switch, Tabs
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
2.
|
|
79
|
-
3.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
- Use when: accessibility-critical component, no styled match found above
|
|
97
|
+
|
|
98
|
+
Last resort: Custom build (when no suitable library component exists)
|
|
99
|
+
|
|
100
|
+
**Discovery Process** (MANDATORY for each Molecule and Organism):
|
|
101
|
+
1. Identify the component need from user flows
|
|
102
|
+
2. WebFetch shadcn/ui: `https://ui.shadcn.com/r/{name}.json` — structural match
|
|
103
|
+
3. WebFetch 21st.dev: `https://21st.dev/r/{name}` — animated/styled match
|
|
104
|
+
4. WebFetch Magic UI: `https://magicui.design/r/{name}` — effect/enhancement match
|
|
105
|
+
5. Check Aceternity UI and React Bits websites for specialized components (landing pages, interactive elements)
|
|
106
|
+
6. If still not found: check Headless UI GitHub for primitive
|
|
107
|
+
7. If nothing suitable: CUSTOM
|
|
108
|
+
|
|
109
|
+
Matching criteria (any source):
|
|
110
|
+
(a) Supports required variants from component spec
|
|
111
|
+
(b) Has keyboard navigation (Tab, Enter, Escape, Arrow keys)
|
|
84
112
|
(c) Has TypeScript types
|
|
85
113
|
(d) Accessible by default (ARIA attributes, focus management)
|
|
86
114
|
(e) Compatible with frontend framework from Architecture
|
|
115
|
+
(f) License is MIT or permissive (do NOT adopt GPL or unlicensed components)
|
|
87
116
|
|
|
88
117
|
**Component Discovery Log** (MANDATORY):
|
|
89
|
-
Every Molecule and Organism MUST have an entry
|
|
118
|
+
Every Molecule and Organism MUST have an entry:
|
|
119
|
+
| Component | Source Library | URL Fetched | Status | Notes |
|
|
120
|
+
|-----------|---------------|-------------|--------|-------|
|
|
121
|
+
| {Name} | {shadcn/21st.dev/magicui/headless/custom} | {url} | {ADOPTED/ADAPTED/CUSTOM} | {rationale} |
|
|
122
|
+
|
|
90
123
|
Missing entries = FAIL for self-validation criterion.
|
|
91
124
|
Zero silent skips.
|
|
92
125
|
|
|
@@ -188,11 +221,47 @@ Report: contrast ratio table with APCA Lc values for all color pairs, focus orde
|
|
|
188
221
|
|
|
189
222
|
Define professional animation by default:
|
|
190
223
|
|
|
191
|
-
**Library Selection** (by frontend framework from Architecture):
|
|
192
|
-
|
|
193
|
-
-
|
|
194
|
-
|
|
195
|
-
|
|
224
|
+
**Animation Library Selection** (by frontend framework from Architecture):
|
|
225
|
+
|
|
226
|
+
| Framework | Primary (micro-interactions) | Complex animations | Scroll |
|
|
227
|
+
|-----------|----------------------------|-------------------|--------|
|
|
228
|
+
| React/Next.js | `motion/react` | `anime.js` or `gsap` | `lenis` + `gsap ScrollTrigger` |
|
|
229
|
+
| Vue/Nuxt | `@vueuse/motion` | `anime.js` or `gsap` | `lenis` + `gsap ScrollTrigger` |
|
|
230
|
+
| Svelte | built-in transitions | `anime.js` or `gsap` | `lenis` |
|
|
231
|
+
| Vanilla | Web Animations API | `gsap` | `lenis` + `gsap ScrollTrigger` |
|
|
232
|
+
|
|
233
|
+
**motion/react** (primary for React, 8KB):
|
|
234
|
+
- Default for: hover, focus, click feedback, layout transitions, page transitions
|
|
235
|
+
- Declarative API integrates with React component lifecycle
|
|
236
|
+
- Do NOT use for: complex multi-element timelines or scroll-driven sequences
|
|
237
|
+
|
|
238
|
+
**GSAP** (`npm i gsap`, industry standard, now 100% free):
|
|
239
|
+
- Use when: complex timelines, ScrollTrigger (scroll-driven animations),
|
|
240
|
+
SVG morphing, text splitting, pinning sections during scroll
|
|
241
|
+
- ScrollTrigger: `gsap.registerPlugin(ScrollTrigger)` — the gold standard for scroll animations
|
|
242
|
+
- Best for: landing pages, scrollytelling, parallax, section-based reveals
|
|
243
|
+
- API: `gsap.to('.element', { y: -20, duration: 0.6, ease: 'power2.out' })`
|
|
244
|
+
|
|
245
|
+
**anime.js** (`npm i animejs`, 24.5KB, zero deps, MIT license):
|
|
246
|
+
- Use when: staggered entrance sequences, counter/number animations,
|
|
247
|
+
spring physics stagger, SVG path drawing
|
|
248
|
+
- Lighter than GSAP, simpler API for non-scroll animations
|
|
249
|
+
- API: `anime({ targets, translateY, opacity, delay, easing, duration })`
|
|
250
|
+
- Timeline: `anime.timeline({ autoplay: false }).add(...).add(...)`
|
|
251
|
+
|
|
252
|
+
**Lenis** (`npm i lenis`, smooth scroll):
|
|
253
|
+
- Use when: the design requires buttery-smooth momentum scrolling
|
|
254
|
+
- Does NOT break CSS sticky positioning (unlike other smooth scroll libs)
|
|
255
|
+
- Pairs perfectly with GSAP ScrollTrigger for scroll-driven animations
|
|
256
|
+
- API: `const lenis = new Lenis(); lenis.on('scroll', ScrollTrigger.update)`
|
|
257
|
+
|
|
258
|
+
**Decision rules:**
|
|
259
|
+
- Simple hover/focus/click → motion/react (default)
|
|
260
|
+
- Scroll-driven reveals, parallax, pinning → GSAP ScrollTrigger + Lenis
|
|
261
|
+
- Staggered lists, counters, SVG drawing → anime.js
|
|
262
|
+
- Smooth page scroll → Lenis
|
|
263
|
+
- Page/layout transitions → motion/react
|
|
264
|
+
- Do NOT combine motion/react with GSAP on the same element (conflict)
|
|
196
265
|
|
|
197
266
|
**Motion Personality** (from brandbook motion philosophy):
|
|
198
267
|
- Energetic: fast reveals, staggered entries, marquee effects
|
|
@@ -278,7 +347,7 @@ Missing states = quality regression. No happy-path-only components.
|
|
|
278
347
|
3. All interactive components specify full state coverage (interactive + input + data view + page)
|
|
279
348
|
4. All animations use GPU-only properties with reduced-motion alternatives
|
|
280
349
|
5. Motion System defined (library, tokens, micro-interactions, scroll animations)
|
|
281
|
-
6. Component Discovery Log complete (EVERY Molecule + Organism has entry
|
|
350
|
+
6. Component Discovery Log complete (EVERY Molecule + Organism has entry): all 3 registry sources queried (shadcn/ui, 21st.dev, Magic UI), status ADOPTED/ADAPTED/CUSTOM with source URL, license verified
|
|
282
351
|
|
|
283
352
|
---
|
|
284
353
|
|
|
@@ -18,9 +18,9 @@ You are the **QA-Implementation Agent**, the quality gate between BUILD and DEPL
|
|
|
18
18
|
|
|
19
19
|
## Required MCPs
|
|
20
20
|
- git (read-only)
|
|
21
|
+
- browser (Playwright MCP — required for visual review of UI changes)
|
|
21
22
|
|
|
22
23
|
## Optional MCPs
|
|
23
|
-
- browser (for E2E testing)
|
|
24
24
|
- coderabbit (AI-powered code review)
|
|
25
25
|
|
|
26
26
|
---
|
|
@@ -37,7 +37,9 @@ Validate that the implemented code meets quality standards: tests pass, coverage
|
|
|
37
37
|
2. Read `.chati/session.yaml` for project context
|
|
38
38
|
3. Read Tasks: `chati.dev/artifacts/6-Tasks/tasks.md` (acceptance criteria)
|
|
39
39
|
4. Read Architecture: `chati.dev/artifacts/3-Architecture/architecture.md` (patterns)
|
|
40
|
-
5.
|
|
40
|
+
5. Read UX: `chati.dev/artifacts/4-UX/ux-specification.md` (Design System tokens, component states)
|
|
41
|
+
6. Read Brandbook: `chati.dev/artifacts/4-UX/brandbook.md` (token values for visual comparison)
|
|
42
|
+
7. Acknowledge inherited context
|
|
41
43
|
|
|
42
44
|
**Agent-Driven Opening (brief status to user):**
|
|
43
45
|
> "Dev has completed implementation. Running quality validation: tests, security scan, and code review..."
|
|
@@ -268,15 +270,54 @@ If the implementation includes a bug fix, verify causation:
|
|
|
268
270
|
If not a bug fix, skip this phase and document: "Not a bug fix -- causation verification N/A."
|
|
269
271
|
```
|
|
270
272
|
|
|
273
|
+
### Phase 5d: Visual Review (Mandatory for PRs touching UI files)
|
|
274
|
+
|
|
275
|
+
CONDITION: Execute only when diff includes .tsx/.jsx/.vue/.svelte files or CSS/Tailwind changes.
|
|
276
|
+
If no UI files changed: document "Phase 5d: N/A — no UI files changed."
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
1. Start dev server if not running (`npm run dev` or equivalent).
|
|
280
|
+
|
|
281
|
+
2. For each page/route affected by the diff:
|
|
282
|
+
|
|
283
|
+
a. Desktop (1280px): browser_navigate → browser_take_screenshot
|
|
284
|
+
b. Tablet (768px): set viewport → navigate → screenshot
|
|
285
|
+
c. Mobile (375px): set viewport → navigate → screenshot
|
|
286
|
+
|
|
287
|
+
3. Analyze each screenshot against:
|
|
288
|
+
- Brandbook token compliance (colors, spacing, typography)
|
|
289
|
+
- Component state completeness (hover, focus, error, loading)
|
|
290
|
+
- No visual regressions (broken layout, overflow, misalignment)
|
|
291
|
+
- Responsive correctness at all 3 viewports
|
|
292
|
+
|
|
293
|
+
4. Classify findings:
|
|
294
|
+
- VISUAL-ERROR (blocks APPROVED): wrong brand colors, broken layout, missing states
|
|
295
|
+
- VISUAL-WARNING (noted, does not block): minor spacing, animation timing
|
|
296
|
+
|
|
297
|
+
5. If VISUAL-ERROR found: send correction to Dev agent (Silent Correction Loop).
|
|
298
|
+
|
|
299
|
+
6. Document in report:
|
|
300
|
+
## Visual Review
|
|
301
|
+
| Route | Desktop | Tablet | Mobile | Tokens | Responsive | Status |
|
|
302
|
+
|-------|---------|--------|--------|--------|------------|--------|
|
|
303
|
+
| /{route} | {screenshot} | {screenshot} | {screenshot} | {PASS/FAIL} | {PASS/FAIL} | {OK/ERROR/WARNING} |
|
|
304
|
+
|
|
305
|
+
FALLBACK: If browser MCP unavailable:
|
|
306
|
+
- Check Dev handoff for screenshot evidence (Step 9.7)
|
|
307
|
+
- If Dev provided screenshots: use those as visual evidence
|
|
308
|
+
- If no screenshots anywhere: flag VISUAL-WARNING "No visual evidence" and note in report
|
|
309
|
+
```
|
|
310
|
+
|
|
271
311
|
### Phase 6: Score & Decide
|
|
272
312
|
```
|
|
273
313
|
Calculate overall quality score:
|
|
274
|
-
Tests: weight 0.
|
|
314
|
+
Tests: weight 0.20
|
|
275
315
|
Coverage: weight 0.10
|
|
276
|
-
Security: weight 0.
|
|
316
|
+
Security: weight 0.20
|
|
277
317
|
Code Quality: weight 0.15
|
|
278
318
|
Acceptance Criteria: weight 0.10
|
|
279
319
|
Adversarial Review: weight 0.15
|
|
320
|
+
Visual Review: weight 0.10 (0.00 when no UI files — redistribute to Tests + Security)
|
|
280
321
|
|
|
281
322
|
Result:
|
|
282
323
|
- All checks pass AND adversarial review complete -> APPROVED -> proceed to DevOps
|
package/framework/config.yaml
CHANGED
|
@@ -489,5 +489,5 @@ When multiple CLI providers are enabled, the system SHALL coordinate agent execu
|
|
|
489
489
|
|
|
490
490
|
---
|
|
491
491
|
|
|
492
|
-
*Chati.dev Constitution v4.1.
|
|
492
|
+
*Chati.dev Constitution v4.1.6 — 19 Articles + Preamble*
|
|
493
493
|
*All agents are bound by this Constitution. Violations are enforced per article.*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Chati.dev System Context
|
|
2
2
|
|
|
3
3
|
## Framework
|
|
4
|
-
- **Version**: 4.1.
|
|
4
|
+
- **Version**: 4.1.6
|
|
5
5
|
- **Agents**: Specialized agents across DISCOVER, PLAN, BUILD, DEPLOY phases
|
|
6
6
|
- **Constitution**: 19 Articles + Preamble
|
|
7
7
|
- **Quality**: 5 pipeline gates + 3-tier verdicts (APPROVED / NEEDS_REVISION / BLOCKED)
|
|
@@ -112,9 +112,10 @@ async function main() {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
if (result.violations.length > 0) {
|
|
115
|
+
// Block all style violations — agent must rewrite
|
|
115
116
|
process.stdout.write(JSON.stringify({
|
|
116
|
-
decision: '
|
|
117
|
-
reason: `[Style Guard] ${result.violations.join(' ')}
|
|
117
|
+
decision: 'block',
|
|
118
|
+
reason: `[Style Guard] ${result.violations.join(' ')} Rewrite without em-dashes and emojis.`,
|
|
118
119
|
}));
|
|
119
120
|
return;
|
|
120
121
|
}
|
package/package.json
CHANGED
|
@@ -533,14 +533,14 @@ main();
|
|
|
533
533
|
|
|
534
534
|
/**
|
|
535
535
|
* Generate the style guard hook for Gemini CLI.
|
|
536
|
-
* BeforeTool event --
|
|
536
|
+
* BeforeTool event -- blocking: rejects em-dashes and emojis in generated content.
|
|
537
537
|
*/
|
|
538
538
|
function generateStyleGuard() {
|
|
539
539
|
return `${HOOK_HEADER}
|
|
540
540
|
/**
|
|
541
541
|
* Style Guard -- BeforeTool
|
|
542
|
-
*
|
|
543
|
-
*
|
|
542
|
+
* Blocking: rejects em-dashes and emojis in generated content.
|
|
543
|
+
* Constitution Article V enforcement.
|
|
544
544
|
*/
|
|
545
545
|
async function main() {
|
|
546
546
|
let input = '';
|
|
@@ -584,8 +584,8 @@ async function main() {
|
|
|
584
584
|
|
|
585
585
|
if (result.violations && result.violations.length > 0) {
|
|
586
586
|
console.log(JSON.stringify({
|
|
587
|
-
decision: '
|
|
588
|
-
reason: \`[Style Guard] \${result.violations.join(' ')}
|
|
587
|
+
decision: 'block',
|
|
588
|
+
reason: \`[Style Guard] \${result.violations.join(' ')} Rewrite without em-dashes and emojis.\`,
|
|
589
589
|
}));
|
|
590
590
|
return;
|
|
591
591
|
}
|
package/src/installer/core.js
CHANGED
|
@@ -148,7 +148,7 @@ export async function installFramework(config) {
|
|
|
148
148
|
/**
|
|
149
149
|
* Copy framework files from the Chati.dev source directory
|
|
150
150
|
*/
|
|
151
|
-
function copyFrameworkFiles(destDir, provider = 'claude') {
|
|
151
|
+
export function copyFrameworkFiles(destDir, provider = 'claude') {
|
|
152
152
|
if (!existsSync(FRAMEWORK_SOURCE)) return;
|
|
153
153
|
|
|
154
154
|
const filesToCopy = [
|
package/src/orchestrator/cli.js
CHANGED
|
@@ -914,8 +914,15 @@ async function handleScan(projectDir, args) {
|
|
|
914
914
|
const { scanProjectSecurity } = await import('../scanning/security-scanner.js');
|
|
915
915
|
return scanProjectSecurity(projectDir, { language: args.language, dir: args.dir });
|
|
916
916
|
}
|
|
917
|
+
case 'ui': {
|
|
918
|
+
const file = args.file;
|
|
919
|
+
if (!file) return errorResult('Missing --file flag for UI scan', 'MISSING_FILE');
|
|
920
|
+
const { scanUIQuality } = await import('../scanning/ui-scanner.js');
|
|
921
|
+
const { join: joinPath } = await import('path');
|
|
922
|
+
return scanUIQuality(joinPath(projectDir, file));
|
|
923
|
+
}
|
|
917
924
|
default:
|
|
918
|
-
return errorResult(`Unknown scan type: ${type}. Valid: placeholders, leakage, density, env, security`, 'UNKNOWN_SCAN_TYPE');
|
|
925
|
+
return errorResult(`Unknown scan type: ${type}. Valid: placeholders, leakage, density, env, security, ui`, 'UNKNOWN_SCAN_TYPE');
|
|
919
926
|
}
|
|
920
927
|
} catch (err) {
|
|
921
928
|
return errorResult(`Scan failed: ${err.message}`, 'SCAN_ERROR');
|
|
@@ -30,7 +30,7 @@ export function executeHandoff(projectDir, params) {
|
|
|
30
30
|
const errors = [];
|
|
31
31
|
|
|
32
32
|
// Validate preconditions
|
|
33
|
-
const preconditions = validateHandoffPreconditions(params);
|
|
33
|
+
const preconditions = validateHandoffPreconditions(params, projectDir);
|
|
34
34
|
if (!preconditions.valid) {
|
|
35
35
|
return {
|
|
36
36
|
success: false,
|
|
@@ -113,7 +113,7 @@ export function executeHandoff(projectDir, params) {
|
|
|
113
113
|
* @param {object} params
|
|
114
114
|
* @returns {{ valid: boolean, issues: string[] }}
|
|
115
115
|
*/
|
|
116
|
-
export function validateHandoffPreconditions(params) {
|
|
116
|
+
export function validateHandoffPreconditions(params, projectDir = '.') {
|
|
117
117
|
const issues = [];
|
|
118
118
|
|
|
119
119
|
// Check validation exists and passed
|
|
@@ -148,6 +148,19 @@ export function validateHandoffPreconditions(params) {
|
|
|
148
148
|
issues.push('Handoff summary is required');
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// Deterministic artifact checks for UX agent handoffs
|
|
152
|
+
const agent = params.fromTask?.agent || params.fromTask?.id?.split('-')[0] || '';
|
|
153
|
+
if (agent === 'ux') {
|
|
154
|
+
const brandbookPath = join(projectDir, 'chati.dev', 'artifacts', '4-UX', 'brandbook.html');
|
|
155
|
+
if (!existsSync(brandbookPath)) {
|
|
156
|
+
issues.push('brandbook.html is missing — UX handoff requires the visual brandbook file on disk');
|
|
157
|
+
}
|
|
158
|
+
const discoveryLogPath = join(projectDir, 'chati.dev', 'artifacts', '4-UX', 'component-discovery-log.md');
|
|
159
|
+
if (!existsSync(discoveryLogPath)) {
|
|
160
|
+
issues.push('component-discovery-log.md is missing — UX handoff requires component discovery documentation');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
151
164
|
return {
|
|
152
165
|
valid: issues.length === 0,
|
|
153
166
|
issues,
|
|
@@ -319,6 +332,20 @@ export function validateHandoffIntegrity(handoff, receivingAgent) {
|
|
|
319
332
|
}
|
|
320
333
|
}
|
|
321
334
|
|
|
335
|
+
// Check for screenshot evidence from Dev agent (UI tasks)
|
|
336
|
+
if (handoff.from_agent === 'dev' || handoff.agent === 'dev') {
|
|
337
|
+
const hasUIOutputs = handoff.outputs && handoff.outputs.some(o =>
|
|
338
|
+
/\.(tsx|jsx|vue|svelte|css)$/i.test(o)
|
|
339
|
+
);
|
|
340
|
+
if (hasUIOutputs) {
|
|
341
|
+
const hasVisualEvidence = handoff.summary &&
|
|
342
|
+
(handoff.summary.includes('Visual review PASSED') || handoff.summary.includes('screenshot'));
|
|
343
|
+
if (!hasVisualEvidence) {
|
|
344
|
+
warnings.push('Dev agent produced UI files but no visual review evidence found in handoff — manual visual review recommended');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
322
349
|
return {
|
|
323
350
|
valid: missing.length === 0,
|
|
324
351
|
missing,
|
package/src/scanning/index.js
CHANGED
|
@@ -5,3 +5,4 @@ export { scanImplementationLeakage, extractFRSection, TECH_NAMES } from './leaka
|
|
|
5
5
|
export { scanInformationDensity, FILLER_PATTERNS } from './density-scanner.js';
|
|
6
6
|
export { scanEnvSync, parseEnvFile, grepEnvUsage } from './env-scanner.js';
|
|
7
7
|
export { scanProjectSecurity, detectLanguage } from './security-scanner.js';
|
|
8
|
+
export { scanUIQuality, UI_QUALITY_CHECKS } from './ui-scanner.js';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Quality Scanner -- Detects hardcoded visual values in UI files.
|
|
3
|
+
* Deterministic regex scan. Zero LLM dependency.
|
|
4
|
+
*
|
|
5
|
+
* Catches common "vibecoded" patterns:
|
|
6
|
+
* - Hardcoded hex colors (should use design tokens)
|
|
7
|
+
* - Inline styles with px values (should use spacing tokens)
|
|
8
|
+
* - !important in CSS (code smell)
|
|
9
|
+
* - Images without alt attributes (accessibility)
|
|
10
|
+
* - Inline style= attributes (should use classes/tokens)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync } from 'fs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* UI quality patterns. Each has an id, pattern, severity, and description.
|
|
17
|
+
*/
|
|
18
|
+
export const UI_QUALITY_CHECKS = [
|
|
19
|
+
{
|
|
20
|
+
id: 'HARDCODED_HEX_COLOR',
|
|
21
|
+
pattern: /(?<![\\w-])(?:color|background|border|fill|stroke)\s*:\s*#[0-9a-fA-F]{3,8}\b/g,
|
|
22
|
+
severity: 'high',
|
|
23
|
+
description: 'Hardcoded hex color in CSS property — use design tokens instead',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'INLINE_STYLE',
|
|
27
|
+
pattern: /\bstyle\s*=\s*["'{]/g,
|
|
28
|
+
severity: 'medium',
|
|
29
|
+
description: 'Inline style attribute — use CSS classes or design tokens',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'CSS_IMPORTANT',
|
|
33
|
+
pattern: /!\s*important/gi,
|
|
34
|
+
severity: 'medium',
|
|
35
|
+
description: '!important in CSS — indicates specificity problem',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'IMG_MISSING_ALT',
|
|
39
|
+
pattern: /<img(?![^>]*\balt\s*=)[^>]*>/gi,
|
|
40
|
+
severity: 'high',
|
|
41
|
+
description: 'Image tag without alt attribute — accessibility violation',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'HARDCODED_PX_INLINE',
|
|
45
|
+
pattern: /style\s*=\s*["'][^"']*\d+px[^"']*["']/g,
|
|
46
|
+
severity: 'medium',
|
|
47
|
+
description: 'Hardcoded px values in inline style — use spacing tokens',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'HARDCODED_RGBA',
|
|
51
|
+
pattern: /(?<![\\w-])(?:color|background|border|fill|stroke)\s*:\s*rgba?\([^)]+\)/g,
|
|
52
|
+
severity: 'high',
|
|
53
|
+
description: 'Hardcoded rgba color — use design tokens instead',
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* File extensions this scanner targets.
|
|
59
|
+
*/
|
|
60
|
+
const UI_FILE_EXTENSIONS = ['.tsx', '.jsx', '.vue', '.svelte', '.css', '.scss'];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Paths to skip (config files, design token definitions, tailwind config).
|
|
64
|
+
*/
|
|
65
|
+
const SKIP_PATTERNS = [
|
|
66
|
+
/tailwind\.config/,
|
|
67
|
+
/globals\.css$/,
|
|
68
|
+
/tokens?\./,
|
|
69
|
+
/theme\./,
|
|
70
|
+
/\.config\./,
|
|
71
|
+
/node_modules/,
|
|
72
|
+
/brandbook/,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a file should be scanned.
|
|
77
|
+
* @param {string} filePath
|
|
78
|
+
* @returns {boolean}
|
|
79
|
+
*/
|
|
80
|
+
function shouldScan(filePath) {
|
|
81
|
+
const hasUIExtension = UI_FILE_EXTENSIONS.some(ext => filePath.endsWith(ext));
|
|
82
|
+
const isSkipped = SKIP_PATTERNS.some(p => p.test(filePath));
|
|
83
|
+
return hasUIExtension && !isSkipped;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Scan a file for UI quality violations.
|
|
88
|
+
* @param {string} filePath - Path to file to scan
|
|
89
|
+
* @returns {{ clean: boolean, findings: Array<{id: string, match: string, line: number, severity: string, description: string}>, count: number, skipped: boolean }}
|
|
90
|
+
*/
|
|
91
|
+
export function scanUIQuality(filePath) {
|
|
92
|
+
if (!shouldScan(filePath)) {
|
|
93
|
+
return { clean: true, findings: [], count: 0, skipped: true };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
97
|
+
const findings = [];
|
|
98
|
+
|
|
99
|
+
for (const check of UI_QUALITY_CHECKS) {
|
|
100
|
+
const re = new RegExp(check.pattern.source, check.pattern.flags);
|
|
101
|
+
let match;
|
|
102
|
+
while ((match = re.exec(content)) !== null) {
|
|
103
|
+
const line = content.slice(0, match.index).split('\n').length;
|
|
104
|
+
findings.push({
|
|
105
|
+
id: check.id,
|
|
106
|
+
match: match[0].slice(0, 80),
|
|
107
|
+
line,
|
|
108
|
+
severity: check.severity,
|
|
109
|
+
description: check.description,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
clean: findings.length === 0,
|
|
116
|
+
findings,
|
|
117
|
+
count: findings.length,
|
|
118
|
+
skipped: false,
|
|
119
|
+
};
|
|
120
|
+
}
|