qualia-framework 5.9.1 → 6.2.7

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 (81) hide show
  1. package/AGENTS.md +2 -1
  2. package/CLAUDE.md +2 -1
  3. package/README.md +45 -29
  4. package/agents/builder.md +1 -5
  5. package/agents/plan-checker.md +1 -1
  6. package/agents/planner.md +2 -6
  7. package/agents/qa-browser.md +3 -3
  8. package/agents/roadmapper.md +2 -2
  9. package/agents/verifier.md +7 -9
  10. package/agents/visual-evaluator.md +1 -3
  11. package/bin/cli.js +370 -205
  12. package/bin/erp-retry.js +11 -3
  13. package/bin/install.js +383 -55
  14. package/bin/knowledge-flush.js +25 -13
  15. package/bin/knowledge.js +11 -1
  16. package/bin/project-snapshot.js +293 -0
  17. package/bin/qualia-ui.js +13 -2
  18. package/bin/report-payload.js +137 -0
  19. package/bin/slop-detect.mjs +81 -9
  20. package/bin/state.js +8 -1
  21. package/bin/statusline.js +14 -2
  22. package/docs/archive/CHANGELOG-pre-v4.md +855 -0
  23. package/docs/changelog-v6.html +864 -0
  24. package/docs/ecosystem-operating-model.md +121 -0
  25. package/docs/erp-contract.md +74 -21
  26. package/docs/onboarding.html +2 -2
  27. package/docs/release.md +44 -0
  28. package/docs/reviews/v6.2.1-revival-audit.md +53 -0
  29. package/docs/reviews/v6.2.2-memory-erp-audit.md +41 -0
  30. package/docs/reviews/v6.2.3-erp-id-guard.md +15 -0
  31. package/guide.md +28 -3
  32. package/hooks/auto-update.js +20 -10
  33. package/hooks/branch-guard.js +10 -2
  34. package/hooks/env-empty-guard.js +15 -5
  35. package/hooks/git-guardrails.js +10 -1
  36. package/hooks/migration-guard.js +4 -1
  37. package/hooks/pre-deploy-gate.js +11 -1
  38. package/hooks/pre-push.js +43 -106
  39. package/hooks/session-start.js +22 -14
  40. package/hooks/stop-session-log.js +11 -3
  41. package/hooks/supabase-destructive-guard.js +11 -1
  42. package/hooks/vercel-account-guard.js +12 -3
  43. package/package.json +4 -3
  44. package/qualia-design/design-reference.md +2 -1
  45. package/qualia-design/frontend.md +4 -4
  46. package/rules/one-opinion.md +59 -0
  47. package/rules/trust-boundary.md +35 -0
  48. package/skills/qualia-feature/SKILL.md +5 -5
  49. package/skills/qualia-flush/SKILL.md +5 -7
  50. package/skills/qualia-hook-gen/SKILL.md +1 -1
  51. package/skills/qualia-learn/SKILL.md +1 -0
  52. package/skills/qualia-map/SKILL.md +2 -1
  53. package/skills/qualia-milestone/SKILL.md +2 -2
  54. package/skills/qualia-new/SKILL.md +6 -6
  55. package/skills/qualia-optimize/SKILL.md +1 -1
  56. package/skills/qualia-plan/SKILL.md +1 -1
  57. package/skills/qualia-polish/REFERENCE.md +8 -6
  58. package/skills/qualia-polish/SKILL.md +11 -9
  59. package/skills/qualia-polish/scripts/loop.mjs +18 -6
  60. package/skills/qualia-postmortem/SKILL.md +1 -1
  61. package/skills/qualia-report/SKILL.md +6 -42
  62. package/skills/qualia-road/SKILL.md +17 -5
  63. package/skills/qualia-verify/SKILL.md +3 -3
  64. package/skills/qualia-vibe/SKILL.md +226 -0
  65. package/skills/qualia-vibe/scripts/extract.mjs +141 -0
  66. package/skills/qualia-vibe/scripts/tokens.mjs +342 -0
  67. package/templates/help.html +10 -3
  68. package/templates/knowledge/agents.md +3 -3
  69. package/templates/knowledge/index.md +1 -1
  70. package/templates/tracking.json +3 -0
  71. package/templates/work-packet.md +46 -0
  72. package/tests/bin.test.sh +423 -25
  73. package/tests/hooks.test.sh +1 -8
  74. package/tests/install-smoke.test.sh +137 -0
  75. package/tests/published-install-smoke.test.sh +126 -0
  76. package/tests/refs.test.sh +43 -1
  77. package/tests/run-all.sh +49 -0
  78. package/tests/runner.js +19 -33
  79. package/tests/slop-detect.test.sh +11 -5
  80. package/tests/state.test.sh +4 -1
  81. package/hooks/pre-compact.js +0 -125
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: qualia-vibe
3
+ description: "Fast aesthetic pivot — swap the design vibe (tokens: color, typography, motion, depth) without touching component structure or layout. Default mode proposes ONE direction (per rules/one-opinion.md). Sub-modes: --variants for A/B/C menu (only when user explicitly asked), --extract URL to reverse-engineer DESIGN.md from a reference site, --sync to back-sync code → DESIGN.md when tokens drifted in code. Triggers: 'different vibe', 'change the look', 'swap the aesthetic', 'try something bolder', 'redesign the look', 'match this site', 'sync the design file'."
4
+ disable-model-invocation: false
5
+ allowed-tools:
6
+ - Bash
7
+ - Read
8
+ - Write
9
+ - Edit
10
+ - Glob
11
+ - Grep
12
+ - AskUserQuestion
13
+ ---
14
+
15
+ # /qualia-vibe — Fast Aesthetic Pivot
16
+
17
+ Swap the **vibe** without redoing the **app**. Tokens only: color, typography, depth, motion, brand accents. Component structure, routing, data flow, and layout grid stay exactly where they were. This is the difference between `/qualia-vibe` (~3 min) and `/qualia-polish --redesign` (~30 min).
18
+
19
+ ## When to use
20
+
21
+ - Client said "I want a different vibe" / "make it look bolder" / "this feels too startup-y" → `/qualia-vibe`
22
+ - You want to test if a different aesthetic is right BEFORE committing the redesign → `/qualia-vibe`
23
+ - You want to match a reference site's design system → `/qualia-vibe --extract https://example.com`
24
+ - Someone changed CSS vars or Tailwind config directly and DESIGN.md is out of sync → `/qualia-vibe --sync`
25
+ - You want to enumerate options (rare — see `rules/one-opinion.md`) → `/qualia-vibe --variants 3`
26
+
27
+ ## When NOT to use
28
+
29
+ - The complaint is "this component is broken" or "the spacing is wrong" → `/qualia-polish` (component or section scope).
30
+ - The site has no DESIGN.md yet → run `/qualia-new` first; vibe is a PIVOT, not a cold start.
31
+ - You want a structural redesign (layout, navigation, information architecture) → `/qualia-polish --redesign` (~30 min ground-up).
32
+
33
+ ## Layout-preservation contract
34
+
35
+ `/qualia-vibe` ONLY changes:
36
+
37
+ - Color tokens (CSS vars / Tailwind palette / brand accents).
38
+ - Typography (font-family, scale, weights, italic accents).
39
+ - Depth tokens (shadow elevations, border treatments).
40
+ - Motion tokens (easing, duration, signature interactions).
41
+ - The "Aesthetic direction" line in DESIGN.md §1.
42
+
43
+ It does NOT touch:
44
+
45
+ - Component structure (`return (…)` JSX is preserved).
46
+ - Routing, data fetching, business logic.
47
+ - Layout grid (responsive breakpoints, container widths).
48
+ - Information architecture or copy.
49
+
50
+ If the user actually wants structural change, route to `/qualia-polish --redesign`.
51
+
52
+ ## Modes
53
+
54
+ ### Default — propose one pivot, apply it
55
+
56
+ Per `rules/one-opinion.md`. Read PRODUCT.md register + anti-references + scene sentence. Propose ONE concrete direction with one-paragraph justification. Ask one yes/no: "Ship this, or pivot?" If user pivots, ask what they didn't like, propose ONE alternative. Never widen.
57
+
58
+ ### `--variants N`
59
+
60
+ The opt-in menu. Generate N concrete direction briefs (each 4 lines: direction / color / typography / motion). Use `AskUserQuestion` to let the user pick. Default N=3, max 5.
61
+
62
+ ### `--extract <URL or image path>`
63
+
64
+ Reverse-engineer a DESIGN.md draft from a reference. Captures a screenshot at 1440 (via `scripts/playwright-capture.mjs`), runs an extract-mode vision prompt (NOT score-mode), outputs structured tokens matching DESIGN.md sections 1-7. User reviews + edits the draft before applying.
65
+
66
+ ### `--sync`
67
+
68
+ Back-sync. Reads the codebase's actual CSS vars, Tailwind config, and font imports. Diffs against DESIGN.md. Outputs (a) tokens in code not in DESIGN.md ("undocumented"), (b) tokens in DESIGN.md not in code ("orphaned"), (c) tokens that drifted in value. With `--sync --write`, patches DESIGN.md to match code.
69
+
70
+ ## Process
71
+
72
+ ### 0. Pre-flight gates
73
+
74
+ ```bash
75
+ node ~/.claude/bin/qualia-ui.js banner vibe 2>/dev/null
76
+
77
+ # DESIGN.md must exist
78
+ test -f .planning/DESIGN.md || {
79
+ echo "DESIGN.md not found. Run /qualia-new (or /qualia-polish --redesign for a ground-up redesign) first."
80
+ exit 1
81
+ }
82
+
83
+ # PRODUCT.md should exist (for register + anti-references context)
84
+ test -f .planning/PRODUCT.md || echo "Warning: PRODUCT.md missing — vibe proposals will be less anchored. Consider /qualia-new first."
85
+ ```
86
+
87
+ ### 1. Parse the mode
88
+
89
+ - `--extract <url-or-path>` → go to Extract flow.
90
+ - `--sync` (with optional `--write`) → go to Sync flow.
91
+ - `--variants [N]` → go to Variants flow.
92
+ - Direction explicitly named on command line (e.g. `/qualia-vibe brutalist`) → skip proposal, jump to Apply.
93
+ - Otherwise → Default (propose one).
94
+
95
+ ### 2. Default flow (propose one)
96
+
97
+ Read substrate:
98
+ - `.planning/PRODUCT.md` (register, anti-references, scene sentence)
99
+ - `.planning/DESIGN.md` (current direction, current token set)
100
+ - `qualia-design/design-laws.md` (banned patterns)
101
+ - `qualia-design/design-brand.md` OR `qualia-design/design-product.md` (matching register)
102
+
103
+ Propose ONE direction. Output format:
104
+
105
+ ```
106
+ Current: {DESIGN.md §1 direction line}
107
+ Proposed: {ONE new direction, concrete — e.g. "editorial broadsheet, ivory ground, deep navy ink, italic Cormorant accents"}
108
+ Why: {1-2 sentences citing PRODUCT.md register, anti-refs, scene sentence}
109
+ Token deltas: {3-7 bullet token changes the pivot implies}
110
+ Cost: ~3 min to apply, layout preserved.
111
+ ```
112
+
113
+ Use `AskUserQuestion` with two options: `Ship this pivot` / `Different direction`.
114
+
115
+ If "Different direction": ask one follow-up — "What about the proposal didn't fit?" — then propose ONE new direction with the rejection incorporated. Never enumerate alternatives.
116
+
117
+ ### 3. Apply
118
+
119
+ Once user approves a direction:
120
+
121
+ 1. Update DESIGN.md §1 (Aesthetic direction).
122
+ 2. Update DESIGN.md §2 (Color — OKLCH token set).
123
+ 3. Update DESIGN.md §3 (Typography — font-family, scale).
124
+ 4. Update DESIGN.md §6 (Depth — shadow elevations).
125
+ 5. Update DESIGN.md §7 (Motion — easing, duration).
126
+ 6. Apply the same token changes to code:
127
+ - CSS vars in `app/globals.css` or `src/styles/globals.css` (whichever exists)
128
+ - `tailwind.config.{ts,js,mjs}` colors / fonts / spacing extensions
129
+ - Font imports (`@import url(...)` in CSS, or `<link>` in `app/layout.tsx`, or `next/font/google` calls)
130
+ 7. Run `node ~/.claude/bin/slop-detect.mjs` on changed files. Block on critical findings (no banned fonts in the new vibe).
131
+ 8. Capture one screenshot at desktop (1440) for visual diff:
132
+ ```bash
133
+ node ~/.claude/skills/qualia-polish/scripts/playwright-capture.mjs \
134
+ --url ${URL:-http://localhost:3000} \
135
+ --out .planning/vibe-after.png \
136
+ --width 1440
137
+ ```
138
+ 9. Commit:
139
+ ```bash
140
+ git -c user.name="Qualia Vibe" -c user.email="vibe@qualia.solutions" \
141
+ commit -m "vibe(pivot): {old direction} → {new direction}"
142
+ ```
143
+
144
+ If the commit fails (no dev server, no slop-detect, network error), surface the exact failure — do NOT silently swallow.
145
+
146
+ ### 4. Variants flow
147
+
148
+ ```bash
149
+ node ~/.claude/skills/qualia-vibe/scripts/tokens.mjs propose-variants \
150
+ --product .planning/PRODUCT.md \
151
+ --design .planning/DESIGN.md \
152
+ --count ${N:-3}
153
+ ```
154
+
155
+ Output N briefs side-by-side. `AskUserQuestion` with N options (one per variant) plus "None of these — propose something else". User picks one → continue to Apply (step 3).
156
+
157
+ ### 5. Extract flow
158
+
159
+ ```bash
160
+ node ~/.claude/skills/qualia-vibe/scripts/extract.mjs \
161
+ --source ${URL_OR_IMAGE} \
162
+ --out .planning/DESIGN-extracted.md
163
+ ```
164
+
165
+ The script:
166
+ 1. If source is a URL, capture screenshot via `playwright-capture.mjs`.
167
+ 2. Sends screenshot + extract-mode prompt to visual-evaluator agent.
168
+ 3. Visual evaluator returns a JSON token bundle (color OKLCH, font families, scale, depth, motion).
169
+ 4. Script renders the bundle into a DESIGN.md draft.
170
+
171
+ Present the draft. Diff against current DESIGN.md. User can: `Apply as new vibe`, `Save draft only`, or `Cancel`. If apply → Apply flow (step 3) using the extracted tokens.
172
+
173
+ ### 6. Sync flow
174
+
175
+ ```bash
176
+ node ~/.claude/skills/qualia-vibe/scripts/tokens.mjs sync \
177
+ --design .planning/DESIGN.md \
178
+ ${WRITE:+--write}
179
+ ```
180
+
181
+ The script:
182
+ 1. Greps CSS for `:root { --token: value; }` declarations.
183
+ 2. Reads `tailwind.config.*` for colors / fonts / spacing extensions.
184
+ 3. Parses font imports.
185
+ 4. Diffs against DESIGN.md token sections.
186
+ 5. Prints three sections: `Undocumented (in code, not in DESIGN.md)`, `Orphaned (in DESIGN.md, not in code)`, `Drifted (different values)`.
187
+ 6. If `--write`, patches DESIGN.md to match code; commits with `vibe(sync): align DESIGN.md to code`.
188
+
189
+ ## Output contracts
190
+
191
+ Vibe always writes ONE of these as its final line:
192
+
193
+ - `DONE — vibe pivot: {old direction} → {new direction} ({sha})`
194
+ - `DONE — vibe extract: draft saved to {path}`
195
+ - `DONE — vibe sync: {N} drift findings ({sha if --write})`
196
+ - `CANCELLED — user declined all proposed pivots`
197
+ - `BLOCKED — {reason}` (DESIGN.md missing, slop-detect critical, etc.)
198
+
199
+ ## Rules
200
+
201
+ 1. **One opinion by default.** Per `rules/one-opinion.md`. Menus require explicit `--variants`.
202
+ 2. **Layout never changes.** If the proposal would require touching JSX structure, route to `/qualia-polish --redesign` and stop.
203
+ 3. **DESIGN.md and code stay in sync.** Every apply writes BOTH. Sync mode exists to recover from drift, not to normalize it.
204
+ 4. **slop-detect is the brake.** A pivot that introduces a banned font / gradient / hex-in-jsx is rejected. The new vibe must pass the same gates as the old one.
205
+ 5. **One screenshot, not three.** Vibe is fast by design. The full 3-viewport check belongs in `/qualia-polish --loop` after the vibe lands.
206
+ 6. **Commit identity is local.** Vibe sets git user inline so it works on fresh clones without global config.
207
+
208
+ ## Anti-patterns
209
+
210
+ - ❌ Presenting "Here are 5 directions: pick one" when the user said "change the vibe". → Use `rules/one-opinion.md`: propose ONE.
211
+ - ❌ Touching `return (…)` JSX. → If the pivot needs structural change, stop and route to `--redesign`.
212
+ - ❌ Skipping slop-detect because "the user is in a hurry". → The vibe pivot is exactly when banned fonts/gradients sneak in.
213
+ - ❌ Auto-running `--sync --write` without surfacing the diff first. → Always show drift before patching DESIGN.md.
214
+ - ❌ Treating `--extract` output as ground truth. → It's a DRAFT. User must approve before apply.
215
+
216
+ ## Examples
217
+
218
+ ```
219
+ /qualia-vibe # propose one pivot, apply on approval
220
+ /qualia-vibe brutalist # explicit pivot to named direction
221
+ /qualia-vibe --variants 3 # generate 3 options (use sparingly)
222
+ /qualia-vibe --extract https://stripe.com
223
+ /qualia-vibe --extract ./refs/inspo.png
224
+ /qualia-vibe --sync # show drift between code and DESIGN.md
225
+ /qualia-vibe --sync --write # patch DESIGN.md to match code, commit
226
+ ```
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * extract.mjs — reverse-engineer a DESIGN.md draft from a URL or screenshot.
4
+ *
5
+ * Pipeline:
6
+ * 1. If source is a URL → capture screenshot at 1440 via playwright-capture.mjs.
7
+ * If source is a local image path → use it directly.
8
+ * 2. Emit a JSON scaffold the LLM uses to generate the extracted token bundle.
9
+ * 3. The skill (qualia-vibe) reads the scaffold, runs the vision evaluator in
10
+ * extract mode, gets the bundle back, renders it as a DESIGN.md draft.
11
+ *
12
+ * This script does NOT call any LLM directly — it stages the inputs and emits
13
+ * a deterministic JSON contract. The /qualia-vibe skill orchestrates the LLM
14
+ * call.
15
+ *
16
+ * Usage:
17
+ * extract.mjs --source <URL or image path> [--out <path>]
18
+ *
19
+ * Exit codes:
20
+ * 0 success — JSON scaffold emitted to stdout, screenshot path included
21
+ * 1 capture failed (network, no playwright, bad URL)
22
+ * 2 invocation error
23
+ */
24
+
25
+ import { existsSync, mkdirSync, statSync } from "node:fs";
26
+ import { spawnSync } from "node:child_process";
27
+ import { argv, exit, env } from "node:process";
28
+ import { dirname, join, resolve } from "node:path";
29
+ import { tmpdir } from "node:os";
30
+
31
+ function flag(name, fallback) {
32
+ const i = argv.indexOf(name);
33
+ if (i < 0) return fallback;
34
+ return argv[i + 1] || fallback;
35
+ }
36
+
37
+ const source = flag("--source");
38
+ const outDraft = flag("--out", ".planning/DESIGN-extracted.md");
39
+
40
+ if (!source) {
41
+ console.error("--source required (URL or local image path)");
42
+ exit(2);
43
+ }
44
+
45
+ // ─── Stage 1: locate or capture screenshot ────────────────────────────
46
+
47
+ let screenshotPath;
48
+
49
+ const isUrl = /^https?:\/\//i.test(source);
50
+ if (isUrl) {
51
+ const stamp = Date.now().toString(36);
52
+ const outDir = join(tmpdir(), `qualia-vibe-extract-${stamp}`);
53
+ mkdirSync(outDir, { recursive: true });
54
+ screenshotPath = join(outDir, "ref-1440.png");
55
+
56
+ // Resolve playwright-capture.mjs — search the same order as loop.mjs.
57
+ const candidates = [
58
+ env.QUALIA_CAPTURE_SCRIPT,
59
+ `${env.HOME}/.claude/skills/qualia-polish/scripts/playwright-capture.mjs`,
60
+ resolve(dirname(new URL(import.meta.url).pathname), "..", "..", "qualia-polish", "scripts", "playwright-capture.mjs"),
61
+ ].filter(Boolean);
62
+ const captureScript = candidates.find((p) => existsSync(p));
63
+ if (!captureScript) {
64
+ console.error("playwright-capture.mjs not found. Install the framework or set QUALIA_CAPTURE_SCRIPT.");
65
+ exit(1);
66
+ }
67
+
68
+ const r = spawnSync("node", [
69
+ captureScript,
70
+ "--url", source,
71
+ "--out", screenshotPath,
72
+ "--width", "1440",
73
+ ], { encoding: "utf8" });
74
+
75
+ if (r.status !== 0 || !existsSync(screenshotPath)) {
76
+ console.error(`screenshot capture failed (exit=${r.status})`);
77
+ if (r.stderr) console.error(r.stderr);
78
+ exit(1);
79
+ }
80
+ } else {
81
+ screenshotPath = resolve(source);
82
+ if (!existsSync(screenshotPath)) {
83
+ console.error(`image not found: ${screenshotPath}`);
84
+ exit(2);
85
+ }
86
+ try { statSync(screenshotPath).isFile(); } catch {
87
+ console.error(`source must be a file: ${screenshotPath}`);
88
+ exit(2);
89
+ }
90
+ }
91
+
92
+ // ─── Stage 2: emit extraction scaffold ────────────────────────────────
93
+
94
+ const scaffold = {
95
+ mode: "extract",
96
+ source: source,
97
+ screenshot: screenshotPath,
98
+ output_draft: outDraft,
99
+ instruction:
100
+ "Examine the screenshot and EXTRACT the visible design tokens. Do NOT score, judge, or critique — describe what is actually present. Output the bundle matching the schema below. If a value is not visible (e.g. motion in a static screenshot), set it to null.",
101
+ schema: {
102
+ aesthetic_direction: "1 sentence — name the aesthetic in concrete terms",
103
+ color: {
104
+ strategy: "Restrained | Committed | Full palette | Drenched",
105
+ ground: "OKLCH or hex of the dominant background",
106
+ ink: "OKLCH or hex of the dominant text color",
107
+ accent: "OKLCH or hex of the single brand accent (if present)",
108
+ notes: "1 sentence on color logic visible (e.g. 'single saturated accent, otherwise neutrals')",
109
+ },
110
+ typography: {
111
+ primary_family: "best guess at the headline font family",
112
+ secondary_family: "best guess at body / sans counterpart, or null",
113
+ scale_observation: "tight | airy | dramatic | flat — one word",
114
+ weight_range: "e.g. '400/600' or 'mono single weight'",
115
+ italic_usage: "common | rare | never",
116
+ },
117
+ spatial: {
118
+ grid_observation: "8px | 12-col | bespoke | dense | airy — one phrase",
119
+ max_width_observation: "narrow | medium | wide | full",
120
+ },
121
+ depth: {
122
+ shadow_intensity: "none | subtle | bold",
123
+ borders: "hairline | medium | heavy | none",
124
+ },
125
+ motion: {
126
+ visible: "true | false | unknown (static)",
127
+ character: "snap | glide | spring | none",
128
+ },
129
+ register_guess: "Brand | Product | hybrid",
130
+ confidence: "low | medium | high",
131
+ },
132
+ rules: [
133
+ "Banned fonts: Inter, Roboto, Arial, Helvetica, system-ui, Space Grotesk, Montserrat, Poppins, Lato, Open Sans. If you see one of these, name it AND flag it so the user can decide whether to ban or accept.",
134
+ "Banned patterns: purple-blue gradient, gradient text, bounce/elastic easing. Flag the same way.",
135
+ "Confidence < high → user must review before /qualia-vibe applies.",
136
+ ],
137
+ next_step: `After producing the bundle, write a DESIGN.md draft to ${outDraft} using the bundle to populate sections 1 (Direction), 2 (Color), 3 (Typography), 4 (Spacing), 6 (Depth), 7 (Motion). Leave sections that depend on PRODUCT.md or anti-references empty — the user will fill them.`,
138
+ };
139
+
140
+ console.log(JSON.stringify(scaffold, null, 2));
141
+ exit(0);