continuous-improvement 3.11.0 → 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 (110) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/CHANGELOG.md +34 -0
  3. package/LICENSE +21 -21
  4. package/QUICKSTART.md +1 -1
  5. package/README.md +16 -15
  6. package/SKILL.md +1 -1
  7. package/bin/analyze.sh +161 -161
  8. package/bin/backfill.mjs +19 -19
  9. package/bin/check-doc-runtime-claims.mjs +0 -0
  10. package/bin/check-docs-substrings.mjs +0 -0
  11. package/bin/check-everything-mirror.mjs +0 -0
  12. package/bin/check-routing-targets.mjs +0 -0
  13. package/bin/check-scripts-citation-drift.mjs +0 -0
  14. package/bin/check-skill-count.mjs +0 -0
  15. package/bin/check-skill-law-tag.mjs +0 -0
  16. package/bin/check-skill-mirror.mjs +0 -0
  17. package/bin/check-skill-tiers.mjs +0 -0
  18. package/bin/check-test-imports-only.mjs +0 -0
  19. package/bin/check-third-party-shape.mjs +0 -0
  20. package/bin/check-tool-count.mjs +129 -0
  21. package/bin/companion-preference-status.mjs +0 -0
  22. package/bin/gateguard-clear.mjs +0 -0
  23. package/bin/generate-plugin-manifests.mjs +1 -0
  24. package/bin/harvest-friction.mjs +1 -1
  25. package/bin/hook-stats.mjs +21 -21
  26. package/bin/install.mjs +3 -2
  27. package/bin/lint-transcript.mjs +0 -0
  28. package/bin/mcp-server.mjs +1 -1
  29. package/bin/observe.mjs +0 -0
  30. package/bin/pre-commit-block-strays.sh +0 -0
  31. package/bin/refresh-third-party.mjs +0 -0
  32. package/bin/unified-cli.mjs +0 -0
  33. package/commands/continuous-improvement.md +115 -115
  34. package/commands/dashboard.md +56 -56
  35. package/commands/harvest.md +1 -1
  36. package/commands/seven-laws.md +16 -16
  37. package/commands/superpowers.md +1 -1
  38. package/commands/workspace-surface-audit.md +77 -77
  39. package/hooks/companion-preference.mjs +0 -0
  40. package/hooks/gateguard.mjs +22 -10
  41. package/hooks/goal-drift-stop.mjs +0 -0
  42. package/hooks/observe.sh +0 -0
  43. package/hooks/recall-briefing.mjs +167 -0
  44. package/hooks/route-prompt.mjs +0 -0
  45. package/hooks/session.sh +106 -106
  46. package/hooks/three-section-close.mjs +0 -0
  47. package/instinct-packs/go.json +58 -58
  48. package/instinct-packs/meta.json +16 -16
  49. package/instinct-packs/python.json +58 -58
  50. package/instinct-packs/react.json +58 -58
  51. package/lib/cli-anything.mjs +0 -0
  52. package/lib/compound-engineering.mjs +0 -0
  53. package/lib/gateguard-state.mjs +0 -0
  54. package/lib/goal-drift-gate.mjs +0 -0
  55. package/lib/goal-state.mjs +8 -8
  56. package/lib/observe-event.mjs +0 -0
  57. package/lib/plugin-metadata.mjs +12 -7
  58. package/lib/pm-marketplace.mjs +0 -0
  59. package/lib/pm-skills.mjs +0 -0
  60. package/lib/recall-briefing.mjs +57 -0
  61. package/lib/recall-index.mjs +2 -2
  62. package/lib/resolve-home-dir.mjs +0 -0
  63. package/lib/skill-distill.mjs +0 -0
  64. package/lib/skill-tiers.mjs +0 -0
  65. package/lib/unified-plugin.mjs +0 -0
  66. package/llms.txt +2 -2
  67. package/package.json +4 -3
  68. package/plugins/beginner.json +3 -3
  69. package/plugins/continuous-improvement/.claude-plugin/marketplace.json +2 -2
  70. package/plugins/continuous-improvement/.claude-plugin/plugin.json +2 -2
  71. package/plugins/continuous-improvement/LICENSE +21 -21
  72. package/plugins/continuous-improvement/agents/README.md +3 -3
  73. package/plugins/continuous-improvement/bin/backfill.mjs +19 -19
  74. package/plugins/continuous-improvement/bin/gateguard-clear.mjs +0 -0
  75. package/plugins/continuous-improvement/bin/mcp-server.mjs +1 -1
  76. package/plugins/continuous-improvement/bin/observe.mjs +0 -0
  77. package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -115
  78. package/plugins/continuous-improvement/commands/dashboard.md +56 -56
  79. package/plugins/continuous-improvement/commands/harvest.md +1 -1
  80. package/plugins/continuous-improvement/commands/seven-laws.md +16 -16
  81. package/plugins/continuous-improvement/commands/superpowers.md +1 -1
  82. package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -77
  83. package/plugins/continuous-improvement/hooks/companion-preference.mjs +0 -0
  84. package/plugins/continuous-improvement/hooks/gateguard.mjs +22 -10
  85. package/plugins/continuous-improvement/hooks/goal-drift-stop.mjs +0 -0
  86. package/plugins/continuous-improvement/hooks/hooks.json +6 -1
  87. package/plugins/continuous-improvement/hooks/observe.sh +0 -0
  88. package/plugins/continuous-improvement/hooks/recall-briefing.mjs +167 -0
  89. package/plugins/continuous-improvement/hooks/route-prompt.mjs +0 -0
  90. package/plugins/continuous-improvement/hooks/session.sh +106 -106
  91. package/plugins/continuous-improvement/hooks/three-section-close.mjs +0 -0
  92. package/plugins/continuous-improvement/instinct-packs/go.json +58 -58
  93. package/plugins/continuous-improvement/instinct-packs/meta.json +16 -16
  94. package/plugins/continuous-improvement/instinct-packs/python.json +58 -58
  95. package/plugins/continuous-improvement/instinct-packs/react.json +58 -58
  96. package/plugins/continuous-improvement/lib/gateguard-state.mjs +0 -0
  97. package/plugins/continuous-improvement/lib/goal-drift-gate.mjs +0 -0
  98. package/plugins/continuous-improvement/lib/goal-state.mjs +8 -8
  99. package/plugins/continuous-improvement/lib/observe-event.mjs +0 -0
  100. package/plugins/continuous-improvement/lib/plugin-metadata.mjs +12 -7
  101. package/plugins/continuous-improvement/lib/recall-briefing.mjs +57 -0
  102. package/plugins/continuous-improvement/lib/recall-index.mjs +2 -2
  103. package/plugins/continuous-improvement/lib/resolve-home-dir.mjs +0 -0
  104. package/plugins/continuous-improvement/lib/skill-distill.mjs +0 -0
  105. package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +1 -1
  106. package/plugins/continuous-improvement/skills/superpowers/SKILL.md +1 -1
  107. package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -411
  108. package/plugins/expert.json +2 -2
  109. package/skills/superpowers.md +1 -1
  110. package/skills/tdd-workflow.md +411 -411
