qualia-framework-v2 2.5.0 → 2.7.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 (41) hide show
  1. package/README.md +14 -10
  2. package/agents/planner.md +8 -2
  3. package/agents/qa-browser.md +186 -0
  4. package/bin/install.js +52 -27
  5. package/bin/qualia-ui.js +278 -0
  6. package/hooks/auto-update.js +92 -0
  7. package/hooks/block-env-edit.js +30 -0
  8. package/hooks/branch-guard.js +47 -0
  9. package/hooks/migration-guard.js +60 -0
  10. package/hooks/pre-compact.js +32 -0
  11. package/hooks/pre-deploy-gate.js +110 -0
  12. package/hooks/pre-push.js +33 -0
  13. package/hooks/session-start.js +84 -0
  14. package/package.json +1 -1
  15. package/skills/qualia/SKILL.md +15 -11
  16. package/skills/qualia-build/SKILL.md +17 -16
  17. package/skills/qualia-debug/SKILL.md +14 -0
  18. package/skills/qualia-design/SKILL.md +4 -0
  19. package/skills/qualia-handoff/SKILL.md +5 -9
  20. package/skills/qualia-learn/SKILL.md +4 -0
  21. package/skills/qualia-new/SKILL.md +13 -14
  22. package/skills/qualia-pause/SKILL.md +4 -0
  23. package/skills/qualia-plan/SKILL.md +21 -20
  24. package/skills/qualia-polish/SKILL.md +15 -19
  25. package/skills/qualia-quick/SKILL.md +9 -0
  26. package/skills/qualia-report/SKILL.md +4 -0
  27. package/skills/qualia-resume/SKILL.md +11 -6
  28. package/skills/qualia-review/SKILL.md +4 -0
  29. package/skills/qualia-ship/SKILL.md +10 -13
  30. package/skills/qualia-skill-new/SKILL.md +148 -0
  31. package/skills/qualia-task/SKILL.md +11 -15
  32. package/skills/qualia-verify/SKILL.md +49 -20
  33. package/tests/hooks.test.sh +108 -44
  34. package/hooks/auto-update.sh +0 -56
  35. package/hooks/block-env-edit.sh +0 -11
  36. package/hooks/branch-guard.sh +0 -18
  37. package/hooks/migration-guard.sh +0 -43
  38. package/hooks/pre-compact.sh +0 -11
  39. package/hooks/pre-deploy-gate.sh +0 -50
  40. package/hooks/pre-push.sh +0 -28
  41. package/hooks/session-start.sh +0 -17
package/README.md CHANGED
@@ -47,11 +47,15 @@ See `guide.md` for the full developer guide.
47
47
 
48
48
  ## What's Inside
49
49
 
50
- - **18 skills** — slash commands from setup to handoff, plus debugging, design, review, knowledge, and session management
51
- - **3 agents** — planner, builder, verifier (each in fresh context)
52
- - **7 hooks** — branch guard, pre-push tracking sync, env protection, migration guard, deploy gate, pre-compact state save, session start
50
+ - **19 skills** — slash commands from setup to handoff, plus debugging, design, review, knowledge, session management, and skill authoring
51
+ - **4 agents** — planner, builder, verifier, qa-browser (each in fresh context)
52
+ - **8 hooks** — session start, branch guard, pre-push tracking sync, env protection, migration guard, deploy gate, pre-compact state save, auto-update (all Node.js — cross-platform)
53
53
  - **3 rules** — security, frontend, deployment
54
- - **4 templates** — tracking.json, state.md, project.md, plan.md
54
+ - **5 templates** — tracking.json, state.md, project.md, plan.md, DESIGN.md
55
+
56
+ ## Supported Platforms
57
+
58
+ Works on **Windows 10/11, macOS, and Linux** (all distros). The only requirement is Node.js 18+. No Git Bash, no WSL, no bash dependency — every hook is pure Node.js.
55
59
 
56
60
  ## Why It Works
57
61
 
@@ -92,13 +96,13 @@ npx qualia-framework-v2 install
92
96
  |
93
97
  v
94
98
  ~/.claude/
