@cogmem/engram 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -26
- package/SKILL.md +40 -33
- package/drizzle/meta/0000_snapshot.json +17 -50
- package/drizzle/meta/_journal.json +1 -1
- package/package.json +27 -27
- package/src/cli/commands/encode.ts +15 -6
- package/src/cli/commands/focus.ts +38 -11
- package/src/cli/commands/health.ts +47 -13
- package/src/cli/commands/inspect.ts +7 -2
- package/src/cli/commands/install.ts +14 -5
- package/src/cli/commands/list.ts +3 -1
- package/src/cli/commands/recall.ts +4 -2
- package/src/cli/commands/sleep.ts +31 -11
- package/src/cli/commands/stats.ts +38 -15
- package/src/cli/format.ts +3 -2
- package/src/cli/index.ts +5 -4
- package/src/cli/providers/claude.ts +16 -5
- package/src/cli/providers/types.ts +5 -1
- package/src/config/defaults.ts +5 -2
- package/src/core/activation.ts +43 -11
- package/src/core/associations.ts +1 -4
- package/src/core/chunking.ts +14 -4
- package/src/core/consolidation.ts +21 -6
- package/src/core/encoder.ts +3 -3
- package/src/core/engine.ts +3 -2
- package/src/core/forgetting.ts +11 -4
- package/src/core/memory.ts +2 -1
- package/src/core/procedural-store.ts +1 -1
- package/src/core/recall.ts +27 -9
- package/src/core/reconsolidation.ts +5 -2
- package/src/core/working-memory.ts +8 -4
- package/src/mcp/server.ts +53 -15
- package/src/mcp/tools.ts +108 -45
- package/src/storage/schema.ts +1 -0
- package/src/storage/sqlite.ts +8 -7
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
|
+
|
|
2
3
|
import { EngramEngine } from "../../core/engine.ts";
|
|
3
|
-
import { focusUtilization } from "../../core/working-memory.ts";
|
|
4
4
|
import { refreshActivations } from "../../core/forgetting.ts";
|
|
5
|
+
import { focusUtilization } from "../../core/working-memory.ts";
|
|
5
6
|
import { bold, dim, green, yellow, red, isInteractive } from "../format.ts";
|
|
6
7
|
|
|
7
8
|
export const healthCommand = defineCommand({
|
|
@@ -13,7 +14,10 @@ export const healthCommand = defineCommand({
|
|
|
13
14
|
const engine = EngramEngine.create();
|
|
14
15
|
try {
|
|
15
16
|
const { atRisk } = refreshActivations(engine.storage, engine.config);
|
|
16
|
-
const { used, capacity } = focusUtilization(
|
|
17
|
+
const { used, capacity } = focusUtilization(
|
|
18
|
+
engine.storage,
|
|
19
|
+
engine.config
|
|
20
|
+
);
|
|
17
21
|
const lastConsolidation = engine.storage.getLastConsolidation();
|
|
18
22
|
const totalMemories = engine.storage.getMemoryCount();
|
|
19
23
|
const associationCount = engine.storage.getAssociationCount();
|
|
@@ -29,7 +33,7 @@ export const healthCommand = defineCommand({
|
|
|
29
33
|
totalMemories,
|
|
30
34
|
associations: associationCount,
|
|
31
35
|
lastConsolidationHoursAgo: hoursAgo ? Math.round(hoursAgo) : null,
|
|
32
|
-
})
|
|
36
|
+
})
|
|
33
37
|
);
|
|
34
38
|
return;
|
|
35
39
|
}
|
|
@@ -37,42 +41,72 @@ export const healthCommand = defineCommand({
|
|
|
37
41
|
console.log(bold(" engram — health check\n"));
|
|
38
42
|
|
|
39
43
|
if (atRisk > 0) {
|
|
40
|
-
console.log(
|
|
44
|
+
console.log(
|
|
45
|
+
yellow(` ! ${atRisk} memories at risk of being forgotten`)
|
|
46
|
+
);
|
|
41
47
|
} else {
|
|
42
48
|
console.log(green(" + All memories above retrieval threshold"));
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
if (used <= capacity) {
|
|
46
|
-
console.log(
|
|
52
|
+
console.log(
|
|
53
|
+
green(` + Working memory within capacity (${used}/${capacity})`)
|
|
54
|
+
);
|
|
47
55
|
} else {
|
|
48
|
-
console.log(
|
|
56
|
+
console.log(
|
|
57
|
+
red(` ! Working memory over capacity (${used}/${capacity})`)
|
|
58
|
+
);
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
if (lastConsolidation) {
|
|
52
62
|
const hoursAgo = (Date.now() - lastConsolidation.ranAt) / 3600000;
|
|
53
63
|
if (hoursAgo > 18) {
|
|
54
|
-
console.log(
|
|
64
|
+
console.log(
|
|
65
|
+
red(
|
|
66
|
+
` ! Consolidation overdue (last: ${Math.round(hoursAgo)}h ago)`
|
|
67
|
+
)
|
|
68
|
+
);
|
|
55
69
|
} else if (hoursAgo > 8) {
|
|
56
70
|
console.log(
|
|
57
|
-
yellow(
|
|
71
|
+
yellow(
|
|
72
|
+
` ~ Consolidation recommended soon (last: ${Math.round(
|
|
73
|
+
hoursAgo
|
|
74
|
+
)}h ago)`
|
|
75
|
+
)
|
|
58
76
|
);
|
|
59
77
|
} else {
|
|
60
|
-
console.log(
|
|
78
|
+
console.log(
|
|
79
|
+
green(
|
|
80
|
+
` + Consolidation recent (last: ${Math.round(hoursAgo)}h ago)`
|
|
81
|
+
)
|
|
82
|
+
);
|
|
61
83
|
}
|
|
62
84
|
} else if (totalMemories > 0) {
|
|
63
|
-
console.log(
|
|
85
|
+
console.log(
|
|
86
|
+
yellow(" ! No consolidation has ever run — run `engram sleep`")
|
|
87
|
+
);
|
|
64
88
|
} else {
|
|
65
89
|
console.log(dim(" ~ No memories encoded yet"));
|
|
66
90
|
}
|
|
67
91
|
|
|
68
92
|
if (totalMemories > 5 && associationCount === 0) {
|
|
69
|
-
console.log(
|
|
93
|
+
console.log(
|
|
94
|
+
yellow(
|
|
95
|
+
" ! No associations formed — run `engram sleep` to discover links"
|
|
96
|
+
)
|
|
97
|
+
);
|
|
70
98
|
} else if (associationCount > 0) {
|
|
71
99
|
const ratio = associationCount / Math.max(1, totalMemories);
|
|
72
100
|
if (ratio > 1) {
|
|
73
|
-
console.log(
|
|
101
|
+
console.log(
|
|
102
|
+
green(` + Association network is rich (${associationCount} links)`)
|
|
103
|
+
);
|
|
74
104
|
} else {
|
|
75
|
-
console.log(
|
|
105
|
+
console.log(
|
|
106
|
+
green(
|
|
107
|
+
` + Association network is healthy (${associationCount} links)`
|
|
108
|
+
)
|
|
109
|
+
);
|
|
76
110
|
}
|
|
77
111
|
}
|
|
78
112
|
} finally {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
|
+
|
|
2
3
|
import { EngramEngine } from "../../core/engine.ts";
|
|
3
4
|
import { formatMemoryInspection, dim } from "../format.ts";
|
|
4
5
|
|
|
@@ -18,7 +19,9 @@ export const inspectCommand = defineCommand({
|
|
|
18
19
|
const engine = EngramEngine.create();
|
|
19
20
|
try {
|
|
20
21
|
const allMemories = engine.storage.getAllMemories();
|
|
21
|
-
const match = allMemories.find(
|
|
22
|
+
const match = allMemories.find(
|
|
23
|
+
(m) => m.id === args.id || m.id.startsWith(args.id)
|
|
24
|
+
);
|
|
22
25
|
|
|
23
26
|
if (!match) {
|
|
24
27
|
console.log(dim(` No memory found matching "${args.id}"`));
|
|
@@ -28,7 +31,9 @@ export const inspectCommand = defineCommand({
|
|
|
28
31
|
const accessLog = engine.storage.getAccessLog(match.id);
|
|
29
32
|
const associations = engine.storage.getAssociations(match.id);
|
|
30
33
|
|
|
31
|
-
console.log(
|
|
34
|
+
console.log(
|
|
35
|
+
formatMemoryInspection(match, accessLog.length, associations.length)
|
|
36
|
+
);
|
|
32
37
|
} finally {
|
|
33
38
|
engine.close();
|
|
34
39
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { defineCommand } from "citty";
|
|
2
1
|
import { readFileSync } from "node:fs";
|
|
3
2
|
import { join, dirname } from "node:path";
|
|
4
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { defineCommand } from "citty";
|
|
5
6
|
import { consola } from "consola";
|
|
6
|
-
|
|
7
|
+
|
|
7
8
|
import { green, dim, yellow, bold } from "../format.ts";
|
|
9
|
+
import { getProvider, availableProviders } from "../providers/index.ts";
|
|
8
10
|
|
|
9
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
12
|
const SKILL_PATH = join(__dirname, "..", "..", "..", "SKILL.md");
|
|
@@ -96,16 +98,23 @@ export const installCommand = defineCommand({
|
|
|
96
98
|
if (result.status === "already_installed") {
|
|
97
99
|
console.log(dim(" already installed — nothing to do"));
|
|
98
100
|
console.log(dim(` skill: ${result.skillPath}`));
|
|
99
|
-
if (result.mcpConfigPath)
|
|
101
|
+
if (result.mcpConfigPath)
|
|
102
|
+
console.log(dim(` mcp: ${result.mcpConfigPath}`));
|
|
100
103
|
return;
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
const prefix = dryRun ? "would install" : "installed";
|
|
104
107
|
const check = dryRun ? yellow("~") : green("\u2713");
|
|
105
108
|
|
|
106
|
-
console.log(
|
|
109
|
+
console.log(
|
|
110
|
+
` ${check} Skill ${prefix} ${dim("\u2192")} ${bold(result.skillPath)}`
|
|
111
|
+
);
|
|
107
112
|
if (result.mcpConfigured && result.mcpConfigPath) {
|
|
108
|
-
console.log(
|
|
113
|
+
console.log(
|
|
114
|
+
` ${check} MCP ${prefix} ${dim("\u2192")} ${bold(
|
|
115
|
+
result.mcpConfigPath
|
|
116
|
+
)}`
|
|
117
|
+
);
|
|
109
118
|
} else if (result.mcpConfigPath) {
|
|
110
119
|
console.log(dim(` - MCP already configured → ${result.mcpConfigPath}`));
|
|
111
120
|
}
|
package/src/cli/commands/list.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
|
+
|
|
2
3
|
import { EngramEngine } from "../../core/engine.ts";
|
|
3
4
|
import { isValidMemoryType } from "../../core/memory.ts";
|
|
4
5
|
import { formatMemoryList } from "../format.ts";
|
|
@@ -32,7 +33,8 @@ export const listCommand = defineCommand({
|
|
|
32
33
|
run({ args }) {
|
|
33
34
|
const engine = EngramEngine.create();
|
|
34
35
|
try {
|
|
35
|
-
const type =
|
|
36
|
+
const type =
|
|
37
|
+
args.type && isValidMemoryType(args.type) ? args.type : undefined;
|
|
36
38
|
const limit = Number(args.limit);
|
|
37
39
|
|
|
38
40
|
let memories;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
|
+
|
|
2
3
|
import { EngramEngine } from "../../core/engine.ts";
|
|
4
|
+
import { isValidMemoryType } from "../../core/memory.ts";
|
|
3
5
|
import { recall } from "../../core/recall.ts";
|
|
4
6
|
import { formatRecallResults } from "../format.ts";
|
|
5
|
-
import { isValidMemoryType } from "../../core/memory.ts";
|
|
6
7
|
|
|
7
8
|
export const recallCommand = defineCommand({
|
|
8
9
|
meta: {
|
|
@@ -38,7 +39,8 @@ export const recallCommand = defineCommand({
|
|
|
38
39
|
run({ args }) {
|
|
39
40
|
const engine = EngramEngine.create();
|
|
40
41
|
try {
|
|
41
|
-
const typeFilter =
|
|
42
|
+
const typeFilter =
|
|
43
|
+
args.type && isValidMemoryType(args.type) ? args.type : undefined;
|
|
42
44
|
|
|
43
45
|
const results = recall(engine.storage, args.cue, engine.config, {
|
|
44
46
|
type: typeFilter,
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
|
-
|
|
3
|
-
import { consolidate } from "../../core/consolidation.ts";
|
|
2
|
+
|
|
4
3
|
import { discoverChunks } from "../../core/chunking.ts";
|
|
4
|
+
import { consolidate } from "../../core/consolidation.ts";
|
|
5
|
+
import { EngramEngine } from "../../core/engine.ts";
|
|
5
6
|
import { bold, dim, green, cyan, isInteractive } from "../format.ts";
|
|
6
7
|
|
|
7
8
|
export const sleepCommand = defineCommand({
|
|
8
9
|
meta: {
|
|
9
10
|
name: "sleep",
|
|
10
|
-
description:
|
|
11
|
+
description:
|
|
12
|
+
"Run consolidation cycle (replay, strengthen, prune, extract, link)",
|
|
11
13
|
},
|
|
12
14
|
args: {
|
|
13
15
|
report: {
|
|
@@ -41,7 +43,7 @@ export const sleepCommand = defineCommand({
|
|
|
41
43
|
})),
|
|
42
44
|
}
|
|
43
45
|
: {}),
|
|
44
|
-
})
|
|
46
|
+
})
|
|
45
47
|
);
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
@@ -49,17 +51,29 @@ export const sleepCommand = defineCommand({
|
|
|
49
51
|
console.log(dim(" Running consolidation cycle...\n"));
|
|
50
52
|
console.log(green(" Consolidation complete:\n"));
|
|
51
53
|
console.log(
|
|
52
|
-
` ${cyan("Strengthened")} ${
|
|
54
|
+
` ${cyan("Strengthened")} ${
|
|
55
|
+
result.memoriesStrengthened
|
|
56
|
+
} frequently-accessed memories`
|
|
53
57
|
);
|
|
54
58
|
console.log(
|
|
55
|
-
` ${cyan("Pruned")} ${
|
|
59
|
+
` ${cyan("Pruned")} ${
|
|
60
|
+
result.memoriesPruned
|
|
61
|
+
} memories below activation threshold`
|
|
56
62
|
);
|
|
57
63
|
console.log(
|
|
58
|
-
` ${cyan("Extracted")} ${
|
|
64
|
+
` ${cyan("Extracted")} ${
|
|
65
|
+
result.factsExtracted
|
|
66
|
+
} semantic facts from episodic patterns`
|
|
67
|
+
);
|
|
68
|
+
console.log(
|
|
69
|
+
` ${cyan("Discovered")} ${
|
|
70
|
+
result.associationsDiscovered
|
|
71
|
+
} new associations`
|
|
59
72
|
);
|
|
60
|
-
console.log(` ${cyan("Discovered")} ${result.associationsDiscovered} new associations`);
|
|
61
73
|
if (chunks.length > 0) {
|
|
62
|
-
console.log(
|
|
74
|
+
console.log(
|
|
75
|
+
` ${cyan("Chunked")} ${chunks.length} new memory groups`
|
|
76
|
+
);
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
if (args.report) {
|
|
@@ -84,14 +98,20 @@ export const sleepCommand = defineCommand({
|
|
|
84
98
|
if (chunks.length > 0) {
|
|
85
99
|
console.log(bold(" New Chunks:"));
|
|
86
100
|
for (const chunk of chunks) {
|
|
87
|
-
console.log(
|
|
101
|
+
console.log(
|
|
102
|
+
` ${dim(">")} ${chunk.label} (${
|
|
103
|
+
chunk.memberIds.length
|
|
104
|
+
} memories)`
|
|
105
|
+
);
|
|
88
106
|
}
|
|
89
107
|
console.log("");
|
|
90
108
|
}
|
|
91
109
|
|
|
92
110
|
if (result.discoveredAssociationPairs.length > 0) {
|
|
93
111
|
console.log(
|
|
94
|
-
bold(
|
|
112
|
+
bold(
|
|
113
|
+
` Associations Discovered: ${result.discoveredAssociationPairs.length}`
|
|
114
|
+
)
|
|
95
115
|
);
|
|
96
116
|
}
|
|
97
117
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
|
+
|
|
2
3
|
import { EngramEngine } from "../../core/engine.ts";
|
|
4
|
+
import { refreshActivations } from "../../core/forgetting.ts";
|
|
3
5
|
import { focusUtilization } from "../../core/working-memory.ts";
|
|
4
6
|
import { bold, dim, green, yellow, red, isInteractive } from "../format.ts";
|
|
5
|
-
import { refreshActivations } from "../../core/forgetting.ts";
|
|
6
7
|
|
|
7
8
|
export const statsCommand = defineCommand({
|
|
8
9
|
meta: {
|
|
@@ -18,16 +19,28 @@ export const statsCommand = defineCommand({
|
|
|
18
19
|
const semanticCount = engine.storage.getMemoryCount("semantic");
|
|
19
20
|
const proceduralCount = engine.storage.getMemoryCount("procedural");
|
|
20
21
|
const associationCount = engine.storage.getAssociationCount();
|
|
21
|
-
const { used, capacity } = focusUtilization(
|
|
22
|
+
const { used, capacity } = focusUtilization(
|
|
23
|
+
engine.storage,
|
|
24
|
+
engine.config
|
|
25
|
+
);
|
|
22
26
|
|
|
23
27
|
const lastConsolidation = engine.storage.getLastConsolidation();
|
|
24
28
|
|
|
25
29
|
const projectContext = engine.projectContext;
|
|
26
30
|
const projectCounts = projectContext
|
|
27
31
|
? {
|
|
28
|
-
episodic: engine.storage.getMemoryCountByContext(
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
episodic: engine.storage.getMemoryCountByContext(
|
|
33
|
+
projectContext,
|
|
34
|
+
"episodic"
|
|
35
|
+
),
|
|
36
|
+
semantic: engine.storage.getMemoryCountByContext(
|
|
37
|
+
projectContext,
|
|
38
|
+
"semantic"
|
|
39
|
+
),
|
|
40
|
+
procedural: engine.storage.getMemoryCountByContext(
|
|
41
|
+
projectContext,
|
|
42
|
+
"procedural"
|
|
43
|
+
),
|
|
31
44
|
total: engine.storage.getMemoryCountByContext(projectContext),
|
|
32
45
|
}
|
|
33
46
|
: null;
|
|
@@ -41,9 +54,13 @@ export const statsCommand = defineCommand({
|
|
|
41
54
|
procedural: proceduralCount,
|
|
42
55
|
associations: associationCount,
|
|
43
56
|
atRisk,
|
|
44
|
-
lastConsolidation: lastConsolidation
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
lastConsolidation: lastConsolidation
|
|
58
|
+
? { ranAt: lastConsolidation.ranAt }
|
|
59
|
+
: null,
|
|
60
|
+
...(projectCounts
|
|
61
|
+
? { project: { context: projectContext, ...projectCounts } }
|
|
62
|
+
: {}),
|
|
63
|
+
})
|
|
47
64
|
);
|
|
48
65
|
return;
|
|
49
66
|
}
|
|
@@ -54,27 +71,33 @@ export const statsCommand = defineCommand({
|
|
|
54
71
|
used >= capacity
|
|
55
72
|
? red(`${used}/${capacity} slots used (FULL)`)
|
|
56
73
|
: used > capacity * 0.7
|
|
57
|
-
|
|
58
|
-
|
|
74
|
+
? yellow(`${used}/${capacity} slots used`)
|
|
75
|
+
: green(`${used}/${capacity} slots used`);
|
|
59
76
|
console.log(` Working Memory: ${wmStatus}`);
|
|
60
77
|
|
|
61
78
|
console.log(
|
|
62
79
|
` Episodic: ${episodicCount} memories` +
|
|
63
|
-
(atRisk > 0 ? ` ${yellow(`(${atRisk} at risk of forgetting)`)}` : "")
|
|
80
|
+
(atRisk > 0 ? ` ${yellow(`(${atRisk} at risk of forgetting)`)}` : "")
|
|
64
81
|
);
|
|
65
82
|
console.log(` Semantic: ${semanticCount} facts`);
|
|
66
|
-
console.log(
|
|
83
|
+
console.log(
|
|
84
|
+
` Procedural: ${proceduralCount} skills ${dim(
|
|
85
|
+
"(immune to decay)"
|
|
86
|
+
)}`
|
|
87
|
+
);
|
|
67
88
|
|
|
68
89
|
console.log(` Associations: ${associationCount} links`);
|
|
69
90
|
|
|
70
91
|
if (lastConsolidation) {
|
|
71
|
-
const hoursAgo = Math.round(
|
|
92
|
+
const hoursAgo = Math.round(
|
|
93
|
+
(Date.now() - lastConsolidation.ranAt) / 3600000
|
|
94
|
+
);
|
|
72
95
|
const consolidationStatus =
|
|
73
96
|
hoursAgo > 12
|
|
74
97
|
? red(`${hoursAgo}h ago (overdue)`)
|
|
75
98
|
: hoursAgo > 6
|
|
76
|
-
|
|
77
|
-
|
|
99
|
+
? yellow(`${hoursAgo}h ago`)
|
|
100
|
+
: green(`${hoursAgo}h ago`);
|
|
78
101
|
console.log(` Last sleep: ${consolidationStatus}`);
|
|
79
102
|
} else {
|
|
80
103
|
console.log(` Last sleep: ${dim("never")}`);
|
package/src/cli/format.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Memory, RecallResult } from "../core/memory.ts";
|
|
2
|
-
import kleur from "kleur";
|
|
3
1
|
import Table from "cli-table3";
|
|
4
2
|
import dayjs from "dayjs";
|
|
5
3
|
import relativeTime from "dayjs/plugin/relativeTime.js";
|
|
4
|
+
import kleur from "kleur";
|
|
5
|
+
|
|
6
|
+
import type { Memory, RecallResult } from "../core/memory.ts";
|
|
6
7
|
|
|
7
8
|
dayjs.extend(relativeTime);
|
|
8
9
|
|
package/src/cli/index.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
|
+
|
|
3
4
|
import pkg from "../../package.json";
|
|
4
5
|
import { encodeCommand } from "./commands/encode.ts";
|
|
5
|
-
import { recallCommand } from "./commands/recall.ts";
|
|
6
6
|
import { focusCommand } from "./commands/focus.ts";
|
|
7
|
+
import { healthCommand } from "./commands/health.ts";
|
|
7
8
|
import { inspectCommand } from "./commands/inspect.ts";
|
|
8
|
-
import {
|
|
9
|
+
import { installCommand } from "./commands/install.ts";
|
|
9
10
|
import { listCommand } from "./commands/list.ts";
|
|
11
|
+
import { recallCommand } from "./commands/recall.ts";
|
|
10
12
|
import { sleepCommand } from "./commands/sleep.ts";
|
|
11
|
-
import {
|
|
12
|
-
import { installCommand } from "./commands/install.ts";
|
|
13
|
+
import { statsCommand } from "./commands/stats.ts";
|
|
13
14
|
|
|
14
15
|
const main = defineCommand({
|
|
15
16
|
meta: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
2
|
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
4
5
|
import type { ProviderInstaller } from "./types.ts";
|
|
5
6
|
|
|
6
7
|
const MCP_SERVER_CONFIG = {
|
|
@@ -55,12 +56,17 @@ export const claudeProvider: ProviderInstaller = {
|
|
|
55
56
|
async installGlobal(skillContent, dryRun) {
|
|
56
57
|
const home = homedir();
|
|
57
58
|
const skillDir = join(home, ".claude", "skills", "engram");
|
|
58
|
-
const configPath = join(home, ".claude
|
|
59
|
+
const configPath = join(home, ".claude.json");
|
|
59
60
|
|
|
60
61
|
const skillInstalled = installSkill(skillDir, skillContent, dryRun);
|
|
61
62
|
const mcpInstalled = configureMcp(configPath, dryRun);
|
|
62
63
|
|
|
63
|
-
const status =
|
|
64
|
+
const status =
|
|
65
|
+
!skillInstalled && !mcpInstalled
|
|
66
|
+
? "already_installed"
|
|
67
|
+
: skillInstalled && mcpInstalled
|
|
68
|
+
? "installed"
|
|
69
|
+
: "updated";
|
|
64
70
|
|
|
65
71
|
return {
|
|
66
72
|
status,
|
|
@@ -72,12 +78,17 @@ export const claudeProvider: ProviderInstaller = {
|
|
|
72
78
|
|
|
73
79
|
async installProject(skillContent, projectDir, dryRun) {
|
|
74
80
|
const skillDir = join(projectDir, ".claude", "skills", "engram");
|
|
75
|
-
const configPath = join(projectDir, ".
|
|
81
|
+
const configPath = join(projectDir, ".mcp.json");
|
|
76
82
|
|
|
77
83
|
const skillInstalled = installSkill(skillDir, skillContent, dryRun);
|
|
78
84
|
const mcpInstalled = configureMcp(configPath, dryRun);
|
|
79
85
|
|
|
80
|
-
const status =
|
|
86
|
+
const status =
|
|
87
|
+
!skillInstalled && !mcpInstalled
|
|
88
|
+
? "already_installed"
|
|
89
|
+
: skillInstalled && mcpInstalled
|
|
90
|
+
? "installed"
|
|
91
|
+
: "updated";
|
|
81
92
|
|
|
82
93
|
return {
|
|
83
94
|
status,
|
|
@@ -12,5 +12,9 @@ export interface ProviderInstaller {
|
|
|
12
12
|
displayName: string;
|
|
13
13
|
available: boolean;
|
|
14
14
|
installGlobal(skillContent: string, dryRun: boolean): Promise<InstallResult>;
|
|
15
|
-
installProject(
|
|
15
|
+
installProject(
|
|
16
|
+
skillContent: string,
|
|
17
|
+
projectDir: string,
|
|
18
|
+
dryRun: boolean
|
|
19
|
+
): Promise<InstallResult>;
|
|
16
20
|
}
|
package/src/config/defaults.ts
CHANGED
|
@@ -50,11 +50,14 @@ export function resolveDbPath(dbPath: string): string {
|
|
|
50
50
|
return dbPath;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export function loadConfig(
|
|
53
|
+
export function loadConfig(
|
|
54
|
+
overrides?: Partial<CognitiveConfig>
|
|
55
|
+
): CognitiveConfig {
|
|
54
56
|
const config = { ...DEFAULT_CONFIG, ...overrides };
|
|
55
57
|
|
|
56
58
|
if (process.env.ENGRAM_DB_PATH) config.dbPath = process.env.ENGRAM_DB_PATH;
|
|
57
|
-
if (process.env.ENGRAM_DECAY_RATE)
|
|
59
|
+
if (process.env.ENGRAM_DECAY_RATE)
|
|
60
|
+
config.decayRate = Number(process.env.ENGRAM_DECAY_RATE);
|
|
58
61
|
if (process.env.ENGRAM_WM_CAPACITY)
|
|
59
62
|
config.workingMemoryCapacity = Number(process.env.ENGRAM_WM_CAPACITY);
|
|
60
63
|
if (process.env.ENGRAM_RETRIEVAL_THRESHOLD)
|
package/src/core/activation.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { CognitiveConfig } from "../config/defaults.ts";
|
|
|
4
4
|
export function baseLevelActivation(
|
|
5
5
|
accessTimestamps: number[],
|
|
6
6
|
now: number,
|
|
7
|
-
decayRate: number
|
|
7
|
+
decayRate: number
|
|
8
8
|
): number {
|
|
9
9
|
if (accessTimestamps.length === 0) return -Infinity;
|
|
10
10
|
|
|
@@ -18,13 +18,20 @@ export function baseLevelActivation(
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// S_ji = S - ln(fan_j)
|
|
21
|
-
export function spreadingActivationStrength(
|
|
21
|
+
export function spreadingActivationStrength(
|
|
22
|
+
maxStrength: number,
|
|
23
|
+
fanCount: number
|
|
24
|
+
): number {
|
|
22
25
|
if (fanCount <= 0) return 0;
|
|
23
26
|
return Math.max(0, maxStrength - Math.log(fanCount));
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
// A_i = B_i + Σ(W_j · S_ji) + ε
|
|
27
|
-
export function totalActivation(
|
|
30
|
+
export function totalActivation(
|
|
31
|
+
baseLevel: number,
|
|
32
|
+
spreadingSum: number,
|
|
33
|
+
noise: number
|
|
34
|
+
): number {
|
|
28
35
|
return baseLevel + spreadingSum + noise;
|
|
29
36
|
}
|
|
30
37
|
|
|
@@ -44,7 +51,7 @@ export function canRetrieve(activation: number, threshold: number): boolean {
|
|
|
44
51
|
export function retrievalLatency(
|
|
45
52
|
activation: number,
|
|
46
53
|
latencyFactor: number,
|
|
47
|
-
latencyExponent: number
|
|
54
|
+
latencyExponent: number
|
|
48
55
|
): number {
|
|
49
56
|
return latencyFactor * Math.exp(-latencyExponent * activation);
|
|
50
57
|
}
|
|
@@ -57,19 +64,44 @@ export function computeActivation(
|
|
|
57
64
|
spreadingSum?: number;
|
|
58
65
|
noiseOverride?: number;
|
|
59
66
|
emotionWeight?: number;
|
|
60
|
-
}
|
|
61
|
-
): {
|
|
62
|
-
|
|
67
|
+
}
|
|
68
|
+
): {
|
|
69
|
+
activation: number;
|
|
70
|
+
baseLevel: number;
|
|
71
|
+
spreading: number;
|
|
72
|
+
noise: number;
|
|
73
|
+
latency: number;
|
|
74
|
+
} {
|
|
75
|
+
const baseLevel = baseLevelActivation(
|
|
76
|
+
accessTimestamps,
|
|
77
|
+
now,
|
|
78
|
+
config.decayRate
|
|
79
|
+
);
|
|
63
80
|
|
|
64
81
|
const emotionBoost = options?.emotionWeight
|
|
65
82
|
? Math.log(1 + options.emotionWeight * config.emotionalBoostFactor)
|
|
66
83
|
: 0;
|
|
67
84
|
|
|
68
85
|
const spreading = options?.spreadingSum ?? 0;
|
|
69
|
-
const noise =
|
|
86
|
+
const noise =
|
|
87
|
+
options?.noiseOverride ?? activationNoise(config.activationNoise);
|
|
70
88
|
|
|
71
|
-
const activation = totalActivation(
|
|
72
|
-
|
|
89
|
+
const activation = totalActivation(
|
|
90
|
+
baseLevel + emotionBoost,
|
|
91
|
+
spreading,
|
|
92
|
+
noise
|
|
93
|
+
);
|
|
94
|
+
const latency = retrievalLatency(
|
|
95
|
+
activation,
|
|
96
|
+
config.latencyFactor,
|
|
97
|
+
config.latencyExponent
|
|
98
|
+
);
|
|
73
99
|
|
|
74
|
-
return {
|
|
100
|
+
return {
|
|
101
|
+
activation,
|
|
102
|
+
baseLevel: baseLevel + emotionBoost,
|
|
103
|
+
spreading,
|
|
104
|
+
noise,
|
|
105
|
+
latency,
|
|
106
|
+
};
|
|
75
107
|
}
|
package/src/core/associations.ts
CHANGED
|
@@ -122,10 +122,7 @@ export function formSemanticAssociations(
|
|
|
122
122
|
const candidates = preloadedMemories ?? storage.getAllMemories();
|
|
123
123
|
const existing = storage.getAssociations(memory.id);
|
|
124
124
|
const linkedSet = new Set(
|
|
125
|
-
existing.flatMap((a) => [
|
|
126
|
-
`${a.sourceId}:${a.targetId}`,
|
|
127
|
-
`${a.targetId}:${a.sourceId}`,
|
|
128
|
-
]),
|
|
125
|
+
existing.flatMap((a) => [`${a.sourceId}:${a.targetId}`, `${a.targetId}:${a.sourceId}`]),
|
|
129
126
|
);
|
|
130
127
|
const formed: Association[] = [];
|
|
131
128
|
|
package/src/core/chunking.ts
CHANGED
|
@@ -62,7 +62,10 @@ class UnionFind {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
export function discoverChunks(
|
|
65
|
+
export function discoverChunks(
|
|
66
|
+
storage: EngramStorage,
|
|
67
|
+
config: CognitiveConfig
|
|
68
|
+
): Chunk[] {
|
|
66
69
|
const allMemories = storage.getAllMemories();
|
|
67
70
|
const memoryMap = new Map<string, Memory>();
|
|
68
71
|
const uf = new UnionFind();
|
|
@@ -77,7 +80,8 @@ export function discoverChunks(storage: EngramStorage, config: CognitiveConfig):
|
|
|
77
80
|
const associations = storage.getAssociations(memory.id);
|
|
78
81
|
for (const assoc of associations) {
|
|
79
82
|
if (assoc.strength < config.chunkingSimilarityThreshold) continue;
|
|
80
|
-
const otherId =
|
|
83
|
+
const otherId =
|
|
84
|
+
assoc.sourceId === memory.id ? assoc.targetId : assoc.sourceId;
|
|
81
85
|
if (!memoryMap.has(otherId)) continue;
|
|
82
86
|
uf.union(memory.id, otherId);
|
|
83
87
|
}
|
|
@@ -89,7 +93,10 @@ export function discoverChunks(storage: EngramStorage, config: CognitiveConfig):
|
|
|
89
93
|
|
|
90
94
|
const members = memberIds.map((id) => memoryMap.get(id)!);
|
|
91
95
|
const chunkId = generateId();
|
|
92
|
-
const keywords = extractKeywords(
|
|
96
|
+
const keywords = extractKeywords(
|
|
97
|
+
members.map((m) => m.content).join(" "),
|
|
98
|
+
3
|
|
99
|
+
);
|
|
93
100
|
const label = keywords.join(" + ") || "chunk";
|
|
94
101
|
|
|
95
102
|
for (const member of members) {
|
|
@@ -103,6 +110,9 @@ export function discoverChunks(storage: EngramStorage, config: CognitiveConfig):
|
|
|
103
110
|
return chunks;
|
|
104
111
|
}
|
|
105
112
|
|
|
106
|
-
export function getChunkMembers(
|
|
113
|
+
export function getChunkMembers(
|
|
114
|
+
storage: EngramStorage,
|
|
115
|
+
chunkId: string
|
|
116
|
+
): Memory[] {
|
|
107
117
|
return storage.getAllMemories().filter((m) => m.chunkId === chunkId);
|
|
108
118
|
}
|