qualia-framework 2.6.0 → 3.2.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 +64 -0
- package/README.md +103 -30
- package/agents/builder.md +110 -0
- package/agents/planner.md +134 -0
- package/agents/qa-browser.md +186 -0
- package/agents/verifier.md +221 -0
- package/bin/cli.js +336 -531
- package/bin/install.js +570 -0
- package/bin/qualia-ui.js +299 -0
- package/bin/state.js +630 -0
- package/bin/statusline.js +252 -0
- package/guide.md +63 -0
- package/hooks/auto-update.js +139 -0
- package/hooks/branch-guard.js +47 -0
- package/hooks/migration-guard.js +60 -0
- package/hooks/pre-compact.js +32 -0
- package/hooks/pre-deploy-gate.js +110 -0
- package/hooks/pre-push.js +33 -0
- package/hooks/session-start.js +170 -0
- package/package.json +29 -20
- package/rules/design-reference.md +179 -0
- package/rules/frontend.md +126 -0
- package/skills/qualia/SKILL.md +87 -0
- package/skills/qualia-build/SKILL.md +97 -0
- package/skills/qualia-debug/SKILL.md +87 -0
- package/skills/qualia-design/SKILL.md +93 -0
- package/skills/qualia-handoff/SKILL.md +66 -0
- package/skills/qualia-idk/SKILL.md +8 -0
- package/skills/qualia-learn/SKILL.md +88 -0
- package/skills/qualia-new/SKILL.md +323 -0
- package/{framework/skills → skills}/qualia-optimize/SKILL.md +1 -1
- package/skills/qualia-pause/SKILL.md +63 -0
- package/skills/qualia-plan/SKILL.md +101 -0
- package/skills/qualia-polish/SKILL.md +157 -0
- package/skills/qualia-quick/SKILL.md +37 -0
- package/skills/qualia-report/SKILL.md +105 -0
- package/skills/qualia-resume/SKILL.md +49 -0
- package/skills/qualia-review/SKILL.md +76 -0
- package/skills/qualia-ship/SKILL.md +90 -0
- package/skills/qualia-skill-new/SKILL.md +167 -0
- package/skills/qualia-task/SKILL.md +91 -0
- package/skills/qualia-verify/SKILL.md +113 -0
- package/templates/DESIGN.md +137 -0
- package/templates/plan.md +28 -0
- package/templates/project.md +22 -0
- package/templates/state.md +27 -0
- package/templates/tracking.json +20 -0
- package/tests/bin.test.sh +673 -0
- package/tests/hooks.test.sh +315 -0
- package/tests/state.test.sh +535 -0
- package/tests/statusline.test.sh +243 -0
- package/bin/collect-metrics.sh +0 -62
- package/framework/.claudeignore +0 -51
- package/framework/CLAUDE.md +0 -51
- package/framework/MCP_SETUP.md +0 -229
- package/framework/agents/architecture-strategist.md +0 -53
- package/framework/agents/backend-agent.md +0 -150
- package/framework/agents/code-simplicity-reviewer.md +0 -86
- package/framework/agents/frontend-agent.md +0 -111
- package/framework/agents/kieran-typescript-reviewer.md +0 -96
- package/framework/agents/performance-oracle.md +0 -111
- package/framework/agents/qualia-codebase-mapper.md +0 -761
- package/framework/agents/qualia-debugger.md +0 -1204
- package/framework/agents/qualia-executor.md +0 -882
- package/framework/agents/qualia-integration-checker.md +0 -424
- package/framework/agents/qualia-phase-researcher.md +0 -457
- package/framework/agents/qualia-plan-checker.md +0 -700
- package/framework/agents/qualia-planner.md +0 -1245
- package/framework/agents/qualia-project-researcher.md +0 -603
- package/framework/agents/qualia-research-synthesizer.md +0 -200
- package/framework/agents/qualia-roadmapper.md +0 -606
- package/framework/agents/qualia-verifier.md +0 -686
- package/framework/agents/red-team-qa.md +0 -130
- package/framework/agents/security-auditor.md +0 -72
- package/framework/agents/team-orchestrator.md +0 -229
- package/framework/agents/teams/framework-audit-team.md +0 -66
- package/framework/agents/teams/full-stack-team.md +0 -48
- package/framework/agents/teams/optimize-team.md +0 -53
- package/framework/agents/teams/review-team.md +0 -70
- package/framework/agents/teams/ship-team.md +0 -86
- package/framework/agents/test-agent.md +0 -182
- package/framework/hooks/auto-format.sh +0 -54
- package/framework/hooks/block-env-edit.sh +0 -42
- package/framework/hooks/branch-guard.sh +0 -43
- package/framework/hooks/confirm-delete.sh +0 -59
- package/framework/hooks/migration-validate.sh +0 -77
- package/framework/hooks/notification-speak.sh +0 -16
- package/framework/hooks/pre-commit.sh +0 -100
- package/framework/hooks/pre-compact.sh +0 -56
- package/framework/hooks/pre-deploy-gate.sh +0 -160
- package/framework/hooks/qualia-colors.sh +0 -32
- package/framework/hooks/retention-cleanup.sh +0 -62
- package/framework/hooks/save-session-state.sh +0 -185
- package/framework/hooks/session-context-loader.sh +0 -96
- package/framework/hooks/session-learn.sh +0 -32
- package/framework/hooks/skill-announce.sh +0 -123
- package/framework/hooks/tool-error-announce.sh +0 -27
- package/framework/install.ps1 +0 -323
- package/framework/install.sh +0 -313
- package/framework/qualia-framework/VERSION +0 -1
- package/framework/qualia-framework/assets/qualia-logo.png +0 -0
- package/framework/qualia-framework/bin/collect-metrics.sh +0 -67
- package/framework/qualia-framework/bin/generate-report-docx.py +0 -429
- package/framework/qualia-framework/bin/qualia-tools.js +0 -2201
- package/framework/qualia-framework/bin/qualia-tools.test.js +0 -1054
- package/framework/qualia-framework/references/checkpoints.md +0 -775
- package/framework/qualia-framework/references/completion-checklists.md +0 -359
- package/framework/qualia-framework/references/continuation-format.md +0 -249
- package/framework/qualia-framework/references/continuation-prompt.md +0 -97
- package/framework/qualia-framework/references/decimal-phase-calculation.md +0 -65
- package/framework/qualia-framework/references/design-quality.md +0 -56
- package/framework/qualia-framework/references/employee-guide.md +0 -167
- package/framework/qualia-framework/references/git-integration.md +0 -254
- package/framework/qualia-framework/references/git-planning-commit.md +0 -50
- package/framework/qualia-framework/references/model-profile-resolution.md +0 -32
- package/framework/qualia-framework/references/model-profiles.md +0 -73
- package/framework/qualia-framework/references/phase-argument-parsing.md +0 -61
- package/framework/qualia-framework/references/planning-config.md +0 -195
- package/framework/qualia-framework/references/questioning.md +0 -141
- package/framework/qualia-framework/references/tdd.md +0 -263
- package/framework/qualia-framework/references/ui-brand.md +0 -160
- package/framework/qualia-framework/references/verification-patterns.md +0 -612
- package/framework/qualia-framework/templates/DEBUG.md +0 -159
- package/framework/qualia-framework/templates/DESIGN.md +0 -81
- package/framework/qualia-framework/templates/UAT.md +0 -247
- package/framework/qualia-framework/templates/codebase/architecture.md +0 -255
- package/framework/qualia-framework/templates/codebase/concerns.md +0 -310
- package/framework/qualia-framework/templates/codebase/conventions.md +0 -307
- package/framework/qualia-framework/templates/codebase/integrations.md +0 -280
- package/framework/qualia-framework/templates/codebase/stack.md +0 -186
- package/framework/qualia-framework/templates/codebase/structure.md +0 -285
- package/framework/qualia-framework/templates/codebase/testing.md +0 -480
- package/framework/qualia-framework/templates/config.json +0 -35
- package/framework/qualia-framework/templates/context.md +0 -283
- package/framework/qualia-framework/templates/continue-here.md +0 -78
- package/framework/qualia-framework/templates/debug-subagent-prompt.md +0 -91
- package/framework/qualia-framework/templates/discovery.md +0 -146
- package/framework/qualia-framework/templates/lab-notes.md +0 -16
- package/framework/qualia-framework/templates/milestone-archive.md +0 -123
- package/framework/qualia-framework/templates/milestone.md +0 -115
- package/framework/qualia-framework/templates/phase-prompt.md +0 -567
- package/framework/qualia-framework/templates/planner-subagent-prompt.md +0 -117
- package/framework/qualia-framework/templates/project.md +0 -184
- package/framework/qualia-framework/templates/projects/ai-agent.md +0 -156
- package/framework/qualia-framework/templates/projects/mobile-app.md +0 -181
- package/framework/qualia-framework/templates/projects/voice-agent.md +0 -134
- package/framework/qualia-framework/templates/projects/website.md +0 -137
- package/framework/qualia-framework/templates/requirements.md +0 -231
- package/framework/qualia-framework/templates/research-project/ARCHITECTURE.md +0 -204
- package/framework/qualia-framework/templates/research-project/FEATURES.md +0 -147
- package/framework/qualia-framework/templates/research-project/PITFALLS.md +0 -200
- package/framework/qualia-framework/templates/research-project/STACK.md +0 -120
- package/framework/qualia-framework/templates/research-project/SUMMARY.md +0 -170
- package/framework/qualia-framework/templates/research.md +0 -552
- package/framework/qualia-framework/templates/roadmap.md +0 -206
- package/framework/qualia-framework/templates/state.md +0 -179
- package/framework/qualia-framework/templates/summary-complex.md +0 -59
- package/framework/qualia-framework/templates/summary-minimal.md +0 -41
- package/framework/qualia-framework/templates/summary-standard.md +0 -48
- package/framework/qualia-framework/templates/summary.md +0 -246
- package/framework/qualia-framework/templates/user-setup.md +0 -311
- package/framework/qualia-framework/templates/verification-report.md +0 -322
- package/framework/qualia-framework/workflows/add-phase.md +0 -179
- package/framework/qualia-framework/workflows/add-todo.md +0 -157
- package/framework/qualia-framework/workflows/audit-milestone.md +0 -241
- package/framework/qualia-framework/workflows/check-todos.md +0 -176
- package/framework/qualia-framework/workflows/complete-milestone.md +0 -858
- package/framework/qualia-framework/workflows/diagnose-issues.md +0 -219
- package/framework/qualia-framework/workflows/discovery-phase.md +0 -289
- package/framework/qualia-framework/workflows/discuss-phase.md +0 -534
- package/framework/qualia-framework/workflows/execute-phase.md +0 -559
- package/framework/qualia-framework/workflows/execute-plan.md +0 -438
- package/framework/qualia-framework/workflows/help.md +0 -470
- package/framework/qualia-framework/workflows/insert-phase.md +0 -220
- package/framework/qualia-framework/workflows/list-phase-assumptions.md +0 -178
- package/framework/qualia-framework/workflows/map-codebase.md +0 -327
- package/framework/qualia-framework/workflows/new-milestone.md +0 -363
- package/framework/qualia-framework/workflows/new-project.md +0 -982
- package/framework/qualia-framework/workflows/pause-work.md +0 -122
- package/framework/qualia-framework/workflows/plan-milestone-gaps.md +0 -256
- package/framework/qualia-framework/workflows/plan-phase.md +0 -422
- package/framework/qualia-framework/workflows/progress.md +0 -389
- package/framework/qualia-framework/workflows/quick.md +0 -252
- package/framework/qualia-framework/workflows/remove-phase.md +0 -326
- package/framework/qualia-framework/workflows/research-phase.md +0 -74
- package/framework/qualia-framework/workflows/resume-project.md +0 -306
- package/framework/qualia-framework/workflows/set-profile.md +0 -80
- package/framework/qualia-framework/workflows/settings.md +0 -145
- package/framework/qualia-framework/workflows/transition.md +0 -556
- package/framework/qualia-framework/workflows/update.md +0 -197
- package/framework/qualia-framework/workflows/verify-phase.md +0 -195
- package/framework/qualia-framework/workflows/verify-work.md +0 -625
- package/framework/rules/context7.md +0 -14
- package/framework/rules/frontend.md +0 -33
- package/framework/rules/speed.md +0 -23
- package/framework/scripts/__pycache__/say.cpython-314.pyc +0 -0
- package/framework/scripts/apply-retention.sh +0 -120
- package/framework/scripts/bootstrap-pop-os.sh +0 -354
- package/framework/scripts/claude-voice +0 -13
- package/framework/scripts/cleanup.sh +0 -131
- package/framework/scripts/cowork-mode.sh +0 -141
- package/framework/scripts/generate-project-claude-md.sh +0 -153
- package/framework/scripts/load-test-webhook.js +0 -172
- package/framework/scripts/say.py +0 -236
- package/framework/scripts/showcase-video-recorder/ffmpeg-builder.js +0 -167
- package/framework/scripts/showcase-video-recorder/playwright-helpers.js +0 -216
- package/framework/scripts/speak.py +0 -55
- package/framework/scripts/speak.sh +0 -18
- package/framework/scripts/status.sh +0 -138
- package/framework/scripts/sync-to-framework.sh +0 -65
- package/framework/scripts/voice-hotkey.py +0 -227
- package/framework/scripts/voice-input.sh +0 -51
- package/framework/skills/animate/SKILL.md +0 -202
- package/framework/skills/bolder/SKILL.md +0 -144
- package/framework/skills/browser-qa/SKILL.md +0 -536
- package/framework/skills/clarify/SKILL.md +0 -179
- package/framework/skills/client-handoff/SKILL.md +0 -135
- package/framework/skills/collab-onboard/SKILL.md +0 -111
- package/framework/skills/colorize/SKILL.md +0 -170
- package/framework/skills/critique/SKILL.md +0 -126
- package/framework/skills/deep-research/SKILL.md +0 -240
- package/framework/skills/delight/SKILL.md +0 -329
- package/framework/skills/deploy/SKILL.md +0 -261
- package/framework/skills/deploy-verify/SKILL.md +0 -377
- package/framework/skills/deploy-verify/scripts/canary-check.sh +0 -206
- package/framework/skills/deploy-verify/scripts/check-console-errors.js +0 -147
- package/framework/skills/deploy-verify/scripts/check-cwv.js +0 -139
- package/framework/skills/deploy-verify/scripts/project-detect.sh +0 -84
- package/framework/skills/deploy-verify/scripts/verify.sh +0 -548
- package/framework/skills/design-quieter/SKILL.md +0 -130
- package/framework/skills/distill/SKILL.md +0 -149
- package/framework/skills/docs-lookup/SKILL.md +0 -79
- package/framework/skills/fcm-notifications/SKILL.md +0 -125
- package/framework/skills/financial-ledger/SKILL.md +0 -1039
- package/framework/skills/frontend-master/NOTICE.md +0 -4
- package/framework/skills/frontend-master/SKILL.md +0 -127
- package/framework/skills/frontend-master/reference/color-and-contrast.md +0 -132
- package/framework/skills/frontend-master/reference/interaction-design.md +0 -123
- package/framework/skills/frontend-master/reference/motion-design.md +0 -99
- package/framework/skills/frontend-master/reference/responsive-design.md +0 -114
- package/framework/skills/frontend-master/reference/spatial-design.md +0 -100
- package/framework/skills/frontend-master/reference/typography.md +0 -131
- package/framework/skills/frontend-master/reference/ux-writing.md +0 -107
- package/framework/skills/harden/SKILL.md +0 -357
- package/framework/skills/i18n-rtl/SKILL.md +0 -752
- package/framework/skills/learn/SKILL.md +0 -95
- package/framework/skills/memory/SKILL.md +0 -50
- package/framework/skills/mobile-expo/SKILL.md +0 -977
- package/framework/skills/mobile-expo/references/store-checklist.md +0 -550
- package/framework/skills/nestjs-backend/README.md +0 -73
- package/framework/skills/nestjs-backend/SKILL.md +0 -446
- package/framework/skills/nestjs-backend/references/templates.md +0 -1173
- package/framework/skills/normalize/SKILL.md +0 -79
- package/framework/skills/onboard/SKILL.md +0 -242
- package/framework/skills/openrouter-agent/SKILL.md +0 -922
- package/framework/skills/polish/SKILL.md +0 -209
- package/framework/skills/pr/SKILL.md +0 -66
- package/framework/skills/qualia/SKILL.md +0 -199
- package/framework/skills/qualia-add-todo/SKILL.md +0 -68
- package/framework/skills/qualia-audit-milestone/SKILL.md +0 -95
- package/framework/skills/qualia-check-todos/SKILL.md +0 -55
- package/framework/skills/qualia-complete-milestone/SKILL.md +0 -134
- package/framework/skills/qualia-debug/SKILL.md +0 -149
- package/framework/skills/qualia-design/SKILL.md +0 -203
- package/framework/skills/qualia-discuss-phase/SKILL.md +0 -72
- package/framework/skills/qualia-evolve/SKILL.md +0 -200
- package/framework/skills/qualia-execute-phase/SKILL.md +0 -89
- package/framework/skills/qualia-framework-audit/SKILL.md +0 -604
- package/framework/skills/qualia-guide/SKILL.md +0 -32
- package/framework/skills/qualia-help/SKILL.md +0 -114
- package/framework/skills/qualia-idk/SKILL.md +0 -352
- package/framework/skills/qualia-list-phase-assumptions/SKILL.md +0 -67
- package/framework/skills/qualia-new-milestone/SKILL.md +0 -72
- package/framework/skills/qualia-new-project/SKILL.md +0 -232
- package/framework/skills/qualia-pause-work/SKILL.md +0 -96
- package/framework/skills/qualia-plan-milestone-gaps/SKILL.md +0 -57
- package/framework/skills/qualia-plan-phase/SKILL.md +0 -104
- package/framework/skills/qualia-production-check/SKILL.md +0 -0
- package/framework/skills/qualia-progress/SKILL.md +0 -53
- package/framework/skills/qualia-quick/SKILL.md +0 -89
- package/framework/skills/qualia-report/SKILL.md +0 -166
- package/framework/skills/qualia-research-phase/SKILL.md +0 -88
- package/framework/skills/qualia-resume-work/SKILL.md +0 -62
- package/framework/skills/qualia-review/SKILL.md +0 -263
- package/framework/skills/qualia-start/SKILL.md +0 -161
- package/framework/skills/qualia-verify-work/SKILL.md +0 -132
- package/framework/skills/rag/SKILL.md +0 -750
- package/framework/skills/responsive/SKILL.md +0 -231
- package/framework/skills/retro/SKILL.md +0 -284
- package/framework/skills/sakani-conventions/SKILL.md +0 -136
- package/framework/skills/sakani-conventions/evals/evals.json +0 -23
- package/framework/skills/sakani-conventions/references/entities.md +0 -365
- package/framework/skills/sakani-conventions/references/error-codes.md +0 -95
- package/framework/skills/seo-master/SKILL.md +0 -490
- package/framework/skills/seo-master/references/checklist.md +0 -199
- package/framework/skills/seo-master/references/structured-data.md +0 -609
- package/framework/skills/ship/SKILL.md +0 -239
- package/framework/skills/stack-researcher/SKILL.md +0 -215
- package/framework/skills/status/SKILL.md +0 -154
- package/framework/skills/status/scripts/health-check.sh +0 -562
- package/framework/skills/subscription-payments/SKILL.md +0 -250
- package/framework/skills/supabase/SKILL.md +0 -973
- package/framework/skills/supabase/references/templates.md +0 -159
- package/framework/skills/team/SKILL.md +0 -67
- package/framework/skills/test-runner/SKILL.md +0 -202
- package/framework/skills/voice-agent/SKILL.md +0 -1312
- package/framework/skills/zoho-workflow/SKILL.md +0 -51
- package/framework/statusline-command.sh +0 -117
- package/framework/teams/default/inboxes/plan-04.json +0 -9
- package/framework/teams/review-team.md +0 -75
- package/framework/teams/ship-team.md +0 -86
- package/profiles/fawzi.json +0 -16
- package/profiles/hasan.json +0 -16
- package/profiles/moayad.json +0 -16
- package/templates/CLAUDE-owner.md +0 -52
- package/templates/CLAUDE.md.hbs +0 -58
- package/templates/env.claude.template +0 -12
- package/templates/settings.json +0 -172
- package/uninstall.sh +0 -90
- /package/{framework/rules → rules}/deployment.md +0 -0
- /package/{framework/rules → rules}/security.md +0 -0
package/bin/cli.js
CHANGED
|
@@ -1,614 +1,419 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const path = require(
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function ok(msg) { log(` ${c.green}\u2713${c.reset} ${msg}`); }
|
|
28
|
-
function warn(msg) { log(` ${c.yellow}\u26A0${c.reset} ${msg}`); }
|
|
29
|
-
function fail(msg) { log(` ${c.red}\u2717${c.reset} ${msg}`); }
|
|
30
|
-
function step(n, total, msg) { log(`\n${c.cyan}[${n}/${total}]${c.reset} ${msg}`); }
|
|
31
|
-
|
|
32
|
-
// ── Helpers ──
|
|
33
|
-
function copyDirSync(src, dest) {
|
|
34
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
35
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
36
|
-
const srcPath = path.join(src, entry.name);
|
|
37
|
-
const destPath = path.join(dest, entry.name);
|
|
38
|
-
if (entry.isDirectory()) {
|
|
39
|
-
copyDirSync(srcPath, destPath);
|
|
40
|
-
} else {
|
|
41
|
-
fs.copyFileSync(srcPath, destPath);
|
|
42
|
-
}
|
|
3
|
+
const { spawnSync } = require("child_process");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const readline = require("readline");
|
|
7
|
+
|
|
8
|
+
const TEAL = "\x1b[38;2;0;206;209m";
|
|
9
|
+
const TG = "\x1b[38;2;0;170;175m";
|
|
10
|
+
const DIM = "\x1b[38;2;80;90;100m";
|
|
11
|
+
const GREEN = "\x1b[38;2;52;211;153m";
|
|
12
|
+
const WHITE = "\x1b[38;2;220;225;230m";
|
|
13
|
+
const YELLOW = "\x1b[38;2;234;179;8m";
|
|
14
|
+
const RED = "\x1b[38;2;239;68;68m";
|
|
15
|
+
const RESET = "\x1b[0m";
|
|
16
|
+
const BOLD = "\x1b[1m";
|
|
17
|
+
|
|
18
|
+
const CLAUDE_DIR = path.join(require("os").homedir(), ".claude");
|
|
19
|
+
const PKG = require("../package.json");
|
|
20
|
+
const CONFIG_FILE = path.join(CLAUDE_DIR, ".qualia-config.json");
|
|
21
|
+
|
|
22
|
+
function readConfig() {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
43
27
|
}
|
|
44
28
|
}
|
|
45
29
|
|
|
46
|
-
function
|
|
47
|
-
if (!fs.existsSync(
|
|
48
|
-
|
|
49
|
-
if (type === 'dirs') return entries.filter(e => e.isDirectory()).length;
|
|
50
|
-
return entries.filter(e => e.isFile()).length;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function deepMerge(target, source) {
|
|
54
|
-
const result = { ...target };
|
|
55
|
-
for (const key of Object.keys(source)) {
|
|
56
|
-
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])
|
|
57
|
-
&& target[key] && typeof target[key] === 'object' && !Array.isArray(target[key])) {
|
|
58
|
-
result[key] = deepMerge(target[key], source[key]);
|
|
59
|
-
} else if (result[key] === undefined || result[key] === null || result[key] === '') {
|
|
60
|
-
result[key] = source[key];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return result;
|
|
30
|
+
function writeConfig(cfg) {
|
|
31
|
+
if (!fs.existsSync(CLAUDE_DIR)) fs.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
32
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n");
|
|
64
33
|
}
|
|
65
34
|
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const profile = JSON.parse(fs.readFileSync(path.join(PROFILES_DIR, file), 'utf8'));
|
|
71
|
-
profiles[profile.code.toUpperCase()] = profile;
|
|
72
|
-
}
|
|
73
|
-
return profiles;
|
|
35
|
+
function banner() {
|
|
36
|
+
console.log("");
|
|
37
|
+
console.log(` ${TEAL}${BOLD}⬢${RESET} ${WHITE}${BOLD}Qualia Framework${RESET} ${DIM}v${PKG.version}${RESET}`);
|
|
38
|
+
console.log(` ${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
74
39
|
}
|
|
75
40
|
|
|
76
|
-
|
|
77
|
-
let template = fs.readFileSync(path.join(TEMPLATES_DIR, 'CLAUDE.md.hbs'), 'utf8');
|
|
41
|
+
// ─── Commands ────────────────────────────────────────────
|
|
78
42
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const roleTitle = isOwner ? 'Founder' : (profile.type === 'full-time' ? 'Full-time Developer' : 'Developer');
|
|
43
|
+
function cmdInstall() {
|
|
44
|
+
require("./install.js");
|
|
45
|
+
}
|
|
84
46
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'{{role}}': profile.role,
|
|
89
|
-
'{{roleTitle}}': roleTitle,
|
|
90
|
-
'{{location}}': profile.location || '',
|
|
91
|
-
};
|
|
47
|
+
function cmdVersion() {
|
|
48
|
+
banner();
|
|
49
|
+
const cfg = readConfig();
|
|
92
50
|
|
|
93
|
-
|
|
94
|
-
|
|
51
|
+
console.log(` ${DIM}Installed:${RESET} ${WHITE}${PKG.version}${RESET}`);
|
|
52
|
+
if (cfg.installed_by) {
|
|
53
|
+
console.log(` ${DIM}User:${RESET} ${WHITE}${cfg.installed_by}${RESET} ${DIM}(${cfg.role})${RESET}`);
|
|
54
|
+
}
|
|
55
|
+
if (cfg.installed_at) {
|
|
56
|
+
console.log(` ${DIM}Date:${RESET} ${WHITE}${cfg.installed_at}${RESET}`);
|
|
95
57
|
}
|
|
96
58
|
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
});
|
|
59
|
+
// Check for updates
|
|
60
|
+
try {
|
|
61
|
+
// spawnSync with argv — no bash-only `2>/dev/null` redirect, no shell
|
|
62
|
+
// interpolation. stdio: "ignore" on stderr silences any npm warnings
|
|
63
|
+
// (offline, proxy, etc.) without a shell redirect. shell: true on
|
|
64
|
+
// Windows because `npm` is a .cmd shim that only resolves through cmd.
|
|
65
|
+
const r = spawnSync("npm", ["view", "qualia-framework", "version"], {
|
|
66
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
67
|
+
shell: process.platform === "win32",
|
|
68
|
+
timeout: 5000,
|
|
69
|
+
encoding: "utf8",
|
|
70
|
+
});
|
|
71
|
+
const latest = (r.stdout || "").trim();
|
|
72
|
+
const semverGt = (a, b) => {
|
|
73
|
+
const pa = a.split(".").map(Number), pb = b.split(".").map(Number);
|
|
74
|
+
for (let i = 0; i < 3; i++) { if (pa[i] > pb[i]) return true; if (pa[i] < pb[i]) return false; }
|
|
75
|
+
return false;
|
|
76
|
+
};
|
|
77
|
+
if (latest && semverGt(latest, PKG.version)) {
|
|
78
|
+
console.log("");
|
|
79
|
+
console.log(` ${YELLOW}Update available:${RESET} ${WHITE}${latest}${RESET}`);
|
|
80
|
+
console.log(` ${DIM}Run:${RESET} npx qualia-framework update`);
|
|
81
|
+
} else if (latest) {
|
|
82
|
+
console.log(` ${DIM}Latest:${RESET} ${GREEN}${latest} ✓${RESET} ${DIM}(up to date)${RESET}`);
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
console.log(` ${DIM}Latest:${RESET} ${DIM}(offline — couldn't check)${RESET}`);
|
|
86
|
+
}
|
|
87
|
+
console.log("");
|
|
119
88
|
}
|
|
120
89
|
|
|
121
|
-
function
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (!fs.existsSync(existingPath)) return template;
|
|
125
|
-
|
|
126
|
-
const existing = JSON.parse(fs.readFileSync(existingPath, 'utf8'));
|
|
90
|
+
function cmdUpdate() {
|
|
91
|
+
banner();
|
|
92
|
+
const cfg = readConfig();
|
|
127
93
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
for (const [key, val] of Object.entries(existing.env)) {
|
|
133
|
-
if (val && val !== '' && !val.startsWith('$')) {
|
|
134
|
-
merged.env[key] = val;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
94
|
+
if (!cfg.code) {
|
|
95
|
+
console.log(` ${RED}✗${RESET} No install code saved. Run ${TEAL}install${RESET} first.`);
|
|
96
|
+
console.log("");
|
|
97
|
+
process.exit(1);
|
|
137
98
|
}
|
|
138
99
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// Use node-based statusline on Windows (no bash), bash on Unix
|
|
143
|
-
if (process.platform === 'win32') {
|
|
144
|
-
merged.statusLine = { type: 'command', command: 'node ~/.claude/statusline-command.js' };
|
|
145
|
-
} else {
|
|
146
|
-
merged.statusLine = template.statusLine;
|
|
147
|
-
}
|
|
100
|
+
console.log(` ${DIM}Current:${RESET} ${WHITE}${PKG.version}${RESET}`);
|
|
101
|
+
console.log(` ${DIM}Updating...${RESET}`);
|
|
102
|
+
console.log("");
|
|
148
103
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
104
|
+
try {
|
|
105
|
+
const r = spawnSync("npx", ["qualia-framework@latest", "install"], {
|
|
106
|
+
input: cfg.code + "\n",
|
|
107
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
108
|
+
shell: process.platform === "win32", // npx is a .cmd shim on Windows — must go through shell
|
|
109
|
+
timeout: 120000,
|
|
110
|
+
encoding: "utf8",
|
|
111
|
+
});
|
|
112
|
+
if (r.status !== 0) {
|
|
113
|
+
console.log(` ${RED}✗${RESET} Update failed. Run manually: npx qualia-framework@latest install`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.log(` ${RED}✗${RESET} Update failed: ${e.message}`);
|
|
118
|
+
console.log(` ${DIM}Run manually:${RESET} npx qualia-framework@latest install`);
|
|
119
|
+
process.exit(1);
|
|
155
120
|
}
|
|
156
|
-
|
|
157
|
-
return merged;
|
|
158
121
|
}
|
|
159
122
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
123
|
+
// ─── Uninstall ───────────────────────────────────────────
|
|
124
|
+
// Surgical removal of the Qualia Framework from ~/.claude/.
|
|
125
|
+
// Preserves CLAUDE.md (user may have customized it) and preserves any
|
|
126
|
+
// non-Qualia entries in settings.json (other hooks, user env vars, etc.).
|
|
127
|
+
// --yes / -y skips the confirmation prompt for scripted use.
|
|
128
|
+
|
|
129
|
+
// 7 Qualia hook filenames — only these are removed from ~/.claude/hooks/,
|
|
130
|
+
// any other hooks the user dropped in there are left alone.
|
|
131
|
+
// Note: block-env-edit.js was retired in v3.2.0; the installer removes any
|
|
132
|
+
// lingering copy, and this uninstall list no longer references it.
|
|
133
|
+
const QUALIA_HOOK_FILES = [
|
|
134
|
+
"session-start.js",
|
|
135
|
+
"auto-update.js",
|
|
136
|
+
"branch-guard.js",
|
|
137
|
+
"pre-push.js",
|
|
138
|
+
"migration-guard.js",
|
|
139
|
+
"pre-deploy-gate.js",
|
|
140
|
+
"pre-compact.js",
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
// 4 Qualia agents — only these are removed.
|
|
144
|
+
const QUALIA_AGENT_FILES = ["planner.md", "builder.md", "verifier.md", "qa-browser.md"];
|
|
145
|
+
|
|
146
|
+
// 3 Qualia bin scripts.
|
|
147
|
+
const QUALIA_BIN_FILES = ["state.js", "qualia-ui.js", "statusline.js"];
|
|
148
|
+
|
|
149
|
+
// 4 Qualia rules.
|
|
150
|
+
const QUALIA_RULE_FILES = ["security.md", "frontend.md", "design-reference.md", "deployment.md"];
|
|
151
|
+
|
|
152
|
+
function promptYesNo(question, defaultYes) {
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
155
|
+
const suffix = defaultYes ? " (Y/n)" : " (y/N)";
|
|
156
|
+
rl.question(` ${WHITE}${question}${RESET}${suffix} `, (answer) => {
|
|
164
157
|
rl.close();
|
|
165
|
-
|
|
158
|
+
const a = String(answer || "").trim().toLowerCase();
|
|
159
|
+
if (!a) return resolve(defaultYes);
|
|
160
|
+
resolve(a === "y" || a === "yes");
|
|
166
161
|
});
|
|
167
162
|
});
|
|
168
163
|
}
|
|
169
164
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
log(`${c.red}\u255A${'═'.repeat(54)}\u255D${c.reset}`);
|
|
176
|
-
log('');
|
|
177
|
-
|
|
178
|
-
if (!fs.existsSync(CLAUDE_DIR)) {
|
|
179
|
-
warn('Nothing to uninstall \u2014 ~/.claude/ does not exist.');
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Count everything in ~/.claude/
|
|
184
|
-
const totalFiles = countFilesRecursive(CLAUDE_DIR);
|
|
185
|
-
|
|
186
|
-
log(`${c.bold} NUCLEAR UNINSTALL \u2014 deleting EVERYTHING in ~/.claude/${c.reset}`);
|
|
187
|
-
log('');
|
|
188
|
-
log(` ${c.red}\u2022${c.reset} ${totalFiles} files will be permanently deleted`);
|
|
189
|
-
log(` ${c.red}\u2022${c.reset} settings.json will be reset to {}`);
|
|
190
|
-
log(` ${c.red}\u2022${c.reset} All skills, agents, hooks, rules, knowledge \u2014 gone`);
|
|
191
|
-
log(` ${c.red}\u2022${c.reset} All memory, projects, plans, cache \u2014 gone`);
|
|
192
|
-
log(` ${c.red}\u2022${c.reset} CLAUDE.md, .env.claude, all config \u2014 gone`);
|
|
193
|
-
log('');
|
|
194
|
-
|
|
195
|
-
// Confirmation
|
|
196
|
-
const force = process.argv.includes('--force');
|
|
197
|
-
if (!force) {
|
|
198
|
-
log(`${c.red} \u26A0 THIS CANNOT BE UNDONE.${c.reset}`);
|
|
199
|
-
log(`${c.red} \u26A0 ~/.claude/ will be wiped clean. Nothing survives.${c.reset}`);
|
|
200
|
-
log('');
|
|
201
|
-
const answer = await prompt(` Type 'UNINSTALL' to confirm: `);
|
|
202
|
-
if (answer !== 'UNINSTALL') {
|
|
203
|
-
log('');
|
|
204
|
-
ok('Cancelled. Nothing was deleted.');
|
|
205
|
-
return;
|
|
165
|
+
function safeUnlink(p, counters) {
|
|
166
|
+
try {
|
|
167
|
+
if (fs.existsSync(p)) {
|
|
168
|
+
fs.unlinkSync(p);
|
|
169
|
+
counters.filesRemoved++;
|
|
206
170
|
}
|
|
207
|
-
|
|
171
|
+
} catch (e) {
|
|
172
|
+
counters.errors.push(`${p}: ${e.message}`);
|
|
208
173
|
}
|
|
174
|
+
}
|
|
209
175
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// Skip settings.json — we reset it in step 2
|
|
217
|
-
if (entry.name === 'settings.json') continue;
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
if (entry.isDirectory()) {
|
|
221
|
-
fs.rmSync(p, { recursive: true, force: true });
|
|
222
|
-
ok(`${entry.name}/`);
|
|
223
|
-
} else if (isSymlink(p)) {
|
|
224
|
-
fs.unlinkSync(p);
|
|
225
|
-
ok(`${entry.name} (symlink)`);
|
|
226
|
-
} else {
|
|
227
|
-
fs.unlinkSync(p);
|
|
228
|
-
ok(entry.name);
|
|
229
|
-
}
|
|
230
|
-
} catch (e) {
|
|
231
|
-
fail(`${entry.name}: ${e.message}`);
|
|
176
|
+
function safeRmDir(p, counters) {
|
|
177
|
+
try {
|
|
178
|
+
if (fs.existsSync(p)) {
|
|
179
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
180
|
+
counters.dirsRemoved++;
|
|
232
181
|
}
|
|
182
|
+
} catch (e) {
|
|
183
|
+
counters.errors.push(`${p}: ${e.message}`);
|
|
233
184
|
}
|
|
234
|
-
|
|
235
|
-
// Step 2: Reset settings.json to empty
|
|
236
|
-
step(2, 2, 'Resetting settings.json ...');
|
|
237
|
-
const settingsPath = path.join(CLAUDE_DIR, 'settings.json');
|
|
238
|
-
if (fs.existsSync(settingsPath)) {
|
|
239
|
-
fs.writeFileSync(settingsPath, '{}\n');
|
|
240
|
-
ok('settings.json reset to {}');
|
|
241
|
-
} else {
|
|
242
|
-
ok('No settings.json found');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
log('');
|
|
246
|
-
log(`${c.green}\u2554${'═'.repeat(54)}\u2557${c.reset}`);
|
|
247
|
-
log(`${c.green}\u2551${c.reset}${c.bold} \u2713 NUKED \u2014 ~/.claude/ is clean ${c.reset}${c.green}\u2551${c.reset}`);
|
|
248
|
-
log(`${c.green}\u255A${'═'.repeat(54)}\u255D${c.reset}`);
|
|
249
|
-
log('');
|
|
250
|
-
log(` ${c.dim}Claude Code will use default behavior on next launch.${c.reset}`);
|
|
251
|
-
log('');
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function isSymlink(p) {
|
|
255
|
-
try { fs.lstatSync(p); return fs.lstatSync(p).isSymbolicLink(); } catch { return false; }
|
|
256
185
|
}
|
|
257
186
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
await runUninstall();
|
|
268
|
-
} else {
|
|
269
|
-
await runInstall();
|
|
187
|
+
function cleanSettingsJson(counters) {
|
|
188
|
+
const settingsPath = path.join(CLAUDE_DIR, "settings.json");
|
|
189
|
+
if (!fs.existsSync(settingsPath)) return;
|
|
190
|
+
let settings;
|
|
191
|
+
try {
|
|
192
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
193
|
+
} catch (e) {
|
|
194
|
+
counters.errors.push(`settings.json: ${e.message}`);
|
|
195
|
+
return;
|
|
270
196
|
}
|
|
271
|
-
}
|
|
272
197
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
log(`${c.cyan}\u2551${c.reset}${c.bold} QUALIA FRAMEWORK \u2014 INSTALLER ${c.reset}${c.cyan}\u2551${c.reset}`);
|
|
277
|
-
log(`${c.cyan}\u255A${'═'.repeat(54)}\u255D${c.reset}`);
|
|
278
|
-
log('');
|
|
279
|
-
|
|
280
|
-
const profiles = loadProfiles();
|
|
281
|
-
const code = await prompt(` Enter your employee code: `);
|
|
282
|
-
const profile = profiles[code.toUpperCase()];
|
|
283
|
-
|
|
284
|
-
if (!profile) {
|
|
285
|
-
log('');
|
|
286
|
-
fail(`Unknown code "${code}". Contact Fawzi for your employee code.`);
|
|
287
|
-
log(`${c.dim} Available format: QS-NAME-YEAR${c.reset}`);
|
|
288
|
-
process.exit(1);
|
|
289
|
-
}
|
|
198
|
+
// Only remove entries that point at qualia paths. Leave everything else.
|
|
199
|
+
const isQualiaCommand = (cmd) =>
|
|
200
|
+
typeof cmd === "string" && (cmd.includes("qualia") || cmd.includes(".claude/hooks/") || cmd.includes(".claude/bin/"));
|
|
290
201
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
if (
|
|
307
|
-
|
|
202
|
+
const filterHookArray = (arr) => {
|
|
203
|
+
if (!Array.isArray(arr)) return arr;
|
|
204
|
+
return arr
|
|
205
|
+
.map((entry) => {
|
|
206
|
+
if (!entry || !Array.isArray(entry.hooks)) return entry;
|
|
207
|
+
const hooks = entry.hooks.filter((h) => !isQualiaCommand(h && h.command));
|
|
208
|
+
return { ...entry, hooks };
|
|
209
|
+
})
|
|
210
|
+
.filter((entry) => Array.isArray(entry.hooks) && entry.hooks.length > 0);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
if (settings.hooks && typeof settings.hooks === "object") {
|
|
214
|
+
for (const key of ["SessionStart", "PreToolUse", "PreCompact"]) {
|
|
215
|
+
if (settings.hooks[key]) {
|
|
216
|
+
const cleaned = filterHookArray(settings.hooks[key]);
|
|
217
|
+
if (cleaned && cleaned.length > 0) {
|
|
218
|
+
settings.hooks[key] = cleaned;
|
|
219
|
+
} else {
|
|
220
|
+
delete settings.hooks[key];
|
|
308
221
|
}
|
|
309
222
|
}
|
|
310
|
-
ok(`Config backed up to ${c.dim}~/.claude.backup-${date}/${c.reset}`);
|
|
311
|
-
} else {
|
|
312
|
-
ok('Backup already exists for today');
|
|
313
223
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
ok('Created ~/.claude/');
|
|
224
|
+
// If hooks is now empty, remove it entirely.
|
|
225
|
+
if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
|
|
317
226
|
}
|
|
318
227
|
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const src = path.join(FRAMEWORK_DIR, dir);
|
|
325
|
-
const dest = path.join(CLAUDE_DIR, dir);
|
|
326
|
-
if (fs.existsSync(src)) {
|
|
327
|
-
copyDirSync(src, dest);
|
|
228
|
+
// Status line — only drop it if it points at our renderer.
|
|
229
|
+
if (settings.statusLine && typeof settings.statusLine === "object") {
|
|
230
|
+
const cmd = settings.statusLine.command || "";
|
|
231
|
+
if (isQualiaCommand(cmd) || cmd.includes("statusline.js") || cmd.includes("qualia-ui")) {
|
|
232
|
+
delete settings.statusLine;
|
|
328
233
|
}
|
|
329
234
|
}
|
|
330
235
|
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (fs.existsSync(src)) {
|
|
335
|
-
fs.copyFileSync(src, path.join(CLAUDE_DIR, f));
|
|
336
|
-
}
|
|
337
|
-
}
|
|
236
|
+
// Qualia-specific spinner overrides.
|
|
237
|
+
if (settings.spinnerVerbs) delete settings.spinnerVerbs;
|
|
238
|
+
if (settings.spinnerTipsOverride) delete settings.spinnerTipsOverride;
|
|
338
239
|
|
|
339
|
-
//
|
|
340
|
-
const hooksDir = path.join(CLAUDE_DIR, 'hooks');
|
|
341
|
-
if (fs.existsSync(hooksDir)) {
|
|
342
|
-
for (const f of fs.readdirSync(hooksDir)) {
|
|
343
|
-
if (f.endsWith('.sh')) {
|
|
344
|
-
fs.chmodSync(path.join(hooksDir, f), 0o755);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
// Make standalone scripts executable
|
|
349
|
-
for (const f of ['statusline-command.sh', 'statusline-command.js', 'askpass.sh']) {
|
|
350
|
-
const p = path.join(CLAUDE_DIR, f);
|
|
351
|
-
if (fs.existsSync(p)) try { fs.chmodSync(p, 0o755); } catch {}
|
|
352
|
-
}
|
|
353
|
-
// Make qualia-framework bin executable
|
|
354
|
-
const engineBin = path.join(CLAUDE_DIR, 'qualia-framework', 'bin');
|
|
355
|
-
if (fs.existsSync(engineBin)) {
|
|
356
|
-
for (const f of fs.readdirSync(engineBin)) {
|
|
357
|
-
const p = path.join(engineBin, f);
|
|
358
|
-
try { fs.chmodSync(p, 0o755); } catch {}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
ok(`${countDir(path.join(CLAUDE_DIR, 'skills'), 'dirs')} skills`);
|
|
363
|
-
ok(`${countDir(hooksDir, 'files')} hooks (chmod +x)`);
|
|
364
|
-
ok(`${countDir(path.join(CLAUDE_DIR, 'agents'), 'files')} agents`);
|
|
365
|
-
ok(`${countDir(path.join(CLAUDE_DIR, 'rules'), 'files')} rule files`);
|
|
366
|
-
ok('qualia-framework');
|
|
367
|
-
ok('knowledge base');
|
|
368
|
-
|
|
369
|
-
// Step 3: Generate CLAUDE.md
|
|
370
|
-
step(3, TOTAL, 'Generating your CLAUDE.md ...');
|
|
371
|
-
const claudeMd = renderCLAUDEmd(profile);
|
|
372
|
-
fs.writeFileSync(path.join(CLAUDE_DIR, 'CLAUDE.md'), claudeMd);
|
|
373
|
-
|
|
374
|
-
const roleDesc = profile.role === 'OWNER' ? 'Full authority' :
|
|
375
|
-
`Feature branches${profile.permissions?.canDeploy ? ', can deploy' : ', no prod deploy'}`;
|
|
376
|
-
ok(`Identity: ${profile.name} \u2014 ${profile.role === 'OWNER' ? 'Founder' : 'Developer'} at Qualia Solutions`);
|
|
377
|
-
ok(`Role: ${profile.role} (${roleDesc})`);
|
|
378
|
-
|
|
379
|
-
// Step 4: Configure settings.json
|
|
380
|
-
step(4, TOTAL, 'Configuring settings.json ...');
|
|
381
|
-
const settingsPath = path.join(CLAUDE_DIR, 'settings.json');
|
|
382
|
-
const templateSettingsPath = path.join(TEMPLATES_DIR, 'settings.json');
|
|
383
|
-
const mergedSettings = mergeSettings(settingsPath, templateSettingsPath);
|
|
384
|
-
|
|
385
|
-
// Fix SUDO_ASKPASS to absolute path
|
|
386
|
-
mergedSettings.env.SUDO_ASKPASS = path.join(CLAUDE_DIR, 'askpass.sh');
|
|
387
|
-
mergedSettings.env.CLAUDE_ENV_FILE = path.join(CLAUDE_DIR, '.env.claude');
|
|
388
|
-
|
|
389
|
-
fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2) + '\n');
|
|
390
|
-
ok('Hooks wired');
|
|
391
|
-
ok('Preserved your existing API keys');
|
|
392
|
-
ok('Settings configured');
|
|
393
|
-
|
|
394
|
-
// Step 5: Environment
|
|
395
|
-
step(5, TOTAL, 'Setting up environment ...');
|
|
396
|
-
const envPath = path.join(CLAUDE_DIR, '.env.claude');
|
|
397
|
-
if (fs.existsSync(envPath)) {
|
|
398
|
-
ok('.env.claude already exists (preserved)');
|
|
399
|
-
} else {
|
|
400
|
-
fs.copyFileSync(path.join(TEMPLATES_DIR, 'env.claude.template'), envPath);
|
|
401
|
-
fs.chmodSync(envPath, 0o600);
|
|
402
|
-
warn(`Fill in your API keys: ${c.dim}~/.claude/.env.claude${c.reset}`);
|
|
403
|
-
}
|
|
240
|
+
// Leave settings.env alone — the user may have other env vars in there.
|
|
404
241
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
242
|
+
try {
|
|
243
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
244
|
+
counters.settingsCleaned = true;
|
|
245
|
+
} catch (e) {
|
|
246
|
+
counters.errors.push(`settings.json write: ${e.message}`);
|
|
408
247
|
}
|
|
248
|
+
}
|
|
409
249
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
let hooksOk = 0;
|
|
414
|
-
const hooksTotal = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh')).length;
|
|
415
|
-
for (const f of fs.readdirSync(hooksDir)) {
|
|
416
|
-
if (!f.endsWith('.sh')) continue;
|
|
417
|
-
try {
|
|
418
|
-
const stat = fs.statSync(path.join(hooksDir, f));
|
|
419
|
-
if (stat.mode & 0o111) hooksOk++;
|
|
420
|
-
} catch {}
|
|
421
|
-
}
|
|
422
|
-
if (hooksOk === hooksTotal) {
|
|
423
|
-
ok(`${hooksOk}/${hooksTotal} hooks executable`);
|
|
424
|
-
} else {
|
|
425
|
-
warn(`${hooksOk}/${hooksTotal} hooks executable`);
|
|
426
|
-
}
|
|
250
|
+
async function cmdUninstall() {
|
|
251
|
+
banner();
|
|
427
252
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
ok('settings.json valid JSON');
|
|
431
|
-
} catch {
|
|
432
|
-
fail('settings.json invalid JSON!');
|
|
433
|
-
}
|
|
253
|
+
const args = process.argv.slice(3);
|
|
254
|
+
const skipConfirm = args.includes("-y") || args.includes("--yes");
|
|
434
255
|
|
|
435
|
-
|
|
436
|
-
|
|
256
|
+
const cfg = readConfig();
|
|
257
|
+
console.log("");
|
|
258
|
+
if (cfg.installed_by) {
|
|
259
|
+
console.log(` ${DIM}User:${RESET} ${WHITE}${cfg.installed_by}${RESET} ${DIM}(${cfg.role || "?"})${RESET}`);
|
|
437
260
|
} else {
|
|
438
|
-
|
|
261
|
+
console.log(` ${DIM}No Qualia config found at${RESET} ${WHITE}${CONFIG_FILE}${RESET}`);
|
|
439
262
|
}
|
|
263
|
+
console.log("");
|
|
440
264
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
async function runUpdate() {
|
|
452
|
-
log('');
|
|
453
|
-
log(`${c.cyan}\u25C6 Qualia Framework \u2014 Update${c.reset}`);
|
|
454
|
-
log('');
|
|
455
|
-
|
|
456
|
-
if (!fs.existsSync(CLAUDE_DIR)) {
|
|
457
|
-
fail('No ~/.claude/ found. Run the installer first (without "update").');
|
|
458
|
-
process.exit(1);
|
|
265
|
+
if (!skipConfirm) {
|
|
266
|
+
const confirm = await promptYesNo("Are you sure you want to uninstall the Qualia Framework?", false);
|
|
267
|
+
if (!confirm) {
|
|
268
|
+
console.log("");
|
|
269
|
+
console.log(` ${DIM}Aborted.${RESET}`);
|
|
270
|
+
console.log("");
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
459
273
|
}
|
|
460
274
|
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
fail(`Unknown code "${code}". Contact Fawzi for your employee code.`);
|
|
469
|
-
process.exit(1);
|
|
275
|
+
// Preserve knowledge base by default.
|
|
276
|
+
let preserveKnowledge = true;
|
|
277
|
+
if (!skipConfirm) {
|
|
278
|
+
preserveKnowledge = await promptYesNo(
|
|
279
|
+
"Preserve knowledge base? (your learned patterns, fixes, client prefs)",
|
|
280
|
+
true
|
|
281
|
+
);
|
|
470
282
|
}
|
|
471
283
|
|
|
472
|
-
log(
|
|
473
|
-
log(
|
|
284
|
+
console.log("");
|
|
285
|
+
console.log(` ${DIM}Removing framework files...${RESET}`);
|
|
286
|
+
console.log("");
|
|
474
287
|
|
|
475
|
-
|
|
476
|
-
const frameworkDirs = ['skills', 'hooks', 'agents', 'rules', 'qualia-framework', 'scripts', 'knowledge'];
|
|
477
|
-
const counts = {};
|
|
478
|
-
let totalFiles = 0;
|
|
288
|
+
const counters = { filesRemoved: 0, dirsRemoved: 0, settingsCleaned: false, errors: [] };
|
|
479
289
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
290
|
+
// Skills — any directory starting with "qualia" under ~/.claude/skills/.
|
|
291
|
+
const skillsDir = path.join(CLAUDE_DIR, "skills");
|
|
292
|
+
try {
|
|
293
|
+
if (fs.existsSync(skillsDir)) {
|
|
294
|
+
for (const name of fs.readdirSync(skillsDir)) {
|
|
295
|
+
if (name === "qualia" || name.startsWith("qualia-")) {
|
|
296
|
+
safeRmDir(path.join(skillsDir, name), counters);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
486
299
|
}
|
|
300
|
+
} catch (e) {
|
|
301
|
+
counters.errors.push(`skills scan: ${e.message}`);
|
|
487
302
|
}
|
|
488
303
|
|
|
489
|
-
//
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
log(` ${c.dim}\u2022${c.reset} ${dir} (${count} files)`);
|
|
304
|
+
// Agents — only the 4 Qualia ones.
|
|
305
|
+
for (const f of QUALIA_AGENT_FILES) {
|
|
306
|
+
safeUnlink(path.join(CLAUDE_DIR, "agents", f), counters);
|
|
493
307
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
let updated = 0;
|
|
499
|
-
for (const dir of frameworkDirs) {
|
|
500
|
-
const src = path.join(FRAMEWORK_DIR, dir);
|
|
501
|
-
const dest = path.join(CLAUDE_DIR, dir);
|
|
502
|
-
if (fs.existsSync(src)) {
|
|
503
|
-
copyDirSync(src, dest);
|
|
504
|
-
updated++;
|
|
505
|
-
ok(`${dir} (${counts[dir]} files)`);
|
|
506
|
-
}
|
|
308
|
+
|
|
309
|
+
// Hooks — only the 8 Qualia ones.
|
|
310
|
+
for (const f of QUALIA_HOOK_FILES) {
|
|
311
|
+
safeUnlink(path.join(CLAUDE_DIR, "hooks", f), counters);
|
|
507
312
|
}
|
|
508
313
|
|
|
509
|
-
//
|
|
510
|
-
for (const f of
|
|
511
|
-
|
|
512
|
-
if (fs.existsSync(src)) {
|
|
513
|
-
fs.copyFileSync(src, path.join(CLAUDE_DIR, f));
|
|
514
|
-
try { fs.chmodSync(path.join(CLAUDE_DIR, f), 0o755); } catch {}
|
|
515
|
-
}
|
|
314
|
+
// Bin scripts — only the 3 Qualia ones.
|
|
315
|
+
for (const f of QUALIA_BIN_FILES) {
|
|
316
|
+
safeUnlink(path.join(CLAUDE_DIR, "bin", f), counters);
|
|
516
317
|
}
|
|
517
318
|
|
|
518
|
-
//
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
for (const f of fs.readdirSync(hooksDir)) {
|
|
522
|
-
if (f.endsWith('.sh')) try { fs.chmodSync(path.join(hooksDir, f), 0o755); } catch {}
|
|
523
|
-
}
|
|
319
|
+
// Rules — all 4.
|
|
320
|
+
for (const f of QUALIA_RULE_FILES) {
|
|
321
|
+
safeUnlink(path.join(CLAUDE_DIR, "rules", f), counters);
|
|
524
322
|
}
|
|
525
323
|
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
324
|
+
// Templates directory (entire).
|
|
325
|
+
safeRmDir(path.join(CLAUDE_DIR, "qualia-templates"), counters);
|
|
326
|
+
|
|
327
|
+
// Knowledge directory (optional preservation).
|
|
328
|
+
if (!preserveKnowledge) {
|
|
329
|
+
safeRmDir(path.join(CLAUDE_DIR, "knowledge"), counters);
|
|
532
330
|
}
|
|
533
331
|
|
|
534
|
-
//
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
332
|
+
// Config + state files.
|
|
333
|
+
safeUnlink(path.join(CLAUDE_DIR, ".qualia-config.json"), counters);
|
|
334
|
+
safeUnlink(path.join(CLAUDE_DIR, ".qualia-last-update-check"), counters);
|
|
335
|
+
safeUnlink(path.join(CLAUDE_DIR, ".erp-api-key"), counters);
|
|
336
|
+
safeUnlink(path.join(CLAUDE_DIR, "qualia-guide.md"), counters);
|
|
337
|
+
|
|
338
|
+
// Clean settings.json surgically.
|
|
339
|
+
cleanSettingsJson(counters);
|
|
340
|
+
|
|
341
|
+
// Summary.
|
|
342
|
+
console.log("");
|
|
343
|
+
console.log(`${TEAL} ⬢ Uninstall complete${RESET}`);
|
|
344
|
+
console.log(`${DIM} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
|
|
345
|
+
console.log(` ${DIM}Files removed:${RESET} ${WHITE}${counters.filesRemoved}${RESET}`);
|
|
346
|
+
console.log(` ${DIM}Directories removed:${RESET} ${WHITE}${counters.dirsRemoved}${RESET}`);
|
|
347
|
+
console.log(
|
|
348
|
+
` ${DIM}settings.json:${RESET} ${counters.settingsCleaned ? `${GREEN}cleaned ✓${RESET}` : `${DIM}not present${RESET}`}`
|
|
349
|
+
);
|
|
350
|
+
if (preserveKnowledge) {
|
|
351
|
+
console.log(` ${DIM}Knowledge base:${RESET} ${GREEN}preserved ✓${RESET}`);
|
|
352
|
+
} else {
|
|
353
|
+
console.log(` ${DIM}Knowledge base:${RESET} ${YELLOW}removed${RESET}`);
|
|
548
354
|
}
|
|
549
355
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
ok(`removed stale ${stale}/`);
|
|
356
|
+
if (counters.errors.length > 0) {
|
|
357
|
+
console.log("");
|
|
358
|
+
console.log(` ${YELLOW}${counters.errors.length} warning(s):${RESET}`);
|
|
359
|
+
for (const err of counters.errors.slice(0, 5)) {
|
|
360
|
+
console.log(` ${DIM}${err}${RESET}`);
|
|
556
361
|
}
|
|
557
362
|
}
|
|
558
363
|
|
|
559
|
-
log(
|
|
560
|
-
log(
|
|
561
|
-
|
|
562
|
-
|
|
364
|
+
console.log("");
|
|
365
|
+
console.log(
|
|
366
|
+
` ${YELLOW}Manual step:${RESET} edit ${WHITE}~/.claude/CLAUDE.md${RESET} to remove the Qualia Framework section if desired.`
|
|
367
|
+
);
|
|
368
|
+
console.log("");
|
|
563
369
|
}
|
|
564
370
|
|
|
565
|
-
function
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
|
|
371
|
+
function cmdHelp() {
|
|
372
|
+
banner();
|
|
373
|
+
console.log("");
|
|
374
|
+
console.log(` ${WHITE}Commands:${RESET}`);
|
|
375
|
+
console.log(` npx qualia-framework ${TEAL}install${RESET} Install or reinstall the framework`);
|
|
376
|
+
console.log(` npx qualia-framework ${TEAL}update${RESET} Update to the latest version`);
|
|
377
|
+
console.log(` npx qualia-framework ${TEAL}version${RESET} Show installed version + check for updates`);
|
|
378
|
+
console.log(` npx qualia-framework ${TEAL}uninstall${RESET} Clean removal from ~/.claude/ (${DIM}-y to skip prompts${RESET})`);
|
|
379
|
+
console.log("");
|
|
380
|
+
console.log(` ${WHITE}After install:${RESET}`);
|
|
381
|
+
console.log(` ${TG}/qualia${RESET} What should I do next?`);
|
|
382
|
+
console.log(` ${TG}/qualia-new${RESET} Set up a new project`);
|
|
383
|
+
console.log(` ${TG}/qualia-plan${RESET} Plan a phase`);
|
|
384
|
+
console.log(` ${TG}/qualia-build${RESET} Build it (parallel tasks)`);
|
|
385
|
+
console.log(` ${TG}/qualia-verify${RESET} Verify it works`);
|
|
386
|
+
console.log(` ${TG}/qualia-design${RESET} One-shot design fix`);
|
|
387
|
+
console.log(` ${TG}/qualia-debug${RESET} Structured debugging`);
|
|
388
|
+
console.log(` ${TG}/qualia-review${RESET} Production audit`);
|
|
389
|
+
console.log(` ${TG}/qualia-ship${RESET} Deploy to production`);
|
|
390
|
+
console.log(` ${TG}/qualia-report${RESET} Log your work`);
|
|
391
|
+
console.log("");
|
|
575
392
|
}
|
|
576
393
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
log('');
|
|
603
|
-
if (pass === total) {
|
|
604
|
-
log(`${c.green}All ${total} checks passed.${c.reset}`);
|
|
605
|
-
} else {
|
|
606
|
-
log(`${c.yellow}${pass}/${total} checks passed.${c.reset}`);
|
|
607
|
-
}
|
|
608
|
-
log('');
|
|
394
|
+
// ─── Main ────────────────────────────────────────────────
|
|
395
|
+
const cmd = process.argv[2];
|
|
396
|
+
|
|
397
|
+
switch (cmd) {
|
|
398
|
+
case "install":
|
|
399
|
+
cmdInstall();
|
|
400
|
+
break;
|
|
401
|
+
case "version":
|
|
402
|
+
case "-v":
|
|
403
|
+
case "--version":
|
|
404
|
+
cmdVersion();
|
|
405
|
+
break;
|
|
406
|
+
case "update":
|
|
407
|
+
case "upgrade":
|
|
408
|
+
cmdUpdate();
|
|
409
|
+
break;
|
|
410
|
+
case "uninstall":
|
|
411
|
+
case "remove":
|
|
412
|
+
cmdUninstall().catch((e) => {
|
|
413
|
+
console.error(`${RED} ✗ Uninstall failed: ${e.message}${RESET}`);
|
|
414
|
+
process.exit(1);
|
|
415
|
+
});
|
|
416
|
+
break;
|
|
417
|
+
default:
|
|
418
|
+
cmdHelp();
|
|
609
419
|
}
|
|
610
|
-
|
|
611
|
-
main().catch(err => {
|
|
612
|
-
console.error(`${c.red}Error:${c.reset}`, err.message);
|
|
613
|
-
process.exit(1);
|
|
614
|
-
});
|