qualia-framework-v2 2.10.0 → 3.0.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/CLAUDE.md CHANGED
@@ -51,9 +51,7 @@ No accumulated garbage. No context rot.
51
51
  ## Quality Gates (always active)
52
52
  - **Frontend guard:** Read .planning/DESIGN.md before any frontend changes
53
53
  - **Deploy guard:** tsc + lint + build + tests must pass before deploy
54
- - **Branch guard:** Employees cannot push to main (OWNER can)
55
- - **Env guard:** Employees cannot edit .env files (OWNER can — add keys, configure secrets directly)
56
- - **Sudo guard:** Employees cannot run sudo (OWNER can)
54
+ - **Migration guard:** Catches dangerous SQL (DROP without IF EXISTS, DELETE without WHERE, CREATE TABLE without RLS)
57
55
  - **Intent verification:** Confirm before modifying 3+ files (OWNER: just do it)
58
56
 
59
57
  ## Tracking
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
- # Qualia Framework v2
1
+ # Qualia Framework v3
2
2
 
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.
3
+ A harness engineering 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
4
 
5
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.
6
6
 
7
+ v3 applies lessons from Anthropic's ["Harness Design for Long-Running Apps"](https://www.anthropic.com/engineering/harness-design-long-running-apps) article: scored evaluator rubrics, verification contracts, smarter guards, hook telemetry, and dynamic team management.
8
+
7
9
  ## Install
8
10
 
9
11
  ```bash
@@ -17,6 +19,9 @@ Enter your team code when prompted. Get your code from Fawzi.
17
19
  npx qualia-framework-v2 version # Check installed version + updates
18
20
  npx qualia-framework-v2 update # Update to latest (remembers your code)
19
21
  npx qualia-framework-v2 uninstall # Clean removal from ~/.claude/
22
+ npx qualia-framework-v2 team list # Show team members
23
+ npx qualia-framework-v2 team add # Add a team member
24
+ npx qualia-framework-v2 traces # View recent hook telemetry
20
25
  ```
21
26
 
22
27
  ## Usage
@@ -66,7 +71,7 @@ Works on **Windows 10/11, macOS, and Linux**. Requires Node.js 18+ and Claude Co
66
71
 
67
72
  ### Goal-Backward Verification
68
73
 
69
- 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.
74
+ Most CI checks "did the task run." Qualia checks "does the outcome actually work." The verifier scores on 4 dimensions (Correctness, Completeness, Wiring, Quality), each 1-5, with a hard threshold at 3. It doesn't trust summaries — it greps the codebase for stubs, placeholders, unwired imports. The planner generates verification contracts (testable commands) that the verifier executes before ad-hoc checks.
70
75
 
71
76
  ### Agent Separation
72
77
 
@@ -84,7 +89,7 @@ All 8 hooks are real ops engineering, not theoretical. Highlights:
84
89
 
85
90
  ### Enforced State Machine
86
91
 
87
- Every workflow step calls `state.js` — a Node.js state machine that validates preconditions, updates both STATE.md and tracking.json atomically, and tracks gap-closure cycles. You can't build without planning, can't verify without building, and can't loop on gap-closure more than twice before escalating.
92
+ Every workflow step calls `state.js` — a Node.js state machine that validates preconditions (including plan content), updates both STATE.md and tracking.json atomically, and tracks gap-closure cycles. The gap-closure limit is configurable per project (default: 2). A `--force` flag enables recovery after failed builds.
88
93
 
89
94
  ### Wave-Based Parallelization
90
95
 
@@ -106,10 +111,10 @@ npx qualia-framework-v2 install
106
111
  ├── hooks/ 8 Node.js hooks — cross-platform (no bash dependency)
107
112
  ├── bin/ state.js (state machine) + qualia-ui.js (cosmetics library)
108
113
  ├── knowledge/ learned-patterns.md, common-fixes.md, client-prefs.md (loaded by plan/debug/new)
109
- ├── rules/ security.md, frontend.md, deployment.md
114
+ ├── rules/ security.md, frontend.md, design-reference.md, deployment.md
110
115
  ├── qualia-templates/ tracking.json, state.md, project.md, plan.md, DESIGN.md
111
116
  ├── CLAUDE.md global instructions (role-configured per team member)
