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.
- package/.claude-plugin/marketplace.json +3 -3
- package/CHANGELOG.md +34 -0
- package/LICENSE +21 -21
- package/QUICKSTART.md +1 -1
- package/README.md +16 -15
- package/SKILL.md +1 -1
- package/bin/analyze.sh +161 -161
- package/bin/backfill.mjs +19 -19
- package/bin/check-doc-runtime-claims.mjs +0 -0
- package/bin/check-docs-substrings.mjs +0 -0
- package/bin/check-everything-mirror.mjs +0 -0
- package/bin/check-routing-targets.mjs +0 -0
- package/bin/check-scripts-citation-drift.mjs +0 -0
- package/bin/check-skill-count.mjs +0 -0
- package/bin/check-skill-law-tag.mjs +0 -0
- package/bin/check-skill-mirror.mjs +0 -0
- package/bin/check-skill-tiers.mjs +0 -0
- package/bin/check-test-imports-only.mjs +0 -0
- package/bin/check-third-party-shape.mjs +0 -0
- package/bin/check-tool-count.mjs +129 -0
- package/bin/companion-preference-status.mjs +0 -0
- package/bin/gateguard-clear.mjs +0 -0
- package/bin/generate-plugin-manifests.mjs +1 -0
- package/bin/harvest-friction.mjs +1 -1
- package/bin/hook-stats.mjs +21 -21
- package/bin/install.mjs +3 -2
- package/bin/lint-transcript.mjs +0 -0
- package/bin/mcp-server.mjs +1 -1
- package/bin/observe.mjs +0 -0
- package/bin/pre-commit-block-strays.sh +0 -0
- package/bin/refresh-third-party.mjs +0 -0
- package/bin/unified-cli.mjs +0 -0
- package/commands/continuous-improvement.md +115 -115
- package/commands/dashboard.md +56 -56
- package/commands/harvest.md +1 -1
- package/commands/seven-laws.md +16 -16
- package/commands/superpowers.md +1 -1
- package/commands/workspace-surface-audit.md +77 -77
- package/hooks/companion-preference.mjs +0 -0
- package/hooks/gateguard.mjs +22 -10
- package/hooks/goal-drift-stop.mjs +0 -0
- package/hooks/observe.sh +0 -0
- package/hooks/recall-briefing.mjs +167 -0
- package/hooks/route-prompt.mjs +0 -0
- package/hooks/session.sh +106 -106
- package/hooks/three-section-close.mjs +0 -0
- 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/cli-anything.mjs +0 -0
- package/lib/compound-engineering.mjs +0 -0
- package/lib/gateguard-state.mjs +0 -0
- package/lib/goal-drift-gate.mjs +0 -0
- package/lib/goal-state.mjs +8 -8
- package/lib/observe-event.mjs +0 -0
- package/lib/plugin-metadata.mjs +12 -7
- package/lib/pm-marketplace.mjs +0 -0
- package/lib/pm-skills.mjs +0 -0
- package/lib/recall-briefing.mjs +57 -0
- package/lib/recall-index.mjs +2 -2
- package/lib/resolve-home-dir.mjs +0 -0
- package/lib/skill-distill.mjs +0 -0
- package/lib/skill-tiers.mjs +0 -0
- package/lib/unified-plugin.mjs +0 -0
- package/llms.txt +2 -2
- package/package.json +4 -3
- package/plugins/beginner.json +3 -3
- package/plugins/continuous-improvement/.claude-plugin/marketplace.json +2 -2
- package/plugins/continuous-improvement/.claude-plugin/plugin.json +2 -2
- package/plugins/continuous-improvement/LICENSE +21 -21
- package/plugins/continuous-improvement/agents/README.md +3 -3
- package/plugins/continuous-improvement/bin/backfill.mjs +19 -19
- package/plugins/continuous-improvement/bin/gateguard-clear.mjs +0 -0
- package/plugins/continuous-improvement/bin/mcp-server.mjs +1 -1
- package/plugins/continuous-improvement/bin/observe.mjs +0 -0
- package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -115
- package/plugins/continuous-improvement/commands/dashboard.md +56 -56
- package/plugins/continuous-improvement/commands/harvest.md +1 -1
- package/plugins/continuous-improvement/commands/seven-laws.md +16 -16
- package/plugins/continuous-improvement/commands/superpowers.md +1 -1
- package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -77
- package/plugins/continuous-improvement/hooks/companion-preference.mjs +0 -0
- package/plugins/continuous-improvement/hooks/gateguard.mjs +22 -10
- package/plugins/continuous-improvement/hooks/goal-drift-stop.mjs +0 -0
- package/plugins/continuous-improvement/hooks/hooks.json +6 -1
- package/plugins/continuous-improvement/hooks/observe.sh +0 -0
- package/plugins/continuous-improvement/hooks/recall-briefing.mjs +167 -0
- package/plugins/continuous-improvement/hooks/route-prompt.mjs +0 -0
- package/plugins/continuous-improvement/hooks/session.sh +106 -106
- package/plugins/continuous-improvement/hooks/three-section-close.mjs +0 -0
- 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/gateguard-state.mjs +0 -0
- package/plugins/continuous-improvement/lib/goal-drift-gate.mjs +0 -0
- package/plugins/continuous-improvement/lib/goal-state.mjs +8 -8
- package/plugins/continuous-improvement/lib/observe-event.mjs +0 -0
- package/plugins/continuous-improvement/lib/plugin-metadata.mjs +12 -7
- package/plugins/continuous-improvement/lib/recall-briefing.mjs +57 -0
- package/plugins/continuous-improvement/lib/recall-index.mjs +2 -2
- package/plugins/continuous-improvement/lib/resolve-home-dir.mjs +0 -0
- package/plugins/continuous-improvement/lib/skill-distill.mjs +0 -0
- package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +1 -1
- package/plugins/continuous-improvement/skills/superpowers/SKILL.md +1 -1
- package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -411
- package/plugins/expert.json +2 -2
- package/skills/superpowers.md +1 -1
- 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
|
+
```
|
|
File without changes
|
|
@@ -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
|
|
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.
|
|
121
|
-
` A.
|
|
122
|
-
` B.
|
|
123
|
-
|
|
124
|
-
"
|
|
125
|
-
"
|
|
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) {
|
|
File without changes
|
|
@@ -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
|
+
}
|
|
File without changes
|
|
@@ -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
|
|
File without changes
|