@neikyun/ciel 6.8.0 → 6.9.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/assets/.claude/hooks/memory-bootstrap.sh +287 -0
- package/assets/.claude/hooks/memory-engine.py +718 -0
- package/assets/.claude/hooks/session-start.sh +99 -0
- package/assets/.claude/hooks/user-prompt-submit.sh +112 -0
- package/assets/commands/ciel-audit.md +77 -17
- package/assets/commands/ciel-memory-bootstrap.md +160 -0
- package/assets/commands/ciel-status.md +1 -1
- package/assets/platforms/opencode/.opencode/agents/ciel-explorer.md +1 -1
- package/assets/platforms/opencode/.opencode/agents/ciel-improver.md +2 -2
- package/assets/platforms/opencode/.opencode/agents/ciel-researcher.md +1 -1
- package/assets/platforms/opencode/.opencode/commands/ciel-audit.md +40 -22
- package/package.json +1 -1
- package/scripts/postinstall.cjs +4 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Ciel — SessionStart hook
|
|
3
|
+
# Trigger: session begins or resumes
|
|
4
|
+
# Purpose: print Ciel banner, load overlay context, set TRACE_ID for eval logging
|
|
5
|
+
# Never blocks (exit 0 always). Stdout is added to Claude's context.
|
|
6
|
+
|
|
7
|
+
INPUT=$(cat 2>/dev/null || echo "{}")
|
|
8
|
+
CWD=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin).get('cwd', ''))" 2>/dev/null || pwd)
|
|
9
|
+
|
|
10
|
+
# Detect overlay presence
|
|
11
|
+
OVERLAY=""
|
|
12
|
+
for candidate in "$CWD/ciel-overlay.md" "$CWD/.claude/ciel-overlay.md"; do
|
|
13
|
+
if [[ -f "$candidate" ]]; then
|
|
14
|
+
OVERLAY="$candidate"
|
|
15
|
+
break
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
# Reset session-scoped edit tracker so META/RELIRE gates don't bleed across sessions
|
|
20
|
+
if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -f "$CLAUDE_PROJECT_DIR/.ciel/tracked-files.json" ]; then
|
|
21
|
+
echo "[]" > "$CLAUDE_PROJECT_DIR/.ciel/tracked-files.json" 2>/dev/null || true
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Generate TRACE_ID for this session (used by eval logging)
|
|
25
|
+
TRACE_ID=$(date -u +%Y%m%dT%H%M%SZ)-$$
|
|
26
|
+
export CIEL_TRACE_ID="$TRACE_ID"
|
|
27
|
+
|
|
28
|
+
MSG="CIEL v6.8.0 — Skills-first deep-reasoning active. "
|
|
29
|
+
if [[ -n "$OVERLAY" ]]; then
|
|
30
|
+
MSG+="Overlay loaded: $OVERLAY. "
|
|
31
|
+
else
|
|
32
|
+
MSG+="No overlay found at $CWD/ciel-overlay.md — create one for project-specific rules. "
|
|
33
|
+
fi
|
|
34
|
+
MSG+="Trace ID: $TRACE_ID. Principle: Understand before generating. Verify before claiming done."
|
|
35
|
+
|
|
36
|
+
# ─── Cued-recall: surface relevant memories ──────────────────────────────────
|
|
37
|
+
# If a memory corpus exists, list active (non-stale) memories so the model
|
|
38
|
+
# knows what cues are available. Full content read on-demand. See ADR-0001.
|
|
39
|
+
MEMORY_INDEX="$CWD/.ciel/memory/index.json"
|
|
40
|
+
if [[ -f "$MEMORY_INDEX" ]]; then
|
|
41
|
+
MEMORY_SUMMARY=$(MEMORY_INDEX="$MEMORY_INDEX" python3 -c "
|
|
42
|
+
import json, os
|
|
43
|
+
try:
|
|
44
|
+
with open(os.environ['MEMORY_INDEX']) as f:
|
|
45
|
+
idx = json.load(f)
|
|
46
|
+
mems = idx.get('memories', {})
|
|
47
|
+
active = [(mid, m) for mid, m in mems.items() if not m.get('stale')]
|
|
48
|
+
if not active:
|
|
49
|
+
print('')
|
|
50
|
+
else:
|
|
51
|
+
# Sort by trigger_count desc, then by last_triggered desc
|
|
52
|
+
active.sort(key=lambda x: (-(x[1].get('trigger_count') or 0), x[1].get('last_triggered') or ''), reverse=False)
|
|
53
|
+
active.sort(key=lambda x: -(x[1].get('trigger_count') or 0))
|
|
54
|
+
top = active[:10]
|
|
55
|
+
lines = [f\" [{mid}, {m.get('trigger_count', 0)}x] {m.get('title', '?')}\" for mid, m in top]
|
|
56
|
+
total = len(active)
|
|
57
|
+
more = f' (+{total - len(top)} more)' if total > len(top) else ''
|
|
58
|
+
print(f'Cued-recall memory active ({total} memories{more}):\\n' + '\\n'.join(lines))
|
|
59
|
+
except Exception:
|
|
60
|
+
print('')
|
|
61
|
+
" 2>/dev/null || echo "")
|
|
62
|
+
if [[ -n "$MEMORY_SUMMARY" ]]; then
|
|
63
|
+
MSG+=$'\n'"$MEMORY_SUMMARY"
|
|
64
|
+
MSG+=$'\n'"Memories auto-inject when path/symbol/intent cues match. Read full content from .ciel/memory/{episodes,concepts,guards}/ when relevant."
|
|
65
|
+
fi
|
|
66
|
+
elif [[ -d "$CWD/.ciel" ]] || [[ -f "$CWD/.claude/settings.json" ]] || [[ -f "$CWD/opencode.json" ]] || [[ -f "$CWD/ciel-overlay.md" ]]; then
|
|
67
|
+
# Only suggest bootstrap if Ciel is actually installed in this project (not
|
|
68
|
+
# any random repo with a CLAUDE.md). Markers checked: .ciel/ dir, .claude
|
|
69
|
+
# settings, opencode config, or an explicit Ciel overlay.
|
|
70
|
+
MSG+=$'\n'"No cued-recall memory yet. Run /ciel-memory-bootstrap to scan project for ingestable tribal docs (lessons.md, ciel-overlay.md, .claude/rules/, etc.)."
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# ─── Update check (throttled to once per 24h, never blocks) ──────────────────
|
|
74
|
+
# Fetches GitHub VERSION non-blocking (max 2s). Any failure → silent skip.
|
|
75
|
+
MANIFEST="$HOME/.ciel/manifest.json"
|
|
76
|
+
LAST_CHECK="$HOME/.ciel/.last-update-check"
|
|
77
|
+
if [[ -f "$MANIFEST" ]]; then
|
|
78
|
+
STALE=false
|
|
79
|
+
if [[ ! -f "$LAST_CHECK" ]]; then
|
|
80
|
+
STALE=true
|
|
81
|
+
elif find "$LAST_CHECK" -mmin +1440 2>/dev/null | grep -q .; then
|
|
82
|
+
STALE=true
|
|
83
|
+
fi
|
|
84
|
+
if $STALE; then
|
|
85
|
+
LOCAL_VER=$(grep -oE '"version":[[:space:]]*"[^"]+"' "$MANIFEST" 2>/dev/null \
|
|
86
|
+
| head -1 | sed 's/.*"\([^"]*\)".*/\1/')
|
|
87
|
+
REMOTE_VER=$(curl -fsSL --max-time 2 \
|
|
88
|
+
https://raw.githubusercontent.com/KaosKyun/Ciel/main/VERSION 2>/dev/null \
|
|
89
|
+
| tr -d '[:space:]')
|
|
90
|
+
if [[ -n "$LOCAL_VER" && -n "$REMOTE_VER" && "$LOCAL_VER" != "$REMOTE_VER" ]]; then
|
|
91
|
+
MSG+=" [UPDATE] Ciel v$LOCAL_VER → v$REMOTE_VER available. Run /ciel-update."
|
|
92
|
+
fi
|
|
93
|
+
mkdir -p "$HOME/.ciel" 2>/dev/null || true
|
|
94
|
+
touch "$LAST_CHECK" 2>/dev/null || true
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo "$MSG"
|
|
99
|
+
exit 0
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Ciel — UserPromptSubmit hook
|
|
3
|
+
# Trigger: user submits a prompt (before Claude processes)
|
|
4
|
+
# Purpose: light depth pre-classification hint injected into context
|
|
5
|
+
# Invokes: depth-classifier skill (lightweight mode)
|
|
6
|
+
# Never blocks (exit 0 always)
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat 2>/dev/null || echo "{}")
|
|
9
|
+
PROMPT=$(echo "$INPUT" | python3 -c "
|
|
10
|
+
import sys, json
|
|
11
|
+
try:
|
|
12
|
+
d = json.load(sys.stdin)
|
|
13
|
+
print(d.get('prompt', ''))
|
|
14
|
+
except:
|
|
15
|
+
print('')
|
|
16
|
+
" 2>/dev/null || echo "")
|
|
17
|
+
|
|
18
|
+
[ -z "$PROMPT" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# Mechanical depth signals
|
|
21
|
+
DEPTH="Standard" # default
|
|
22
|
+
REASON=""
|
|
23
|
+
|
|
24
|
+
# Check for Critical signals
|
|
25
|
+
if echo "$PROMPT" | grep -qiE '\b(auth|authenti|author|jwt|oauth|password|secret|token|session|payment|credit.card|migration.*schema|2fa|mfa|encryption|credential|cookie.*security)\b'; then
|
|
26
|
+
DEPTH="Critical"
|
|
27
|
+
REASON="auth/security/payment keyword detected"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Check for Trivial signals (only if not Critical)
|
|
31
|
+
if [[ "$DEPTH" != "Critical" ]] && echo "$PROMPT" | grep -qiE '\b(rename|typo|copyright|comment|readme|1-line|one.line|fix.typo|spelling)\b'; then
|
|
32
|
+
DEPTH="Trivial"
|
|
33
|
+
REASON="rename/typo/docs keyword detected"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
DISPATCH_GATE=""
|
|
37
|
+
if [[ "$DEPTH" == "Standard" || "$DEPTH" == "Critical" ]]; then
|
|
38
|
+
DISPATCH_GATE=" | DISPATCH GATE: dispatch ciel-researcher + ciel-explorer in parallel BEFORE first Bash/Read/Edit."
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
META_GATE=""
|
|
42
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-}"
|
|
43
|
+
if [ -n "$PROJECT_DIR" ] && [ -f "$PROJECT_DIR/.ciel/tracked-files.json" ]; then
|
|
44
|
+
EDIT_COUNT=$(CIEL_PATH="$PROJECT_DIR/.ciel/tracked-files.json" python3 -c "
|
|
45
|
+
import json, os
|
|
46
|
+
try: print(len(json.load(open(os.environ['CIEL_PATH']))))
|
|
47
|
+
except: print(0)
|
|
48
|
+
" 2>/dev/null || echo "0")
|
|
49
|
+
if [ "${EDIT_COUNT:-0}" -gt 0 ] 2>/dev/null; then
|
|
50
|
+
META_GATE=" | META GATE: ${EDIT_COUNT} file(s) edited this session — complete 10-item META if previous task ended at PROUVER."
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# ─── Cued-recall: intervention pattern detection ─────────────────────────────
|
|
55
|
+
# When the user message contains a clear correction/intervention pattern,
|
|
56
|
+
# suggest capturing it as a memory. Patterns are intentionally narrow to keep
|
|
57
|
+
# false-positive rate low — generic words like "wait" / "stop" / "actually"
|
|
58
|
+
# are NOT triggers on their own; they must combine with a negation/correction
|
|
59
|
+
# adjacent. Never auto-silent — the model surfaces a question to the user.
|
|
60
|
+
# See ADR-0001 and skill `memoire`.
|
|
61
|
+
INTERVENTION_GATE=""
|
|
62
|
+
# POSIX-ERE only (no PCRE lookahead). Patterns are intentionally high-precision
|
|
63
|
+
# to avoid false positives on generic words (wait/stop/actually). Each pattern
|
|
64
|
+
# is a clear signal of correction or "you missed something".
|
|
65
|
+
if echo "$PROMPT" | grep -qiE "(tu as oublié|t'as oublié|n'oublie pas (que|de)|non en fait|non,? en fait|attention que|rappelle-toi (que|de)|ici on (fait|utilise) plutôt|non on (fait|utilise) plutôt|en fait c'est pas|c'est pas comme ça|mauvaise approche|tu te trompes|you forgot (to|that)|don't forget (to|that)|that's not (right|correct|how)|that's wrong|no[,]? actually|actually,? no|wait[,—-] (no|don't|you forgot)|stop[,—-] (no|you forgot|don't))"; then
|
|
66
|
+
INTERVENTION_GATE=" | CAPTURE GATE: intervention pattern detected — propose AskUserQuestion to capture as memory under .ciel/memory/episodes/ (skill: memoire). Never silent-write."
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# ─── Cued-recall: query memory engine for matching memories ──────────────────
|
|
70
|
+
# Calls hooks/memory-engine.py if installed and a memory corpus exists. The
|
|
71
|
+
# engine handles cue extraction (paths, symbols, intents, language), scoring,
|
|
72
|
+
# token cap, decay, and trigger updates. See docs/adrs/0001-cued-recall-memory.md.
|
|
73
|
+
MEMORY_OUTPUT=""
|
|
74
|
+
ENGINE_PATH=""
|
|
75
|
+
# Resolution order: same dir as this script (most reliable, found via BASH_SOURCE)
|
|
76
|
+
# → project-relative paths in priority order → $HOME fallbacks. Covers local-mode
|
|
77
|
+
# install (top-level hooks/), curl-mode install (.claude/hooks/ or ~/.claude/plugins/ciel/),
|
|
78
|
+
# and OpenCode plugin layout (~/.config/opencode/...).
|
|
79
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd 2>/dev/null || echo "")"
|
|
80
|
+
for candidate in \
|
|
81
|
+
"$SCRIPT_DIR/memory-engine.py" \
|
|
82
|
+
"$PROJECT_DIR/.claude/hooks/memory-engine.py" \
|
|
83
|
+
"$PROJECT_DIR/hooks/memory-engine.py" \
|
|
84
|
+
"$HOME/.claude/plugins/ciel/memory-engine.py" \
|
|
85
|
+
"$HOME/.ciel/hooks/memory-engine.py"; do
|
|
86
|
+
if [[ -n "$candidate" ]] && [[ -f "$candidate" ]]; then
|
|
87
|
+
ENGINE_PATH="$candidate"
|
|
88
|
+
break
|
|
89
|
+
fi
|
|
90
|
+
done
|
|
91
|
+
|
|
92
|
+
if [[ -n "$ENGINE_PATH" ]] && [[ -n "$PROJECT_DIR" ]] && [[ -f "$PROJECT_DIR/.ciel/memory/index.json" ]]; then
|
|
93
|
+
DEPTH_LOWER=$(echo "$DEPTH" | tr '[:upper:]' '[:lower:]')
|
|
94
|
+
MEMORY_OUTPUT=$(python3 "$ENGINE_PATH" query --prompt "$PROMPT" --cwd "$PROJECT_DIR" --depth "$DEPTH_LOWER" 2>/dev/null || echo "")
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
MSG_BASE="CIEL depth hint: $DEPTH ($REASON).$DISPATCH_GATE$META_GATE$INTERVENTION_GATE Invoke depth-classifier if ambiguous before routing pipeline."
|
|
98
|
+
|
|
99
|
+
# Emit JSON via python to handle newlines and quoting safely
|
|
100
|
+
MSG_BASE="$MSG_BASE" MEMORY_OUTPUT="$MEMORY_OUTPUT" python3 -c "
|
|
101
|
+
import os, json
|
|
102
|
+
base = os.environ.get('MSG_BASE', '')
|
|
103
|
+
mem = os.environ.get('MEMORY_OUTPUT', '').strip()
|
|
104
|
+
combined = base + ('\n\n' + mem if mem else '')
|
|
105
|
+
print(json.dumps({
|
|
106
|
+
'hookSpecificOutput': {
|
|
107
|
+
'hookEventName': 'UserPromptSubmit',
|
|
108
|
+
'additionalContext': combined,
|
|
109
|
+
}
|
|
110
|
+
}))
|
|
111
|
+
"
|
|
112
|
+
exit 0
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Audits the current Claude Code session for Ciel paradigm violations (missed Task dispatches, inline gathering, hook inactivity, skill overlaps, intent routing misses). Produces a structured report with a Ciel Health Score (0-100). If score <
|
|
2
|
+
description: Audits the current Claude Code session for Ciel paradigm violations (missed Task dispatches, inline gathering, hook inactivity, skill overlaps, intent routing misses). Produces a structured report with a Ciel Health Score (0-100). If score < 90, creates a GitHub Issue on the Ciel repository with the findings and session timeline. Hook-independent — works even when Ciel hooks are broken.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# /ciel-audit — Session post-mortem
|
|
6
6
|
|
|
7
|
-
*Generates a structured report of Ciel behavior violations observed in the current session. Calculates a Ciel Health Score (0-100). If the score is below
|
|
7
|
+
*Generates a structured report of Ciel behavior violations observed in the current session. Calculates a Ciel Health Score (0-100). If the score is below 90, creates a GitHub Issue on the Ciel repository (github.com/KaosKyun/Ciel) with the full timeline and findings — otherwise produces the report only without creating an issue.*
|
|
8
8
|
|
|
9
9
|
Usage: `/ciel-audit`
|
|
10
10
|
|
|
@@ -85,17 +85,78 @@ If npm version > local version, include an **Update notification** in the report
|
|
|
85
85
|
|
|
86
86
|
#### Dimension 8: Platform health — penalty up to -5
|
|
87
87
|
|
|
88
|
-
Check
|
|
88
|
+
Check that Ciel platform installations exist and are valid. Ciel currently supports two platforms: **claude** and **opencode**.
|
|
89
89
|
|
|
90
|
+
**Claude Code** — check for expected agent and hook files:
|
|
90
91
|
```bash
|
|
91
|
-
ls /
|
|
92
|
+
CLAUDE_AGENTS=$(ls .claude/agents/ciel-*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
93
|
+
CLAUDE_HOOK=$(test -f .claude/hooks/session-start.sh && echo "1" || echo "0")
|
|
94
|
+
CLAUDE_SETTINGS=$(test -f .claude/settings.json && echo "1" || echo "0")
|
|
95
|
+
echo "Claude: agents=$CLAUDE_AGENTS hook=$CLAUDE_HOOK settings=$CLAUDE_SETTINGS"
|
|
96
|
+
if [ "$CLAUDE_AGENTS" -ge 3 ] && [ "$CLAUDE_HOOK" = "1" ] && [ "$CLAUDE_SETTINGS" = "1" ]; then
|
|
97
|
+
echo "Claude platform: OK"
|
|
98
|
+
else
|
|
99
|
+
echo "Claude platform: INCOMPLETE"
|
|
100
|
+
fi
|
|
92
101
|
```
|
|
102
|
+
Expected: at least 3 agent files + session-start.sh + settings.json
|
|
93
103
|
|
|
94
|
-
|
|
104
|
+
**OpenCode** — check for expected plugin, agent, and command files:
|
|
105
|
+
```bash
|
|
106
|
+
OPENCODE_PLUGIN=$(test -f .opencode/plugins/ciel.ts && echo "1" || echo "0")
|
|
107
|
+
OPENCODE_AGENTS=$(ls .opencode/agents/ciel-*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
108
|
+
OPENCODE_COMMANDS=$(ls .opencode/commands/ciel*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
109
|
+
echo "OpenCode: plugin=$OPENCODE_PLUGIN agents=$OPENCODE_AGENTS commands=$OPENCODE_COMMANDS"
|
|
110
|
+
if [ "$OPENCODE_PLUGIN" = "1" ] && [ "$OPENCODE_AGENTS" -ge 3 ] && [ "$OPENCODE_COMMANDS" -ge 5 ]; then
|
|
111
|
+
echo "OpenCode platform: OK"
|
|
112
|
+
else
|
|
113
|
+
echo "OpenCode platform: INCOMPLETE"
|
|
114
|
+
fi
|
|
115
|
+
```
|
|
116
|
+
Expected: ciel.ts plugin + at least 3 agent files + at least 5 command files
|
|
117
|
+
|
|
118
|
+
Scoring:
|
|
119
|
+
- Both platforms fully present and valid: **0**
|
|
120
|
+
- One platform missing or incomplete: **-3**
|
|
121
|
+
- Both platforms missing or critically incomplete: **-5**
|
|
122
|
+
|
|
123
|
+
**Important**: Do NOT check for `.claude/plugins/ciel/platforms/` or `.opencode/platforms/` directories — these are not part of the v6 architecture. Platform files are installed directly into `.claude/` and `.opencode/` respectively. Do NOT check for codex, cursor, kilocode, lmstudio, ollama, or windsurf — these platforms are not yet implemented.
|
|
124
|
+
|
|
125
|
+
#### Dimension 9: Memory health — penalty up to -10
|
|
95
126
|
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
-
|
|
127
|
+
Check the cued-recall memory system (see `docs/adrs/0001-cued-recall-memory.md`):
|
|
128
|
+
|
|
129
|
+
- **index.json missing**: `.ciel/memory/index.json` does not exist. The memory system was never bootstrapped. **-10**
|
|
130
|
+
- **index.json exists but episodes/ empty**: `.ciel/memory/episodes/` has no files. Bootstrap ran but no memories were ingested, or the directory structure is incomplete. **-5**
|
|
131
|
+
- **Low trigger ratio**: Count memories with `trigger_count > 0` vs total. If < 30% of memories have ever been triggered, the cue-matching system may be misconfigured or the memories are not relevant to actual usage. **-3**
|
|
132
|
+
- **Stale memories**: Any memory with `stale: true` or with `last_triggered` older than `stale_after_days` (default 90). Stale memories waste index space and should be cleaned up by `memory-engine.py rebuild-index`. **-2**
|
|
133
|
+
|
|
134
|
+
Scoring:
|
|
135
|
+
- index.json missing: **-10** (blocks all other checks)
|
|
136
|
+
- index.json present but no episode files: **-5**
|
|
137
|
+
- All checks pass: **0**
|
|
138
|
+
|
|
139
|
+
Run these checks:
|
|
140
|
+
```bash
|
|
141
|
+
# Check index.json exists
|
|
142
|
+
test -f .ciel/memory/index.json && echo "index: OK" || echo "index: MISSING"
|
|
143
|
+
|
|
144
|
+
# Count episodes
|
|
145
|
+
EPISODES=$(ls .ciel/memory/episodes/*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
146
|
+
echo "episodes: $EPISODES"
|
|
147
|
+
|
|
148
|
+
# Count triggered vs total (requires python3)
|
|
149
|
+
python3 -c "
|
|
150
|
+
import json
|
|
151
|
+
with open('.ciel/memory/index.json') as f:
|
|
152
|
+
idx = json.load(f)
|
|
153
|
+
mems = idx.get('memories', {})
|
|
154
|
+
total = len(mems)
|
|
155
|
+
triggered = sum(1 for m in mems.values() if m.get('trigger_count', 0) > 0)
|
|
156
|
+
stale = sum(1 for m in mems.values() if m.get('stale'))
|
|
157
|
+
print(f'total: {total}, triggered: {triggered} ({0 if total==0 else triggered*100//total}%), stale: {stale}')
|
|
158
|
+
" 2>/dev/null || echo "memory check failed (no python3?)"
|
|
159
|
+
```
|
|
99
160
|
|
|
100
161
|
---
|
|
101
162
|
|
|
@@ -106,9 +167,7 @@ Expected platforms: codex, cursor, kilocode, lmstudio, ollama, opencode, windsur
|
|
|
106
167
|
| Score range | Status | Issue created? |
|
|
107
168
|
|-------------|--------|----------------|
|
|
108
169
|
| 90-100 | Excellent | No |
|
|
109
|
-
|
|
|
110
|
-
| 50-74 | Needs improvement | **Yes** — creates issue with timeline |
|
|
111
|
-
| 0-49 | Critical | **Yes** — creates issue with timeline |
|
|
170
|
+
| 0-89 | Needs improvement | **Yes** — creates issue with timeline |
|
|
112
171
|
|
|
113
172
|
The score is calculated automatically from the detected violations.
|
|
114
173
|
|
|
@@ -124,7 +183,7 @@ Begin the output with the literal line `# Ciel Session Audit Report`. End with t
|
|
|
124
183
|
**Date**: <today's date>
|
|
125
184
|
**Ciel Health Score**: <N>/100 — <Excellent|Good|Needs improvement|Critical>
|
|
126
185
|
**npm**: local v<X.Y.Z> | npm v<X.Y.Z> | <up-to-date|update available>
|
|
127
|
-
**Platforms**:
|
|
186
|
+
**Platforms**: claude ✓ opencode ✓ (or ✗ if missing)
|
|
128
187
|
**Session summary**: <N> /ciel invocation(s), <N> total tool calls, <N> Task() dispatches, <N> inline Bash/Read/Grep/WebSearch calls in main session.
|
|
129
188
|
|
|
130
189
|
**Verdict**: <PASS | VIOLATIONS FOUND>
|
|
@@ -173,6 +232,7 @@ Begin the output with the literal line `# Ciel Session Audit Report`. End with t
|
|
|
173
232
|
| D6 — Intent routing | -<N> |
|
|
174
233
|
| D7 — npm version | -<N> |
|
|
175
234
|
| D8 — Platform health | -<N> |
|
|
235
|
+
| D9 — Memory health | -<N> |
|
|
176
236
|
| **Total** | **-<N>** |
|
|
177
237
|
| **Health Score** | **<N>/100** |
|
|
178
238
|
|
|
@@ -202,7 +262,7 @@ Output a single short section — **no issue is created** for PASS verdicts:
|
|
|
202
262
|
**Date**: <today's date>
|
|
203
263
|
**Ciel Health Score**: 100/100 — Excellent
|
|
204
264
|
**npm**: local v<X.Y.Z> | npm v<X.Y.Z> | up-to-date
|
|
205
|
-
**Platforms**:
|
|
265
|
+
**Platforms**: claude ✓ opencode ✓
|
|
206
266
|
**Session summary**: <N> /ciel invocation(s), <N> tool calls, <N> Task() dispatches.
|
|
207
267
|
**Verdict**: PASS
|
|
208
268
|
|
|
@@ -213,11 +273,11 @@ Output a single short section — **no issue is created** for PASS verdicts:
|
|
|
213
273
|
|
|
214
274
|
---
|
|
215
275
|
|
|
216
|
-
### GitHub Issue creation (only if score <
|
|
276
|
+
### GitHub Issue creation (only if score < 90)
|
|
217
277
|
|
|
218
|
-
If the Ciel Health Score is **below
|
|
278
|
+
If the Ciel Health Score is **below 90**, create a GitHub Issue with the report AND the session timeline.
|
|
219
279
|
|
|
220
|
-
**Important**: Do NOT create an issue if score >=
|
|
280
|
+
**Important**: Do NOT create an issue if score >= 90. Only create for scores < 90.
|
|
221
281
|
|
|
222
282
|
1. **Check for duplicate issues first**:
|
|
223
283
|
```bash
|
|
@@ -299,6 +359,6 @@ If the Ciel Health Score is **below 75**, create a GitHub Issue with the report
|
|
|
299
359
|
- Do NOT invoke other Ciel skills. This command is fully self-contained.
|
|
300
360
|
- Do NOT dispatch `Task()` agents. Audit happens inline.
|
|
301
361
|
- Do NOT ask clarifying questions. Produce the report with the information you have.
|
|
302
|
-
- Do NOT create an issue if score >=
|
|
362
|
+
- Do NOT create an issue if score >= 90. Only create for score < 90.
|
|
303
363
|
- Do NOT create duplicate issues — run the `gh issue list` check before creating.
|
|
304
364
|
- Do NOT restart, rerun, or attempt to fix the session in-flight. The audit report is the deliverable.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Scan project for ingestable tribal docs (lessons.md, ciel-overlay.md, .claude/rules/, etc.) and propose ingestion into the cued-recall memory under .ciel/memory/. Reports findings if no sources found. Always confirms each candidate with the user before writing.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# /ciel-memory-bootstrap — Initialize Cued-Recall Memory
|
|
6
|
+
|
|
7
|
+
**Purpose:** First-run scan of an existing project to convert tribal knowledge already documented in `lessons.md`, `ciel-overlay.md`, `.claude/rules/`, and similar files into the structured cued-recall memory at `.ciel/memory/`.
|
|
8
|
+
|
|
9
|
+
**Usage:** `/ciel-memory-bootstrap` (no args)
|
|
10
|
+
|
|
11
|
+
This is **deterministic**: no agent dispatch, no pipeline, no DIVERGE/EVALUER. Just scan, propose, write on user confirmation.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Instructions
|
|
16
|
+
|
|
17
|
+
You are bootstrapping the cued-recall memory for this project. Follow these steps in order.
|
|
18
|
+
|
|
19
|
+
### Step 1 — Scan
|
|
20
|
+
|
|
21
|
+
Run the bootstrap script in `scan` mode:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Try installed location first, fallback to dev location
|
|
25
|
+
script="$CLAUDE_PROJECT_DIR/.claude/hooks/memory-bootstrap.sh"
|
|
26
|
+
[ -f "$script" ] || script="$CLAUDE_PROJECT_DIR/hooks/memory-bootstrap.sh"
|
|
27
|
+
bash "$script" scan
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or, if running on an installed Ciel: `bash "$HOME/.ciel/hooks/memory-bootstrap.sh" scan`.
|
|
31
|
+
|
|
32
|
+
Report the output verbatim to the user.
|
|
33
|
+
|
|
34
|
+
### Step 2 — Decide path
|
|
35
|
+
|
|
36
|
+
Based on the scan output:
|
|
37
|
+
|
|
38
|
+
- **If 0 sources found** → tell the user clearly: "No tribal docs to bootstrap from. The cued-recall memory will populate organically as you intervene with me. Nothing more to do." End here.
|
|
39
|
+
- **If sources found** → proceed to Step 3.
|
|
40
|
+
|
|
41
|
+
### Step 3 — Initialize structure
|
|
42
|
+
|
|
43
|
+
Run:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
script="$CLAUDE_PROJECT_DIR/.claude/hooks/memory-bootstrap.sh"
|
|
47
|
+
[ -f "$script" ] || script="$CLAUDE_PROJECT_DIR/hooks/memory-bootstrap.sh"
|
|
48
|
+
bash "$script" ingest
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This creates `.ciel/memory/{episodes,concepts,guards}/` and an empty `index.json`. It does NOT auto-write memories — auto-ingestion would create cargo-cult entries from possibly-stale docs (see ADR-0001).
|
|
52
|
+
|
|
53
|
+
### Step 4 — Read each source
|
|
54
|
+
|
|
55
|
+
For each source found in Step 1, `Read` the file fully. Identify candidate memories:
|
|
56
|
+
|
|
57
|
+
| Source format | What becomes a memory |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `[YYYY-MM-DD] MISTAKE: X → RULE: Y` lines (lessons.md style) | One memory per line. Title = the rule. |
|
|
60
|
+
| `## Heading\n\n- rule\n- rule` (rules.md style) | One memory per rule. |
|
|
61
|
+
| Numbered lessons in `ciel-overlay.md` "Key Lessons" | One memory per lesson. |
|
|
62
|
+
| `## section` in CLAUDE.md/AGENTS.md describing a non-obvious convention | One memory per section. |
|
|
63
|
+
|
|
64
|
+
Skip:
|
|
65
|
+
- The pipeline / workflow descriptions (those belong in CLAUDE.md, not memory)
|
|
66
|
+
- General principles already in CLAUDE.md
|
|
67
|
+
- Anything that's just project description (READMEish)
|
|
68
|
+
- Code examples (those go in skills/, not memory)
|
|
69
|
+
|
|
70
|
+
### Step 5 — Propose batch capture
|
|
71
|
+
|
|
72
|
+
Once you have N candidate memories from the sources, present them to the user **as a batch**, not one by one (avoid 50 confirmation prompts). Use a single `AskUserQuestion` with the structure:
|
|
73
|
+
|
|
74
|
+
> "Found N candidates from your tribal docs. I'll list them; you tell me which to capture, which to skip, or 'all'."
|
|
75
|
+
|
|
76
|
+
For each candidate, show:
|
|
77
|
+
- **Title** (one line)
|
|
78
|
+
- **Source** (file:line)
|
|
79
|
+
- **Suggested tags** (paths, symbols, intents, language inferred from the lesson content)
|
|
80
|
+
|
|
81
|
+
The user replies with: "all", "1,3,5,8" (specific indices), or "skip".
|
|
82
|
+
|
|
83
|
+
### Step 6 — Write captured memories
|
|
84
|
+
|
|
85
|
+
For each captured candidate, create `.ciel/memory/episodes/<YYYY-MM-DD>-<slug>.md` with frontmatter:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
---
|
|
89
|
+
id: mem_<NNN>
|
|
90
|
+
title: <title>
|
|
91
|
+
languages: [<inferred>]
|
|
92
|
+
path_patterns:
|
|
93
|
+
- <pattern>
|
|
94
|
+
symbols: [<inferred>]
|
|
95
|
+
intents: [<inferred>]
|
|
96
|
+
captured_at: <ISO8601 now>
|
|
97
|
+
captured_from: bootstrap
|
|
98
|
+
source: <original-file:line>
|
|
99
|
+
trigger_count: 0
|
|
100
|
+
last_triggered: null
|
|
101
|
+
stale_after_days: 90
|
|
102
|
+
stale: false
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
# <title>
|
|
106
|
+
|
|
107
|
+
<content from source, lightly cleaned>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
ID strategy: read existing `index.json` for max id, increment. Slug = first 5 words of title, kebab-cased.
|
|
111
|
+
|
|
112
|
+
### Step 7 — Rebuild index
|
|
113
|
+
|
|
114
|
+
After all writes, regenerate `.ciel/memory/index.json` by parsing every frontmatter under `.ciel/memory/{episodes,concepts,guards}/`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# pseudo — use python3 -c '...' inline
|
|
118
|
+
for each *.md file:
|
|
119
|
+
parse frontmatter
|
|
120
|
+
add to memories dict by id
|
|
121
|
+
for each path_pattern, symbol, intent, language:
|
|
122
|
+
append id to corresponding by_* index
|
|
123
|
+
write back to index.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 8 — Confirm
|
|
127
|
+
|
|
128
|
+
Report:
|
|
129
|
+
|
|
130
|
+
- N memories captured
|
|
131
|
+
- Sources processed
|
|
132
|
+
- Index rebuilt with M total entries
|
|
133
|
+
- Suggest: "Cued-recall memory now active. Memories will auto-inject when their cues match in future tasks. Run `/ciel-memory-bootstrap` again anytime to re-scan for new tribal docs."
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Constraints
|
|
138
|
+
|
|
139
|
+
- **Never write a memory without user confirmation.** Even on bulk confirmation ("all"), display the list first.
|
|
140
|
+
- **Do not delete the source files.** Bootstrap converts; the user keeps the originals as long as they want.
|
|
141
|
+
- **Tag conservatively.** A memory tagged with `**/*` will fire on every task and pollute. If unsure, narrow the path pattern.
|
|
142
|
+
- **No agent dispatch.** This command is deterministic and runs inline.
|
|
143
|
+
- **Idempotent.** Re-running on an already-bootstrapped project should detect existing memories (by source field) and offer to skip duplicates.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Failure modes
|
|
148
|
+
|
|
149
|
+
| Symptom | Cause | Fix |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| Script not found | `$CLAUDE_PROJECT_DIR` not set | Try `$HOME/.ciel/hooks/memory-bootstrap.sh` instead |
|
|
152
|
+
| Nothing scanned | No tribal docs in this project | Working as intended; report and end |
|
|
153
|
+
| Memories all tagged with broad paths | Source content didn't include path hints | Ask user to refine tags after listing |
|
|
154
|
+
| index.json malformed after rebuild | python3 parse error | Recreate empty index, re-run rebuild step |
|
|
155
|
+
|
|
156
|
+
## See also
|
|
157
|
+
|
|
158
|
+
- `docs/adrs/0001-cued-recall-memory.md` — full design rationale
|
|
159
|
+
- `skills/workflow/memoire/SKILL.md` — capture/recall flow
|
|
160
|
+
- `skills/workflow/memoire-consolidator/SKILL.md` — periodic maintenance
|