@@ -1,77 +1,77 @@
1
- ---
2
- name: workspace-surface-audit
3
- description: "Audit workspace capabilities and recommend continuous-improvement-native skills, hooks, and workflows"
4
- ---
5
-
6
- # /workspace-surface-audit
7
-
8
- Audit the active repo, MCP servers, plugins, connectors, env surfaces, and harness setup. Returns a 5-section report with specific next moves.
9
-
10
- ## Usage
11
-
12
- ```
13
- /workspace-surface-audit
14
- ```
15
-
16
- ## What It Checks
17
-
18
- 1. **Repo surface** — `package.json`, lockfiles, framework config, `.mcp.json`, `.claude/settings*.json`, `AGENTS.md`
19
- 2. **Environment surface** — `.env*` files (key names only, no secrets)
20
- 3. **Connected tools** — installed plugins, MCP servers, LSPs, app integrations
21
- 4. **continuous-improvement surface** — existing skills, commands, hooks, agents
22
-
23
- ## Output Format
24
-
25
- ```
26
- === Workspace Surface Audit ===
27
-
28
- ## Current Surface
29
- - [What is usable right now]
30
-
31
- ## Parity
32
- - [Where continuous-improvement matches or exceeds benchmarks]
33
-
34
- ## Primitive-Only Gaps
35
- - [Tools exist, but continuous-improvement lacks clean operator skills]
36
-
37
- ## Missing Integrations
38
- - [Capabilities not available yet]
39
-
40
- ## Top 3-5 Next Moves
41
- 1. [Concrete continuous-improvement-native addition]
42
- 2. [Ordered by impact]
43
- ```
44
-
45
- ## Rules
46
-
47
- - **Never print secrets** — only provider names, capability names, file paths
48
- - **Prefer continuous-improvement** over generic "install another plugin" advice
49
- - **Organize by workflows**, not API brands
50
- - **Specific recommendations** — concrete enough to implement without another discovery pass
51
-
52
- ## Example Output
53
-
54
- ```
55
- ## Current Surface
56
- - MCP: filesystem, github, playwright
57
- - Plugins: beginner, expert (continuous-improvement)
58
- - Env: STRIPE_API_KEY, FAL_KEY configured
59
- - Framework: Next.js 15, React 19, TypeScript
60
-
61
- ## Parity
62
- - Continuous improvement: covered (native)
63
- - Testing discipline: covered (TDD workflows)
64
-
65
- ## Primitive-Only Gaps
66
- - Stripe connected but no billing-operator skill
67
- - GitHub MCP available but no issue-triage workflow
68
-
69
- ## Missing Integrations
70
- - No Linear connector detected
71
- - No Slack webhook configured
72
-
73
- ## Top 3-5 Next Moves
74
- 1. Create billing-operator skill (Stripe integration)
75
- 2. Create github-ops workflow for issue triage
76
- 3. Add Linear connector skill for project management
77
- ```
1
+ ---
2
+ name: workspace-surface-audit
3
+ description: "Audit workspace capabilities and recommend continuous-improvement-native skills, hooks, and workflows"
4
+ ---
5
+
6
+ # /workspace-surface-audit
7
+
8
+ Audit the active repo, MCP servers, plugins, connectors, env surfaces, and harness setup. Returns a 5-section report with specific next moves.
9
+
10
+ ## Usage
11
+
12
+ ```
13
+ /workspace-surface-audit
14
+ ```
15
+
16
+ ## What It Checks
17
+
18
+ 1. **Repo surface** — `package.json`, lockfiles, framework config, `.mcp.json`, `.claude/settings*.json`, `AGENTS.md`
19
+ 2. **Environment surface** — `.env*` files (key names only, no secrets)
20
+ 3. **Connected tools** — installed plugins, MCP servers, LSPs, app integrations
21
+ 4. **continuous-improvement surface** — existing skills, commands, hooks, agents
22
+
23
+ ## Output Format
24
+
25
+ ```
26
+ === Workspace Surface Audit ===
27
+
28
+ ## Current Surface
29
+ - [What is usable right now]
30
+
31
+ ## Parity
32
+ - [Where continuous-improvement matches or exceeds benchmarks]
33
+
34
+ ## Primitive-Only Gaps
35
+ - [Tools exist, but continuous-improvement lacks clean operator skills]
36
+
37
+ ## Missing Integrations
38
+ - [Capabilities not available yet]
39
+
40
+ ## Top 3-5 Next Moves
41
+ 1. [Concrete continuous-improvement-native addition]
42
+ 2. [Ordered by impact]
43
+ ```
44
+
45
+ ## Rules
46
+
47
+ - **Never print secrets** — only provider names, capability names, file paths
48
+ - **Prefer continuous-improvement** over generic "install another plugin" advice
49
+ - **Organize by workflows**, not API brands
50
+ - **Specific recommendations** — concrete enough to implement without another discovery pass
51
+
52
+ ## Example Output
53
+
54
+ ```
55
+ ## Current Surface
56
+ - MCP: filesystem, github, playwright
57
+ - Plugins: beginner, expert (continuous-improvement)
58
+ - Env: STRIPE_API_KEY, FAL_KEY configured
59
+ - Framework: Next.js 15, React 19, TypeScript
60
+
61
+ ## Parity
62
+ - Continuous improvement: covered (native)
63
+ - Testing discipline: covered (TDD workflows)
64
+
65
+ ## Primitive-Only Gaps
66
+ - Stripe connected but no billing-operator skill
67
+ - GitHub MCP available but no issue-triage workflow
68
+
69
+ ## Missing Integrations
70
+ - No Linear connector detected
71
+ - No Slack webhook configured
72
+
73
+ ## Top 3-5 Next Moves
74
+ 1. Create billing-operator skill (Stripe integration)
75
+ 2. Create github-ops workflow for issue triage
76
+ 3. Add Linear connector skill for project management
77
+ ```
@@ -27,7 +27,8 @@
27
27
  * path.
