continuous-improvement 3.9.2 → 3.12.3

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 (160) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/CHANGELOG.md +95 -0
  3. package/LICENSE +21 -21
  4. package/QUICKSTART.md +13 -8
  5. package/README.md +94 -162
  6. package/SKILL.md +1 -1
  7. package/bin/analyze.sh +161 -161
  8. package/bin/backfill.mjs +19 -19
  9. package/bin/check-docs-substrings.mjs +73 -0
  10. package/bin/check-scripts-citation-drift.mjs +210 -0
  11. package/bin/check-skill-count.mjs +110 -0
  12. package/bin/check-test-imports-only.mjs +126 -0
  13. package/bin/check-tool-count.mjs +129 -0
  14. package/bin/companion-preference-status.mjs +231 -0
  15. package/bin/gateguard-clear.mjs +53 -0
  16. package/bin/generate-plugin-manifests.mjs +8 -1
  17. package/bin/harvest-friction.mjs +1 -1
  18. package/bin/hook-stats.mjs +21 -21
  19. package/bin/install.mjs +185 -28
  20. package/bin/mcp-server.mjs +216 -3
  21. package/bin/refresh-third-party.mjs +315 -313
  22. package/commands/audit.md +34 -0
  23. package/commands/companion-preference.md +58 -0
  24. package/commands/continuous-improvement.md +115 -115
  25. package/commands/dashboard.md +56 -56
  26. package/commands/discipline.md +14 -0
  27. package/commands/distill.md +39 -0
  28. package/commands/goal-check.md +53 -0
  29. package/commands/grill-me.md +40 -0
  30. package/commands/grill-with-docs.md +38 -0
  31. package/commands/handoff.md +42 -0
  32. package/commands/harvest.md +1 -1
  33. package/commands/proceed-with-the-recommendation.md +20 -0
  34. package/commands/recall.md +49 -0
  35. package/commands/reconcile.md +47 -0
  36. package/commands/seven-laws.md +16 -16
  37. package/commands/superpowers.md +3 -3
  38. package/commands/verify-install.md +55 -0
  39. package/commands/workspace-surface-audit.md +77 -77
  40. package/hooks/companion-preference.mjs +190 -0
  41. package/hooks/gateguard.mjs +72 -25
  42. package/hooks/goal-drift-stop.mjs +183 -0
  43. package/hooks/observe.sh +15 -1
  44. package/hooks/recall-briefing.mjs +167 -0
  45. package/hooks/route-prompt.mjs +180 -0
  46. package/hooks/route-table.json +35 -0
  47. package/hooks/session.sh +106 -106
  48. package/hooks/three-section-close.mjs +134 -132
  49. package/instinct-packs/go.json +58 -58
  50. package/instinct-packs/meta.json +16 -16
  51. package/instinct-packs/python.json +58 -58
  52. package/instinct-packs/react.json +58 -58
  53. package/lib/gateguard-state.mjs +54 -2
  54. package/lib/goal-drift-gate.mjs +50 -0
  55. package/lib/goal-state.mjs +285 -0
  56. package/lib/plugin-metadata.mjs +134 -15
  57. package/lib/recall-briefing.mjs +57 -0
  58. package/lib/recall-index.mjs +175 -0
  59. package/lib/skill-distill.mjs +222 -0
  60. package/llms.txt +2 -2
  61. package/package.json +12 -7
  62. package/plugins/beginner.json +9 -4
  63. package/plugins/continuous-improvement/.claude-plugin/marketplace.json +2 -2
  64. package/plugins/continuous-improvement/.claude-plugin/plugin.json +2 -2
  65. package/plugins/continuous-improvement/LICENSE +21 -21
  66. package/plugins/continuous-improvement/agents/README.md +3 -3
  67. package/plugins/continuous-improvement/bin/backfill.mjs +19 -19
  68. package/plugins/continuous-improvement/bin/gateguard-clear.mjs +53 -0
  69. package/plugins/continuous-improvement/bin/mcp-server.mjs +216 -3
  70. package/plugins/continuous-improvement/commands/audit.md +34 -0
  71. package/plugins/continuous-improvement/commands/companion-preference.md +58 -0
  72. package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -115
  73. package/plugins/continuous-improvement/commands/dashboard.md +56 -56
  74. package/plugins/continuous-improvement/commands/discipline.md +14 -0
  75. package/plugins/continuous-improvement/commands/distill.md +39 -0
  76. package/plugins/continuous-improvement/commands/goal-check.md +53 -0
  77. package/plugins/continuous-improvement/commands/grill-me.md +40 -0
  78. package/plugins/continuous-improvement/commands/grill-with-docs.md +38 -0
  79. package/plugins/continuous-improvement/commands/handoff.md +42 -0
  80. package/plugins/continuous-improvement/commands/harvest.md +1 -1
  81. package/plugins/continuous-improvement/commands/proceed-with-the-recommendation.md +20 -0
  82. package/plugins/continuous-improvement/commands/recall.md +49 -0
  83. package/plugins/continuous-improvement/commands/reconcile.md +47 -0
  84. package/plugins/continuous-improvement/commands/seven-laws.md +16 -16
  85. package/plugins/continuous-improvement/commands/superpowers.md +3 -3
  86. package/plugins/continuous-improvement/commands/verify-install.md +55 -0
  87. package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -77
  88. package/plugins/continuous-improvement/hooks/companion-preference.mjs +190 -0
  89. package/plugins/continuous-improvement/hooks/gateguard.mjs +72 -25
  90. package/plugins/continuous-improvement/hooks/goal-drift-stop.mjs +183 -0
  91. package/plugins/continuous-improvement/hooks/hooks.json +23 -2
  92. package/plugins/continuous-improvement/hooks/observe.sh +15 -1
  93. package/plugins/continuous-improvement/hooks/recall-briefing.mjs +167 -0
  94. package/plugins/continuous-improvement/hooks/route-prompt.mjs +180 -0
  95. package/plugins/continuous-improvement/hooks/route-table.json +35 -0
  96. package/plugins/continuous-improvement/hooks/session.sh +106 -106
  97. package/plugins/continuous-improvement/hooks/three-section-close.mjs +134 -132
  98. package/plugins/continuous-improvement/instinct-packs/go.json +58 -58
  99. package/plugins/continuous-improvement/instinct-packs/meta.json +16 -16
  100. package/plugins/continuous-improvement/instinct-packs/python.json +58 -58
  101. package/plugins/continuous-improvement/instinct-packs/react.json +58 -58
  102. package/plugins/continuous-improvement/lib/gateguard-state.mjs +137 -0
  103. package/plugins/continuous-improvement/lib/goal-drift-gate.mjs +50 -0
  104. package/plugins/continuous-improvement/lib/goal-state.mjs +285 -0
  105. package/plugins/continuous-improvement/lib/plugin-metadata.mjs +134 -15
  106. package/plugins/continuous-improvement/lib/recall-briefing.mjs +57 -0
  107. package/plugins/continuous-improvement/lib/recall-index.mjs +175 -0
  108. package/plugins/continuous-improvement/lib/skill-distill.mjs +222 -0
  109. package/plugins/continuous-improvement/skills/README.md +8 -0
  110. package/plugins/continuous-improvement/skills/audit/SKILL.md +73 -0
  111. package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +1 -1
  112. package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +13 -17
  113. package/plugins/continuous-improvement/skills/gateguard/SKILL.md +36 -17
  114. package/plugins/continuous-improvement/skills/goal-monitor/SKILL.md +81 -0
  115. package/plugins/continuous-improvement/skills/grill-me/SKILL.md +66 -0
  116. package/plugins/continuous-improvement/skills/grill-with-docs/SKILL.md +252 -0
  117. package/plugins/continuous-improvement/skills/handoff/SKILL.md +59 -0
  118. package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +1 -1
  119. package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +82 -6
  120. package/plugins/continuous-improvement/skills/ralph/SKILL.md +1 -0
  121. package/plugins/continuous-improvement/skills/recall/SKILL.md +50 -0
  122. package/plugins/continuous-improvement/skills/reconcile/SKILL.md +80 -0
  123. package/plugins/continuous-improvement/skills/recovery-classification/SKILL.md +1 -0
  124. package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +1 -0
  125. package/plugins/continuous-improvement/skills/skill-distillation/SKILL.md +57 -0
  126. package/plugins/continuous-improvement/skills/state-reconciliation/SKILL.md +1 -0
  127. package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +1 -0
  128. package/plugins/continuous-improvement/skills/superpowers/SKILL.md +77 -3
  129. package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -411
  130. package/plugins/continuous-improvement/skills/token-budget-advisor/SKILL.md +1 -1
  131. package/plugins/continuous-improvement/skills/verification-loop/SKILL.md +28 -20
  132. package/plugins/continuous-improvement/skills/wild-risa-balance/SKILL.md +1 -0
  133. package/plugins/continuous-improvement/skills/workspace-surface-audit/SKILL.md +1 -1
  134. package/plugins/continuous-improvement/skills/worktree-safety/SKILL.md +3 -2
  135. package/plugins/expert.json +28 -3
  136. package/skills/README.md +3 -0
  137. package/skills/audit.md +73 -0
  138. package/skills/deploy-receipt.md +13 -17
  139. package/skills/gateguard.md +36 -17
  140. package/skills/goal-monitor.md +81 -0
  141. package/skills/grill-me.md +66 -0
  142. package/skills/grill-with-docs.md +252 -0
  143. package/skills/handoff.md +59 -0
  144. package/skills/para-memory-files.md +1 -1
  145. package/skills/proceed-with-the-recommendation.md +82 -6
  146. package/skills/ralph.md +1 -0
  147. package/skills/recall.md +50 -0
  148. package/skills/reconcile.md +80 -0
  149. package/skills/recovery-classification.md +1 -0
  150. package/skills/safety-guard.md +1 -0
  151. package/skills/skill-distillation.md +57 -0
  152. package/skills/state-reconciliation.md +1 -0
  153. package/skills/strategic-compact.md +1 -0
  154. package/skills/superpowers.md +77 -3
  155. package/skills/tdd-workflow.md +411 -411
  156. package/skills/token-budget-advisor.md +1 -1
  157. package/skills/verification-loop.md +28 -20
  158. package/skills/wild-risa-balance.md +1 -0
  159. package/skills/workspace-surface-audit.md +1 -1
  160. package/skills/worktree-safety.md +3 -2
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Runtime UserPromptSubmit prompt-router hook.
4
+ *
5
+ * Reads the user prompt, matches it against patterns in
6
+ * hooks/route-table.json, and emits a single `<system-reminder>` recommending
7
+ * the matched skill. When no row matches, the hook emits nothing — the prompt
8
+ * passes through untouched.
9
+ *
10
+ * Stdin : JSON { prompt, session_id?, transcript_path?, cwd? }
11
+ * Stdout : JSON { hookSpecificOutput: { hookEventName: "UserPromptSubmit",
12
+ * additionalContext: string } }
13
+ * OR empty when there is no match.
14
+ * Exit : 0 always — routing decisions live in stdout, never in exit code.
15
+ *
16
+ * Fail-open: any unexpected error reading stdin / parsing the route table /
17
+ * compiling a pattern → emit nothing and exit 0. The hook never blocks a
18
+ * prompt on its own bugs.
19
+ *
20
+ * Telemetry: each match appends one JSONL line to
21
+ * ~/.claude/instincts/<project-hash>/route-prompt.jsonl with the row index,
22
+ * skill, and pattern. Telemetry failures never change the decision.
23
+ *
24
+ * Route table schema (hooks/route-table.json):
25
+ * {
26
+ * "rows": [
27
+ * { "pattern": "regex source", "flags": "i", "skill": "skill-name",
28
+ * "reason": "one-line description" }
29
+ * ]
30
+ * }
31
+ * Rows are evaluated in order; first match wins.
32
+ */
33
+ import { createHash } from "node:crypto";
34
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
35
+ import { homedir } from "node:os";
36
+ import { dirname, join } from "node:path";
37
+ import { execFileSync } from "node:child_process";
38
+ import { fileURLToPath } from "node:url";
39
+ function readStdin() {
40
+ try {
41
+ return readFileSync(0, "utf8");
42
+ }
43
+ catch {
44
+ return "";
45
+ }
46
+ }
47
+ function resolveHome() {
48
+ return process.env.HOME ?? process.env.USERPROFILE ?? homedir();
49
+ }
50
+ function resolveProjectRoot() {
51
+ const fromEnv = process.env.CLAUDE_PROJECT_DIR;
52
+ if (fromEnv)
53
+ return fromEnv;
54
+ try {
55
+ const root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
56
+ encoding: "utf8",
57
+ stdio: ["ignore", "pipe", "ignore"],
58
+ }).trim();
59
+ if (root)
60
+ return root;
61
+ }
62
+ catch {
63
+ // not in a git repo
64
+ }
65
+ return "global";
66
+ }
67
+ function telemetryPath(home) {
68
+ const hash = createHash("sha256")
69
+ .update(resolveProjectRoot())
70
+ .digest("hex")
71
+ .slice(0, 12);
72
+ return join(home, ".claude", "instincts", hash, "route-prompt.jsonl");
73
+ }
74
+ function writeTelemetry(home, event) {
75
+ try {
76
+ const path = telemetryPath(home);
77
+ const dir = dirname(path);
78
+ if (!existsSync(dir))
79
+ mkdirSync(dir, { recursive: true });
80
+ appendFileSync(path, `${JSON.stringify(event)}\n`);
81
+ }
82
+ catch {
83
+ // fail-open
84
+ }
85
+ }
86
+ function loadRouteTable() {
87
+ // Hook lives at <plugin-root>/hooks/route-prompt.mjs at runtime; the table
88
+ // sits next to it. import.meta.url gives the file:// URL of the compiled
89
+ // .mjs.
90
+ try {
91
+ const here = fileURLToPath(new URL(".", import.meta.url));
92
+ const tablePath = join(here, "route-table.json");
93
+ const raw = readFileSync(tablePath, "utf8");
94
+ const parsed = JSON.parse(raw);
95
+ if (!parsed || !Array.isArray(parsed.rows))
96
+ return null;
97
+ return parsed;
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ }
103
+ function findMatch(prompt, table) {
104
+ for (let i = 0; i < table.rows.length; i++) {
105
+ const row = table.rows[i];
106
+ if (!row || typeof row.pattern !== "string" || typeof row.skill !== "string") {
107
+ continue;
108
+ }
109
+ let rx;
110
+ try {
111
+ rx = new RegExp(row.pattern, row.flags ?? "");
112
+ }
113
+ catch {
114
+ continue;
115
+ }
116
+ if (rx.test(prompt)) {
117
+ return { row, index: i };
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+ function buildContext(row) {
123
+ const reason = row.reason
124
+ ? `${row.reason}\nConsider invoking \`/${row.skill}\` now.`
125
+ : `Consider invoking \`/${row.skill}\` now.`;
126
+ return `<system-reminder>\nPrompt-router suggestion: ${reason}\n</system-reminder>`;
127
+ }
128
+ function emit(output) {
129
+ if (output !== null) {
130
+ process.stdout.write(`${JSON.stringify(output)}\n`);
131
+ }
132
+ process.exit(0);
133
+ }
134
+ function main() {
135
+ const raw = readStdin();
136
+ let payload;
137
+ try {
138
+ payload = JSON.parse(raw);
139
+ }
140
+ catch {
141
+ emit(null);
142
+ return;
143
+ }
144
+ const prompt = payload.prompt;
145
+ if (typeof prompt !== "string" || prompt.length === 0) {
146
+ emit(null);
147
+ return;
148
+ }
149
+ const table = loadRouteTable();
150
+ if (!table) {
151
+ emit(null);
152
+ return;
153
+ }
154
+ const match = findMatch(prompt, table);
155
+ const home = resolveHome();
156
+ if (!match) {
157
+ writeTelemetry(home, {
158
+ ts: new Date().toISOString(),
159
+ hook: "route-prompt",
160
+ matched: false,
161
+ });
162
+ emit(null);
163
+ return;
164
+ }
165
+ writeTelemetry(home, {
166
+ ts: new Date().toISOString(),
167
+ hook: "route-prompt",
168
+ matched: true,
169
+ row_index: match.index,
170
+ skill: match.row.skill,
171
+ pattern: match.row.pattern,
172
+ });
173
+ emit({
174
+ hookSpecificOutput: {
175
+ hookEventName: "UserPromptSubmit",
176
+ additionalContext: buildContext(match.row),
177
+ },
178
+ });
179
+ }
180
+ main();
@@ -0,0 +1,35 @@
1
+ {
2
+ "description": "Declarative prompt-router for the continuous-improvement UserPromptSubmit hook. Each row maps a regex (prompt-side trigger) to a skill the agent should consider invoking. First match wins; rows are evaluated in order. Edit this file to grow the routing table — no code change required.",
3
+ "rows": [
4
+ {
5
+ "pattern": "\\b(write|add|new)\\s+(a\\s+)?(failing\\s+)?test\\b|\\btdd\\b|\\bred[- ]green[- ]refactor\\b",
6
+ "flags": "i",
7
+ "skill": "tdd-workflow",
8
+ "reason": "Prompt mentions writing tests, TDD, or RED-GREEN-REFACTOR."
9
+ },
10
+ {
11
+ "pattern": "\\b(verify|verification|prove\\s+it\\s+works|smallest\\s+check|check\\s+it\\s+passes)\\b",
12
+ "flags": "i",
13
+ "skill": "verification-loop",
14
+ "reason": "Prompt asks for verification before declaring done."
15
+ },
16
+ {
17
+ "pattern": "\\b(plan|blueprint|design\\s+(the\\s+)?approach|step[- ]by[- ]step\\s+plan|roadmap)\\b",
18
+ "flags": "i",
19
+ "skill": "planning-with-files",
20
+ "reason": "Prompt asks for a plan, blueprint, or design before implementation."
21
+ },
22
+ {
23
+ "pattern": "\\b(security\\s+review|threat\\s+model|owasp|vulnerab|auth(entication|orization)\\s+check)\\b",
24
+ "flags": "i",
25
+ "skill": "security-review",
26
+ "reason": "Prompt touches security review, threat modeling, or auth surface."
27
+ },
28
+ {
29
+ "pattern": "\\b(code\\s+review|review\\s+(my|the|this)\\s+(code|pr|diff|patch)|critique\\s+(the|this)\\s+code)\\b",
30
+ "flags": "i",
31
+ "skill": "code-review",
32
+ "reason": "Prompt asks for a code review on existing changes."
33
+ }
34
+ ]
35
+ }
@@ -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