112
- └── statusline.sh teal-branded 2-line status bar
117
+ └── statusline.js teal-branded 2-line status bar
113
118
  ```
114
119
 
115
120
  ## For Qualia Solutions Team
package/agents/planner.md CHANGED
@@ -91,6 +91,58 @@ Your training data is often stale. A two-second lookup is cheaper than a wrong t
91
91
 
92
92
  **Self-check:** Before returning the plan, verify every task has specific file paths, concrete actions, and testable done-when criteria. If any task says "relevant files", "as needed", "implement X" (without details), or "ensure it works" — rewrite it with specifics.
93
93
 
94
+ ## Verification Contracts
95
+
96
+ Every plan MUST include a `## Verification Contract` section after `## Success Criteria`. Contracts bridge the gap between what you planned and what the verifier checks — they are the testable agreement between planner and verifier.
97
+
98
+ ### Contract Format
99
+
100
+ For each task, generate at least one contract entry:
101
+
102
+ ```markdown
103
+ ## Verification Contract
104
+
105
+ ### Contract for Task 1 — {title}
106
+ **Check type:** file-exists
107
+ **Command:** `test -f src/lib/auth.ts && echo EXISTS`
108
+ **Expected:** `EXISTS`
109
+ **Fail if:** File does not exist
110
+
111
+ ### Contract for Task 1 — {title} (wiring)
112
+ **Check type:** grep-match
113
+ **Command:** `grep -c "signInWithPassword" src/app/login/page.tsx`
114
+ **Expected:** Non-zero (≥ 1)
115
+ **Fail if:** Returns 0 — function exists in lib but isn't called from the login page
116
+
117
+ ### Contract for Task 2 — {title}
118
+ **Check type:** command-exit
119
+ **Command:** `npx tsc --noEmit 2>&1 | grep -c "error TS"`
120
+ **Expected:** `0`
121
+ **Fail if:** Any TypeScript compilation errors
122
+
123
+ ### Contract for Task 3 — {title}
124
+ **Check type:** behavioral
125
+ **Command:** (manual verification by verifier)
126
+ **Expected:** User can log in with email/password and see the dashboard
127
+ **Fail if:** Login form submits but no redirect occurs, or dashboard shows empty state
128
+ ```
129
+
130
+ ### Contract Types
131
+
132
+ | Type | When to use | Verifier action |
133
+ |------|-------------|-----------------|
134
+ | `file-exists` | A file must be created | Run the command, check output |
135
+ | `grep-match` | A function/import/pattern must appear in code | Run grep, check count > 0 |
136
+ | `command-exit` | A tool must exit cleanly (tsc, lint, test) | Run command, check exit code or output |
137
+ | `behavioral` | A user-facing flow must work | Verifier tests manually or via browser QA |
138
+
139
+ ### Rules for Contracts
140
+
141
+ 1. **Every task gets at least one contract.** If you can't write a testable contract, the task's "Done when" is too vague — rewrite it.
142
+ 2. **Contracts must be copy-pasteable.** The verifier runs them verbatim. No placeholders, no `{variable}` — use actual file paths.
143
+ 3. **Include wiring contracts.** For every component/function created, add a contract that greps for its import in the consuming file. This catches the #1 failure mode: code that exists but isn't connected.
144
+ 4. **Behavioral contracts are last resort.** Prefer grep-match and command-exit — they're deterministic. Use behavioral only for user-facing flows that can't be verified by grep.
145
+
94
146
  ## Design-Aware Planning
95
147
 
96
148
  When a phase involves frontend work (pages, components, layouts, UI):
@@ -40,10 +40,38 @@ For each success criterion in the plan:
40
40
  - Are database queries returning data to the UI?
41
41
  - This is where stubs hide.
42
42
 
