continuous-improvement 2.1.0 → 2.1.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.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="promo/combined.gif" alt="Before vs After continuous-improvement" width="700" />
2
+ <img src="assets/combined.gif" alt="Before vs After continuous-improvement" width="700" />
3
3
  </p>
4
4
 
5
5
  # continuous-improvement
package/bin/analyze.sh CHANGED
@@ -1,115 +1,167 @@
1
1
  #!/usr/bin/env bash
2
- # analyze.sh — Read observations.jsonl, detect patterns, append rules to rules.md
3
- # This is the actual analysis pipeline. Runs via /continuous-improvement or observer-loop.
4
- # Uses claude CLI with Haiku for cost-efficient analysis.
2
+ # analyze.sh — Read observations.jsonl, detect patterns, create instinct YAML files
3
+ # Runs via /continuous-improvement command. Uses claude CLI with Haiku for cost-efficient analysis.
5
4
 
6
5
  set -euo pipefail
7
6
 
8
- MULAHAZAH_DIR="${HOME}/.claude/mulahazah"
9
- RULES_FILE="${MULAHAZAH_DIR}/rules.md"
10
- GLOBAL_OBS="${MULAHAZAH_DIR}/observations.jsonl"
7
+ INSTINCTS_DIR="${HOME}/.claude/instincts"
11
8
 
9
+ # ---------------------------------------------------------------------------
12
10
  # Detect project
13
- PROJECT_DIR=""
14
- PROJECT_OBS=""
15
- if REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null); then
16
- PROJECT_HASH=$(printf '%s' "$REPO_ROOT" | sha256sum | cut -c1-12)
17
- PROJECT_DIR="${MULAHAZAH_DIR}/projects/${PROJECT_HASH}"
18
- PROJECT_OBS="${PROJECT_DIR}/observations.jsonl"
11
+ # ---------------------------------------------------------------------------
12
+ PROJECT_ROOT=""
13
+ if [[ -n "${CLAUDE_PROJECT_DIR:-}" && -d "${CLAUDE_PROJECT_DIR}" ]]; then
14
+ PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
19
15
  fi
20
-
21
- # Find observations file
22
- OBS_FILE=""
23
- if [ -n "$PROJECT_OBS" ] && [ -f "$PROJECT_OBS" ]; then
24
- OBS_FILE="$PROJECT_OBS"
25
- elif [ -f "$GLOBAL_OBS" ]; then
26
- OBS_FILE="$GLOBAL_OBS"
16
+ if [[ -z "$PROJECT_ROOT" ]]; then
17
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
18
+ fi
19
+ if [[ -z "$PROJECT_ROOT" ]]; then
20
+ PROJECT_ROOT="global"
27
21
  fi
28
22
 
29
- if [ -z "$OBS_FILE" ] || [ ! -s "$OBS_FILE" ]; then
30
- echo "No observations found. Use Claude Code with hooks installed to generate observations."
23
+ PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
24
+ PROJECT_NAME="$(basename "${PROJECT_ROOT%.git}")"
25
+ PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
26
+ OBS_FILE="${PROJECT_DIR}/observations.jsonl"
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Check observations exist
30
+ # ---------------------------------------------------------------------------
31
+ if [[ ! -f "$OBS_FILE" ]] || [[ ! -s "$OBS_FILE" ]]; then
32
+ echo "No observations found at ${OBS_FILE}"
33
+ echo "Use Claude Code with hooks installed to generate observations."
31
34
  exit 0
32
35
  fi
33
36
 
34
37
  OBS_COUNT=$(wc -l < "$OBS_FILE")
35
- echo "Found $OBS_COUNT observations in $OBS_FILE"
38
+ echo "Found ${OBS_COUNT} observations in ${OBS_FILE}"
36
39
 
37
- if [ "$OBS_COUNT" -lt 5 ]; then
38
- echo "Need at least 5 observations for meaningful analysis. Keep using Claude Code."
40
+ if (( OBS_COUNT < 20 )); then
41
+ echo "Need at least 20 observations for meaningful analysis. Keep using Claude Code (${OBS_COUNT}/20)."
39
42
  exit 0
40
43
  fi
41
44
 
