@neikyun/ciel 6.8.0 → 6.9.1

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.
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+ # Ciel — PreToolUse hook for Agent (subagent type gate)
3
+ # Trigger: PreToolUse on Agent
4
+ # Purpose: block Agent dispatches that don't use a ciel-* subagent_type.
5
+ # Generic agents bypass Haiku model, call caps, and researcher waterfall
6
+ # → cost 20K-160K tokens vs ~5K for a constrained Ciel agent.
7
+ #
8
+ # Escape hatch: include [CIEL_GATE_BYPASS] anywhere in the prompt to allow
9
+ # a non-ciel agent through (e.g. legitimate one-off native dispatch).
10
+
11
+ set -euo pipefail
12
+
13
+ input_json=""
14
+ if [ ! -t 0 ]; then
15
+ input_json=$(cat)
16
+ fi
17
+ [ -z "$input_json" ] && exit 0
18
+
19
+ parsed=$(echo "$input_json" | python3 -c "
20
+ import json, sys
21
+ try:
22
+ d = json.load(sys.stdin)
23
+ tool_input = d.get('tool_input', {})
24
+ subagent_type = tool_input.get('subagent_type', '')
25
+ prompt = tool_input.get('prompt', '')
26
+ print(subagent_type + '\t' + prompt[:200])
27
+ except Exception:
28
+ print('\t')
29
+ " 2>/dev/null)
30
+
31
+ subagent_type="${parsed%%$'\t'*}"
32
+ prompt_head="${parsed#*$'\t'}"
33
+
34
+ # Allow explicit bypass
35
+ if echo "$prompt_head" | grep -q "\[CIEL_GATE_BYPASS\]"; then
36
+ exit 0
37
+ fi
38
+
39
+ # Allow ciel-* subagent types
40
+ if [[ "$subagent_type" == ciel-* ]]; then
41
+ exit 0
42
+ fi
43
+
44
+ # Block everything else — generic or non-ciel subagent_type
45
+ label="${subagent_type:-<missing>}"
46
+ python3 -c "
47
+ import json
48
+ print(json.dumps({
49
+ 'hookSpecificOutput': {
50
+ 'hookEventName': 'PreToolUse',
51
+ 'permissionDecision': 'deny',
52
+ 'permissionDecisionReason': '[CIEL AGENT GATE] Blocked: subagent_type=\"${label}\" is not a Ciel agent. Re-dispatch with subagent_type=\"ciel-researcher\" | \"ciel-explorer\" | \"ciel-critic\" | \"ciel-improver\". Generic agents bypass Haiku model, call caps, and researcher waterfall early-exit. Add [CIEL_GATE_BYPASS] to prompt to force-allow a non-Ciel dispatch.'
53
+ }
54
+ }))"
55
+ exit 0
@@ -0,0 +1,83 @@
1
+ #!/bin/bash
2
+ # Ciel — PreToolUse hook for Write/Edit
3
+ # Trigger: PreToolUse on Write|Edit
4
+ # Purpose: inject faire-gatekeeper + dispatch gate + pipeline reminders before code write
5
+ # Critical files get additional stride-analyzer hint
6
+ # Always exits 0 (never blocks), outputs reminders via stderr (reliable channel)
7
+ # Dispatch counter: /tmp/ciel_dispatched set by SubagentStart hooks (ciel-researcher/explorer)
8
+
9
+ INPUT=$(cat)
10
+
11
+ FILE_PATH=$(echo "$INPUT" | python3 -c "
12
+ import sys, json
13
+ try:
14
+ d = json.load(sys.stdin)
15
+ tip = d.get('tool_input', {})
16
+ print(tip.get('file_path', tip.get('path', '')))
17
+ except:
18
+ print('')
19
+ " 2>/dev/null || echo "")
20
+
21
+ [ -z "$FILE_PATH" ] && exit 0
22
+
23
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-}"
24
+
25
+ # === DISPATCH GATE CHECK ===
26
+ # /tmp/ciel_dispatched is created by SubagentStart hooks when ciel-researcher/explorer dispatch
27
+ DISPATCHED=0
28
+ DISPATCH_FLAG="/tmp/ciel_dispatched"
29
+ if [ -f "$DISPATCH_FLAG" ]; then
30
+ DISPATCHED=1
31
+ fi
32
+
33
+ # === FILE TRACK COUNT (RELIRE GATE) ===
34
+ COUNT=0
35
+ if [ -n "$PROJECT_DIR" ] && [ -f "$PROJECT_DIR/.ciel/tracked-files.json" ]; then
36
+ COUNT=$(CIEL_PATH="$PROJECT_DIR/.ciel/tracked-files.json" python3 -c "
37
+ import json, os
38
+ try: print(len(json.load(open(os.environ['CIEL_PATH']))))
39
+ except: print(0)
40
+ " 2>/dev/null || echo "0")
41
+ fi
42
+
43
+ # Build warnings
44
+ WARNINGS=""
45
+
46
+ # Dispatch gate warning (no dispatched agents on non-trivial write)
47
+ if [ "$DISPATCHED" -eq 0 ] && [ "$COUNT" -ge 1 ]; then
48
+ WARNINGS="${WARNINGS}[DISPATCH GATE] WARNING: Writing file ${FILE_PATH} without prior Task() dispatch (ciel-researcher + ciel-explorer). Was this classified as Trivial? If Standard+, dispatch subagents BEFORE writing code."
49
+ fi
50
+
51
+ # RELIRE gate warning
52
+ if [ "${COUNT:-0}" -ge 2 ] 2>/dev/null; then
53
+ PIPELINE_WARN=" | CIEL PIPELINE: ${COUNT} file(s) edited. Have researcher+explorer been dispatched? If 3+ files: ciel-critic MODE=RELIRE required before merge."
54
+ WARNINGS="${WARNINGS}${PIPELINE_WARN}"
55
+ fi
56
+
57
+ # If only dispatch gate fires, prefix to std err
58
+ if [ -n "$WARNINGS" ]; then
59
+ echo "[CIEL PRE-WRITE]" >&2
60
+ echo "$WARNINGS" | while IFS= read -r line; do
61
+ echo " $line" >&2
62
+ done
63
+ fi
64
+
65
+ # === FAIRE GATE REMINDER ===
66
+ # Skip non-code files
67
+ if ! echo "$FILE_PATH" | grep -qE '\.(kt|java|ts|tsx|js|jsx|py|go|rs|rb|php|cs|cpp|c|swift|scala|vue|svelte|sql|sh|json|yaml|yml|toml)$'; then
68
+ exit 0
69
+ fi
70
+
71
+ # Check if file is critical path
72
+ CRITICAL=false
73
+ if echo "$FILE_PATH" | grep -qiE '(auth|Auth|security|Security|Token|Session|Password|Secret)'; then
74
+ CRITICAL=true
75
+ fi
76
+
77
+ if $CRITICAL; then
78
+ echo " [CIEL] Critical path: invoke faire-gatekeeper, stride-analyzer must have run, test-first (RED)." >&2
79
+ else
80
+ echo " [CIEL] Standard path: invoke faire-gatekeeper (alternatives, idiomatic, quality, removal, test-first)." >&2
81
+ fi
82
+
83
+ exit 0
@@ -0,0 +1,132 @@
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
+ # python3 succeeds with an empty string when stdin JSON lacks a 'cwd' key — fall through to pwd.
10
+ [ -z "$CWD" ] && CWD="$(pwd)"
11
+
12
+ # Detect overlay presence
13
+ OVERLAY=""
14
+ for candidate in "$CWD/ciel-overlay.md" "$CWD/.claude/ciel-overlay.md"; do
15
+ if [[ -f "$candidate" ]]; then
16
+ OVERLAY="$candidate"
17
+ break
18
+ fi
19
+ done
20
+
21
+ # Reset session-scoped edit tracker so META/RELIRE gates don't bleed across sessions
22
+ if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -f "$CLAUDE_PROJECT_DIR/.ciel/tracked-files.json" ]; then
23
+ echo "[]" > "$CLAUDE_PROJECT_DIR/.ciel/tracked-files.json" 2>/dev/null || true
24
+ fi
25
+
26
+ # Generate TRACE_ID for this session (used by eval logging)
27
+ TRACE_ID=$(date -u +%Y%m%dT%H%M%SZ)-$$
28
+ export CIEL_TRACE_ID="$TRACE_ID"
29
+
30
+ # Resolve Ciel version at runtime (single source of truth, no hardcoded drift).
31
+ # Fallback chain: project sentinel → user sentinel → npm package → marketplace plugin → repo VERSION → unknown.
32
+ # Note: $HOME/.ciel/version is "last writer wins" across npm installs from different projects —
33
+ # project sentinel ($CWD/.ciel/version) is authoritative when present.
34
+ _resolve_ciel_version() {
35
+ local v=""
36
+ for f in \
37
+ "$CWD/.ciel/version" \
38
+ "$HOME/.ciel/version" \
39
+ "$HOME/.claude/plugins/ciel/package.json" \
40
+ "$HOME/.claude/plugins/ciel/.claude-plugin/plugin.json" \
41
+ "$(dirname "$0")/../../VERSION" \
42
+ "$(dirname "$0")/../VERSION"; do
43
+ # Skip entries that resolved against an empty $CWD/$HOME (e.g., "/.ciel/version").
44
+ case "$f" in /.ciel/*|/.claude/*) continue ;; esac
45
+ [ -r "$f" ] || continue
46
+ case "$f" in
47
+ *.json)
48
+ # Anchor to line-start whitespace so we only match top-level "version",
49
+ # not nested keys like "schema_version" or `"version"` deeper in the doc.
50
+ v=$(sed -n 's/^[[:space:]]*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$f" 2>/dev/null | head -1)
51
+ ;;
52
+ *)
53
+ v=$(tr -d '[:space:]' <"$f" 2>/dev/null)
54
+ ;;
55
+ esac
56
+ [ -n "$v" ] && { echo "$v"; return; }
57
+ done
58
+ echo "unknown"
59
+ }
60
+ CIEL_VERSION="$(_resolve_ciel_version)"
61
+ MSG="CIEL v${CIEL_VERSION} — Skills-first deep-reasoning active. "
62
+ if [[ -n "$OVERLAY" ]]; then
63
+ MSG+="Overlay loaded: $OVERLAY. "
64
+ else
65
+ MSG+="No overlay found at $CWD/ciel-overlay.md — create one for project-specific rules. "
66
+ fi
67
+ MSG+="Trace ID: $TRACE_ID. Principle: Understand before generating. Verify before claiming done."
68
+
69
+ # ─── Cued-recall: surface relevant memories ──────────────────────────────────
70
+ # If a memory corpus exists, list active (non-stale) memories so the model
71
+ # knows what cues are available. Full content read on-demand. See ADR-0001.
72
+ MEMORY_INDEX="$CWD/.ciel/memory/index.json"
73
+ if [[ -f "$MEMORY_INDEX" ]]; then
74
+ MEMORY_SUMMARY=$(MEMORY_INDEX="$MEMORY_INDEX" python3 -c "
75
+ import json, os
76
+ try:
77
+ with open(os.environ['MEMORY_INDEX']) as f:
78
+ idx = json.load(f)
79
+ mems = idx.get('memories', {})
80
+ active = [(mid, m) for mid, m in mems.items() if not m.get('stale')]
81
+ if not active:
82
+ print('')
83
+ else:
84
+ # Sort by trigger_count desc, then by last_triggered desc
85
+ active.sort(key=lambda x: (-(x[1].get('trigger_count') or 0), x[1].get('last_triggered') or ''), reverse=False)
86
+ active.sort(key=lambda x: -(x[1].get('trigger_count') or 0))
87
+ top = active[:10]
88
+ lines = [f\" [{mid}, {m.get('trigger_count', 0)}x] {m.get('title', '?')}\" for mid, m in top]
89
+ total = len(active)
90
+ more = f' (+{total - len(top)} more)' if total > len(top) else ''
91
+ print(f'Cued-recall memory active ({total} memories{more}):\\n' + '\\n'.join(lines))
92
+ except Exception:
93
+ print('')
94
+ " 2>/dev/null || echo "")
95
+ if [[ -n "$MEMORY_SUMMARY" ]]; then
96
+ MSG+=$'\n'"$MEMORY_SUMMARY"
97
+ MSG+=$'\n'"Memories auto-inject when path/symbol/intent cues match. Read full content from .ciel/memory/{episodes,concepts,guards}/ when relevant."
98
+ fi
99
+ elif [[ -d "$CWD/.ciel" ]] || [[ -f "$CWD/.claude/settings.json" ]] || [[ -f "$CWD/opencode.json" ]] || [[ -f "$CWD/ciel-overlay.md" ]]; then
100
+ # Only suggest bootstrap if Ciel is actually installed in this project (not
101
+ # any random repo with a CLAUDE.md). Markers checked: .ciel/ dir, .claude
102
+ # settings, opencode config, or an explicit Ciel overlay.
103
+ 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.)."
104
+ fi
105
+
106
+ # ─── Update check (throttled to once per 24h, never blocks) ──────────────────
107
+ # Fetches GitHub VERSION non-blocking (max 2s). Any failure → silent skip.
108
+ MANIFEST="$HOME/.ciel/manifest.json"
109
+ LAST_CHECK="$HOME/.ciel/.last-update-check"
110
+ if [[ -f "$MANIFEST" ]]; then
111
+ STALE=false
112
+ if [[ ! -f "$LAST_CHECK" ]]; then
113
+ STALE=true
114
+ elif find "$LAST_CHECK" -mmin +1440 2>/dev/null | grep -q .; then
115
+ STALE=true
116
+ fi
117
+ if $STALE; then
118
+ LOCAL_VER=$(grep -oE '"version":[[:space:]]*"[^"]+"' "$MANIFEST" 2>/dev/null \
119
+ | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
120
+ REMOTE_VER=$(curl -fsSL --max-time 2 \
121
+ https://raw.githubusercontent.com/KaosKyun/Ciel/main/VERSION 2>/dev/null \
122
+ | tr -d '[:space:]')
123
+ if [[ -n "$LOCAL_VER" && -n "$REMOTE_VER" && "$LOCAL_VER" != "$REMOTE_VER" ]]; then
124
+ MSG+=" [UPDATE] Ciel v$LOCAL_VER → v$REMOTE_VER available. Run /ciel-update."
125
+ fi
126
+ mkdir -p "$HOME/.ciel" 2>/dev/null || true
127
+ touch "$LAST_CHECK" 2>/dev/null || true
128
+ fi
129
+ fi
130
+
131
+ echo "$MSG"
132
+ exit 0
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env bash
2
+ # Ciel — Session-start version check
3
+ # Outputs to stderr so the user sees the notification.
4
+ # Non-blocking: silent on fetch failure (offline/proxy).
5
+ set -euo pipefail
6
+
7
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
8
+ LOCAL_VERSION=$(cat "$PROJECT_DIR/VERSION" 2>/dev/null || echo "0.0.0")
9
+
10
+ REMOTE_VERSION=$(curl -fsSL --connect-timeout 3 "https://raw.githubusercontent.com/KaosKyun/Ciel/main/VERSION" 2>/dev/null || true)
11
+
12
+ if [ -n "$REMOTE_VERSION" ] && [ "$REMOTE_VERSION" != "$LOCAL_VERSION" ]; then
13
+ echo "[CIEL] Update available: v${LOCAL_VERSION} → v${REMOTE_VERSION}. Run /ciel-update to upgrade." >&2
14
+ fi
@@ -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
@@ -20,7 +20,7 @@
20
20
  "hooks": [
21
21
  {
22
22
  "type": "command",
23
- "command": "echo \"[CIEL v6] Session started — Pipeline: DOCS → QUOI → ASK → AVEC QUOI → DIVERGE → RECHERCHE → CODEBASE → EVALUER → ASK2 → FAIRE → RELIRE → PROUVER → MEMOIRE → META\" && \"$CLAUDE_PROJECT_DIR\"/hooks/session-version-check.sh"
23
+ "command": "echo \"[CIEL v6] Session started — Pipeline: DOCS → QUOI → ASK → AVEC QUOI → DIVERGE → RECHERCHE → CODEBASE → EVALUER → ASK2 → FAIRE → RELIRE → PROUVER → MEMOIRE → META\" && \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-version-check.sh"
24
24
  }
25
25
  ]
26
26
  }
@@ -35,7 +35,7 @@
35
35
  },
36
36
  {
37
37
  "type": "command",
38
- "command": "\"$CLAUDE_PROJECT_DIR\"/hooks/pre-tool-write.sh"
38
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-tool-write.sh"
39
39
  }
40
40
  ]
41
41
  },
@@ -54,7 +54,7 @@
54
54
  "hooks": [
55
55
  {
56
56
  "type": "command",
57
- "command": "\"$CLAUDE_PROJECT_DIR\"/hooks/pre-agent-gate.sh"
57
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-agent-gate.sh"
58
58
  }
59
59
  ]
60
60
  }
@@ -69,7 +69,7 @@
69
69
  },
70
70
  {
71
71
  "type": "command",
72
- "command": "COUNT=$(cat \"$CLAUDE_PROJECT_DIR/.ciel/tracked-files.json\" 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(len(d))\" 2>/dev/null || echo \"?\"); echo \"[CIEL] Tracked files: $COUNT — RELIRE recommended at 5+ files or on Critical paths\" >&2"
72
+ "command": "COUNT=$(cat \"${CLAUDE_PROJECT_DIR:-$PWD}/.ciel/tracked-files.json\" 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(len(d))\" 2>/dev/null || echo \"?\"); echo \"[CIEL] Tracked files: $COUNT — RELIRE recommended at 5+ files or on Critical paths\" >&2"
73
73
  }
74
74
  ]
75
75
  }
@@ -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 < 75, creates a GitHub Issue on the Ciel repository with the findings and session timeline. Hook-independent — works even when Ciel hooks are broken.
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 75, 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.*
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,79 @@ 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 which Ciel platforms are present and whether they have valid configurations:
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 /path/to/platforms/ 2>/dev/null
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
- Expected platforms: codex, cursor, kilocode, lmstudio, ollama, opencode, windsurf
104
+ **OpenCode** check for expected plugin, agent, and command files:
105
+ ```bash
106
+ # Plugin file may be ciel.ts (legacy) or ciel.js (v6+ runtime); accept either.
107
+ OPENCODE_PLUGIN=$( ( test -f .opencode/plugins/ciel.ts || test -f .opencode/plugins/ciel.js ) && echo "1" || echo "0")
108
+ OPENCODE_AGENTS=$(ls .opencode/agents/ciel-*.md 2>/dev/null | wc -l | tr -d ' ')
109
+ OPENCODE_COMMANDS=$(ls .opencode/commands/ciel*.md 2>/dev/null | wc -l | tr -d ' ')
110
+ echo "OpenCode: plugin=$OPENCODE_PLUGIN agents=$OPENCODE_AGENTS commands=$OPENCODE_COMMANDS"
111
+ if [ "$OPENCODE_PLUGIN" = "1" ] && [ "$OPENCODE_AGENTS" -ge 3 ] && [ "$OPENCODE_COMMANDS" -ge 5 ]; then
112
+ echo "OpenCode platform: OK"
113
+ else
114
+ echo "OpenCode platform: INCOMPLETE"
115
+ fi
116
+ ```
117
+ Expected: ciel plugin (ciel.ts or ciel.js) + at least 3 agent files + at least 5 command files
118
+
119
+ Scoring:
120
+ - Both platforms fully present and valid: **0**
121
+ - One platform missing or incomplete: **-3**
122
+ - Both platforms missing or critically incomplete: **-5**
123
+
124
+ **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.
125
+
126
+ #### Dimension 9: Memory health — penalty up to -10
95
127
 
96
- - All platforms present: **0**
97
- - Missing 1-2 platforms: **-3**
98
- - Missing 3+ platforms: **-5**
128
+ Check the cued-recall memory system (see `docs/adrs/0001-cued-recall-memory.md`):
129
+
130
+ - **index.json missing**: `.ciel/memory/index.json` does not exist. The memory system was never bootstrapped. **-10**
131
+ - **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**
132
+ - **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**
133
+ - **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**
134
+
135
+ Scoring:
136
+ - index.json missing: **-10** (blocks all other checks)
137
+ - index.json present but no episode files: **-5**
138
+ - All checks pass: **0**
139
+
140
+ Run these checks:
141
+ ```bash
142
+ # Check index.json exists
143
+ test -f .ciel/memory/index.json && echo "index: OK" || echo "index: MISSING"
144
+
145
+ # Count episodes
146
+ EPISODES=$(ls .ciel/memory/episodes/*.md 2>/dev/null | wc -l | tr -d ' ')
147
+ echo "episodes: $EPISODES"
148
+
149
+ # Count triggered vs total (requires python3)
150
+ python3 -c "
151
+ import json
152
+ with open('.ciel/memory/index.json') as f:
153
+ idx = json.load(f)
154
+ mems = idx.get('memories', {})
155
+ total = len(mems)
156
+ triggered = sum(1 for m in mems.values() if m.get('trigger_count', 0) > 0)
157
+ stale = sum(1 for m in mems.values() if m.get('stale'))
158
+ print(f'total: {total}, triggered: {triggered} ({0 if total==0 else triggered*100//total}%), stale: {stale}')
159
+ " 2>/dev/null || echo "memory check failed (no python3?)"
160
+ ```
99
161
 
100
162
  ---
101
163
 
@@ -106,9 +168,7 @@ Expected platforms: codex, cursor, kilocode, lmstudio, ollama, opencode, windsur
106
168
  | Score range | Status | Issue created? |
107
169
  |-------------|--------|----------------|
108
170
  | 90-100 | Excellent | No |
109
- | 75-89 | Good (minor issues) | No |
110
- | 50-74 | Needs improvement | **Yes** — creates issue with timeline |
111
- | 0-49 | Critical | **Yes** — creates issue with timeline |
171
+ | 0-89 | Needs improvement | **Yes** — creates issue with timeline |
112
172
 
113
173
  The score is calculated automatically from the detected violations.
114
174
 
@@ -124,7 +184,7 @@ Begin the output with the literal line `# Ciel Session Audit Report`. End with t
124
184
  **Date**: <today's date>
125
185
  **Ciel Health Score**: <N>/100 — <Excellent|Good|Needs improvement|Critical>
126
186
  **npm**: local v<X.Y.Z> | npm v<X.Y.Z> | <up-to-date|update available>
127
- **Platforms**: codexcursor ✓ kilo ✓ lmstudio ✓ ollama ✓ opencode ✓ windsurf ✓ (or ✗ if missing)
187
+ **Platforms**: claude ✓ opencode ✓ (or ✗ if missing)
128
188
  **Session summary**: <N> /ciel invocation(s), <N> total tool calls, <N> Task() dispatches, <N> inline Bash/Read/Grep/WebSearch calls in main session.
129
189
 
130
190
  **Verdict**: <PASS | VIOLATIONS FOUND>
@@ -173,6 +233,7 @@ Begin the output with the literal line `# Ciel Session Audit Report`. End with t
173
233
  | D6 — Intent routing | -<N> |
174
234
  | D7 — npm version | -<N> |
175
235
  | D8 — Platform health | -<N> |
236
+ | D9 — Memory health | -<N> |
176
237
  | **Total** | **-<N>** |
177
238
  | **Health Score** | **<N>/100** |
178
239
 
@@ -202,7 +263,7 @@ Output a single short section — **no issue is created** for PASS verdicts:
202
263
  **Date**: <today's date>
203
264
  **Ciel Health Score**: 100/100 — Excellent
204
265
  **npm**: local v<X.Y.Z> | npm v<X.Y.Z> | up-to-date
205
- **Platforms**: all 7 platforms present
266
+ **Platforms**: claude opencode
206
267
  **Session summary**: <N> /ciel invocation(s), <N> tool calls, <N> Task() dispatches.
207
268
  **Verdict**: PASS
208
269
 
@@ -213,11 +274,11 @@ Output a single short section — **no issue is created** for PASS verdicts:
213
274
 
214
275
  ---
215
276
 
216
- ### GitHub Issue creation (only if score < 75)
277
+ ### GitHub Issue creation (only if score < 90)
217
278
 
218
- If the Ciel Health Score is **below 75**, create a GitHub Issue with the report AND the session timeline.
279
+ If the Ciel Health Score is **below 90**, create a GitHub Issue with the report AND the session timeline.
219
280
 
220
- **Important**: Do NOT create an issue if score >= 75. Only create for scores < 75.
281
+ **Important**: Do NOT create an issue if score >= 90. Only create for scores < 90.
221
282
 
222
283
  1. **Check for duplicate issues first**:
223
284
  ```bash
@@ -299,6 +360,6 @@ If the Ciel Health Score is **below 75**, create a GitHub Issue with the report
299
360
  - Do NOT invoke other Ciel skills. This command is fully self-contained.
300
361
  - Do NOT dispatch `Task()` agents. Audit happens inline.
301
362
  - Do NOT ask clarifying questions. Produce the report with the information you have.
302
- - Do NOT create an issue if score >= 75. Only create for score < 75.
363
+ - Do NOT create an issue if score >= 90. Only create for score < 90.
303
364
  - Do NOT create duplicate issues — run the `gh issue list` check before creating.
304
365
  - Do NOT restart, rerun, or attempt to fix the session in-flight. The audit report is the deliverable.