continuous-improvement 3.8.0 → 3.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.
Files changed (96) hide show
  1. package/.claude-plugin/marketplace.json +2 -14
  2. package/CHANGELOG.md +25 -0
  3. package/LICENSE +21 -21
  4. package/QUICKSTART.md +148 -101
  5. package/README.md +50 -18
  6. package/action.yml +33 -33
  7. package/bin/analyze.sh +161 -161
  8. package/bin/backfill.mjs +19 -19
  9. package/bin/check-doc-runtime-claims.mjs +120 -0
  10. package/bin/generate-plugin-manifests.mjs +2 -0
  11. package/bin/hook-stats.mjs +21 -21
  12. package/bin/install.mjs +51 -44
  13. package/bin/mcp-server.mjs +43 -40
  14. package/bin/refresh-third-party.mjs +416 -416
  15. package/commands/continuous-improvement.md +115 -115
  16. package/commands/dashboard.md +56 -56
  17. package/commands/discipline.md +51 -51
  18. package/commands/proceed-with-the-recommendation.md +62 -62
  19. package/commands/seven-laws.md +16 -16
  20. package/commands/superpowers.md +119 -92
  21. package/commands/workspace-surface-audit.md +77 -77
  22. package/hooks/gateguard.mjs +172 -0
  23. package/hooks/observe.sh +172 -172
  24. package/hooks/session.sh +106 -106
  25. package/hooks/three-section-close.mjs +181 -181
  26. package/instinct-packs/go.json +58 -58
  27. package/instinct-packs/meta.json +16 -16
  28. package/instinct-packs/python.json +58 -58
  29. package/instinct-packs/react.json +58 -58
  30. package/lib/gateguard-state.mjs +85 -0
  31. package/lib/plugin-metadata.mjs +21 -18
  32. package/llms.txt +68 -68
  33. package/package.json +3 -2
  34. package/plugins/beginner.json +1 -1
  35. package/plugins/continuous-improvement/.claude-plugin/marketplace.json +1 -1
  36. package/plugins/continuous-improvement/.claude-plugin/plugin.json +1 -1
  37. package/plugins/continuous-improvement/LICENSE +21 -21
  38. package/plugins/continuous-improvement/README.md +1 -0
  39. package/plugins/continuous-improvement/agents/README.md +120 -0
  40. package/plugins/continuous-improvement/agents/code-reviewer.md +97 -0
  41. package/plugins/continuous-improvement/agents/security-auditor.md +101 -0
  42. package/plugins/continuous-improvement/agents/test-engineer.md +95 -0
  43. package/plugins/continuous-improvement/bin/backfill.mjs +19 -19
  44. package/plugins/continuous-improvement/bin/mcp-server.mjs +43 -40
  45. package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -115
  46. package/plugins/continuous-improvement/commands/dashboard.md +56 -56
  47. package/plugins/continuous-improvement/commands/discipline.md +51 -51
  48. package/plugins/continuous-improvement/commands/proceed-with-the-recommendation.md +62 -62
  49. package/plugins/continuous-improvement/commands/seven-laws.md +16 -16
  50. package/plugins/continuous-improvement/commands/superpowers.md +119 -92
  51. package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -77
  52. package/plugins/continuous-improvement/hooks/gateguard.mjs +172 -0
  53. package/plugins/continuous-improvement/hooks/hooks.json +6 -1
  54. package/plugins/continuous-improvement/hooks/observe.sh +172 -172
  55. package/plugins/continuous-improvement/hooks/session.sh +106 -106
  56. package/plugins/continuous-improvement/hooks/three-section-close.mjs +181 -181
  57. package/plugins/continuous-improvement/instinct-packs/go.json +58 -58
  58. package/plugins/continuous-improvement/instinct-packs/meta.json +16 -16
  59. package/plugins/continuous-improvement/instinct-packs/python.json +58 -58
  60. package/plugins/continuous-improvement/instinct-packs/react.json +58 -58
  61. package/plugins/continuous-improvement/lib/plugin-metadata.mjs +21 -18
  62. package/plugins/continuous-improvement/skills/README.md +4 -1
  63. package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +47 -0
  64. package/plugins/continuous-improvement/skills/gateguard/SKILL.md +17 -9
  65. package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +108 -108
  66. package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +1 -8
  67. package/plugins/continuous-improvement/skills/ralph/SKILL.md +221 -221
  68. package/plugins/continuous-improvement/skills/recovery-classification/SKILL.md +73 -0
  69. package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +76 -76
  70. package/plugins/continuous-improvement/skills/state-reconciliation/SKILL.md +63 -0
  71. package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +104 -104
  72. package/plugins/continuous-improvement/skills/superpowers/SKILL.md +22 -15
  73. package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -411
  74. package/plugins/continuous-improvement/skills/token-budget-advisor/SKILL.md +136 -136
  75. package/plugins/continuous-improvement/skills/verification-loop/SKILL.md +32 -0
  76. package/plugins/continuous-improvement/skills/wild-risa-balance/SKILL.md +191 -191
  77. package/plugins/continuous-improvement/skills/worktree-safety/SKILL.md +66 -0
  78. package/plugins/expert.json +1 -1
  79. package/skills/README.md +79 -79
  80. package/skills/deploy-receipt.md +47 -0
  81. package/skills/gateguard.md +17 -9
  82. package/skills/para-memory-files.md +108 -108
  83. package/skills/proceed-with-the-recommendation.md +1 -8
  84. package/skills/ralph.md +221 -221
  85. package/skills/recovery-classification.md +73 -0
  86. package/skills/safety-guard.md +76 -76
  87. package/skills/state-reconciliation.md +63 -0
  88. package/skills/strategic-compact.md +104 -104
  89. package/skills/superpowers.md +22 -15
  90. package/skills/tdd-workflow.md +411 -411
  91. package/skills/token-budget-advisor.md +136 -136
  92. package/skills/verification-loop.md +32 -0
  93. package/skills/wild-risa-balance.md +191 -191
  94. package/skills/worktree-safety.md +66 -0
  95. package/templates/insights-claude-md.md +91 -0
  96. 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