continuous-improvement 1.0.0
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/LICENSE +21 -0
- package/README.md +175 -0
- package/agents/observer-loop.sh +282 -0
- package/agents/observer.md +145 -0
- package/agents/start-observer.sh +115 -0
- package/config.json +9 -0
- package/docs/failure-taxonomy.md +153 -0
- package/docs/integration-guide.md +105 -0
- package/docs/philosophy.md +127 -0
- package/docs/superpowers/plans/2026-04-05-mulahazah-implementation.md +1666 -0
- package/docs/superpowers/specs/2026-04-05-mulahazah-instinct-learning-design.md +636 -0
- package/hooks/observe.sh +133 -0
- package/package.json +23 -0
- package/prompts/coding-agent.md +67 -0
- package/prompts/core.md +115 -0
- package/prompts/minimal.md +17 -0
- package/prompts/product-agent.md +59 -0
- package/prompts/research-agent.md +59 -0
- package/scripts/install.js +433 -0
- package/skills/continuous-improvement/SKILL.md +111 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Naim Katiman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# continuous-improvement
|
|
2
|
+
|
|
3
|
+
### Install discipline into your AI agent in one command.
|
|
4
|
+
|
|
5
|
+
AI agents are useful. They're also reckless by default.
|
|
6
|
+
|
|
7
|
+
**continuous-improvement** makes an agent research first, plan before coding, verify before saying "done", and reflect so it stops repeating the same mistakes.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx continuous-improvement install
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If auto-detect misses your setup:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx continuous-improvement install --claude
|
|
19
|
+
npx continuous-improvement install --codex
|
|
20
|
+
npx continuous-improvement install --cursor
|
|
21
|
+
npx continuous-improvement install --openclaw
|
|
22
|
+
npx continuous-improvement install --chatgpt
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Optional global Claude install:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx continuous-improvement install --claude --global
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Uninstall
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx continuous-improvement uninstall --claude
|
|
35
|
+
npx continuous-improvement uninstall --codex
|
|
36
|
+
npx continuous-improvement uninstall --cursor
|
|
37
|
+
npx continuous-improvement uninstall --openclaw
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## What it installs
|
|
41
|
+
|
|
42
|
+
Depending on your target, it does one of these:
|
|
43
|
+
|
|
44
|
+
- **Claude Code** → appends the rules to `CLAUDE.md`
|
|
45
|
+
- **Codex / OpenClaw AGENTS flow** → appends the rules to `AGENTS.md`
|
|
46
|
+
- **Cursor** → appends the rules to `.cursorrules`
|
|
47
|
+
- **OpenClaw** → installs the local skill at `~/.openclaw/skills/continuous-improvement/`
|
|
48
|
+
- **ChatGPT** → prints the exact block to paste into Custom Instructions
|
|
49
|
+
|
|
50
|
+
## Use
|
|
51
|
+
|
|
52
|
+
One-shot usage is simple:
|
|
53
|
+
|
|
54
|
+
> Use continuous-improvement on this task: add a caching layer to my single-server API.
|
|
55
|
+
|
|
56
|
+
A good response should come back with:
|
|
57
|
+
|
|
58
|
+
1. **Research** — what exists, constraints, risks, simplest path
|
|
59
|
+
2. **Plan** — what will be built
|
|
60
|
+
3. **Anti-scope** — what will not be built
|
|
61
|
+
4. **Verification** — exact checks
|
|
62
|
+
5. **Fallback** — what to do if it fails
|
|
63
|
+
6. **Reflection** — what to learn after execution
|
|
64
|
+
|
|
65
|
+
## The 7 rules
|
|
66
|
+
|
|
67
|
+
1. **Research before executing**
|
|
68
|
+
2. **Plan before coding**
|
|
69
|
+
3. **Do one thing at a time**
|
|
70
|
+
4. **Verify before reporting**
|
|
71
|
+
5. **Reflect after non-trivial work**
|
|
72
|
+
6. **Iterate one change at a time**
|
|
73
|
+
7. **Learn from every session**
|
|
74
|
+
|
|
75
|
+
## What's new in v1.0: Mulahazah
|
|
76
|
+
|
|
77
|
+
Mulahazah (Arabic: observation) adds instinct-based learning to the loop.
|
|
78
|
+
|
|
79
|
+
Agents don't just follow rules — they build instincts over time.
|
|
80
|
+
|
|
81
|
+
**Key features:**
|
|
82
|
+
|
|
83
|
+
- Hooks observe every tool call (<50ms overhead)
|
|
84
|
+
- Instincts carry confidence scores (0.3–0.9 range)
|
|
85
|
+
- Graduated behavior: silent at low confidence, suggest at mid, auto-apply at high
|
|
86
|
+
- Project scoping: instincts can be global or project-specific
|
|
87
|
+
- Confidence decay: unused instincts weaken, wrong ones get corrected
|
|
88
|
+
|
|
89
|
+
**One command:**
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
/continuous-improvement
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Background observer (optional):**
|
|
96
|
+
|
|
97
|
+
A lightweight Haiku agent watches sessions and extracts patterns automatically.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
~/.claude/mulahazah/agents/start-observer.sh
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Why this exists
|
|
104
|
+
|
|
105
|
+
Agents usually fail in predictable ways:
|
|
106
|
+
|
|
107
|
+
- they skip docs and existing code
|
|
108
|
+
- they overbuild
|
|
109
|
+
- they claim success before testing
|
|
110
|
+
- they pile on changes before verifying the first one
|
|
111
|
+
- they repeat mistakes because nothing gets logged
|
|
112
|
+
|
|
113
|
+
continuous-improvement fixes that with a tiny, reusable operating loop:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
Research → Plan → Execute (one thing) → Verify → Reflect → Learn → Iterate
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Skill first
|
|
120
|
+
|
|
121
|
+
This repo is designed as a **skill first** product:
|
|
122
|
+
|
|
123
|
+
- easy to install
|
|
124
|
+
- easy to invoke in one shot
|
|
125
|
+
- useful before any complex task
|
|
126
|
+
|
|
127
|
+
Prompt variants in `prompts/` are still here, but they are **supporting material**, not the main product.
|
|
128
|
+
|
|
129
|
+
The OpenClaw skill lives in:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
skills/continuous-improvement/SKILL.md
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Testing
|
|
136
|
+
|
|
137
|
+
Use these files to test whether the install and behavior are actually good:
|
|
138
|
+
|
|
139
|
+
- `tests/install-checklist.md`
|
|
140
|
+
- `tests/official-evals.md`
|
|
141
|
+
- `tests/prompt-eval.md`
|
|
142
|
+
- `examples/real-test-cases.md`
|
|
143
|
+
- `.github/ISSUE_TEMPLATE/test-report.yml`
|
|
144
|
+
|
|
145
|
+
Best practice: have multiple people test across Claude Code, Codex, Cursor, OpenClaw, and ChatGPT, then submit structured reports instead of random comments.
|
|
146
|
+
|
|
147
|
+
## Fallback: manual install
|
|
148
|
+
|
|
149
|
+
If you do not want the installer, copy the right block manually:
|
|
150
|
+
|
|
151
|
+
- `prompts/coding-agent.md`
|
|
152
|
+
- `prompts/core.md`
|
|
153
|
+
- `prompts/minimal.md`
|
|
154
|
+
- `skills/continuous-improvement/SKILL.md`
|
|
155
|
+
|
|
156
|
+
More setup detail lives in `docs/integration-guide.md`.
|
|
157
|
+
|
|
158
|
+
## Quick reality check
|
|
159
|
+
|
|
160
|
+
If the agent still jumps straight into writing 300 lines without:
|
|
161
|
+
|
|
162
|
+
- checking what already exists
|
|
163
|
+
- defining anti-scope
|
|
164
|
+
- stating how it will verify success
|
|
165
|
+
- and it doesn't learn from corrections
|
|
166
|
+
|
|
167
|
+
then continuous-improvement is not installed properly.
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
Keep it sharp.
|
|
172
|
+
Keep it practical.
|
|
173
|
+
Kill anything that adds friction.
|
|
174
|
+
|
|
175
|
+
MIT.
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# observer-loop.sh — Mulahazah background observer loop
|
|
3
|
+
# Periodically analyzes observation logs and generates instincts via Haiku.
|
|
4
|
+
# Started by start-observer.sh. Should not be invoked directly.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
MULAHAZAH_DIR="${HOME}/.claude/mulahazah"
|
|
9
|
+
CONFIG_FILE="${MULAHAZAH_DIR}/config.json"
|
|
10
|
+
PROJECTS_DIR="${MULAHAZAH_DIR}/projects"
|
|
11
|
+
INSTINCTS_DIR="${MULAHAZAH_DIR}/instincts"
|
|
12
|
+
OBSERVER_PROMPT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/observer.md"
|
|
13
|
+
LOG_FILE="${MULAHAZAH_DIR}/observer.log"
|
|
14
|
+
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
# Logging
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
log() {
|
|
19
|
+
local level="$1"; shift
|
|
20
|
+
printf '[%s] [%s] %s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$level" "$*" >> "$LOG_FILE"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Read config values (with defaults)
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
read_config() {
|
|
27
|
+
local key="$1"
|
|
28
|
+
local default="$2"
|
|
29
|
+
if [[ -f "$CONFIG_FILE" ]]; then
|
|
30
|
+
local val
|
|
31
|
+
val="$(jq -r "${key} // empty" "$CONFIG_FILE" 2>/dev/null || true)"
|
|
32
|
+
if [[ -n "$val" && "$val" != "null" ]]; then
|
|
33
|
+
printf '%s' "$val"
|
|
34
|
+
return
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
printf '%s' "$default"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Signal handling
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
FORCE_RUN=false
|
|
44
|
+
SHUTDOWN=false
|
|
45
|
+
|
|
46
|
+
handle_sigterm() {
|
|
47
|
+
log INFO "Received SIGTERM — shutting down gracefully"
|
|
48
|
+
SHUTDOWN=true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
handle_sigusr1() {
|
|
52
|
+
log INFO "Received SIGUSR1 — forcing immediate analysis run"
|
|
53
|
+
FORCE_RUN=true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
trap handle_sigterm SIGTERM
|
|
57
|
+
trap handle_sigusr1 SIGUSR1
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Analyze a single project directory
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
analyze_project() {
|
|
63
|
+
local project_dir="$1"
|
|
64
|
+
local obs_file="${project_dir}/observations.jsonl"
|
|
65
|
+
local project_json="${project_dir}/project.json"
|
|
66
|
+
|
|
67
|
+
[[ -f "$obs_file" ]] || return 0
|
|
68
|
+
|
|
69
|
+
local obs_count
|
|
70
|
+
obs_count="$(wc -l < "$obs_file" 2>/dev/null || echo 0)"
|
|
71
|
+
|
|
72
|
+
local min_obs
|
|
73
|
+
min_obs="$(read_config '.observer.min_observations_to_analyze' '20')"
|
|
74
|
+
|
|
75
|
+
if (( obs_count < min_obs )); then
|
|
76
|
+
log DEBUG "Skipping ${project_dir} — only ${obs_count} observations (min: ${min_obs})"
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
local project_id project_name
|
|
81
|
+
project_id="$(basename "$project_dir")"
|
|
82
|
+
project_name="$(jq -r '.name // "unknown"' "$project_json" 2>/dev/null || echo "unknown")"
|
|
83
|
+
|
|
84
|
+
log INFO "Analyzing project '${project_name}' (${project_id}) — ${obs_count} observations"
|
|
85
|
+
|
|
86
|
+
# Build the prompt payload for claude
|
|
87
|
+
local prompt
|
|
88
|
+
prompt="$(cat <<PROMPT
|
|
89
|
+
You are the Mulahazah observer agent. Analyze the following observation data and existing instincts.
|
|
90
|
+
|
|
91
|
+
## Project
|
|
92
|
+
ID: ${project_id}
|
|
93
|
+
Name: ${project_name}
|
|
94
|
+
|
|
95
|
+
## Observations (last 500 lines of observations.jsonl)
|
|
96
|
+
$(tail -500 "$obs_file" 2>/dev/null || true)
|
|
97
|
+
|
|
98
|
+
## Existing Instincts
|
|
99
|
+
$(ls "${INSTINCTS_DIR}/${project_id}/"*.yaml 2>/dev/null | xargs -I{} cat {} 2>/dev/null || echo "(none)")
|
|
100
|
+
|
|
101
|
+
## Global Instincts
|
|
102
|
+
$(ls "${INSTINCTS_DIR}/global/"*.yaml 2>/dev/null | xargs -I{} cat {} 2>/dev/null || echo "(none)")
|
|
103
|
+
|
|
104
|
+
Follow the instructions in your system prompt. Output only YAML instinct blocks.
|
|
105
|
+
PROMPT
|
|
106
|
+
)"
|
|
107
|
+
|
|
108
|
+
# Run claude with observer.md as the system prompt
|
|
109
|
+
local output
|
|
110
|
+
output="$(printf '%s' "$prompt" | \
|
|
111
|
+
claude --model haiku --print --system-prompt "$OBSERVER_PROMPT" 2>>"$LOG_FILE" || true)"
|
|
112
|
+
|
|
113
|
+
if [[ -z "$output" ]]; then
|
|
114
|
+
log WARN "No output from observer for project '${project_name}'"
|
|
115
|
+
return 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Write instincts to disk
|
|
119
|
+
write_instincts "$output" "$project_id"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
# Analyze global observations
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
analyze_global() {
|
|
126
|
+
local global_dir="${PROJECTS_DIR}/global"
|
|
127
|
+
local obs_file="${global_dir}/observations.jsonl"
|
|
128
|
+
|
|
129
|
+
[[ -f "$obs_file" ]] || return 0
|
|
130
|
+
|
|
131
|
+
local obs_count
|
|
132
|
+
obs_count="$(wc -l < "$obs_file" 2>/dev/null || echo 0)"
|
|
133
|
+
|
|
134
|
+
local min_obs
|
|
135
|
+
min_obs="$(read_config '.observer.min_observations_to_analyze' '20')"
|
|
136
|
+
|
|
137
|
+
if (( obs_count < min_obs )); then
|
|
138
|
+
log DEBUG "Skipping global observations — only ${obs_count} lines (min: ${min_obs})"
|
|
139
|
+
return 0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
log INFO "Analyzing global observations — ${obs_count} lines"
|
|
143
|
+
|
|
144
|
+
local prompt
|
|
145
|
+
prompt="$(cat <<PROMPT
|
|
146
|
+
You are the Mulahazah observer agent. Analyze the following global observation data.
|
|
147
|
+
|
|
148
|
+
## Global Observations (last 500 lines)
|
|
149
|
+
$(tail -500 "$obs_file" 2>/dev/null || true)
|
|
150
|
+
|
|
151
|
+
## Existing Global Instincts
|
|
152
|
+
$(ls "${INSTINCTS_DIR}/global/"*.yaml 2>/dev/null | xargs -I{} cat {} 2>/dev/null || echo "(none)")
|
|
153
|
+
|
|
154
|
+
Follow the instructions in your system prompt. Output only YAML instinct blocks with scope: global.
|
|
155
|
+
PROMPT
|
|
156
|
+
)"
|
|
157
|
+
|
|
158
|
+
local output
|
|
159
|
+
output="$(printf '%s' "$prompt" | \
|
|
160
|
+
claude --model haiku --print --system-prompt "$OBSERVER_PROMPT" 2>>"$LOG_FILE" || true)"
|
|
161
|
+
|
|
162
|
+
if [[ -z "$output" ]]; then
|
|
163
|
+
log WARN "No output from observer for global observations"
|
|
164
|
+
return 0
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
write_instincts "$output" "global"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# ---------------------------------------------------------------------------
|
|
171
|
+
# Parse and write instinct YAML blocks to disk
|
|
172
|
+
# ---------------------------------------------------------------------------
|
|
173
|
+
write_instincts() {
|
|
174
|
+
local yaml_output="$1"
|
|
175
|
+
local project_id="$2"
|
|
176
|
+
|
|
177
|
+
# Split on --- separators and process each block
|
|
178
|
+
local instinct_dir="${INSTINCTS_DIR}/${project_id}"
|
|
179
|
+
mkdir -p "$instinct_dir"
|
|
180
|
+
|
|
181
|
+
# Write raw output to a temp file, then split by ---
|
|
182
|
+
local tmpfile
|
|
183
|
+
tmpfile="$(mktemp)"
|
|
184
|
+
printf '%s' "$yaml_output" > "$tmpfile"
|
|
185
|
+
|
|
186
|
+
# Use awk to split YAML documents on '---' separator
|
|
187
|
+
awk 'BEGIN{n=0; block=""} /^---$/{if(block!=""){print block > "/tmp/mulahazah_instinct_"n".yaml"; n++; block=""}} !/^---$/{block=block"\n"$0} END{if(block!=""){print block > "/tmp/mulahazah_instinct_"n".yaml"}}' "$tmpfile"
|
|
188
|
+
|
|
189
|
+
local written=0
|
|
190
|
+
for instinct_file in /tmp/mulahazah_instinct_*.yaml; do
|
|
191
|
+
[[ -f "$instinct_file" ]] || continue
|
|
192
|
+
|
|
193
|
+
# Extract the instinct id
|
|
194
|
+
local instinct_id
|
|
195
|
+
instinct_id="$(grep -m1 '^id:' "$instinct_file" | sed 's/^id: *//' | tr -d '"' | tr -d "'" | xargs 2>/dev/null || true)"
|
|
196
|
+
|
|
197
|
+
if [[ -z "$instinct_id" ]]; then
|
|
198
|
+
log WARN "Skipping instinct block with no id"
|
|
199
|
+
rm -f "$instinct_file"
|
|
200
|
+
continue
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
local dest="${instinct_dir}/${instinct_id}.yaml"
|
|
204
|
+
mv "$instinct_file" "$dest"
|
|
205
|
+
log INFO "Wrote instinct '${instinct_id}' to ${dest}"
|
|
206
|
+
(( written++ )) || true
|
|
207
|
+
done
|
|
208
|
+
|
|
209
|
+
# Clean up any leftover temp files
|
|
210
|
+
rm -f /tmp/mulahazah_instinct_*.yaml "$tmpfile"
|
|
211
|
+
|
|
212
|
+
log INFO "Wrote ${written} instincts for project '${project_id}'"
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# ---------------------------------------------------------------------------
|
|
216
|
+
# Main loop
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
main() {
|
|
219
|
+
log INFO "Mulahazah observer loop started (PID $$)"
|
|
220
|
+
|
|
221
|
+
# Validate dependencies
|
|
222
|
+
command -v jq &>/dev/null || { log ERROR "jq not found — observer cannot run"; exit 1; }
|
|
223
|
+
command -v claude &>/dev/null || { log ERROR "claude CLI not found — observer cannot run"; exit 1; }
|
|
224
|
+
[[ -f "$OBSERVER_PROMPT" ]] || { log ERROR "observer.md not found at ${OBSERVER_PROMPT}"; exit 1; }
|
|
225
|
+
|
|
226
|
+
# Ensure instincts directory exists
|
|
227
|
+
mkdir -p "${INSTINCTS_DIR}/global"
|
|
228
|
+
|
|
229
|
+
while true; do
|
|
230
|
+
# Check if observer is enabled
|
|
231
|
+
local enabled
|
|
232
|
+
enabled="$(read_config '.observer.enabled' 'true')"
|
|
233
|
+
if [[ "$enabled" != "true" ]]; then
|
|
234
|
+
log INFO "Observer is disabled in config — sleeping"
|
|
235
|
+
sleep 60
|
|
236
|
+
[[ "$SHUTDOWN" == "true" ]] && break
|
|
237
|
+
continue
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
if [[ "$FORCE_RUN" == "true" || "$SHUTDOWN" == "false" ]]; then
|
|
241
|
+
FORCE_RUN=false
|
|
242
|
+
log INFO "Starting analysis run"
|
|
243
|
+
|
|
244
|
+
# Analyze each project directory
|
|
245
|
+
if [[ -d "$PROJECTS_DIR" ]]; then
|
|
246
|
+
for project_dir in "${PROJECTS_DIR}"/*/; do
|
|
247
|
+
[[ -d "$project_dir" ]] || continue
|
|
248
|
+
[[ "$(basename "$project_dir")" == "global" ]] && continue
|
|
249
|
+
analyze_project "$project_dir" || log WARN "Analysis failed for ${project_dir}"
|
|
250
|
+
[[ "$SHUTDOWN" == "true" ]] && break
|
|
251
|
+
done
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
# Analyze global observations
|
|
255
|
+
analyze_global || log WARN "Global analysis failed"
|
|
256
|
+
|
|
257
|
+
log INFO "Analysis run complete"
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
[[ "$SHUTDOWN" == "true" ]] && break
|
|
261
|
+
|
|
262
|
+
# Sleep for the configured interval
|
|
263
|
+
local interval_minutes
|
|
264
|
+
interval_minutes="$(read_config '.observer.run_interval_minutes' '5')"
|
|
265
|
+
local interval_seconds=$(( interval_minutes * 60 ))
|
|
266
|
+
|
|
267
|
+
log DEBUG "Sleeping for ${interval_minutes} minutes"
|
|
268
|
+
|
|
269
|
+
# Sleep in 1-second chunks to remain responsive to signals
|
|
270
|
+
local elapsed=0
|
|
271
|
+
while (( elapsed < interval_seconds )); do
|
|
272
|
+
sleep 1
|
|
273
|
+
(( elapsed++ )) || true
|
|
274
|
+
[[ "$SHUTDOWN" == "true" ]] && break
|
|
275
|
+
[[ "$FORCE_RUN" == "true" ]] && break
|
|
276
|
+
done
|
|
277
|
+
done
|
|
278
|
+
|
|
279
|
+
log INFO "Mulahazah observer loop exiting"
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
main "$@"
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mulahazah-observer
|
|
3
|
+
description: Background analysis agent that reads raw tool observations and distills them into actionable instincts. Runs on a Haiku model for cost efficiency. Identifies patterns, user preferences, and recurring workflows from JSONL observation logs.
|
|
4
|
+
model: haiku
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Mulahazah Observer — Background Analysis Agent
|
|
8
|
+
|
|
9
|
+
You are the Mulahazah observer. Your job is to analyze raw tool-use observations recorded by the `observe.sh` hook and extract reusable instincts that improve future Claude Code sessions.
|
|
10
|
+
|
|
11
|
+
You run in the background, periodically. You are cost-sensitive (Haiku model). Be concise and conservative.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Inputs
|
|
16
|
+
|
|
17
|
+
You will be given:
|
|
18
|
+
1. The path to `observations.jsonl` for a project (or global observations)
|
|
19
|
+
2. The path to existing instincts (YAML files in `~/.claude/mulahazah/instincts/`)
|
|
20
|
+
3. The project metadata from `project.json` (if available)
|
|
21
|
+
|
|
22
|
+
Read these files and analyze the patterns within.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Pattern Detection Rules
|
|
27
|
+
|
|
28
|
+
Scan observations for these signal types, in priority order:
|
|
29
|
+
|
|
30
|
+
### 1. User Corrections (highest signal)
|
|
31
|
+
- A tool call was made, then immediately followed by an Edit or Write that undoes or modifies what was just produced
|
|
32
|
+
- The same tool is called with a different argument within the same session after an error
|
|
33
|
+
- A `Bash` command fails (non-zero exit in output) and is retried with a modified form
|
|
34
|
+
|
|
35
|
+
### 2. Error Resolutions
|
|
36
|
+
- A tool produces an error, followed by a sequence of tools that resolves it
|
|
37
|
+
- The resolution sequence is compact (3–7 tool calls) and clearly purposeful
|
|
38
|
+
- Extract the resolution pattern as a workflow instinct
|
|
39
|
+
|
|
40
|
+
### 3. Repeated Workflows
|
|
41
|
+
- The same sequence of 3+ tool calls appears in 3+ sessions
|
|
42
|
+
- Order matters — a repeated sequence is only a pattern if the tools appear in the same relative order
|
|
43
|
+
- Common examples: `Bash(git status)` → `Bash(git diff)` → `Bash(git commit)`, or `Read` → `Edit` → `Bash(npm run build)`
|
|
44
|
+
|
|
45
|
+
### 4. Tool Preferences
|
|
46
|
+
- User consistently uses one tool over a functionally equivalent alternative
|
|
47
|
+
- Example: always uses `Bash(rg ...)` via the Grep tool rather than raw `Bash(grep ...)`
|
|
48
|
+
- Example: always uses `Edit` for single-file changes, never `Write` on existing files
|
|
49
|
+
- Capture these as style or workflow instincts
|
|
50
|
+
|
|
51
|
+
### 5. Rejected Suggestions
|
|
52
|
+
- A tool call produces output, session ends shortly after without using the output
|
|
53
|
+
- Or an Edit is immediately reverted in the next tool call
|
|
54
|
+
- These indicate something to avoid — create a negative instinct (what not to do)
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Scope Decision Guide
|
|
59
|
+
|
|
60
|
+
Assign scope based on these rules:
|
|
61
|
+
|
|
62
|
+
| Condition | Scope |
|
|
63
|
+
|-----------|-------|
|
|
64
|
+
| Pattern appears in only one project's observations | `project` |
|
|
65
|
+
| Pattern appears in 3+ different projects | `global` |
|
|
66
|
+
| Pattern involves language/framework-specific behavior | `project` (unless 3+ projects use same stack) |
|
|
67
|
+
| Pattern involves user meta-habits (git, file editing, tool choice) | `global` |
|
|
68
|
+
| Pattern involves project naming, directory structure, specific paths | `project` |
|
|
69
|
+
| Uncertain | default to `project` |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Instinct YAML Format
|
|
74
|
+
|
|
75
|
+
Output each new instinct as a YAML block. Do not wrap in markdown code fences — output raw YAML only, one instinct per file.
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
id: <kebab-case-id>
|
|
79
|
+
title: <short human-readable title, max 60 chars>
|
|
80
|
+
scope: project | global
|
|
81
|
+
project_id: <12-char hash if scope=project, omit if global>
|
|
82
|
+
domain: <one of: code-style, testing, git, debugging, workflow, security, architecture>
|
|
83
|
+
confidence: <float 0.0–0.85>
|
|
84
|
+
observation_count: <number of observations supporting this instinct>
|
|
85
|
+
last_seen: <ISO 8601 date>
|
|
86
|
+
content: |
|
|
87
|
+
<The instinct text. Written as a direct instruction to Claude.
|
|
88
|
+
Max 5 sentences. No raw code snippets. No file paths unless abstract.
|
|
89
|
+
Use imperative voice. Example: "When editing TypeScript files, always
|
|
90
|
+
check for existing type aliases before creating new ones.">
|
|
91
|
+
tags:
|
|
92
|
+
- <tag1>
|
|
93
|
+
- <tag2>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Confidence Rules
|
|
99
|
+
|
|
100
|
+
- **Never set confidence above 0.85** from observation data alone. Human review is required to reach 0.9.
|
|
101
|
+
- **Confidence cap is 0.9** — no instinct may ever exceed this value.
|
|
102
|
+
- Start new instincts at confidence 0.4–0.6 based on evidence strength:
|
|
103
|
+
- 3–5 supporting observations: 0.4
|
|
104
|
+
- 6–10 supporting observations: 0.55
|
|
105
|
+
- 11–20 supporting observations: 0.65
|
|
106
|
+
- 21+ supporting observations: 0.75
|
|
107
|
+
- Strong signal (user correction or error resolution): add 0.1 bonus, capped at 0.85
|
|
108
|
+
- **Decay rules** — reduce confidence by 0.05 if:
|
|
109
|
+
- The instinct was not observed in the last 30 days
|
|
110
|
+
- The instinct was observed but then contradicted (a counter-example appeared)
|
|
111
|
+
- The session count for the project drops to zero for 60+ days
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Domain Tags
|
|
116
|
+
|
|
117
|
+
Use exactly one domain per instinct:
|
|
118
|
+
|
|
119
|
+
| Domain | Covers |
|
|
120
|
+
|--------|--------|
|
|
121
|
+
| `code-style` | Formatting, naming, language idioms, linting preferences |
|
|
122
|
+
| `testing` | Test frameworks, coverage, test file conventions |
|
|
123
|
+
| `git` | Commit messages, branch naming, staging habits |
|
|
124
|
+
| `debugging` | Error resolution sequences, diagnostic tool preferences |
|
|
125
|
+
| `workflow` | Multi-step task sequences, tool ordering preferences |
|
|
126
|
+
| `security` | Auth patterns, secret handling, input validation |
|
|
127
|
+
| `architecture` | File structure, module boundaries, design patterns |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Observer Rules
|
|
132
|
+
|
|
133
|
+
1. **Be conservative.** It is better to produce no instinct than a wrong instinct. Only emit an instinct if you have clear, repeated evidence.
|
|
134
|
+
|
|
135
|
+
2. **Merge similar instincts.** If a new pattern is substantially similar to an existing instinct (same domain, same behavior), increase `observation_count` and update `confidence` on the existing instinct rather than creating a duplicate.
|
|
136
|
+
|
|
137
|
+
3. **Default to project scope.** When in doubt about scope, use `project`. Promotion to global scope should only happen when the same pattern is confirmed across multiple distinct projects.
|
|
138
|
+
|
|
139
|
+
4. **No raw code in instinct content.** Instinct content must be natural language instructions. Never embed shell commands, code snippets, or file paths in the `content` field. Use abstract descriptions instead.
|
|
140
|
+
|
|
141
|
+
5. **No hallucinated patterns.** Only describe patterns you can trace to specific observation lines. If you cannot point to concrete evidence, do not emit the instinct.
|
|
142
|
+
|
|
143
|
+
6. **Respect existing instincts.** Before creating a new instinct, check the existing instinct files. Do not duplicate, do not contradict without strong evidence.
|
|
144
|
+
|
|
145
|
+
7. **Output only YAML.** Your output must be valid YAML instinct blocks (one per instinct) separated by `---`. Do not include explanatory text, markdown, or commentary in your output — only the YAML instincts ready to be written to disk.
|