qualia-framework 6.2.9 → 6.3.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/AGENTS.md +1 -0
- package/CLAUDE.md +1 -0
- package/README.md +26 -30
- package/agents/builder.md +7 -7
- package/agents/planner.md +39 -3
- package/agents/research-synthesizer.md +1 -1
- package/agents/researcher.md +3 -3
- package/agents/roadmapper.md +7 -7
- package/agents/verifier.md +18 -6
- package/agents/visual-evaluator.md +8 -7
- package/bin/cli.js +160 -16
- package/bin/command-surface.js +71 -0
- package/bin/contract-runner.js +219 -0
- package/bin/harness-eval.js +296 -0
- package/bin/host-adapters.js +66 -0
- package/bin/install.js +116 -172
- package/bin/knowledge-flush.js +21 -10
- package/bin/knowledge.js +1 -1
- package/bin/plan-contract.js +99 -2
- package/bin/planning-hygiene.js +262 -0
- package/bin/project-snapshot.js +20 -0
- package/bin/report-payload.js +18 -0
- package/bin/runtime-manifest.js +35 -0
- package/bin/state-ledger.js +184 -0
- package/bin/state.js +330 -20
- package/bin/trust-score.js +268 -0
- package/bin/work-packet.js +228 -0
- package/docs/erp-contract.md +81 -1
- package/docs/onboarding.html +4 -14
- package/guide.md +16 -16
- package/hooks/fawzi-approval-guard.js +143 -0
- package/hooks/pre-deploy-gate.js +74 -1
- package/hooks/session-start.js +29 -1
- package/package.json +1 -1
- package/qualia-design/design-rubric.md +17 -5
- package/qualia-design/frontend.md +6 -2
- package/qualia-design/graphics.md +47 -0
- package/rules/codex-goal.md +1 -1
- package/rules/command-output.md +35 -0
- package/rules/one-opinion.md +2 -2
- package/rules/speed.md +0 -1
- package/skills/qualia/SKILL.md +12 -12
- package/skills/qualia-build/SKILL.md +20 -14
- package/skills/qualia-discuss/SKILL.md +10 -10
- package/skills/qualia-doctor/SKILL.md +140 -0
- package/skills/qualia-feature/SKILL.md +24 -22
- package/skills/qualia-fix/SKILL.md +216 -0
- package/skills/qualia-handoff/SKILL.md +9 -9
- package/skills/qualia-learn/SKILL.md +11 -11
- package/skills/qualia-map/SKILL.md +2 -2
- package/skills/qualia-milestone/SKILL.md +15 -15
- package/skills/qualia-new/REFERENCE.md +9 -9
- package/skills/qualia-new/SKILL.md +14 -14
- package/skills/qualia-optimize/REFERENCE.md +1 -1
- package/skills/qualia-optimize/SKILL.md +23 -16
- package/skills/qualia-plan/SKILL.md +23 -13
- package/skills/qualia-polish/REFERENCE.md +15 -15
- package/skills/qualia-polish/SKILL.md +81 -21
- package/skills/qualia-polish/scripts/loop.mjs +3 -3
- package/skills/qualia-polish/scripts/score.mjs +9 -3
- package/skills/{qualia-vibe/scripts/extract.mjs → qualia-polish/scripts/vibe-extract.mjs} +5 -5
- package/skills/{qualia-vibe/scripts/tokens.mjs → qualia-polish/scripts/vibe-tokens.mjs} +6 -6
- package/skills/qualia-postmortem/SKILL.md +9 -9
- package/skills/qualia-report/SKILL.md +23 -23
- package/skills/qualia-research/SKILL.md +5 -5
- package/skills/qualia-review/SKILL.md +28 -12
- package/skills/qualia-road/SKILL.md +30 -22
- package/skills/qualia-ship/SKILL.md +31 -24
- package/skills/qualia-test/SKILL.md +5 -5
- package/skills/qualia-verify/SKILL.md +45 -23
- package/skills/zoho-workflow/SKILL.md +1 -1
- package/templates/help.html +11 -20
- package/tests/bin.test.sh +178 -76
- package/tests/hooks.test.sh +81 -1
- package/tests/install-smoke.test.sh +35 -5
- package/tests/lib.test.sh +432 -0
- package/tests/published-install-smoke.test.sh +4 -3
- package/tests/refs.test.sh +9 -4
- package/tests/runner.js +32 -28
- package/tests/skills.test.sh +4 -4
- package/tests/state.test.sh +133 -3
- package/skills/qualia-debug/SKILL.md +0 -185
- package/skills/qualia-flush/SKILL.md +0 -198
- package/skills/qualia-help/SKILL.md +0 -74
- package/skills/qualia-hook-gen/SKILL.md +0 -206
- package/skills/qualia-idk/SKILL.md +0 -166
- package/skills/qualia-issues/SKILL.md +0 -151
- package/skills/qualia-pause/SKILL.md +0 -68
- package/skills/qualia-resume/SKILL.md +0 -52
- package/skills/qualia-skill-new/SKILL.md +0 -173
- package/skills/qualia-triage/SKILL.md +0 -152
- package/skills/qualia-vibe/SKILL.md +0 -226
- package/skills/qualia-zoom/SKILL.md +0 -51
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Qualia harness eval — deterministic project scoring + evidence artifact.
|
|
3
|
+
//
|
|
4
|
+
// This is the mechanical layer above trust-score and contract-runner. It writes
|
|
5
|
+
// one JSON/Markdown artifact that can be attached to reports, snapshots, and
|
|
6
|
+
// state transitions instead of relying on a prose "looks good" verdict.
|
|
7
|
+
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const { spawnSync } = require("child_process");
|
|
11
|
+
const pc = require("./plan-contract.js");
|
|
12
|
+
const contractRunner = require("./contract-runner.js");
|
|
13
|
+
const trust = require("./trust-score.js");
|
|
14
|
+
|
|
15
|
+
function parseArgs(argv) {
|
|
16
|
+
const args = { cwd: process.cwd() };
|
|
17
|
+
for (let i = 2; i < argv.length; i++) {
|
|
18
|
+
const a = argv[i];
|
|
19
|
+
if (a === "--json") args.json = true;
|
|
20
|
+
else if (a === "--run") args.run = true;
|
|
21
|
+
else if (a === "--write") args.write = true;
|
|
22
|
+
else if (a === "--no-write") args.no_write = true;
|
|
23
|
+
else if (a === "--phase") args.phase = Number(argv[++i]);
|
|
24
|
+
else if (a.startsWith("--phase=")) args.phase = Number(a.slice("--phase=".length));
|
|
25
|
+
else if (a === "--cwd") args.cwd = argv[++i];
|
|
26
|
+
else if (a.startsWith("--cwd=")) args.cwd = a.slice("--cwd=".length);
|
|
27
|
+
}
|
|
28
|
+
return args;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readJson(file, fallback = null) {
|
|
32
|
+
try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch { return fallback; }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readText(file, fallback = "") {
|
|
36
|
+
try { return fs.readFileSync(file, "utf8"); } catch { return fallback; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function rel(root, file) {
|
|
40
|
+
return path.relative(root, file).replace(/\\/g, "/");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function stateCheck(cwd) {
|
|
44
|
+
const r = spawnSync(process.execPath, [path.join(__dirname, "state.js"), "check"], {
|
|
45
|
+
cwd,
|
|
46
|
+
encoding: "utf8",
|
|
47
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
48
|
+
timeout: 5000,
|
|
49
|
+
});
|
|
50
|
+
const parsed = readJsonFromString(r.stdout);
|
|
51
|
+
return {
|
|
52
|
+
ok: r.status === 0 && parsed && parsed.ok === true,
|
|
53
|
+
status: r.status,
|
|
54
|
+
stdout: r.stdout,
|
|
55
|
+
stderr: r.stderr,
|
|
56
|
+
parsed,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readJsonFromString(text) {
|
|
61
|
+
try { return JSON.parse(text); } catch { return null; }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function latestEval(cwd) {
|
|
65
|
+
const dir = path.join(cwd, ".planning", "evals");
|
|
66
|
+
try {
|
|
67
|
+
const files = fs.readdirSync(dir)
|
|
68
|
+
.filter((f) => /^harness-eval-.*\.json$/.test(f))
|
|
69
|
+
.map((f) => path.join(dir, f))
|
|
70
|
+
.sort();
|
|
71
|
+
if (!files.length) return null;
|
|
72
|
+
return readJson(files[files.length - 1], null);
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function addCheck(checks, name, weight, status, evidence, data) {
|
|
79
|
+
checks.push({
|
|
80
|
+
name,
|
|
81
|
+
weight,
|
|
82
|
+
status,
|
|
83
|
+
score: status === "pass" || status === "not_applicable" ? weight : status === "warn" ? Math.floor(weight / 2) : 0,
|
|
84
|
+
evidence,
|
|
85
|
+
...(data && typeof data === "object" ? data : {}),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function verificationVerdict(text) {
|
|
90
|
+
if (!text) return "";
|
|
91
|
+
const explicit = text.match(/^result:\s*(PASS|FAIL)$/im);
|
|
92
|
+
if (explicit) return explicit[1].toLowerCase();
|
|
93
|
+
const md = text.match(/\bVerdict\b.*\b(PASS|FAIL)\b/i);
|
|
94
|
+
if (md) return md[1].toLowerCase();
|
|
95
|
+
if (/PHASE\s+PASS|ALL\s+CRITERIA\s+PASSED/i.test(text)) return "pass";
|
|
96
|
+
if (/PHASE\s+FAIL|FAILED\s+CRITERIA|GAPS\s+FOUND/i.test(text)) return "fail";
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function writeArtifacts(cwd, result) {
|
|
101
|
+
const dir = path.join(cwd, ".planning", "evals");
|
|
102
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
103
|
+
const stamp = result.generated_at.replace(/[:.]/g, "-");
|
|
104
|
+
const jsonPath = path.join(dir, `harness-eval-${stamp}.json`);
|
|
105
|
+
const mdPath = path.join(dir, `harness-eval-${stamp}.md`);
|
|
106
|
+
fs.writeFileSync(jsonPath, JSON.stringify(result, null, 2) + "\n");
|
|
107
|
+
const lines = [
|
|
108
|
+
`# Harness Eval`,
|
|
109
|
+
``,
|
|
110
|
+
`**Generated:** ${result.generated_at}`,
|
|
111
|
+
`**Phase:** ${result.phase || "n/a"}`,
|
|
112
|
+
`**Status:** ${result.status}`,
|
|
113
|
+
`**Score:** ${result.score}/100`,
|
|
114
|
+
``,
|
|
115
|
+
`## Checks`,
|
|
116
|
+
``,
|
|
117
|
+
`| Check | Status | Score | Evidence |`,
|
|
118
|
+
`|---|---:|---:|---|`,
|
|
119
|
+
...result.checks.map((c) => `| ${c.name} | ${c.status} | ${c.score}/${c.weight} | ${String(c.evidence || "").replace(/\|/g, "\\|")} |`),
|
|
120
|
+
``,
|
|
121
|
+
];
|
|
122
|
+
fs.writeFileSync(mdPath, lines.join("\n"));
|
|
123
|
+
result.artifacts = {
|
|
124
|
+
json: rel(cwd, jsonPath),
|
|
125
|
+
markdown: rel(cwd, mdPath),
|
|
126
|
+
};
|
|
127
|
+
fs.writeFileSync(jsonPath, JSON.stringify(result, null, 2) + "\n");
|
|
128
|
+
return result.artifacts;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function buildHarnessEval(options = {}) {
|
|
132
|
+
const cwd = path.resolve(options.cwd || process.cwd());
|
|
133
|
+
const planning = path.join(cwd, ".planning");
|
|
134
|
+
const generatedAt = options.now || new Date().toISOString();
|
|
135
|
+
const checks = [];
|
|
136
|
+
|
|
137
|
+
const state = stateCheck(cwd);
|
|
138
|
+
if (!fs.existsSync(planning)) {
|
|
139
|
+
addCheck(checks, "planning_state", 15, "fail", "No .planning directory; run /qualia-new");
|
|
140
|
+
return finalize({ cwd, generatedAt, phase: options.phase || 0, checks, statusOverride: "FAIL" }, options);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const tracking = readJson(path.join(planning, "tracking.json"), {});
|
|
144
|
+
const phase = Number(options.phase || state.parsed?.phase || tracking.phase || 0);
|
|
145
|
+
addCheck(
|
|
146
|
+
checks,
|
|
147
|
+
"planning_state",
|
|
148
|
+
15,
|
|
149
|
+
state.ok ? "pass" : "fail",
|
|
150
|
+
state.ok ? "state.js check returned ok" : "state.js check failed",
|
|
151
|
+
{ state: state.parsed || null }
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const ledgerResult = (() => {
|
|
155
|
+
try {
|
|
156
|
+
const stateLedger = require("./state-ledger.js");
|
|
157
|
+
return stateLedger.validate(cwd);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return { ok: false, errors: [e.message] };
|
|
160
|
+
}
|
|
161
|
+
})();
|
|
162
|
+
addCheck(
|
|
163
|
+
checks,
|
|
164
|
+
"state_ledger",
|
|
165
|
+
10,
|
|
166
|
+
ledgerResult.ok ? "pass" : "fail",
|
|
167
|
+
ledgerResult.ok ? `${ledgerResult.count || 0} hash-chained event(s)` : (ledgerResult.errors || []).join("; ")
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const planPath = path.join(planning, `phase-${phase}-plan.md`);
|
|
171
|
+
const contractPath = path.join(planning, `phase-${phase}-contract.json`);
|
|
172
|
+
const hasPlan = fs.existsSync(planPath);
|
|
173
|
+
const hasContract = fs.existsSync(contractPath);
|
|
174
|
+
let loadedContract = null;
|
|
175
|
+
|
|
176
|
+
if (!hasPlan && !hasContract) {
|
|
177
|
+
addCheck(checks, "plan_contract", 20, "not_applicable", `No current phase plan/contract for phase ${phase}`);
|
|
178
|
+
} else if (!hasContract) {
|
|
179
|
+
addCheck(checks, "plan_contract", 20, "fail", `Missing ${rel(cwd, contractPath)}`);
|
|
180
|
+
} else {
|
|
181
|
+
const loaded = pc.readContractFile(contractPath);
|
|
182
|
+
loadedContract = loaded.ok ? loaded.contract : null;
|
|
183
|
+
const errors = loaded.ok ? pc.validate(loaded.contract) : [loaded.message || loaded.error];
|
|
184
|
+
const drift = hasPlan ? pc.checkDrift(contractPath, planPath) : { ok: true, drift: false };
|
|
185
|
+
const ok = loaded.ok && errors.length === 0 && !(drift.ok && drift.drift);
|
|
186
|
+
addCheck(
|
|
187
|
+
checks,
|
|
188
|
+
"plan_contract",
|
|
189
|
+
20,
|
|
190
|
+
ok ? "pass" : "fail",
|
|
191
|
+
ok ? `${rel(cwd, contractPath)} valid and in sync` : [...errors, drift.drift ? "contract drifted from plan" : ""].filter(Boolean).join("; "),
|
|
192
|
+
{ contract: rel(cwd, contractPath) }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const evidencePath = path.join(planning, "evidence", `phase-${phase}-contract-run.json`);
|
|
197
|
+
let evidence = readJson(evidencePath, null);
|
|
198
|
+
if (loadedContract && options.run) {
|
|
199
|
+
evidence = contractRunner.runContract(loadedContract, { cwd });
|
|
200
|
+
}
|
|
201
|
+
if (!loadedContract) {
|
|
202
|
+
addCheck(checks, "machine_evidence", 20, "not_applicable", "No contract to execute");
|
|
203
|
+
} else if (evidence && evidence.ok === true) {
|
|
204
|
+
addCheck(checks, "machine_evidence", 20, "pass", `${rel(cwd, evidencePath)} passed ${evidence.checked || 0} check(s)`);
|
|
205
|
+
} else {
|
|
206
|
+
addCheck(
|
|
207
|
+
checks,
|
|
208
|
+
"machine_evidence",
|
|
209
|
+
20,
|
|
210
|
+
"fail",
|
|
211
|
+
evidence ? `${evidence.failed || "unknown"} failing machine check(s)` : `Missing ${rel(cwd, evidencePath)}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const verificationPath = path.join(planning, `phase-${phase}-verification.md`);
|
|
216
|
+
const verification = readText(verificationPath, "");
|
|
217
|
+
const ieCount = (verification.match(/INSUFFICIENT EVIDENCE/g) || []).length;
|
|
218
|
+
const verdict = verificationVerdict(verification);
|
|
219
|
+
if (!verification) {
|
|
220
|
+
addCheck(checks, "verification_report", 15, "fail", `Missing ${rel(cwd, verificationPath)}`);
|
|
221
|
+
} else if (ieCount > 0) {
|
|
222
|
+
addCheck(checks, "verification_report", 15, "fail", `${ieCount} insufficient-evidence marker(s)`);
|
|
223
|
+
} else if (verdict === "pass" || verdict === "fail") {
|
|
224
|
+
addCheck(checks, "verification_report", 15, verdict === "pass" ? "pass" : "warn", `${rel(cwd, verificationPath)} verdict=${verdict}`);
|
|
225
|
+
} else {
|
|
226
|
+
addCheck(checks, "verification_report", 15, "warn", `${rel(cwd, verificationPath)} has no machine-readable verdict`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const trustScore = trust.buildTrustScore(cwd);
|
|
230
|
+
addCheck(
|
|
231
|
+
checks,
|
|
232
|
+
"framework_trust_score",
|
|
233
|
+
10,
|
|
234
|
+
trustScore.status === "FAIL" ? "fail" : trustScore.status === "DEGRADED" ? "warn" : "pass",
|
|
235
|
+
`trust-score=${trustScore.score}/100 status=${trustScore.status}`,
|
|
236
|
+
{ trust_score: trustScore.score }
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const hasErpId = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tracking.erp_project_id || "");
|
|
240
|
+
const hasProjectKey = !!(tracking.project_id || tracking.project);
|
|
241
|
+
addCheck(
|
|
242
|
+
checks,
|
|
243
|
+
"erp_linkage",
|
|
244
|
+
10,
|
|
245
|
+
hasErpId ? "pass" : hasProjectKey ? "warn" : "fail",
|
|
246
|
+
hasErpId ? "tracking.json has erp_project_id UUID" : hasProjectKey ? "project key exists, ERP UUID missing" : "missing project/project_id for ERP correlation"
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return finalize({ cwd, generatedAt, phase, checks }, options);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function finalize(base, options) {
|
|
253
|
+
const score = Math.max(0, Math.min(100, base.checks.reduce((n, c) => n + (c.score || 0), 0)));
|
|
254
|
+
const failed = base.checks.filter((c) => c.status === "fail").length;
|
|
255
|
+
const warned = base.checks.filter((c) => c.status === "warn").length;
|
|
256
|
+
const status = base.statusOverride || (failed ? "FAIL" : score >= 85 ? "PASS" : warned ? "WARN" : "PASS");
|
|
257
|
+
const result = {
|
|
258
|
+
ok: status !== "FAIL",
|
|
259
|
+
status,
|
|
260
|
+
score,
|
|
261
|
+
phase: base.phase,
|
|
262
|
+
generated_at: base.generatedAt,
|
|
263
|
+
checks: base.checks,
|
|
264
|
+
};
|
|
265
|
+
if (options.write && !options.no_write && fs.existsSync(path.join(base.cwd, ".planning"))) {
|
|
266
|
+
writeArtifacts(base.cwd, result);
|
|
267
|
+
}
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function printHuman(result) {
|
|
272
|
+
console.log(`Harness eval: ${result.score}/100 (${result.status})`);
|
|
273
|
+
for (const c of result.checks) {
|
|
274
|
+
console.log(`${c.name}: ${c.status} (${c.score}/${c.weight}) — ${c.evidence}`);
|
|
275
|
+
}
|
|
276
|
+
if (result.artifacts) {
|
|
277
|
+
console.log(`Artifacts: ${result.artifacts.json}, ${result.artifacts.markdown}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function main(argv) {
|
|
282
|
+
const args = parseArgs(argv);
|
|
283
|
+
const result = buildHarnessEval(args);
|
|
284
|
+
if (args.json) console.log(JSON.stringify(result, null, 2));
|
|
285
|
+
else printHuman(result);
|
|
286
|
+
return result.ok ? 0 : 1;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = {
|
|
290
|
+
buildHarnessEval,
|
|
291
|
+
latestEval,
|
|
292
|
+
verificationVerdict,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
if (require.main === module) process.exit(main(process.argv));
|
|
296
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Host adapter rendering for installed Qualia text surfaces.
|
|
3
|
+
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
const HOSTS = {
|
|
8
|
+
claude: {
|
|
9
|
+
name: "Claude Code",
|
|
10
|
+
home: path.join(os.homedir(), ".claude"),
|
|
11
|
+
},
|
|
12
|
+
codex: {
|
|
13
|
+
name: "OpenAI Codex",
|
|
14
|
+
home: path.join(os.homedir(), ".codex"),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function adapter(name) {
|
|
19
|
+
const host = HOSTS[name];
|
|
20
|
+
if (!host) throw new Error(`Unknown Qualia host adapter: ${name}`);
|
|
21
|
+
const home = host.home;
|
|
22
|
+
return {
|
|
23
|
+
...host,
|
|
24
|
+
tokens: {
|
|
25
|
+
QUALIA_HOME: home,
|
|
26
|
+
QUALIA_BIN: `${home}/bin`,
|
|
27
|
+
QUALIA_AGENTS: `${home}/agents`,
|
|
28
|
+
QUALIA_SKILLS: `${home}/skills`,
|
|
29
|
+
QUALIA_RULES: `${home}/rules`,
|
|
30
|
+
QUALIA_TEMPLATES: `${home}/qualia-templates`,
|
|
31
|
+
QUALIA_KNOWLEDGE: `${home}/knowledge`,
|
|
32
|
+
QUALIA_REFERENCES: `${home}/qualia-references`,
|
|
33
|
+
QUALIA_DESIGN: `${home}/qualia-design`,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderText(content, hostName) {
|
|
39
|
+
const host = adapter(hostName);
|
|
40
|
+
let out = String(content);
|
|
41
|
+
for (const [token, value] of Object.entries(host.tokens)) {
|
|
42
|
+
out = out.replaceAll(`\${${token}}`, value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Backward-compatible rendering while source files migrate from hardcoded
|
|
46
|
+
// Claude paths to explicit ${QUALIA_*} tokens.
|
|
47
|
+
out = out
|
|
48
|
+
.replaceAll("~/.claude/", `${host.home}/`)
|
|
49
|
+
.replaceAll("$HOME/.claude/", `${host.home}/`)
|
|
50
|
+
.replaceAll("${HOME}/.claude/", `${host.home}/`)
|
|
51
|
+
.replaceAll("@~/.claude/", `@${host.home}/`)
|
|
52
|
+
.replaceAll(".claude/", `${path.basename(host.home)}/`);
|
|
53
|
+
|
|
54
|
+
if (hostName === "codex") {
|
|
55
|
+
out = out
|
|
56
|
+
.replaceAll("Claude Code", "Codex")
|
|
57
|
+
.replaceAll("Claude's", "Codex's");
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
HOSTS,
|
|
64
|
+
adapter,
|
|
65
|
+
renderText,
|
|
66
|
+
};
|