28
28
  */
29
29
  import { readFileSync } from "node:fs";
30
- import { join } from "node:path";
30
+ import { dirname, join } from "node:path";
31
+ import { fileURLToPath } from "node:url";
31
32
  import { MAX_CLEARED_FILES, canonicalizeFileKey, isCapReached, isFileCleared, loadState, markFileCleared, resolveSessionDir, saveState, } from "../lib/gateguard-state.mjs";
32
33
  const TOOL_ROUTE = {
33
34
  Read: "allow",
@@ -104,11 +105,23 @@ function formatFileTarget(filePaths) {
104
105
  return nonEmpty[0];
105
106
  return `${nonEmpty.length} files (${nonEmpty.join(", ")})`;
106
107
  }
108
+ // Resolved from the hook's own location at load time. The block reason must
109
+ // print a command the agent can run in its OWN shell, where ${CLAUDE_PLUGIN_ROOT}
110
+ // is empty — that variable is only populated while this hook itself executes.
111
+ // bin/ is a sibling of hooks/ in both the repo layout and the plugin-bundle
112
+ // mirror, so this resolves correctly from either.
113
+ const CLEAR_CLI_PATH = join(dirname(fileURLToPath(import.meta.url)), "..", "bin", "gateguard-clear.mjs");
114
+ // Quote a path for the printed clearance command. Forward slashes (node accepts
115
+ // them on every platform and they survive a Bash double-quote without escaping)
116
+ // inside JSON quotes — JSON.stringify still escapes any embedded quote, so a
117
+ // path like a"b.ts stays safe rather than collapsing into a bare-quoted token.
118
+ function quotePath(p) {
119
+ return JSON.stringify(p.replace(/\\/g, "/"));
120
+ }
107
121
  function buildMutatingFileReason(toolName, filePaths, stateFilePath) {
108
122
  const target = formatFileTarget(filePaths);
109
123
  const pathList = filePaths.filter((p) => p !== "");
110
- const jsonArray = (pathList.length > 0 ? pathList : [target]).map((p) => JSON.stringify(p)).join(", ");
111
- const cliArgs = (pathList.length > 0 ? pathList : [target]).map((p) => JSON.stringify(p)).join(" ");
124
+ const quoted = (pathList.length > 0 ? pathList : [target]).map((p) => quotePath(p));
112
125
  return [
113
126
  `Before ${toolName === "Write" ? "creating" : "editing"} ${target}, present these facts:`,
114
127
  "",
@@ -117,13 +130,12 @@ function buildMutatingFileReason(toolName, filePaths, stateFilePath) {
117
130
  " 3. If this file reads/writes data files, show field names, structure, and date format",
118
131
  " 4. Quote the user's current instruction verbatim",
119
132
  "",
120
- "Then clear the gate and retry the same call. Any one of:",
121
- ` A. MCP tool: ci_gateguard_clear {file_paths: [${jsonArray}]}`,
122
- ` B. CLI (Bash, never gated): node "\${CLAUDE_PLUGIN_ROOT}/bin/gateguard-clear.mjs" ${cliArgs}`,
123
- ` C. Manual: append each path to "cleared_files" in ${stateFilePath} via a non-destructive Bash write.`,
124
- " Clearance matches regardless of drive-letter case or path separator.",
125
- " (On harnesses that forward unknown tool params you may instead retry with",
126
- " `_gateguard_facts_presented: true`; Claude Code's strict schema rejects that, so use A/B/C.)",
133
+ "Then clear the gate and retry the same call. Either route works:",
134
+ ` A. Bash (always works — run verbatim): node ${quotePath(CLEAR_CLI_PATH)} --state ${quotePath(stateFilePath)} ${quoted.join(" ")}`,
135
+ ` B. MCP tool (when the continuous-improvement server is connected): ci_gateguard_clear {file_paths: [${quoted.join(", ")}]}`,
136
+ " Both canonicalize paths drive-letter case and separators don't matter.",
137
+ " (Harnesses that forward unknown tool params may instead retry the call with",
138
+ " `_gateguard_facts_presented: true`; Claude Code's strict schema rejects that, so use A or B.)",
127
139
  ].join("\n");
128
140
  }
129
141
  function buildDestructiveBashReason(command) {
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Gateguard fact-forcing PreToolUse, companion-preference enforcement, observation, session lifecycle, 3-section-close discipline, goal-drift Stop gate, and UserPromptSubmit lazy-routing hooks for continuous-improvement.",
2
+ "description": "Gateguard fact-forcing PreToolUse, companion-preference enforcement, observation, session lifecycle, 3-section-close discipline, goal-drift Stop gate, and UserPromptSubmit lazy-routing plus opt-in proactive recall-briefing hooks for continuous-improvement.",
3
3
  "hooks": {
4
4
  "PreToolUse": [
5
5
  {
@@ -35,6 +35,11 @@
35
35
  "type": "command",
36
36
  "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/route-prompt.mjs\"",
37
37
  "timeout": 5
38
+ },
39
+ {
40
+ "type": "command",
41
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/recall-briefing.mjs\"",
42
+ "timeout": 5
38
43
  }
39
44
  ]
40
45
  }
File without changes
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Runtime UserPromptSubmit recall-briefing hook (opt-in amplifier).
4
+ *
5
+ * On the first substantive prompt of a session, searches this project's past
6
+ * observations (BM25, via recall-index.mts) and injects a one-time
7
+ * `<system-reminder>` listing the most relevant prior activity — so the agent
8
+ * reuses a past fix instead of re-deriving it. This makes episodic memory
9
+ * proactive instead of opt-in-per-query (the ci_recall MCP tool stays available
10
+ * for explicit, deeper searches).
11
+ *
12
+ * Opt-in: runs only when CLAUDE_RECALL_BRIEFING is 1/on/true. Default off, so it
13
+ * never changes the prompt path for users who have not enabled it.
14
+ *
15
+ * Stdin : JSON { prompt, session_id?, cwd? }
16
+ * Stdout : JSON { hookSpecificOutput: { hookEventName: "UserPromptSubmit",
17
+ * additionalContext: string } }
18
+ * OR empty when it should not brief.
19
+ * Exit : 0 always — this is an amplifier, never a gate; it cannot block a prompt.
20
+ *
21
+ * Fail-open: any error (no env flag, bad stdin, missing/short observations log,
22
+ * unreadable state) emits nothing and exits 0. State at
23
+ * ~/.claude/instincts/<project-hash>/recall-briefing-session.json records which
24
+ * session_ids have been briefed so each session is briefed at most once.
25
+ */
26
+ import { createHash } from "node:crypto";
27
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
28
+ import { homedir } from "node:os";
29
+ import { join } from "node:path";
30
+ import { execFileSync } from "node:child_process";
31
+ import { buildIndex, query } from "../lib/recall-index.mjs";
32
+ import { DEFAULT_MAX_HITS, decideBriefing } from "../lib/recall-briefing.mjs";
33
+ const ENABLED_VALUES = new Set(["1", "on", "true", "yes"]);
34
+ const MAX_BRIEFED_KEYS = 1000;
35
+ function isEnabled() {
36
+ const flag = (process.env.CLAUDE_RECALL_BRIEFING ?? "").trim().toLowerCase();
37
+ return ENABLED_VALUES.has(flag);
38
+ }
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 instinctsDir(home) {
68
+ const hash = createHash("sha256").update(resolveProjectRoot()).digest("hex").slice(0, 12);
69
+ return join(home, ".claude", "instincts", hash);
70
+ }
71
+ function readObservations(dir) {
72
+ const path = join(dir, "observations.jsonl");
73
+ let raw;
74
+ try {
75
+ raw = readFileSync(path, "utf8");
76
+ }
77
+ catch {
78
+ return [];
79
+ }
80
+ const rows = [];
81
+ for (const line of raw.split(/\r?\n/)) {
82
+ const trimmed = line.trim();
83
+ if (!trimmed)
84
+ continue;
85
+ try {
86
+ rows.push(JSON.parse(trimmed));
87
+ }
88
+ catch {
89
+ // skip a malformed line; never let one bad row sink the briefing
90
+ }
91
+ }
92
+ return rows;
93
+ }
94
+ function readState(dir) {
95
+ try {
96
+ const parsed = JSON.parse(readFileSync(join(dir, "recall-briefing-session.json"), "utf8"));
97
+ if (parsed && typeof parsed.briefed === "object" && parsed.briefed !== null)
98
+ return parsed;
99
+ }
100
+ catch {
101
+ // missing or malformed → fresh state
102
+ }
103
+ return { briefed: {} };
104
+ }
105
+ function writeState(dir, state, sessionKey) {
106
+ try {
107
+ const keys = Object.keys(state.briefed);
108
+ // Bound growth: if the map is huge, keep only the most recent run plus this one.
109
+ const briefed = keys.length >= MAX_BRIEFED_KEYS ? {} : state.briefed;
110
+ briefed[sessionKey] = new Date().toISOString();
111
+ if (!existsSync(dir))
112
+ mkdirSync(dir, { recursive: true });
113
+ writeFileSync(join(dir, "recall-briefing-session.json"), `${JSON.stringify({ briefed }, null, 2)}\n`);
114
+ }
115
+ catch {
116
+ // fail-open: a missed state write at worst briefs twice; it never blocks
117
+ }
118
+ }
119
+ function emit(additionalContext) {
120
+ if (additionalContext !== null) {
121
+ process.stdout.write(`${JSON.stringify({
122
+ hookSpecificOutput: { hookEventName: "UserPromptSubmit", additionalContext },
123
+ })}\n`);
124
+ }
125
+ process.exit(0);
126
+ }
127
+ function main() {
128
+ if (!isEnabled()) {
129
+ emit(null);
130
+ return;
131
+ }
132
+ let payload;
133
+ try {
134
+ payload = JSON.parse(readStdin());
135
+ }
136
+ catch {
137
+ emit(null);
138
+ return;
139
+ }
140
+ const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
141
+ if (prompt.length === 0) {
142
+ emit(null);
143
+ return;
144
+ }
145
+ const sessionKey = typeof payload.session_id === "string" && payload.session_id ? payload.session_id : "no-session";
146
+ const home = resolveHome();
147
+ const dir = instinctsDir(home);
148
+ const state = readState(dir);
149
+ const alreadyBriefed = Object.prototype.hasOwnProperty.call(state.briefed, sessionKey);
150
+ const observations = readObservations(dir);
151
+ const index = buildIndex(observations);
152
+ const hits = query(index, prompt, { k: DEFAULT_MAX_HITS });
153
+ const decision = decideBriefing({ enabled: true, alreadyBriefed, prompt, hits });
154
+ if (!decision.shouldBrief || decision.reminder === null) {
155
+ emit(null);
156
+ return;
157
+ }
158
+ writeState(dir, state, sessionKey);
159
+ emit(decision.reminder);
160
+ }
161
+ try {
162
+ main();
163
+ }
164
+ catch {
165
+ // Absolute fail-open backstop: never let a hook bug block a prompt.
166
+ process.exit(0);
167
+ }
@@ -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