qualia-framework 6.3.0 → 6.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/AGENTS.md +8 -8
- package/CLAUDE.md +5 -5
- package/README.md +17 -39
- package/bin/cli.js +64 -16
- package/bin/command-surface.js +5 -1
- package/bin/install.js +26 -11
- package/bin/learning-candidates.js +217 -0
- package/bin/prune-deprecated.js +64 -0
- package/bin/runtime-manifest.js +4 -0
- package/bin/security-scan.js +409 -0
- package/bin/status-snapshot.js +363 -0
- package/guide.md +11 -33
- package/hooks/pre-compact.js +232 -0
- package/package.json +1 -1
- package/skills/qualia/SKILL.md +1 -1
- package/skills/qualia-build/SKILL.md +1 -1
- package/skills/qualia-discuss/SKILL.md +1 -1
- package/skills/qualia-doctor/SKILL.md +1 -1
- package/skills/qualia-feature/SKILL.md +1 -1
- package/skills/qualia-fix/SKILL.md +1 -1
- package/skills/qualia-idk/SKILL.md +245 -0
- package/skills/qualia-learn/SKILL.md +1 -1
- package/skills/qualia-map/SKILL.md +1 -1
- package/skills/qualia-milestone/SKILL.md +1 -1
- package/skills/qualia-new/SKILL.md +1 -1
- package/skills/qualia-optimize/SKILL.md +1 -1
- package/skills/qualia-plan/SKILL.md +1 -1
- package/skills/qualia-polish/SKILL.md +1 -1
- package/skills/qualia-postmortem/SKILL.md +1 -1
- package/skills/qualia-report/SKILL.md +1 -1
- package/skills/qualia-research/SKILL.md +1 -1
- package/skills/qualia-review/SKILL.md +1 -1
- package/skills/qualia-road/SKILL.md +1 -1
- package/skills/qualia-secure/SKILL.md +105 -0
- package/skills/qualia-test/SKILL.md +1 -1
- package/skills/qualia-verify/SKILL.md +1 -1
- package/skills/zoho-workflow/SKILL.md +1 -1
- package/tests/bin.test.sh +9 -9
- package/tests/install-smoke.test.sh +3 -3
- package/tests/lib.test.sh +6 -6
- package/tests/published-install-smoke.test.sh +3 -3
- package/tests/refs.test.sh +29 -22
- package/tests/runner.js +3 -3
- package/tests/state.test.sh +38 -7
package/tests/lib.test.sh
CHANGED
|
@@ -369,9 +369,9 @@ CS="$FRAMEWORK_DIR/bin/command-surface.js"
|
|
|
369
369
|
$NODE --check "$CS" >/dev/null 2>&1 && ok "command-surface.js parses" || fail "command-surface.js parse"
|
|
370
370
|
RES=$($NODE -e '
|
|
371
371
|
const cs = require("'"$CS"'");
|
|
372
|
-
console.log(cs.ACTIVE_SKILLS.length ===
|
|
372
|
+
console.log(cs.ACTIVE_SKILLS.length === 25 && cs.ACTIVE_SKILLS.includes("qualia-idk") && cs.ACTIVE_SKILLS.includes("qualia-secure") && cs.RETIRED_SKILLS.includes("qualia-debug") && cs.RETIRED_SKILLS.includes("qualia-vibe") ? "SURFACE-OK" : JSON.stringify(cs));
|
|
373
373
|
')
|
|
374
|
-
[ "$RES" = "SURFACE-OK" ] && ok "command-surface defines
|
|
374
|
+
[ "$RES" = "SURFACE-OK" ] && ok "command-surface defines 25 active skills (incl qualia-idk + qualia-secure) and retired folds" || fail "command-surface manifest: $RES"
|
|
375
375
|
RES=$($NODE -e '
|
|
376
376
|
const fs = require("fs");
|
|
377
377
|
const path = require("path");
|
|
@@ -500,7 +500,7 @@ TMP=$(mktmp)
|
|
|
500
500
|
mkdir -p "$TMP/home/.claude/bin" "$TMP/home/.claude/hooks" "$TMP/home/.claude/knowledge/daily-log" "$TMP/home/.claude/qualia-design" "$TMP/home/.claude/agents" "$TMP/home/.claude/qualia-templates" "$TMP/project"
|
|
501
501
|
echo '{"installed_by":"Test","role":"OWNER","version":"6.3.0","erp":{"enabled":false}}' > "$TMP/home/.claude/.qualia-config.json"
|
|
502
502
|
touch "$TMP/home/.claude/CLAUDE.md" "$TMP/home/.claude/settings.json"
|
|
503
|
-
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js; do
|
|
503
|
+
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js prune-deprecated.js learning-candidates.js status-snapshot.js security-scan.js; do
|
|
504
504
|
touch "$TMP/home/.claude/bin/$f"
|
|
505
505
|
done
|
|
506
506
|
for h in session-start.js auto-update.js branch-guard.js pre-push.js pre-deploy-gate.js migration-guard.js git-guardrails.js stop-session-log.js fawzi-approval-guard.js vercel-account-guard.js env-empty-guard.js supabase-destructive-guard.js; do
|
|
@@ -510,7 +510,7 @@ touch "$TMP/home/.claude/knowledge/index.md" "$TMP/home/.claude/knowledge/agents
|
|
|
510
510
|
for f in design-laws.md design-rubric.md design-brand.md design-product.md design-reference.md frontend.md graphics.md; do
|
|
511
511
|
touch "$TMP/home/.claude/qualia-design/$f"
|
|
512
512
|
done
|
|
513
|
-
for s in qualia qualia-new qualia-discuss qualia-map qualia-research qualia-plan qualia-build qualia-verify qualia-fix qualia-feature qualia-review qualia-optimize qualia-polish qualia-test qualia-milestone qualia-ship qualia-handoff qualia-report qualia-doctor qualia-road qualia-learn qualia-postmortem zoho-workflow; do
|
|
513
|
+
for s in qualia qualia-new qualia-discuss qualia-map qualia-research qualia-plan qualia-build qualia-verify qualia-fix qualia-feature qualia-review qualia-optimize qualia-polish qualia-test qualia-milestone qualia-ship qualia-handoff qualia-report qualia-doctor qualia-road qualia-learn qualia-postmortem qualia-idk qualia-secure zoho-workflow; do
|
|
514
514
|
mkdir -p "$TMP/home/.claude/skills/$s"
|
|
515
515
|
touch "$TMP/home/.claude/skills/$s/SKILL.md"
|
|
516
516
|
done
|
|
@@ -615,7 +615,7 @@ TMP=$(mktmp)
|
|
|
615
615
|
mkdir -p "$TMP/.claude/bin" "$TMP/.claude/hooks" "$TMP/.claude/knowledge/daily-log" "$TMP/.claude/qualia-design" "$TMP/.claude/agents" "$TMP/.claude/qualia-templates" "$TMP/project/.planning"
|
|
616
616
|
echo '{"installed_by":"Test","role":"OWNER","erp":{"enabled":false}}' > "$TMP/.claude/.qualia-config.json"
|
|
617
617
|
touch "$TMP/.claude/CLAUDE.md" "$TMP/.claude/settings.json"
|
|
618
|
-
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js; do
|
|
618
|
+
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js prune-deprecated.js learning-candidates.js status-snapshot.js security-scan.js; do
|
|
619
619
|
touch "$TMP/.claude/bin/$f"
|
|
620
620
|
done
|
|
621
621
|
for h in session-start.js auto-update.js branch-guard.js pre-push.js pre-deploy-gate.js migration-guard.js git-guardrails.js stop-session-log.js fawzi-approval-guard.js vercel-account-guard.js env-empty-guard.js supabase-destructive-guard.js; do
|
|
@@ -625,7 +625,7 @@ touch "$TMP/.claude/knowledge/index.md" "$TMP/.claude/knowledge/agents.md"
|
|
|
625
625
|
for f in design-laws.md design-rubric.md design-brand.md design-product.md design-reference.md frontend.md graphics.md; do
|
|
626
626
|
touch "$TMP/.claude/qualia-design/$f"
|
|
627
627
|
done
|
|
628
|
-
for s in qualia qualia-new qualia-discuss qualia-map qualia-research qualia-plan qualia-build qualia-verify qualia-fix qualia-feature qualia-review qualia-optimize qualia-polish qualia-test qualia-milestone qualia-ship qualia-handoff qualia-report qualia-doctor qualia-road qualia-learn qualia-postmortem zoho-workflow; do
|
|
628
|
+
for s in qualia qualia-new qualia-discuss qualia-map qualia-research qualia-plan qualia-build qualia-verify qualia-fix qualia-feature qualia-review qualia-optimize qualia-polish qualia-test qualia-milestone qualia-ship qualia-handoff qualia-report qualia-doctor qualia-road qualia-learn qualia-postmortem qualia-idk qualia-secure zoho-workflow; do
|
|
629
629
|
mkdir -p "$TMP/.claude/skills/$s"
|
|
630
630
|
touch "$TMP/.claude/skills/$s/SKILL.md"
|
|
631
631
|
done
|
|
@@ -102,10 +102,10 @@ else
|
|
|
102
102
|
fi
|
|
103
103
|
|
|
104
104
|
if [ -d "$HOME_DIR/.claude/hooks" ] \
|
|
105
|
-
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "
|
|
105
|
+
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "13" ] \
|
|
106
106
|
&& [ -f "$HOME_DIR/.claude/hooks/fawzi-approval-guard.js" ] \
|
|
107
|
-
&& [
|
|
108
|
-
pass "public install has
|
|
107
|
+
&& [ -f "$HOME_DIR/.claude/hooks/pre-compact.js" ]; then
|
|
108
|
+
pass "public install has 13 hooks including pre-compact (v6.3.2 sidecar snapshot)"
|
|
109
109
|
else
|
|
110
110
|
fail_case "public install hook set mismatch"
|
|
111
111
|
fi
|
package/tests/refs.test.sh
CHANGED
|
@@ -79,27 +79,33 @@ SCAN_FILES=$(
|
|
|
79
79
|
# 1. `/qualia-foo` — backticked, the canonical command-doc style
|
|
80
80
|
# 2. <dt>/qualia-foo</dt> — HTML help/onboarding pages
|
|
81
81
|
# We capture name + file:line so we can show context per failure.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
#
|
|
83
|
+
# Storage uses a temp file (one line per "ref<TAB>location") instead of
|
|
84
|
+
# bash 4 associative arrays — macOS ships bash 3.2 (frozen at that version
|
|
85
|
+
# for licensing reasons) which lacks `declare -A`. Temp file is portable
|
|
86
|
+
# and behaves correctly under `set -u`.
|
|
87
|
+
REF_STORE=$(mktemp)
|
|
88
|
+
ACTIVE_SKILLS_LIST=$("$NODE" -e 'const {ACTIVE_SKILLS}=require(process.argv[1]); console.log(ACTIVE_SKILLS.map(s => "/" + s).join(" "))' "$FRAMEWORK_ROOT/bin/command-surface.js")
|
|
89
|
+
trap 'rm -f "$REF_STORE"' EXIT
|
|
90
|
+
|
|
91
|
+
# is_active "/qualia-foo" → returns 0 if in ACTIVE_SKILLS_LIST.
|
|
92
|
+
is_active() {
|
|
93
|
+
case " $ACTIVE_SKILLS_LIST " in
|
|
94
|
+
*" $1 "*) return 0 ;;
|
|
95
|
+
*) return 1 ;;
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
89
98
|
|
|
90
99
|
while IFS= read -r file; do
|
|
91
100
|
# Pattern A: backtick-quoted commands. Allow trailing flag/word but only capture base name.
|
|
92
101
|
while IFS=: read -r path lineno line; do
|
|
93
102
|
[ -z "$line" ] && continue
|
|
94
|
-
# Skip migration-context lines.
|
|
95
103
|
if echo "$line" | grep -qE "$MIGRATION_CONTEXT_REGEX"; then
|
|
96
104
|
continue
|
|
97
105
|
fi
|
|
98
|
-
# Extract every backticked /qualia-foo on this line.
|
|
99
106
|
matches=$(echo "$line" | grep -oE '`/qualia(-[a-z]+){0,3}`' | sed 's/^`//; s/`$//')
|
|
100
107
|
for ref in $matches; do
|
|
101
|
-
|
|
102
|
-
REF_LOCATIONS["$ref"]="${REF_LOCATIONS[$ref]:+${REF_LOCATIONS[$ref]}, }$(basename "$path"):$lineno"
|
|
108
|
+
printf '%s\t%s:%s\n' "$ref" "$(basename "$path")" "$lineno" >> "$REF_STORE"
|
|
103
109
|
done
|
|
104
110
|
done < <(grep -nE '`/qualia(-[a-z]+){0,3}`' "$file" 2>/dev/null)
|
|
105
111
|
|
|
@@ -111,36 +117,37 @@ while IFS= read -r file; do
|
|
|
111
117
|
fi
|
|
112
118
|
matches=$(echo "$line" | grep -oE '<dt>/qualia(-[a-z]+){0,3}( [^<]*)?</dt>' | sed -E 's|<dt>(/qualia(-[a-z]+){0,3}).*|\1|')
|
|
113
119
|
for ref in $matches; do
|
|
114
|
-
|
|
115
|
-
REF_LOCATIONS["$ref"]="${REF_LOCATIONS[$ref]:+${REF_LOCATIONS[$ref]}, }$(basename "$path"):$lineno"
|
|
120
|
+
printf '%s\t%s:%s\n' "$ref" "$(basename "$path")" "$lineno" >> "$REF_STORE"
|
|
116
121
|
done
|
|
117
122
|
done < <(grep -nE '<dt>/qualia' "$file" 2>/dev/null)
|
|
118
123
|
done <<<"$SCAN_FILES"
|
|
119
124
|
|
|
120
|
-
if [
|
|
125
|
+
if [ ! -s "$REF_STORE" ]; then
|
|
121
126
|
fail_case "scan" "no backticked /qualia-* references found across active surfaces (scanner broken?)"
|
|
122
127
|
echo ""
|
|
123
128
|
echo "Results: $PASS passed, $FAIL failed"
|
|
124
129
|
exit 1
|
|
125
130
|
fi
|
|
126
131
|
|
|
127
|
-
#
|
|
128
|
-
for ref in $(
|
|
132
|
+
# Iterate unique refs, deterministic order.
|
|
133
|
+
for ref in $(cut -f1 "$REF_STORE" | sort -u); do
|
|
129
134
|
name="${ref#/}"
|
|
130
135
|
skill_dir="$SKILLS_DIR/$name"
|
|
131
|
-
locations
|
|
132
|
-
|
|
136
|
+
# Comma-join all locations for this ref.
|
|
137
|
+
locations=$(awk -v r="$ref" -F'\t' '$1==r {print $2}' "$REF_STORE" | paste -sd, -)
|
|
138
|
+
if is_active "$ref" && [ -d "$skill_dir" ] && [ -f "$skill_dir/SKILL.md" ]; then
|
|
133
139
|
pass "$ref → active skills/$name/SKILL.md"
|
|
134
140
|
continue
|
|
135
141
|
fi
|
|
136
142
|
fail_case "$ref" "not an active shipped command or missing skills/$name/SKILL.md — referenced by: $locations"
|
|
137
143
|
done
|
|
138
144
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
# README title is version-less by design — version lives in package.json + CHANGELOG.md,
|
|
146
|
+
# not in the README h1, so the orientation block above the fold stays version-stable.
|
|
147
|
+
if grep -qE '^# Qualia Framework( |$)' "$FRAMEWORK_ROOT/README.md"; then
|
|
148
|
+
pass "README h1 is version-less (version lives in CHANGELOG.md)"
|
|
142
149
|
else
|
|
143
|
-
fail_case "README
|
|
150
|
+
fail_case "README h1 shape" "README.md must open with '# Qualia Framework' (no version suffix)"
|
|
144
151
|
fi
|
|
145
152
|
|
|
146
153
|
forbidden_surface_patterns=(
|
package/tests/runner.js
CHANGED
|
@@ -2578,14 +2578,14 @@ describe("install.js", () => {
|
|
|
2578
2578
|
}
|
|
2579
2579
|
});
|
|
2580
2580
|
|
|
2581
|
-
it("
|
|
2581
|
+
it("13 hooks installed (v6.3.2: pre-compact reintroduced with sidecar-snapshot mechanism)", () => {
|
|
2582
2582
|
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "qualia-install-"));
|
|
2583
2583
|
try {
|
|
2584
2584
|
runInstall("QS-FAWZI-11", tmpHome);
|
|
2585
2585
|
const hooks = fs.readdirSync(path.join(tmpHome, ".claude", "hooks")).filter(f => f.endsWith(".js"));
|
|
2586
|
-
assert.equal(hooks.length,
|
|
2586
|
+
assert.equal(hooks.length, 13, `expected 13 hooks, got ${hooks.length}: ${hooks.join(", ")}`);
|
|
2587
2587
|
assert.ok(hooks.includes("fawzi-approval-guard.js"), "fawzi-approval-guard.js must be installed");
|
|
2588
|
-
assert.ok(
|
|
2588
|
+
assert.ok(hooks.includes("pre-compact.js"), "pre-compact.js must be installed (v6.3.2 sidecar-snapshot mechanism)");
|
|
2589
2589
|
} finally {
|
|
2590
2590
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
2591
2591
|
}
|
package/tests/state.test.sh
CHANGED
|
@@ -4,9 +4,40 @@
|
|
|
4
4
|
|
|
5
5
|
PASS=0
|
|
6
6
|
FAIL=0
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
# Windows portability — coerce shell-native MSYS paths (/d/a/..., /tmp/...) to
|
|
8
|
+
# Windows-native form (D:/a/..., C:/Users/.../Temp/...) before they get
|
|
9
|
+
# interpolated into Node `$NODE -e` snippets. Without coercion, Node receives
|
|
10
|
+
# the MSYS string literally and resolves it against the current drive, yielding
|
|
11
|
+
# non-existent paths like `D:\tmp\tmp.XXX\.planning\tracking.json`.
|
|
12
|
+
#
|
|
13
|
+
# Mechanism: `cygpath -m` is the canonical MSYS path converter and is present
|
|
14
|
+
# on Git Bash / MSYS2 (Windows GitHub runners use Git Bash for `shell: bash`).
|
|
15
|
+
# `pwd -W` doesn't work because bash's builtin pwd doesn't accept -W, and
|
|
16
|
+
# /usr/bin/pwd's -W support varies by MSYS distribution.
|
|
17
|
+
# On Linux/macOS, cygpath doesn't exist — fall through to plain pwd.
|
|
18
|
+
_pwd_native() {
|
|
19
|
+
local p
|
|
20
|
+
p=$(pwd)
|
|
21
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
22
|
+
cygpath -m "$p" 2>/dev/null || echo "$p"
|
|
23
|
+
else
|
|
24
|
+
echo "$p"
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
_mktemp_native() {
|
|
28
|
+
local d
|
|
29
|
+
d=$(mktemp -d) || return 1
|
|
30
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
31
|
+
cygpath -m "$d" 2>/dev/null || echo "$d"
|
|
32
|
+
else
|
|
33
|
+
echo "$d"
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Resolve STATE_JS to an ABSOLUTE Windows-native path so `cd` inside subshells
|
|
38
|
+
# doesn't break it.
|
|
39
|
+
STATE_JS="$(cd "$(dirname "$0")/../bin" && _pwd_native)/state.js"
|
|
40
|
+
FRAMEWORK_DIR="$(cd "$(dirname "$0")/.." && _pwd_native)"
|
|
10
41
|
STATE_LEDGER_JS="$FRAMEWORK_DIR/bin/state-ledger.js"
|
|
11
42
|
NODE="${NODE:-node}"
|
|
12
43
|
|
|
@@ -23,7 +54,7 @@ trap cleanup EXIT
|
|
|
23
54
|
# Prints the absolute path to the new tmp dir (does NOT cd).
|
|
24
55
|
make_project() {
|
|
25
56
|
local TMP
|
|
26
|
-
TMP=$(
|
|
57
|
+
TMP=$(_mktemp_native)
|
|
27
58
|
TMP_DIRS+=("$TMP")
|
|
28
59
|
(
|
|
29
60
|
cd "$TMP" || exit 1
|
|
@@ -129,7 +160,7 @@ fi
|
|
|
129
160
|
echo "basic I/O:"
|
|
130
161
|
|
|
131
162
|
# 1. cmdInit produces valid tracking.json + STATE.md
|
|
132
|
-
TMP=$(
|
|
163
|
+
TMP=$(_mktemp_native); TMP_DIRS+=("$TMP")
|
|
133
164
|
(
|
|
134
165
|
cd "$TMP" || exit 1
|
|
135
166
|
$NODE "$STATE_JS" init \
|
|
@@ -190,7 +221,7 @@ else
|
|
|
190
221
|
fi
|
|
191
222
|
|
|
192
223
|
# 3. cmdCheck with no project → ok:false NO_PROJECT, exit 1
|
|
193
|
-
TMP2=$(
|
|
224
|
+
TMP2=$(_mktemp_native); TMP_DIRS+=("$TMP2")
|
|
194
225
|
OUT=$(cd "$TMP2" && $NODE "$STATE_JS" check 2>&1)
|
|
195
226
|
CHECK_EXIT=$?
|
|
196
227
|
if [ "$CHECK_EXIT" -eq 1 ] \
|
|
@@ -1014,7 +1045,7 @@ else
|
|
|
1014
1045
|
fi
|
|
1015
1046
|
|
|
1016
1047
|
# 49. First-time init (no existing tracking.json) sets lifetime to zeros
|
|
1017
|
-
TMP=$(
|
|
1048
|
+
TMP=$(_mktemp_native); TMP_DIRS+=("$TMP")
|
|
1018
1049
|
(cd "$TMP" && $NODE "$STATE_JS" init \
|
|
1019
1050
|
--project "FreshProject" \
|
|
1020
1051
|
--phases '[{"name":"P1","goal":"G1"}]' \
|