qualia-framework 3.6.0 → 4.0.3

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 (56) hide show
  1. package/CLAUDE.md +23 -11
  2. package/README.md +96 -51
  3. package/agents/builder.md +25 -14
  4. package/agents/plan-checker.md +29 -16
  5. package/agents/planner.md +33 -24
  6. package/agents/research-synthesizer.md +25 -12
  7. package/agents/roadmapper.md +89 -84
  8. package/agents/verifier.md +11 -2
  9. package/bin/cli.js +18 -13
  10. package/bin/install.js +34 -45
  11. package/bin/qualia-ui.js +267 -1
  12. package/bin/state.js +164 -12
  13. package/bin/statusline.js +4 -1
  14. package/docs/erp-contract.md +12 -0
  15. package/guide.md +85 -22
  16. package/hooks/migration-guard.js +23 -9
  17. package/hooks/pre-compact.js +39 -11
  18. package/hooks/pre-deploy-gate.js +3 -4
  19. package/hooks/pre-push.js +6 -3
  20. package/hooks/session-start.js +8 -8
  21. package/package.json +1 -1
  22. package/rules/frontend.md +5 -13
  23. package/skills/qualia/SKILL.md +8 -1
  24. package/skills/qualia-build/SKILL.md +49 -4
  25. package/skills/qualia-debug/SKILL.md +6 -0
  26. package/skills/qualia-design/SKILL.md +9 -1
  27. package/skills/qualia-discuss/SKILL.md +6 -0
  28. package/skills/qualia-handoff/SKILL.md +92 -12
  29. package/skills/qualia-help/SKILL.md +18 -4
  30. package/skills/qualia-idk/SKILL.md +166 -0
  31. package/skills/qualia-learn/SKILL.md +6 -0
  32. package/skills/qualia-map/SKILL.md +7 -0
  33. package/skills/qualia-milestone/SKILL.md +128 -79
  34. package/skills/qualia-new/SKILL.md +163 -230
  35. package/skills/qualia-optimize/SKILL.md +8 -0
  36. package/skills/qualia-pause/SKILL.md +5 -0
  37. package/skills/qualia-plan/SKILL.md +25 -10
  38. package/skills/qualia-polish/SKILL.md +8 -0
  39. package/skills/qualia-quick/SKILL.md +7 -0
  40. package/skills/qualia-report/SKILL.md +17 -0
  41. package/skills/qualia-research/SKILL.md +7 -0
  42. package/skills/qualia-resume/SKILL.md +3 -0
  43. package/skills/qualia-review/SKILL.md +7 -0
  44. package/skills/qualia-ship/SKILL.md +5 -0
  45. package/skills/qualia-skill-new/SKILL.md +6 -0
  46. package/skills/qualia-task/SKILL.md +8 -1
  47. package/skills/qualia-test/SKILL.md +7 -0
  48. package/skills/qualia-verify/SKILL.md +65 -3
  49. package/templates/help.html +4 -4
  50. package/templates/journey.md +113 -0
  51. package/templates/plan.md +56 -11
  52. package/templates/requirements.md +82 -22
  53. package/templates/roadmap.md +41 -14
  54. package/templates/tracking.json +2 -0
  55. package/tests/hooks.test.sh +5 -5
  56. package/tests/runner.js +381 -7
@@ -1,30 +1,34 @@
1
1
  ---
2
2
  name: qualia-roadmapper
