qualia-framework 4.1.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -11
- package/agents/builder.md +28 -0
- package/agents/research-synthesizer.md +7 -0
- package/bin/agent-runs.js +233 -0
- package/bin/cli.js +355 -16
- package/bin/install.js +87 -6
- package/bin/knowledge-flush.js +164 -0
- package/bin/knowledge.js +317 -0
- package/bin/plan-contract.js +220 -0
- package/bin/state.js +15 -9
- package/docs/agent-runs.md +273 -0
- package/docs/journey-demo.html +1008 -0
- package/docs/plan-contract.md +321 -0
- package/docs/reviews/v4.1.0-audit.html +1488 -0
- package/docs/reviews/v4.1.0-audit.md +263 -0
- package/hooks/auto-update.js +3 -7
- package/hooks/git-guardrails.js +167 -0
- package/hooks/pre-compact.js +22 -11
- package/hooks/pre-deploy-gate.js +16 -2
- package/hooks/pre-push.js +22 -2
- package/hooks/stop-session-log.js +180 -0
- package/package.json +8 -2
- package/skills/qualia-build/SKILL.md +5 -5
- package/skills/qualia-debug/SKILL.md +1 -1
- package/skills/qualia-design/SKILL.md +15 -0
- package/skills/qualia-flush/SKILL.md +200 -0
- package/skills/qualia-learn/SKILL.md +47 -37
- package/skills/qualia-new/SKILL.md +1 -1
- package/skills/qualia-plan/SKILL.md +3 -2
- package/skills/qualia-postmortem/SKILL.md +238 -0
- package/skills/qualia-quick/SKILL.md +1 -1
- package/skills/qualia-report/SKILL.md +1 -1
- package/skills/qualia-review/SKILL.md +3 -2
- package/skills/qualia-ship/SKILL.md +12 -10
- package/skills/qualia-verify/SKILL.md +60 -0
- package/templates/help.html +13 -7
- package/templates/knowledge/agents.md +71 -0
- package/templates/knowledge/index.md +47 -0
- package/tests/bin.test.sh +322 -12
- package/tests/hooks.test.sh +131 -20
- package/tests/lib.test.sh +217 -0
- package/tests/runner.js +103 -77
- package/tests/state.test.sh +4 -3
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ~/.claude/hooks/stop-session-log.js — append a one-line session checkpoint
|
|
3
|
+
// to ~/.claude/knowledge/daily-log/{YYYY-MM-DD}.md.
|
|
4
|
+
//
|
|
5
|
+
// Stop hook (fires when a Claude Code turn ends). This is the seed of the
|
|
6
|
+
// memory layer (v4.2.0): every turn that produced something noteworthy gets a
|
|
7
|
+
// line in today's daily log. Future versions will run an LLM "flush" job over
|
|
8
|
+
// the daily log to promote durable knowledge into the wiki tier.
|
|
9
|
+
//
|
|
10
|
+
// Design constraints:
|
|
11
|
+
// • NEVER block — exit 0 always, even on internal failure.
|
|
12
|
+
// • Fast — under 100ms, no network, no LLM call. The log is mechanical.
|
|
13
|
+
// • Cheap to skip — if there's been no git activity since the last write,
|
|
14
|
+
// and we wrote a line within the last 5 minutes, we skip. Daily log
|
|
15
|
+
// stays scannable instead of becoming a turn-by-turn replay.
|
|
16
|
+
// • No PII / secrets — only project name, branch, phase, task counts,
|
|
17
|
+
// commit count. Never the contents of files or env vars.
|
|
18
|
+
//
|
|
19
|
+
// Format: append to today's file under a single H2 header per project:
|
|
20
|
+
//
|
|
21
|
+
// ## qualia-framework
|
|
22
|
+
// - 14:32 · branch=feat/x · phase=2/4 · tasks=3/5 · commits=2 · touched=src/foo.ts,src/bar.ts
|
|
23
|
+
// - 16:08 · branch=feat/x · phase=2/4 · tasks=4/5 · commits=4
|
|
24
|
+
//
|
|
25
|
+
// Cross-platform (Windows/macOS/Linux). Zero shell dependencies.
|
|
26
|
+
|
|
27
|
+
const fs = require("fs");
|
|
28
|
+
const path = require("path");
|
|
29
|
+
const os = require("os");
|
|
30
|
+
const { spawnSync } = require("child_process");
|
|
31
|
+
|
|
32
|
+
const _traceStart = Date.now();
|
|
33
|
+
|
|
34
|
+
const KNOWLEDGE_DIR = path.join(os.homedir(), ".claude", "knowledge");
|
|
35
|
+
const DAILY_DIR = path.join(KNOWLEDGE_DIR, "daily-log");
|
|
36
|
+
const LAST_WRITE_FILE = path.join(os.homedir(), ".claude", ".qualia-last-session-log");
|
|
37
|
+
const MIN_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
38
|
+
|
|
39
|
+
function _trace(result, extra) {
|
|
40
|
+
try {
|
|
41
|
+
const traceDir = path.join(os.homedir(), ".claude", ".qualia-traces");
|
|
42
|
+
if (!fs.existsSync(traceDir)) fs.mkdirSync(traceDir, { recursive: true });
|
|
43
|
+
fs.appendFileSync(
|
|
44
|
+
path.join(traceDir, `${new Date().toISOString().split("T")[0]}.jsonl`),
|
|
45
|
+
JSON.stringify({
|
|
46
|
+
hook: "stop-session-log",
|
|
47
|
+
result,
|
|
48
|
+
timestamp: new Date().toISOString(),
|
|
49
|
+
duration_ms: Date.now() - _traceStart,
|
|
50
|
+
...extra,
|
|
51
|
+
}) + "\n",
|
|
52
|
+
);
|
|
53
|
+
} catch {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function git(args, opts = {}) {
|
|
57
|
+
try {
|
|
58
|
+
const r = spawnSync("git", args, {
|
|
59
|
+
encoding: "utf8",
|
|
60
|
+
timeout: 2000,
|
|
61
|
+
shell: process.platform === "win32",
|
|
62
|
+
...opts,
|
|
63
|
+
});
|
|
64
|
+
if (r.status !== 0) return "";
|
|
65
|
+
return (r.stdout || "").trim();
|
|
66
|
+
} catch {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readJson(p) {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(fs.readFileSync(p, "utf8"));
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// ── Skip if too soon since last write ────────────────────
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
let lastWrite = 0;
|
|
83
|
+
try {
|
|
84
|
+
if (fs.existsSync(LAST_WRITE_FILE)) {
|
|
85
|
+
lastWrite = parseInt(fs.readFileSync(LAST_WRITE_FILE, "utf8").trim(), 10) || 0;
|
|
86
|
+
}
|
|
87
|
+
} catch {}
|
|
88
|
+
if (now - lastWrite < MIN_INTERVAL_MS) {
|
|
89
|
+
_trace("skip", { reason: "interval", since_last_ms: now - lastWrite });
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── Resolve project context ──────────────────────────────
|
|
94
|
+
// Operate from the cwd; if not a git repo, fall back to basename.
|
|
95
|
+
const cwd = process.cwd();
|
|
96
|
+
const repoRoot = git(["rev-parse", "--show-toplevel"], { cwd }) || cwd;
|
|
97
|
+
const project = path.basename(repoRoot);
|
|
98
|
+
const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: repoRoot }) || "";
|
|
99
|
+
|
|
100
|
+
// Phase + task info from .planning/tracking.json (Qualia projects only).
|
|
101
|
+
let phase = "";
|
|
102
|
+
let tasks = "";
|
|
103
|
+
const tracking = readJson(path.join(repoRoot, ".planning", "tracking.json"));
|
|
104
|
+
if (tracking) {
|
|
105
|
+
const p = tracking.phase || 0;
|
|
106
|
+
const pt = tracking.total_phases || tracking.phase_total || 0;
|
|
107
|
+
if (pt > 0) phase = `${p}/${pt}`;
|
|
108
|
+
const td = tracking.tasks_done || 0;
|
|
109
|
+
const tt = tracking.tasks_total || 0;
|
|
110
|
+
if (tt > 0) tasks = `${td}/${tt}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Commits in this session window — heuristic: commits in last 8 hours by us.
|
|
114
|
+
// (Stop fires per turn, so a tighter window misses session boundaries.)
|
|
115
|
+
const commitCount = (() => {
|
|
116
|
+
const out = git(["log", "--since=8.hours", "--pretty=oneline"], { cwd: repoRoot });
|
|
117
|
+
if (!out) return 0;
|
|
118
|
+
return out.split("\n").filter(Boolean).length;
|
|
119
|
+
})();
|
|
120
|
+
|
|
121
|
+
// Top 3 touched files (uncommitted) — useful for resuming next session.
|
|
122
|
+
const touched = (() => {
|
|
123
|
+
const out = git(["diff", "--name-only", "HEAD"], { cwd: repoRoot });
|
|
124
|
+
if (!out) return [];
|
|
125
|
+
return out.split("\n").filter(Boolean).slice(0, 3);
|
|
126
|
+
})();
|
|
127
|
+
|
|
128
|
+
// Skip if literally nothing happened — no commits, no diff, no phase progress.
|
|
129
|
+
if (commitCount === 0 && touched.length === 0 && !phase) {
|
|
130
|
+
_trace("skip", { reason: "no-activity" });
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── Compose the line ─────────────────────────────────────
|
|
135
|
+
const time = new Date().toTimeString().slice(0, 5); // HH:MM, local time
|
|
136
|
+
const parts = [`${time}`];
|
|
137
|
+
if (branch) parts.push(`branch=${branch}`);
|
|
138
|
+
if (phase) parts.push(`phase=${phase}`);
|
|
139
|
+
if (tasks) parts.push(`tasks=${tasks}`);
|
|
140
|
+
if (commitCount > 0) parts.push(`commits=${commitCount}`);
|
|
141
|
+
if (touched.length > 0) parts.push(`touched=${touched.join(",")}`);
|
|
142
|
+
const line = `- ${parts.join(" · ")}`;
|
|
143
|
+
|
|
144
|
+
// ── Append to today's file ───────────────────────────────
|
|
145
|
+
if (!fs.existsSync(DAILY_DIR)) fs.mkdirSync(DAILY_DIR, { recursive: true });
|
|
146
|
+
const today = new Date().toISOString().split("T")[0];
|
|
147
|
+
const file = path.join(DAILY_DIR, `${today}.md`);
|
|
148
|
+
|
|
149
|
+
let existing = "";
|
|
150
|
+
try {
|
|
151
|
+
if (fs.existsSync(file)) existing = fs.readFileSync(file, "utf8");
|
|
152
|
+
} catch {}
|
|
153
|
+
|
|
154
|
+
const projectHeader = `## ${project}`;
|
|
155
|
+
if (!existing) {
|
|
156
|
+
const header = `# Daily log — ${today}\n\n_Auto-generated by Qualia Framework. Each entry is one Stop-hook checkpoint per project per session._\n\n${projectHeader}\n${line}\n`;
|
|
157
|
+
fs.writeFileSync(file, header);
|
|
158
|
+
} else if (!existing.includes(projectHeader)) {
|
|
159
|
+
fs.appendFileSync(file, `\n${projectHeader}\n${line}\n`);
|
|
160
|
+
} else {
|
|
161
|
+
// Append the line under the existing project header. Insert after the last
|
|
162
|
+
// line that belongs to this project's section (next ## or EOF).
|
|
163
|
+
const headerIdx = existing.indexOf(projectHeader);
|
|
164
|
+
const afterHeader = headerIdx + projectHeader.length;
|
|
165
|
+
const nextHeaderIdx = existing.indexOf("\n## ", afterHeader);
|
|
166
|
+
const insertAt = nextHeaderIdx === -1 ? existing.length : nextHeaderIdx;
|
|
167
|
+
// Trim trailing newlines in the section so our append lands on a clean line.
|
|
168
|
+
const before = existing.slice(0, insertAt).replace(/\n+$/, "");
|
|
169
|
+
const after = existing.slice(insertAt);
|
|
170
|
+
fs.writeFileSync(file, `${before}\n${line}\n${after}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fs.writeFileSync(LAST_WRITE_FILE, String(now));
|
|
174
|
+
_trace("logged", { project, branch, phase, tasks, commits: commitCount });
|
|
175
|
+
process.exit(0);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
_trace("error", { error: err && err.message ? err.message : String(err) });
|
|
178
|
+
// Stop hooks must never block the user — exit 0 even on internal error.
|
|
179
|
+
process.exit(0);
|
|
180
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qualia-framework",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "Claude Code workflow framework by Qualia Solutions. Plan, build, verify, ship.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"qualia-framework": "./bin/cli.js"
|
|
@@ -23,7 +23,13 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/Qualiasolutions/qualia-framework#readme",
|
|
25
25
|
"scripts": {
|
|
26
|
-
"test": "
|
|
26
|
+
"test": "npm run test:shell",
|
|
27
|
+
"test:state": "bash tests/state.test.sh",
|
|
28
|
+
"test:hooks": "bash tests/hooks.test.sh",
|
|
29
|
+
"test:bin": "bash tests/bin.test.sh",
|
|
30
|
+
"test:lib": "bash tests/lib.test.sh",
|
|
31
|
+
"test:statusline": "bash tests/statusline.test.sh",
|
|
32
|
+
"test:shell": "bash tests/statusline.test.sh && bash tests/state.test.sh && bash tests/hooks.test.sh && bash tests/bin.test.sh && bash tests/lib.test.sh"
|
|
27
33
|
},
|
|
28
34
|
"files": [
|
|
29
35
|
"bin/",
|
|
@@ -32,9 +32,9 @@ cat .planning/phase-{N}-plan.md
|
|
|
32
32
|
|
|
33
33
|
Parse: tasks, waves, file references.
|
|
34
34
|
|
|
35
|
-
### 1b. Create Recovery
|
|
35
|
+
### 1b. Create Recovery Reference
|
|
36
36
|
|
|
37
|
-
Before executing any tasks,
|
|
37
|
+
Before executing any tasks, record current HEAD for diagnosis. This is a reference, not an automatic rollback instruction.
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
git tag -f "pre-build-phase-{N}" HEAD 2>/dev/null
|
|
@@ -44,10 +44,10 @@ git tag -f "pre-build-phase-{N}" HEAD 2>/dev/null
|
|
|
44
44
|
node ~/.claude/bin/qualia-ui.js info "Recovery point: pre-build-phase-{N}"
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
If a wave fails and the user
|
|
47
|
+
If a wave fails, stop and inspect `git status` plus the failed task output. Do not run destructive rollback commands automatically. Preserve user work, then either fix forward or ask before reverting specific files.
|
|
48
48
|
```bash
|
|
49
|
-
git
|
|
50
|
-
|
|
49
|
+
git status --short
|
|
50
|
+
git diff --stat
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
### 2. Execute Waves
|
|
@@ -40,7 +40,7 @@ node ~/.claude/bin/qualia-ui.js banner debug
|
|
|
40
40
|
### 2. Check Known Fixes First (cheap)
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
|
|
43
|
+
node ~/.claude/bin/knowledge.js search "{symptom_keywords}"
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
If a known fix matches, apply it and jump to step 5 (verify). Known fixes are pre-verified patterns — no need to re-investigate.
|
|
@@ -59,6 +59,21 @@ Apply fixes to every HIGH and CRITICAL item. MEDIUM items fixed if cheap (same f
|
|
|
59
59
|
|
|
60
60
|
Split target files into batches of 5. Spawn one Agent per batch IN THE SAME RESPONSE TURN (parallel execution). Each agent receives DESIGN.md inlined + its 5 files + the Design Quality Rubric from `rules/grounding.md`. Agents return their batch's critique table + the actual edits applied. The skill orchestrator fans in the results and runs the final verification (step 5).
|
|
61
61
|
|
|
62
|
+
**Forked subagents (v4.2.0+):** if the current conversation already contains
|
|
63
|
+
design taste discussion (font choices, palette discussion, motion preferences,
|
|
64
|
+
or any color/typography critique threaded across multiple turns) AND
|
|
65
|
+
`CLAUDE_AGENT_FORK_ENABLED=1` is set in `~/.claude/settings.json` (the v4.2.0
|
|
66
|
+
default), prefer **forked subagents** over blank-context fan-out. Forks
|
|
67
|
+
inherit the entire conversation history + share the prompt cache, so the
|
|
68
|
+
batch agents see the 50k tokens of accumulated taste instead of a 2k-token
|
|
69
|
+
compression. Anthropic shipped this in 2026-04 specifically to solve the
|
|
70
|
+
"design subagent loses nuance" failure mode (NotebookLM 2026-04-25 source).
|
|
71
|
+
Tell Claude explicitly: "spawn forked subagents to handle these batches in
|
|
72
|
+
parallel." For variation-generation work (3 alternative homepage designs)
|
|
73
|
+
forks are almost always the right call. For mechanical anti-pattern fixes
|
|
74
|
+
(rip out `outline:none`, swap font tokens) blank context is fine — no
|
|
75
|
+
nuance to inherit. When in doubt, fork — the cost is the same prompt cache.
|
|
76
|
+
|
|
62
77
|
```
|
|
63
78
|
Agent(prompt="
|
|
64
79
|
Read your role: builder for design transformation.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qualia-flush
|
|
3
|
+
description: "Promote daily-log raw entries to the curated knowledge tier — Karpathy-style raw→wiki flush. Reads ~/.claude/knowledge/daily-log/*.md, identifies recurring patterns and decisions, writes them to ~/.claude/knowledge/concepts/{topic}.md, updates index.md. Trigger on 'flush memory', 'promote learnings', 'consolidate logs', 'qualia-flush', 'process daily logs', or run weekly."
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Grep
|
|
10
|
+
- Glob
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# /qualia-flush — Promote Raw Daily Logs to Curated Concepts
|
|
14
|
+
|
|
15
|
+
Closes the **raw → wiki** loop in the Qualia memory layer. The Stop hook
|
|
16
|
+
(`hooks/stop-session-log.js`, shipped in v4.2.0 foundation) appends
|
|
17
|
+
mechanical session checkpoints to `~/.claude/knowledge/daily-log/{date}.md`.
|
|
18
|
+
Those entries accumulate but stay raw — they describe what happened, not
|
|
19
|
+
what to do about it. This skill reads the recent daily-log entries with an
|
|
20
|
+
LLM (you) and writes durable concepts that the builder, planner, and
|
|
21
|
+
debug skills will surface later via `node ~/.claude/bin/knowledge.js`.
|
|
22
|
+
|
|
23
|
+
Inspired by Karpathy's LLM knowledge bases and Cole Medin's self-evolving
|
|
24
|
+
Claude memory pattern (NotebookLM, 2026-04-25). Both run a daily/weekly
|
|
25
|
+
flush that promotes raw observations into structured wiki articles. We do
|
|
26
|
+
the same — manually-triggered, internal-data only, no vector DB.
|
|
27
|
+
|
|
28
|
+
## When to run
|
|
29
|
+
|
|
30
|
+
- **Manually:** `/qualia-flush` whenever the daily-log feels rich. Once a week
|
|
31
|
+
is the recommended cadence. More than once a day is wasteful — the
|
|
32
|
+
signal-to-noise ratio is too low at single-day windows.
|
|
33
|
+
- **CLI runner:** `qualia-framework flush` wraps the cron-friendly
|
|
34
|
+
`bin/knowledge-flush.js` non-interactive runner.
|
|
35
|
+
|
|
36
|
+
## Inputs
|
|
37
|
+
|
|
38
|
+
- `--days N` (optional, default 14) — how many days of daily-log to consider
|
|
39
|
+
- `--project NAME` (optional) — only flush entries for one project
|
|
40
|
+
- `--dry-run` (optional) — print the proposed writes, don't touch disk
|
|
41
|
+
|
|
42
|
+
If the user invokes the skill bare (no args), default to `--days 14` and
|
|
43
|
+
all projects. Show a one-line preview before writing anything destructive.
|
|
44
|
+
|
|
45
|
+
## Process
|
|
46
|
+
|
|
47
|
+
### 1. Banner + check the floor
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node ~/.claude/bin/qualia-ui.js banner flush 2>/dev/null || true
|
|
51
|
+
|
|
52
|
+
# Resolve the knowledge dir. Fail loud if it doesn't exist — flush is
|
|
53
|
+
# meaningless without a daily-log to read.
|
|
54
|
+
KNOWLEDGE_DIR="$HOME/.claude/knowledge"
|
|
55
|
+
DAILY_DIR="$KNOWLEDGE_DIR/daily-log"
|
|
56
|
+
if [ ! -d "$DAILY_DIR" ]; then
|
|
57
|
+
echo "QUALIA: No daily-log at $DAILY_DIR — Stop hook hasn't run yet, or knowledge layer wasn't initialized."
|
|
58
|
+
echo "Run: npx qualia-framework@latest install"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Default 14-day window. Date math is cross-platform-safe with Node.
|
|
63
|
+
WINDOW_DAYS="${WINDOW_DAYS:-14}"
|
|
64
|
+
node -e "
|
|
65
|
+
const d = new Date();
|
|
66
|
+
d.setDate(d.getDate() - $WINDOW_DAYS);
|
|
67
|
+
console.log(d.toISOString().split('T')[0]);
|
|
68
|
+
" > /tmp/qualia-flush-cutoff
|
|
69
|
+
CUTOFF=$(cat /tmp/qualia-flush-cutoff)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. Collect the daily-log entries in window
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Iterate every file in daily-log/ whose name (YYYY-MM-DD.md) is >= CUTOFF.
|
|
76
|
+
# Concatenate them into one stream so the LLM (you) can scan as one corpus.
|
|
77
|
+
ls "$DAILY_DIR"/*.md 2>/dev/null | while read -r f; do
|
|
78
|
+
base=$(basename "$f" .md)
|
|
79
|
+
if [ "$base" \> "$CUTOFF" ] || [ "$base" = "$CUTOFF" ]; then
|
|
80
|
+
echo "=== $base ==="
|
|
81
|
+
cat "$f"
|
|
82
|
+
echo ""
|
|
83
|
+
fi
|
|
84
|
+
done
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
You now have the raw stream. Read it.
|
|
88
|
+
|
|
89
|
+
### 3. Identify what's worth promoting
|
|
90
|
+
|
|
91
|
+
Read every entry. Group by project. Look for these signals — these are
|
|
92
|
+
the things that promote into the wiki:
|
|
93
|
+
|
|
94
|
+
| Signal in raw entry | What to extract | Goes to |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| Same fix appears in 2+ sessions | A common fix recipe | `common-fixes.md` (via `knowledge.js append --type fix`) |
|
|
97
|
+
| A pattern shows up in 3+ projects | A reusable pattern | `learned-patterns.md` (via `knowledge.js append --type pattern`) |
|
|
98
|
+
| A client-name or project preference recurs | A client preference | `client-prefs.md` (via `knowledge.js append --type client`) |
|
|
99
|
+
| A new technology/library used successfully | A stack note | `concepts/{tech}.md` (new file, Write directly) |
|
|
100
|
+
| A recurring failure mode (verify-fail, regression) | A pitfall | `learned-patterns.md` framed as "anti-pattern: …" |
|
|
101
|
+
|
|
102
|
+
Things to **NOT** promote:
|
|
103
|
+
- Single-occurrence quirks (they're noise until they recur).
|
|
104
|
+
- Bare commit/branch info — that's already in git, no value duplicating.
|
|
105
|
+
- Anything containing secrets, tokens, customer PII. The knowledge layer
|
|
106
|
+
is plain markdown, never put secrets here.
|
|
107
|
+
- Entries from `--dry-run` runs of other skills (they'll show as activity
|
|
108
|
+
but didn't actually do anything).
|
|
109
|
+
|
|
110
|
+
### 4. Write the promotions
|
|
111
|
+
|
|
112
|
+
For each thing worth promoting, use the loader's `append`:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
node ~/.claude/bin/knowledge.js append \
|
|
116
|
+
--type {pattern|fix|client} \
|
|
117
|
+
--title "{Concise title — what's the recurring thing?}" \
|
|
118
|
+
--body "{The promoted lesson. Be specific. Include the project name(s) and dates where this pattern was observed so future you can verify.}" \
|
|
119
|
+
--project "{specific project, or 'general' if cross-project}" \
|
|
120
|
+
--context "Promoted by /qualia-flush from daily-log entries on {dates}"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
For a brand-new topic that doesn't fit pattern/fix/client (e.g. a Stripe
|
|
124
|
+
integration approach worth its own file), Write to
|
|
125
|
+
`~/.claude/knowledge/concepts/{topic}.md`. Then **update `index.md`** so the
|
|
126
|
+
new file is reachable — list it under "What's where" with one line:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# After writing concepts/stripe-checkout.md:
|
|
130
|
+
node ~/.claude/bin/knowledge.js path stripe-checkout
|
|
131
|
+
# Returned path = ~/.claude/knowledge/stripe-checkout.md (NOTE: top-level, not concepts/)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
> **Subdirectory caveat:** the v4.2.0 loader resolves bare filenames at
|
|
135
|
+
> `~/.claude/knowledge/{name}.md`. Subdirectory `concepts/` files are not
|
|
136
|
+
> reachable via `knowledge.js load <name>` yet (deferred to v4.3.0). For
|
|
137
|
+
> now, write durable concept files at the top level — flat structure is
|
|
138
|
+
> fine, the index keeps it organized.
|
|
139
|
+
|
|
140
|
+
### 5. Mark the window as flushed
|
|
141
|
+
|
|
142
|
+
Write a stamp file so subsequent flushes can default to "since the last
|
|
143
|
+
flush" instead of "last 14 days":
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
date -u +%Y-%m-%dT%H:%M:%SZ > "$HOME/.claude/.qualia-last-flush"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 6. Summarize
|
|
150
|
+
|
|
151
|
+
Print to the user, in plain language:
|
|
152
|
+
|
|
153
|
+
- N daily-log files scanned (date range)
|
|
154
|
+
- M promotions written (with file:title for each)
|
|
155
|
+
- K things considered but not promoted (single-occurrence — wait for them to recur)
|
|
156
|
+
|
|
157
|
+
Format:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
⬢ Flushed daily-log {start} → {end} ({N} files, {total entries} entries)
|
|
161
|
+
Promoted to wiki:
|
|
162
|
+
+ learned-patterns.md "Supabase RLS in same migration" (3 sessions, 2 projects)
|
|
163
|
+
+ common-fixes.md "next/font crash on Vercel" (2 sessions)
|
|
164
|
+
+ concepts/voice-agent-call-state.md (new file)
|
|
165
|
+
Skipped {K} single-occurrence entries — will revisit if they recur.
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Style
|
|
169
|
+
|
|
170
|
+
- **Be conservative.** False-positive promotions (writing noise as if it's a
|
|
171
|
+
pattern) pollute the wiki and erode trust. Better to skip a candidate and
|
|
172
|
+
let it recur next week than to inflate the curated tier.
|
|
173
|
+
- **Cite your sources.** Every promoted entry should reference the
|
|
174
|
+
daily-log dates that sourced it, in the `--context` field. If a future
|
|
175
|
+
flush wants to update it, the trail is there.
|
|
176
|
+
- **Keep titles short.** `--title "Supabase RLS same migration"` not `"You should always remember that when working with Supabase you need to..."`. The body is for nuance.
|
|
177
|
+
- **Don't promote private projects across boundaries.** A pattern from
|
|
178
|
+
Project A is fine to promote as cross-project ONLY if it generalizes.
|
|
179
|
+
Client-specific things stay client-specific (`--type client --project X`).
|
|
180
|
+
|
|
181
|
+
## Anti-patterns
|
|
182
|
+
|
|
183
|
+
- **Mass-promoting everything:** if you found "promotions" for 90% of
|
|
184
|
+
daily-log entries, you're labeling, not promoting. Be selective.
|
|
185
|
+
- **Re-promoting on every flush:** before appending, run
|
|
186
|
+
`node ~/.claude/bin/knowledge.js search "{title keywords}"` to check if
|
|
187
|
+
the pattern already exists. If it does, either update it (find by `**ID:**`
|
|
188
|
+
line) or skip — never duplicate.
|
|
189
|
+
- **Hand-writing to `learned-patterns.md` etc. directly:** always go
|
|
190
|
+
through `knowledge.js append` so the canonical entry format and ID
|
|
191
|
+
generation stay consistent. This skill is the only sanctioned promoter,
|
|
192
|
+
but even it uses the loader for writes.
|
|
193
|
+
|
|
194
|
+
## Output contract
|
|
195
|
+
|
|
196
|
+
If invoked with `--dry-run`, print the proposed writes and exit without
|
|
197
|
+
touching disk. Otherwise, after step 6 returns the summary, the skill is
|
|
198
|
+
done — no follow-up prompts. The user sees the summary and the wiki tier
|
|
199
|
+
has new entries that are immediately reachable to every other skill via
|
|
200
|
+
the loader.
|
|
@@ -54,49 +54,45 @@ What did you learn?
|
|
|
54
54
|
|
|
55
55
|
### 2. Check for Duplicates
|
|
56
56
|
|
|
57
|
-
Before saving,
|
|
57
|
+
Before saving, search the existing knowledge for a similar entry. Use the
|
|
58
|
+
unified loader, **never** raw `cat` or `grep` directly — the loader handles
|
|
59
|
+
missing-file edge cases and stays consistent across skills.
|
|
58
60
|
|
|
59
61
|
```bash
|
|
60
|
-
|
|
61
|
-
grep -i "{title keywords}" ~/.claude/knowledge/{type}.md 2>/dev/null
|
|
62
|
+
node ~/.claude/bin/knowledge.js search "{title keywords}"
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
If a near-match exists
|
|
65
|
+
If a near-match exists:
|
|
65
66
|
- Show the existing entry to the user
|
|
66
67
|
- Ask: "A similar entry exists. Update it, create a new one, or skip?"
|
|
67
|
-
- If update:
|
|
68
|
+
- If update: edit the file directly (find the entry by `**ID:**` line). If
|
|
69
|
+
new: continue to step 3. If skip: done.
|
|
68
70
|
|
|
69
|
-
### 3.
|
|
71
|
+
### 3. Append the Entry
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
### {Title}
|
|
78
|
-
**ID:** {random 8-char hex, e.g. a3f7c1e9}
|
|
79
|
-
**Date:** {ISO 8601, e.g. 2026-04-11}
|
|
80
|
-
**Project:** {current project name or "general"}
|
|
81
|
-
**Context:** {brief context — what you were building when you learned this}
|
|
82
|
-
|
|
83
|
-
{The learning — be specific enough that future-you understands without context}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 4. Append to Knowledge File
|
|
87
|
-
|
|
88
|
-
Append-only — never overwrite the file, always add at the end:
|
|
73
|
+
The loader's `append` subcommand handles ID generation, ISO date, project
|
|
74
|
+
detection, and the canonical entry format — one call, no shell escaping
|
|
75
|
+
concerns:
|
|
89
76
|
|
|
90
77
|
```bash
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
node ~/.claude/bin/knowledge.js append \
|
|
79
|
+
--type {pattern|fix|client} \
|
|
80
|
+
--title "{Title}" \
|
|
81
|
+
--body "{The learning — be specific enough that future-you understands without context}" \
|
|
82
|
+
--project "{current project name or 'general'}" \
|
|
83
|
+
--context "{brief context — what you were building when you learned this}"
|
|
93
84
|
```
|
|
94
85
|
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
86
|
+
Type → file mapping (handled by the loader):
|
|
87
|
+
- `pattern` → `learned-patterns.md`
|
|
88
|
+
- `fix` → `common-fixes.md`
|
|
89
|
+
- `client` → `client-prefs.md`
|
|
90
|
+
|
|
91
|
+
The loader prints `appended {id} to {file}` on success. If the destination
|
|
92
|
+
file does not exist, the loader creates it with a header — you do not need
|
|
93
|
+
to bootstrap it.
|
|
98
94
|
|
|
99
|
-
###
|
|
95
|
+
### 4. Confirm
|
|
100
96
|
|
|
101
97
|
```
|
|
102
98
|
⬢ Saved to {file}
|
|
@@ -105,13 +101,27 @@ echo "{formatted entry}" >> ~/.claude/knowledge/{type}.md
|
|
|
105
101
|
|
|
106
102
|
## Reading Knowledge
|
|
107
103
|
|
|
108
|
-
|
|
104
|
+
**Always use the loader.** Hardcoded `cat ~/.claude/knowledge/X.md` is an
|
|
105
|
+
anti-pattern — it makes new files invisible (this was v4.1.0 audit finding
|
|
106
|
+
#3). The loader, by contrast, lets agents discover available knowledge via
|
|
107
|
+
the index.
|
|
108
|
+
|
|
109
109
|
```bash
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
# Print the index (entry point — read this first)
|
|
111
|
+
node ~/.claude/bin/knowledge.js
|
|
112
|
+
|
|
113
|
+
# Print a specific file (accepts aliases: patterns, fixes, client)
|
|
114
|
+
node ~/.claude/bin/knowledge.js load patterns
|
|
115
|
+
node ~/.claude/bin/knowledge.js load common-fixes.md
|
|
116
|
+
|
|
117
|
+
# List everything available
|
|
118
|
+
node ~/.claude/bin/knowledge.js list
|
|
119
|
+
|
|
120
|
+
# Search across all files
|
|
121
|
+
node ~/.claude/bin/knowledge.js search "RLS"
|
|
113
122
|
```
|
|
114
123
|
|
|
115
|
-
The `/qualia-debug` skill should check `common-fixes.md` before
|
|
116
|
-
The `/qualia-new` skill should check `client-prefs.md`
|
|
117
|
-
|
|
124
|
+
The `/qualia-debug` skill should check `common-fixes.md` (`load fixes`) before
|
|
125
|
+
investigating. The `/qualia-new` skill should check `client-prefs.md`
|
|
126
|
+
(`load client`) when setting up client projects. The `/qualia-plan` skill
|
|
127
|
+
should check `learned-patterns.md` (`load patterns`) when planning phases.
|
|
@@ -94,7 +94,7 @@ Plus free-text: "Any brand colors or reference sites I should look at?"
|
|
|
94
94
|
|
|
95
95
|
If client, ask name. Check saved prefs:
|
|
96
96
|
```bash
|
|
97
|
-
|
|
97
|
+
node ~/.claude/bin/knowledge.js search "{client name}"
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
### Step 5. Write PROJECT.md
|
|
@@ -33,8 +33,9 @@ Spawn a planner agent to break the current phase into executable tasks, then val
|
|
|
33
33
|
cat .planning/STATE.md 2>/dev/null
|
|
34
34
|
cat .planning/ROADMAP.md 2>/dev/null
|
|
35
35
|
cat .planning/PROJECT.md 2>/dev/null
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
node ~/.claude/bin/knowledge.js
|
|
37
|
+
node ~/.claude/bin/knowledge.js load patterns
|
|
38
|
+
node ~/.claude/bin/knowledge.js load client
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
If no phase number given, use the current phase from STATE.md.
|