43
+ ## Contract-Based Verification
44
+
45
+ If the phase plan contains a `## Verification Contract` section, execute those contracts FIRST before any ad-hoc verification.
46
+
47
+ ### How Contracts Work
48
+
49
+ The planner generates testable contracts for each task. Each contract is a specific check you run verbatim:
50
+
51
+ ```markdown
52
+ ### Contract for Task 1 — {title}
53
+ **Check type:** file-exists | grep-match | command-exit | behavioral
54
+ **Command:** {exact command to run}
55
+ **Expected:** {what the output should be}
56
+ **Fail if:** {what constitutes failure}
57
+ ```
58
+
59
+ ### Contract Execution
60
+
61
+ 1. Read the `## Verification Contract` section from the plan file
62
+ 2. For each contract entry, run the **Command** exactly as written
63
+ 3. Compare output against **Expected**
64
+ 4. Score: PASS if output matches expected, FAIL if it matches the fail condition
65
+ 5. Record results in the report under `## Contract Results`
66
+
67
+ Contracts take priority over ad-hoc verification. If a contract covers a success criterion, use the contract result. Only fall back to the 3-level check (Truths → Artifacts → Wiring) for criteria NOT covered by contracts.
68
+
69
+ If the plan has no `## Verification Contract` section (older plans), skip this step and proceed with the full 3-level check below.
70
+
43
71
  ## How to Verify
44
72
 
45
73
  ### 1. Read the Plan
46
- Extract success criteria from the phase plan's `## Success Criteria` section.
74
+ Extract success criteria from the phase plan's `## Success Criteria` section. Also extract the `## Verification Contract` if present.
47
75
 
48
76
  ### 2. For Each Criterion, Run the 3-Level Check
49
77
 
@@ -111,12 +139,21 @@ gaps: {count of failures}
111
139
 
112
140
  # Phase {N} Verification
113
141
 
114
- ## Results
142
+ ## Contract Results (if contracts exist in plan)
143
+
144
+ | Task | Check | Command | Result | Notes |
145
+ |------|-------|---------|--------|-------|
146
+ | Task 1 | file-exists | `test -f src/lib/auth.ts` | PASS | File exists, 142 lines |
147
+ | Task 2 | grep-match | `grep -c "signIn" src/lib/auth.ts` | PASS | 3 matches |
115
148
 
