hebbian 0.1.0 → 0.3.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 +11 -9
- package/dist/api.d.ts +23 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/bin/hebbian.js +2199 -0
- package/dist/bin/hebbian.js.map +1 -0
- package/dist/constants.d.ts +22 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/decay.d.ts +9 -0
- package/dist/decay.d.ts.map +1 -0
- package/dist/dedup.d.ts +9 -0
- package/dist/dedup.d.ts.map +1 -0
- package/dist/digest.d.ts +30 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/emit.d.ts +26 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/episode.d.ts +16 -0
- package/dist/episode.d.ts.map +1 -0
- package/dist/fire.d.ts +10 -0
- package/dist/fire.d.ts.map +1 -0
- package/dist/grow.d.ts +11 -0
- package/dist/grow.d.ts.map +1 -0
- package/dist/hooks.d.ts +20 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/inbox.d.ts +28 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1817 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +5 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/rollback.d.ts +5 -0
- package/dist/rollback.d.ts.map +1 -0
- package/dist/scanner.d.ts +6 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/signal.d.ts +6 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/similarity.d.ts +16 -0
- package/dist/similarity.d.ts.map +1 -0
- package/dist/snapshot.d.ts +5 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/subsumption.d.ts +6 -0
- package/dist/subsumption.d.ts.map +1 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/watch.d.ts +6 -0
- package/dist/watch.d.ts.map +1 -0
- package/package.json +17 -9
- package/bin/hebbian.js +0 -199
- package/lib/constants.js +0 -83
- package/lib/decay.js +0 -100
- package/lib/dedup.js +0 -66
- package/lib/emit.js +0 -376
- package/lib/fire.js +0 -59
- package/lib/grow.js +0 -98
- package/lib/init.js +0 -89
- package/lib/rollback.js +0 -34
- package/lib/scanner.js +0 -227
- package/lib/signal.js +0 -68
- package/lib/similarity.js +0 -62
- package/lib/snapshot.js +0 -46
- package/lib/subsumption.js +0 -77
- package/lib/watch.js +0 -79
|
@@ -0,0 +1,2199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/constants.ts
|
|
13
|
+
import { resolve } from "path";
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
function resolveBrainRoot(brainFlag) {
|
|
16
|
+
if (brainFlag) return resolve(brainFlag);
|
|
17
|
+
if (process.env.HEBBIAN_BRAIN) return resolve(process.env.HEBBIAN_BRAIN);
|
|
18
|
+
if (existsSync(resolve("./brain"))) return resolve("./brain");
|
|
19
|
+
return resolve(process.env.HOME || "~", "hebbian", "brain");
|
|
20
|
+
}
|
|
21
|
+
var REGIONS, REGION_PRIORITY, REGION_ICONS, REGION_KO, EMIT_THRESHOLD, SPOTLIGHT_DAYS, JACCARD_THRESHOLD, MAX_DEPTH, EMIT_TARGETS, SIGNAL_TYPES, MARKER_START, MARKER_END, HOOK_MARKER, MAX_CORRECTIONS_PER_SESSION, MIN_CORRECTION_LENGTH, DIGEST_LOG_DIR;
|
|
22
|
+
var init_constants = __esm({
|
|
23
|
+
"src/constants.ts"() {
|
|
24
|
+
"use strict";
|
|
25
|
+
REGIONS = [
|
|
26
|
+
"brainstem",
|
|
27
|
+
"limbic",
|
|
28
|
+
"hippocampus",
|
|
29
|
+
"sensors",
|
|
30
|
+
"cortex",
|
|
31
|
+
"ego",
|
|
32
|
+
"prefrontal"
|
|
33
|
+
];
|
|
34
|
+
REGION_PRIORITY = {
|
|
35
|
+
brainstem: 0,
|
|
36
|
+
limbic: 1,
|
|
37
|
+
hippocampus: 2,
|
|
38
|
+
sensors: 3,
|
|
39
|
+
cortex: 4,
|
|
40
|
+
ego: 5,
|
|
41
|
+
prefrontal: 6
|
|
42
|
+
};
|
|
43
|
+
REGION_ICONS = {
|
|
44
|
+
brainstem: "\u{1F6E1}\uFE0F",
|
|
45
|
+
limbic: "\u{1F493}",
|
|
46
|
+
hippocampus: "\u{1F4DD}",
|
|
47
|
+
sensors: "\u{1F441}\uFE0F",
|
|
48
|
+
cortex: "\u{1F9E0}",
|
|
49
|
+
ego: "\u{1F3AD}",
|
|
50
|
+
prefrontal: "\u{1F3AF}"
|
|
51
|
+
};
|
|
52
|
+
REGION_KO = {
|
|
53
|
+
brainstem: "\uC591\uC2EC/\uBCF8\uB2A5",
|
|
54
|
+
limbic: "\uAC10\uC815 \uD544\uD130",
|
|
55
|
+
hippocampus: "\uAE30\uB85D/\uAE30\uC5B5",
|
|
56
|
+
sensors: "\uD658\uACBD \uC81C\uC57D",
|
|
57
|
+
cortex: "\uC9C0\uC2DD/\uAE30\uC220",
|
|
58
|
+
ego: "\uC131\uD5A5/\uD1A4",
|
|
59
|
+
prefrontal: "\uBAA9\uD45C/\uACC4\uD68D"
|
|
60
|
+
};
|
|
61
|
+
EMIT_THRESHOLD = 5;
|
|
62
|
+
SPOTLIGHT_DAYS = 7;
|
|
63
|
+
JACCARD_THRESHOLD = 0.6;
|
|
64
|
+
MAX_DEPTH = 6;
|
|
65
|
+
EMIT_TARGETS = {
|
|
66
|
+
gemini: ".gemini/GEMINI.md",
|
|
67
|
+
cursor: ".cursorrules",
|
|
68
|
+
claude: "CLAUDE.md",
|
|
69
|
+
copilot: ".github/copilot-instructions.md",
|
|
70
|
+
generic: ".neuronrc"
|
|
71
|
+
};
|
|
72
|
+
SIGNAL_TYPES = ["dopamine", "bomb", "memory"];
|
|
73
|
+
MARKER_START = "<!-- HEBBIAN:START -->";
|
|
74
|
+
MARKER_END = "<!-- HEBBIAN:END -->";
|
|
75
|
+
HOOK_MARKER = "[hebbian]";
|
|
76
|
+
MAX_CORRECTIONS_PER_SESSION = 10;
|
|
77
|
+
MIN_CORRECTION_LENGTH = 15;
|
|
78
|
+
DIGEST_LOG_DIR = "hippocampus/digest_log";
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// src/init.ts
|
|
83
|
+
var init_exports = {};
|
|
84
|
+
__export(init_exports, {
|
|
85
|
+
initBrain: () => initBrain
|
|
86
|
+
});
|
|
87
|
+
import { mkdirSync, writeFileSync, existsSync as existsSync2, readdirSync } from "fs";
|
|
88
|
+
import { join } from "path";
|
|
89
|
+
function initBrain(brainPath) {
|
|
90
|
+
if (existsSync2(brainPath)) {
|
|
91
|
+
const entries = readdirSync(brainPath);
|
|
92
|
+
if (entries.some((e) => REGIONS.includes(e))) {
|
|
93
|
+
console.log(`\u26A0\uFE0F Brain already exists at ${brainPath}`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
mkdirSync(brainPath, { recursive: true });
|
|
98
|
+
for (const regionName of REGIONS) {
|
|
99
|
+
const regionDir = join(brainPath, regionName);
|
|
100
|
+
mkdirSync(regionDir, { recursive: true });
|
|
101
|
+
const template = REGION_TEMPLATES[regionName];
|
|
102
|
+
const icon = REGION_ICONS[regionName];
|
|
103
|
+
const ko = REGION_KO[regionName];
|
|
104
|
+
writeFileSync(
|
|
105
|
+
join(regionDir, "_rules.md"),
|
|
106
|
+
`# ${icon} ${regionName} (${ko})
|
|
107
|
+
|
|
108
|
+
${template.description}
|
|
109
|
+
`,
|
|
110
|
+
"utf8"
|
|
111
|
+
);
|
|
112
|
+
for (const starter of template.starters) {
|
|
113
|
+
const neuronDir = join(regionDir, starter);
|
|
114
|
+
mkdirSync(neuronDir, { recursive: true });
|
|
115
|
+
writeFileSync(join(neuronDir, "1.neuron"), "", "utf8");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
mkdirSync(join(brainPath, "_agents", "global_inbox"), { recursive: true });
|
|
119
|
+
console.log(`\u{1F9E0} Brain initialized at ${brainPath}`);
|
|
120
|
+
console.log(` 7 regions created: ${REGIONS.join(", ")}`);
|
|
121
|
+
console.log("");
|
|
122
|
+
console.log(" Next steps:");
|
|
123
|
+
console.log(` hebbian grow brainstem/NO_your_rule --brain ${brainPath}`);
|
|
124
|
+
console.log(` hebbian emit claude --brain ${brainPath}`);
|
|
125
|
+
}
|
|
126
|
+
var REGION_TEMPLATES;
|
|
127
|
+
var init_init = __esm({
|
|
128
|
+
"src/init.ts"() {
|
|
129
|
+
"use strict";
|
|
130
|
+
init_constants();
|
|
131
|
+
REGION_TEMPLATES = {
|
|
132
|
+
brainstem: {
|
|
133
|
+
description: "Absolute principles. Immutable. Read-only conscience.\nP0 \u2014 highest priority. bomb here halts EVERYTHING.",
|
|
134
|
+
starters: ["NO_fallback", "DO_execute_not_debate"]
|
|
135
|
+
},
|
|
136
|
+
limbic: {
|
|
137
|
+
description: "Emotional filters and somatic markers.\nP1 \u2014 automatic, influences downstream regions.",
|
|
138
|
+
starters: []
|
|
139
|
+
},
|
|
140
|
+
hippocampus: {
|
|
141
|
+
description: "Memory and episode recording.\nP2 \u2014 accumulated experience, session logs.",
|
|
142
|
+
starters: []
|
|
143
|
+
},
|
|
144
|
+
sensors: {
|
|
145
|
+
description: "Environment constraints and input validation.\nP3 \u2014 read-only, set by environment.",
|
|
146
|
+
starters: []
|
|
147
|
+
},
|
|
148
|
+
cortex: {
|
|
149
|
+
description: "Knowledge and skills. The largest region.\nP4 \u2014 learnable, grows with corrections.",
|
|
150
|
+
starters: []
|
|
151
|
+
},
|
|
152
|
+
ego: {
|
|
153
|
+
description: "Personality, tone, and communication style.\nP5 \u2014 set by user preference.",
|
|
154
|
+
starters: []
|
|
155
|
+
},
|
|
156
|
+
prefrontal: {
|
|
157
|
+
description: "Goals, projects, and planning.\nP6 \u2014 lowest priority, longest time horizon.",
|
|
158
|
+
starters: []
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// src/scanner.ts
|
|
165
|
+
var scanner_exports = {};
|
|
166
|
+
__export(scanner_exports, {
|
|
167
|
+
scanBrain: () => scanBrain
|
|
168
|
+
});
|
|
169
|
+
import { readdirSync as readdirSync2, statSync, readFileSync, existsSync as existsSync3 } from "fs";
|
|
170
|
+
import { join as join2, relative, sep } from "path";
|
|
171
|
+
function scanBrain(brainRoot) {
|
|
172
|
+
const regions = [];
|
|
173
|
+
for (const regionName of REGIONS) {
|
|
174
|
+
const regionPath = join2(brainRoot, regionName);
|
|
175
|
+
if (!existsSync3(regionPath)) {
|
|
176
|
+
regions.push({
|
|
177
|
+
name: regionName,
|
|
178
|
+
priority: REGION_PRIORITY[regionName],
|
|
179
|
+
path: regionPath,
|
|
180
|
+
neurons: [],
|
|
181
|
+
axons: [],
|
|
182
|
+
hasBomb: false
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const neurons = walkRegion(regionPath, regionPath, 0);
|
|
187
|
+
const axons = readAxons(regionPath);
|
|
188
|
+
const hasBomb = neurons.some((n) => n.hasBomb);
|
|
189
|
+
regions.push({
|
|
190
|
+
name: regionName,
|
|
191
|
+
priority: REGION_PRIORITY[regionName],
|
|
192
|
+
path: regionPath,
|
|
193
|
+
neurons,
|
|
194
|
+
axons,
|
|
195
|
+
hasBomb
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return { root: brainRoot, regions };
|
|
199
|
+
}
|
|
200
|
+
function walkRegion(dir, regionRoot, depth) {
|
|
201
|
+
if (depth > MAX_DEPTH) return [];
|
|
202
|
+
const neurons = [];
|
|
203
|
+
let entries;
|
|
204
|
+
try {
|
|
205
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
206
|
+
} catch {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
let counter = 0;
|
|
210
|
+
let contra = 0;
|
|
211
|
+
let dopamine = 0;
|
|
212
|
+
let hasBomb = false;
|
|
213
|
+
let hasMemory = false;
|
|
214
|
+
let isDormant = false;
|
|
215
|
+
let modTime = /* @__PURE__ */ new Date(0);
|
|
216
|
+
let hasTraceFile = false;
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
if (entry.name.startsWith("_")) continue;
|
|
219
|
+
if (entry.isFile()) {
|
|
220
|
+
const name = entry.name;
|
|
221
|
+
if (name.endsWith(".neuron") && !name.startsWith("dopamine") && !name.startsWith("memory") && name !== "bomb.neuron") {
|
|
222
|
+
const n = parseInt(name, 10);
|
|
223
|
+
if (!isNaN(n) && n > counter) {
|
|
224
|
+
counter = n;
|
|
225
|
+
hasTraceFile = true;
|
|
226
|
+
try {
|
|
227
|
+
const st = statSync(join2(dir, name));
|
|
228
|
+
if (st.mtime > modTime) modTime = st.mtime;
|
|
229
|
+
} catch {
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (name.endsWith(".contra")) {
|
|
234
|
+
const n = parseInt(name, 10);
|
|
235
|
+
if (!isNaN(n) && n > contra) {
|
|
236
|
+
contra = n;
|
|
237
|
+
hasTraceFile = true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (name.startsWith("dopamine") && name.endsWith(".neuron")) {
|
|
241
|
+
const n = parseInt(name.replace("dopamine", ""), 10);
|
|
242
|
+
if (!isNaN(n) && n > dopamine) {
|
|
243
|
+
dopamine = n;
|
|
244
|
+
hasTraceFile = true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (name === "bomb.neuron") {
|
|
248
|
+
hasBomb = true;
|
|
249
|
+
hasTraceFile = true;
|
|
250
|
+
}
|
|
251
|
+
if (name.startsWith("memory") && name.endsWith(".neuron")) {
|
|
252
|
+
hasMemory = true;
|
|
253
|
+
hasTraceFile = true;
|
|
254
|
+
}
|
|
255
|
+
if (name.endsWith(".dormant")) {
|
|
256
|
+
isDormant = true;
|
|
257
|
+
hasTraceFile = true;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (hasTraceFile) {
|
|
262
|
+
const relPath = relative(regionRoot, dir) || ".";
|
|
263
|
+
const folderName = dir.split(sep).pop() || "";
|
|
264
|
+
const total = counter + contra + dopamine;
|
|
265
|
+
const intensity = counter - contra + dopamine;
|
|
266
|
+
const polarity = total > 0 ? intensity / total : 0;
|
|
267
|
+
neurons.push({
|
|
268
|
+
name: folderName,
|
|
269
|
+
path: relPath,
|
|
270
|
+
fullPath: dir,
|
|
271
|
+
counter,
|
|
272
|
+
contra,
|
|
273
|
+
dopamine,
|
|
274
|
+
intensity,
|
|
275
|
+
polarity: Math.round(polarity * 100) / 100,
|
|
276
|
+
hasBomb,
|
|
277
|
+
hasMemory,
|
|
278
|
+
isDormant,
|
|
279
|
+
depth,
|
|
280
|
+
modTime
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
for (const entry of entries) {
|
|
284
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
285
|
+
if (entry.isDirectory()) {
|
|
286
|
+
const subNeurons = walkRegion(join2(dir, entry.name), regionRoot, depth + 1);
|
|
287
|
+
neurons.push(...subNeurons);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return neurons;
|
|
291
|
+
}
|
|
292
|
+
function readAxons(regionPath) {
|
|
293
|
+
const axonPath = join2(regionPath, ".axon");
|
|
294
|
+
if (!existsSync3(axonPath)) return [];
|
|
295
|
+
try {
|
|
296
|
+
const content = readFileSync(axonPath, "utf8").trim();
|
|
297
|
+
return content.split(/[\n,]+/).map((s) => s.trim()).filter(Boolean);
|
|
298
|
+
} catch {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
var init_scanner = __esm({
|
|
303
|
+
"src/scanner.ts"() {
|
|
304
|
+
"use strict";
|
|
305
|
+
init_constants();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// src/subsumption.ts
|
|
310
|
+
var subsumption_exports = {};
|
|
311
|
+
__export(subsumption_exports, {
|
|
312
|
+
runSubsumption: () => runSubsumption
|
|
313
|
+
});
|
|
314
|
+
function runSubsumption(brain) {
|
|
315
|
+
const activeRegions = [];
|
|
316
|
+
const blockedRegions = [];
|
|
317
|
+
let bombSource = "";
|
|
318
|
+
let firedNeurons = 0;
|
|
319
|
+
let totalNeurons = 0;
|
|
320
|
+
let totalCounter = 0;
|
|
321
|
+
let blocked = false;
|
|
322
|
+
for (const region of brain.regions) {
|
|
323
|
+
totalNeurons += region.neurons.length;
|
|
324
|
+
if (blocked) {
|
|
325
|
+
blockedRegions.push(region);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (region.hasBomb) {
|
|
329
|
+
bombSource = region.name;
|
|
330
|
+
blockedRegions.push(region);
|
|
331
|
+
blocked = true;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
activeRegions.push(region);
|
|
335
|
+
for (const neuron of region.neurons) {
|
|
336
|
+
if (!neuron.isDormant) {
|
|
337
|
+
firedNeurons++;
|
|
338
|
+
totalCounter += neuron.counter;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
activeRegions,
|
|
344
|
+
blockedRegions,
|
|
345
|
+
bombSource,
|
|
346
|
+
firedNeurons,
|
|
347
|
+
totalNeurons,
|
|
348
|
+
totalCounter
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
var init_subsumption = __esm({
|
|
352
|
+
"src/subsumption.ts"() {
|
|
353
|
+
"use strict";
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// src/emit.ts
|
|
358
|
+
var emit_exports = {};
|
|
359
|
+
__export(emit_exports, {
|
|
360
|
+
emitBootstrap: () => emitBootstrap,
|
|
361
|
+
emitIndex: () => emitIndex,
|
|
362
|
+
emitRegionRules: () => emitRegionRules,
|
|
363
|
+
emitToTarget: () => emitToTarget,
|
|
364
|
+
printDiag: () => printDiag,
|
|
365
|
+
writeAllTiers: () => writeAllTiers
|
|
366
|
+
});
|
|
367
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
368
|
+
import { join as join3, dirname } from "path";
|
|
369
|
+
function emitBootstrap(result, brain) {
|
|
370
|
+
const lines = [];
|
|
371
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "");
|
|
372
|
+
lines.push(MARKER_START);
|
|
373
|
+
lines.push(`<!-- Generated: ${now} -->`);
|
|
374
|
+
lines.push("<!-- Axiom: Folder=Neuron | File=Trace | Path=Sentence -->");
|
|
375
|
+
lines.push(`<!-- Active: ${result.firedNeurons}/${result.totalNeurons} neurons | Total activation: ${result.totalCounter} -->`);
|
|
376
|
+
lines.push("");
|
|
377
|
+
if (result.bombSource) {
|
|
378
|
+
lines.push(`## \u{1F6A8} CIRCUIT BREAKER: ${result.bombSource}`);
|
|
379
|
+
lines.push("**ALL OPERATIONS HALTED. REPAIR REQUIRED.**");
|
|
380
|
+
lines.push("");
|
|
381
|
+
lines.push(MARKER_END);
|
|
382
|
+
return lines.join("\n");
|
|
383
|
+
}
|
|
384
|
+
lines.push("## hebbian Active Rules");
|
|
385
|
+
lines.push("");
|
|
386
|
+
lines.push(`### ${REGION_ICONS.ego} Persona`);
|
|
387
|
+
for (const region of result.activeRegions) {
|
|
388
|
+
if (region.name === "ego") {
|
|
389
|
+
const top = sortedActive(region.neurons, 10);
|
|
390
|
+
for (const n of top) {
|
|
391
|
+
lines.push(`- ${pathToSentence(n.path)}`);
|
|
392
|
+
}
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
lines.push("");
|
|
397
|
+
lines.push("### \u{1F517} Subsumption Cascade");
|
|
398
|
+
lines.push("```");
|
|
399
|
+
lines.push("brainstem \u2190\u2192 limbic \u2190\u2192 hippocampus \u2190\u2192 sensors \u2190\u2192 cortex \u2190\u2192 ego \u2190\u2192 prefrontal");
|
|
400
|
+
lines.push(" (P0) (P1) (P2) (P3) (P4) (P5) (P6)");
|
|
401
|
+
lines.push("```");
|
|
402
|
+
lines.push("Lower P always overrides higher P. bomb = full stop.");
|
|
403
|
+
lines.push("");
|
|
404
|
+
lines.push(`### ${REGION_ICONS.brainstem} Core Directives TOP 5`);
|
|
405
|
+
for (const region of result.activeRegions) {
|
|
406
|
+
if (region.name === "brainstem") {
|
|
407
|
+
const top = sortedActive(region.neurons, 5);
|
|
408
|
+
top.forEach((n, i) => {
|
|
409
|
+
lines.push(`${i + 1}. **${pathToSentence(n.path)}**`);
|
|
410
|
+
});
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
lines.push("");
|
|
415
|
+
lines.push("### Active Regions");
|
|
416
|
+
lines.push("| Region | Neurons | Activation |");
|
|
417
|
+
lines.push("|--------|---------|------------|");
|
|
418
|
+
for (const region of result.activeRegions) {
|
|
419
|
+
const active = region.neurons.filter((n) => !n.isDormant);
|
|
420
|
+
const activation = active.reduce((sum, n) => sum + n.counter, 0);
|
|
421
|
+
const icon = REGION_ICONS[region.name] || "";
|
|
422
|
+
lines.push(`| ${icon} ${region.name} | ${active.length} | ${activation} |`);
|
|
423
|
+
}
|
|
424
|
+
lines.push("");
|
|
425
|
+
lines.push(MARKER_END);
|
|
426
|
+
return lines.join("\n");
|
|
427
|
+
}
|
|
428
|
+
function emitIndex(result, brain) {
|
|
429
|
+
const lines = [];
|
|
430
|
+
lines.push("# hebbian Brain Index");
|
|
431
|
+
lines.push("");
|
|
432
|
+
lines.push(`> ${result.firedNeurons} active / ${result.totalNeurons} total neurons | activation: ${result.totalCounter}`);
|
|
433
|
+
lines.push("");
|
|
434
|
+
if (result.bombSource) {
|
|
435
|
+
lines.push(`## \u{1F6A8} CIRCUIT BREAKER: ${result.bombSource}`);
|
|
436
|
+
lines.push("");
|
|
437
|
+
return lines.join("\n");
|
|
438
|
+
}
|
|
439
|
+
const allNeurons = result.activeRegions.flatMap(
|
|
440
|
+
(r) => r.neurons.filter((n) => !n.isDormant && n.counter >= EMIT_THRESHOLD)
|
|
441
|
+
);
|
|
442
|
+
allNeurons.sort((a, b) => b.counter - a.counter);
|
|
443
|
+
lines.push("## Top 10 Active Neurons");
|
|
444
|
+
lines.push("| # | Path | Counter | Strength |");
|
|
445
|
+
lines.push("|---|------|---------|----------|");
|
|
446
|
+
for (const n of allNeurons.slice(0, 10)) {
|
|
447
|
+
const strength = n.counter >= 10 ? "\u{1F534}" : n.counter >= 5 ? "\u{1F7E1}" : "\u26AA";
|
|
448
|
+
lines.push(`| ${allNeurons.indexOf(n) + 1} | ${n.path} | ${n.counter} | ${strength} |`);
|
|
449
|
+
}
|
|
450
|
+
lines.push("");
|
|
451
|
+
const cutoff = new Date(Date.now() - SPOTLIGHT_DAYS * 24 * 60 * 60 * 1e3);
|
|
452
|
+
const spotlightNeurons = result.activeRegions.flatMap(
|
|
453
|
+
(r) => r.neurons.filter((n) => !n.isDormant && n.counter < EMIT_THRESHOLD && n.modTime > cutoff)
|
|
454
|
+
);
|
|
455
|
+
if (spotlightNeurons.length > 0) {
|
|
456
|
+
lines.push("## Spotlight (Probation)");
|
|
457
|
+
for (const n of spotlightNeurons) {
|
|
458
|
+
lines.push(`- ${n.path} (${n.counter}) \u2014 new`);
|
|
459
|
+
}
|
|
460
|
+
lines.push("");
|
|
461
|
+
}
|
|
462
|
+
lines.push("## Regions");
|
|
463
|
+
lines.push("| Region | Active | Dormant | Activation | Rules |");
|
|
464
|
+
lines.push("|--------|--------|---------|------------|-------|");
|
|
465
|
+
for (const region of result.activeRegions) {
|
|
466
|
+
const active = region.neurons.filter((n) => !n.isDormant);
|
|
467
|
+
const dormant = region.neurons.filter((n) => n.isDormant);
|
|
468
|
+
const activation = active.reduce((sum, n) => sum + n.counter, 0);
|
|
469
|
+
const icon = REGION_ICONS[region.name] || "";
|
|
470
|
+
lines.push(`| ${icon} ${region.name} | ${active.length} | ${dormant.length} | ${activation} | [_rules.md](${region.name}/_rules.md) |`);
|
|
471
|
+
}
|
|
472
|
+
lines.push("");
|
|
473
|
+
return lines.join("\n");
|
|
474
|
+
}
|
|
475
|
+
function emitRegionRules(region) {
|
|
476
|
+
const icon = REGION_ICONS[region.name] || "";
|
|
477
|
+
const ko = REGION_KO[region.name] || "";
|
|
478
|
+
const active = region.neurons.filter((n) => !n.isDormant);
|
|
479
|
+
const dormant = region.neurons.filter((n) => n.isDormant);
|
|
480
|
+
const activation = active.reduce((sum, n) => sum + n.counter, 0);
|
|
481
|
+
const lines = [];
|
|
482
|
+
lines.push(`# ${icon} ${region.name} (${ko})`);
|
|
483
|
+
lines.push(`> Active: ${active.length} | Dormant: ${dormant.length} | Activation: ${activation}`);
|
|
484
|
+
lines.push("");
|
|
485
|
+
if (region.axons.length > 0) {
|
|
486
|
+
lines.push("## Connections");
|
|
487
|
+
for (const axon of region.axons) {
|
|
488
|
+
lines.push(`- \u2194 ${axon}`);
|
|
489
|
+
}
|
|
490
|
+
lines.push("");
|
|
491
|
+
}
|
|
492
|
+
if (active.length > 0) {
|
|
493
|
+
lines.push("## Rules");
|
|
494
|
+
const sorted = [...active].sort((a, b) => b.counter - a.counter);
|
|
495
|
+
for (const n of sorted) {
|
|
496
|
+
const indent = " ".repeat(Math.min(n.depth, 4));
|
|
497
|
+
const prefix = strengthPrefix(n.counter);
|
|
498
|
+
const signals = [];
|
|
499
|
+
if (n.dopamine > 0) signals.push(`\u{1F7E2}+${n.dopamine}`);
|
|
500
|
+
if (n.hasBomb) signals.push("\u{1F4A3}");
|
|
501
|
+
if (n.hasMemory) signals.push("\u{1F4BE}");
|
|
502
|
+
const signalStr = signals.length > 0 ? ` ${signals.join(" ")}` : "";
|
|
503
|
+
lines.push(`${indent}- ${prefix}${pathToSentence(n.path)} (${n.counter})${signalStr}`);
|
|
504
|
+
}
|
|
505
|
+
lines.push("");
|
|
506
|
+
}
|
|
507
|
+
if (dormant.length > 0) {
|
|
508
|
+
lines.push("## Dormant");
|
|
509
|
+
for (const n of dormant) {
|
|
510
|
+
lines.push(`- ~~${pathToSentence(n.path)} (${n.counter})~~`);
|
|
511
|
+
}
|
|
512
|
+
lines.push("");
|
|
513
|
+
}
|
|
514
|
+
return lines.join("\n");
|
|
515
|
+
}
|
|
516
|
+
function emitToTarget(brainRoot, target) {
|
|
517
|
+
const brain = scanBrain(brainRoot);
|
|
518
|
+
const result = runSubsumption(brain);
|
|
519
|
+
const content = emitBootstrap(result, brain);
|
|
520
|
+
if (target === "all") {
|
|
521
|
+
for (const [name, filePath] of Object.entries(EMIT_TARGETS)) {
|
|
522
|
+
writeTarget(filePath, content);
|
|
523
|
+
console.log(`\u{1F4E4} emitted: ${name} \u2192 ${filePath}`);
|
|
524
|
+
}
|
|
525
|
+
} else if (EMIT_TARGETS[target]) {
|
|
526
|
+
writeTarget(EMIT_TARGETS[target], content);
|
|
527
|
+
console.log(`\u{1F4E4} emitted: ${target} \u2192 ${EMIT_TARGETS[target]}`);
|
|
528
|
+
} else {
|
|
529
|
+
throw new Error(`Unknown target: ${target}. Valid: ${Object.keys(EMIT_TARGETS).join(", ")}, all`);
|
|
530
|
+
}
|
|
531
|
+
writeAllTiers(brainRoot, result, brain);
|
|
532
|
+
}
|
|
533
|
+
function writeAllTiers(brainRoot, result, brain) {
|
|
534
|
+
const indexContent = emitIndex(result, brain);
|
|
535
|
+
writeFileSync2(join3(brainRoot, "_index.md"), indexContent, "utf8");
|
|
536
|
+
for (const region of result.activeRegions) {
|
|
537
|
+
if (existsSync4(region.path)) {
|
|
538
|
+
const rulesContent = emitRegionRules(region);
|
|
539
|
+
writeFileSync2(join3(region.path, "_rules.md"), rulesContent, "utf8");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function writeTarget(filePath, content) {
|
|
544
|
+
const dir = dirname(filePath);
|
|
545
|
+
if (dir !== "." && !existsSync4(dir)) {
|
|
546
|
+
mkdirSync2(dir, { recursive: true });
|
|
547
|
+
}
|
|
548
|
+
if (existsSync4(filePath)) {
|
|
549
|
+
const existing = readFileSync2(filePath, "utf8");
|
|
550
|
+
const startIdx = existing.indexOf(MARKER_START);
|
|
551
|
+
const endIdx = existing.indexOf(MARKER_END);
|
|
552
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
553
|
+
const before = existing.slice(0, startIdx);
|
|
554
|
+
const after = existing.slice(endIdx + MARKER_END.length);
|
|
555
|
+
writeFileSync2(filePath, before + content + after, "utf8");
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
writeFileSync2(filePath, content, "utf8");
|
|
560
|
+
}
|
|
561
|
+
function printDiag(brain, result) {
|
|
562
|
+
console.log("");
|
|
563
|
+
console.log(`\u{1F9E0} hebbian Brain Diagnostics`);
|
|
564
|
+
console.log(` Root: ${brain.root}`);
|
|
565
|
+
console.log(` Neurons: ${result.firedNeurons} active / ${result.totalNeurons} total`);
|
|
566
|
+
console.log(` Activation: ${result.totalCounter}`);
|
|
567
|
+
if (result.bombSource) {
|
|
568
|
+
console.log(` \u{1F4A3} BOMB: ${result.bombSource} \u2014 cascade halted!`);
|
|
569
|
+
}
|
|
570
|
+
console.log("");
|
|
571
|
+
for (const region of brain.regions) {
|
|
572
|
+
const icon = REGION_ICONS[region.name] || "";
|
|
573
|
+
const active = region.neurons.filter((n) => !n.isDormant);
|
|
574
|
+
const dormant = region.neurons.filter((n) => n.isDormant);
|
|
575
|
+
const activation = active.reduce((sum, n) => sum + n.counter, 0);
|
|
576
|
+
const isBlocked = result.blockedRegions.some((r) => r.name === region.name);
|
|
577
|
+
const status = region.hasBomb ? "\u{1F4A3} BOMB" : isBlocked ? "\u{1F6AB} BLOCKED" : "\u2705 ACTIVE";
|
|
578
|
+
console.log(` ${icon} ${region.name} [${status}]`);
|
|
579
|
+
console.log(` neurons: ${active.length} active, ${dormant.length} dormant | activation: ${activation}`);
|
|
580
|
+
if (region.axons.length > 0) {
|
|
581
|
+
console.log(` axons: ${region.axons.join(", ")}`);
|
|
582
|
+
}
|
|
583
|
+
const top3 = sortedActive(region.neurons, 3);
|
|
584
|
+
for (const n of top3) {
|
|
585
|
+
console.log(` \u251C ${n.path} (${n.counter})`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
console.log("");
|
|
589
|
+
}
|
|
590
|
+
function pathToSentence(path) {
|
|
591
|
+
return path.replace(/\//g, " > ").replace(/_/g, " ");
|
|
592
|
+
}
|
|
593
|
+
function sortedActive(neurons, n) {
|
|
594
|
+
return [...neurons].filter((neuron) => !neuron.isDormant).sort((a, b) => b.counter - a.counter).slice(0, n);
|
|
595
|
+
}
|
|
596
|
+
function strengthPrefix(counter) {
|
|
597
|
+
if (counter >= 10) return "**[ABSOLUTE]** ";
|
|
598
|
+
if (counter >= 5) return "**[MUST]** ";
|
|
599
|
+
return "";
|
|
600
|
+
}
|
|
601
|
+
var init_emit = __esm({
|
|
602
|
+
"src/emit.ts"() {
|
|
603
|
+
"use strict";
|
|
604
|
+
init_scanner();
|
|
605
|
+
init_subsumption();
|
|
606
|
+
init_constants();
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// src/fire.ts
|
|
611
|
+
var fire_exports = {};
|
|
612
|
+
__export(fire_exports, {
|
|
613
|
+
fireNeuron: () => fireNeuron,
|
|
614
|
+
getCurrentCounter: () => getCurrentCounter
|
|
615
|
+
});
|
|
616
|
+
import { readdirSync as readdirSync3, renameSync, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
617
|
+
import { join as join4 } from "path";
|
|
618
|
+
function fireNeuron(brainRoot, neuronPath) {
|
|
619
|
+
const fullPath = join4(brainRoot, neuronPath);
|
|
620
|
+
if (!existsSync5(fullPath)) {
|
|
621
|
+
mkdirSync3(fullPath, { recursive: true });
|
|
622
|
+
writeFileSync3(join4(fullPath, "1.neuron"), "", "utf8");
|
|
623
|
+
console.log(`\u{1F331} grew + fired: ${neuronPath} (1)`);
|
|
624
|
+
return 1;
|
|
625
|
+
}
|
|
626
|
+
const current = getCurrentCounter(fullPath);
|
|
627
|
+
const newCounter = current + 1;
|
|
628
|
+
if (current > 0) {
|
|
629
|
+
renameSync(join4(fullPath, `${current}.neuron`), join4(fullPath, `${newCounter}.neuron`));
|
|
630
|
+
} else {
|
|
631
|
+
writeFileSync3(join4(fullPath, `${newCounter}.neuron`), "", "utf8");
|
|
632
|
+
}
|
|
633
|
+
console.log(`\u{1F525} fired: ${neuronPath} (${current} \u2192 ${newCounter})`);
|
|
634
|
+
return newCounter;
|
|
635
|
+
}
|
|
636
|
+
function getCurrentCounter(dir) {
|
|
637
|
+
let max = 0;
|
|
638
|
+
try {
|
|
639
|
+
for (const entry of readdirSync3(dir)) {
|
|
640
|
+
if (entry.endsWith(".neuron") && !entry.startsWith("dopamine") && !entry.startsWith("memory") && entry !== "bomb.neuron") {
|
|
641
|
+
const n = parseInt(entry, 10);
|
|
642
|
+
if (!isNaN(n) && n > max) max = n;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
} catch {
|
|
646
|
+
}
|
|
647
|
+
return max;
|
|
648
|
+
}
|
|
649
|
+
var init_fire = __esm({
|
|
650
|
+
"src/fire.ts"() {
|
|
651
|
+
"use strict";
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// src/similarity.ts
|
|
656
|
+
function tokenize(name) {
|
|
657
|
+
return name.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[_\-\s]+/g, " ").toLowerCase().split(" ").map(stem).filter((t) => t.length > 1);
|
|
658
|
+
}
|
|
659
|
+
function stem(word) {
|
|
660
|
+
const suffixes = ["ing", "tion", "sion", "ness", "ment", "able", "ible", "ful", "less", "ous", "ive", "ity", "ies", "ed", "er", "es", "ly", "al", "en"];
|
|
661
|
+
for (const suffix of suffixes) {
|
|
662
|
+
if (word.length > suffix.length + 2 && word.endsWith(suffix)) {
|
|
663
|
+
return word.slice(0, -suffix.length);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return word;
|
|
667
|
+
}
|
|
668
|
+
function jaccardSimilarity(a, b) {
|
|
669
|
+
if (a.length === 0 && b.length === 0) return 1;
|
|
670
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
671
|
+
const setA = new Set(a);
|
|
672
|
+
const setB = new Set(b);
|
|
673
|
+
let intersection = 0;
|
|
674
|
+
for (const item of setA) {
|
|
675
|
+
if (setB.has(item)) intersection++;
|
|
676
|
+
}
|
|
677
|
+
const union = (/* @__PURE__ */ new Set([...setA, ...setB])).size;
|
|
678
|
+
return intersection / union;
|
|
679
|
+
}
|
|
680
|
+
var init_similarity = __esm({
|
|
681
|
+
"src/similarity.ts"() {
|
|
682
|
+
"use strict";
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
// src/grow.ts
|
|
687
|
+
var grow_exports = {};
|
|
688
|
+
__export(grow_exports, {
|
|
689
|
+
growNeuron: () => growNeuron
|
|
690
|
+
});
|
|
691
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, existsSync as existsSync6, readdirSync as readdirSync4 } from "fs";
|
|
692
|
+
import { join as join5, relative as relative2 } from "path";
|
|
693
|
+
function growNeuron(brainRoot, neuronPath) {
|
|
694
|
+
const fullPath = join5(brainRoot, neuronPath);
|
|
695
|
+
if (existsSync6(fullPath)) {
|
|
696
|
+
const counter = fireNeuron(brainRoot, neuronPath);
|
|
697
|
+
return { action: "fired", path: neuronPath, counter };
|
|
698
|
+
}
|
|
699
|
+
const parts = neuronPath.split("/");
|
|
700
|
+
const regionName = parts[0];
|
|
701
|
+
if (!REGIONS.includes(regionName)) {
|
|
702
|
+
throw new Error(`Invalid region: ${regionName}. Valid: ${REGIONS.join(", ")}`);
|
|
703
|
+
}
|
|
704
|
+
const leafName = parts[parts.length - 1];
|
|
705
|
+
const newTokens = tokenize(leafName);
|
|
706
|
+
const regionPath = join5(brainRoot, regionName);
|
|
707
|
+
if (existsSync6(regionPath)) {
|
|
708
|
+
const match = findSimilar(regionPath, regionPath, newTokens);
|
|
709
|
+
if (match) {
|
|
710
|
+
const matchRelPath = regionName + "/" + relative2(regionPath, match);
|
|
711
|
+
console.log(`\u{1F504} consolidation: "${neuronPath}" \u2248 "${matchRelPath}" (firing existing)`);
|
|
712
|
+
const counter = fireNeuron(brainRoot, matchRelPath);
|
|
713
|
+
return { action: "fired", path: matchRelPath, counter };
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
mkdirSync4(fullPath, { recursive: true });
|
|
717
|
+
writeFileSync4(join5(fullPath, "1.neuron"), "", "utf8");
|
|
718
|
+
console.log(`\u{1F331} grew: ${neuronPath} (1)`);
|
|
719
|
+
return { action: "grew", path: neuronPath, counter: 1 };
|
|
720
|
+
}
|
|
721
|
+
function findSimilar(dir, regionRoot, targetTokens) {
|
|
722
|
+
let entries;
|
|
723
|
+
try {
|
|
724
|
+
entries = readdirSync4(dir, { withFileTypes: true });
|
|
725
|
+
} catch {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const hasNeuron = entries.some((e) => e.isFile() && e.name.endsWith(".neuron"));
|
|
729
|
+
if (hasNeuron) {
|
|
730
|
+
const folderName = dir.split("/").pop() || "";
|
|
731
|
+
const existingTokens = tokenize(folderName);
|
|
732
|
+
const similarity = jaccardSimilarity(targetTokens, existingTokens);
|
|
733
|
+
if (similarity >= JACCARD_THRESHOLD) {
|
|
734
|
+
return dir;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
for (const entry of entries) {
|
|
738
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
739
|
+
if (entry.isDirectory()) {
|
|
740
|
+
const match = findSimilar(join5(dir, entry.name), regionRoot, targetTokens);
|
|
741
|
+
if (match) return match;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return null;
|
|
745
|
+
}
|
|
746
|
+
var init_grow = __esm({
|
|
747
|
+
"src/grow.ts"() {
|
|
748
|
+
"use strict";
|
|
749
|
+
init_constants();
|
|
750
|
+
init_similarity();
|
|
751
|
+
init_fire();
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
// src/rollback.ts
|
|
756
|
+
var rollback_exports = {};
|
|
757
|
+
__export(rollback_exports, {
|
|
758
|
+
rollbackNeuron: () => rollbackNeuron
|
|
759
|
+
});
|
|
760
|
+
import { renameSync as renameSync2 } from "fs";
|
|
761
|
+
import { join as join6 } from "path";
|
|
762
|
+
function rollbackNeuron(brainRoot, neuronPath) {
|
|
763
|
+
const fullPath = join6(brainRoot, neuronPath);
|
|
764
|
+
const current = getCurrentCounter(fullPath);
|
|
765
|
+
if (current === 0) {
|
|
766
|
+
throw new Error(`Neuron not found: ${neuronPath}`);
|
|
767
|
+
}
|
|
768
|
+
if (current <= 1) {
|
|
769
|
+
throw new Error(`Counter already at minimum (1): ${neuronPath}`);
|
|
770
|
+
}
|
|
771
|
+
const newCounter = current - 1;
|
|
772
|
+
renameSync2(join6(fullPath, `${current}.neuron`), join6(fullPath, `${newCounter}.neuron`));
|
|
773
|
+
console.log(`\u23EA rollback: ${neuronPath} (${current} \u2192 ${newCounter})`);
|
|
774
|
+
return newCounter;
|
|
775
|
+
}
|
|
776
|
+
var init_rollback = __esm({
|
|
777
|
+
"src/rollback.ts"() {
|
|
778
|
+
"use strict";
|
|
779
|
+
init_fire();
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
// src/signal.ts
|
|
784
|
+
var signal_exports = {};
|
|
785
|
+
__export(signal_exports, {
|
|
786
|
+
signalNeuron: () => signalNeuron
|
|
787
|
+
});
|
|
788
|
+
import { writeFileSync as writeFileSync5, existsSync as existsSync7, readdirSync as readdirSync5 } from "fs";
|
|
789
|
+
import { join as join7 } from "path";
|
|
790
|
+
function signalNeuron(brainRoot, neuronPath, signalType) {
|
|
791
|
+
if (!SIGNAL_TYPES.includes(signalType)) {
|
|
792
|
+
throw new Error(`Invalid signal type: ${signalType}. Valid: ${SIGNAL_TYPES.join(", ")}`);
|
|
793
|
+
}
|
|
794
|
+
const fullPath = join7(brainRoot, neuronPath);
|
|
795
|
+
if (!existsSync7(fullPath)) {
|
|
796
|
+
throw new Error(`Neuron not found: ${neuronPath}`);
|
|
797
|
+
}
|
|
798
|
+
switch (signalType) {
|
|
799
|
+
case "bomb": {
|
|
800
|
+
writeFileSync5(join7(fullPath, "bomb.neuron"), "", "utf8");
|
|
801
|
+
console.log(`\u{1F4A3} bomb planted: ${neuronPath}`);
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
case "dopamine": {
|
|
805
|
+
const level = getNextSignalLevel(fullPath, "dopamine");
|
|
806
|
+
writeFileSync5(join7(fullPath, `dopamine${level}.neuron`), "", "utf8");
|
|
807
|
+
console.log(`\u{1F7E2} dopamine +${level}: ${neuronPath}`);
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
case "memory": {
|
|
811
|
+
const level = getNextSignalLevel(fullPath, "memory");
|
|
812
|
+
writeFileSync5(join7(fullPath, `memory${level}.neuron`), "", "utf8");
|
|
813
|
+
console.log(`\u{1F4BE} memory +${level}: ${neuronPath}`);
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function getNextSignalLevel(dir, prefix) {
|
|
819
|
+
let max = 0;
|
|
820
|
+
try {
|
|
821
|
+
for (const entry of readdirSync5(dir)) {
|
|
822
|
+
if (entry.startsWith(prefix) && entry.endsWith(".neuron")) {
|
|
823
|
+
const n = parseInt(entry.replace(prefix, ""), 10);
|
|
824
|
+
if (!isNaN(n) && n > max) max = n;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
} catch {
|
|
828
|
+
}
|
|
829
|
+
return max + 1;
|
|
830
|
+
}
|
|
831
|
+
var init_signal = __esm({
|
|
832
|
+
"src/signal.ts"() {
|
|
833
|
+
"use strict";
|
|
834
|
+
init_constants();
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
// src/decay.ts
|
|
839
|
+
var decay_exports = {};
|
|
840
|
+
__export(decay_exports, {
|
|
841
|
+
runDecay: () => runDecay
|
|
842
|
+
});
|
|
843
|
+
import { readdirSync as readdirSync6, statSync as statSync2, writeFileSync as writeFileSync6, existsSync as existsSync8 } from "fs";
|
|
844
|
+
import { join as join8 } from "path";
|
|
845
|
+
function runDecay(brainRoot, days) {
|
|
846
|
+
const threshold = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
847
|
+
let scanned = 0;
|
|
848
|
+
let decayed = 0;
|
|
849
|
+
for (const regionName of REGIONS) {
|
|
850
|
+
const regionPath = join8(brainRoot, regionName);
|
|
851
|
+
if (!existsSync8(regionPath)) continue;
|
|
852
|
+
const result = decayWalk(regionPath, threshold, 0);
|
|
853
|
+
scanned += result.scanned;
|
|
854
|
+
decayed += result.decayed;
|
|
855
|
+
}
|
|
856
|
+
console.log(`\u{1F4A4} decay: scanned ${scanned} neurons, decayed ${decayed} (>${days} days inactive)`);
|
|
857
|
+
return { scanned, decayed };
|
|
858
|
+
}
|
|
859
|
+
function decayWalk(dir, threshold, depth) {
|
|
860
|
+
if (depth > MAX_DEPTH) return { scanned: 0, decayed: 0 };
|
|
861
|
+
let scanned = 0;
|
|
862
|
+
let decayed = 0;
|
|
863
|
+
let entries;
|
|
864
|
+
try {
|
|
865
|
+
entries = readdirSync6(dir, { withFileTypes: true });
|
|
866
|
+
} catch {
|
|
867
|
+
return { scanned: 0, decayed: 0 };
|
|
868
|
+
}
|
|
869
|
+
let hasNeuronFile = false;
|
|
870
|
+
let isDormant = false;
|
|
871
|
+
let latestMod = 0;
|
|
872
|
+
for (const entry of entries) {
|
|
873
|
+
if (entry.isFile()) {
|
|
874
|
+
if (entry.name.endsWith(".neuron")) {
|
|
875
|
+
hasNeuronFile = true;
|
|
876
|
+
try {
|
|
877
|
+
const st = statSync2(join8(dir, entry.name));
|
|
878
|
+
if (st.mtimeMs > latestMod) latestMod = st.mtimeMs;
|
|
879
|
+
} catch {
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (entry.name.endsWith(".dormant")) {
|
|
883
|
+
isDormant = true;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (hasNeuronFile) {
|
|
888
|
+
scanned++;
|
|
889
|
+
if (!isDormant && latestMod < threshold) {
|
|
890
|
+
const age = Math.floor((Date.now() - latestMod) / (24 * 60 * 60 * 1e3));
|
|
891
|
+
writeFileSync6(
|
|
892
|
+
join8(dir, "decay.dormant"),
|
|
893
|
+
`Dormant since ${(/* @__PURE__ */ new Date()).toISOString()} (${age} days inactive)`,
|
|
894
|
+
"utf8"
|
|
895
|
+
);
|
|
896
|
+
decayed++;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
for (const entry of entries) {
|
|
900
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
901
|
+
if (entry.isDirectory()) {
|
|
902
|
+
const sub = decayWalk(join8(dir, entry.name), threshold, depth + 1);
|
|
903
|
+
scanned += sub.scanned;
|
|
904
|
+
decayed += sub.decayed;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return { scanned, decayed };
|
|
908
|
+
}
|
|
909
|
+
var init_decay = __esm({
|
|
910
|
+
"src/decay.ts"() {
|
|
911
|
+
"use strict";
|
|
912
|
+
init_constants();
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
// src/dedup.ts
|
|
917
|
+
var dedup_exports = {};
|
|
918
|
+
__export(dedup_exports, {
|
|
919
|
+
runDedup: () => runDedup
|
|
920
|
+
});
|
|
921
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
922
|
+
import { join as join9 } from "path";
|
|
923
|
+
function runDedup(brainRoot) {
|
|
924
|
+
const brain = scanBrain(brainRoot);
|
|
925
|
+
let scanned = 0;
|
|
926
|
+
let merged = 0;
|
|
927
|
+
for (const region of brain.regions) {
|
|
928
|
+
const neurons = region.neurons.filter((n) => !n.isDormant);
|
|
929
|
+
scanned += neurons.length;
|
|
930
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
931
|
+
for (let i = 0; i < neurons.length; i++) {
|
|
932
|
+
if (consumed.has(i)) continue;
|
|
933
|
+
const ni = neurons[i];
|
|
934
|
+
const tokensI = tokenize(ni.name);
|
|
935
|
+
for (let j = i + 1; j < neurons.length; j++) {
|
|
936
|
+
if (consumed.has(j)) continue;
|
|
937
|
+
const nj = neurons[j];
|
|
938
|
+
const tokensJ = tokenize(nj.name);
|
|
939
|
+
const sim = jaccardSimilarity(tokensI, tokensJ);
|
|
940
|
+
if (sim >= JACCARD_THRESHOLD) {
|
|
941
|
+
const [keep, drop] = ni.counter >= nj.counter ? [ni, nj] : [nj, ni];
|
|
942
|
+
const relKeep = `${region.name}/${keep.path}`;
|
|
943
|
+
fireNeuron(brainRoot, relKeep);
|
|
944
|
+
writeFileSync7(
|
|
945
|
+
join9(drop.fullPath, "dedup.dormant"),
|
|
946
|
+
`Merged into ${keep.path} on ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
947
|
+
"utf8"
|
|
948
|
+
);
|
|
949
|
+
consumed.add(ni === drop ? i : j);
|
|
950
|
+
merged++;
|
|
951
|
+
console.log(`\u{1F500} merged: "${drop.path}" \u2192 "${keep.path}" (sim=${sim.toFixed(2)})`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
console.log(`\u{1F9F9} dedup: scanned ${scanned} neurons, merged ${merged}`);
|
|
957
|
+
return { scanned, merged };
|
|
958
|
+
}
|
|
959
|
+
var init_dedup = __esm({
|
|
960
|
+
"src/dedup.ts"() {
|
|
961
|
+
"use strict";
|
|
962
|
+
init_constants();
|
|
963
|
+
init_similarity();
|
|
964
|
+
init_fire();
|
|
965
|
+
init_scanner();
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
// src/snapshot.ts
|
|
970
|
+
var snapshot_exports = {};
|
|
971
|
+
__export(snapshot_exports, {
|
|
972
|
+
gitSnapshot: () => gitSnapshot
|
|
973
|
+
});
|
|
974
|
+
import { execSync } from "child_process";
|
|
975
|
+
import { existsSync as existsSync9 } from "fs";
|
|
976
|
+
import { join as join10 } from "path";
|
|
977
|
+
function gitSnapshot(brainRoot) {
|
|
978
|
+
if (!existsSync9(join10(brainRoot, ".git"))) {
|
|
979
|
+
try {
|
|
980
|
+
execSync("git rev-parse --is-inside-work-tree", { cwd: brainRoot, stdio: "pipe" });
|
|
981
|
+
} catch {
|
|
982
|
+
console.log("\u26A0\uFE0F Not a git repository. Run `git init` in the brain directory first.");
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
try {
|
|
987
|
+
const status = execSync("git status --porcelain .", { cwd: brainRoot, encoding: "utf8" });
|
|
988
|
+
if (!status.trim()) {
|
|
989
|
+
console.log("\u2705 No changes to snapshot.");
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
execSync("git add .", { cwd: brainRoot, stdio: "pipe" });
|
|
993
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
994
|
+
execSync(`git commit -m "hebbian snapshot ${ts}"`, { cwd: brainRoot, stdio: "pipe" });
|
|
995
|
+
console.log(`\u{1F4F8} snapshot: committed brain state at ${ts}`);
|
|
996
|
+
return true;
|
|
997
|
+
} catch (err) {
|
|
998
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
999
|
+
console.error(`\u274C snapshot failed: ${message}`);
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
var init_snapshot = __esm({
|
|
1004
|
+
"src/snapshot.ts"() {
|
|
1005
|
+
"use strict";
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
// src/watch.ts
|
|
1010
|
+
var watch_exports = {};
|
|
1011
|
+
__export(watch_exports, {
|
|
1012
|
+
startWatch: () => startWatch
|
|
1013
|
+
});
|
|
1014
|
+
import { watch } from "fs";
|
|
1015
|
+
async function startWatch(brainRoot) {
|
|
1016
|
+
let lastHash = "";
|
|
1017
|
+
let debounceTimer = null;
|
|
1018
|
+
function recompile() {
|
|
1019
|
+
const brain = scanBrain(brainRoot);
|
|
1020
|
+
const result = runSubsumption(brain);
|
|
1021
|
+
const hash = computeHash(result);
|
|
1022
|
+
if (hash === lastHash) return;
|
|
1023
|
+
lastHash = hash;
|
|
1024
|
+
writeAllTiers(brainRoot, result, brain);
|
|
1025
|
+
const ts = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
1026
|
+
console.log(`[${ts}] \u{1F504} recompiled \u2014 ${result.firedNeurons} neurons, activation ${result.totalCounter}${result.bombSource ? ` \u{1F4A3} BOMB: ${result.bombSource}` : ""}`);
|
|
1027
|
+
}
|
|
1028
|
+
recompile();
|
|
1029
|
+
console.log(`\u{1F440} watching: ${brainRoot}`);
|
|
1030
|
+
console.log(" Press Ctrl+C to stop.\n");
|
|
1031
|
+
try {
|
|
1032
|
+
const watcher = watch(brainRoot, { recursive: true }, (eventType, filename) => {
|
|
1033
|
+
if (!filename) return;
|
|
1034
|
+
if (filename.endsWith("_index.md") || filename.endsWith("_rules.md")) return;
|
|
1035
|
+
if (filename.startsWith(".") || filename.includes("/_") || filename.includes("\\_")) return;
|
|
1036
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
1037
|
+
debounceTimer = setTimeout(recompile, 200);
|
|
1038
|
+
});
|
|
1039
|
+
await new Promise((resolve4) => {
|
|
1040
|
+
process.on("SIGINT", () => {
|
|
1041
|
+
watcher.close();
|
|
1042
|
+
console.log("\n\u{1F44B} watch stopped.");
|
|
1043
|
+
resolve4();
|
|
1044
|
+
});
|
|
1045
|
+
});
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM") {
|
|
1048
|
+
console.error("Recursive fs.watch not supported on this platform. Use Node.js >= 22.");
|
|
1049
|
+
} else {
|
|
1050
|
+
throw err;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
function computeHash(result) {
|
|
1055
|
+
return `${result.firedNeurons}:${result.totalCounter}:${result.bombSource}:${result.activeRegions.length}`;
|
|
1056
|
+
}
|
|
1057
|
+
var init_watch = __esm({
|
|
1058
|
+
"src/watch.ts"() {
|
|
1059
|
+
"use strict";
|
|
1060
|
+
init_scanner();
|
|
1061
|
+
init_subsumption();
|
|
1062
|
+
init_emit();
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
// src/episode.ts
|
|
1067
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync10 } from "fs";
|
|
1068
|
+
import { join as join11 } from "path";
|
|
1069
|
+
function logEpisode(brainRoot, type, path, detail) {
|
|
1070
|
+
const logDir = join11(brainRoot, SESSION_LOG_DIR);
|
|
1071
|
+
if (!existsSync10(logDir)) {
|
|
1072
|
+
mkdirSync5(logDir, { recursive: true });
|
|
1073
|
+
}
|
|
1074
|
+
const nextSlot = getNextSlot(logDir);
|
|
1075
|
+
const episode = {
|
|
1076
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1077
|
+
type,
|
|
1078
|
+
path,
|
|
1079
|
+
detail
|
|
1080
|
+
};
|
|
1081
|
+
writeFileSync8(
|
|
1082
|
+
join11(logDir, `memory${nextSlot}.neuron`),
|
|
1083
|
+
JSON.stringify(episode),
|
|
1084
|
+
"utf8"
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
function getNextSlot(logDir) {
|
|
1088
|
+
let maxSlot = 0;
|
|
1089
|
+
try {
|
|
1090
|
+
for (const entry of readdirSync7(logDir)) {
|
|
1091
|
+
if (entry.startsWith("memory") && entry.endsWith(".neuron")) {
|
|
1092
|
+
const n = parseInt(entry.replace("memory", "").replace(".neuron", ""), 10);
|
|
1093
|
+
if (!isNaN(n) && n > maxSlot) maxSlot = n;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
} catch {
|
|
1097
|
+
}
|
|
1098
|
+
const next = maxSlot + 1;
|
|
1099
|
+
return next > MAX_EPISODES ? maxSlot % MAX_EPISODES + 1 : next;
|
|
1100
|
+
}
|
|
1101
|
+
var MAX_EPISODES, SESSION_LOG_DIR;
|
|
1102
|
+
var init_episode = __esm({
|
|
1103
|
+
"src/episode.ts"() {
|
|
1104
|
+
"use strict";
|
|
1105
|
+
MAX_EPISODES = 100;
|
|
1106
|
+
SESSION_LOG_DIR = "hippocampus/session_log";
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
// src/inbox.ts
|
|
1111
|
+
var inbox_exports = {};
|
|
1112
|
+
__export(inbox_exports, {
|
|
1113
|
+
appendCorrection: () => appendCorrection,
|
|
1114
|
+
ensureInbox: () => ensureInbox,
|
|
1115
|
+
processInbox: () => processInbox
|
|
1116
|
+
});
|
|
1117
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync9, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
|
|
1118
|
+
import { join as join12 } from "path";
|
|
1119
|
+
function processInbox(brainRoot) {
|
|
1120
|
+
const inboxPath = join12(brainRoot, INBOX_DIR, CORRECTIONS_FILE);
|
|
1121
|
+
if (!existsSync11(inboxPath)) {
|
|
1122
|
+
return { processed: 0, skipped: 0, errors: [] };
|
|
1123
|
+
}
|
|
1124
|
+
const content = readFileSync4(inboxPath, "utf8").trim();
|
|
1125
|
+
if (!content) {
|
|
1126
|
+
return { processed: 0, skipped: 0, errors: [] };
|
|
1127
|
+
}
|
|
1128
|
+
const lines = content.split("\n").filter(Boolean);
|
|
1129
|
+
let processed = 0;
|
|
1130
|
+
let skipped = 0;
|
|
1131
|
+
const errors = [];
|
|
1132
|
+
for (const line of lines) {
|
|
1133
|
+
let correction;
|
|
1134
|
+
try {
|
|
1135
|
+
correction = JSON.parse(line);
|
|
1136
|
+
} catch {
|
|
1137
|
+
errors.push(`Malformed JSON: ${line.slice(0, 80)}`);
|
|
1138
|
+
skipped++;
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
if (!correction.path || !correction.type) {
|
|
1142
|
+
errors.push(`Missing path or type: ${line.slice(0, 80)}`);
|
|
1143
|
+
skipped++;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (!isPathSafe(correction.path)) {
|
|
1147
|
+
errors.push(`Path traversal blocked: ${correction.path}`);
|
|
1148
|
+
skipped++;
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
const region = correction.path.split("/")[0];
|
|
1152
|
+
if (!region || !REGIONS.includes(region)) {
|
|
1153
|
+
errors.push(`Invalid region in path: ${correction.path}`);
|
|
1154
|
+
skipped++;
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
try {
|
|
1158
|
+
applyCorrection(brainRoot, correction);
|
|
1159
|
+
processed++;
|
|
1160
|
+
} catch (err) {
|
|
1161
|
+
errors.push(`Failed to apply ${correction.path}: ${err.message}`);
|
|
1162
|
+
skipped++;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
writeFileSync9(inboxPath, "", "utf8");
|
|
1166
|
+
console.log(`\u{1F4E5} inbox: processed ${processed}, skipped ${skipped}`);
|
|
1167
|
+
if (errors.length > 0) {
|
|
1168
|
+
for (const err of errors) {
|
|
1169
|
+
console.log(` \u26A0\uFE0F ${err}`);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
return { processed, skipped, errors };
|
|
1173
|
+
}
|
|
1174
|
+
function applyCorrection(brainRoot, correction) {
|
|
1175
|
+
const neuronPath = correction.path;
|
|
1176
|
+
const fullPath = join12(brainRoot, neuronPath);
|
|
1177
|
+
const counterAdd = Math.max(1, correction.counter_add || 1);
|
|
1178
|
+
if (existsSync11(fullPath)) {
|
|
1179
|
+
for (let i = 0; i < counterAdd; i++) {
|
|
1180
|
+
fireNeuron(brainRoot, neuronPath);
|
|
1181
|
+
}
|
|
1182
|
+
} else {
|
|
1183
|
+
growNeuron(brainRoot, neuronPath);
|
|
1184
|
+
for (let i = 1; i < counterAdd; i++) {
|
|
1185
|
+
fireNeuron(brainRoot, neuronPath);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
if (correction.dopamine && correction.dopamine > 0) {
|
|
1189
|
+
const author = (correction.author || "").toLowerCase();
|
|
1190
|
+
if (DOPAMINE_ALLOWED_ROLES.includes(author)) {
|
|
1191
|
+
signalNeuron(brainRoot, neuronPath, "dopamine");
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
logEpisode(brainRoot, "inbox", neuronPath, correction.text || "");
|
|
1195
|
+
}
|
|
1196
|
+
function isPathSafe(path) {
|
|
1197
|
+
if (path.includes("..")) return false;
|
|
1198
|
+
if (path.startsWith("/")) return false;
|
|
1199
|
+
if (path.includes("\\")) return false;
|
|
1200
|
+
if (path.includes("\0")) return false;
|
|
1201
|
+
return true;
|
|
1202
|
+
}
|
|
1203
|
+
function ensureInbox(brainRoot) {
|
|
1204
|
+
const inboxDir = join12(brainRoot, INBOX_DIR);
|
|
1205
|
+
if (!existsSync11(inboxDir)) {
|
|
1206
|
+
mkdirSync6(inboxDir, { recursive: true });
|
|
1207
|
+
}
|
|
1208
|
+
const filePath = join12(inboxDir, CORRECTIONS_FILE);
|
|
1209
|
+
if (!existsSync11(filePath)) {
|
|
1210
|
+
writeFileSync9(filePath, "", "utf8");
|
|
1211
|
+
}
|
|
1212
|
+
return filePath;
|
|
1213
|
+
}
|
|
1214
|
+
function appendCorrection(brainRoot, correction) {
|
|
1215
|
+
const filePath = ensureInbox(brainRoot);
|
|
1216
|
+
const line = JSON.stringify(correction) + "\n";
|
|
1217
|
+
const existing = readFileSync4(filePath, "utf8");
|
|
1218
|
+
writeFileSync9(filePath, existing + line, "utf8");
|
|
1219
|
+
}
|
|
1220
|
+
var INBOX_DIR, CORRECTIONS_FILE, DOPAMINE_ALLOWED_ROLES;
|
|
1221
|
+
var init_inbox = __esm({
|
|
1222
|
+
"src/inbox.ts"() {
|
|
1223
|
+
"use strict";
|
|
1224
|
+
init_constants();
|
|
1225
|
+
init_grow();
|
|
1226
|
+
init_fire();
|
|
1227
|
+
init_signal();
|
|
1228
|
+
init_episode();
|
|
1229
|
+
INBOX_DIR = "_inbox";
|
|
1230
|
+
CORRECTIONS_FILE = "corrections.jsonl";
|
|
1231
|
+
DOPAMINE_ALLOWED_ROLES = ["pm", "admin", "lead"];
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
// src/api.ts
|
|
1236
|
+
var api_exports = {};
|
|
1237
|
+
__export(api_exports, {
|
|
1238
|
+
clearReports: () => clearReports,
|
|
1239
|
+
getLastActivity: () => getLastActivity,
|
|
1240
|
+
getPendingReports: () => getPendingReports,
|
|
1241
|
+
startAPI: () => startAPI
|
|
1242
|
+
});
|
|
1243
|
+
import { createServer } from "http";
|
|
1244
|
+
function buildHealthJSON(brainRoot) {
|
|
1245
|
+
const brain = scanBrain(brainRoot);
|
|
1246
|
+
const result = runSubsumption(brain);
|
|
1247
|
+
return {
|
|
1248
|
+
status: "ok",
|
|
1249
|
+
brain: brainRoot,
|
|
1250
|
+
neurons: result.totalNeurons,
|
|
1251
|
+
activeNeurons: result.firedNeurons,
|
|
1252
|
+
totalActivation: result.totalCounter,
|
|
1253
|
+
bombSource: result.bombSource || null,
|
|
1254
|
+
lastActivity: new Date(lastAPIActivity).toISOString(),
|
|
1255
|
+
uptime: process.uptime()
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
function buildBrainJSON(brainRoot) {
|
|
1259
|
+
const brain = scanBrain(brainRoot);
|
|
1260
|
+
const result = runSubsumption(brain);
|
|
1261
|
+
return {
|
|
1262
|
+
root: brain.root,
|
|
1263
|
+
regions: brain.regions.map((region) => ({
|
|
1264
|
+
name: region.name,
|
|
1265
|
+
icon: REGION_ICONS[region.name] || "",
|
|
1266
|
+
ko: REGION_KO[region.name] || "",
|
|
1267
|
+
priority: region.priority,
|
|
1268
|
+
hasBomb: region.hasBomb,
|
|
1269
|
+
neurons: region.neurons.map((n) => ({
|
|
1270
|
+
name: n.name,
|
|
1271
|
+
path: n.path,
|
|
1272
|
+
counter: n.counter,
|
|
1273
|
+
contra: n.contra,
|
|
1274
|
+
dopamine: n.dopamine,
|
|
1275
|
+
hasBomb: n.hasBomb,
|
|
1276
|
+
hasMemory: n.hasMemory,
|
|
1277
|
+
isDormant: n.isDormant,
|
|
1278
|
+
depth: n.depth,
|
|
1279
|
+
modTime: n.modTime.getTime()
|
|
1280
|
+
})),
|
|
1281
|
+
axons: region.axons
|
|
1282
|
+
})),
|
|
1283
|
+
bombSource: result.bombSource || null,
|
|
1284
|
+
firedNeurons: result.firedNeurons,
|
|
1285
|
+
totalNeurons: result.totalNeurons,
|
|
1286
|
+
totalCounter: result.totalCounter
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
function json(res, data, status = 200) {
|
|
1290
|
+
const body = JSON.stringify(data);
|
|
1291
|
+
res.writeHead(status, {
|
|
1292
|
+
"Content-Type": "application/json",
|
|
1293
|
+
"Access-Control-Allow-Origin": "*",
|
|
1294
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
1295
|
+
"Access-Control-Allow-Headers": "Content-Type"
|
|
1296
|
+
});
|
|
1297
|
+
res.end(body);
|
|
1298
|
+
}
|
|
1299
|
+
function error(res, message, status = 400) {
|
|
1300
|
+
json(res, { error: message }, status);
|
|
1301
|
+
}
|
|
1302
|
+
async function readBody(req) {
|
|
1303
|
+
return new Promise((resolve4, reject) => {
|
|
1304
|
+
const chunks = [];
|
|
1305
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
1306
|
+
req.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
|
|
1307
|
+
req.on("error", reject);
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
async function parseJSON(req) {
|
|
1311
|
+
const body = await readBody(req);
|
|
1312
|
+
if (!body.trim()) return {};
|
|
1313
|
+
return JSON.parse(body);
|
|
1314
|
+
}
|
|
1315
|
+
async function handleRequest(req, res, brainRoot) {
|
|
1316
|
+
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
1317
|
+
const path = url.pathname;
|
|
1318
|
+
const method = req.method || "GET";
|
|
1319
|
+
if (method === "OPTIONS") {
|
|
1320
|
+
json(res, null, 204);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const isMutation = method === "POST";
|
|
1324
|
+
if (isMutation) lastAPIActivity = Date.now();
|
|
1325
|
+
try {
|
|
1326
|
+
if (method === "GET") {
|
|
1327
|
+
switch (path) {
|
|
1328
|
+
case "/api/health":
|
|
1329
|
+
json(res, buildHealthJSON(brainRoot));
|
|
1330
|
+
return;
|
|
1331
|
+
case "/api/brain":
|
|
1332
|
+
json(res, buildBrainJSON(brainRoot));
|
|
1333
|
+
return;
|
|
1334
|
+
case "/api/read": {
|
|
1335
|
+
const region = url.searchParams.get("region");
|
|
1336
|
+
if (!region || !REGIONS.includes(region)) {
|
|
1337
|
+
error(res, `Invalid region. Valid: ${REGIONS.join(", ")}`);
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
const brain = scanBrain(brainRoot);
|
|
1341
|
+
const result = runSubsumption(brain);
|
|
1342
|
+
const regionData = result.activeRegions.find((r) => r.name === region);
|
|
1343
|
+
if (!regionData) {
|
|
1344
|
+
error(res, `Region "${region}" is blocked or empty`);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
const top3 = [...regionData.neurons].filter((n) => !n.isDormant).sort((a, b) => b.counter - a.counter).slice(0, 3);
|
|
1348
|
+
for (const n of top3) {
|
|
1349
|
+
fireNeuron(brainRoot, `${region}/${n.path}`);
|
|
1350
|
+
}
|
|
1351
|
+
json(res, {
|
|
1352
|
+
region,
|
|
1353
|
+
neurons: regionData.neurons,
|
|
1354
|
+
fired: top3.map((n) => n.path)
|
|
1355
|
+
});
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
case "/api/reports":
|
|
1359
|
+
json(res, { reports: pendingReports });
|
|
1360
|
+
return;
|
|
1361
|
+
default:
|
|
1362
|
+
error(res, "Not found", 404);
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (method === "POST") {
|
|
1367
|
+
const body = await parseJSON(req);
|
|
1368
|
+
switch (path) {
|
|
1369
|
+
case "/api/grow": {
|
|
1370
|
+
const neuronPath = body.path;
|
|
1371
|
+
if (!neuronPath) {
|
|
1372
|
+
error(res, 'Missing "path"');
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
const result = growNeuron(brainRoot, neuronPath);
|
|
1376
|
+
json(res, result);
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
case "/api/fire": {
|
|
1380
|
+
const neuronPath = body.path;
|
|
1381
|
+
if (!neuronPath) {
|
|
1382
|
+
error(res, 'Missing "path"');
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
const counter = fireNeuron(brainRoot, neuronPath);
|
|
1386
|
+
json(res, { path: neuronPath, counter });
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
case "/api/signal": {
|
|
1390
|
+
const neuronPath = body.path;
|
|
1391
|
+
const signalType = body.type;
|
|
1392
|
+
if (!neuronPath || !signalType) {
|
|
1393
|
+
error(res, 'Missing "path" or "type"');
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
signalNeuron(brainRoot, neuronPath, signalType);
|
|
1397
|
+
json(res, { path: neuronPath, type: signalType });
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
case "/api/rollback": {
|
|
1401
|
+
const neuronPath = body.path;
|
|
1402
|
+
if (!neuronPath) {
|
|
1403
|
+
error(res, 'Missing "path"');
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
const counter = rollbackNeuron(brainRoot, neuronPath);
|
|
1407
|
+
json(res, { path: neuronPath, counter });
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
case "/api/decay": {
|
|
1411
|
+
const days = typeof body.days === "number" ? body.days : 30;
|
|
1412
|
+
const result = runDecay(brainRoot, days);
|
|
1413
|
+
json(res, result);
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
case "/api/dedup": {
|
|
1417
|
+
const result = runDedup(brainRoot);
|
|
1418
|
+
json(res, result);
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
case "/api/inject": {
|
|
1422
|
+
const brain = scanBrain(brainRoot);
|
|
1423
|
+
const result = runSubsumption(brain);
|
|
1424
|
+
writeAllTiers(brainRoot, result, brain);
|
|
1425
|
+
json(res, { injected: true });
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
case "/api/inbox": {
|
|
1429
|
+
const result = processInbox(brainRoot);
|
|
1430
|
+
json(res, result);
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
case "/api/report": {
|
|
1434
|
+
const message = body.message;
|
|
1435
|
+
const priority = body.priority || "normal";
|
|
1436
|
+
if (!message) {
|
|
1437
|
+
error(res, 'Missing "message"');
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
const entry = {
|
|
1441
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1442
|
+
message,
|
|
1443
|
+
priority
|
|
1444
|
+
};
|
|
1445
|
+
pendingReports.push(entry);
|
|
1446
|
+
json(res, entry);
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
default:
|
|
1450
|
+
error(res, "Not found", 404);
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
error(res, "Method not allowed", 405);
|
|
1455
|
+
} catch (err) {
|
|
1456
|
+
error(res, err.message, 500);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
function startAPI(brainRoot, port = 9090) {
|
|
1460
|
+
const server = createServer((req, res) => {
|
|
1461
|
+
handleRequest(req, res, brainRoot).catch((err) => {
|
|
1462
|
+
error(res, err.message, 500);
|
|
1463
|
+
});
|
|
1464
|
+
});
|
|
1465
|
+
server.listen(port, () => {
|
|
1466
|
+
console.log(`\u{1F9E0} hebbian API listening on http://localhost:${port}`);
|
|
1467
|
+
console.log(` Brain: ${brainRoot}`);
|
|
1468
|
+
console.log("");
|
|
1469
|
+
console.log(" Endpoints:");
|
|
1470
|
+
console.log(" GET /api/health Process health + brain stats");
|
|
1471
|
+
console.log(" GET /api/brain Full brain state JSON");
|
|
1472
|
+
console.log(" GET /api/read?region=X Read region (auto-fires top 3)");
|
|
1473
|
+
console.log(" GET /api/reports List pending reports");
|
|
1474
|
+
console.log(' POST /api/grow {"path":"cortex/..."}');
|
|
1475
|
+
console.log(' POST /api/fire {"path":"cortex/..."}');
|
|
1476
|
+
console.log(' POST /api/signal {"path":"...","type":"dopamine"}');
|
|
1477
|
+
console.log(' POST /api/rollback {"path":"cortex/..."}');
|
|
1478
|
+
console.log(' POST /api/decay {"days":30}');
|
|
1479
|
+
console.log(" POST /api/dedup Batch merge similar neurons");
|
|
1480
|
+
console.log(" POST /api/inject Force re-emit all tiers");
|
|
1481
|
+
console.log(" POST /api/inbox Process corrections inbox");
|
|
1482
|
+
console.log(' POST /api/report {"message":"...","priority":"normal"}');
|
|
1483
|
+
});
|
|
1484
|
+
return server;
|
|
1485
|
+
}
|
|
1486
|
+
function getLastActivity() {
|
|
1487
|
+
return lastAPIActivity;
|
|
1488
|
+
}
|
|
1489
|
+
function getPendingReports() {
|
|
1490
|
+
return pendingReports;
|
|
1491
|
+
}
|
|
1492
|
+
function clearReports() {
|
|
1493
|
+
pendingReports.length = 0;
|
|
1494
|
+
}
|
|
1495
|
+
var lastAPIActivity, pendingReports;
|
|
1496
|
+
var init_api = __esm({
|
|
1497
|
+
"src/api.ts"() {
|
|
1498
|
+
"use strict";
|
|
1499
|
+
init_scanner();
|
|
1500
|
+
init_subsumption();
|
|
1501
|
+
init_fire();
|
|
1502
|
+
init_rollback();
|
|
1503
|
+
init_grow();
|
|
1504
|
+
init_signal();
|
|
1505
|
+
init_decay();
|
|
1506
|
+
init_dedup();
|
|
1507
|
+
init_emit();
|
|
1508
|
+
init_inbox();
|
|
1509
|
+
init_constants();
|
|
1510
|
+
lastAPIActivity = Date.now();
|
|
1511
|
+
pendingReports = [];
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1515
|
+
// src/hooks.ts
|
|
1516
|
+
var hooks_exports = {};
|
|
1517
|
+
__export(hooks_exports, {
|
|
1518
|
+
checkHooks: () => checkHooks,
|
|
1519
|
+
installHooks: () => installHooks,
|
|
1520
|
+
uninstallHooks: () => uninstallHooks
|
|
1521
|
+
});
|
|
1522
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync10, existsSync as existsSync12, mkdirSync as mkdirSync7 } from "fs";
|
|
1523
|
+
import { join as join13, resolve as resolve2 } from "path";
|
|
1524
|
+
function installHooks(brainRoot, projectRoot) {
|
|
1525
|
+
const root = projectRoot || process.cwd();
|
|
1526
|
+
const settingsDir = join13(root, SETTINGS_DIR);
|
|
1527
|
+
const settingsPath = join13(settingsDir, SETTINGS_FILE);
|
|
1528
|
+
const defaultBrain = resolve2(root, "brain");
|
|
1529
|
+
const resolvedBrain = resolve2(brainRoot);
|
|
1530
|
+
const brainFlag = resolvedBrain === defaultBrain ? "" : ` --brain ${resolvedBrain}`;
|
|
1531
|
+
let settings = {};
|
|
1532
|
+
if (existsSync12(settingsPath)) {
|
|
1533
|
+
try {
|
|
1534
|
+
settings = JSON.parse(readFileSync5(settingsPath, "utf8"));
|
|
1535
|
+
} catch {
|
|
1536
|
+
console.log(`\u26A0\uFE0F settings.local.json was malformed, overwriting`);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
if (!settings.hooks || typeof settings.hooks !== "object") {
|
|
1540
|
+
settings.hooks = {};
|
|
1541
|
+
}
|
|
1542
|
+
const hooks = settings.hooks;
|
|
1543
|
+
const hebbianHooks = [
|
|
1544
|
+
{
|
|
1545
|
+
event: "SessionStart",
|
|
1546
|
+
matcher: "startup|resume",
|
|
1547
|
+
entry: {
|
|
1548
|
+
type: "command",
|
|
1549
|
+
command: `hebbian emit claude${brainFlag}`,
|
|
1550
|
+
timeout: 10,
|
|
1551
|
+
statusMessage: `${HOOK_MARKER} refreshing brain`
|
|
1552
|
+
}
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
event: "Stop",
|
|
1556
|
+
entry: {
|
|
1557
|
+
type: "command",
|
|
1558
|
+
command: `hebbian digest${brainFlag}`,
|
|
1559
|
+
timeout: 30,
|
|
1560
|
+
statusMessage: `${HOOK_MARKER} digesting session`
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
];
|
|
1564
|
+
for (const { event, matcher, entry } of hebbianHooks) {
|
|
1565
|
+
if (!hooks[event]) {
|
|
1566
|
+
hooks[event] = [];
|
|
1567
|
+
}
|
|
1568
|
+
const existingIdx = hooks[event].findIndex(
|
|
1569
|
+
(group2) => group2.hooks.some((h) => h.statusMessage?.startsWith(HOOK_MARKER))
|
|
1570
|
+
);
|
|
1571
|
+
const group = {
|
|
1572
|
+
...matcher ? { matcher } : {},
|
|
1573
|
+
hooks: [entry]
|
|
1574
|
+
};
|
|
1575
|
+
if (existingIdx >= 0) {
|
|
1576
|
+
hooks[event][existingIdx] = group;
|
|
1577
|
+
} else {
|
|
1578
|
+
hooks[event].push(group);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
if (!existsSync12(settingsDir)) {
|
|
1582
|
+
mkdirSync7(settingsDir, { recursive: true });
|
|
1583
|
+
}
|
|
1584
|
+
writeFileSync10(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
1585
|
+
console.log(`\u2705 hebbian hooks installed at ${settingsPath}`);
|
|
1586
|
+
console.log(` SessionStart \u2192 hebbian emit claude${brainFlag}`);
|
|
1587
|
+
console.log(` Stop \u2192 hebbian digest${brainFlag}`);
|
|
1588
|
+
}
|
|
1589
|
+
function uninstallHooks(projectRoot) {
|
|
1590
|
+
const root = projectRoot || process.cwd();
|
|
1591
|
+
const settingsPath = join13(root, SETTINGS_DIR, SETTINGS_FILE);
|
|
1592
|
+
if (!existsSync12(settingsPath)) {
|
|
1593
|
+
console.log("No hooks installed (settings.local.json not found)");
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
let settings;
|
|
1597
|
+
try {
|
|
1598
|
+
settings = JSON.parse(readFileSync5(settingsPath, "utf8"));
|
|
1599
|
+
} catch {
|
|
1600
|
+
console.log("settings.local.json is malformed, nothing to uninstall");
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (!settings.hooks || typeof settings.hooks !== "object") {
|
|
1604
|
+
console.log("No hooks found in settings.local.json");
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
const hooks = settings.hooks;
|
|
1608
|
+
let removed = 0;
|
|
1609
|
+
for (const event of Object.keys(hooks)) {
|
|
1610
|
+
const before = hooks[event].length;
|
|
1611
|
+
hooks[event] = hooks[event].filter(
|
|
1612
|
+
(group) => !group.hooks.some((h) => h.statusMessage?.startsWith(HOOK_MARKER))
|
|
1613
|
+
);
|
|
1614
|
+
removed += before - hooks[event].length;
|
|
1615
|
+
if (hooks[event].length === 0) {
|
|
1616
|
+
delete hooks[event];
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
if (Object.keys(hooks).length === 0) {
|
|
1620
|
+
delete settings.hooks;
|
|
1621
|
+
}
|
|
1622
|
+
writeFileSync10(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
1623
|
+
console.log(`\u2705 removed ${removed} hebbian hook(s) from ${settingsPath}`);
|
|
1624
|
+
}
|
|
1625
|
+
function checkHooks(projectRoot) {
|
|
1626
|
+
const root = projectRoot || process.cwd();
|
|
1627
|
+
const settingsPath = join13(root, SETTINGS_DIR, SETTINGS_FILE);
|
|
1628
|
+
const status = {
|
|
1629
|
+
installed: false,
|
|
1630
|
+
path: settingsPath,
|
|
1631
|
+
events: []
|
|
1632
|
+
};
|
|
1633
|
+
if (!existsSync12(settingsPath)) {
|
|
1634
|
+
console.log(`\u274C hebbian hooks not installed (${settingsPath} not found)`);
|
|
1635
|
+
return status;
|
|
1636
|
+
}
|
|
1637
|
+
let settings;
|
|
1638
|
+
try {
|
|
1639
|
+
settings = JSON.parse(readFileSync5(settingsPath, "utf8"));
|
|
1640
|
+
} catch {
|
|
1641
|
+
console.log(`\u274C settings.local.json is malformed`);
|
|
1642
|
+
return status;
|
|
1643
|
+
}
|
|
1644
|
+
if (!settings.hooks || typeof settings.hooks !== "object") {
|
|
1645
|
+
console.log(`\u274C no hooks in ${settingsPath}`);
|
|
1646
|
+
return status;
|
|
1647
|
+
}
|
|
1648
|
+
const hooks = settings.hooks;
|
|
1649
|
+
for (const event of Object.keys(hooks)) {
|
|
1650
|
+
const hasHebbian = hooks[event].some(
|
|
1651
|
+
(group) => group.hooks.some((h) => h.statusMessage?.startsWith(HOOK_MARKER))
|
|
1652
|
+
);
|
|
1653
|
+
if (hasHebbian) {
|
|
1654
|
+
status.events.push(event);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
status.installed = status.events.length > 0;
|
|
1658
|
+
if (status.installed) {
|
|
1659
|
+
console.log(`\u2705 hebbian hooks installed at ${settingsPath}`);
|
|
1660
|
+
for (const event of status.events) {
|
|
1661
|
+
console.log(` ${event} \u2714`);
|
|
1662
|
+
}
|
|
1663
|
+
} else {
|
|
1664
|
+
console.log(`\u274C hebbian hooks not found in ${settingsPath}`);
|
|
1665
|
+
}
|
|
1666
|
+
return status;
|
|
1667
|
+
}
|
|
1668
|
+
var SETTINGS_DIR, SETTINGS_FILE;
|
|
1669
|
+
var init_hooks = __esm({
|
|
1670
|
+
"src/hooks.ts"() {
|
|
1671
|
+
"use strict";
|
|
1672
|
+
init_constants();
|
|
1673
|
+
SETTINGS_DIR = ".claude";
|
|
1674
|
+
SETTINGS_FILE = "settings.local.json";
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
// src/digest.ts
|
|
1679
|
+
var digest_exports = {};
|
|
1680
|
+
__export(digest_exports, {
|
|
1681
|
+
digestTranscript: () => digestTranscript,
|
|
1682
|
+
extractCorrections: () => extractCorrections,
|
|
1683
|
+
readHookInput: () => readHookInput
|
|
1684
|
+
});
|
|
1685
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync11, existsSync as existsSync13, mkdirSync as mkdirSync8 } from "fs";
|
|
1686
|
+
import { join as join14, basename } from "path";
|
|
1687
|
+
function readHookInput(stdin) {
|
|
1688
|
+
if (!stdin.trim()) return null;
|
|
1689
|
+
try {
|
|
1690
|
+
const input = JSON.parse(stdin);
|
|
1691
|
+
if (input.transcript_path) {
|
|
1692
|
+
const sessionId = input.session_id || basename(input.transcript_path, ".jsonl");
|
|
1693
|
+
return { transcriptPath: input.transcript_path, sessionId };
|
|
1694
|
+
}
|
|
1695
|
+
return null;
|
|
1696
|
+
} catch {
|
|
1697
|
+
return null;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
function digestTranscript(brainRoot, transcriptPath, sessionId) {
|
|
1701
|
+
if (!existsSync13(transcriptPath)) {
|
|
1702
|
+
throw new Error(`Transcript not found: ${transcriptPath}`);
|
|
1703
|
+
}
|
|
1704
|
+
const resolvedSessionId = sessionId || basename(transcriptPath, ".jsonl");
|
|
1705
|
+
const logDir = join14(brainRoot, DIGEST_LOG_DIR);
|
|
1706
|
+
const logPath = join14(logDir, `${resolvedSessionId}.jsonl`);
|
|
1707
|
+
if (existsSync13(logPath)) {
|
|
1708
|
+
console.log(`\u23ED already digested session ${resolvedSessionId}, skip`);
|
|
1709
|
+
return { corrections: 0, skipped: 0, transcriptPath, sessionId: resolvedSessionId };
|
|
1710
|
+
}
|
|
1711
|
+
const messages = parseTranscript(transcriptPath);
|
|
1712
|
+
const corrections = extractCorrections(messages);
|
|
1713
|
+
if (corrections.length === 0) {
|
|
1714
|
+
console.log(`\u{1F4DD} digest: no corrections found in session ${resolvedSessionId}`);
|
|
1715
|
+
writeAuditLog(brainRoot, resolvedSessionId, []);
|
|
1716
|
+
return { corrections: 0, skipped: messages.length, transcriptPath, sessionId: resolvedSessionId };
|
|
1717
|
+
}
|
|
1718
|
+
let applied = 0;
|
|
1719
|
+
const auditEntries = [];
|
|
1720
|
+
for (const correction of corrections) {
|
|
1721
|
+
try {
|
|
1722
|
+
growNeuron(brainRoot, correction.path);
|
|
1723
|
+
logEpisode(brainRoot, "digest", correction.path, correction.text);
|
|
1724
|
+
auditEntries.push({ correction, applied: true });
|
|
1725
|
+
applied++;
|
|
1726
|
+
} catch (err) {
|
|
1727
|
+
console.log(` \u26A0\uFE0F failed to apply: ${correction.path} \u2014 ${err.message}`);
|
|
1728
|
+
auditEntries.push({ correction, applied: false });
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
writeAuditLog(brainRoot, resolvedSessionId, auditEntries);
|
|
1732
|
+
console.log(`\u{1F4DD} digest: ${applied} correction(s) from session ${resolvedSessionId}`);
|
|
1733
|
+
return {
|
|
1734
|
+
corrections: applied,
|
|
1735
|
+
skipped: messages.length - corrections.length,
|
|
1736
|
+
transcriptPath,
|
|
1737
|
+
sessionId: resolvedSessionId
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
function parseTranscript(transcriptPath) {
|
|
1741
|
+
const content = readFileSync6(transcriptPath, "utf8");
|
|
1742
|
+
const lines = content.split("\n").filter(Boolean);
|
|
1743
|
+
const messages = [];
|
|
1744
|
+
for (const line of lines) {
|
|
1745
|
+
let entry;
|
|
1746
|
+
try {
|
|
1747
|
+
entry = JSON.parse(line);
|
|
1748
|
+
} catch {
|
|
1749
|
+
continue;
|
|
1750
|
+
}
|
|
1751
|
+
if (entry.type !== "user") continue;
|
|
1752
|
+
if (!entry.message || entry.message.role !== "user") continue;
|
|
1753
|
+
const text = extractText(entry.message.content);
|
|
1754
|
+
if (text) messages.push(text);
|
|
1755
|
+
}
|
|
1756
|
+
return messages;
|
|
1757
|
+
}
|
|
1758
|
+
function extractText(content) {
|
|
1759
|
+
if (!content) return null;
|
|
1760
|
+
if (typeof content === "string") return content;
|
|
1761
|
+
if (Array.isArray(content)) {
|
|
1762
|
+
const texts = content.filter((block) => block.type === "text" && block.text).map((block) => block.text);
|
|
1763
|
+
return texts.length > 0 ? texts.join("\n") : null;
|
|
1764
|
+
}
|
|
1765
|
+
return null;
|
|
1766
|
+
}
|
|
1767
|
+
function extractCorrections(messages) {
|
|
1768
|
+
const corrections = [];
|
|
1769
|
+
for (const text of messages) {
|
|
1770
|
+
if (corrections.length >= MAX_CORRECTIONS_PER_SESSION) break;
|
|
1771
|
+
if (text.length < MIN_CORRECTION_LENGTH) continue;
|
|
1772
|
+
if (/^[\/!]/.test(text.trim())) continue;
|
|
1773
|
+
if (text.trim().endsWith("?")) continue;
|
|
1774
|
+
const correction = detectCorrection(text);
|
|
1775
|
+
if (correction) {
|
|
1776
|
+
corrections.push(correction);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
return corrections;
|
|
1780
|
+
}
|
|
1781
|
+
function detectCorrection(text) {
|
|
1782
|
+
const isNegation = NEGATION_PATTERNS.some((p) => p.test(text));
|
|
1783
|
+
const isAffirmation = AFFIRMATION_PATTERNS.some((p) => p.test(text));
|
|
1784
|
+
if (!isNegation && !isAffirmation) return null;
|
|
1785
|
+
const prefix = isNegation ? "NO" : "DO";
|
|
1786
|
+
const keywords = extractKeywords(text);
|
|
1787
|
+
if (keywords.length === 0) return null;
|
|
1788
|
+
const pathSegment = `${prefix}_${keywords.slice(0, 4).join("_")}`;
|
|
1789
|
+
const path = `cortex/${pathSegment}`;
|
|
1790
|
+
return { text, path, prefix, keywords };
|
|
1791
|
+
}
|
|
1792
|
+
function extractKeywords(text) {
|
|
1793
|
+
const STOP_WORDS = /* @__PURE__ */ new Set([
|
|
1794
|
+
"the",
|
|
1795
|
+
"a",
|
|
1796
|
+
"an",
|
|
1797
|
+
"is",
|
|
1798
|
+
"are",
|
|
1799
|
+
"was",
|
|
1800
|
+
"were",
|
|
1801
|
+
"be",
|
|
1802
|
+
"been",
|
|
1803
|
+
"being",
|
|
1804
|
+
"have",
|
|
1805
|
+
"has",
|
|
1806
|
+
"had",
|
|
1807
|
+
"do",
|
|
1808
|
+
"does",
|
|
1809
|
+
"did",
|
|
1810
|
+
"will",
|
|
1811
|
+
"would",
|
|
1812
|
+
"could",
|
|
1813
|
+
"should",
|
|
1814
|
+
"may",
|
|
1815
|
+
"might",
|
|
1816
|
+
"shall",
|
|
1817
|
+
"can",
|
|
1818
|
+
"need",
|
|
1819
|
+
"dare",
|
|
1820
|
+
"ought",
|
|
1821
|
+
"to",
|
|
1822
|
+
"of",
|
|
1823
|
+
"in",
|
|
1824
|
+
"for",
|
|
1825
|
+
"on",
|
|
1826
|
+
"with",
|
|
1827
|
+
"at",
|
|
1828
|
+
"by",
|
|
1829
|
+
"from",
|
|
1830
|
+
"as",
|
|
1831
|
+
"into",
|
|
1832
|
+
"through",
|
|
1833
|
+
"during",
|
|
1834
|
+
"before",
|
|
1835
|
+
"after",
|
|
1836
|
+
"above",
|
|
1837
|
+
"below",
|
|
1838
|
+
"and",
|
|
1839
|
+
"but",
|
|
1840
|
+
"or",
|
|
1841
|
+
"nor",
|
|
1842
|
+
"not",
|
|
1843
|
+
"so",
|
|
1844
|
+
"yet",
|
|
1845
|
+
"both",
|
|
1846
|
+
"either",
|
|
1847
|
+
"neither",
|
|
1848
|
+
"each",
|
|
1849
|
+
"every",
|
|
1850
|
+
"all",
|
|
1851
|
+
"any",
|
|
1852
|
+
"few",
|
|
1853
|
+
"more",
|
|
1854
|
+
"most",
|
|
1855
|
+
"other",
|
|
1856
|
+
"some",
|
|
1857
|
+
"such",
|
|
1858
|
+
"no",
|
|
1859
|
+
"only",
|
|
1860
|
+
"own",
|
|
1861
|
+
"same",
|
|
1862
|
+
"than",
|
|
1863
|
+
"too",
|
|
1864
|
+
"very",
|
|
1865
|
+
"just",
|
|
1866
|
+
"because",
|
|
1867
|
+
"until",
|
|
1868
|
+
"while",
|
|
1869
|
+
"that",
|
|
1870
|
+
"this",
|
|
1871
|
+
"these",
|
|
1872
|
+
"those",
|
|
1873
|
+
"it",
|
|
1874
|
+
"its",
|
|
1875
|
+
"i",
|
|
1876
|
+
"me",
|
|
1877
|
+
"my",
|
|
1878
|
+
"we",
|
|
1879
|
+
"us",
|
|
1880
|
+
"you",
|
|
1881
|
+
"your",
|
|
1882
|
+
"he",
|
|
1883
|
+
"she",
|
|
1884
|
+
"they",
|
|
1885
|
+
"them",
|
|
1886
|
+
"what",
|
|
1887
|
+
"which",
|
|
1888
|
+
"who",
|
|
1889
|
+
"whom",
|
|
1890
|
+
// Correction-specific stop words
|
|
1891
|
+
"don",
|
|
1892
|
+
"dont",
|
|
1893
|
+
"stop",
|
|
1894
|
+
"never",
|
|
1895
|
+
"always",
|
|
1896
|
+
"instead",
|
|
1897
|
+
"use",
|
|
1898
|
+
"avoid",
|
|
1899
|
+
"please",
|
|
1900
|
+
"must",
|
|
1901
|
+
"should",
|
|
1902
|
+
"like",
|
|
1903
|
+
"want",
|
|
1904
|
+
"think"
|
|
1905
|
+
]);
|
|
1906
|
+
const tokens = tokenize(text);
|
|
1907
|
+
return tokens.filter((t) => !STOP_WORDS.has(t) && t.length > 2);
|
|
1908
|
+
}
|
|
1909
|
+
function writeAuditLog(brainRoot, sessionId, entries) {
|
|
1910
|
+
const logDir = join14(brainRoot, DIGEST_LOG_DIR);
|
|
1911
|
+
if (!existsSync13(logDir)) {
|
|
1912
|
+
mkdirSync8(logDir, { recursive: true });
|
|
1913
|
+
}
|
|
1914
|
+
const logPath = join14(logDir, `${sessionId}.jsonl`);
|
|
1915
|
+
const lines = entries.map(
|
|
1916
|
+
(e) => JSON.stringify({
|
|
1917
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1918
|
+
path: e.correction.path,
|
|
1919
|
+
text: e.correction.text,
|
|
1920
|
+
prefix: e.correction.prefix,
|
|
1921
|
+
keywords: e.correction.keywords,
|
|
1922
|
+
applied: e.applied
|
|
1923
|
+
})
|
|
1924
|
+
);
|
|
1925
|
+
writeFileSync11(logPath, lines.join("\n") + (lines.length > 0 ? "\n" : ""), "utf8");
|
|
1926
|
+
}
|
|
1927
|
+
var NEGATION_PATTERNS, AFFIRMATION_PATTERNS;
|
|
1928
|
+
var init_digest = __esm({
|
|
1929
|
+
"src/digest.ts"() {
|
|
1930
|
+
"use strict";
|
|
1931
|
+
init_constants();
|
|
1932
|
+
init_grow();
|
|
1933
|
+
init_episode();
|
|
1934
|
+
init_similarity();
|
|
1935
|
+
NEGATION_PATTERNS = [
|
|
1936
|
+
/\bdon[''\u2019]?t\b/i,
|
|
1937
|
+
/\bdo not\b/i,
|
|
1938
|
+
/\bstop\s+\w+ing\b/i,
|
|
1939
|
+
/\bnever\b/i,
|
|
1940
|
+
/\binstead\b/i,
|
|
1941
|
+
/^no[,.\s!]/i,
|
|
1942
|
+
/\bdon[''\u2019]?t\s+use\b/i,
|
|
1943
|
+
/\bavoid\b/i,
|
|
1944
|
+
// Korean negation
|
|
1945
|
+
/하지\s*마/,
|
|
1946
|
+
/안\s*돼/,
|
|
1947
|
+
/대신/,
|
|
1948
|
+
/쓰지\s*마/,
|
|
1949
|
+
/않/
|
|
1950
|
+
];
|
|
1951
|
+
AFFIRMATION_PATTERNS = [
|
|
1952
|
+
/\balways\b/i,
|
|
1953
|
+
/\bmust\b/i,
|
|
1954
|
+
/\bshould\s+always\b/i,
|
|
1955
|
+
/\buse\s+\w+\s+instead\b/i,
|
|
1956
|
+
// Korean affirmation
|
|
1957
|
+
/항상/,
|
|
1958
|
+
/반드시/
|
|
1959
|
+
];
|
|
1960
|
+
}
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
// src/cli.ts
|
|
1964
|
+
init_constants();
|
|
1965
|
+
import { parseArgs } from "util";
|
|
1966
|
+
import { resolve as resolve3 } from "path";
|
|
1967
|
+
var VERSION = "0.3.0";
|
|
1968
|
+
var HELP = `
|
|
1969
|
+
hebbian v${VERSION} \u2014 Folder-as-neuron brain for any AI agent.
|
|
1970
|
+
|
|
1971
|
+
"Neurons that fire together, wire together." \u2014 Donald Hebb (1949)
|
|
1972
|
+
|
|
1973
|
+
USAGE:
|
|
1974
|
+
hebbian <command> [options]
|
|
1975
|
+
|
|
1976
|
+
COMMANDS:
|
|
1977
|
+
init <path> Create brain with 7 regions
|
|
1978
|
+
emit <target> [--brain <path>] Compile rules (claude/cursor/gemini/copilot/generic/all)
|
|
1979
|
+
fire <neuron-path> Increment neuron counter (+1)
|
|
1980
|
+
grow <neuron-path> Create neuron (with merge detection)
|
|
1981
|
+
rollback <neuron-path> Decrement neuron counter (min=1)
|
|
1982
|
+
signal <type> <neuron-path> Add signal (dopamine/bomb/memory)
|
|
1983
|
+
decay [--days N] Mark inactive neurons dormant (default 30)
|
|
1984
|
+
dedup Batch merge similar neurons (Jaccard >= 0.6)
|
|
1985
|
+
snapshot Git commit current brain state
|
|
1986
|
+
watch Watch for changes + auto-recompile
|
|
1987
|
+
api [--port N] Start REST API server (default 9090)
|
|
1988
|
+
inbox Process corrections inbox
|
|
1989
|
+
claude install|uninstall|status Manage Claude Code hooks
|
|
1990
|
+
digest [--transcript <path>] Extract corrections from conversation
|
|
1991
|
+
diag Print brain diagnostics
|
|
1992
|
+
stats Print brain statistics
|
|
1993
|
+
|
|
1994
|
+
OPTIONS:
|
|
1995
|
+
--brain <path> Brain directory (default: $HEBBIAN_BRAIN or ./brain)
|
|
1996
|
+
--help, -h Show this help
|
|
1997
|
+
--version, -v Show version
|
|
1998
|
+
|
|
1999
|
+
EXAMPLES:
|
|
2000
|
+
hebbian init ./my-brain
|
|
2001
|
+
hebbian grow cortex/frontend/NO_console_log --brain ./my-brain
|
|
2002
|
+
hebbian fire cortex/frontend/NO_console_log --brain ./my-brain
|
|
2003
|
+
hebbian emit claude --brain ./my-brain
|
|
2004
|
+
hebbian emit all
|
|
2005
|
+
`.trim();
|
|
2006
|
+
function readStdin() {
|
|
2007
|
+
return new Promise((resolve4) => {
|
|
2008
|
+
if (process.stdin.isTTY) {
|
|
2009
|
+
resolve4("");
|
|
2010
|
+
return;
|
|
2011
|
+
}
|
|
2012
|
+
const chunks = [];
|
|
2013
|
+
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
2014
|
+
process.stdin.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
|
|
2015
|
+
process.stdin.on("error", () => resolve4(""));
|
|
2016
|
+
setTimeout(() => {
|
|
2017
|
+
process.stdin.destroy();
|
|
2018
|
+
resolve4(Buffer.concat(chunks).toString("utf8"));
|
|
2019
|
+
}, 1e3);
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
async function main(argv) {
|
|
2023
|
+
const { values, positionals } = parseArgs({
|
|
2024
|
+
args: argv,
|
|
2025
|
+
options: {
|
|
2026
|
+
brain: { type: "string", short: "b" },
|
|
2027
|
+
days: { type: "string", short: "d" },
|
|
2028
|
+
port: { type: "string", short: "p" },
|
|
2029
|
+
transcript: { type: "string", short: "t" },
|
|
2030
|
+
help: { type: "boolean", short: "h" },
|
|
2031
|
+
version: { type: "boolean", short: "v" }
|
|
2032
|
+
},
|
|
2033
|
+
allowPositionals: true,
|
|
2034
|
+
strict: false
|
|
2035
|
+
});
|
|
2036
|
+
if (values.version) {
|
|
2037
|
+
console.log(`hebbian v${VERSION}`);
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
const command = positionals[0];
|
|
2041
|
+
if (values.help || !command) {
|
|
2042
|
+
console.log(HELP);
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
const brainRoot = resolveBrainRoot(values.brain);
|
|
2046
|
+
switch (command) {
|
|
2047
|
+
case "init": {
|
|
2048
|
+
const target = positionals[1];
|
|
2049
|
+
if (!target) {
|
|
2050
|
+
console.error("Usage: hebbian init <path>");
|
|
2051
|
+
process.exit(1);
|
|
2052
|
+
}
|
|
2053
|
+
const { initBrain: initBrain2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
2054
|
+
await initBrain2(resolve3(target));
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
2057
|
+
case "emit": {
|
|
2058
|
+
const target = positionals[1];
|
|
2059
|
+
if (!target) {
|
|
2060
|
+
console.error("Usage: hebbian emit <target> (claude/cursor/gemini/copilot/generic/all)");
|
|
2061
|
+
process.exit(1);
|
|
2062
|
+
}
|
|
2063
|
+
const { emitToTarget: emitToTarget2 } = await Promise.resolve().then(() => (init_emit(), emit_exports));
|
|
2064
|
+
await emitToTarget2(brainRoot, target);
|
|
2065
|
+
break;
|
|
2066
|
+
}
|
|
2067
|
+
case "fire": {
|
|
2068
|
+
const neuronPath = positionals[1];
|
|
2069
|
+
if (!neuronPath) {
|
|
2070
|
+
console.error("Usage: hebbian fire <neuron-path>");
|
|
2071
|
+
process.exit(1);
|
|
2072
|
+
}
|
|
2073
|
+
const { fireNeuron: fireNeuron2 } = await Promise.resolve().then(() => (init_fire(), fire_exports));
|
|
2074
|
+
await fireNeuron2(brainRoot, neuronPath);
|
|
2075
|
+
break;
|
|
2076
|
+
}
|
|
2077
|
+
case "grow": {
|
|
2078
|
+
const neuronPath = positionals[1];
|
|
2079
|
+
if (!neuronPath) {
|
|
2080
|
+
console.error("Usage: hebbian grow <neuron-path>");
|
|
2081
|
+
process.exit(1);
|
|
2082
|
+
}
|
|
2083
|
+
const { growNeuron: growNeuron2 } = await Promise.resolve().then(() => (init_grow(), grow_exports));
|
|
2084
|
+
await growNeuron2(brainRoot, neuronPath);
|
|
2085
|
+
break;
|
|
2086
|
+
}
|
|
2087
|
+
case "rollback": {
|
|
2088
|
+
const neuronPath = positionals[1];
|
|
2089
|
+
if (!neuronPath) {
|
|
2090
|
+
console.error("Usage: hebbian rollback <neuron-path>");
|
|
2091
|
+
process.exit(1);
|
|
2092
|
+
}
|
|
2093
|
+
const { rollbackNeuron: rollbackNeuron2 } = await Promise.resolve().then(() => (init_rollback(), rollback_exports));
|
|
2094
|
+
await rollbackNeuron2(brainRoot, neuronPath);
|
|
2095
|
+
break;
|
|
2096
|
+
}
|
|
2097
|
+
case "signal": {
|
|
2098
|
+
const signalType = positionals[1];
|
|
2099
|
+
const neuronPath = positionals[2];
|
|
2100
|
+
if (!signalType || !neuronPath) {
|
|
2101
|
+
console.error("Usage: hebbian signal <type> <neuron-path> (type: dopamine/bomb/memory)");
|
|
2102
|
+
process.exit(1);
|
|
2103
|
+
}
|
|
2104
|
+
const { signalNeuron: signalNeuron2 } = await Promise.resolve().then(() => (init_signal(), signal_exports));
|
|
2105
|
+
await signalNeuron2(brainRoot, neuronPath, signalType);
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2108
|
+
case "decay": {
|
|
2109
|
+
const days = values.days ? parseInt(values.days, 10) : 30;
|
|
2110
|
+
const { runDecay: runDecay2 } = await Promise.resolve().then(() => (init_decay(), decay_exports));
|
|
2111
|
+
await runDecay2(brainRoot, days);
|
|
2112
|
+
break;
|
|
2113
|
+
}
|
|
2114
|
+
case "dedup": {
|
|
2115
|
+
const { runDedup: runDedup2 } = await Promise.resolve().then(() => (init_dedup(), dedup_exports));
|
|
2116
|
+
runDedup2(brainRoot);
|
|
2117
|
+
break;
|
|
2118
|
+
}
|
|
2119
|
+
case "snapshot": {
|
|
2120
|
+
const { gitSnapshot: gitSnapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
|
|
2121
|
+
gitSnapshot2(brainRoot);
|
|
2122
|
+
break;
|
|
2123
|
+
}
|
|
2124
|
+
case "watch": {
|
|
2125
|
+
const { startWatch: startWatch2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
|
|
2126
|
+
await startWatch2(brainRoot);
|
|
2127
|
+
break;
|
|
2128
|
+
}
|
|
2129
|
+
case "api": {
|
|
2130
|
+
const port = values.port ? parseInt(values.port, 10) : 9090;
|
|
2131
|
+
const { startAPI: startAPI2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
2132
|
+
startAPI2(brainRoot, port);
|
|
2133
|
+
await new Promise(() => {
|
|
2134
|
+
});
|
|
2135
|
+
break;
|
|
2136
|
+
}
|
|
2137
|
+
case "inbox": {
|
|
2138
|
+
const { processInbox: processInbox2 } = await Promise.resolve().then(() => (init_inbox(), inbox_exports));
|
|
2139
|
+
processInbox2(brainRoot);
|
|
2140
|
+
break;
|
|
2141
|
+
}
|
|
2142
|
+
case "claude": {
|
|
2143
|
+
const sub = positionals[1];
|
|
2144
|
+
const { installHooks: installHooks2, uninstallHooks: uninstallHooks2, checkHooks: checkHooks2 } = await Promise.resolve().then(() => (init_hooks(), hooks_exports));
|
|
2145
|
+
switch (sub) {
|
|
2146
|
+
case "install":
|
|
2147
|
+
installHooks2(brainRoot);
|
|
2148
|
+
break;
|
|
2149
|
+
case "uninstall":
|
|
2150
|
+
uninstallHooks2();
|
|
2151
|
+
break;
|
|
2152
|
+
case "status":
|
|
2153
|
+
checkHooks2();
|
|
2154
|
+
break;
|
|
2155
|
+
default:
|
|
2156
|
+
console.error("Usage: hebbian claude <install|uninstall|status>");
|
|
2157
|
+
process.exit(1);
|
|
2158
|
+
}
|
|
2159
|
+
break;
|
|
2160
|
+
}
|
|
2161
|
+
case "digest": {
|
|
2162
|
+
const transcriptFlag = values.transcript;
|
|
2163
|
+
const { digestTranscript: digestTranscript2, readHookInput: readHookInput2 } = await Promise.resolve().then(() => (init_digest(), digest_exports));
|
|
2164
|
+
if (transcriptFlag) {
|
|
2165
|
+
digestTranscript2(brainRoot, resolve3(transcriptFlag));
|
|
2166
|
+
} else {
|
|
2167
|
+
const stdin = await readStdin();
|
|
2168
|
+
const hookInput = readHookInput2(stdin);
|
|
2169
|
+
if (hookInput) {
|
|
2170
|
+
digestTranscript2(brainRoot, hookInput.transcriptPath, hookInput.sessionId);
|
|
2171
|
+
} else {
|
|
2172
|
+
console.error("Usage: hebbian digest --transcript <path>");
|
|
2173
|
+
console.error(" Or pipe hook input via stdin (Claude Code Stop hook)");
|
|
2174
|
+
process.exit(1);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
break;
|
|
2178
|
+
}
|
|
2179
|
+
case "diag":
|
|
2180
|
+
case "stats": {
|
|
2181
|
+
const { scanBrain: scanBrain2 } = await Promise.resolve().then(() => (init_scanner(), scanner_exports));
|
|
2182
|
+
const { runSubsumption: runSubsumption2 } = await Promise.resolve().then(() => (init_subsumption(), subsumption_exports));
|
|
2183
|
+
const brain = scanBrain2(brainRoot);
|
|
2184
|
+
const result = runSubsumption2(brain);
|
|
2185
|
+
const { printDiag: printDiag2 } = await Promise.resolve().then(() => (init_emit(), emit_exports));
|
|
2186
|
+
printDiag2(brain, result);
|
|
2187
|
+
break;
|
|
2188
|
+
}
|
|
2189
|
+
default:
|
|
2190
|
+
console.error(`Unknown command: ${command}`);
|
|
2191
|
+
console.log(HELP);
|
|
2192
|
+
process.exit(1);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
2196
|
+
console.error(err.message);
|
|
2197
|
+
process.exit(1);
|
|
2198
|
+
});
|
|
2199
|
+
//# sourceMappingURL=hebbian.js.map
|