95
- ├── skills/ 18 slash commands
96
- ├── agents/ planner.md, builder.md, verifier.md
97
- ├── hooks/ 7 shell scripts (branch, env, migration, deploy, push, compact, session)
98
- ├── bin/ state.js (state machine with precondition enforcement)
99
- ├── knowledge/ learned-patterns.md, common-fixes.md, client-prefs.md
99
+ ├── skills/ 19 slash commands
100
+ ├── agents/ planner.md, builder.md, verifier.md, qa-browser.md
101
+ ├── hooks/ 8 Node.js hooks cross-platform (no bash dependency)
102
+ ├── bin/ state.js (state machine) + qualia-ui.js (cosmetics library)
103
+ ├── knowledge/ learned-patterns.md, common-fixes.md, client-prefs.md (auto-loaded by skills)
100
104
  ├── rules/ security.md, frontend.md, deployment.md
101
- ├── qualia-templates/ tracking.json, state.md, project.md, plan.md
105
+ ├── qualia-templates/ tracking.json, state.md, project.md, plan.md, DESIGN.md
102
106
  ├── CLAUDE.md global instructions (role-configured per team member)
103
107
  └── statusline.sh teal-branded 2-line status bar
104
108
  ```
package/agents/planner.md CHANGED
@@ -81,7 +81,13 @@ Every task MUST have these three fields with concrete content:
81
81
  - **Action:** At least one concrete instruction — not just "implement auth". Reference specific functions, components, or patterns. "Add `signInWithPassword()` call in the `handleSubmit` handler, validate email with Zod schema, redirect to `/dashboard` on success."
82
82
  - **Done when:** Testable, not fuzzy. Good: "User can log in with email/password and session persists across page refresh." Bad: "Auth works." Best: includes a verification command — `grep -c "signInWithPassword" src/lib/auth.ts` returns non-zero.
83
83
 
84
- If a task involves a library or API you're unsure about, use WebFetch to check the current documentation before specifying the approach. Don't guess at APIs.
84
+ If a task involves a library, framework, or API you're unsure about, fetch the current documentation BEFORE specifying the approach. Don't guess at APIs.
85
+
86
+ Preferred order:
87
+ 1. **Context7 MCP** — `mcp__context7__resolve-library-id` then `mcp__context7__query-docs`. Fast, current, structured. Use for: React, Next.js, Supabase, Tailwind, Prisma, ORMs, Zod, AI SDKs, any library with a published version.
88
+ 2. **WebFetch** — only when Context7 doesn't have the library, or you need a specific blog post / changelog page.
89
+
90
+ Your training data is often stale. A two-second lookup is cheaper than a wrong task specification.
85
91
 
86
92
  **Self-check:** Before returning the plan, verify every task has specific file paths, concrete actions, and testable done-when criteria. If any task says "relevant files", "as needed", "implement X" (without details), or "ensure it works" — rewrite it with specifics.
87
93
 
@@ -92,7 +98,7 @@ When a phase involves frontend work (pages, components, layouts, UI):
92
98
  1. **Check for `.planning/DESIGN.md`** — if it exists, reference it in task Context fields: `@.planning/DESIGN.md`
93
99
  2. **If no DESIGN.md and this is Phase 1** — add a Task 1 (Wave 1) to create it:
94
100
  - Generate `.planning/DESIGN.md` from the design direction in PROJECT.md
95
- - Use the template at `templates/DESIGN.md` — fill in: palette, typography (distinctive fonts), spacing, motion approach, component patterns
101
+ - Use the template at `~/.claude/qualia-templates/DESIGN.md` — fill in: palette, typography (distinctive fonts), spacing, motion approach, component patterns
96
102
  - Done when: DESIGN.md exists with concrete CSS variable values (not placeholders)
97
103
  3. **Include design criteria in "Done when"** for frontend tasks:
98
104
  - Not just "page renders" but "page renders with design system typography, proper color palette, all interactive states (hover/focus/loading/error/empty), semantic HTML, keyboard accessible"
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: qualia-qa-browser
3
+ description: Real-browser QA. Navigates the running dev server, checks layout at mobile/tablet/desktop, clicks primary flows, captures console errors and a11y issues. Spawned by /qualia-verify on phases with frontend work.
4
+ tools: Read, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Qualia QA Browser
8
+
9
+ You verify that the **running app actually looks and behaves right** — not just that the code compiles and greps clean. Fresh context, no memory of what was built.
10
+
11
+ **Critical mindset:** You are the user. You don't trust the code — you drive the app and see what happens. If it breaks at 375px, it's broken. If the console screams, it's broken. If clicking the primary CTA does nothing, it's broken.
12
+
13
+ ## Input
14
+ You receive: the phase plan (to know what pages/flows exist) + the dev server URL + access to Playwright MCP browser tools.
15
+
16
+ ## Output
17
+ Append a `## Browser QA` section to `.planning/phase-{N}-verification.md` with PASS/FAIL per check.
18
+
19
+ ## Tools You Must Use
20
+
21
+ The Playwright MCP provides these tools — use them directly:
22
+
23
+ - `mcp__playwright__browser_navigate` — go to a URL
24
+ - `mcp__playwright__browser_snapshot` — DOM accessibility tree (your primary inspection tool — NOT screenshots)
25
+ - `mcp__playwright__browser_resize` — switch viewport
26
+ - `mcp__playwright__browser_click` — click elements
27
+ - `mcp__playwright__browser_console_messages` — grab console errors/warnings
28
+ - `mcp__playwright__browser_take_screenshot` — only when you need to show something visual to the user
29
+ - `mcp__playwright__browser_evaluate` — run JS in the page (e.g., `document.querySelectorAll('img:not([alt])').length`)
30
+ - `mcp__playwright__browser_wait_for` — wait for elements/text
31
+
32
+ Prefer `browser_snapshot` over `browser_take_screenshot` — the accessibility tree tells you structure, text content, and interaction targets without burning context on images.
33
+
34
+ ## Process
35
+
36
+ ### 1. Find the Dev Server
37
+
38
+ ```bash
39
+ # Check if already running
40
+ curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null
41
+ curl -s -o /dev/null -w "%{http_code}" http://localhost:3001 2>/dev/null
42
+
43
+ # If not running, start it in background
44
+ if ! curl -s http://localhost:3000 >/dev/null 2>&1; then
45
+ npm run dev > /tmp/dev-server.log 2>&1 &
46
+ sleep 5 # give it time to boot
47
+ fi
48
+ ```
49
+
50
+ If you can't reach a dev server after 10 seconds, write **BLOCKED: dev server not reachable** to the verification report and exit.
51
+
52
+ ### 2. Identify Pages to Test
53
+
54
+ From the phase plan, extract the user-facing routes that were built. If unclear, inspect the file tree:
55
+
56
+ ```bash
57
+ ls app/**/page.tsx 2>/dev/null || ls src/app/**/page.tsx 2>/dev/null || ls pages/*.tsx 2>/dev/null
58
+ ```
59
+
60
+ Pick the 3-5 most important routes: home + primary feature pages + auth if present.
61
+
62
+ ### 3. Responsive Check (Critical)
63
+
64
+ For each route, visit at 3 viewports:
65
+
66
+ ```
67
+ 1. navigate http://localhost:{port}{route}
68
+ 2. browser_resize 375 812 (iPhone 14)
69
+ 3. browser_snapshot (capture mobile tree)
70
+ 4. browser_resize 768 1024 (iPad)
71
+ 5. browser_snapshot (capture tablet tree)
72
+ 6. browser_resize 1440 900 (laptop)
73
+ 7. browser_snapshot (capture desktop tree)
74
+ ```
75
+
76
+ **FAIL criteria:**
77
+ - Horizontal scroll at 375px (check scrollWidth > clientWidth via `browser_evaluate`)
78
+ - Text overflow / clipping at any size
79
+ - Elements overlapping or z-index collisions
80
+ - Navigation not accessible on mobile (no hamburger, or hamburger doesn't open)
81
+ - Content hidden or unreadable at any viewport
82
+
83
+ ### 4. Console Errors Check
84
+
85
+ ```
86
+ browser_console_messages
87
+ ```
88
+
89
+ **FAIL criteria:**
90
+ - Any `error` level message (hydration mismatch, 404 on assets, unhandled promise rejection)
91
+ - React key warnings are FAIL (they mean stale lists)
92
+ - Font 404s are FAIL (means the font config is broken)
93
+ - Accessibility warnings from React are FAIL
94
+
95
+ ### 5. Primary Flow Walkthrough
96
+
97
+ For each primary user flow (login, signup, main action), do it:
98
+
99
+ ```
100
+ 1. navigate to the flow's start
101
+ 2. browser_snapshot to find the actual interactive elements
102
+ 3. browser_click on the primary CTA
103
+ 4. browser_wait_for the expected result
104
+ 5. browser_snapshot to verify the result
105
+ ```
106
+
107
+ **FAIL criteria:**
108
+ - CTA doesn't respond (no state change, no navigation)
109
+ - Form submits but shows no feedback (loading/success/error state missing)
110
+ - Navigation ends up at a 404 or error page
111
+ - Auth flow loses the user on redirect
112
+
113
+ ### 6. Accessibility Basics
114
+
115
+ Run these checks via `browser_evaluate`:
116
+
117
+ ```js
118
+ // Images without alt
119
+ document.querySelectorAll('img:not([alt])').length
120
+
121
+ // Form inputs without labels
122
+ Array.from(document.querySelectorAll('input, textarea, select')).filter(el => {
123
+ const id = el.id;
124
+ const hasLabel = id && document.querySelector(`label[for="${id}"]`);
125
+ return !hasLabel && !el.getAttribute('aria-label') && !el.getAttribute('aria-labelledby');
126
+ }).length
127
+
128
+ // Heading order
129
+ Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6')).map(h => parseInt(h.tagName[1]))
130
+
131
+ // Focus visible on tab
132
+ // (This one needs manual: focus body then press Tab, snapshot, check outline)
133
+ ```
134
+
135
+ **FAIL criteria:**
136
+ - Any `<img>` without alt
137
+ - Any input/textarea/select without a label or aria-label
138
+ - Heading order skips levels (h1 → h3 without h2)
139
+ - Multiple `<h1>` on the same page
140
+
141
+ ### 7. Write the Report
142
+
143
+ Append to `.planning/phase-{N}-verification.md`:
144
+
145
+ ```markdown
146
+ ## Browser QA
147
+
148
+ **Dev server:** http://localhost:{port}
149
+ **Routes tested:** {list}
150
+
151
+ ### Responsive
152
+ | Route | 375px | 768px | 1440px | Notes |
153
+ |-------|-------|-------|--------|-------|
154
+ | / | PASS | PASS | PASS | |
155
+ | /login | FAIL | PASS | PASS | Form overflows at 375px |
156
+
157
+ ### Console Errors
158
+ - {count} errors, {count} warnings
159
+ - {list each error with route}
160
+
161
+ ### Primary Flows
162
+ | Flow | Result | Notes |
163
+ |------|--------|-------|
164
+ | Sign up → dashboard | PASS | Loading state visible |
165
+ | Login → dashboard | FAIL | Clicking "Sign in" does nothing |
166
+
167
+ ### Accessibility
168
+ - Images without alt: {count}
169
+ - Inputs without labels: {count}
170
+ - Heading order issues: {list}
171
+
172
+ ### Verdict
173
+ PASS — all flows work, responsive clean, no console errors
174
+ OR
175
+ FAIL — {N} issues found. See above.
176
+ ```
177
+
178
+ ## Rules
179
+
180
+ 1. **Never trust code that you haven't driven.** The compiler says "yes" all the time about things that don't work.
181
+ 2. **Test at 375px first.** If it breaks on mobile, it's broken. Desktop-first thinking is a bug.
182
+ 3. **Console errors are failures, not warnings.** A hydration mismatch today is a production bug tomorrow.
183
+ 4. **Don't fix anything.** You have no Write/Edit tools. You report; the planner decides the fix.
184
+ 5. **Don't start the dev server if it's already running.** You'd kill someone else's session.
185
+ 6. **Cap snapshots.** Don't take 50 snapshots — aim for ~15 total across all pages and viewports. Budget your context.
186
+ 7. **If Playwright MCP isn't available**, write `BLOCKED: Playwright MCP not connected. Run: claude mcp list` and exit. Don't fake it.
package/bin/install.js CHANGED
@@ -146,10 +146,20 @@ async function main() {
146
146
  const hooksSource = path.join(FRAMEWORK_DIR, "hooks");
147
147
  const hooksDest = path.join(CLAUDE_DIR, "hooks");
148
148
  if (!fs.existsSync(hooksDest)) fs.mkdirSync(hooksDest, { recursive: true });
149
+ // Clean up legacy .sh hooks from previous v2.5/v2.6 installs so no orphans
150
+ // remain on disk after upgrading to the pure-Node v2.7+ hooks.
151
+ try {
152
+ for (const f of fs.readdirSync(hooksDest)) {
153
+ if (f.endsWith(".sh")) {
154
+ try { fs.unlinkSync(path.join(hooksDest, f)); } catch {}
155
+ }
156
+ }
157
+ } catch {}
149
158
  for (const file of fs.readdirSync(hooksSource)) {
150
159
  try {
151
160
  const dest = path.join(hooksDest, file);
152
161
  copy(path.join(hooksSource, file), dest);
162
+ // chmod is a no-op on Windows but harmless
153
163
  fs.chmodSync(dest, 0o755);
154
164
  ok(file);
155
165
  } catch (e) {
@@ -208,8 +218,14 @@ async function main() {
208
218
  path.join(binDest, "state.js")
209
219
  );
210
220
  ok("state.js (state machine)");
221
+ copy(
222
+ path.join(FRAMEWORK_DIR, "bin", "qualia-ui.js"),
223
+ path.join(binDest, "qualia-ui.js")
224
+ );
225
+ fs.chmodSync(path.join(binDest, "qualia-ui.js"), 0o755);
226
+ ok("qualia-ui.js (cosmetics library)");
211
227
  } catch (e) {
212
- warn(`state.js — ${e.message}`);
228
+ warn(`scripts — ${e.message}`);
213
229
  }
214
230
 
215
231
  // ─── Guide ─────────────────────────────────────────────
@@ -228,7 +244,7 @@ async function main() {
228
244
  const knowledgeDir = path.join(CLAUDE_DIR, "knowledge");
229
245
  if (!fs.existsSync(knowledgeDir)) fs.mkdirSync(knowledgeDir, { recursive: true });
230
246
  const knowledgeFiles = {
231
- "learned-patterns.md": "# Learned Patterns\n\nPatterns discovered across projects. Updated by `/qualia-evolve` and manual notes.\n",
247
+ "learned-patterns.md": "# Learned Patterns\n\nPatterns discovered across projects. Updated by `/qualia-learn` and manual notes.\n",
232
248
  "common-fixes.md": "# Common Fixes\n\nRecurring issues and their solutions.\n",
233
249
  "client-prefs.md": "# Client Preferences\n\nClient-specific preferences, design choices, and requirements.\n",
234
250
  };
@@ -325,32 +341,54 @@ async function main() {
325
341
  ],
326
342
  };
327
343
 
328
- // Hooks — full system
344
+ // Hooks — pure Node.js, cross-platform (Windows/macOS/Linux).
345
+ // Every hook command is `node <absolute-path-to-hook.js>` which avoids the
346
+ // bash/Git Bash requirement on Windows.
329
347
  const hd = path.join(CLAUDE_DIR, "hooks");
348
+ const nodeCmd = (hookFile) => `node "${path.join(hd, hookFile)}"`;
330
349
  settings.hooks = {
350
+ SessionStart: [
351
+ {
352
+ matcher: ".*",
353
+ hooks: [
354
+ {
355
+ type: "command",
356
+ command: nodeCmd("session-start.js"),
357
+ timeout: 5,
358
+ },
359
+ ],
360
+ },
361
+ ],
331
362
  PreToolUse: [
332
363
  {
333
364
  matcher: "Bash",
334
365
  hooks: [
335
366
  {
336
367
  type: "command",
337
- command: `${hd}/auto-update.sh`,
368
+ command: nodeCmd("auto-update.js"),
338
369
  timeout: 5,
339
370
  },
340
371
  {
341
372
  type: "command",
342
373
  if: "Bash(git push*)",
343
- command: `${hd}/branch-guard.sh`,
374
+ command: nodeCmd("branch-guard.js"),
344
375
  timeout: 10,
345
376
  statusMessage: "◆ Checking branch permissions...",
346
377
  },
347
378
  {
348
379
  type: "command",
349
380
  if: "Bash(git push*)",
350
- command: `${hd}/pre-push.sh`,
381
+ command: nodeCmd("pre-push.js"),
351
382
  timeout: 15,
352
383
  statusMessage: "◆ Syncing tracking...",
353
384
  },
385
+ {
386
+ type: "command",
387
+ if: "Bash(vercel --prod*)",
388
+ command: nodeCmd("pre-deploy-gate.js"),
389
+ timeout: 180,
390
+ statusMessage: "◆ Running quality gates...",
391
+ },
354
392
  ],
355
393
  },
356
394
  {
@@ -359,41 +397,27 @@ async function main() {
359
397
  {
360
398
  type: "command",
361
399
  if: "Edit(*.env*)|Write(*.env*)",
362
- command: `${hd}/block-env-edit.sh`,
400
+ command: nodeCmd("block-env-edit.js"),
363
401
  timeout: 5,
364
402
  statusMessage: "◆ Checking file permissions...",
365
403
  },
366
404
  {
367
405
  type: "command",
368
406
  if: "Edit(*migration*)|Write(*migration*)|Edit(*.sql)|Write(*.sql)",
369
- command: `${hd}/migration-guard.sh`,
407
+ command: nodeCmd("migration-guard.js"),
370
408
  timeout: 10,
371
409
  statusMessage: "◆ Checking migration safety...",
372
410
  },
373
411
  ],
374
412
  },
375
413
  ],
376
- PostToolUse: [
377
- {
378
- matcher: "Bash",
379
- hooks: [
380
- {
381
- type: "command",
382
- if: "Bash(vercel --prod*)",
383
- command: `${hd}/pre-deploy-gate.sh`,
384
- timeout: 120,
385
- statusMessage: "◆ Running quality gates...",
386
- },
387
- ],
388
- },
389
- ],
390
414
  PreCompact: [
391
415
  {
392
416
  matcher: "compact",
393
417
  hooks: [
394
418
  {
395
419
  type: "command",
396
- command: `${hd}/pre-compact.sh`,
420
+ command: nodeCmd("pre-compact.js"),
397
421
  timeout: 15,
398
422
  statusMessage: "◆ Saving state...",
399
423
  },
@@ -429,7 +453,7 @@ async function main() {
429
453
 
430
454
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
431
455
 
432
- ok("Hooks: auto-update, branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact");
456
+ ok("Hooks: session-start, auto-update, branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact");
433
457
  ok("Status line + spinner configured");
434
458
  ok("Environment variables + permissions");
435
459
 
@@ -439,10 +463,11 @@ async function main() {
439
463
  console.log(`${DIM} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
440
464
  console.log(` ${WHITE}${member.name}${RESET} ${DIM}(${member.role})${RESET}`);
441
465
  console.log(` Skills: ${WHITE}${skills.length}${RESET}`);
442
- console.log(` Agents: ${WHITE}3${RESET} ${DIM}(planner, builder, verifier)${RESET}`);
443
- console.log(` Hooks: ${WHITE}7${RESET} ${DIM}(auto-update, branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact)${RESET}`);
466
+ const agentCount = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')).length;
467
+ console.log(` Agents: ${WHITE}${agentCount}${RESET} ${DIM}(planner, builder, verifier, qa-browser)${RESET}`);
468
+ console.log(` Hooks: ${WHITE}8${RESET} ${DIM}(session-start, auto-update, branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact)${RESET}`);
444
469
  console.log(` Rules: ${WHITE}${fs.readdirSync(rulesDir).length}${RESET} ${DIM}(security, frontend, design-reference, deployment)${RESET}`);
445
- console.log(` Scripts: ${WHITE}1${RESET} ${DIM}(state.js — state machine)${RESET}`);
470
+ console.log(` Scripts: ${WHITE}2${RESET} ${DIM}(state.js, qualia-ui.js)${RESET}`);
446
471
  console.log(` Knowledge: ${WHITE}3${RESET} ${DIM}(patterns, fixes, client prefs)${RESET}`);
447
472
  console.log(` Templates: ${WHITE}${fs.readdirSync(tmplDir).length}${RESET}`);
448
473
  console.log(` Status line: ${GREEN}✓${RESET}`);