42
- # Read existing rules to avoid duplicates
43
- EXISTING_RULES=""
44
- if [ -f "$RULES_FILE" ]; then
45
- EXISTING_RULES=$(cat "$RULES_FILE")
46
- fi
45
+ # ---------------------------------------------------------------------------
46
+ # Read existing instincts to avoid duplicates
47
+ # ---------------------------------------------------------------------------
48
+ EXISTING_INSTINCTS=""
49
+ for f in "${PROJECT_DIR}"/*.yaml "${INSTINCTS_DIR}/global"/*.yaml; do
50
+ [[ -f "$f" ]] && EXISTING_INSTINCTS="${EXISTING_INSTINCTS}$(cat "$f")"$'\n'
51
+ done
47
52
 
48
- # Take last 200 observations (keep prompt size manageable)
49
- RECENT_OBS=$(tail -200 "$OBS_FILE")
53
+ # ---------------------------------------------------------------------------
54
+ # Take last 500 observations
55
+ # ---------------------------------------------------------------------------
56
+ RECENT_OBS=$(tail -500 "$OBS_FILE")
50
57
 
58
+ # ---------------------------------------------------------------------------
51
59
  # Build analysis prompt
52
- ANALYSIS_PROMPT="Analyze these Claude Code session observations and extract behavioral patterns.
60
+ # ---------------------------------------------------------------------------
61
+ ANALYSIS_PROMPT="Analyze these Claude Code session observations and extract behavioral patterns as instinct YAML files.
53
62
 
54
63
  OBSERVATIONS (JSONL — each line is a tool call):
55
64
  ${RECENT_OBS}
56
65
 
57
- EXISTING RULES (already learned — do NOT duplicate these):
58
- ${EXISTING_RULES}
66
+ EXISTING INSTINCTS (already learned — do NOT duplicate these):
67
+ ${EXISTING_INSTINCTS}
59
68
 
60
69
  YOUR TASK:
61
70
  1. Look for REPEATED PATTERNS — same tool sequence used 3+ times
62
71
  2. Look for ERROR-THEN-FIX sequences — tool fails, next tools fix it
63
72
  3. Look for TOOL PREFERENCES — one tool consistently chosen over alternatives
64
73
  4. Look for WORKFLOW PATTERNS — consistent ordering of operations
65
-
66
- OUTPUT FORMAT — output ONLY new rules, one per line, as markdown list items:
67
- - Rule: [specific actionable rule based on observed pattern]
68
-
69
- Rules must be:
70
- - Specific and actionable (not vague advice)
71
- - Based on actual observed patterns (cite the tools/sequence you saw)
72
- - Different from existing rules listed above
73
- - Useful for future sessions
74
+ 5. Look for USER CORRECTIONS — user says no/stop/don't after an action
75
+
76
+ OUTPUT FORMAT output ONLY new instincts as YAML blocks, separated by ---:
77
+
78
+ id: descriptive-kebab-case-id
79
+ trigger: \"when [specific situation]\"
80
+ confidence: 0.5
81
+ domain: workflow|tooling|testing|patterns
82
+ source: observation
83
+ scope: project
84
+ project_id: ${PROJECT_HASH}
85
+ created: \"$(date -u +%Y-%m-%d)\"
86
+ last_seen: \"$(date -u +%Y-%m-%d)\"
87
+ observation_count: [number of times pattern was seen]
88
+ ---
89
+ [One sentence describing the specific actionable behavior]
90
+
91
+ Rules:
92
+ - Only create instincts for patterns seen 3+ times
93
+ - Start confidence at 0.5 (suggest level)
94
+ - Be specific and actionable (not vague advice)
95
+ - Different from existing instincts listed above
74
96
 
75
97
  If no new patterns are found, output exactly: NO_NEW_PATTERNS
76
98
 
77
- Output ONLY the rules list or NO_NEW_PATTERNS. No explanation, no preamble."
99
+ Output ONLY the YAML blocks or NO_NEW_PATTERNS. No explanation, no preamble."
78
100
 
101
+ # ---------------------------------------------------------------------------
79
102
  # Run analysis with Haiku
103
+ # ---------------------------------------------------------------------------
80
104
  echo "Analyzing patterns with Haiku..."
81
105
  RESULT=$(echo "$ANALYSIS_PROMPT" | claude --model haiku --print -p - 2>/dev/null) || {
82
106
  echo "Analysis failed — claude CLI error. Try running manually."
83
107
  exit 1
84
108
  }
85
109
 
86
- if [ "$RESULT" = "NO_NEW_PATTERNS" ] || [ -z "$RESULT" ]; then
110
+ if [[ "$RESULT" == "NO_NEW_PATTERNS" ]] || [[ -z "$RESULT" ]]; then
87
111
  echo "No new patterns detected yet. Keep using Claude Code — patterns emerge over time."
88
112
  exit 0
89
113
  fi
90
114
 
91
- # Append new rules to rules.md
92
- echo "" >> "$RULES_FILE" 2>/dev/null || true
93
- mkdir -p "$(dirname "$RULES_FILE")"
94
- {
95
- if [ ! -f "$RULES_FILE" ]; then
96
- echo "# Learned Rules"
97
- echo ""
98
- echo "Rules extracted from session observations by Mulahazah."
99
- echo "Remove any rule that causes problems. Keep what helps."
100
- echo ""
101
- echo "---"
102
- echo ""
115
+ # ---------------------------------------------------------------------------
116
+ # Write instinct YAML files
117
+ # ---------------------------------------------------------------------------
118
+ mkdir -p "$PROJECT_DIR"
119
+
120
+ NEW_COUNT=0
121
+ while IFS= read -r -d '' block; do
122
+ [[ -z "$block" ]] && continue
123
+ # Extract id from the block
124
+ INSTINCT_ID=$(echo "$block" | grep -oP '(?<=^id: ).*' | head -1 | tr -d '"' | tr -d "'")
125
+ if [[ -n "$INSTINCT_ID" ]]; then
126
+ DEST="${PROJECT_DIR}/${INSTINCT_ID}.yaml"
127
+ printf '%s\n' "$block" > "$DEST"
128
+ echo " + ${INSTINCT_ID} → ${DEST}"
129
+ NEW_COUNT=$((NEW_COUNT + 1))
103
130
  fi
104
- echo "## $(date +%Y-%m-%d) analysis"
105
- echo ""
106
- echo "$RESULT"
107
- echo ""
108
- } >> "$RULES_FILE"
109
-
110
- # Count new rules
111
- NEW_COUNT=$(echo "$RESULT" | grep -c "^- " || true)
112
- echo ""
113
- echo "Added $NEW_COUNT new rules to $RULES_FILE"
131
+ done < <(printf '%s\0' "$RESULT" | sed 's/\n---\n/\x00/g')
132
+
133
+ # Fallback: if the splitting didn't work, try line-based parsing
134
+ if (( NEW_COUNT == 0 )); then
135
+ # Try splitting on --- delimiter
136
+ INSTINCT_ID=""
137
+ BLOCK=""
138
+ while IFS= read -r line; do
139
+ if [[ "$line" == "---" ]] && [[ -n "$BLOCK" ]]; then
140
+ if [[ -n "$INSTINCT_ID" ]]; then
141
+ DEST="${PROJECT_DIR}/${INSTINCT_ID}.yaml"
142
+ printf '%s\n' "$BLOCK" > "$DEST"
143
+ echo " + ${INSTINCT_ID} → ${DEST}"
144
+ NEW_COUNT=$((NEW_COUNT + 1))
145
+ fi
146
+ INSTINCT_ID=""
147
+ BLOCK=""
148
+ else
149
+ BLOCK="${BLOCK}${line}"$'\n'
150
+ if [[ "$line" =~ ^id:\ (.+) ]]; then
151
+ INSTINCT_ID="${BASH_REMATCH[1]}"
152
+ INSTINCT_ID="${INSTINCT_ID//\"/}"
153
+ INSTINCT_ID="${INSTINCT_ID//\'/}"
154
+ fi
155
+ fi
156
+ done <<< "$RESULT"
157
+ # Handle last block
158
+ if [[ -n "$INSTINCT_ID" ]] && [[ -n "$BLOCK" ]]; then
159
+ DEST="${PROJECT_DIR}/${INSTINCT_ID}.yaml"
160
+ printf '%s\n' "$BLOCK" > "$DEST"
161
+ echo " + ${INSTINCT_ID} → ${DEST}"
162
+ NEW_COUNT=$((NEW_COUNT + 1))
163
+ fi
164
+ fi
165
+
114
166
  echo ""
115
- echo "$RESULT"
167
+ echo "Created ${NEW_COUNT} new instinct(s) in ${PROJECT_DIR}/"
package/bin/install.mjs CHANGED
@@ -169,23 +169,82 @@ function patchClaudeSettings(observePath) {
169
169
 
170
170
  function uninstallAll() {
171
171
  console.log("\nUninstalling continuous-improvement skill...\n");
172
+ const home = homedir();
172
173
  let removed = 0;
174
+
175
+ // 1. Remove skill files from all targets
173
176
  for (const [key, target] of Object.entries(TARGETS)) {
174
177
  const skillFile = join(target.dir, "SKILL.md");
175
178
  if (existsSync(skillFile)) {
176
179
  try {
177
180
  rmSync(target.dir, { recursive: true });
178
- console.log(` ✓ Removed from ${target.label}`);
181
+ console.log(` ✓ Removed skill from ${target.label}`);
179
182
  removed++;
180
183
  } catch (err) {
181
184
  console.error(` ✗ ${target.label}: ${err.message}`);
182
185
  }
183
186
  }
184
187
  }
188
+
189
+ // 2. Remove /continuous-improvement command
190
+ const cmdFile = join(home, ".claude", "commands", "continuous-improvement.md");
191
+ if (existsSync(cmdFile)) {
192
+ try {
193
+ rmSync(cmdFile);
194
+ console.log(` ✓ Removed /continuous-improvement command`);
195
+ } catch (err) {
196
+ console.error(` ✗ Command file: ${err.message}`);
197
+ }
198
+ }
199
+
200
+ // 3. Remove observe.sh from instincts dir
201
+ const observeFile = join(home, ".claude", "instincts", "observe.sh");
202
+ if (existsSync(observeFile)) {
203
+ try {
204
+ rmSync(observeFile);
205
+ console.log(` ✓ Removed observe.sh`);
206
+ } catch (err) {
207
+ console.error(` ✗ observe.sh: ${err.message}`);
208
+ }
209
+ }
210
+
211
+ // 4. Remove hooks from settings.json
212
+ const settingsPath = join(home, ".claude", "settings.json");
213
+ if (existsSync(settingsPath)) {
214
+ try {
215
+ const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
216
+ let changed = false;
217
+ for (const hookType of ["PreToolUse", "PostToolUse"]) {
218
+ if (Array.isArray(settings.hooks?.[hookType])) {
219
+ const before = settings.hooks[hookType].length;
220
+ settings.hooks[hookType] = settings.hooks[hookType].filter(
221
+ (h) =>
222
+ !(
223
+ Array.isArray(h.hooks) &&
224
+ h.hooks.some(
225
+ (hh) => hh.command && hh.command.includes("observe.sh")
226
+ )
227
+ )
228
+ );
229
+ if (settings.hooks[hookType].length < before) changed = true;
230
+ }
231
+ }
232
+ if (changed) {
233
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
234
+ console.log(` ✓ Removed hooks from settings.json`);
235
+ }
236
+ } catch {
237
+ console.warn(` ! Could not clean settings.json — remove hooks manually`);
238
+ }
239
+ }
240
+
185
241
  if (removed === 0) {
186
- console.log(" No installations found.");
242
+ console.log(" No skill installations found.");
187
243
  }
188
- console.log();
244
+ console.log(
245
+ "\n Note: Instinct data in ~/.claude/instincts/ was preserved.\n" +
246
+ " To remove learned data too: rm -rf ~/.claude/instincts/\n"
247
+ );
189
248
  }
190
249
 
191
250
  function printUsage() {
@@ -207,12 +266,18 @@ Examples:
207
266
  // --- Main ---
208
267
 
209
268
  const args = process.argv.slice(2);
269
+ const command = args[0];
210
270
 
211
271
  if (args.includes("--help") || args.includes("-h")) {
212
272
  printUsage();
213
273
  process.exit(0);
214
274
  }
215
275
 
276
+ if (!command || !["install", "--help", "-h", "--uninstall"].includes(command)) {
277
+ printUsage();
278
+ process.exit(command ? 1 : 0);
279
+ }
280
+
216
281
  if (args.includes("--uninstall")) {
217
282
  uninstallAll();
218
283
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "continuous-improvement",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "7-law discipline framework with auto-leveling instinct learning for AI agents — research, plan, execute, verify, reflect, learn, iterate",
5
5
  "keywords": [
6
6
  "ai-agent",