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 +1 -1
- package/bin/analyze.sh +120 -68
- package/bin/install.mjs +68 -3
- package/package.json +1 -1
package/README.md
CHANGED
package/bin/analyze.sh
CHANGED
|
@@ -1,115 +1,167 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# analyze.sh — Read observations.jsonl, detect patterns,
|
|
3
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
if
|
|
16
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
if [
|
|
24
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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
|
|
38
|
-
echo "Need at least
|
|
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
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
#
|
|
49
|
-
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Take last 500 observations
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
RECENT_OBS=$(tail -500 "$OBS_FILE")
|
|
50
57
|
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
51
59
|
# Build analysis prompt
|
|
52
|
-
|
|
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
|
|
58
|
-
${
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
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"
|
|
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
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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 "$
|
|
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.
|
|
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",
|