qualia-framework 6.3.0 → 6.5.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 +6 -5
- package/README.md +17 -39
- package/bin/cli.js +64 -16
- package/bin/command-surface.js +6 -1
- package/bin/install.js +26 -11
- package/bin/learning-candidates.js +217 -0
- package/bin/prune-deprecated.js +64 -0
- package/bin/qualia-ui.js +1 -0
- package/bin/runtime-manifest.js +4 -0
- package/bin/security-scan.js +409 -0
- package/bin/state.js +106 -1
- package/bin/status-snapshot.js +363 -0
- package/guide.md +18 -33
- package/hooks/pre-compact.js +232 -0
- package/package.json +8 -2
- package/references/archetypes/ai-agent.md +89 -0
- package/references/archetypes/voice-agent.md +60 -0
- package/references/archetypes/web-app.md +67 -0
- package/references/archetypes/website.md +78 -0
- package/rules/constitution.md +42 -0
- package/skills/qualia/SKILL.md +3 -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-scope/SKILL.md +123 -0
- 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 +17 -10
- 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/docs/archive/CHANGELOG-pre-v4.md +0 -855
- package/docs/archive/v4.0.0-review.md +0 -288
- package/docs/ecosystem-operating-model.md +0 -121
- package/docs/research/2026-04-21-command-quality-deep-research.md +0 -128
- package/docs/research/2026-04-21-industry-best-practices.md +0 -255
- package/docs/research/2026-05-11-deep-research.md +0 -189
- package/docs/reviews/matt-pocock-skills-analysis.md +0 -300
- package/docs/reviews/v4.1.0-audit.html +0 -1488
- package/docs/reviews/v4.1.0-audit.md +0 -263
- package/docs/reviews/v6.2.1-revival-audit.md +0 -53
- package/docs/reviews/v6.2.2-memory-erp-audit.md +0 -41
- package/docs/reviews/v6.2.3-erp-id-guard.md +0 -15
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zoho-workflow
|
|
3
|
-
description: "Zoho Invoice
|
|
3
|
+
description: "Zoho Invoice + Mail ops via ERP-first routing. Invoices from templates, cover emails, contacts, inbox, payment reminders. Triggers: 'invoice this client', 'send an email', 'check inbox', 'create a Zoho contact', 'payment reminder'."
|
|
4
4
|
tags: [zoho, invoice, email, billing, crm]
|
|
5
5
|
---
|
|
6
6
|
|
package/tests/bin.test.sh
CHANGED
|
@@ -487,13 +487,14 @@ else
|
|
|
487
487
|
fail_case "CLAUDE.md role substitution"
|
|
488
488
|
fi
|
|
489
489
|
|
|
490
|
-
# 31. All
|
|
490
|
+
# 31. All 13 hooks installed (block-env-edit removed in v3.2.0;
|
|
491
491
|
# git-guardrails + stop-session-log added in v4.2.0;
|
|
492
492
|
# vercel-account-guard + env-empty-guard + supabase-destructive-guard added in v5.0.0;
|
|
493
|
-
#
|
|
493
|
+
# fawzi-approval-guard added in v6.2.11; pre-compact removed in v6.2.0 and
|
|
494
|
+
# REINTRODUCED in v6.3.2 with sidecar-snapshot mechanism)
|
|
494
495
|
HOOK_COUNT=$(ls "$TMP/.claude/hooks/"*.js 2>/dev/null | wc -l)
|
|
495
|
-
if [ "$HOOK_COUNT" -eq
|
|
496
|
-
pass "
|
|
496
|
+
if [ "$HOOK_COUNT" -eq 13 ]; then
|
|
497
|
+
pass "13 hooks installed in hooks/ (incl. pre-compact v6.3.2)"
|
|
497
498
|
else
|
|
498
499
|
fail_case "hook count" "got $HOOK_COUNT"
|
|
499
500
|
fi
|
|
@@ -509,8 +510,7 @@ else
|
|
|
509
510
|
fail_case "settings.json contents"
|
|
510
511
|
fi
|
|
511
512
|
|
|
512
|
-
# 33. settings.json contains all
|
|
513
|
-
# pre-compact.js was removed in v6.2.0 — verify it's NOT in settings.json.
|
|
513
|
+
# 33. settings.json contains all 13 hooks wired correctly (v6.3.2 reintroduces pre-compact).
|
|
514
514
|
if grep -q 'branch-guard.js' "$TMP/.claude/settings.json" \
|
|
515
515
|
&& grep -q 'migration-guard.js' "$TMP/.claude/settings.json" \
|
|
516
516
|
&& grep -q 'pre-push.js' "$TMP/.claude/settings.json" \
|
|
@@ -523,10 +523,10 @@ if grep -q 'branch-guard.js' "$TMP/.claude/settings.json" \
|
|
|
523
523
|
&& grep -q 'fawzi-approval-guard.js' "$TMP/.claude/settings.json" \
|
|
524
524
|
&& grep -q 'env-empty-guard.js' "$TMP/.claude/settings.json" \
|
|
525
525
|
&& grep -q 'supabase-destructive-guard.js' "$TMP/.claude/settings.json" \
|
|
526
|
-
&&
|
|
527
|
-
pass "settings.json has all
|
|
526
|
+
&& grep -q 'pre-compact.js' "$TMP/.claude/settings.json"; then
|
|
527
|
+
pass "settings.json has all 13 hooks wired (incl. pre-compact)"
|
|
528
528
|
else
|
|
529
|
-
fail_case "settings.json hooks misconfigured
|
|
529
|
+
fail_case "settings.json hooks misconfigured"
|
|
530
530
|
fi
|
|
531
531
|
|
|
532
532
|
# 34. Lowercase code works (resolveTeamCode normalizes)
|
|
@@ -124,10 +124,10 @@ else
|
|
|
124
124
|
fi
|
|
125
125
|
|
|
126
126
|
if [ -d "$HOME_DIR/.claude/hooks" ] \
|
|
127
|
-
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "
|
|
127
|
+
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "13" ] \
|
|
128
128
|
&& [ -f "$HOME_DIR/.claude/hooks/fawzi-approval-guard.js" ] \
|
|
129
|
-
&& [
|
|
130
|
-
pass "packaged install has
|
|
129
|
+
&& [ -f "$HOME_DIR/.claude/hooks/pre-compact.js" ]; then
|
|
130
|
+
pass "packaged install has 13 hooks including pre-compact (v6.3.2 sidecar snapshot)"
|
|
131
131
|
else
|
|
132
132
|
fail_case "packaged hook set mismatch"
|
|
133
133
|
fi
|
package/tests/lib.test.sh
CHANGED
|
@@ -369,9 +369,16 @@ 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
|
-
|
|
372
|
+
const active = cs.ACTIVE_SKILLS;
|
|
373
|
+
const retired = cs.RETIRED_SKILLS;
|
|
374
|
+
const ok =
|
|
375
|
+
Array.isArray(active) && active.length > 0 &&
|
|
376
|
+
active.includes("qualia-scope") && active.includes("qualia-idk") && active.includes("qualia-secure") &&
|
|
377
|
+
!active.includes("qualia-debug") && retired.includes("qualia-debug") &&
|
|
378
|
+
!active.includes("qualia-vibe") && retired.includes("qualia-vibe");
|
|
379
|
+
console.log(ok ? "SURFACE-OK" : JSON.stringify(cs));
|
|
373
380
|
')
|
|
374
|
-
[ "$RES" = "SURFACE-OK" ] && ok "command-surface
|
|
381
|
+
[ "$RES" = "SURFACE-OK" ] && ok "command-surface manifest: active surface excludes retired folds (qualia-debug/qualia-vibe), count derived from ACTIVE_SKILLS" || fail "command-surface manifest: $RES"
|
|
375
382
|
RES=$($NODE -e '
|
|
376
383
|
const fs = require("fs");
|
|
377
384
|
const path = require("path");
|
|
@@ -382,12 +389,12 @@ const dirs = fs.readdirSync(path.join(root, "skills"), { withFileTypes: true })
|
|
|
382
389
|
.map((d) => d.name)
|
|
383
390
|
.sort();
|
|
384
391
|
const active = [...ACTIVE_SKILLS].sort();
|
|
385
|
-
const
|
|
392
|
+
const known = new Set([...ACTIVE_SKILLS, ...RETIRED_SKILLS]);
|
|
393
|
+
const orphans = dirs.filter((d) => !known.has(d));
|
|
386
394
|
const missing = active.filter((d) => !dirs.includes(d));
|
|
387
|
-
|
|
388
|
-
console.log(!extra.length && !missing.length && !retiredPresent.length ? "SKILL-DIRS-OK" : JSON.stringify({ extra, missing, retiredPresent }));
|
|
395
|
+
console.log(!orphans.length && !missing.length ? "SKILL-DIRS-OK" : JSON.stringify({ orphans, missing }));
|
|
389
396
|
')
|
|
390
|
-
[ "$RES" = "SKILL-DIRS-OK" ] && ok "
|
|
397
|
+
[ "$RES" = "SKILL-DIRS-OK" ] && ok "every active skill has a folder; no orphan skill folders" || fail "skill directory surface: $RES"
|
|
391
398
|
|
|
392
399
|
TMP=$(mktmp)
|
|
393
400
|
RES=$(cd "$TMP" && $NODE -e '
|
|
@@ -500,7 +507,7 @@ TMP=$(mktmp)
|
|
|
500
507
|
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
508
|
echo '{"installed_by":"Test","role":"OWNER","version":"6.3.0","erp":{"enabled":false}}' > "$TMP/home/.claude/.qualia-config.json"
|
|
502
509
|
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
|
|
510
|
+
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
511
|
touch "$TMP/home/.claude/bin/$f"
|
|
505
512
|
done
|
|
506
513
|
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 +517,7 @@ touch "$TMP/home/.claude/knowledge/index.md" "$TMP/home/.claude/knowledge/agents
|
|
|
510
517
|
for f in design-laws.md design-rubric.md design-brand.md design-product.md design-reference.md frontend.md graphics.md; do
|
|
511
518
|
touch "$TMP/home/.claude/qualia-design/$f"
|
|
512
519
|
done
|
|
513
|
-
for s in
|
|
520
|
+
for s in $($NODE -e 'console.log(require(process.argv[1]).ACTIVE_SKILLS.join(" "))' "$CS"); do
|
|
514
521
|
mkdir -p "$TMP/home/.claude/skills/$s"
|
|
515
522
|
touch "$TMP/home/.claude/skills/$s/SKILL.md"
|
|
516
523
|
done
|
|
@@ -615,7 +622,7 @@ TMP=$(mktmp)
|
|
|
615
622
|
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
623
|
echo '{"installed_by":"Test","role":"OWNER","erp":{"enabled":false}}' > "$TMP/.claude/.qualia-config.json"
|
|
617
624
|
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
|
|
625
|
+
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
626
|
touch "$TMP/.claude/bin/$f"
|
|
620
627
|
done
|
|
621
628
|
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 +632,7 @@ touch "$TMP/.claude/knowledge/index.md" "$TMP/.claude/knowledge/agents.md"
|
|
|
625
632
|
for f in design-laws.md design-rubric.md design-brand.md design-product.md design-reference.md frontend.md graphics.md; do
|
|
626
633
|
touch "$TMP/.claude/qualia-design/$f"
|
|
627
634
|
done
|
|
628
|
-
for s in
|
|
635
|
+
for s in $($NODE -e 'console.log(require(process.argv[1]).ACTIVE_SKILLS.join(" "))' "$CS"); do
|
|
629
636
|
mkdir -p "$TMP/.claude/skills/$s"
|
|
630
637
|
touch "$TMP/.claude/skills/$s/SKILL.md"
|
|
631
638
|
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"}]' \
|