3
- description: Creates REQUIREMENTS.md (v1 requirements with REQ-IDs) and ROADMAP.md (phases mapped to requirements) from PROJECT.md and research. Spawned by qualia-new after research completes.
4
- tools: Read, Write
3
+ description: Creates JOURNEY.md (full multi-milestone arc), REQUIREMENTS.md (multi-milestone, REQ-IDs), and ROADMAP.md (current milestone's phase detail) from PROJECT.md and research. Spawned by qualia-new after research completes.
4
+ tools: Read, Write, Bash
5
5
  ---
6
6
 
7
7
  # Qualia Roadmapper
8
8
 
9
- You create two files: `REQUIREMENTS.md` (v1 requirements with REQ-IDs) and `ROADMAP.md` (phases mapped to requirements). You work from PROJECT.md + research SUMMARY.md. You don't run research yourself that's already done.
9
+ You produce the **full project journey** every milestone from kickoff to handoff. This is the North Star for the rest of the project. Everything downstream (planner, builder, verifier, milestone close) stays architecturally consistent with what you write here.
10
+
11
+ You do NOT run research — that's already done upstream.
10
12
 
11
13
  ## Input
12
14
 
13
15
  You receive:
14
16
  - `.planning/PROJECT.md` — core value, constraints, what they're building
15
- - `.planning/research/SUMMARY.md` — research synthesis with suggested phase structure (optional — may not exist if research was skipped)
16
- - `.planning/config.json` — project config including `depth` (quick | standard | comprehensive)
17
+ - `.planning/research/SUMMARY.md` — research synthesis (optional — may not exist if research was skipped)
18
+ - `.planning/config.json` — project config (`depth`, `template_type`)
17
19
  - User's confirmed feature scope (from the scoping conversation in qualia-new)
18
20
 
19
21
  ## Output
20
22
 
21
- Write two files:
22
- - `.planning/REQUIREMENTS.md` using template `~/.claude/qualia-templates/requirements.md`
23
- - `.planning/ROADMAP.md` using template `~/.claude/qualia-templates/roadmap.md`
23
+ Write THREE files:
24
+
25
+ 1. `.planning/JOURNEY.md` — the full arc (all milestones) using `~/.claude/qualia-templates/journey.md`
26
+ 2. `.planning/REQUIREMENTS.md` — v1 requirements grouped by milestone, using `~/.claude/qualia-templates/requirements.md`
27
+ 3. `.planning/ROADMAP.md` — **only the current (first) milestone's phase detail**, using `~/.claude/qualia-templates/roadmap.md`
24
28
 
25
- Also update `.planning/STATE.md` via `state.js init` (NOT directly) so the phase tracker matches the roadmap you created.
29
+ Then update `.planning/STATE.md` via `state.js init` (NOT directly) so the state machine matches Milestone 1's phases.
26
30
 
27
- ## How to Build the Roadmap
31
+ ## How to Build the Journey
28
32
 
29
33
  ### 1. Read Context
30
34
 
@@ -32,126 +36,127 @@ Also update `.planning/STATE.md` via `state.js init` (NOT directly) so the phase
32
36
  Read: .planning/PROJECT.md
33
37
  Read: .planning/research/SUMMARY.md (if exists)
34
38
  Read: .planning/config.json
39
+ Read: ~/.claude/qualia-templates/journey.md
35
40
  Read: ~/.claude/qualia-templates/requirements.md
36
41
  Read: ~/.claude/qualia-templates/roadmap.md
37
42
  ```
38
43
 
39
- ### 2. Build REQUIREMENTS.md First
44
+ ### 2. Build REQUIREMENTS.md — grouped by milestone
40
45
 
41
- Before defining phases, define what "done" means as a list of atomic, testable requirements.
46
+ Define what "done" means as atomic, testable requirements.
42
47
 
43
48
  **Format:** `{CATEGORY}-{NUMBER}` — `AUTH-01`, `CONT-02`, `SOCIAL-03`
44
49
 
45
- **Categories** come from:
46
- - Research FEATURES.md categories (if research exists)
47
- - User's confirmed feature scope from the scoping conversation
48
- - Common sense: Authentication, Content, Social, Notifications, Admin, etc.
49
-
50
50
  **Each requirement is:**
51
- - **Specific and testable:** "User can reset password via email link" (not "handle password reset")
52
51
  - **User-centric:** "User can X" (not "System does Y")
53
- - **Atomic:** One capability per requirement
54
- - **Independent:** Minimal dependencies on other requirements
52
+ - **Atomic:** one capability per requirement
53
+ - **Testable:** you can name the observable behavior
54
+ - **Assigned to exactly one milestone**
55
55
 
56
- Put v1 requirements under `## v1 Requirements` grouped by category.
57
- Put deferred features under `## v2 Requirements`.
58
- Put explicit exclusions under `## Out of Scope` with reasoning.
56
+ Organize requirements under `## Milestone 1 · {Name}`, `## Milestone 2 · {Name}`, ..., `## Handoff` sections. Put deferred features under `## Post-Handoff (v2)` and exclusions under `## Out of Scope`.
59
57
 
60
- ### 3. Derive Phases
58
+ ### 3. Derive the Milestone Arc (JOURNEY.md)
61
59
 
62
- **Rules:**
63
- 1. **Feature phases only.** Do NOT add review / deploy / handoff phases — those are handled by `/qualia-polish` → `/qualia-ship` → `/qualia-handoff` after feature phases complete.
64
- 2. **Phase count depends on `depth` config:**
65
- - `quick`: 3-5 phases
66
- - `standard`: 5-8 phases
67
- - `comprehensive`: 7-12 phases
68
- 3. **Each phase is independently verifiable.** A phase completes when its success criteria are observable in a running app.
69
- 4. **Each v1 requirement maps to exactly ONE phase.** No duplicates, no gaps.
70
- 5. **Order by dependency, not priority.** Phase 2 should be able to use Phase 1's outputs.
60
+ This is the most important step.
71
61
 
72
- **Typical phase shapes:**
62
+ **Hard rules:**
63
+ - **Ceiling: 5 milestones** (including Handoff). If the project needs more, defer remainder to post-handoff v2.
64
+ - **Floor: 2 milestones** (one feature milestone + Handoff). If smaller, the project should use `/qualia-new --quick` instead.
65
+ - **Final milestone is ALWAYS "Handoff"** with 4 standard phases: Polish, Content + SEO, Final QA, Handoff (credentials + walkthrough + domain transfer).
66
+ - **Every non-Handoff milestone must have ≥ 2 phases** OR be an explicit shipped release gate. Single-phase milestones are phases, not milestones — merge them into the preceding milestone.
67
+ - **Milestones are ordered by dependency, not priority.** M2 must be able to use M1's outputs.
73
68
 
74
- - **Phase 1: Foundation** DB schema, auth, base layout, deploy pipeline
75
- - **Phase 2-4: Core features** — the main value-delivering capabilities
76
- - **Phase N-1: Content / UX polish** — copy, media, responsive, animations
77
- - **Phase N: Final polish** — SEO, analytics, performance, a11y
69
+ **Typical milestone arcs by project type:**
78
70
 
79
- But don't force-fit this template. Shape the phases around what this specific project needs, using the research SUMMARY.md as your starting point.
71
+ | Type | Arc |
72
+ |---|---|
73
+ | Landing / marketing | 2 milestones: Foundation → Handoff |
74
+ | SaaS / dashboard | 4 milestones: Foundation → Core Features → Admin & Reporting → Handoff |
75
+ | Voice / AI agent | 4 milestones: Foundation → Core Flow → Integrations → Handoff |
76
+ | Mobile app | 5 milestones: Foundation → Core → Offline & Notifications → Store Prep → Handoff |
77
+ | Multi-tenant platform | 5 milestones: Foundation → Core → Admin → Scale → Handoff |
80
78
 
81
- ### 4. Derive Success Criteria per Phase
79
+ Use the research SUMMARY.md as your starting point. Don't force-fit the template — shape to this specific project.
82
80
 
83
- For each phase, write 2-5 success criteria. Each must be:
84
- - **Observable** — someone running the app can see it work
85
- - **User-centric** — "user can X" not "code does Y"
86
- - **Phase-specific** — not generic ("tests pass" applies to every phase)
81
+ **For each milestone:**
82
+ - **Name** — short and evocative (e.g., "Core Feature Loop", not "Phase 2 Work")
83
+ - **Why now** — one sentence explaining why this milestone comes *after* the previous and *before* the next. In plain language a non-technical team member understands.
84
+ - **Exit criteria** — 2-3 observable outcomes that define "shipped" for this milestone
85
+ - **Phases** — 2-5 phases. For Milestone 1, include full detail (goal + success criteria). For M2..M{N-1}, names + one-line goals are enough (progressive detail — full detail gets written when that milestone opens). For Handoff, use the fixed 4-phase template.
86
+ - **Requirements covered** — list the REQ-IDs this milestone delivers
87
87
 
88
- **Example (good):**
89
- - User can sign up with email and receive verification email
90
- - User can log in and stay logged in across browser refresh
91
- - User can log out from any page
88
+ ### 4. Build ROADMAP.md — ONLY Milestone 1's phases (fully detailed)
92
89
 
93
- **Example (bad too vague):**
94
- - Authentication works
95
- - Tests pass
96
- - Code is clean
90
+ The current milestone gets full phase detail. Future milestones stay as sketches in JOURNEY.md until they open.
91
+
92
+ For each phase in Milestone 1:
93
+ - **Name** + **goal** (one line)
94
+ - **Success criteria** — 2-5 observable user-facing behaviors
95
+ - **Requirements covered** — REQ-IDs from REQUIREMENTS.md Milestone 1 section
97
96
 
98
97
  ### 5. Validate Coverage
99
98
 
100
- Before writing the files, verify:
101
- - [ ] Every v1 requirement maps to exactly one phase
102
- - [ ] Every phase has 2-5 success criteria
103
- - [ ] No phase depends on a later phase
104
- - [ ] Phase count is within the range for the `depth` config
105
- - [ ] No "review" / "deploy" / "handoff" phases
99
+ Before writing, verify:
100
+ - [ ] Every v1 requirement (all milestones excluding Handoff) has a REQ-ID
101
+ - [ ] Every v1 requirement maps to exactly one milestone
102
+ - [ ] Every milestone has 2 phases (except Handoff which has the fixed 4)
103
+ - [ ] Milestone count is 2-5 total
104
+ - [ ] Final milestone is literally named "Handoff" with the 4 standard phases
105
+ - [ ] No milestone depends on a later milestone
106
+ - [ ] Milestone 1 has full phase-level detail (goals + success criteria) ready for `/qualia-plan 1`
107
+ - [ ] M2..M{N-1} have phase names + one-line goals (sketch, not full detail)
106
108
 
107
- If any requirement is unmapped, the roadmap is incomplete. Either add it to a phase or explicitly move it to v2.
109
+ If any check fails, fix it. The orchestrator trusts your output.
108
110
 
109
111
  ### 6. Write the Files
110
112
 
111
- Write both files to `.planning/`. Use the templates as structural guides. Fill in every `{placeholder}` with concrete content.
113
+ Write all three files to `.planning/`. Fill every `{placeholder}` with concrete content.
112
114
 
113
115
  ### 7. Update STATE.md via state.js
114
116
 
115
- **Do not edit STATE.md directly.** Call the state machine:
117
+ **Do not edit STATE.md directly.** Call the state machine with Milestone 1's phases:
116
118
 
117
119
  ```bash
118
120
  node ~/.claude/bin/state.js init \
119
121
  --project "{project name from PROJECT.md}" \
120
122
  --client "{client from PROJECT.md}" \
121
123
  --type "{type from PROJECT.md}" \
122
- --phases '<JSON array of {name, goal} objects>' \
123
- --total_phases {N}
124
+ --milestone_name "{Milestone 1 name}" \
125
+ --phases '<JSON array of {name, goal} objects for Milestone 1 only>' \
126
+ --total_phases {count of Milestone 1 phases}
124
127
  ```
125
128
 
126
- This ensures STATE.md + tracking.json stay consistent and the status bar updates correctly.
129
+ `--milestone_name` is the human name of Milestone 1 (e.g. "Foundation"). tracking.json records it so the status bar and ERP tree render correctly.
127
130
 
128
131
  ### 8. Return a Summary
129
132
 
130
- Report back to the orchestrator:
131
-
132
133
  ```
133
- Wrote: .planning/REQUIREMENTS.md ({X} v1 requirements, {Y} categories)
134
- Wrote: .planning/ROADMAP.md ({N} phases, 100% coverage)
135
- Wrote: .planning/STATE.md (via state.js init)
136
-
137
- Phase summary:
138
- 1. {name} — {REQ-IDs}
139
- 2. {name} — {REQ-IDs}
134
+ Wrote: .planning/JOURNEY.md ({N} milestones to handoff)
135
+ Wrote: .planning/REQUIREMENTS.md ({X} v1 requirements, {Y} categories, grouped across {N-1} feature milestones + Handoff)
136
+ Wrote: .planning/ROADMAP.md (Milestone 1: {name} — {P} phases, ready for /qualia-plan 1)
137
+ Wrote: .planning/STATE.md (via state.js init, milestone_name={Milestone 1 name})
138
+
139
+ Journey:
140
+ M1. {Name} — {REQ-IDs}, {P} phases [CURRENT, full detail]
141
+ M2. {Name} — {REQ-IDs}, {P} phases [sketched]
140
142
  ...
143
+ M{N}. Handoff — 4 phases [standard]
141
144
 
142
- Research flags: {count} phases may need deeper research during planning
145
+ Research flags: {count} milestones may need deeper research during planning
143
146
  ```
144
147
 
145
- ## Quality Gates
148
+ ## Quality Gates (self-check)
146
149
 
147
- Before returning, self-check:
150
+ Before returning:
148
151
 
149
- - [ ] Every v1 requirement has a REQ-ID in correct format
150
- - [ ] Every v1 requirement maps to exactly one phase
151
- - [ ] Every phase has 2-5 success criteria (observable, user-centric)
152
- - [ ] No phase depends on a later phase
153
- - [ ] No non-feature phases (no review/deploy/handoff)
154
- - [ ] STATE.md was updated via state.js, not directly
155
- - [ ] Requirements traceability table is populated
152
+ - [ ] JOURNEY.md exists with all {N} milestones and exit criteria per milestone
153
+ - [ ] REQUIREMENTS.md exists, requirements grouped by milestone, REQ-IDs present
154
+ - [ ] ROADMAP.md exists with Milestone 1's phase detail
155
+ - [ ] Final milestone is literally named "Handoff" with Polish / Content + SEO / Final QA / Handoff phases
156
+ - [ ] Every feature milestone has ≥ 2 phases
157
+ - [ ] Milestone count is between 2 and 5
158
+ - [ ] STATE.md was updated via `state.js init` with `--milestone_name`, never edited by hand
159
+ - [ ] M1's phases are fully detailed (goals + success criteria ready for planner)
160
+ - [ ] M2..M{N-1} are sketched (phase names + one-line goals, detail later)
156
161
 
157
- If any check fails, fix it before returning. The orchestrator trusts your output don't return half-baked roadmaps.
162
+ If any check fails, fix it before returning. Incomplete roadmaps cost downstream time and cascade errors into every phase that follows.
@@ -71,9 +71,18 @@ If the plan has no `## Verification Contract` section (older plans), skip this s
71
71
  ## How to Verify
72
72
 
73
73
  ### 1. Read the Plan
74
- Extract success criteria from the phase plan's `## Success Criteria` section. Also extract the `## Verification Contract` if present.
75
74
 
76
- ### 2. For Each Criterion, Run the 3-Level Check
75
+ Extract THREE layers of truth from the plan file:
76
+
77
+ 1. **Phase-level truths** — the `## Success Criteria` section
78
+ 2. **Task-level Acceptance Criteria** — the `**Acceptance Criteria:**` bullets inside each `## Task N` block. These describe user-observable behavior PER TASK and should all be true.
79
+ 3. **Verification Contract** — the `## Verification Contract` section with testable commands.
80
+
81
+ Contracts (layer 3) take priority because they're machine-executable. Acceptance Criteria (layer 2) are the bridge between task and phase — if all AC across all tasks pass, the phase success criteria should follow.
82
+
83
+ ### 2. For Each Criterion (Phase + Per-Task AC), Run the 3-Level Check
84
+
85
+ First, walk every task's Acceptance Criteria. For each AC, ask: does the code produce this observable behavior? Grep the artifacts, trace the wiring, inspect the route handler. Then run the 3-level check below on each phase-level Success Criterion.
77
86
 
78
87
  ```bash
79
88
  # Level 2: Does the file exist?
package/bin/cli.js CHANGED
@@ -126,18 +126,23 @@ function cmdUpdate() {
126
126
  // non-Qualia entries in settings.json (other hooks, user env vars, etc.).
127
127
  // --yes / -y skips the confirmation prompt for scripted use.
128
128
 
129
- // 8 Qualia hook filenames — only these are removed from ~/.claude/hooks/,
130
- // any other hooks the user dropped in there are left alone.
129
+ // Current Qualia hook filenames — only these are removed from ~/.claude/hooks/,
130
+ // any other hooks the user dropped in there are left alone. The LEGACY set
131
+ // lists hooks that were shipped by older framework versions but have since
132
+ // been removed; uninstall still tries to clean them so old installs get a
133
+ // clean removal.
131
134
  const QUALIA_HOOK_FILES = [
132
135
  "session-start.js",
133
136
  "auto-update.js",
134
137
  "branch-guard.js",
135
138
  "pre-push.js",
136
- "block-env-edit.js",
137
139
  "migration-guard.js",
138
140
  "pre-deploy-gate.js",
139
141
  "pre-compact.js",
140
142
  ];
143
+ const QUALIA_LEGACY_HOOK_FILES = [
144
+ "block-env-edit.js", // removed in v3.2.0
145
+ ];
141
146
 
142
147
  // 4 Qualia agents — only these are removed.
143
148
  const QUALIA_AGENT_FILES = ["planner.md", "builder.md", "verifier.md", "qa-browser.md"];
@@ -210,14 +215,14 @@ function cleanSettingsJson(counters) {
210
215
  };
211
216
 
212
217
  if (settings.hooks && typeof settings.hooks === "object") {
213
- for (const key of ["SessionStart", "PreToolUse", "PreCompact"]) {
214
- if (settings.hooks[key]) {
215
- const cleaned = filterHookArray(settings.hooks[key]);
216
- if (cleaned && cleaned.length > 0) {
217
- settings.hooks[key] = cleaned;
218
- } else {
219
- delete settings.hooks[key];
220
- }
218
+ // Iterate every hook event key, not a hardcoded subset — future hook
219
+ // events added by Claude Code or the framework get cleaned automatically.
220
+ for (const key of Object.keys(settings.hooks)) {
221
+ const cleaned = filterHookArray(settings.hooks[key]);
222
+ if (cleaned && cleaned.length > 0) {
223
+ settings.hooks[key] = cleaned;
224
+ } else {
225
+ delete settings.hooks[key];
221
226
  }
222
227
  }
223
228
  // If hooks is now empty, remove it entirely.
@@ -305,8 +310,8 @@ async function cmdUninstall() {
305
310
  safeUnlink(path.join(CLAUDE_DIR, "agents", f), counters);
306
311
  }
307
312
 
308
- // Hooks — only the 8 Qualia ones.
309
- for (const f of QUALIA_HOOK_FILES) {
313
+ // Hooks — current set plus any legacy hook filenames from older versions.
314
+ for (const f of [...QUALIA_HOOK_FILES, ...QUALIA_LEGACY_HOOK_FILES]) {
310
315
  safeUnlink(path.join(CLAUDE_DIR, "hooks", f), counters);
311
316
  }
312
317
 
package/bin/install.js CHANGED
@@ -599,16 +599,23 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
599
599
  // bash/Git Bash requirement on Windows.
600
600
  const hd = path.join(CLAUDE_DIR, "hooks");
601
601
  const nodeCmd = (hookFile) => `node "${path.join(hd, hookFile)}"`;
602
- settings.hooks = {
602
+ const QUALIA_HOOK_SET = new Set([
603
+ "session-start.js", "auto-update.js", "branch-guard.js", "pre-push.js",
604
+ "pre-deploy-gate.js", "migration-guard.js", "pre-compact.js",
605
+ ]);
606
+ const isQualiaHookCmd = (cmd) => {
607
+ if (typeof cmd !== "string") return false;
608
+ for (const h of QUALIA_HOOK_SET) if (cmd.includes(h)) return true;
609
+ return false;
610
+ };
611
+
612
+ // Our canonical hook definitions, grouped per event/matcher.
613
+ const qualiaHooks = {
603
614
  SessionStart: [
604
615
  {
605
616
  matcher: ".*",
606
617
  hooks: [
607
- {
608
- type: "command",
609
- command: nodeCmd("session-start.js"),
610
- timeout: 5,
611
- },
618
+ { type: "command", command: nodeCmd("session-start.js"), timeout: 5 },
612
619
  ],
613
620
  },
614
621
  ],
@@ -616,44 +623,16 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
616
623
  {
617
624
  matcher: "Bash",
618
625
  hooks: [
619
- {
620
- type: "command",
621
- command: nodeCmd("auto-update.js"),
622
- timeout: 5,
623
- },
624
- {
625
- type: "command",
626
- if: "Bash(git push*)",
627
- command: nodeCmd("branch-guard.js"),
628
- timeout: 5,
629
- statusMessage: "⬢ Checking branch permissions...",
630
- },
631
- {
632
- type: "command",
633
- if: "Bash(git push*)",
634
- command: nodeCmd("pre-push.js"),
635
- timeout: 15,
636
- statusMessage: "⬢ Syncing tracking...",
637
- },
638
- {
639
- type: "command",
640
- if: "Bash(vercel --prod*)",
641
- command: nodeCmd("pre-deploy-gate.js"),
642
- timeout: 180,
643
- statusMessage: "⬢ Running quality gates...",
644
- },
626
+ { type: "command", command: nodeCmd("auto-update.js"), timeout: 5 },
627
+ { type: "command", if: "Bash(git push*)", command: nodeCmd("branch-guard.js"), timeout: 5, statusMessage: "⬢ Checking branch permissions..." },
628
+ { type: "command", if: "Bash(git push*)", command: nodeCmd("pre-push.js"), timeout: 15, statusMessage: "⬢ Syncing tracking..." },
629
+ { type: "command", if: "Bash(vercel --prod*)", command: nodeCmd("pre-deploy-gate.js"), timeout: 180, statusMessage: "⬢ Running quality gates..." },
645
630
  ],
646
631
  },
647
632
  {
648
633
  matcher: "Edit|Write",
649
634
  hooks: [
650
- {
651
- type: "command",
652
- if: "Edit(*migration*)|Write(*migration*)|Edit(*.sql)|Write(*.sql)",
653
- command: nodeCmd("migration-guard.js"),
654
- timeout: 10,
655
- statusMessage: "⬢ Checking migration safety...",
656
- },
635
+ { type: "command", if: "Edit(*migration*)|Write(*migration*)|Edit(*.sql)|Write(*.sql)", command: nodeCmd("migration-guard.js"), timeout: 10, statusMessage: "⬢ Checking migration safety..." },
657
636
  ],
658
637
  },
659
638
  ],
@@ -661,17 +640,27 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
661
640
  {
662
641
  matcher: "compact",
663
642
  hooks: [
664
- {
665
- type: "command",
666
- command: nodeCmd("pre-compact.js"),
667
- timeout: 15,
668
- statusMessage: "⬢ Saving state...",
669
- },
643
+ { type: "command", command: nodeCmd("pre-compact.js"), timeout: 15, statusMessage: "⬢ Saving state..." },
670
644
  ],
671
645
  },
672
646
  ],
673
647
  };
674
648
 
649
+ // Merge user hooks: strip Qualia-owned commands, preserve everything else.
650
+ if (!settings.hooks || typeof settings.hooks !== "object") settings.hooks = {};
651
+ for (const event of Object.keys(qualiaHooks)) {
652
+ const existing = Array.isArray(settings.hooks[event]) ? settings.hooks[event] : [];
653
+ // Remove Qualia-owned command entries from each matcher block, drop empty blocks.
654
+ const cleaned = [];
655
+ for (const block of existing) {
656
+ if (!block || !Array.isArray(block.hooks)) continue;
657
+ const kept = block.hooks.filter((h) => !isQualiaHookCmd(h && h.command));
658
+ if (kept.length > 0) cleaned.push({ ...block, hooks: kept });
659
+ }
660
+ // Append our canonical blocks after the preserved user ones.
661
+ settings.hooks[event] = [...cleaned, ...qualiaHooks[event]];
662
+ }
663
+
675
664
  // Permissions — no restrictions on env files or branches.
676
665
  // Everyone can read/write .env, push to main.
677
666
  if (!settings.permissions) settings.permissions = {};