@nguyentamdat/mempalace 1.0.0 → 1.0.2

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 CHANGED
@@ -6,27 +6,66 @@ A structured, persistent memory system for AI agents — featuring a palace meta
6
6
 
7
7
  This is a **Bun/TypeScript** port of the original [mempalace](https://github.com/milla-jovovich/mempalace) by [milla-jovovich](https://github.com/milla-jovovich).
8
8
 
9
+ > **OpenCode users**: See [opencode-mempalace](https://github.com/nguyentamdat/opencode-mempalace) for plug-and-play integration.
10
+
9
11
  ## Requirements
10
12
 
11
13
  - [Bun](https://bun.sh/) ≥ 1.0
12
- - [ChromaDB](https://www.trychroma.com/) server running (`chroma run`)
14
+ - [ChromaDB](https://www.trychroma.com/) server running
13
15
 
14
16
  ## Install
15
17
 
16
18
  ```bash
19
+ npm install @nguyentamdat/mempalace
20
+ # or
17
21
  git clone https://github.com/nguyentamdat/mempalace-js.git
18
22
  cd mempalace-js
19
23
  bun install
20
24
  ```
21
25
 
26
+ ## Setup
27
+
28
+ ### 1. Start ChromaDB
29
+
30
+ ```bash
31
+ # Docker (recommended)
32
+ docker run -d --name chromadb -p 127.0.0.1:8001:8000 \
33
+ -v chromadb-data:/chroma/chroma --restart unless-stopped \
34
+ chromadb/chroma:latest
35
+
36
+ # Or pip
37
+ pip install chromadb
38
+ chroma run --port 8001
39
+ ```
40
+
41
+ ### 2. Configure
42
+
43
+ The default ChromaDB URL is `http://localhost:8000`. Override via environment variable or config:
44
+
45
+ ```bash
46
+ # Environment variable
47
+ export CHROMA_URL=http://localhost:8001
48
+
49
+ # Or in ~/.mempalace/config.json
50
+ {
51
+ "chroma_url": "http://localhost:8001",
52
+ "palace_path": "~/.mempalace/palace",
53
+ "collection_name": "mempalace_drawers",
54
+ "topic_wings": ["work", "personal", "technical", "creative"]
55
+ }
56
+ ```
57
+
58
+ ### 3. Initialize
59
+
60
+ ```bash
61
+ bun run src/index.ts init /path/to/project
62
+ ```
63
+
22
64
  ## Usage
23
65
 
24
66
  ### CLI
25
67
 
26
68
  ```bash
27
- # Initialize a new palace
28
- bun run src/index.ts init
29
-
30
69
  # Mine project files into the palace
31
70
  bun run src/index.ts mine --mode projects --path ./my-project
32
71
 
@@ -36,7 +75,7 @@ bun run src/index.ts mine --mode convos --path ./transcripts
36
75
  # Search memories
37
76
  bun run src/index.ts search "some query"
38
77
 
39
- # Compress memories with AAAK dialect
78
+ # Compress memories with AAAK dialect (~30x reduction)
40
79
  bun run src/index.ts compress
41
80
 
42
81
  # Load memory layers (wake-up)
@@ -54,23 +93,29 @@ bun run src/index.ts status
54
93
  Run as a JSON-RPC MCP server over stdin/stdout (19 tools):
55
94
 
56
95
  ```bash
57
- bun run src/mcp-server.ts
96
+ CHROMA_URL=http://localhost:8001 bun run src/mcp-server.ts
58
97
  ```
59
98
 
60
- Tools include: `mempalace_search`, `mempalace_kg_query`, `mempalace_kg_timeline`, `mempalace_kg_add`, `mempalace_kg_invalidate`, `mempalace_diary_write`, `mempalace_diary_read`, `mempalace_status`, `mempalace_list_wings`, `mempalace_list_rooms`, `mempalace_add_drawer`, `mempalace_delete_drawer`, `mempalace_traverse`, `mempalace_find_tunnels`, and more.
99
+ Tools: `mempalace_search`, `mempalace_kg_query`, `mempalace_kg_timeline`, `mempalace_kg_add`, `mempalace_kg_invalidate`, `mempalace_diary_write`, `mempalace_diary_read`, `mempalace_status`, `mempalace_list_wings`, `mempalace_list_rooms`, `mempalace_get_taxonomy`, `mempalace_add_drawer`, `mempalace_delete_drawer`, `mempalace_check_duplicate`, `mempalace_traverse`, `mempalace_find_tunnels`, `mempalace_graph_stats`, `mempalace_get_aaak_spec`.
61
100
 
62
101
  ## Architecture
63
102
 
64
103
  ### Palace Structure
65
104
 
66
105
  ```
67
- Palace (~/.mempalace/)
68
- ├── Wings (top-level categories: work, personal, health, etc.)
106
+ ~/.mempalace/
107
+ ├── palace/ (ChromaDB collection data reference)
108
+ ├── config.json (palace configuration)
109
+ ├── entity_registry.json
110
+ ├── identity.txt (Layer 0 — who am I)
111
+ └── knowledge_graph.db (SQLite)
112
+
113
+ Palace (in ChromaDB)
114
+ ├── Wings (top-level categories: work, personal, technical, etc.)
69
115
  │ └── Rooms (specific topics within a wing)
70
- │ └── Drawers (individual memories stored in ChromaDB)
71
- ├── Knowledge Graph (SQLite entities + temporal triples)
72
- ├── Diary (daily entries)
73
- └── Config (config.json, entity_registry.json, identity.txt)
116
+ │ └── Drawers (individual memories with metadata)
117
+ ├── Diary wing (per-agent daily entries)
118
+ └── Embeddings (DefaultEmbeddingFunction)
74
119
  ```
75
120
 
76
121
  ### Memory Layers
@@ -84,7 +129,7 @@ Palace (~/.mempalace/)
84
129
 
85
130
  ### AAAK Compression Dialect
86
131
 
87
- A compact encoding format for memories that reduces token usage while preserving meaning. Includes emotion codes, flag signals, and stop-word removal.
132
+ A compact encoding format for memories that reduces token usage ~30x while preserving meaning. Uses 3-letter entity codes, emotion markers, pipe-separated fields, and importance stars (★–★★★★★).
88
133
 
89
134
  ### Knowledge Graph
90
135
 
@@ -94,7 +139,8 @@ SQLite-based temporal entity-relationship graph with validity windows and confid
94
139
 
95
140
  | Module | Description |
96
141
  |--------|-------------|
97
- | `config.ts` | Palace configuration management |
142
+ | `config.ts` | Palace configuration (env vars, config.json, defaults) |
143
+ | `mcp-server.ts` | JSON-RPC MCP server (19 tools) |
98
144
  | `knowledge-graph.ts` | Temporal entity-relationship graph (bun:sqlite) |
99
145
  | `dialect.ts` | AAAK compression dialect |
100
146
  | `layers.ts` | 4-layer memory stack |
@@ -110,7 +156,6 @@ SQLite-based temporal entity-relationship graph with validity windows and confid
110
156
  | `room-detector-local.ts` | Room detection from folder structure |
111
157
  | `spellcheck.ts` | Optional spell correction |
112
158
  | `split-mega-files.ts` | Split concatenated transcripts |
113
- | `mcp-server.ts` | JSON-RPC MCP server (19 tools) |
114
159
 
115
160
  ## Tests
116
161
 
@@ -118,6 +163,11 @@ SQLite-based temporal entity-relationship graph with validity windows and confid
118
163
  bun test
119
164
  ```
120
165
 
166
+ ## Related
167
+
168
+ - [opencode-mempalace](https://github.com/nguyentamdat/opencode-mempalace) — OpenCode plugin for automatic integration
169
+ - [mempalace](https://github.com/milla-jovovich/mempalace) — Original Python implementation
170
+
121
171
  ## Acknowledgements
122
172
 
123
173
  This project is a Bun/TypeScript port of the original [mempalace](https://github.com/milla-jovovich/mempalace) by [milla-jovovich](https://github.com/milla-jovovich). All credit for the palace architecture, AAAK compression dialect, knowledge graph design, memory layer system, and MCP tool definitions goes to their work.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nguyentamdat/mempalace",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Give your AI a memory. No API key required.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,10 +13,11 @@
13
13
  "mcp": "bun run src/mcp-server.ts"
14
14
  },
15
15
  "dependencies": {
16
+ "@clack/prompts": "^0.9.1",
16
17
  "chromadb": "^1.9.2",
18
+ "chromadb-default-embed": "^2.14.0",
17
19
  "citty": "^0.1.6",
18
- "js-yaml": "^4.1.0",
19
- "@clack/prompts": "^0.9.1"
20
+ "js-yaml": "^4.1.0"
20
21
  },
21
22
  "devDependencies": {
22
23
  "@types/js-yaml": "^4.0.9",
@@ -1,5 +1,6 @@
1
1
  import { defineCommand } from "citty";
2
2
  import { resolvePalacePath } from "../cli";
3
+ import { MempalaceConfig } from "../config";
3
4
 
4
5
  export default defineCommand({
5
6
  meta: { description: "Compress drawers using AAAK Dialect (~30x reduction)" },
@@ -40,7 +41,7 @@ export default defineCommand({
40
41
  let client: InstanceType<typeof ChromaClient>;
41
42
  let col: Awaited<ReturnType<InstanceType<typeof ChromaClient>["getCollection"]>>;
42
43
  try {
43
- client = new ChromaClient({ path: palacePath });
44
+ client = new ChromaClient({ path: new MempalaceConfig().chromaUrl });
44
45
  col = await client.getCollection({
45
46
  name: "mempalace_drawers",
46
47
  embeddingFunction: new DefaultEmbeddingFunction(),
package/src/config.ts CHANGED
@@ -10,6 +10,7 @@ import { homedir } from "os";
10
10
 
11
11
  export const DEFAULT_PALACE_PATH = join(homedir(), ".mempalace", "palace");
12
12
  export const DEFAULT_COLLECTION_NAME = "mempalace_drawers";
13
+ export const DEFAULT_CHROMA_URL = process.env.CHROMA_URL ?? "http://localhost:8000";
13
14
 
14
15
  export const DEFAULT_TOPIC_WINGS = [
15
16
  "emotions",
@@ -89,6 +90,12 @@ export class MempalaceConfig {
89
90
  return (this.fileConfig.hall_keywords as Record<string, string[]>) ?? DEFAULT_HALL_KEYWORDS;
90
91
  }
91
92
 
93
+ get chromaUrl(): string {
94
+ const envVal = process.env.CHROMA_URL;
95
+ if (envVal) return envVal;
96
+ return (this.fileConfig.chroma_url as string) ?? DEFAULT_CHROMA_URL;
97
+ }
98
+
92
99
  init(): string {
93
100
  mkdirSync(this.configDir, { recursive: true });
94
101
  if (!existsSync(this.configFile)) {
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { mkdirSync, readdirSync, statSync } from "node:fs";
3
3
  import { basename, extname, resolve } from "node:path";
4
4
  import { ChromaClient } from "chromadb";
5
+ import { MempalaceConfig } from "./config";
5
6
 
6
7
  import { extractMemories } from "./general-extractor";
7
8
  import { normalize } from "./normalize";
@@ -163,7 +164,7 @@ export function detectConvoRoom(content: string): string {
163
164
 
164
165
  export async function getCollection(palacePath: string): Promise<DrawerCollection> {
165
166
  mkdirSync(palacePath, { recursive: true });
166
- const client = new ChromaClient();
167
+ const client = new ChromaClient({ path: new MempalaceConfig().chromaUrl });
167
168
  return client.getOrCreateCollection({ name: COLLECTION_NAME });
168
169
  }
169
170
 
package/src/layers.ts CHANGED
@@ -146,8 +146,8 @@ async function loadDialect(): Promise<DialectLike> {
146
146
  return dialectPromise;
147
147
  }
148
148
 
149
- async function getCollection(palacePath: string, collectionName: string): Promise<Collection> {
150
- const client = new ChromaClient({ path: palacePath });
149
+ async function getCollection(collectionName: string): Promise<Collection> {
150
+ const client = new ChromaClient({ path: new MempalaceConfig().chromaUrl });
151
151
  return await client.getCollection({ name: collectionName } as never);
152
152
  }
153
153
 
@@ -198,7 +198,7 @@ export class Layer1 {
198
198
  let collection: Collection;
199
199
 
200
200
  try {
201
- collection = await getCollection(this.palacePath, this.collectionName);
201
+ collection = await getCollection(this.collectionName);
202
202
  } catch {
203
203
  return "## L1 — No palace found. Run: mempalace mine <dir>";
204
204
  }
@@ -294,7 +294,7 @@ export class Layer2 {
294
294
  let collection: Collection;
295
295
 
296
296
  try {
297
- collection = await getCollection(this.palacePath, this.collectionName);
297
+ collection = await getCollection(this.collectionName);
298
298
  } catch {
299
299
  return "No palace found.";
300
300
  }
@@ -354,7 +354,7 @@ export class Layer3 {
354
354
  let collection: Collection;
355
355
 
356
356
  try {
357
- collection = await getCollection(this.palacePath, this.collectionName);
357
+ collection = await getCollection(this.collectionName);
358
358
  } catch {
359
359
  return "No palace found.";
360
360
  }
@@ -402,7 +402,7 @@ export class Layer3 {
402
402
  let collection: Collection;
403
403
 
404
404
  try {
405
- collection = await getCollection(this.palacePath, this.collectionName);
405
+ collection = await getCollection(this.collectionName);
406
406
  } catch {
407
407
  return [];
408
408
  }
@@ -499,7 +499,7 @@ export class MemoryStack {
499
499
  };
500
500
 
501
501
  try {
502
- const collection = await getCollection(this.palacePath, new MempalaceConfig().collectionName);
502
+ const collection = await getCollection(new MempalaceConfig().collectionName);
503
503
  result.totalDrawers = await collection.count();
504
504
  } catch {
505
505
  result.totalDrawers = 0;
package/src/mcp-server.ts CHANGED
@@ -176,7 +176,7 @@ function toDirection(value: unknown): KgDirection {
176
176
 
177
177
  async function getCollection(create = false): Promise<DrawerCollection | null> {
178
178
  try {
179
- const client = new ChromaClient();
179
+ const client = new ChromaClient({ path: config.chromaUrl });
180
180
  if (create) {
181
181
  return await client.getOrCreateCollection({
182
182
  name: config.collectionName,
package/src/miner.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  import { basename, extname, relative, resolve } from "node:path";
10
10
  import { ChromaClient, DefaultEmbeddingFunction, IncludeEnum } from "chromadb";
11
11
  import yaml from "js-yaml";
12
+ import { MempalaceConfig } from "./config";
12
13
 
13
14
  export const READABLE_EXTENSIONS = new Set([
14
15
  ".txt",
@@ -291,7 +292,7 @@ export async function getCollection(
291
292
  palacePath: string,
292
293
  ): Promise<DrawerCollection> {
293
294
  mkdirSync(palacePath, { recursive: true });
294
- const client = new ChromaClient();
295
+ const client = new ChromaClient({ path: new MempalaceConfig().chromaUrl });
295
296
  return client.getOrCreateCollection({ name: COLLECTION_NAME });
296
297
  }
297
298
 
@@ -562,7 +563,7 @@ export async function mine(
562
563
  export async function status(palacePath: string): Promise<void> {
563
564
  try {
564
565
  mkdirSync(palacePath, { recursive: true });
565
- const client = new ChromaClient();
566
+ const client = new ChromaClient({ path: new MempalaceConfig().chromaUrl });
566
567
  const collection = await client.getCollection({
567
568
  name: COLLECTION_NAME,
568
569
  embeddingFunction: new DefaultEmbeddingFunction(),
@@ -37,7 +37,7 @@ export async function getCollection(
37
37
  config: MempalaceConfig = new MempalaceConfig(),
38
38
  ): Promise<ChromaCollection | null> {
39
39
  try {
40
- const client = new ChromaClient({ path: config.palacePath });
40
+ const client = new ChromaClient({ path: config.chromaUrl });
41
41
  return (await client.getCollection({
42
42
  name: config.collectionName,
43
43
  embeddingFunction: undefined as any,
package/src/searcher.ts CHANGED
@@ -38,7 +38,7 @@ function buildWhereFilter(wing?: string, room?: string) {
38
38
  async function getDrawerCollection(palacePath: string) {
39
39
  const config = new MempalaceConfig();
40
40
  const collectionName = config.collectionName;
41
- const client = new ChromaClient({ path: palacePath || config.palacePath });
41
+ const client = new ChromaClient({ path: config.chromaUrl });
42
42
  const embeddingFunction = new DefaultEmbeddingFunction();
43
43
 
44
44
  try {
@@ -69,8 +69,8 @@ export async function search({
69
69
 
70
70
  try {
71
71
  const kwargs = {
72
- query_texts: [query],
73
- n_results: nResults,
72
+ queryTexts: [query],
73
+ nResults: nResults,
74
74
  include: ["documents", "metadatas", "distances"],
75
75
  } as unknown as DrawerQueryParams;
76
76
 
@@ -141,8 +141,8 @@ export async function searchMemories({
141
141
 
142
142
  try {
143
143
  const kwargs = {
144
- query_texts: [query],
145
- n_results: nResults,
144
+ queryTexts: [query],
145
+ nResults: nResults,
146
146
  include: ["documents", "metadatas", "distances"],
147
147
  } as unknown as DrawerQueryParams;
148
148