116
- | Criterion | Status | Evidence |
117
- |-----------|--------|----------|
118
- | {criterion 1} | PASS | {what you found} |
119
- | {criterion 2} | FAIL | {what's wrong} |
149
+ ## Scores
150
+
151
+ | Criterion | Correctness | Completeness | Wiring | Quality | Verdict |
152
+ |-----------|-------------|--------------|--------|---------|---------|
153
+ | {criterion 1} | {1-5} | {1-5} | {1-5} | {1-5} | PASS/FAIL |
154
+ | {criterion 2} | {1-5} | {1-5} | {1-5} | {1-5} | PASS/FAIL |
155
+
156
+ **Minimum threshold check:** {any score < 3? If YES → FAIL}
120
157
 
121
158
  ## Code Quality
122
159
  - TypeScript: PASS/FAIL
@@ -125,28 +162,92 @@ gaps: {count of failures}
125
162
  - Unused imports: {count}
126
163
 
127
164
  ## Gaps (if any)
128
- 1. {what failed and why}
129
- 2. {what failed and why}
165
+ 1. {criterion}: {dimension} scored {score} — {what's wrong, what file, what's needed}
166
+ 2. {criterion}: {dimension} scored {score} — {what's wrong}
130
167
 
131
168
  ## Verdict
132
- PASS — Phase {N} goal achieved. Proceed to Phase {N+1}.
169
+ PASS — Phase {N} goal achieved. All criteria scored ≥ 3 on all dimensions. Proceed to Phase {N+1}.
133
170
  OR
134
- FAIL — {N} gaps found. Run `/qualia-plan {N} --gaps` to fix.
171
+ FAIL — {N} gaps found. {N} criteria scored below threshold. Run `/qualia-plan {N} --gaps` to fix.
135
172
  ```
136
173
 
137
- ## Scoring
174
+ ## Scoring Rubric
175
+
176
+ Every success criterion is scored on 4 dimensions, each rated 1-5:
177
+
178
+ ### Correctness (1-5)
179
+ Does it produce the right output?
180
+ - **1** — Crashes, errors, or wrong output
181
+ - **2** — Works for the happy path only; any deviation breaks it
182
+ - **3** — Handles common edge cases (empty input, missing data, basic validation)
183
+ - **4** — Handles most edge cases; error messages are user-friendly
184
+ - **5** — Comprehensive error handling; graceful degradation; defensive coding
185
+
186
+ ### Completeness (1-5)
187
+ Were all contracted requirements met?
188
+ - **1** — Less than half of the requirements implemented
189
+ - **2** — Over half done, but significant gaps remain
190
+ - **3** — All requirements present, but some are partial (e.g., UI exists but missing states)
191
+ - **4** — All requirements fully implemented as specified
192
+ - **5** — All requirements plus defensive coding, edge case coverage, and polish
193
+
194
+ ### Wiring (1-5)
195
+ Is everything connected end-to-end?
196
+ - **1** — Files exist but are not imported anywhere
197
+ - **2** — Imported but never called (dead code)
198
+ - **3** — Called, but data flow is incomplete (e.g., API route exists, component calls it, but response isn't rendered)
199
+ - **4** — Full data flow with minor gaps (e.g., loading state missing, error not surfaced)
200
+ - **5** — Complete wiring verified by grep — every export is imported, every API is consumed, every component is rendered
201
+
202
+ ### Quality (1-5)
203
+ Code quality, security, accessibility?
204
+ - **1** — Stubs and placeholders throughout; `// TODO` everywhere
205
+ - **2** — Works but violates project conventions (wrong patterns, hardcoded values, no types)
206
+ - **3** — Follows conventions with minor issues (a few missing types, inconsistent naming)
207
+ - **4** — Clean code; good patterns; types complete; security rules followed
208
+ - **5** — Exemplary — accessible, performant, secure, well-structured, follows all rules
209
+
210
+ ### Hard Threshold
211
+
212
+ **Any criterion scoring below 3 triggers FAIL regardless of other scores.**
213
+
214
+ A component with Correctness=5, Completeness=5, Wiring=1, Quality=5 is FAIL — it's perfect code that nobody can use because it's not connected.
215
+
216
+ ### Phase Verdict
217
+ - **ALL criteria ≥ 3 on all dimensions** → PASS. Phase verified.
218
+ - **ANY criterion < 3 on ANY dimension** → FAIL. List each gap with: what scored low, what file, what's needed. Suggest `/qualia-plan {N} --gaps`.
219
+
220
+ Never round up. A 2 is not a 3. The goal of verification is to catch the work that LOOKS done but ISN'T.
221
+
222
+ ## Few-Shot Calibration
223
+
224
+ Use these examples to calibrate your judgment. Real verification should match this level of rigor.
225
+
226
+ ### Example A: PASS — Auth Phase
227
+
228
+ Phase goal: "User can sign up, log in, and access protected routes."
229
+
230
+ | Criterion | Score | Evidence |
231
+ |-----------|-------|----------|
232
+ | Correctness | 4 | `signInWithPassword()` called in handler; session persists across refresh; invalid credentials show error; tested login→dashboard→logout→login flow |
233
+ | Completeness | 4 | Sign up, login, logout, protected route redirect all implemented; password validation with Zod; email verification flow present |
234
+ | Wiring | 5 | `grep -r "signInWithPassword" src/` shows call in `app/login/page.tsx`; `grep -r "createClient" src/lib/` shows server client used in middleware; `grep -r "auth.uid" supabase/` shows RLS policies reference auth |
235
+ | Quality | 4 | Server-side auth only; RLS on all tables; Zod validation on inputs; no service_role in client code; semantic HTML on forms; visible focus rings on inputs |
236
+
237
+ **Verdict: PASS** — All scores ≥ 3. Minimum threshold check: NO scores below 3.
138
238
 
139
- Each success criterion from the plan gets a verdict:
239
+ ### Example B: FAIL Chat Component Phase
140
240
 
141
- - **PASS** All 3 levels check out. File exists, has real implementation (not stubs), and is imported/used by the system.
142
- - **PARTIAL** — File exists and has real code, but isn't fully wired (e.g., component exists but isn't rendered in any page, API route exists but no client calls it). This is NOT a pass.
143
- - **FAIL** — File missing, is a stub, or has 0 connections to the rest of the codebase.
241
+ Phase goal: "Working real-time chat interface with message history."
144
242
 
145
- Phase verdict:
146
- - **ALL PASS** → Phase verified. Update STATE.md status to "verified".
147
- - **ANY PARTIAL or FAIL** Phase has gaps. List each gap with: what's wrong, what file, what's needed. Suggest `/qualia-plan {N} --gaps`.
243
+ | Criterion | Score | Evidence |
244
+ |-----------|-------|----------|
245
+ | Correctness | 4 | Chat component renders messages correctly; timestamps formatted; scroll-to-bottom works |
246
+ | Completeness | 3 | Message send, receive, history all present; emoji support missing but not in spec |
247
+ | Wiring | 1 | `grep -r "ChatWindow" app/` returns 0 results — component exists at `components/chat/ChatWindow.tsx` but is NOT rendered in any page. `grep -r "from.*chat" app/` returns 0. The component is an island. |
248
+ | Quality | 3 | Clean code; types present; but no loading state, no error state, no empty state |
148
249
 
149
- Never round up. A PARTIAL is not a PASS. The goal of verification is to catch the work that LOOKS done but ISN'T.
250
+ **Verdict: FAIL** Wiring scored 1 (below threshold of 3). The chat component is well-built code that nobody can access because it's not mounted in any route. This is the exact kind of "looks done but isn't" that verification exists to catch.
150
251
 
151
252
  ## Design Verification (for phases with frontend work)
152
253
 
package/bin/cli.js CHANGED
@@ -332,8 +332,12 @@ async function cmdUninstall() {
332
332
  safeUnlink(path.join(CLAUDE_DIR, ".qualia-config.json"), counters);
333
333
  safeUnlink(path.join(CLAUDE_DIR, ".qualia-last-update-check"), counters);
334
334
  safeUnlink(path.join(CLAUDE_DIR, ".erp-api-key"), counters);
335
+ safeUnlink(path.join(CLAUDE_DIR, ".qualia-team.json"), counters);
335
336
  safeUnlink(path.join(CLAUDE_DIR, "qualia-guide.md"), counters);
336
337
 
338
+ // Traces directory.
339
+ safeRmDir(path.join(CLAUDE_DIR, ".qualia-traces"), counters);
340
+
337
341
  // Clean settings.json surgically.
338
342
  cleanSettingsJson(counters);
339
343
 
@@ -367,6 +371,143 @@ async function cmdUninstall() {
367
371
  console.log("");
368
372
  }
369
373
 
374
+ // ─── Team Management ────────────────────────────────────
375
+ // External team file at ~/.claude/.qualia-team.json.
376
+ // Falls back to embedded defaults in install.js.
377
+
378
+ function getDefaultTeam() {
379
+ return {
380
+ "QS-FAWZI-01": { name: "Fawzi Goussous", role: "OWNER", description: "Company owner. Full access. Can push to main, approve deploys, edit secrets." },
381
+ "QS-HASAN-02": { name: "Hasan", role: "EMPLOYEE", description: "Developer. Feature branches only. Cannot push to main or edit .env files." },
382
+ "QS-MOAYAD-03": { name: "Moayad", role: "EMPLOYEE", description: "Developer. Feature branches only. Cannot push to main or edit .env files." },
383
+ "QS-RAMA-04": { name: "Rama", role: "EMPLOYEE", description: "Developer. Feature branches only. Cannot push to main or edit .env files." },
384
+ "QS-SALLY-05": { name: "Sally", role: "EMPLOYEE", description: "Developer. Feature branches only. Cannot push to main or edit .env files." },
385
+ };
386
+ }
387
+
388
+ function readTeamFile() {
389
+ const teamFile = path.join(CLAUDE_DIR, ".qualia-team.json");
390
+ try {
391
+ if (fs.existsSync(teamFile)) {
392
+ const data = JSON.parse(fs.readFileSync(teamFile, "utf8"));
393
+ if (data && typeof data === "object" && Object.keys(data).length > 0) return data;
394
+ }
395
+ } catch {}
396
+ return null;
397
+ }
398
+
399
+ function writeTeamFile(team) {
400
+ if (!fs.existsSync(CLAUDE_DIR)) fs.mkdirSync(CLAUDE_DIR, { recursive: true });
401
+ fs.writeFileSync(path.join(CLAUDE_DIR, ".qualia-team.json"), JSON.stringify(team, null, 2) + "\n");
402
+ }
403
+
404
+ function parseTeamArgs(argv) {
405
+ const args = {};
406
+ for (let i = 0; i < argv.length; i++) {
407
+ if (argv[i] === "--code" && argv[i + 1]) { args.code = argv[++i]; }
408
+ else if (argv[i] === "--name" && argv[i + 1]) { args.name = argv[++i]; }
409
+ else if (argv[i] === "--role" && argv[i + 1]) { args.role = argv[++i].toUpperCase(); }
410
+ }
411
+ return args;
412
+ }
413
+
414
+ function cmdTeam() {
415
+ const sub = process.argv[3];
416
+
417
+ switch (sub) {
418
+ case "list": {
419
+ banner();
420
+ console.log("");
421
+ const team = readTeamFile();
422
+ const source = team || getDefaultTeam();
423
+ const label = team ? "team file" : "embedded defaults";
424
+ console.log(` ${DIM}Source: ${label}${RESET}`);
425
+ console.log("");
426
+ for (const [code, member] of Object.entries(source)) {
427
+ const roleColor = member.role === "OWNER" ? TEAL : WHITE;
428
+ console.log(` ${WHITE}${code}${RESET} ${roleColor}${member.role}${RESET} ${DIM}${member.name}${RESET}`);
429
+ }
430
+ console.log("");
431
+ break;
432
+ }
433
+
434
+ case "add": {
435
+ const args = parseTeamArgs(process.argv.slice(4));
436
+ if (!args.code || !args.name) {
437
+ console.log(` ${RED}Usage:${RESET} qualia-framework-v2 team add --code QS-NAME-NN --name "Full Name" [--role EMPLOYEE|OWNER]`);
438
+ process.exit(1);
439
+ }
440
+ const team = readTeamFile() || getDefaultTeam();
441
+ const code = args.code.toUpperCase();
442
+ const role = args.role || "EMPLOYEE";
443
+ team[code] = {
444
+ name: args.name,
445
+ role,
446
+ description: role === "OWNER"
447
+ ? "Company owner. Full access. Can push to main, approve deploys, edit secrets."
448
+ : "Developer. Feature branches only. Cannot push to main or edit .env files.",
449
+ };
450
+ writeTeamFile(team);
451
+ console.log(` ${GREEN}+${RESET} ${WHITE}${code}${RESET} ${DIM}(${args.name}, ${role})${RESET}`);
452
+ break;
453
+ }
454
+
455
+ case "remove": {
456
+ const code = (process.argv[4] || "").toUpperCase();
457
+ if (!code) {
458
+ console.log(` ${RED}Usage:${RESET} qualia-framework-v2 team remove QS-NAME-NN`);
459
+ process.exit(1);
460
+ }
461
+ const team = readTeamFile();
462
+ if (!team || !team[code]) {
463
+ console.log(` ${YELLOW}!${RESET} ${code} not found in team file.`);
464
+ process.exit(1);
465
+ }
466
+ delete team[code];
467
+ writeTeamFile(team);
468
+ console.log(` ${RED}-${RESET} ${WHITE}${code}${RESET} removed`);
469
+ break;
470
+ }
471
+
472
+ default:
473
+ console.log(` ${RED}Usage:${RESET} qualia-framework-v2 team <list|add|remove>`);
474
+ process.exit(1);
475
+ }
476
+ }
477
+
478
+ // ─── Traces ─────────────────────────────────────────────
479
+
480
+ function cmdTraces() {
481
+ banner();
482
+ console.log("");
483
+ const tracesDir = path.join(CLAUDE_DIR, ".qualia-traces");
484
+ if (!fs.existsSync(tracesDir)) {
485
+ console.log(` ${DIM}No traces found. Traces are written by hooks during normal operation.${RESET}`);
486
+ console.log("");
487
+ return;
488
+ }
489
+ const files = fs.readdirSync(tracesDir).filter((f) => f.endsWith(".jsonl")).sort().reverse();
490
+ if (files.length === 0) {
491
+ console.log(` ${DIM}No trace files found.${RESET}`);
492
+ console.log("");
493
+ return;
494
+ }
495
+ const latest = path.join(tracesDir, files[0]);
496
+ const lines = fs.readFileSync(latest, "utf8").trim().split("\n").slice(-20);
497
+ console.log(` ${WHITE}Recent traces${RESET} ${DIM}(${files[0]})${RESET}`);
498
+ console.log("");
499
+ for (const line of lines) {
500
+ try {
501
+ const e = JSON.parse(line);
502
+ const color = e.result === "block" ? RED : e.result === "allow" ? GREEN : DIM;
503
+ const time = (e.timestamp || "").split("T")[1] || "";
504
+ const ts = time.split(".")[0] || "";
505
+ console.log(` ${DIM}${ts}${RESET} ${color}${e.result}${RESET} ${WHITE}${e.hook}${RESET} ${DIM}${e.duration_ms || 0}ms${RESET}`);
506
+ } catch {}
507
+ }
508
+ console.log("");
509
+ }
510
+
370
511
  function cmdHelp() {
371
512
  banner();
372
513
  console.log("");
@@ -375,6 +516,8 @@ function cmdHelp() {
375
516
  console.log(` npx qualia-framework-v2 ${TEAL}update${RESET} Update to the latest version`);
376
517
  console.log(` npx qualia-framework-v2 ${TEAL}version${RESET} Show installed version + check for updates`);
377
518
  console.log(` npx qualia-framework-v2 ${TEAL}uninstall${RESET} Clean removal from ~/.claude/ (${DIM}-y to skip prompts${RESET})`);
519
+ console.log(` npx qualia-framework-v2 ${TEAL}team${RESET} Manage team members (${DIM}list|add|remove${RESET})`);
520
+ console.log(` npx qualia-framework-v2 ${TEAL}traces${RESET} View recent hook telemetry`);
378
521
  console.log("");
379
522
  console.log(` ${WHITE}After install:${RESET}`);
380
523
  console.log(` ${TG}/qualia${RESET} What should I do next?`);
@@ -413,6 +556,12 @@ switch (cmd) {
413
556
  process.exit(1);
414
557
  });
415
558
  break;
559
+ case "team":
560
+ cmdTeam();
561
+ break;
562
+ case "traces":
563
+ cmdTraces();
564
+ break;
416
565
  default:
417
566
  cmdHelp();
418
567
  }
package/bin/install.js CHANGED
@@ -13,8 +13,11 @@ const YELLOW = "\x1b[38;2;234;179;8m";
13
13
  const RED = "\x1b[38;2;239;68;68m";
14
14
  const RESET = "\x1b[0m";
15
15
 
16
+ const CLAUDE_DIR = path.join(require("os").homedir(), ".claude");
17
+ const FRAMEWORK_DIR = path.resolve(__dirname, "..");
18
+
16
19
  // ─── Team codes ──────────────────────────────────────────
17
- const TEAM = {
20
+ const DEFAULT_TEAM = {
18
21
  "QS-FAWZI-01": {
19
22
  name: "Fawzi Goussous",
20
23
  role: "OWNER",
@@ -42,8 +45,21 @@ const TEAM = {
42
45
  },
43
46
  };
44
47
 
45
- const CLAUDE_DIR = path.join(require("os").homedir(), ".claude");
46
- const FRAMEWORK_DIR = path.resolve(__dirname, "..");
48
+ // Load team from external file, fall back to embedded defaults.
49
+ function loadTeam() {
50
+ const teamFile = path.join(CLAUDE_DIR, ".qualia-team.json");
51
+ try {
52
+ if (fs.existsSync(teamFile)) {
53
+ const external = JSON.parse(fs.readFileSync(teamFile, "utf8"));
54
+ if (external && typeof external === "object" && Object.keys(external).length > 0) {
55
+ return external;
56
+ }
57
+ }
58
+ } catch {}
59
+ return DEFAULT_TEAM;
60
+ }
61
+
62
+ const TEAM = loadTeam();
47
63
 
48
64
  let installed = 0;
49
65
  let errors = 0;
@@ -185,16 +201,6 @@ async function main() {
185
201
  }
186
202
  }
187
203
 
188
- // ─── Status line ───────────────────────────────────────
189
- log(`${WHITE}Status line${RESET}`);
190
- try {
191
- const slDest = path.join(CLAUDE_DIR, "bin", "statusline.js");
192
- copy(path.join(FRAMEWORK_DIR, "bin", "statusline.js"), slDest);
193
- ok("statusline.js");
194
- } catch (e) {
195
- warn(`statusline.js — ${e.message}`);
196
- }
197
-
198
204
  // ─── Templates ─────────────────────────────────────────
199
205
  log(`${WHITE}Templates${RESET}`);
200
206
  const tmplDir = path.join(FRAMEWORK_DIR, "templates");
@@ -363,6 +369,11 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
363
369
  role: member.role,
364
370
  version: require("../package.json").version,
365
371
  installed_at: new Date().toISOString().split("T")[0],
372
+ erp: {
373
+ enabled: true,
374
+ url: "https://portal.qualiasolutions.net",
375
+ api_key_file: ".erp-api-key",
376
+ },
366
377
  };
367
378
  fs.writeFileSync(configFile, JSON.stringify(config, null, 2) + "\n");
368
379
 
@@ -469,7 +480,7 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
469
480
  type: "command",
470
481
  if: "Bash(git push*)",
471
482
  command: nodeCmd("branch-guard.js"),
472
- timeout: 10,
483
+ timeout: 5,
473
484
  statusMessage: "⬢ Checking branch permissions...",
474
485
  },
475
486
  {
@@ -493,7 +504,6 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
493
504
  hooks: [
494
505
  {
495
506
  type: "command",
496
- if: "Edit(*.env*)|Write(*.env*)",
497
507
  command: nodeCmd("block-env-edit.js"),
498
508
  timeout: 5,
499
509
  statusMessage: "⬢ Checking file permissions...",
@@ -523,20 +533,15 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
523
533
  ],
524
534
  };
525
535
 
526
- // Permissions
536
+ // Permissions — no restrictions on env files or branches.
537
+ // Everyone can read/write .env, push to main.
527
538
  if (!settings.permissions) settings.permissions = {};
528
539
  if (!settings.permissions.allow) settings.permissions.allow = [];
529
- if (!settings.permissions.deny) {
530
- settings.permissions.deny = [
531
- "Read(./.env)",
532
- "Read(./.env.*)",
533
- "Read(./secrets/**)",
534
- ];
535
- }
540
+ if (!settings.permissions.deny) settings.permissions.deny = [];
536
541
 
537
542
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
538
543
 
539
- ok("Hooks: session-start, auto-update, branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact");
544
+ ok("Hooks: session-start, auto-update, branch-guard, pre-push, block-env-edit, migration-guard, deploy-gate, pre-compact");
540
545
  ok("Status line + spinner configured");
541
546
  ok("Environment variables + permissions");
542
547
 
@@ -548,7 +553,7 @@ Client-specific preferences, design choices, and requirements. Loaded by \`/qual
548
553
  console.log(` Skills: ${WHITE}${skills.length}${RESET}`);
549
554
  const agentCount = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')).length;
550
555
  console.log(` Agents: ${WHITE}${agentCount}${RESET} ${DIM}(planner, builder, verifier, qa-browser)${RESET}`);
551
- console.log(` Hooks: ${WHITE}8${RESET} ${DIM}(session-start, auto-update, branch-guard, pre-push, env-block, migration-guard, deploy-gate, pre-compact)${RESET}`);
556
+ console.log(` Hooks: ${WHITE}8${RESET} ${DIM}(session-start, auto-update, branch-guard, pre-push, block-env-edit, migration-guard, deploy-gate, pre-compact)${RESET}`);
552
557
  console.log(` Rules: ${WHITE}${fs.readdirSync(rulesDir).length}${RESET} ${DIM}(security, frontend, design-reference, deployment)${RESET}`);
553
558
  console.log(` Scripts: ${WHITE}3${RESET} ${DIM}(state.js, qualia-ui.js, statusline.js)${RESET}`);
554
559
  console.log(` Knowledge: ${WHITE}3${RESET} ${DIM}(patterns, fixes, client prefs)${RESET}`);
package/bin/qualia-ui.js CHANGED
@@ -159,7 +159,7 @@ function cmdBanner(action, phase, subtitle) {
159
159
  const bar = progressBar(state.phase, state.total_phases);
160
160
  if (bar) console.log(` ${pad(DIM + "Progress" + RESET, 20)}${bar}`);
161
161
  if (state.gap_cycles > 0) {
162
- console.log(` ${pad(DIM + "Gap cycles" + RESET, 20)}${YELLOW}${state.gap_cycles}/2${RESET}`);
162
+ console.log(` ${pad(DIM + "Gap cycles" + RESET, 20)}${YELLOW}${state.gap_cycles}/${state.gap_cycle_limit || 2}${RESET}`);
163
163
  }
164
164
  }
165
165