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.
- package/CLAUDE.md +23 -11
- package/README.md +96 -51
- package/agents/builder.md +25 -14
- package/agents/plan-checker.md +29 -16
- package/agents/planner.md +33 -24
- package/agents/research-synthesizer.md +25 -12
- package/agents/roadmapper.md +89 -84
- package/agents/verifier.md +11 -2
- package/bin/cli.js +18 -13
- package/bin/install.js +34 -45
- package/bin/qualia-ui.js +267 -1
- package/bin/state.js +164 -12
- package/bin/statusline.js +4 -1
- package/docs/erp-contract.md +12 -0
- package/guide.md +85 -22
- package/hooks/migration-guard.js +23 -9
- package/hooks/pre-compact.js +39 -11
- package/hooks/pre-deploy-gate.js +3 -4
- package/hooks/pre-push.js +6 -3
- package/hooks/session-start.js +8 -8
- package/package.json +1 -1
- package/rules/frontend.md +5 -13
- package/skills/qualia/SKILL.md +8 -1
- package/skills/qualia-build/SKILL.md +49 -4
- package/skills/qualia-debug/SKILL.md +6 -0
- package/skills/qualia-design/SKILL.md +9 -1
- package/skills/qualia-discuss/SKILL.md +6 -0
- package/skills/qualia-handoff/SKILL.md +92 -12
- package/skills/qualia-help/SKILL.md +18 -4
- package/skills/qualia-idk/SKILL.md +166 -0
- package/skills/qualia-learn/SKILL.md +6 -0
- package/skills/qualia-map/SKILL.md +7 -0
- package/skills/qualia-milestone/SKILL.md +128 -79
- package/skills/qualia-new/SKILL.md +163 -230
- package/skills/qualia-optimize/SKILL.md +8 -0
- package/skills/qualia-pause/SKILL.md +5 -0
- package/skills/qualia-plan/SKILL.md +25 -10
- package/skills/qualia-polish/SKILL.md +8 -0
- package/skills/qualia-quick/SKILL.md +7 -0
- package/skills/qualia-report/SKILL.md +17 -0
- package/skills/qualia-research/SKILL.md +7 -0
- package/skills/qualia-resume/SKILL.md +3 -0
- package/skills/qualia-review/SKILL.md +7 -0
- package/skills/qualia-ship/SKILL.md +5 -0
- package/skills/qualia-skill-new/SKILL.md +6 -0
- package/skills/qualia-task/SKILL.md +8 -1
- package/skills/qualia-test/SKILL.md +7 -0
- package/skills/qualia-verify/SKILL.md +65 -3
- package/templates/help.html +4 -4
- package/templates/journey.md +113 -0
- package/templates/plan.md +56 -11
- package/templates/requirements.md +82 -22
- package/templates/roadmap.md +41 -14
- package/templates/tracking.json +2 -0
- package/tests/hooks.test.sh +5 -5
- package/tests/runner.js +381 -7
package/agents/roadmapper.md
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qualia-roadmapper
|
|
3
|
-
description: Creates
|
|
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
|
|
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
|
|
16
|
-
- `.planning/config.json` — project config
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
44
|
+
### 2. Build REQUIREMENTS.md — grouped by milestone
|
|
40
45
|
|
|
41
|
-
|
|
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:**
|
|
54
|
-
- **
|
|
52
|
+
- **Atomic:** one capability per requirement
|
|
53
|
+
- **Testable:** you can name the observable behavior
|
|
54
|
+
- **Assigned to exactly one milestone**
|
|
55
55
|
|
|
56
|
-
Put
|
|
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
|
|
58
|
+
### 3. Derive the Milestone Arc (JOURNEY.md)
|
|
61
59
|
|
|
62
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
84
|
-
- **
|
|
85
|
-
- **
|
|
86
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
-
|
|
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
|
|
101
|
-
- [ ] Every v1 requirement
|
|
102
|
-
- [ ] Every
|
|
103
|
-
- [ ]
|
|
104
|
-
- [ ]
|
|
105
|
-
- [ ]
|
|
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
|
|
109
|
+
If any check fails, fix it. The orchestrator trusts your output.
|
|
108
110
|
|
|
109
111
|
### 6. Write the Files
|
|
110
112
|
|
|
111
|
-
Write
|
|
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
|
-
--
|
|
123
|
-
--
|
|
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
|
-
|
|
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/
|
|
134
|
-
Wrote: .planning/
|
|
135
|
-
Wrote: .planning/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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}
|
|
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
|
|
150
|
+
Before returning:
|
|
148
151
|
|
|
149
|
-
- [ ]
|
|
150
|
-
- [ ]
|
|
151
|
-
- [ ]
|
|
152
|
-
- [ ]
|
|
153
|
-
- [ ]
|
|
154
|
-
- [ ]
|
|
155
|
-
- [ ]
|
|
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.
|
|
162
|
+
If any check fails, fix it before returning. Incomplete roadmaps cost downstream time and cascade errors into every phase that follows.
|
package/agents/verifier.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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 —
|
|
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
|
-
|
|
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
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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 = {};
|