qualia-framework-v2 2.0.0 → 2.1.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.
package/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # Qualia Framework v2
2
2
 
3
- Claude Code workflow framework for Qualia Solutions. Guides projects from setup to client handoff.
3
+ A prompt orchestration framework for [Claude Code](https://claude.ai/code). It installs into `~/.claude/` and wraps your AI-assisted development workflow with structured planning, execution, verification, and deployment gates.
4
+
5
+ It is not an application framework like Rails or Next.js. It doesn't generate code, run servers, or process data. It's an opinionated workflow layer that tells Claude how to plan, build, and verify your projects.
4
6
 
5
7
  ## Install
6
8
 
7
9
  ```bash
8
- git clone https://github.com/qualia-solutions/qualia-framework-v2.git
9
- cd qualia-framework-v2
10
- chmod +x install.sh
11
- ./install.sh
10
+ npx qualia-framework-v2 install
12
11
  ```
13
12
 
13
+ Enter your team code when prompted. Get your code from Fawzi.
14
+
14
15
  ## Usage
15
16
 
16
17
  Open Claude Code in any project directory:
@@ -29,20 +30,58 @@ See `guide.md` for the full developer guide.
29
30
 
30
31
  ## What's Inside
31
32
 
32
- - **10 skills** — the commands that guide you from setup to handoff
33
+ - **11 skills** — slash commands that guide you from setup to handoff
33
34
  - **3 agents** — planner, builder, verifier (each in fresh context)
34
- - **6 hooks** — session start, branch guard, env protection, deploy gate, state save, tracking sync
35
+ - **7 hooks** — branch guard, pre-push tracking sync, env protection, migration guard, deploy gate, pre-compact state save, session start
35
36
  - **3 rules** — security, frontend, deployment
36
37
  - **4 templates** — tracking.json, state.md, project.md, plan.md
37
38
 
39
+ ## Why It Works
40
+
41
+ ### Goal-Backward Verification
42
+
43
+ Most CI checks "did the task run." Qualia checks "does the outcome actually work." The verifier doesn't trust summaries — it greps the codebase for stubs, placeholders, unwired imports. When Claude says "I built the chat component," this catches the cases where it wrote a skeleton with `// TODO` inside.
44
+
45
+ ### Agent Separation
46
+
47
+ Splitting planner, builder, and verifier into separate agents with separate contexts prevents the "God prompt" problem where one massive context tries to plan AND code AND test. Each agent gets fresh context. This directly addresses Claude's quality degradation curve — task 50 gets the same quality as task 1.
48
+
49
+ ### Production-Grade Hooks
50
+
51
+ The `settings.json` hooks are real ops engineering, not theoretical:
52
+
53
+ - **Pre-deploy gate** — TypeScript, lint, tests, build, and `service_role` leak scan before `vercel --prod`
54
+ - **Branch guard** — Role-aware: owner can push to main, employees can't
55
+ - **Migration guard** — Catches `DROP TABLE` without `IF EXISTS`, `DELETE` without `WHERE`, `CREATE TABLE` without RLS
56
+ - **Env block** — Prevents Claude from touching `.env` files
57
+ - **Pre-compact** — Saves state before context compression
58
+
59
+ ### Wave-Based Parallelization
60
+
61
+ Plans are grouped into waves for parallel execution. No fancy DAG solver — the planner assigns wave numbers, the orchestrator spawns agents per wave. Pragmatic over clever.
62
+
63
+ ### Plans Are Prompts
64
+
65
+ Plan files aren't documents that get translated into prompts — they ARE the prompts. `@file` references, explicit task actions, and verification criteria baked in. This eliminates translation loss between "what we planned" and "what Claude actually reads."
66
+
38
67
  ## Architecture
39
68
 
40
- - **Context isolation:** Each task runs in a fresh AI context. No quality degradation.
41
- - **Goal-backward verification:** Verifier greps the code to check if things actually work.
42
- - **Plans are prompts:** Plan files ARE the builder's instructions.
43
- - **Wave execution:** Independent tasks run in parallel.
44
- - **ERP integration:** `tracking.json` updated on every push, ERP reads via git.
69
+ ```
70
+ npx qualia-framework-v2 install
71
+ |
72
+ v
73
+ ~/.claude/
74
+ ├── skills/ 11 slash commands
75
+ ├── agents/ planner.md, builder.md, verifier.md
76
+ ├── hooks/ 7 shell scripts (branch, env, migration, deploy, push, compact, session)
77
+ ├── rules/ security.md, frontend.md, deployment.md
78
+ ├── qualia-templates/ tracking.json, state.md, project.md, plan.md
79
+ ├── CLAUDE.md global instructions (role-configured per team member)
80
+ └── statusline.sh teal-branded 2-line status bar
81
+ ```
45
82
 
46
83
  ## For Qualia Solutions Team
47
84
 
48
- Stack: Next.js, React, TypeScript, Supabase, Vercel.
85
+ Stack: Next.js 16+, React 19, TypeScript, Supabase, Vercel.
86
+
87
+ Built by [Qualia Solutions](https://qualiasolutions.net) — Nicosia, Cyprus.
package/bin/cli.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ const TEAL = "\x1b[38;2;0;206;209m";
4
+ const DIM = "\x1b[38;2;80;90;100m";
5
+ const WHITE = "\x1b[38;2;220;225;230m";
6
+ const RESET = "\x1b[0m";
7
+
8
+ const cmd = process.argv[2];
9
+
10
+ if (cmd === "install") {
11
+ require("./install.js");
12
+ } else {
13
+ console.log("");
14
+ console.log(`${TEAL} ◆ Qualia Framework v2${RESET}`);
15
+ console.log(`${DIM} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
16
+ console.log("");
17
+ console.log(` ${WHITE}Usage:${RESET}`);
18
+ console.log(` npx qualia-framework-v2 ${TEAL}install${RESET} Install the framework`);
19
+ console.log("");
20
+ }
package/bin/install.js CHANGED
@@ -1,248 +1,393 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { execSync } = require("child_process");
3
+ const { createInterface } = require("readline");
4
4
  const path = require("path");
5
5
  const fs = require("fs");
6
6
 
7
+ // ─── Colors ──────────────────────────────────────────────
7
8
  const TEAL = "\x1b[38;2;0;206;209m";
8
9
  const DIM = "\x1b[38;2;80;90;100m";
9
10
  const GREEN = "\x1b[38;2;52;211;153m";
10
11
  const WHITE = "\x1b[38;2;220;225;230m";
12
+ const YELLOW = "\x1b[38;2;234;179;8m";
13
+ const RED = "\x1b[38;2;239;68;68m";
11
14
  const RESET = "\x1b[0m";
12
15
 
16
+ // ─── Team codes ──────────────────────────────────────────
17
+ const TEAM = {
18
+ "QS-FAWZI-01": {
19
+ name: "Fawzi Goussous",
20
+ role: "OWNER",
21
+ description: "Company owner. Full access. Can push to main, approve deploys, edit secrets.",
22
+ },
23
+ "QS-HASAN-02": {
24
+ name: "Hasan",
25
+ role: "EMPLOYEE",
26
+ description: "Developer. Feature branches only. Cannot push to main or edit .env files.",
27
+ },
28
+ "QS-MOAYAD-03": {
29
+ name: "Moayad",
30
+ role: "EMPLOYEE",
31
+ description: "Developer. Feature branches only. Cannot push to main or edit .env files.",
32
+ },
33
+ };
34
+
13
35
  const CLAUDE_DIR = path.join(require("os").homedir(), ".claude");
14
36
  const FRAMEWORK_DIR = path.resolve(__dirname, "..");
15
37
 
38
+ let installed = 0;
39
+ let errors = 0;
40
+
16
41
  function log(msg) {
17
42
  console.log(` ${msg}`);
18
43
  }
19
-
44
+ function ok(label) {
45
+ installed++;
46
+ log(`${GREEN}✓${RESET} ${label}`);
47
+ }
48
+ function warn(label) {
49
+ errors++;
50
+ log(`${YELLOW}✗${RESET} ${label}`);
51
+ }
20
52
  function copy(src, dest) {
21
53
  const destDir = path.dirname(dest);
22
54
  if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
23
55
  fs.copyFileSync(src, dest);
24
56
  }
25
57
 
26
- function copyDir(src, dest) {
27
- if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
28
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
29
- const srcPath = path.join(src, entry.name);
30
- const destPath = path.join(dest, entry.name);
31
- if (entry.isDirectory()) copyDir(srcPath, destPath);
32
- else fs.copyFileSync(srcPath, destPath);
33
- }
58
+ // ─── Prompt for code ─────────────────────────────────────
59
+ function askCode() {
60
+ return new Promise((resolve) => {
61
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
62
+ console.log("");
63
+ console.log(`${TEAL} ◆ Qualia Framework v2${RESET}`);
64
+ console.log(`${DIM} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
65
+ console.log("");
66
+ rl.question(` ${WHITE}Enter install code:${RESET} `, (answer) => {
67
+ rl.close();
68
+ resolve(answer.trim());
69
+ });
70
+ });
34
71
  }
35
72
 
36
- // Banner
37
- console.log("");
38
- console.log(`${TEAL} Qualia Framework v2${RESET}`);
39
- console.log(`${DIM} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
40
- console.log(` Installing to ${WHITE}${CLAUDE_DIR}${RESET}`);
41
- console.log("");
42
-
43
- // Skills
44
- const skillsDir = path.join(FRAMEWORK_DIR, "skills");
45
- const skills = fs.readdirSync(skillsDir).filter((d) =>
46
- fs.statSync(path.join(skillsDir, d)).isDirectory()
47
- );
48
- log(`${WHITE}Skills${RESET}`);
49
- for (const skill of skills) {
50
- const src = path.join(skillsDir, skill, "SKILL.md");
51
- const dest = path.join(CLAUDE_DIR, "skills", skill, "SKILL.md");
52
- copy(src, dest);
53
- log(` ${GREEN}✓${RESET} ${skill}`);
54
- }
73
+ // ─── Main ────────────────────────────────────────────────
74
+ async function main() {
75
+ const code = await askCode();
76
+ const member = TEAM[code];
55
77
 
56
- // Agents
57
- log(`${WHITE}Agents${RESET}`);
58
- const agentsDir = path.join(FRAMEWORK_DIR, "agents");
59
- for (const file of fs.readdirSync(agentsDir)) {
60
- copy(path.join(agentsDir, file), path.join(CLAUDE_DIR, "agents", file));
61
- log(` ${GREEN}✓${RESET} ${file}`);
62
- }
78
+ if (!member) {
79
+ console.log("");
80
+ log(`${RED}✗${RESET} Invalid code. Get your install code from Fawzi.`);
81
+ console.log("");
82
+ process.exit(1);
83
+ }
63
84
 
64
- // Rules
65
- log(`${WHITE}Rules${RESET}`);
66
- const rulesDir = path.join(FRAMEWORK_DIR, "rules");
67
- for (const file of fs.readdirSync(rulesDir)) {
68
- copy(path.join(rulesDir, file), path.join(CLAUDE_DIR, "rules", file));
69
- log(` ${GREEN}✓${RESET} ${file}`);
70
- }
85
+ console.log("");
86
+ log(`${GREEN}✓${RESET} ${WHITE}${member.name}${RESET} ${DIM}(${member.role})${RESET}`);
87
+ console.log("");
88
+ log(`Installing to ${WHITE}${CLAUDE_DIR}${RESET}`);
89
+ console.log("");
71
90
 
72
- // Hooks
73
- log(`${WHITE}Hooks${RESET}`);
74
- const hooksDir = path.join(FRAMEWORK_DIR, "hooks");
75
- const hooksDest = path.join(CLAUDE_DIR, "hooks");
76
- if (!fs.existsSync(hooksDest)) fs.mkdirSync(hooksDest, { recursive: true });
77
- for (const file of fs.readdirSync(hooksDir)) {
78
- const dest = path.join(hooksDest, file);
79
- copy(path.join(hooksDir, file), dest);
80
- fs.chmodSync(dest, 0o755);
81
- log(` ${GREEN}✓${RESET} ${file}`);
82
- }
91
+ // ─── Skills ──────────────────────────────────────────
92
+ const skillsDir = path.join(FRAMEWORK_DIR, "skills");
93
+ const skills = fs
94
+ .readdirSync(skillsDir)
95
+ .filter((d) => fs.statSync(path.join(skillsDir, d)).isDirectory());
83
96
 
84
- // Status line
85
- log(`${WHITE}Status line${RESET}`);
86
- const slDest = path.join(CLAUDE_DIR, "statusline.sh");
87
- copy(path.join(FRAMEWORK_DIR, "statusline.sh"), slDest);
88
- fs.chmodSync(slDest, 0o755);
89
- log(` ${GREEN}✓${RESET} statusline.sh`);
90
-
91
- // Templates
92
- log(`${WHITE}Templates${RESET}`);
93
- const tmplDir = path.join(FRAMEWORK_DIR, "templates");
94
- const tmplDest = path.join(CLAUDE_DIR, "qualia-templates");
95
- if (!fs.existsSync(tmplDest)) fs.mkdirSync(tmplDest, { recursive: true });
96
- for (const file of fs.readdirSync(tmplDir)) {
97
- copy(path.join(tmplDir, file), path.join(tmplDest, file));
98
- log(` ${GREEN}✓${RESET} ${file}`);
99
- }
97
+ log(`${WHITE}Skills${RESET}`);
98
+ for (const skill of skills) {
99
+ try {
100
+ copy(
101
+ path.join(skillsDir, skill, "SKILL.md"),
102
+ path.join(CLAUDE_DIR, "skills", skill, "SKILL.md")
103
+ );
104
+ ok(skill);
105
+ } catch (e) {
106
+ warn(`${skill} ${e.message}`);
107
+ }
108
+ }
100
109
 
101
- // CLAUDE.md
102
- log(`${WHITE}CLAUDE.md${RESET}`);
103
- copy(path.join(FRAMEWORK_DIR, "CLAUDE.md"), path.join(CLAUDE_DIR, "CLAUDE.md"));
104
- log(` ${GREEN}✓${RESET} user-level instructions`);
110
+ // ─── Agents ────────────────────────────────────────────
111
+ log(`${WHITE}Agents${RESET}`);
112
+ const agentsDir = path.join(FRAMEWORK_DIR, "agents");
113
+ for (const file of fs.readdirSync(agentsDir)) {
114
+ try {
115
+ copy(path.join(agentsDir, file), path.join(CLAUDE_DIR, "agents", file));
116
+ ok(file);
117
+ } catch (e) {
118
+ warn(`${file} — ${e.message}`);
119
+ }
120
+ }
105
121
 
106
- // Guide
107
- copy(path.join(FRAMEWORK_DIR, "guide.md"), path.join(CLAUDE_DIR, "qualia-guide.md"));
108
- log(` ${GREEN}✓${RESET} guide.md`);
122
+ // ─── Rules ─────────────────────────────────────────────
123
+ log(`${WHITE}Rules${RESET}`);
124
+ const rulesDir = path.join(FRAMEWORK_DIR, "rules");
125
+ for (const file of fs.readdirSync(rulesDir)) {
126
+ try {
127
+ copy(path.join(rulesDir, file), path.join(CLAUDE_DIR, "rules", file));
128
+ ok(file);
129
+ } catch (e) {
130
+ warn(`${file} — ${e.message}`);
131
+ }
132
+ }
109
133
 
110
- // Configure settings.json
111
- console.log("");
112
- log(`${WHITE}Configuring settings.json...${RESET}`);
134
+ // ─── Hooks ─────────────────────────────────────────────
135
+ log(`${WHITE}Hooks${RESET}`);
136
+ const hooksSource = path.join(FRAMEWORK_DIR, "hooks");
137
+ const hooksDest = path.join(CLAUDE_DIR, "hooks");
138
+ if (!fs.existsSync(hooksDest)) fs.mkdirSync(hooksDest, { recursive: true });
139
+ for (const file of fs.readdirSync(hooksSource)) {
140
+ try {
141
+ const dest = path.join(hooksDest, file);
142
+ copy(path.join(hooksSource, file), dest);
143
+ fs.chmodSync(dest, 0o755);
144
+ ok(file);
145
+ } catch (e) {
146
+ warn(`${file} — ${e.message}`);
147
+ }
148
+ }
113
149
 
114
- const settingsPath = path.join(CLAUDE_DIR, "settings.json");
115
- let settings = {};
116
- if (fs.existsSync(settingsPath)) {
150
+ // ─── Status line ───────────────────────────────────────
151
+ log(`${WHITE}Status line${RESET}`);
117
152
  try {
118
- settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
119
- } catch {}
120
- }
153
+ const slDest = path.join(CLAUDE_DIR, "statusline.sh");
154
+ copy(path.join(FRAMEWORK_DIR, "statusline.sh"), slDest);
155
+ fs.chmodSync(slDest, 0o755);
156
+ ok("statusline.sh");
157
+ } catch (e) {
158
+ warn(`statusline.sh — ${e.message}`);
159
+ }
121
160
 
122
- // Env
123
- if (!settings.env) settings.env = {};
124
- Object.assign(settings.env, {
125
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
126
- CLAUDE_CODE_DISABLE_AUTO_MEMORY: "0",
127
- MAX_MCP_OUTPUT_TOKENS: "25000",
128
- CLAUDE_CODE_NO_FLICKER: "1",
129
- });
161
+ // ─── Templates ─────────────────────────────────────────
162
+ log(`${WHITE}Templates${RESET}`);
163
+ const tmplDir = path.join(FRAMEWORK_DIR, "templates");
164
+ const tmplDest = path.join(CLAUDE_DIR, "qualia-templates");
165
+ if (!fs.existsSync(tmplDest)) fs.mkdirSync(tmplDest, { recursive: true });
166
+ for (const file of fs.readdirSync(tmplDir)) {
167
+ try {
168
+ copy(path.join(tmplDir, file), path.join(tmplDest, file));
169
+ ok(file);
170
+ } catch (e) {
171
+ warn(`${file} — ${e.message}`);
172
+ }
173
+ }
130
174
 
131
- // Status line
132
- settings.statusLine = { type: "command", command: "~/.claude/statusline.sh" };
133
-
134
- // Spinner
135
- settings.spinnerVerbs = {
136
- mode: "replace",
137
- verbs: [
138
- "Qualia-fying", "Solution-ing", "Teal-crafting", "Vibe-forging",
139
- "Shipping", "Wiring", "Polishing", "Verifying",
140
- "Orchestrating", "Architecting", "Deploying", "Hardening",
141
- ],
142
- };
175
+ // ─── CLAUDE.md with role ───────────────────────────────
176
+ log(`${WHITE}CLAUDE.md${RESET}`);
177
+ try {
178
+ let claudeMd = fs.readFileSync(
179
+ path.join(FRAMEWORK_DIR, "CLAUDE.md"),
180
+ "utf8"
181
+ );
182
+ claudeMd = claudeMd.replace("{{ROLE}}", member.role);
183
+ claudeMd = claudeMd.replace("{{ROLE_DESCRIPTION}}", member.description);
184
+ const claudeDest = path.join(CLAUDE_DIR, "CLAUDE.md");
185
+ fs.writeFileSync(claudeDest, claudeMd, "utf8");
186
+ ok(`Configured as ${member.role}`);
187
+ } catch (e) {
188
+ warn(`CLAUDE.md — ${e.message}`);
189
+ }
143
190
 
144
- settings.spinnerTipsOverride = {
145
- excludeDefault: true,
146
- tips: [
147
- "◆ Lost? Type /qualia for the next step",
148
- "◆ Small fix? Use /qualia-quick to skip planning",
149
- "◆ End of day? /qualia-report before you clock out",
150
- "◆ Context isolation: every task gets a fresh AI brain",
151
- "◆ The verifier doesn't trust claims — it greps the code",
152
- "◆ Plans are prompts the plan IS what the builder reads",
153
- "◆ Feature branches only — never push to main",
154
- "◆ Read before write — no exceptions",
155
- "◆ MVP first — build what's asked, nothing extra",
156
- "◆ tracking.json syncs to ERP on every push",
157
- ],
158
- };
191
+ // ─── Guide ─────────────────────────────────────────────
192
+ try {
193
+ copy(
194
+ path.join(FRAMEWORK_DIR, "guide.md"),
195
+ path.join(CLAUDE_DIR, "qualia-guide.md")
196
+ );
197
+ ok("guide.md");
198
+ } catch (e) {
199
+ warn(`guide.md${e.message}`);
200
+ }
159
201
 
160
- // Hooks
161
- const hd = path.join(CLAUDE_DIR, "hooks");
162
- settings.hooks = {
163
- PreToolUse: [
164
- {
165
- matcher: "Bash",
166
- hooks: [{
167
- type: "command",
168
- if: "Bash(git push*)",
169
- command: `${hd}/branch-guard.sh`,
170
- timeout: 10,
171
- statusMessage: "◆ Checking branch permissions...",
172
- }],
173
- },
174
- {
175
- matcher: "Edit|Write",
176
- hooks: [{
177
- type: "command",
178
- if: "Edit(*.env*)",
179
- command: `${hd}/block-env-edit.sh`,
180
- timeout: 5,
181
- statusMessage: "◆ Checking file permissions...",
182
- }],
183
- },
184
- ],
185
- PostToolUse: [
186
- {
187
- matcher: "Bash",
188
- hooks: [{
189
- type: "command",
190
- if: "Bash(vercel --prod*)",
191
- command: `${hd}/pre-deploy-gate.sh`,
192
- timeout: 120,
193
- statusMessage: "◆ Running quality gates...",
194
- }],
195
- },
196
- ],
197
- PreCompact: [
198
- {
199
- matcher: "compact",
200
- hooks: [{
201
- type: "command",
202
- command: `${hd}/pre-compact.sh`,
203
- timeout: 15,
204
- statusMessage: "◆ Saving state...",
205
- }],
206
- },
207
- ],
208
- SubagentStart: [
209
- {
210
- matcher: ".*",
211
- hooks: [{
212
- type: "command",
213
- command: 'echo \'{"additionalContext": "◆ Qualia agent spawned"}\'',
214
- }],
215
- },
216
- ],
217
- };
202
+ // ─── Configure settings.json ───────────────────────────
203
+ console.log("");
204
+ log(`${WHITE}Configuring settings.json...${RESET}`);
205
+
206
+ const settingsPath = path.join(CLAUDE_DIR, "settings.json");
207
+ let settings = {};
208
+ if (fs.existsSync(settingsPath)) {
209
+ try {
210
+ settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
211
+ } catch {}
212
+ }
213
+
214
+ // Env
215
+ if (!settings.env) settings.env = {};
216
+ Object.assign(settings.env, {
217
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
218
+ CLAUDE_CODE_DISABLE_AUTO_MEMORY: "0",
219
+ MAX_MCP_OUTPUT_TOKENS: "25000",
220
+ CLAUDE_CODE_NO_FLICKER: "1",
221
+ });
222
+
223
+ // Status line
224
+ settings.statusLine = {
225
+ type: "command",
226
+ command: "~/.claude/statusline.sh",
227
+ };
228
+
229
+ // Spinner
230
+ settings.spinnerVerbs = {
231
+ mode: "replace",
232
+ verbs: [
233
+ "Qualia-fying",
234
+ "Solution-ing",
235
+ "Teal-crafting",
236
+ "Vibe-forging",
237
+ "Shipping",
238
+ "Wiring",
239
+ "Polishing",
240
+ "Verifying",
241
+ "Orchestrating",
242
+ "Architecting",
243
+ "Deploying",
244
+ "Hardening",
245
+ ],
246
+ };
218
247
 
219
- // Permissions
220
- if (!settings.permissions) settings.permissions = {};
221
- if (!settings.permissions.allow) settings.permissions.allow = [];
222
- if (!settings.permissions.deny) {
223
- settings.permissions.deny = [
224
- "Read(./.env)",
225
- "Read(./.env.*)",
226
- "Read(./secrets/**)",
227
- ];
248
+ settings.spinnerTipsOverride = {
249
+ excludeDefault: true,
250
+ tips: [
251
+ "◆ Lost? Type /qualia for the next step",
252
+ "◆ Small fix? Use /qualia-quick to skip planning",
253
+ "◆ End of day? /qualia-report before you clock out",
254
+ "◆ Context isolation: every task gets a fresh AI brain",
255
+ "◆ The verifier doesn't trust claims — it greps the code",
256
+ "◆ Plans are prompts — the plan IS what the builder reads",
257
+ "◆ Feature branches only — never push to main",
258
+ "◆ Read before write — no exceptions",
259
+ "◆ MVP first — build what's asked, nothing extra",
260
+ "◆ tracking.json syncs to ERP on every push",
261
+ ],
262
+ };
263
+
264
+ // Hooks — full system
265
+ const hd = path.join(CLAUDE_DIR, "hooks");
266
+ settings.hooks = {
267
+ PreToolUse: [
268
+ {
269
+ matcher: "Bash",
270
+ hooks: [
271
+ {
272
+ type: "command",
273
+ if: "Bash(git push*)",
274
+ command: `${hd}/branch-guard.sh`,
275
+ timeout: 10,
276
+ statusMessage: "◆ Checking branch permissions...",
277
+ },
278
+ {
279
+ type: "command",
280
+ if: "Bash(git push*)",
281
+ command: `${hd}/pre-push.sh`,
282
+ timeout: 15,
283
+ statusMessage: "◆ Syncing tracking...",
284
+ },
285
+ ],
286
+ },
287
+ {
288
+ matcher: "Edit|Write",
289
+ hooks: [
290
+ {
291
+ type: "command",
292
+ if: "Edit(*.env*)|Write(*.env*)",
293
+ command: `${hd}/block-env-edit.sh`,
294
+ timeout: 5,
295
+ statusMessage: "◆ Checking file permissions...",
296
+ },
297
+ {
298
+ type: "command",
299
+ if: "Edit(*migration*)|Write(*migration*)|Edit(*.sql)|Write(*.sql)",
300
+ command: `${hd}/migration-guard.sh`,
301
+ timeout: 10,
302
+ statusMessage: "◆ Checking migration safety...",
303
+ },
304
+ ],
305
+ },
306
+ ],
307
+ PostToolUse: [
308
+ {
309
+ matcher: "Bash",
310
+ hooks: [
311
+ {
312
+ type: "command",
313
+ if: "Bash(vercel --prod*)",
314
+ command: `${hd}/pre-deploy-gate.sh`,
315
+ timeout: 120,
316
+ statusMessage: "◆ Running quality gates...",
317
+ },
318
+ ],
319
+ },
320
+ ],
321
+ PreCompact: [
322
+ {
323
+ matcher: "compact",
324
+ hooks: [
325
+ {
326
+ type: "command",
327
+ command: `${hd}/pre-compact.sh`,
328
+ timeout: 15,
329
+ statusMessage: "◆ Saving state...",
330
+ },
331
+ ],
332
+ },
333
+ ],
334
+ SubagentStart: [
335
+ {
336
+ matcher: ".*",
337
+ hooks: [
338
+ {
339
+ type: "command",
340
+ command:
341
+ 'echo \'{"additionalContext": "◆ Qualia agent spawned"}\'',
342
+ },
343
+ ],
344
+ },
345
+ ],
346
+ };
347
+
348
+ // Permissions
349
+ if (!settings.permissions) settings.permissions = {};
350
+ if (!settings.permissions.allow) settings.permissions.allow = [];
351
+ if (!settings.permissions.deny) {
352
+ settings.permissions.deny = [
353
+ "Read(./.env)",
354
+ "Read(./.env.*)",
355
+ "Read(./secrets/**)",
356
+ ];
357
+ }
358
+
359
+ settings.effortLevel = "high";
360
+
361
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
362
+
363
+ ok("Hooks: branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact");
364
+ ok("Status line + spinner configured");
365
+ ok("Environment variables + permissions");
366
+
367
+ // ─── Summary ───────────────────────────────────────────
368
+ console.log("");
369
+ console.log(`${TEAL} ◆ Installed ✓${RESET}`);
370
+ console.log(`${DIM} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
371
+ console.log(` ${WHITE}${member.name}${RESET} ${DIM}(${member.role})${RESET}`);
372
+ console.log(` Skills: ${WHITE}${skills.length}${RESET}`);
373
+ console.log(` Agents: ${WHITE}3${RESET} ${DIM}(planner, builder, verifier)${RESET}`);
374
+ console.log(` Hooks: ${WHITE}6${RESET} ${DIM}(branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact)${RESET}`);
375
+ console.log(` Rules: ${WHITE}3${RESET} ${DIM}(security, frontend, deployment)${RESET}`);
376
+ console.log(` Templates: ${WHITE}4${RESET}`);
377
+ console.log(` Status line: ${GREEN}✓${RESET}`);
378
+ console.log(` CLAUDE.md: ${GREEN}✓${RESET} ${DIM}(${member.role})${RESET}`);
379
+
380
+ if (errors > 0) {
381
+ console.log("");
382
+ console.log(` ${YELLOW}${errors} warning(s)${RESET} — check output above`);
383
+ }
384
+
385
+ console.log("");
386
+ console.log(` Restart Claude Code, then type ${TEAL}/qualia${RESET} in any project.`);
387
+ console.log("");
228
388
  }
229
389
 
230
- settings.effortLevel = "high";
231
-
232
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
233
- log(` ${GREEN}✓${RESET} hooks, status line, spinner, env vars`);
234
-
235
- // Done
236
- console.log("");
237
- console.log(`${TEAL} ◆ Installed ✓${RESET}`);
238
- console.log(`${DIM} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
239
- console.log(` Skills: ${WHITE}${skills.length}${RESET}`);
240
- console.log(` Agents: ${WHITE}3${RESET} ${DIM}(planner, builder, verifier)${RESET}`);
241
- console.log(` Hooks: ${WHITE}5${RESET} ${DIM}(branch-guard, env-block, deploy-gate, pre-compact, subagent-label)${RESET}`);
242
- console.log(` Rules: ${WHITE}3${RESET} ${DIM}(security, frontend, deployment)${RESET}`);
243
- console.log(` Templates: ${WHITE}4${RESET}`);
244
- console.log(` Status line: ${GREEN}✓${RESET}`);
245
- console.log(` CLAUDE.md: ${GREEN}✓${RESET} ${DIM}(user-level)${RESET}`);
246
- console.log("");
247
- console.log(` Restart Claude Code, then type ${TEAL}/qualia${RESET} in any project.`);
248
- console.log("");
390
+ main().catch((e) => {
391
+ console.error(`${RED} ✗ Installation failed: ${e.message}${RESET}`);
392
+ process.exit(1);
393
+ });
@@ -1,8 +1,11 @@
1
1
  #!/bin/bash
2
2
  # Prevent Claude from editing .env files
3
+ # Claude Code hooks receive JSON on stdin with tool_input.file_path
4
+
5
+ INPUT=$(cat)
6
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.command // ""' 2>/dev/null)
3
7
 
4
- FILE="$1"
5
8
  if [[ "$FILE" == *.env* ]] || [[ "$FILE" == *".env.local"* ]] || [[ "$FILE" == *".env.production"* ]]; then
6
9
  echo "BLOCKED: Cannot edit environment files. Ask Fawzi to update secrets."
7
- exit 1
10
+ exit 2
8
11
  fi
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Catch dangerous SQL patterns in migration files
3
+ # Runs as PreToolUse hook on Write/Edit of migration files
4
+
5
+ INPUT=$(cat)
6
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null)
7
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // ""' 2>/dev/null)
8
+
9
+ # Only check migration/SQL files
10
+ case "$FILE" in
11
+ *migration*|*migrate*|*.sql) ;;
12
+ *) exit 0 ;;
13
+ esac
14
+
15
+ ERRORS=""
16
+
17
+ # DROP TABLE without safeguards
18
+ if echo "$CONTENT" | grep -qi "DROP TABLE" && ! echo "$CONTENT" | grep -qi "IF EXISTS"; then
19
+ ERRORS="${ERRORS}\n ✗ DROP TABLE without IF EXISTS"
20
+ fi
21
+
22
+ # DELETE without WHERE
23
+ if echo "$CONTENT" | grep -qi "DELETE FROM" && ! echo "$CONTENT" | grep -qi "WHERE"; then
24
+ ERRORS="${ERRORS}\n ✗ DELETE FROM without WHERE clause"
25
+ fi
26
+
27
+ # TRUNCATE (almost always wrong in migrations)
28
+ if echo "$CONTENT" | grep -qi "TRUNCATE"; then
29
+ ERRORS="${ERRORS}\n ✗ TRUNCATE detected — are you sure?"
30
+ fi
31
+
32
+ # CREATE TABLE without RLS
33
+ if echo "$CONTENT" | grep -qi "CREATE TABLE" && ! echo "$CONTENT" | grep -qi "ENABLE ROW LEVEL SECURITY"; then
34
+ ERRORS="${ERRORS}\n ✗ CREATE TABLE without ENABLE ROW LEVEL SECURITY"
35
+ fi
36
+
37
+ if [ -n "$ERRORS" ]; then
38
+ echo "◆ Migration guard — dangerous patterns found:"
39
+ echo -e "$ERRORS"
40
+ echo ""
41
+ echo "Fix these before proceeding. If intentional, ask Fawzi to approve."
42
+ exit 2
43
+ fi
@@ -12,6 +12,24 @@ if [ -f "tsconfig.json" ]; then
12
12
  echo " ✓ TypeScript"
13
13
  fi
14
14
 
15
+ # Lint check
16
+ if [ -f "package.json" ] && grep -q '"lint"' package.json; then
17
+ if ! npm run lint 2>/dev/null; then
18
+ echo "BLOCKED: Lint errors. Fix before deploying."
19
+ exit 1
20
+ fi
21
+ echo " ✓ Lint"
22
+ fi
23
+
24
+ # Test check
25
+ if [ -f "package.json" ] && grep -q '"test"' package.json; then
26
+ if ! npm test 2>/dev/null; then
27
+ echo "BLOCKED: Tests failed. Fix before deploying."
28
+ exit 1
29
+ fi
30
+ echo " ✓ Tests"
31
+ fi
32
+
15
33
  # Build check
16
34
  if [ -f "package.json" ] && grep -q '"build"' package.json; then
17
35
  if ! npm run build 2>/dev/null; then
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "qualia-framework-v2",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Claude Code workflow framework by Qualia Solutions. Plan, build, verify, ship.",
5
5
  "bin": {
6
- "qualia-framework-v2": "./bin/install.js"
6
+ "qualia-framework-v2": "./bin/cli.js"
7
7
  },
8
8
  "keywords": [
9
9
  "claude-code",
@@ -31,8 +31,7 @@
31
31
  "templates/",
32
32
  "CLAUDE.md",
33
33
  "guide.md",
34
- "statusline.sh",
35
- "install.sh"
34
+ "statusline.sh"
36
35
  ],
37
36
  "engines": {
38
37
  "node": ">=18"
@@ -49,7 +49,7 @@ YOUR TASK:
49
49
  {paste the single task block from the plan — title, files, action, context refs, done-when}
50
50
 
51
51
  Execute this task. Read all @file references before writing. Commit when done.
52
- ", subagent_type="general-purpose", description="Task {N}: {title}")
52
+ ", subagent_type="qualia-builder", description="Task {N}: {title}")
53
53
  ```
54
54
 
55
55
  **After each task completes:**
@@ -48,7 +48,7 @@ Phase {N} success criteria: {criteria from STATE.md}
48
48
  {If --gaps: Also read @.planning/phase-{N}-verification.md for failures to fix}
49
49
 
50
50
  Create the plan file at .planning/phase-{N}-plan.md
51
- ", subagent_type="general-purpose", description="Plan phase {N}")
51
+ ", subagent_type="qualia-planner", description="Plan phase {N}")
52
52
  ```
53
53
 
54
54
  ### 3. Review Plan
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: qualia-task
3
+ description: "Build a single task — more structured than /qualia-quick, lighter than /qualia-build. Spawns a fresh builder agent for one focused task."
4
+ ---
5
+
6
+ # /qualia-task — Single Task Builder
7
+
8
+ Build one thing properly. Fresh builder context, atomic commit, but no phase plan needed.
9
+
10
+ ## Usage
11
+ `/qualia-task` — describe what to build interactively
12
+ `/qualia-task {description}` — build it directly
13
+
14
+ ## When to Use
15
+ - Too big for a quick fix, too small for a full phase
16
+ - Adding a single feature, component, API route, or integration
17
+ - Refactoring one module
18
+ - Building something specific someone asked for
19
+
20
+ ## Process
21
+
22
+ ### 1. Clarify
23
+
24
+ If no description provided, ask: **"What do you want to build?"**
25
+
26
+ Then use AskUserQuestion:
27
+
28
+ ```
29
+ question: "How complex is this task?"
30
+ header: "Scope"
31
+ options:
32
+ - label: "Small (30min-1hr)"
33
+ description: "Single file or component, straightforward implementation"
34
+ - label: "Medium (1-3hrs)"
35
+ description: "Multiple files, some integration work, needs testing"
36
+ - label: "Large (3hrs+)"
37
+ description: "Significant feature, multiple components, consider /qualia-plan instead"
38
+ ```
39
+
40
+ If "Large" — suggest `/qualia-plan` instead. Ask if they want to proceed anyway.
41
+
42
+ ### 2. Task Spec
43
+
44
+ Write a quick task spec (don't save to file, just confirm with user):
45
+
46
+ ```
47
+ ◆ QUALIA ► TASK
48
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
49
+
50
+ What: {what to build}
51
+ Files: {files to create/modify}
52
+ Done: {what "done" looks like}
53
+ ```
54
+
55
+ Ask: **"Good to build?"**
56
+
57
+ ### 3. Build
58
+
59
+ Spawn a builder agent with the task:
60
+
61
+ ```
62
+ Agent(subagent_type: "qualia-builder")
63
+
64
+ Task: {task description}
65
+ Files: {files to create/modify}
66
+ Done when: {completion criteria}
67
+
68
+ Context: Read PROJECT.md if it exists. Follow all rules (security, frontend, deployment).
69
+ ```
70
+
71
+ The builder runs in fresh context — reads before writing, follows rules, commits atomically.
72
+
73
+ ### 4. Verify
74
+
75
+ After the builder finishes:
76
+ - Run `npx tsc --noEmit` if TypeScript
77
+ - Quick smoke test if applicable
78
+ - Review what was built
79
+
80
+ ### 5. Report
81
+
82
+ ```
83
+ ◆ QUALIA ► TASK COMPLETE
84
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
85
+
86
+ Task {description}
87
+ Files {files changed}
88
+ Commit {commit hash}
89
+ Status ✓ Done
90
+ ```
91
+
92
+ Update `.planning/tracking.json` notes field if it exists.
@@ -41,7 +41,7 @@ Phase plan with success criteria:
41
41
  {@.planning/phase-{N}-verification.md}
42
42
 
43
43
  Verify this phase. Write report to .planning/phase-{N}-verification.md
44
- ", subagent_type="general-purpose", description="Verify phase {N}")
44
+ ", subagent_type="qualia-verifier", description="Verify phase {N}")
45
45
  ```
46
46
 
47
47
  ### 3. Present Results
package/install.sh DELETED
@@ -1,223 +0,0 @@
1
- #!/bin/bash
2
- # Qualia Framework v2 — Installer
3
- # Installs skills, agents, rules, hooks, templates, status line, and configures settings.json
4
-
5
- set -e
6
-
7
- FRAMEWORK_DIR="$(cd "$(dirname "$0")" && pwd)"
8
- CLAUDE_DIR="$HOME/.claude"
9
-
10
- echo "◆ Qualia Framework v2"
11
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
12
- echo " Installing to $CLAUDE_DIR"
13
- echo ""
14
-
15
- # Skills
16
- echo " Installing skills..."
17
- for skill_dir in "$FRAMEWORK_DIR"/skills/*/; do
18
- skill_name=$(basename "$skill_dir")
19
- mkdir -p "$CLAUDE_DIR/skills/$skill_name"
20
- cp "$skill_dir/SKILL.md" "$CLAUDE_DIR/skills/$skill_name/SKILL.md"
21
- echo " ✓ $skill_name"
22
- done
23
-
24
- # Agents
25
- echo " Installing agents..."
26
- mkdir -p "$CLAUDE_DIR/agents"
27
- for agent in "$FRAMEWORK_DIR"/agents/*.md; do
28
- cp "$agent" "$CLAUDE_DIR/agents/"
29
- echo " ✓ $(basename "$agent")"
30
- done
31
-
32
- # Rules
33
- echo " Installing rules..."
34
- mkdir -p "$CLAUDE_DIR/rules"
35
- for rule in "$FRAMEWORK_DIR"/rules/*.md; do
36
- cp "$rule" "$CLAUDE_DIR/rules/"
37
- echo " ✓ $(basename "$rule")"
38
- done
39
-
40
- # Hook scripts
41
- echo " Installing hook scripts..."
42
- mkdir -p "$CLAUDE_DIR/hooks"
43
- for hook in "$FRAMEWORK_DIR"/hooks/*.sh; do
44
- cp "$hook" "$CLAUDE_DIR/hooks/"
45
- chmod +x "$CLAUDE_DIR/hooks/$(basename "$hook")"
46
- echo " ✓ $(basename "$hook")"
47
- done
48
-
49
- # Status line
50
- echo " Installing status line..."
51
- cp "$FRAMEWORK_DIR/statusline.sh" "$CLAUDE_DIR/statusline.sh"
52
- chmod +x "$CLAUDE_DIR/statusline.sh"
53
- echo " ✓ statusline.sh"
54
-
55
- # Templates
56
- echo " Installing templates..."
57
- mkdir -p "$CLAUDE_DIR/qualia-templates"
58
- for tmpl in "$FRAMEWORK_DIR"/templates/*; do
59
- cp "$tmpl" "$CLAUDE_DIR/qualia-templates/"
60
- echo " ✓ $(basename "$tmpl")"
61
- done
62
-
63
- # CLAUDE.md (user-level — applies to all projects)
64
- echo " Installing CLAUDE.md..."
65
- cp "$FRAMEWORK_DIR/CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md"
66
- echo " ✓ CLAUDE.md"
67
-
68
- # Guide
69
- cp "$FRAMEWORK_DIR/guide.md" "$CLAUDE_DIR/qualia-guide.md"
70
- echo " ✓ guide.md"
71
-
72
- # Configure settings.json with hooks, status line, and env
73
- echo ""
74
- echo " Configuring settings.json..."
75
-
76
- python3 << 'PYEOF'
77
- import json, os
78
-
79
- settings_path = os.path.expanduser("~/.claude/settings.json")
80
-
81
- # Load existing settings or start fresh
82
- if os.path.exists(settings_path):
83
- with open(settings_path) as f:
84
- settings = json.load(f)
85
- else:
86
- settings = {}
87
-
88
- # Env vars
89
- settings.setdefault("env", {})
90
- settings["env"].update({
91
- "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
92
- "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "0",
93
- "MAX_MCP_OUTPUT_TOKENS": "25000",
94
- "CLAUDE_CODE_NO_FLICKER": "1"
95
- })
96
-
97
- # Status line — teal branded, 2-line
98
- settings["statusLine"] = {
99
- "type": "command",
100
- "command": "~/.claude/statusline.sh"
101
- }
102
-
103
- # Spinner verbs — Qualia branded
104
- settings["spinnerVerbs"] = {
105
- "mode": "replace",
106
- "verbs": [
107
- "Qualia-fying", "Solution-ing", "Teal-crafting", "Vibe-forging",
108
- "Shipping", "Wiring", "Polishing", "Verifying",
109
- "Orchestrating", "Architecting", "Deploying", "Hardening"
110
- ]
111
- }
112
-
113
- # Spinner tips — Qualia tips
114
- settings["spinnerTipsOverride"] = {
115
- "excludeDefault": True,
116
- "tips": [
117
- "◆ Lost? Type /qualia for the next step",
118
- "◆ Small fix? Use /qualia-quick to skip planning",
119
- "◆ End of day? /qualia-report before you clock out",
120
- "◆ Context isolation: every task gets a fresh AI brain",
121
- "◆ The verifier doesn't trust claims — it greps the code",
122
- "◆ Plans are prompts — the plan IS what the builder reads",
123
- "◆ Feature branches only — never push to main",
124
- "◆ Read before write — no exceptions",
125
- "◆ MVP first — build what's asked, nothing extra",
126
- "◆ tracking.json syncs to ERP on every push"
127
- ]
128
- }
129
-
130
- # Hooks
131
- hooks_dir = os.path.expanduser("~/.claude/hooks")
132
- settings["hooks"] = {
133
- "PreToolUse": [
134
- {
135
- "matcher": "Bash",
136
- "hooks": [{
137
- "type": "command",
138
- "if": "Bash(git push*)",
139
- "command": f"{hooks_dir}/branch-guard.sh",
140
- "timeout": 10,
141
- "statusMessage": "◆ Checking branch permissions..."
142
- }]
143
- },
144
- {
145
- "matcher": "Edit|Write",
146
- "hooks": [{
147
- "type": "command",
148
- "if": "Edit(*.env*)",
149
- "command": f"{hooks_dir}/block-env-edit.sh",
150
- "timeout": 5,
151
- "statusMessage": "◆ Checking file permissions..."
152
- }]
153
- }
154
- ],
155
- "PostToolUse": [
156
- {
157
- "matcher": "Bash",
158
- "hooks": [{
159
- "type": "command",
160
- "if": "Bash(vercel --prod*)",
161
- "command": f"{hooks_dir}/pre-deploy-gate.sh",
162
- "timeout": 120,
163
- "statusMessage": "◆ Running quality gates..."
164
- }]
165
- }
166
- ],
167
- "PreCompact": [
168
- {
169
- "matcher": "compact",
170
- "hooks": [{
171
- "type": "command",
172
- "command": f"{hooks_dir}/pre-compact.sh",
173
- "timeout": 15,
174
- "statusMessage": "◆ Saving state..."
175
- }]
176
- }
177
- ],
178
- "SubagentStart": [
179
- {
180
- "matcher": ".*",
181
- "hooks": [{
182
- "type": "command",
183
- "command": "echo '{\"additionalContext\": \"◆ Qualia agent spawned\"}'"
184
- }]
185
- }
186
- ]
187
- }
188
-
189
- # Permissions
190
- settings.setdefault("permissions", {})
191
- settings["permissions"].setdefault("allow", [])
192
- settings["permissions"].setdefault("deny", [
193
- "Read(./.env)",
194
- "Read(./.env.*)",
195
- "Read(./secrets/**)"
196
- ])
197
-
198
- # Effort level
199
- settings["effortLevel"] = "high"
200
-
201
- with open(settings_path, "w") as f:
202
- json.dump(settings, f, indent=2)
203
-
204
- print(" ✓ Hooks registered (branch-guard, env-block, deploy-gate, pre-compact, subagent-label)")
205
- print(" ✓ Status line configured (2-line teal branded)")
206
- print(" ✓ Spinner verbs: Qualia branded")
207
- print(" ✓ Spinner tips: Qualia tips")
208
- print(" ✓ Environment variables set")
209
- PYEOF
210
-
211
- echo ""
212
- echo "◆ Installed ✓"
213
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
214
- echo " Skills: 10"
215
- echo " Agents: 3 (planner, builder, verifier)"
216
- echo " Hooks: 4 (branch-guard, env-block, deploy-gate, pre-compact)"
217
- echo " Rules: 3 (security, frontend, deployment)"
218
- echo " Templates: 4"
219
- echo " Status line: ✓"
220
- echo " CLAUDE.md: ✓ (user-level)"
221
- echo ""
222
- echo " Restart Claude Code, then type /qualia in any project."
223
- echo ""