continuous-improvement 3.8.0 → 3.9.2
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/.claude-plugin/marketplace.json +2 -14
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -21
- package/QUICKSTART.md +148 -101
- package/README.md +50 -18
- package/action.yml +33 -33
- package/bin/analyze.sh +161 -161
- package/bin/backfill.mjs +19 -19
- package/bin/check-doc-runtime-claims.mjs +120 -0
- package/bin/generate-plugin-manifests.mjs +2 -0
- package/bin/hook-stats.mjs +21 -21
- package/bin/install.mjs +51 -44
- package/bin/mcp-server.mjs +43 -40
- package/bin/refresh-third-party.mjs +416 -416
- package/commands/continuous-improvement.md +115 -115
- package/commands/dashboard.md +56 -56
- package/commands/discipline.md +51 -51
- package/commands/proceed-with-the-recommendation.md +62 -62
- package/commands/seven-laws.md +16 -16
- package/commands/superpowers.md +119 -92
- package/commands/workspace-surface-audit.md +77 -77
- package/hooks/gateguard.mjs +172 -0
- package/hooks/observe.sh +172 -172
- package/hooks/session.sh +106 -106
- package/hooks/three-section-close.mjs +181 -181
- package/instinct-packs/go.json +58 -58
- package/instinct-packs/meta.json +16 -16
- package/instinct-packs/python.json +58 -58
- package/instinct-packs/react.json +58 -58
- package/lib/gateguard-state.mjs +85 -0
- package/lib/plugin-metadata.mjs +25 -18
- package/llms.txt +68 -68
- package/package.json +3 -2
- package/plugins/beginner.json +1 -1
- package/plugins/continuous-improvement/.claude-plugin/marketplace.json +1 -1
- package/plugins/continuous-improvement/.claude-plugin/plugin.json +1 -1
- package/plugins/continuous-improvement/LICENSE +21 -21
- package/plugins/continuous-improvement/README.md +1 -0
- package/plugins/continuous-improvement/agents/README.md +120 -0
- package/plugins/continuous-improvement/agents/code-reviewer.md +97 -0
- package/plugins/continuous-improvement/agents/security-auditor.md +101 -0
- package/plugins/continuous-improvement/agents/test-engineer.md +95 -0
- package/plugins/continuous-improvement/bin/backfill.mjs +19 -19
- package/plugins/continuous-improvement/bin/mcp-server.mjs +43 -40
- package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -115
- package/plugins/continuous-improvement/commands/dashboard.md +56 -56
- package/plugins/continuous-improvement/commands/discipline.md +51 -51
- package/plugins/continuous-improvement/commands/proceed-with-the-recommendation.md +62 -62
- package/plugins/continuous-improvement/commands/seven-laws.md +16 -16
- package/plugins/continuous-improvement/commands/superpowers.md +119 -92
- package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -77
- package/plugins/continuous-improvement/hooks/gateguard.mjs +172 -0
- package/plugins/continuous-improvement/hooks/hooks.json +6 -1
- package/plugins/continuous-improvement/hooks/observe.sh +172 -172
- package/plugins/continuous-improvement/hooks/session.sh +106 -106
- package/plugins/continuous-improvement/hooks/three-section-close.mjs +181 -181
- package/plugins/continuous-improvement/instinct-packs/go.json +58 -58
- package/plugins/continuous-improvement/instinct-packs/meta.json +16 -16
- package/plugins/continuous-improvement/instinct-packs/python.json +58 -58
- package/plugins/continuous-improvement/instinct-packs/react.json +58 -58
- package/plugins/continuous-improvement/lib/plugin-metadata.mjs +25 -18
- package/plugins/continuous-improvement/skills/README.md +4 -1
- package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +47 -0
- package/plugins/continuous-improvement/skills/gateguard/SKILL.md +17 -9
- package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +108 -108
- package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +1 -8
- package/plugins/continuous-improvement/skills/ralph/SKILL.md +221 -221
- package/plugins/continuous-improvement/skills/recovery-classification/SKILL.md +73 -0
- package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +76 -76
- package/plugins/continuous-improvement/skills/state-reconciliation/SKILL.md +63 -0
- package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +104 -104
- package/plugins/continuous-improvement/skills/superpowers/SKILL.md +22 -15
- package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -411
- package/plugins/continuous-improvement/skills/token-budget-advisor/SKILL.md +136 -136
- package/plugins/continuous-improvement/skills/verification-loop/SKILL.md +32 -0
- package/plugins/continuous-improvement/skills/wild-risa-balance/SKILL.md +191 -191
- package/plugins/continuous-improvement/skills/worktree-safety/SKILL.md +66 -0
- package/plugins/expert.json +1 -1
- package/skills/README.md +79 -79
- package/skills/deploy-receipt.md +47 -0
- package/skills/gateguard.md +17 -9
- package/skills/para-memory-files.md +108 -108
- package/skills/proceed-with-the-recommendation.md +1 -8
- package/skills/ralph.md +221 -221
- package/skills/recovery-classification.md +73 -0
- package/skills/safety-guard.md +76 -76
- package/skills/state-reconciliation.md +63 -0
- package/skills/strategic-compact.md +104 -104
- package/skills/superpowers.md +22 -15
- package/skills/tdd-workflow.md +411 -411
- package/skills/token-budget-advisor.md +136 -136
- package/skills/verification-loop.md +32 -0
- package/skills/wild-risa-balance.md +191 -191
- package/skills/worktree-safety.md +66 -0
- package/templates/insights-claude-md.md +91 -0
- package/templates/verify-ladder.example.json +37 -24
package/hooks/observe.sh
CHANGED
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# observe.sh — Mulahazah PreToolUse/PostToolUse observation hook.
|
|
3
|
-
#
|
|
4
|
-
# Two-phase design:
|
|
5
|
-
# 1. PREFERRED: exec the Node observer (bin/observe.mjs) which captures the
|
|
6
|
-
# rich event schema (tool_input.command for Bash, file_path for Edit/
|
|
7
|
-
# Write/Read, tool_output) without depending on jq.
|
|
8
|
-
# 2. FALLBACK: if either node is missing OR the Node observer file is not
|
|
9
|
-
# installed alongside this script, fall through to the in-bash thin-
|
|
10
|
-
# schema path below. The fallback preserves prior behavior so an
|
|
11
|
-
# operator who never re-runs `npx continuous-improvement install` is
|
|
12
|
-
# no worse off than today.
|
|
13
|
-
#
|
|
14
|
-
# Always exits 0 — never blocks the Claude session.
|
|
15
|
-
# Usage: echo '<hook_json>' | observe.sh
|
|
16
|
-
|
|
17
|
-
trap 'exit 0' EXIT ERR INT TERM
|
|
18
|
-
|
|
19
|
-
# Phase 1 — try the Node observer. The shim copies bin/observe.mjs and
|
|
20
|
-
# lib/observe-event.mjs alongside this script under ${HOME}/.claude/instincts/
|
|
21
|
-
# preserving the bin/ + lib/ subdirectory layout so the relative import
|
|
22
|
-
# resolves: observe.mjs lives at .../instincts/bin/observe.mjs, the imported
|
|
23
|
-
# observe-event.mjs at .../instincts/lib/observe-event.mjs.
|
|
24
|
-
NODE_OBSERVER="$(dirname "${BASH_SOURCE[0]}")/bin/observe.mjs"
|
|
25
|
-
if [[ -f "$NODE_OBSERVER" ]] && command -v node &>/dev/null; then
|
|
26
|
-
exec node "$NODE_OBSERVER"
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
# Phase 2 — bash fallback (thin schema; preserved for back-compat).
|
|
30
|
-
INSTINCTS_DIR="${HOME}/.claude/instincts"
|
|
31
|
-
|
|
32
|
-
# ---------------------------------------------------------------------------
|
|
33
|
-
# Read stdin (hook payload) — single read for performance
|
|
34
|
-
# ---------------------------------------------------------------------------
|
|
35
|
-
INPUT="$(cat)"
|
|
36
|
-
[[ -z "$INPUT" ]] && exit 0
|
|
37
|
-
|
|
38
|
-
# ---------------------------------------------------------------------------
|
|
39
|
-
# Parse hook payload — use jq if available, otherwise basic extraction
|
|
40
|
-
#
|
|
41
|
-
# When jq is missing, observation rows lack tool_input/tool_output, which
|
|
42
|
-
# defeats the auto-instinct pipeline. Emit a one-shot stderr warning per
|
|
43
|
-
# project so the operator notices at the next session start instead of
|
|
44
|
-
# discovering the gap weeks later. The marker file makes it idempotent.
|
|
45
|
-
# ---------------------------------------------------------------------------
|
|
46
|
-
if command -v jq &>/dev/null; then
|
|
47
|
-
read -r TOOL_NAME SESSION_ID HAS_OUTPUT INPUT_JSON OUTPUT_JSON <<< "$(
|
|
48
|
-
printf '%s' "$INPUT" | jq -r '
|
|
49
|
-
(.tool_name // ""),
|
|
50
|
-
(.session_id // ""),
|
|
51
|
-
(if has("tool_output") then "yes" else "no" end),
|
|
52
|
-
((.tool_input // {} | tostring) | .[0:500]),
|
|
53
|
-
((.tool_output // {} | tostring) | .[0:200])
|
|
54
|
-
' | paste - - - - -
|
|
55
|
-
)"
|
|
56
|
-
else
|
|
57
|
-
# One-shot stderr warning per HOME so the operator learns the constraint.
|
|
58
|
-
# Marker lives next to instincts/ (not inside it) so it never collides with
|
|
59
|
-
# tests or analysis loops that iterate INSTINCTS_DIR for project-hash dirs.
|
|
60
|
-
JQ_WARN_MARKER="${HOME}/.claude/.continuous-improvement-jq-warned"
|
|
61
|
-
if [[ ! -f "$JQ_WARN_MARKER" ]]; then
|
|
62
|
-
mkdir -p "${HOME}/.claude" 2>/dev/null
|
|
63
|
-
printf '[continuous-improvement] jq not found on PATH — observe.sh is using thin-schema fallback. Install jq to enable auto-instinct detection (winget install jqlang.jq | brew install jq | apt install jq). This warning is one-shot per host.\n' >&2
|
|
64
|
-
: > "$JQ_WARN_MARKER"
|
|
65
|
-
fi
|
|
66
|
-
# Fallback: extract tool_name with basic pattern matching
|
|
67
|
-
TOOL_NAME="$(printf '%s' "$INPUT" | sed -n 's/.*"tool_name" *: *"\([^"]*\)".*/\1/p' | head -1)"
|
|
68
|
-
SESSION_ID="$(printf '%s' "$INPUT" | sed -n 's/.*"session_id" *: *"\([^"]*\)".*/\1/p' | head -1)"
|
|
69
|
-
HAS_OUTPUT="no"
|
|
70
|
-
printf '%s' "$INPUT" | grep -q '"tool_output"' && HAS_OUTPUT="yes"
|
|
71
|
-
INPUT_JSON="$(printf '%s' "$INPUT" | head -c 500)"
|
|
72
|
-
OUTPUT_JSON=""
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
[[ -z "$TOOL_NAME" ]] && exit 0
|
|
76
|
-
|
|
77
|
-
# Determine event type
|
|
78
|
-
if [[ "$HAS_OUTPUT" == "yes" ]]; then
|
|
79
|
-
EVENT="tool_complete"
|
|
80
|
-
else
|
|
81
|
-
EVENT="tool_start"
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# ---------------------------------------------------------------------------
|
|
85
|
-
# Project detection
|
|
86
|
-
# ---------------------------------------------------------------------------
|
|
87
|
-
PROJECT_ROOT=""
|
|
88
|
-
|
|
89
|
-
if [[ -n "${CLAUDE_PROJECT_DIR:-}" && -d "${CLAUDE_PROJECT_DIR}" ]]; then
|
|
90
|
-
PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
|
|
91
|
-
fi
|
|
92
|
-
|
|
93
|
-
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
94
|
-
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
98
|
-
PROJECT_ROOT="global"
|
|
99
|
-
fi
|
|
100
|
-
|
|
101
|
-
# ---------------------------------------------------------------------------
|
|
102
|
-
# Compute project hash and name
|
|
103
|
-
# ---------------------------------------------------------------------------
|
|
104
|
-
PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
|
|
105
|
-
PROJECT_NAME="$(basename "${PROJECT_ROOT%.git}")"
|
|
106
|
-
|
|
107
|
-
# ---------------------------------------------------------------------------
|
|
108
|
-
# Directory setup
|
|
109
|
-
# ---------------------------------------------------------------------------
|
|
110
|
-
PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
|
|
111
|
-
OBS_FILE="${PROJECT_DIR}/observations.jsonl"
|
|
112
|
-
|
|
113
|
-
[[ -d "$PROJECT_DIR" ]] || mkdir -p "$PROJECT_DIR"
|
|
114
|
-
|
|
115
|
-
# ---------------------------------------------------------------------------
|
|
116
|
-
# Rotate observations.jsonl at 10,000 lines
|
|
117
|
-
# ---------------------------------------------------------------------------
|
|
118
|
-
if [[ -f "$OBS_FILE" ]]; then
|
|
119
|
-
LINE_COUNT="$(wc -l < "$OBS_FILE")"
|
|
120
|
-
if (( LINE_COUNT >= 10000 )); then
|
|
121
|
-
mv "$OBS_FILE" "${PROJECT_DIR}/observations.$(date -u +"%Y-%m-%d-%H%M%S").jsonl"
|
|
122
|
-
|
|
123
|
-
# Clean up old archives - keep only 10 most recent
|
|
124
|
-
ls -t "${PROJECT_DIR}"/observations.*.jsonl 2>/dev/null | tail -n +11 | xargs -r rm -f
|
|
125
|
-
fi
|
|
126
|
-
fi
|
|
127
|
-
|
|
128
|
-
# ---------------------------------------------------------------------------
|
|
129
|
-
# Append JSONL observation line
|
|
130
|
-
# ---------------------------------------------------------------------------
|
|
131
|
-
TS="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
132
|
-
|
|
133
|
-
if command -v jq &>/dev/null; then
|
|
134
|
-
printf '%s\n' "$(jq -cn \
|
|
135
|
-
--arg ts "$TS" \
|
|
136
|
-
--arg event "$EVENT" \
|
|
137
|
-
--arg session "$SESSION_ID" \
|
|
138
|
-
--arg tool "$TOOL_NAME" \
|
|
139
|
-
--arg input_summary "$INPUT_JSON" \
|
|
140
|
-
--arg output_summary "$OUTPUT_JSON" \
|
|
141
|
-
--arg project_id "$PROJECT_HASH" \
|
|
142
|
-
--arg project_name "$PROJECT_NAME" \
|
|
143
|
-
'{ts:$ts,event:$event,session:$session,tool:$tool,input_summary:$input_summary,output_summary:$output_summary,project_id:$project_id,project_name:$project_name}')" \
|
|
144
|
-
>> "$OBS_FILE"
|
|
145
|
-
else
|
|
146
|
-
# Fallback: manual JSON construction
|
|
147
|
-
printf '{"ts":"%s","event":"%s","session":"%s","tool":"%s","project_id":"%s","project_name":"%s"}\n' \
|
|
148
|
-
"$TS" "$EVENT" "$SESSION_ID" "$TOOL_NAME" "$PROJECT_HASH" "$PROJECT_NAME" \
|
|
149
|
-
>> "$OBS_FILE"
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
# ---------------------------------------------------------------------------
|
|
153
|
-
# Write project.json if new project
|
|
154
|
-
# ---------------------------------------------------------------------------
|
|
155
|
-
PROJECT_JSON="${PROJECT_DIR}/project.json"
|
|
156
|
-
if [[ ! -f "$PROJECT_JSON" ]]; then
|
|
157
|
-
if command -v jq &>/dev/null; then
|
|
158
|
-
jq -n \
|
|
159
|
-
--arg id "$PROJECT_HASH" \
|
|
160
|
-
--arg name "$PROJECT_NAME" \
|
|
161
|
-
--arg root "$PROJECT_ROOT" \
|
|
162
|
-
--arg created_at "$TS" \
|
|
163
|
-
'{id:$id,name:$name,root:$root,created_at:$created_at}' \
|
|
164
|
-
> "$PROJECT_JSON"
|
|
165
|
-
else
|
|
166
|
-
printf '{"id":"%s","name":"%s","root":"%s","created_at":"%s"}\n' \
|
|
167
|
-
"$PROJECT_HASH" "$PROJECT_NAME" "$PROJECT_ROOT" "$TS" \
|
|
168
|
-
> "$PROJECT_JSON"
|
|
169
|
-
fi
|
|
170
|
-
fi
|
|
171
|
-
|
|
172
|
-
exit 0
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# observe.sh — Mulahazah PreToolUse/PostToolUse observation hook.
|
|
3
|
+
#
|
|
4
|
+
# Two-phase design:
|
|
5
|
+
# 1. PREFERRED: exec the Node observer (bin/observe.mjs) which captures the
|
|
6
|
+
# rich event schema (tool_input.command for Bash, file_path for Edit/
|
|
7
|
+
# Write/Read, tool_output) without depending on jq.
|
|
8
|
+
# 2. FALLBACK: if either node is missing OR the Node observer file is not
|
|
9
|
+
# installed alongside this script, fall through to the in-bash thin-
|
|
10
|
+
# schema path below. The fallback preserves prior behavior so an
|
|
11
|
+
# operator who never re-runs `npx continuous-improvement install` is
|
|
12
|
+
# no worse off than today.
|
|
13
|
+
#
|
|
14
|
+
# Always exits 0 — never blocks the Claude session.
|
|
15
|
+
# Usage: echo '<hook_json>' | observe.sh
|
|
16
|
+
|
|
17
|
+
trap 'exit 0' EXIT ERR INT TERM
|
|
18
|
+
|
|
19
|
+
# Phase 1 — try the Node observer. The shim copies bin/observe.mjs and
|
|
20
|
+
# lib/observe-event.mjs alongside this script under ${HOME}/.claude/instincts/
|
|
21
|
+
# preserving the bin/ + lib/ subdirectory layout so the relative import
|
|
22
|
+
# resolves: observe.mjs lives at .../instincts/bin/observe.mjs, the imported
|
|
23
|
+
# observe-event.mjs at .../instincts/lib/observe-event.mjs.
|
|
24
|
+
NODE_OBSERVER="$(dirname "${BASH_SOURCE[0]}")/bin/observe.mjs"
|
|
25
|
+
if [[ -f "$NODE_OBSERVER" ]] && command -v node &>/dev/null; then
|
|
26
|
+
exec node "$NODE_OBSERVER"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Phase 2 — bash fallback (thin schema; preserved for back-compat).
|
|
30
|
+
INSTINCTS_DIR="${HOME}/.claude/instincts"
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# Read stdin (hook payload) — single read for performance
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
INPUT="$(cat)"
|
|
36
|
+
[[ -z "$INPUT" ]] && exit 0
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Parse hook payload — use jq if available, otherwise basic extraction
|
|
40
|
+
#
|
|
41
|
+
# When jq is missing, observation rows lack tool_input/tool_output, which
|
|
42
|
+
# defeats the auto-instinct pipeline. Emit a one-shot stderr warning per
|
|
43
|
+
# project so the operator notices at the next session start instead of
|
|
44
|
+
# discovering the gap weeks later. The marker file makes it idempotent.
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
if command -v jq &>/dev/null; then
|
|
47
|
+
read -r TOOL_NAME SESSION_ID HAS_OUTPUT INPUT_JSON OUTPUT_JSON <<< "$(
|
|
48
|
+
printf '%s' "$INPUT" | jq -r '
|
|
49
|
+
(.tool_name // ""),
|
|
50
|
+
(.session_id // ""),
|
|
51
|
+
(if has("tool_output") then "yes" else "no" end),
|
|
52
|
+
((.tool_input // {} | tostring) | .[0:500]),
|
|
53
|
+
((.tool_output // {} | tostring) | .[0:200])
|
|
54
|
+
' | paste - - - - -
|
|
55
|
+
)"
|
|
56
|
+
else
|
|
57
|
+
# One-shot stderr warning per HOME so the operator learns the constraint.
|
|
58
|
+
# Marker lives next to instincts/ (not inside it) so it never collides with
|
|
59
|
+
# tests or analysis loops that iterate INSTINCTS_DIR for project-hash dirs.
|
|
60
|
+
JQ_WARN_MARKER="${HOME}/.claude/.continuous-improvement-jq-warned"
|
|
61
|
+
if [[ ! -f "$JQ_WARN_MARKER" ]]; then
|
|
62
|
+
mkdir -p "${HOME}/.claude" 2>/dev/null
|
|
63
|
+
printf '[continuous-improvement] jq not found on PATH — observe.sh is using thin-schema fallback. Install jq to enable auto-instinct detection (winget install jqlang.jq | brew install jq | apt install jq). This warning is one-shot per host.\n' >&2
|
|
64
|
+
: > "$JQ_WARN_MARKER"
|
|
65
|
+
fi
|
|
66
|
+
# Fallback: extract tool_name with basic pattern matching
|
|
67
|
+
TOOL_NAME="$(printf '%s' "$INPUT" | sed -n 's/.*"tool_name" *: *"\([^"]*\)".*/\1/p' | head -1)"
|
|
68
|
+
SESSION_ID="$(printf '%s' "$INPUT" | sed -n 's/.*"session_id" *: *"\([^"]*\)".*/\1/p' | head -1)"
|
|
69
|
+
HAS_OUTPUT="no"
|
|
70
|
+
printf '%s' "$INPUT" | grep -q '"tool_output"' && HAS_OUTPUT="yes"
|
|
71
|
+
INPUT_JSON="$(printf '%s' "$INPUT" | head -c 500)"
|
|
72
|
+
OUTPUT_JSON=""
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
[[ -z "$TOOL_NAME" ]] && exit 0
|
|
76
|
+
|
|
77
|
+
# Determine event type
|
|
78
|
+
if [[ "$HAS_OUTPUT" == "yes" ]]; then
|
|
79
|
+
EVENT="tool_complete"
|
|
80
|
+
else
|
|
81
|
+
EVENT="tool_start"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
# Project detection
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
PROJECT_ROOT=""
|
|
88
|
+
|
|
89
|
+
if [[ -n "${CLAUDE_PROJECT_DIR:-}" && -d "${CLAUDE_PROJECT_DIR}" ]]; then
|
|
90
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
94
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
98
|
+
PROJECT_ROOT="global"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
# Compute project hash and name
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
|
|
105
|
+
PROJECT_NAME="$(basename "${PROJECT_ROOT%.git}")"
|
|
106
|
+
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
# Directory setup
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
|
|
111
|
+
OBS_FILE="${PROJECT_DIR}/observations.jsonl"
|
|
112
|
+
|
|
113
|
+
[[ -d "$PROJECT_DIR" ]] || mkdir -p "$PROJECT_DIR"
|
|
114
|
+
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# Rotate observations.jsonl at 10,000 lines
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
if [[ -f "$OBS_FILE" ]]; then
|
|
119
|
+
LINE_COUNT="$(wc -l < "$OBS_FILE")"
|
|
120
|
+
if (( LINE_COUNT >= 10000 )); then
|
|
121
|
+
mv "$OBS_FILE" "${PROJECT_DIR}/observations.$(date -u +"%Y-%m-%d-%H%M%S").jsonl"
|
|
122
|
+
|
|
123
|
+
# Clean up old archives - keep only 10 most recent
|
|
124
|
+
ls -t "${PROJECT_DIR}"/observations.*.jsonl 2>/dev/null | tail -n +11 | xargs -r rm -f
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
# Append JSONL observation line
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
TS="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
132
|
+
|
|
133
|
+
if command -v jq &>/dev/null; then
|
|
134
|
+
printf '%s\n' "$(jq -cn \
|
|
135
|
+
--arg ts "$TS" \
|
|
136
|
+
--arg event "$EVENT" \
|
|
137
|
+
--arg session "$SESSION_ID" \
|
|
138
|
+
--arg tool "$TOOL_NAME" \
|
|
139
|
+
--arg input_summary "$INPUT_JSON" \
|
|
140
|
+
--arg output_summary "$OUTPUT_JSON" \
|
|
141
|
+
--arg project_id "$PROJECT_HASH" \
|
|
142
|
+
--arg project_name "$PROJECT_NAME" \
|
|
143
|
+
'{ts:$ts,event:$event,session:$session,tool:$tool,input_summary:$input_summary,output_summary:$output_summary,project_id:$project_id,project_name:$project_name}')" \
|
|
144
|
+
>> "$OBS_FILE"
|
|
145
|
+
else
|
|
146
|
+
# Fallback: manual JSON construction
|
|
147
|
+
printf '{"ts":"%s","event":"%s","session":"%s","tool":"%s","project_id":"%s","project_name":"%s"}\n' \
|
|
148
|
+
"$TS" "$EVENT" "$SESSION_ID" "$TOOL_NAME" "$PROJECT_HASH" "$PROJECT_NAME" \
|
|
149
|
+
>> "$OBS_FILE"
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# ---------------------------------------------------------------------------
|
|
153
|
+
# Write project.json if new project
|
|
154
|
+
# ---------------------------------------------------------------------------
|
|
155
|
+
PROJECT_JSON="${PROJECT_DIR}/project.json"
|
|
156
|
+
if [[ ! -f "$PROJECT_JSON" ]]; then
|
|
157
|
+
if command -v jq &>/dev/null; then
|
|
158
|
+
jq -n \
|
|
159
|
+
--arg id "$PROJECT_HASH" \
|
|
160
|
+
--arg name "$PROJECT_NAME" \
|
|
161
|
+
--arg root "$PROJECT_ROOT" \
|
|
162
|
+
--arg created_at "$TS" \
|
|
163
|
+
'{id:$id,name:$name,root:$root,created_at:$created_at}' \
|
|
164
|
+
> "$PROJECT_JSON"
|
|
165
|
+
else
|
|
166
|
+
printf '{"id":"%s","name":"%s","root":"%s","created_at":"%s"}\n' \
|
|
167
|
+
"$PROJECT_HASH" "$PROJECT_NAME" "$PROJECT_ROOT" "$TS" \
|
|
168
|
+
> "$PROJECT_JSON"
|
|
169
|
+
fi
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
exit 0
|
package/hooks/session.sh
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# session.sh — SessionStart/SessionEnd hook for continuous-improvement
|
|
3
|
-
# SessionStart: loads instincts and prints status
|
|
4
|
-
# SessionEnd: prompts reflection
|
|
5
|
-
# Always exits 0 — never blocks the session
|
|
6
|
-
trap 'exit 0' EXIT ERR INT TERM
|
|
7
|
-
|
|
8
|
-
INSTINCTS_DIR="${HOME}/.claude/instincts"
|
|
9
|
-
|
|
10
|
-
# Read stdin
|
|
11
|
-
INPUT="$(cat)"
|
|
12
|
-
[[ -z "$INPUT" ]] && exit 0
|
|
13
|
-
|
|
14
|
-
# ---------------------------------------------------------------------------
|
|
15
|
-
# Detect event type from hook context
|
|
16
|
-
# ---------------------------------------------------------------------------
|
|
17
|
-
# SessionStart hooks receive no tool_name, SessionEnd hooks may vary.
|
|
18
|
-
# We detect based on the hook_type field or the calling context.
|
|
19
|
-
EVENT_TYPE=""
|
|
20
|
-
if command -v jq &>/dev/null; then
|
|
21
|
-
EVENT_TYPE="$(printf '%s' "$INPUT" | jq -r '.hook_type // .event_type // "unknown"' 2>/dev/null)"
|
|
22
|
-
else
|
|
23
|
-
if printf '%s' "$INPUT" | grep -q '"SessionStart"'; then
|
|
24
|
-
EVENT_TYPE="SessionStart"
|
|
25
|
-
elif printf '%s' "$INPUT" | grep -q '"SessionEnd"'; then
|
|
26
|
-
EVENT_TYPE="SessionEnd"
|
|
27
|
-
fi
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
# ---------------------------------------------------------------------------
|
|
31
|
-
# Project detection (same as observe.sh)
|
|
32
|
-
# ---------------------------------------------------------------------------
|
|
33
|
-
PROJECT_ROOT=""
|
|
34
|
-
if [[ -n "${CLAUDE_PROJECT_DIR:-}" && -d "${CLAUDE_PROJECT_DIR}" ]]; then
|
|
35
|
-
PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
|
|
36
|
-
fi
|
|
37
|
-
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
38
|
-
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
39
|
-
fi
|
|
40
|
-
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
41
|
-
PROJECT_ROOT="global"
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
|
|
45
|
-
PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
|
|
46
|
-
|
|
47
|
-
# ---------------------------------------------------------------------------
|
|
48
|
-
# SessionStart: count observations and instincts, print brief status
|
|
49
|
-
# ---------------------------------------------------------------------------
|
|
50
|
-
if [[ "$EVENT_TYPE" == "SessionStart" || "$EVENT_TYPE" == "unknown" ]]; then
|
|
51
|
-
OBS_COUNT=0
|
|
52
|
-
INSTINCT_COUNT=0
|
|
53
|
-
|
|
54
|
-
OBS_FILE="${PROJECT_DIR}/observations.jsonl"
|
|
55
|
-
if [[ -f "$OBS_FILE" ]]; then
|
|
56
|
-
OBS_COUNT="$(wc -l < "$OBS_FILE" 2>/dev/null || echo 0)"
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
# Count yaml files in project + global
|
|
60
|
-
for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
|
|
61
|
-
if [[ -d "$dir" ]]; then
|
|
62
|
-
count="$(find "$dir" -maxdepth 1 -name '*.yaml' 2>/dev/null | wc -l)"
|
|
63
|
-
INSTINCT_COUNT=$((INSTINCT_COUNT + count))
|
|
64
|
-
fi
|
|
65
|
-
done
|
|
66
|
-
|
|
67
|
-
# Determine level
|
|
68
|
-
LEVEL="CAPTURE"
|
|
69
|
-
if (( OBS_COUNT >= 20 )) || (( INSTINCT_COUNT > 0 )); then
|
|
70
|
-
LEVEL="ANALYZE"
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
# Check for high-confidence instincts
|
|
74
|
-
if (( INSTINCT_COUNT > 0 )); then
|
|
75
|
-
for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
|
|
76
|
-
if [[ -d "$dir" ]]; then
|
|
77
|
-
for f in "$dir"/*.yaml; do
|
|
78
|
-
[[ -f "$f" ]] || continue
|
|
79
|
-
conf="$(grep '^confidence:' "$f" 2>/dev/null | head -1 | sed 's/confidence: *//')"
|
|
80
|
-
if [[ -n "$conf" ]]; then
|
|
81
|
-
# Compare as integer (multiply by 100) using awk for portability
|
|
82
|
-
int_conf="$(printf '%s' "$conf" | awk '{printf "%.0f", $1 * 100}')"
|
|
83
|
-
if [[ -n "$int_conf" ]] && (( int_conf >= 70 )); then
|
|
84
|
-
LEVEL="AUTO-APPLY"
|
|
85
|
-
break 2
|
|
86
|
-
elif (( int_conf >= 50 )); then
|
|
87
|
-
LEVEL="SUGGEST"
|
|
88
|
-
fi
|
|
89
|
-
fi
|
|
90
|
-
done
|
|
91
|
-
fi
|
|
92
|
-
done
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
# Write status to stderr (visible in hook output, not blocking)
|
|
96
|
-
echo "[continuous-improvement] Level: ${LEVEL} | Observations: ${OBS_COUNT} | Instincts: ${INSTINCT_COUNT}" >&2
|
|
97
|
-
fi
|
|
98
|
-
|
|
99
|
-
# ---------------------------------------------------------------------------
|
|
100
|
-
# SessionEnd: remind to reflect
|
|
101
|
-
# ---------------------------------------------------------------------------
|
|
102
|
-
if [[ "$EVENT_TYPE" == "SessionEnd" ]]; then
|
|
103
|
-
echo "[continuous-improvement] Session ending. Run /continuous-improvement to reflect and capture learnings." >&2
|
|
104
|
-
fi
|
|
105
|
-
|
|
106
|
-
exit 0
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# session.sh — SessionStart/SessionEnd hook for continuous-improvement
|
|
3
|
+
# SessionStart: loads instincts and prints status
|
|
4
|
+
# SessionEnd: prompts reflection
|
|
5
|
+
# Always exits 0 — never blocks the session
|
|
6
|
+
trap 'exit 0' EXIT ERR INT TERM
|
|
7
|
+
|
|
8
|
+
INSTINCTS_DIR="${HOME}/.claude/instincts"
|
|
9
|
+
|
|
10
|
+
# Read stdin
|
|
11
|
+
INPUT="$(cat)"
|
|
12
|
+
[[ -z "$INPUT" ]] && exit 0
|
|
13
|
+
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
# Detect event type from hook context
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
# SessionStart hooks receive no tool_name, SessionEnd hooks may vary.
|
|
18
|
+
# We detect based on the hook_type field or the calling context.
|
|
19
|
+
EVENT_TYPE=""
|
|
20
|
+
if command -v jq &>/dev/null; then
|
|
21
|
+
EVENT_TYPE="$(printf '%s' "$INPUT" | jq -r '.hook_type // .event_type // "unknown"' 2>/dev/null)"
|
|
22
|
+
else
|
|
23
|
+
if printf '%s' "$INPUT" | grep -q '"SessionStart"'; then
|
|
24
|
+
EVENT_TYPE="SessionStart"
|
|
25
|
+
elif printf '%s' "$INPUT" | grep -q '"SessionEnd"'; then
|
|
26
|
+
EVENT_TYPE="SessionEnd"
|
|
27
|
+
fi
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Project detection (same as observe.sh)
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
PROJECT_ROOT=""
|
|
34
|
+
if [[ -n "${CLAUDE_PROJECT_DIR:-}" && -d "${CLAUDE_PROJECT_DIR}" ]]; then
|
|
35
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
|
|
36
|
+
fi
|
|
37
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
38
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
39
|
+
fi
|
|
40
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
41
|
+
PROJECT_ROOT="global"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
|
|
45
|
+
PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# SessionStart: count observations and instincts, print brief status
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
if [[ "$EVENT_TYPE" == "SessionStart" || "$EVENT_TYPE" == "unknown" ]]; then
|
|
51
|
+
OBS_COUNT=0
|
|
52
|
+
INSTINCT_COUNT=0
|
|
53
|
+
|
|
54
|
+
OBS_FILE="${PROJECT_DIR}/observations.jsonl"
|
|
55
|
+
if [[ -f "$OBS_FILE" ]]; then
|
|
56
|
+
OBS_COUNT="$(wc -l < "$OBS_FILE" 2>/dev/null || echo 0)"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Count yaml files in project + global
|
|
60
|
+
for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
|
|
61
|
+
if [[ -d "$dir" ]]; then
|
|
62
|
+
count="$(find "$dir" -maxdepth 1 -name '*.yaml' 2>/dev/null | wc -l)"
|
|
63
|
+
INSTINCT_COUNT=$((INSTINCT_COUNT + count))
|
|
64
|
+
fi
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
# Determine level
|
|
68
|
+
LEVEL="CAPTURE"
|
|
69
|
+
if (( OBS_COUNT >= 20 )) || (( INSTINCT_COUNT > 0 )); then
|
|
70
|
+
LEVEL="ANALYZE"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Check for high-confidence instincts
|
|
74
|
+
if (( INSTINCT_COUNT > 0 )); then
|
|
75
|
+
for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
|
|
76
|
+
if [[ -d "$dir" ]]; then
|
|
77
|
+
for f in "$dir"/*.yaml; do
|
|
78
|
+
[[ -f "$f" ]] || continue
|
|
79
|
+
conf="$(grep '^confidence:' "$f" 2>/dev/null | head -1 | sed 's/confidence: *//')"
|
|
80
|
+
if [[ -n "$conf" ]]; then
|
|
81
|
+
# Compare as integer (multiply by 100) using awk for portability
|
|
82
|
+
int_conf="$(printf '%s' "$conf" | awk '{printf "%.0f", $1 * 100}')"
|
|
83
|
+
if [[ -n "$int_conf" ]] && (( int_conf >= 70 )); then
|
|
84
|
+
LEVEL="AUTO-APPLY"
|
|
85
|
+
break 2
|
|
86
|
+
elif (( int_conf >= 50 )); then
|
|
87
|
+
LEVEL="SUGGEST"
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
done
|
|
91
|
+
fi
|
|
92
|
+
done
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Write status to stderr (visible in hook output, not blocking)
|
|
96
|
+
echo "[continuous-improvement] Level: ${LEVEL} | Observations: ${OBS_COUNT} | Instincts: ${INSTINCT_COUNT}" >&2
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
# SessionEnd: remind to reflect
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
if [[ "$EVENT_TYPE" == "SessionEnd" ]]; then
|
|
103
|
+
echo "[continuous-improvement] Session ending. Run /continuous-improvement to reflect and capture learnings." >&2
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
exit 0
|