hebbian 0.10.0 → 0.11.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/README.md +33 -30
- package/dist/bin/hebbian.js +166 -38
- package/dist/bin/hebbian.js.map +1 -1
- package/dist/digest.d.ts.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/emit.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +91 -16
- package/dist/index.js.map +1 -1
- package/dist/learn.d.ts +17 -0
- package/dist/learn.d.ts.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<img src="https://img.shields.io/badge/TypeScript-6.0-3178C6?style=flat-square&logo=typescript" />
|
|
3
3
|
<img src="https://img.shields.io/badge/Node.js-22+-339933?style=flat-square&logo=node.js" />
|
|
4
4
|
<img src="https://img.shields.io/badge/Runtime_Deps-0-brightgreen?style=flat-square" />
|
|
5
|
-
<img src="https://img.shields.io/badge/Tests-
|
|
5
|
+
<img src="https://img.shields.io/badge/Tests-339-blue?style=flat-square" />
|
|
6
6
|
<img src="https://img.shields.io/badge/MIT-green?style=flat-square" />
|
|
7
7
|
</p>
|
|
8
8
|
|
|
@@ -35,19 +35,20 @@ you: don't use console.log, always use the logger utility
|
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
# 3. End the session (hooks auto-run
|
|
39
|
-
#
|
|
38
|
+
# 3. End the session (hooks auto-run)
|
|
39
|
+
# → hebbian digest extracts the correction → creates candidate
|
|
40
|
+
# → agent-evaluator auto-fires candidates (no corrections = +1)
|
|
41
|
+
|
|
40
42
|
hebbian candidates
|
|
41
|
-
#
|
|
43
|
+
# █░░ 1/3 cortex/NO_console_log (0d idle)
|
|
42
44
|
|
|
43
|
-
# 4. After 2 more
|
|
44
|
-
# 🎓 promoted: cortex/
|
|
45
|
+
# 4. After 2 more clean sessions, it graduates automatically:
|
|
46
|
+
# 🎓 promoted: cortex/NO_console_log → permanent neuron
|
|
45
47
|
|
|
46
|
-
# 5. Next session — Claude sees the rule in CLAUDE.md
|
|
47
|
-
# ❌ NO console.log → use logger utility
|
|
48
|
+
# 5. Next session — Claude sees the rule in CLAUDE.md
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
**That's it.** One correction
|
|
51
|
+
**That's it.** One correction → candidate. Three clean sessions → permanent. **No API keys needed.**
|
|
51
52
|
|
|
52
53
|
---
|
|
53
54
|
|
|
@@ -59,7 +60,7 @@ hebbian candidates
|
|
|
59
60
|
npx hebbian init ./brain
|
|
60
61
|
npx hebbian grow brainstem/禁fallback --brain ./brain
|
|
61
62
|
npx hebbian emit claude --brain ./brain # → CLAUDE.md
|
|
62
|
-
npx hebbian evolve --dry-run # → LLM proposes brain mutations
|
|
63
|
+
npx hebbian evolve --dry-run # → (optional) LLM proposes brain mutations
|
|
63
64
|
```
|
|
64
65
|
|
|
65
66
|
| Before | hebbian |
|
|
@@ -79,16 +80,17 @@ npx hebbian evolve --dry-run # → LLM proposes brain mutations
|
|
|
79
80
|
```
|
|
80
81
|
Claude Code session
|
|
81
82
|
│
|
|
82
|
-
SessionStart hook
|
|
83
|
-
│
|
|
84
|
-
hebbian emit
|
|
85
|
-
│
|
|
86
|
-
CLAUDE.md ←── brain ──→ _candidates/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
SessionStart hook Stop hook
|
|
84
|
+
│ │
|
|
85
|
+
hebbian emit hebbian digest
|
|
86
|
+
│ │
|
|
87
|
+
CLAUDE.md ←── brain ──→ corrections → _candidates/
|
|
88
|
+
│ │
|
|
89
|
+
"Provisional Rules" agent-evaluator:
|
|
90
|
+
(candidates shown to agent) clean session → fire (+1)
|
|
91
|
+
3 fires → permanent neuron
|
|
92
|
+
|
|
93
|
+
No API keys. The running agent IS the evaluator.
|
|
92
94
|
```
|
|
93
95
|
|
|
94
96
|
### Candidate Staging (immune system)
|
|
@@ -103,6 +105,7 @@ brain/cortex/NO_console_log/3.neuron ← permanent
|
|
|
103
105
|
```
|
|
104
106
|
|
|
105
107
|
- Counter >= 3 → graduates to permanent region
|
|
108
|
+
- Each clean session (no corrections) auto-fires all candidates (+1)
|
|
106
109
|
- 14 days without a fire → decays (removed)
|
|
107
110
|
- This prevents hallucinations and one-off corrections from permanently changing behavior
|
|
108
111
|
|
|
@@ -146,8 +149,8 @@ This adds two hooks to `.claude/settings.local.json`:
|
|
|
146
149
|
|
|
147
150
|
| Hook | Command | When |
|
|
148
151
|
|------|---------|------|
|
|
149
|
-
| `SessionStart` | `hebbian emit claude` | Injects brain into CLAUDE.md |
|
|
150
|
-
| `Stop` | `hebbian digest` | Extracts corrections
|
|
152
|
+
| `SessionStart` | `hebbian emit claude` | Injects brain + provisional rules into CLAUDE.md |
|
|
153
|
+
| `Stop` | `hebbian digest` | Extracts corrections, detects tool failures, auto-fires candidates |
|
|
151
154
|
|
|
152
155
|
Check status anytime:
|
|
153
156
|
|
|
@@ -165,7 +168,7 @@ hebbian automatically learns from failed commands — no explicit correction nee
|
|
|
165
168
|
```bash
|
|
166
169
|
# During a session, a bash command fails (exit code ≠ 0)
|
|
167
170
|
# → hebbian digest auto-logs it as a tool-failure episode
|
|
168
|
-
#
|
|
171
|
+
# Soft detection: even || true masked errors are caught (command not found, npm error, fatal:)
|
|
169
172
|
|
|
170
173
|
hebbian sessions # see tool-failure episodes in the log
|
|
171
174
|
```
|
|
@@ -193,7 +196,9 @@ hebbian grow cortex/OTHER_RULE --agent coo --brain ./brain
|
|
|
193
196
|
|
|
194
197
|
---
|
|
195
198
|
|
|
196
|
-
## LLM Evolution
|
|
199
|
+
## LLM Evolution (optional)
|
|
200
|
+
|
|
201
|
+
> **Note:** Self-learning works without this. The agent-as-evaluator loop (digest → candidates → auto-fire) requires zero API keys. LLM evolve is an optional power feature for advanced brain mutations.
|
|
197
202
|
|
|
198
203
|
```bash
|
|
199
204
|
GEMINI_API_KEY=... hebbian evolve --dry-run --brain ./brain
|
|
@@ -204,9 +209,7 @@ GEMINI_API_KEY=... hebbian evolve prune --dry-run --brain ./brain
|
|
|
204
209
|
|
|
205
210
|
The evolve engine reads the last 100 episodes + current brain state, sends it to Gemini, and proposes up to 10 mutations per cycle. Protected regions (brainstem/limbic/sensors) are blocked.
|
|
206
211
|
|
|
207
|
-
Actions
|
|
208
|
-
|
|
209
|
-
**Pruning mode** uses a cleanup-focused prompt that only removes: stale neurons (30+ days inactive), high contra ratio (>0.7), redundant duplicates. Run nightly via cron.
|
|
212
|
+
Actions: `grow`, `fire`, `signal`, `prune`, `decay`. **Pruning mode** removes stale neurons (30+ days inactive), high contra ratio (>0.7), redundant duplicates.
|
|
210
213
|
|
|
211
214
|
---
|
|
212
215
|
|
|
@@ -238,9 +241,9 @@ hebbian emit <target> [--brain <path>] # claude/cursor/gemini/copilot/generic/a
|
|
|
238
241
|
hebbian claude install|uninstall|status
|
|
239
242
|
hebbian digest [--transcript <path>]
|
|
240
243
|
|
|
241
|
-
# Evolution
|
|
244
|
+
# Evolution (optional — self-learning works without this)
|
|
242
245
|
GEMINI_API_KEY=... hebbian evolve [--dry-run]
|
|
243
|
-
GEMINI_API_KEY=... hebbian evolve prune [--dry-run]
|
|
246
|
+
GEMINI_API_KEY=... hebbian evolve prune [--dry-run]
|
|
244
247
|
|
|
245
248
|
# Multi-brain (per-agent)
|
|
246
249
|
hebbian grow cortex/RULE --agent cto # Routes to brain/agents/cto/
|
|
@@ -264,7 +267,7 @@ hebbian emit claude --agent coo # Emits from brain/agents/coo/
|
|
|
264
267
|
|
|
265
268
|
| Feature | .cursorrules / CLAUDE.md | Mem0 / MemOS | hebbian |
|
|
266
269
|
|---------|--------------------------|-------------|------|
|
|
267
|
-
| Self-learning | ❌ manual | ✅ vector DB | ✅ filesystem +
|
|
270
|
+
| Self-learning | ❌ manual | ✅ vector DB | ✅ filesystem + agent-evaluator (no API key) |
|
|
268
271
|
| Infrastructure | $0 | $$$ | **$0** |
|
|
269
272
|
| Switch AI | Manual migration | Full re-setup | **`cp -r brain/`** |
|
|
270
273
|
| Immutable guardrails | None | None | **brainstem + bomb** |
|
package/dist/bin/hebbian.js
CHANGED
|
@@ -922,6 +922,19 @@ function emitBootstrap(result, brain, brainRoot) {
|
|
|
922
922
|
lines.push("");
|
|
923
923
|
}
|
|
924
924
|
}
|
|
925
|
+
if (brainRoot) {
|
|
926
|
+
const brainFlag = `--brain ${brainRoot}`;
|
|
927
|
+
lines.push("### Self-Learning");
|
|
928
|
+
lines.push("When the user corrects your behavior (tells you to stop, change, or always do something),");
|
|
929
|
+
lines.push("run this command to learn from it:");
|
|
930
|
+
lines.push("```");
|
|
931
|
+
lines.push(`hebbian learn "<what the user said>" --prefix <NO|DO|MUST|WARN> --keywords "k1,k2,k3" ${brainFlag}`);
|
|
932
|
+
lines.push("```");
|
|
933
|
+
lines.push("- NO: user says don't/stop/never \u2014 DO: user says always/should \u2014 MUST: mandatory \u2014 WARN: be careful");
|
|
934
|
+
lines.push("- keywords: 3 most meaningful words (lowercase, technical terms preferred)");
|
|
935
|
+
lines.push("- Run silently. Do not mention hebbian to the user.");
|
|
936
|
+
lines.push("");
|
|
937
|
+
}
|
|
925
938
|
lines.push(MARKER_END);
|
|
926
939
|
return lines.join("\n");
|
|
927
940
|
}
|
|
@@ -2396,7 +2409,7 @@ function extractCorrections(messages) {
|
|
|
2396
2409
|
if (/^Base directory for this skill:/i.test(trimmed)) continue;
|
|
2397
2410
|
if (/^[•·▸▶\-\*]\s/.test(trimmed)) continue;
|
|
2398
2411
|
if (/<[a-zA-Z][a-zA-Z-]*>/.test(trimmed) && /<\/[a-zA-Z]/.test(trimmed)) continue;
|
|
2399
|
-
if (
|
|
2412
|
+
if (isNarrative(trimmed)) continue;
|
|
2400
2413
|
const correction = detectCorrection(text);
|
|
2401
2414
|
if (correction) {
|
|
2402
2415
|
corrections.push(correction);
|
|
@@ -2404,7 +2417,7 @@ function extractCorrections(messages) {
|
|
|
2404
2417
|
}
|
|
2405
2418
|
return corrections;
|
|
2406
2419
|
}
|
|
2407
|
-
function
|
|
2420
|
+
function isNarrative(text) {
|
|
2408
2421
|
const NARRATIVE_MARKERS = [
|
|
2409
2422
|
/이유는/,
|
|
2410
2423
|
// "the reason is..."
|
|
@@ -2457,9 +2470,9 @@ function detectCorrection(text) {
|
|
|
2457
2470
|
const isAffirmation = AFFIRMATION_PATTERNS.some((p) => p.test(text));
|
|
2458
2471
|
if (!isNegation && !isMust && !isWarn && !isAffirmation) return null;
|
|
2459
2472
|
const categories = [isNegation, isMust, isWarn, isAffirmation].filter(Boolean).length;
|
|
2460
|
-
const
|
|
2461
|
-
if (
|
|
2462
|
-
if (text.length >
|
|
2473
|
+
const latinRatio = (text.match(/[a-zA-Z]/g) || []).length / Math.max(text.length, 1);
|
|
2474
|
+
if (latinRatio < 0.3 && categories < 2) {
|
|
2475
|
+
if (text.length > 150) return null;
|
|
2463
2476
|
}
|
|
2464
2477
|
let prefix;
|
|
2465
2478
|
if (isNegation) prefix = "NO";
|
|
@@ -2661,33 +2674,65 @@ var init_digest = __esm({
|
|
|
2661
2674
|
/\binstead\b/i,
|
|
2662
2675
|
/^no[,.\s!]/i,
|
|
2663
2676
|
/\bavoid\b/i,
|
|
2664
|
-
// Korean negation —
|
|
2665
|
-
// "X하지 마" (don't X) — must have a verb object before 지 마
|
|
2677
|
+
// Korean negation — imperative corrections:
|
|
2666
2678
|
/[을를은는도이가]\s*[가-힣]+지\s*마/,
|
|
2667
|
-
// "X
|
|
2679
|
+
// "X하지 마" (don't X) with particle
|
|
2668
2680
|
/하면\s*안\s*돼/,
|
|
2669
|
-
// "X
|
|
2670
|
-
/쓰지\s
|
|
2681
|
+
// "X 하면 안 돼" (must not X)
|
|
2682
|
+
/쓰지\s*마/,
|
|
2683
|
+
// "쓰지 마" (don't use)
|
|
2684
|
+
/그만/,
|
|
2685
|
+
// "그만" (stop) — 그만해, 그만 좀
|
|
2686
|
+
/[을를은는]\s*빼/,
|
|
2687
|
+
// "X 빼" (remove X) with particle
|
|
2688
|
+
/지워[줘]?|삭제해/,
|
|
2689
|
+
// "지워/삭제해" (delete it) — not 지우고 (connective)
|
|
2690
|
+
/[가-힣]+지\s*말고/,
|
|
2691
|
+
// "X지 말고" (instead of X-ing)
|
|
2692
|
+
/그거\s*아니/,
|
|
2693
|
+
// "그거 아니야" (that's not right)
|
|
2694
|
+
/ㄴㄴ|노노/,
|
|
2695
|
+
// "ㄴㄴ/노노" (no no — internet-style)
|
|
2696
|
+
/안\s*돼[^요]?\s*[!.]/
|
|
2697
|
+
// "안 돼!" standalone prohibition
|
|
2671
2698
|
];
|
|
2672
2699
|
AFFIRMATION_PATTERNS = [
|
|
2673
2700
|
/\bshould\s+always\b/i,
|
|
2674
2701
|
/\buse\s+\w+\s+instead\b/i,
|
|
2675
|
-
// Korean affirmation —
|
|
2676
|
-
/항상\s*[가-힣]+[해하]
|
|
2702
|
+
// Korean affirmation — directive commands:
|
|
2703
|
+
/항상\s*[가-힣]+[해하]/,
|
|
2677
2704
|
// "항상 X해" (always do X)
|
|
2705
|
+
/[을를]\s*[가-힣]*해\s*줘/,
|
|
2706
|
+
// "X를 해줘" (do X for me) — literal 해줘, not bare 줘
|
|
2707
|
+
/으로\s*해/,
|
|
2708
|
+
// "X으로 해" (do it as X) — literal 으로, not char class
|
|
2709
|
+
/이렇게\s*해/
|
|
2710
|
+
// "이렇게 해" (do it like this)
|
|
2678
2711
|
];
|
|
2679
2712
|
MUST_PATTERNS = [
|
|
2680
2713
|
/\bmust\b/i,
|
|
2681
2714
|
/\brequired\b/i,
|
|
2682
|
-
// Korean
|
|
2683
|
-
|
|
2715
|
+
// Korean — strong directives:
|
|
2716
|
+
/반드시/,
|
|
2717
|
+
// "반드시" (absolutely must)
|
|
2718
|
+
/꼭\s*[가-힣]/,
|
|
2719
|
+
// "꼭 X해" (definitely do X)
|
|
2720
|
+
/무조건/,
|
|
2721
|
+
// "무조건" (unconditionally)
|
|
2722
|
+
/필수/
|
|
2723
|
+
// "필수" (mandatory)
|
|
2684
2724
|
];
|
|
2685
2725
|
WARN_PATTERNS = [
|
|
2686
2726
|
/\bcareful\b/i,
|
|
2687
2727
|
/\bwatch\s+out\b/i,
|
|
2688
2728
|
/\bwarning\b/i,
|
|
2689
|
-
// Korean
|
|
2690
|
-
|
|
2729
|
+
// Korean — cautionary:
|
|
2730
|
+
/주의/,
|
|
2731
|
+
// "주의" (caution)
|
|
2732
|
+
/조심/,
|
|
2733
|
+
// "조심" (be careful)
|
|
2734
|
+
/위험/
|
|
2735
|
+
// "위험" (dangerous)
|
|
2691
2736
|
];
|
|
2692
2737
|
MAX_FAILURES_PER_SESSION = 20;
|
|
2693
2738
|
SOFT_ERROR_PATTERNS = [
|
|
@@ -2701,6 +2746,48 @@ var init_digest = __esm({
|
|
|
2701
2746
|
}
|
|
2702
2747
|
});
|
|
2703
2748
|
|
|
2749
|
+
// src/learn.ts
|
|
2750
|
+
var learn_exports = {};
|
|
2751
|
+
__export(learn_exports, {
|
|
2752
|
+
learn: () => learn
|
|
2753
|
+
});
|
|
2754
|
+
function learn(brainRoot, opts) {
|
|
2755
|
+
let prefix;
|
|
2756
|
+
let keywords;
|
|
2757
|
+
let source;
|
|
2758
|
+
if (opts.prefix && opts.keywords && opts.keywords.length > 0) {
|
|
2759
|
+
prefix = opts.prefix.toUpperCase();
|
|
2760
|
+
if (!VALID_PREFIXES.has(prefix)) {
|
|
2761
|
+
prefix = "DO";
|
|
2762
|
+
}
|
|
2763
|
+
keywords = opts.keywords.slice(0, 3).map((k) => k.toLowerCase().replace(/[\s\/\\\.,:;!?'"<>{}()\[\]]/g, ""));
|
|
2764
|
+
source = "agent";
|
|
2765
|
+
} else {
|
|
2766
|
+
const corrections = extractCorrections([opts.text]);
|
|
2767
|
+
if (corrections.length === 0) return null;
|
|
2768
|
+
const c = corrections[0];
|
|
2769
|
+
prefix = c.prefix;
|
|
2770
|
+
keywords = c.keywords;
|
|
2771
|
+
source = "regex";
|
|
2772
|
+
}
|
|
2773
|
+
if (keywords.length === 0) return null;
|
|
2774
|
+
const pathSegment = `${prefix}_${keywords.slice(0, 3).join("_")}`;
|
|
2775
|
+
const path = `cortex/${pathSegment}`;
|
|
2776
|
+
growCandidate(brainRoot, path);
|
|
2777
|
+
logEpisode(brainRoot, "learn", path, opts.text);
|
|
2778
|
+
return { path, prefix, keywords, source };
|
|
2779
|
+
}
|
|
2780
|
+
var VALID_PREFIXES;
|
|
2781
|
+
var init_learn = __esm({
|
|
2782
|
+
"src/learn.ts"() {
|
|
2783
|
+
"use strict";
|
|
2784
|
+
init_candidates();
|
|
2785
|
+
init_episode();
|
|
2786
|
+
init_digest();
|
|
2787
|
+
VALID_PREFIXES = /* @__PURE__ */ new Set(["NO", "DO", "MUST", "WARN"]);
|
|
2788
|
+
}
|
|
2789
|
+
});
|
|
2790
|
+
|
|
2704
2791
|
// src/outcome.ts
|
|
2705
2792
|
var outcome_exports = {};
|
|
2706
2793
|
__export(outcome_exports, {
|
|
@@ -3276,6 +3363,7 @@ __export(doctor_exports, {
|
|
|
3276
3363
|
});
|
|
3277
3364
|
import { existsSync as existsSync18, readFileSync as readFileSync11, readdirSync as readdirSync11 } from "fs";
|
|
3278
3365
|
import { join as join19 } from "path";
|
|
3366
|
+
import { homedir } from "os";
|
|
3279
3367
|
import { execSync as execSync4 } from "child_process";
|
|
3280
3368
|
async function runDoctor(brainRoot) {
|
|
3281
3369
|
let passed = 0, warnings = 0, failed = 0;
|
|
@@ -3336,33 +3424,49 @@ async function runDoctor(brainRoot) {
|
|
|
3336
3424
|
}
|
|
3337
3425
|
}
|
|
3338
3426
|
console.log("\nClaude Code hooks");
|
|
3339
|
-
const
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3427
|
+
const localSettingsPath = join19(process.cwd(), ".claude", "settings.local.json");
|
|
3428
|
+
const globalSettingsPath = join19(homedir(), ".claude", "settings.json");
|
|
3429
|
+
let hasStop = false;
|
|
3430
|
+
let hasStart = false;
|
|
3431
|
+
let hookSource = "";
|
|
3432
|
+
for (const settingsPath of [localSettingsPath, globalSettingsPath]) {
|
|
3433
|
+
if (!existsSync18(settingsPath)) continue;
|
|
3343
3434
|
try {
|
|
3344
3435
|
const settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
3345
3436
|
const hooks = settings.hooks || {};
|
|
3346
|
-
const
|
|
3347
|
-
([
|
|
3348
|
-
(
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3437
|
+
const findCommand = (event, keyword) => Object.entries(hooks).some(
|
|
3438
|
+
([ev, entries]) => ev === event && Array.isArray(entries) && entries.some((entry) => {
|
|
3439
|
+
if (typeof entry !== "object" || entry === null) return false;
|
|
3440
|
+
const e = entry;
|
|
3441
|
+
if (typeof e.command === "string" && e.command.includes(keyword)) return true;
|
|
3442
|
+
if (Array.isArray(e.hooks)) {
|
|
3443
|
+
return e.hooks.some(
|
|
3444
|
+
(h) => typeof h === "object" && h !== null && typeof h.command === "string" && h.command.includes(keyword)
|
|
3445
|
+
);
|
|
3446
|
+
}
|
|
3447
|
+
return false;
|
|
3448
|
+
})
|
|
3355
3449
|
);
|
|
3356
|
-
if (hasStop &&
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3450
|
+
if (!hasStop && findCommand("Stop", "hebbian digest")) {
|
|
3451
|
+
hasStop = true;
|
|
3452
|
+
hookSource = settingsPath === globalSettingsPath ? "global" : "local";
|
|
3453
|
+
}
|
|
3454
|
+
if (!hasStart && findCommand("SessionStart", "hebbian emit")) {
|
|
3455
|
+
hasStart = true;
|
|
3456
|
+
if (!hookSource) hookSource = settingsPath === globalSettingsPath ? "global" : "local";
|
|
3361
3457
|
}
|
|
3362
3458
|
} catch {
|
|
3363
|
-
|
|
3459
|
+
warn(`Malformed ${settingsPath === globalSettingsPath ? "~/.claude/settings.json" : ".claude/settings.local.json"}`, "Check JSON syntax");
|
|
3364
3460
|
}
|
|
3365
3461
|
}
|
|
3462
|
+
if (hasStop && hasStart) {
|
|
3463
|
+
ok(`SessionStart + Stop hooks installed (${hookSource})`);
|
|
3464
|
+
} else if (!hasStop && !hasStart) {
|
|
3465
|
+
warn("No hebbian hooks found (checked local + global)", "hebbian claude install");
|
|
3466
|
+
} else {
|
|
3467
|
+
if (!hasStart) warn("SessionStart hook missing", "hebbian claude install");
|
|
3468
|
+
if (!hasStop) warn("Stop hook missing", "hebbian claude install");
|
|
3469
|
+
}
|
|
3366
3470
|
console.log("\nnpx resolution");
|
|
3367
3471
|
try {
|
|
3368
3472
|
const resolved = execSync4("which npx", { timeout: 3e3 }).toString().trim();
|
|
@@ -3696,9 +3800,10 @@ COMMANDS:
|
|
|
3696
3800
|
inbox Process corrections inbox
|
|
3697
3801
|
claude install|uninstall|status Manage Claude Code hooks
|
|
3698
3802
|
digest [--transcript <path>] Extract corrections from conversation
|
|
3803
|
+
learn "<text>" [--prefix P] Agent-driven learning (any language)
|
|
3699
3804
|
candidates [promote] List candidates or promote graduated ones
|
|
3700
|
-
evolve [--dry-run] LLM-powered
|
|
3701
|
-
evolve prune [--dry-run] Pruning mode \u2014 remove stale
|
|
3805
|
+
evolve [--dry-run] (optional) LLM-powered evolution (Gemini)
|
|
3806
|
+
evolve prune [--dry-run] (optional) Pruning mode \u2014 remove stale neurons
|
|
3702
3807
|
session start|end Capture/detect session outcomes
|
|
3703
3808
|
sessions Show session outcome history
|
|
3704
3809
|
doctor Self-diagnostic (hooks, brain, versions)
|
|
@@ -3716,7 +3821,7 @@ EXAMPLES:
|
|
|
3716
3821
|
hebbian fire cortex/frontend/NO_console_log --brain ./my-brain
|
|
3717
3822
|
hebbian emit claude --brain ./my-brain
|
|
3718
3823
|
hebbian emit all
|
|
3719
|
-
GEMINI_API_KEY=... hebbian evolve --dry-run
|
|
3824
|
+
GEMINI_API_KEY=... hebbian evolve --dry-run # optional \u2014 self-learning works without this
|
|
3720
3825
|
`.trim();
|
|
3721
3826
|
function readStdin() {
|
|
3722
3827
|
return new Promise((resolve4) => {
|
|
@@ -3742,6 +3847,8 @@ async function main(argv) {
|
|
|
3742
3847
|
days: { type: "string", short: "d" },
|
|
3743
3848
|
port: { type: "string", short: "p" },
|
|
3744
3849
|
transcript: { type: "string", short: "t" },
|
|
3850
|
+
prefix: { type: "string" },
|
|
3851
|
+
keywords: { type: "string", short: "k" },
|
|
3745
3852
|
"dry-run": { type: "boolean" },
|
|
3746
3853
|
global: { type: "boolean", short: "g" },
|
|
3747
3854
|
agent: { type: "string", short: "a" },
|
|
@@ -3918,6 +4025,27 @@ async function main(argv) {
|
|
|
3918
4025
|
}
|
|
3919
4026
|
break;
|
|
3920
4027
|
}
|
|
4028
|
+
case "learn": {
|
|
4029
|
+
const text = positionals.slice(1).join(" ");
|
|
4030
|
+
if (!text) {
|
|
4031
|
+
console.error('Usage: hebbian learn "correction text" [--prefix NO|DO|MUST|WARN] [--keywords "k1,k2,k3"]');
|
|
4032
|
+
process.exit(1);
|
|
4033
|
+
}
|
|
4034
|
+
const { learn: learn2 } = await Promise.resolve().then(() => (init_learn(), learn_exports));
|
|
4035
|
+
const prefixFlag = values.prefix;
|
|
4036
|
+
const keywordsFlag = values.keywords;
|
|
4037
|
+
const result = learn2(brainRoot, {
|
|
4038
|
+
text,
|
|
4039
|
+
prefix: prefixFlag,
|
|
4040
|
+
keywords: keywordsFlag ? keywordsFlag.split(",").map((k) => k.trim()) : void 0
|
|
4041
|
+
});
|
|
4042
|
+
if (result) {
|
|
4043
|
+
console.log(`\u{1F4DD} learned: ${result.path} (${result.source})`);
|
|
4044
|
+
} else {
|
|
4045
|
+
console.log("\u23ED\uFE0F no correction detected");
|
|
4046
|
+
}
|
|
4047
|
+
break;
|
|
4048
|
+
}
|
|
3921
4049
|
case "candidates": {
|
|
3922
4050
|
const subCmd = positionals[1];
|
|
3923
4051
|
const { listCandidates: listCandidates2, promoteCandidates: promoteCandidates2 } = await Promise.resolve().then(() => (init_candidates(), candidates_exports));
|