agent-working-memory 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -21
- package/README.md +21 -3
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +7 -0
- package/dist/api/routes.js.map +1 -1
- package/dist/cli.d.ts +0 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +69 -67
- package/dist/cli.js.map +1 -1
- package/dist/core/decay.d.ts.map +1 -1
- package/dist/core/decay.js +2 -0
- package/dist/core/decay.js.map +1 -1
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +2 -0
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/hebbian.d.ts.map +1 -1
- package/dist/core/hebbian.js +2 -0
- package/dist/core/hebbian.js.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +2 -0
- package/dist/core/logger.js.map +1 -1
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +2 -0
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +2 -0
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts +3 -1
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +29 -10
- package/dist/core/salience.js.map +1 -1
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +9 -0
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/connections.d.ts.map +1 -1
- package/dist/engine/connections.js +2 -0
- package/dist/engine/connections.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
- package/dist/engine/consolidation-scheduler.js +2 -0
- package/dist/engine/consolidation-scheduler.js.map +1 -1
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +5 -3
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +2 -0
- package/dist/engine/eval.js.map +1 -1
- package/dist/engine/eviction.d.ts.map +1 -1
- package/dist/engine/eviction.js +2 -0
- package/dist/engine/eviction.js.map +1 -1
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +2 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/retraction.d.ts.map +1 -1
- package/dist/engine/retraction.js +2 -0
- package/dist/engine/retraction.js.map +1 -1
- package/dist/engine/staging.d.ts.map +1 -1
- package/dist/engine/staging.js +2 -0
- package/dist/engine/staging.js.map +1 -1
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +2 -0
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +69 -3
- package/dist/mcp.js.map +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/sqlite.d.ts +12 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +52 -5
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js +2 -0
- package/dist/types/agent.js.map +1 -1
- package/dist/types/checkpoint.d.ts.map +1 -1
- package/dist/types/checkpoint.js +2 -0
- package/dist/types/checkpoint.js.map +1 -1
- package/dist/types/engram.d.ts +16 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js +2 -0
- package/dist/types/engram.js.map +1 -1
- package/dist/types/eval.d.ts.map +1 -1
- package/dist/types/eval.js +2 -0
- package/dist/types/eval.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +2 -2
- package/src/api/index.ts +2 -0
- package/src/api/routes.ts +8 -0
- package/src/cli.ts +385 -383
- package/src/core/decay.ts +2 -0
- package/src/core/embeddings.ts +2 -0
- package/src/core/hebbian.ts +2 -0
- package/src/core/index.ts +2 -0
- package/src/core/logger.ts +2 -0
- package/src/core/query-expander.ts +2 -0
- package/src/core/reranker.ts +2 -0
- package/src/core/salience.ts +34 -13
- package/src/engine/activation.ts +10 -0
- package/src/engine/connections.ts +2 -0
- package/src/engine/consolidation-scheduler.ts +125 -123
- package/src/engine/consolidation.ts +5 -3
- package/src/engine/eval.ts +2 -0
- package/src/engine/eviction.ts +2 -0
- package/src/engine/index.ts +2 -0
- package/src/engine/retraction.ts +2 -0
- package/src/engine/staging.ts +2 -0
- package/src/hooks/sidecar.ts +2 -0
- package/src/index.ts +2 -0
- package/src/mcp.ts +82 -3
- package/src/storage/index.ts +2 -0
- package/src/storage/sqlite.ts +61 -5
- package/src/types/agent.ts +2 -0
- package/src/types/checkpoint.ts +46 -44
- package/src/types/engram.ts +23 -0
- package/src/types/eval.ts +2 -0
- package/src/types/index.ts +2 -0
package/src/cli.ts
CHANGED
|
@@ -1,383 +1,385 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* awm
|
|
10
|
-
* awm
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
awm
|
|
49
|
-
|
|
50
|
-
awm
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
--
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let
|
|
77
|
-
let
|
|
78
|
-
let
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
} else if (args[i] === '--
|
|
87
|
-
|
|
88
|
-
} else if (args[i] === '--no-
|
|
89
|
-
|
|
90
|
-
} else if (args[i] === '--
|
|
91
|
-
|
|
92
|
-
} else if (args[i] === '--
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
existing = {
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
- A
|
|
192
|
-
- A
|
|
193
|
-
- A
|
|
194
|
-
- A
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
-
|
|
200
|
-
-
|
|
201
|
-
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
settings =
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
${
|
|
308
|
-
${
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
console.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
case
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CLI entrypoint for AgentWorkingMemory.
|
|
7
|
+
*
|
|
8
|
+
* Commands:
|
|
9
|
+
* awm setup — configure MCP for the current project
|
|
10
|
+
* awm mcp — start the MCP server (called by Claude Code)
|
|
11
|
+
* awm serve — start the HTTP API server
|
|
12
|
+
* awm health — check if a running server is healthy
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
16
|
+
import { resolve, basename, join, dirname } from 'node:path';
|
|
17
|
+
import { execSync } from 'node:child_process';
|
|
18
|
+
import { randomBytes } from 'node:crypto';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { homedir as osHomedir } from 'node:os';
|
|
21
|
+
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = dirname(__filename);
|
|
24
|
+
|
|
25
|
+
// Load .env if present
|
|
26
|
+
try {
|
|
27
|
+
const envPath = resolve(process.cwd(), '.env');
|
|
28
|
+
const envContent = readFileSync(envPath, 'utf-8');
|
|
29
|
+
for (const line of envContent.split('\n')) {
|
|
30
|
+
const trimmed = line.trim();
|
|
31
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
32
|
+
const eqIdx = trimmed.indexOf('=');
|
|
33
|
+
if (eqIdx === -1) continue;
|
|
34
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
35
|
+
const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
|
|
36
|
+
if (!process.env[key]) process.env[key] = val;
|
|
37
|
+
}
|
|
38
|
+
} catch { /* No .env file */ }
|
|
39
|
+
|
|
40
|
+
const args = process.argv.slice(2);
|
|
41
|
+
const command = args[0];
|
|
42
|
+
|
|
43
|
+
function printUsage() {
|
|
44
|
+
console.log(`
|
|
45
|
+
AgentWorkingMemory — Cognitive memory for AI agents
|
|
46
|
+
|
|
47
|
+
Usage:
|
|
48
|
+
awm setup [--global] [--agent-id <id>] [--db-path <path>] [--no-claude-md]
|
|
49
|
+
[--no-hooks] [--hook-port <port>] Configure MCP for Claude Code
|
|
50
|
+
awm mcp Start MCP server (used by Claude Code)
|
|
51
|
+
awm serve [--port <port>] Start HTTP API server
|
|
52
|
+
awm health [--port <port>] Check server health
|
|
53
|
+
|
|
54
|
+
Setup:
|
|
55
|
+
awm setup --global Recommended. Writes ~/.mcp.json so AWM is available
|
|
56
|
+
in every project — one brain across all your work.
|
|
57
|
+
|
|
58
|
+
awm setup Project-level. Writes .mcp.json in the current directory
|
|
59
|
+
and appends workflow instructions to CLAUDE.md.
|
|
60
|
+
|
|
61
|
+
--no-claude-md Skip CLAUDE.md modification
|
|
62
|
+
--no-hooks Skip hook installation (no auto-checkpoint)
|
|
63
|
+
--hook-port PORT Sidecar port for hooks (default: 8401)
|
|
64
|
+
|
|
65
|
+
Restart Claude Code after setup to pick up the new MCP server.
|
|
66
|
+
`.trim());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── SETUP ──────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
function setup() {
|
|
72
|
+
const cwd = process.cwd();
|
|
73
|
+
const projectName = basename(cwd).toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
74
|
+
|
|
75
|
+
// Parse flags
|
|
76
|
+
let agentId = projectName;
|
|
77
|
+
let dbPath: string | null = null;
|
|
78
|
+
let skipClaudeMd = false;
|
|
79
|
+
let isGlobal = false;
|
|
80
|
+
let skipHooks = false;
|
|
81
|
+
let hookPort = '8401';
|
|
82
|
+
|
|
83
|
+
for (let i = 1; i < args.length; i++) {
|
|
84
|
+
if (args[i] === '--agent-id' && args[i + 1]) {
|
|
85
|
+
agentId = args[++i];
|
|
86
|
+
} else if (args[i] === '--db-path' && args[i + 1]) {
|
|
87
|
+
dbPath = args[++i];
|
|
88
|
+
} else if (args[i] === '--no-claude-md') {
|
|
89
|
+
skipClaudeMd = true;
|
|
90
|
+
} else if (args[i] === '--no-hooks') {
|
|
91
|
+
skipHooks = true;
|
|
92
|
+
} else if (args[i] === '--hook-port' && args[i + 1]) {
|
|
93
|
+
hookPort = args[++i];
|
|
94
|
+
} else if (args[i] === '--global') {
|
|
95
|
+
isGlobal = true;
|
|
96
|
+
agentId = 'claude'; // unified agent ID for global setup
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Find the package root (where src/mcp.ts lives)
|
|
101
|
+
const packageRoot = resolve(__dirname, '..');
|
|
102
|
+
const mcpScript = join(packageRoot, 'src', 'mcp.ts');
|
|
103
|
+
const mcpDist = join(packageRoot, 'dist', 'mcp.js');
|
|
104
|
+
|
|
105
|
+
// Determine DB path — default to <awm-root>/data/memory.db (shared across projects)
|
|
106
|
+
if (!dbPath) {
|
|
107
|
+
dbPath = join(packageRoot, 'data', 'memory.db');
|
|
108
|
+
}
|
|
109
|
+
const dbDir = dirname(dbPath);
|
|
110
|
+
|
|
111
|
+
// Ensure data directory exists
|
|
112
|
+
if (!existsSync(dbDir)) {
|
|
113
|
+
mkdirSync(dbDir, { recursive: true });
|
|
114
|
+
console.log(`Created data directory: ${dbDir}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Generate hook secret (or reuse existing one)
|
|
118
|
+
let hookSecret = '';
|
|
119
|
+
const secretPath = join(dirname(dbPath!), '.awm-hook-secret');
|
|
120
|
+
if (existsSync(secretPath)) {
|
|
121
|
+
hookSecret = readFileSync(secretPath, 'utf-8').trim();
|
|
122
|
+
}
|
|
123
|
+
if (!hookSecret) {
|
|
124
|
+
hookSecret = randomBytes(32).toString('hex');
|
|
125
|
+
mkdirSync(dirname(secretPath), { recursive: true });
|
|
126
|
+
writeFileSync(secretPath, hookSecret + '\n');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Determine command based on platform and whether dist exists
|
|
130
|
+
const isWindows = process.platform === 'win32';
|
|
131
|
+
const hasDist = existsSync(mcpDist);
|
|
132
|
+
|
|
133
|
+
const envVars: Record<string, string> = {
|
|
134
|
+
AWM_DB_PATH: (isWindows ? dbPath!.replace(/\\/g, '/') : dbPath!),
|
|
135
|
+
AWM_AGENT_ID: agentId,
|
|
136
|
+
AWM_HOOK_PORT: hookPort,
|
|
137
|
+
AWM_HOOK_SECRET: hookSecret,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
let mcpConfig: { command: string; args: string[]; env: Record<string, string> };
|
|
141
|
+
|
|
142
|
+
if (hasDist) {
|
|
143
|
+
mcpConfig = {
|
|
144
|
+
command: 'node',
|
|
145
|
+
args: [mcpDist.replace(/\\/g, '/')],
|
|
146
|
+
env: envVars,
|
|
147
|
+
};
|
|
148
|
+
} else if (isWindows) {
|
|
149
|
+
mcpConfig = {
|
|
150
|
+
command: 'cmd',
|
|
151
|
+
args: ['/c', 'npx', 'tsx', mcpScript.replace(/\\/g, '/')],
|
|
152
|
+
env: envVars,
|
|
153
|
+
};
|
|
154
|
+
} else {
|
|
155
|
+
mcpConfig = {
|
|
156
|
+
command: 'npx',
|
|
157
|
+
args: ['tsx', mcpScript],
|
|
158
|
+
env: envVars,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Read or create .mcp.json
|
|
163
|
+
const mcpJsonPath = isGlobal ? join(osHomedir(), '.mcp.json') : join(cwd, '.mcp.json');
|
|
164
|
+
let existing: any = { mcpServers: {} };
|
|
165
|
+
if (existsSync(mcpJsonPath)) {
|
|
166
|
+
try {
|
|
167
|
+
existing = JSON.parse(readFileSync(mcpJsonPath, 'utf-8'));
|
|
168
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
169
|
+
} catch {
|
|
170
|
+
existing = { mcpServers: {} };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
existing.mcpServers['agent-working-memory'] = mcpConfig;
|
|
175
|
+
writeFileSync(mcpJsonPath, JSON.stringify(existing, null, 2) + '\n');
|
|
176
|
+
|
|
177
|
+
// Auto-append CLAUDE.md snippet unless --no-claude-md
|
|
178
|
+
let claudeMdAction = '';
|
|
179
|
+
const claudeMdSnippet = `
|
|
180
|
+
|
|
181
|
+
## Memory (AWM)
|
|
182
|
+
You have persistent memory via the agent-working-memory MCP server.
|
|
183
|
+
|
|
184
|
+
### Lifecycle (always do these)
|
|
185
|
+
- Session start: call memory_restore to recover previous context
|
|
186
|
+
- Starting a task: call memory_task_begin (checkpoints + recalls relevant memories)
|
|
187
|
+
- Finishing a task: call memory_task_end with a summary
|
|
188
|
+
- Auto-checkpoint: hooks handle compaction, session end, and 15-min timer (no action needed)
|
|
189
|
+
|
|
190
|
+
### Write memory when:
|
|
191
|
+
- A project decision is made or changed
|
|
192
|
+
- A root cause is discovered after debugging
|
|
193
|
+
- A reusable implementation pattern is established
|
|
194
|
+
- A user preference, constraint, or requirement is clarified
|
|
195
|
+
- A prior assumption is found to be wrong
|
|
196
|
+
- A significant piece of work is completed
|
|
197
|
+
|
|
198
|
+
### Recall memory when:
|
|
199
|
+
- Starting work on a new task or subsystem
|
|
200
|
+
- Re-entering code you haven't touched recently
|
|
201
|
+
- After a failed attempt — check if there's prior knowledge
|
|
202
|
+
- Before refactoring or making architectural changes
|
|
203
|
+
- When a topic comes up that you might have prior context on
|
|
204
|
+
|
|
205
|
+
### Also:
|
|
206
|
+
- After using a recalled memory: call memory_feedback (useful/not-useful)
|
|
207
|
+
- To correct wrong info: call memory_retract
|
|
208
|
+
- To track work items: memory_task_add, memory_task_update, memory_task_list, memory_task_next
|
|
209
|
+
`;
|
|
210
|
+
|
|
211
|
+
// For global: write to ~/.claude/CLAUDE.md (loaded by Claude Code in every session)
|
|
212
|
+
// For project: write to ./CLAUDE.md in the current directory
|
|
213
|
+
const claudeMdPath = isGlobal
|
|
214
|
+
? join(osHomedir(), '.claude', 'CLAUDE.md')
|
|
215
|
+
: join(cwd, 'CLAUDE.md');
|
|
216
|
+
|
|
217
|
+
// Ensure parent directory exists (for ~/.claude/CLAUDE.md)
|
|
218
|
+
const claudeMdDir = dirname(claudeMdPath);
|
|
219
|
+
if (!existsSync(claudeMdDir)) {
|
|
220
|
+
mkdirSync(claudeMdDir, { recursive: true });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (skipClaudeMd) {
|
|
224
|
+
claudeMdAction = ' CLAUDE.md: skipped (--no-claude-md)';
|
|
225
|
+
} else if (existsSync(claudeMdPath)) {
|
|
226
|
+
const content = readFileSync(claudeMdPath, 'utf-8');
|
|
227
|
+
if (content.includes('## Memory (AWM)')) {
|
|
228
|
+
claudeMdAction = ' CLAUDE.md: already has AWM section (skipped)';
|
|
229
|
+
} else {
|
|
230
|
+
writeFileSync(claudeMdPath, content.trimEnd() + '\n' + claudeMdSnippet);
|
|
231
|
+
claudeMdAction = ' CLAUDE.md: appended AWM workflow section';
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
const title = isGlobal ? '# Global Instructions' : `# ${basename(cwd)}`;
|
|
235
|
+
writeFileSync(claudeMdPath, `${title}\n${claudeMdSnippet}`);
|
|
236
|
+
claudeMdAction = ' CLAUDE.md: created with AWM workflow section';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// --- Hook configuration ---
|
|
240
|
+
let hookAction = '';
|
|
241
|
+
if (skipHooks) {
|
|
242
|
+
hookAction = ' Hooks: skipped (--no-hooks)';
|
|
243
|
+
} else {
|
|
244
|
+
// Write hooks to Claude Code settings (~/.claude/settings.json)
|
|
245
|
+
const settingsPath = join(osHomedir(), '.claude', 'settings.json');
|
|
246
|
+
let settings: any = {};
|
|
247
|
+
if (existsSync(settingsPath)) {
|
|
248
|
+
try {
|
|
249
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
250
|
+
} catch {
|
|
251
|
+
settings = {};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!settings.hooks) settings.hooks = {};
|
|
256
|
+
|
|
257
|
+
const hookUrl = `http://127.0.0.1:${hookPort}/hooks/checkpoint`;
|
|
258
|
+
|
|
259
|
+
// Stop — remind Claude to write/recall/switch tasks after each response
|
|
260
|
+
settings.hooks.Stop = [{
|
|
261
|
+
matcher: '',
|
|
262
|
+
hooks: [{
|
|
263
|
+
type: 'command',
|
|
264
|
+
command: 'echo "MEMORY: (1) Did you learn anything new? Call memory_write. (2) Are you about to work on a topic you might have prior knowledge about? Call memory_recall. (3) Switching tasks? Call memory_task_begin."',
|
|
265
|
+
timeout: 5,
|
|
266
|
+
async: true,
|
|
267
|
+
}],
|
|
268
|
+
}];
|
|
269
|
+
|
|
270
|
+
// PreCompact — auto-checkpoint before context compaction
|
|
271
|
+
settings.hooks.PreCompact = [{
|
|
272
|
+
matcher: '',
|
|
273
|
+
hooks: [{
|
|
274
|
+
type: 'command',
|
|
275
|
+
command: `curl -sf -X POST ${hookUrl} -H "Content-Type: application/json" -H "Authorization: Bearer ${hookSecret}" -d "{\\"hook_event_name\\":\\"PreCompact\\"}" --max-time 2`,
|
|
276
|
+
timeout: 3,
|
|
277
|
+
}],
|
|
278
|
+
}];
|
|
279
|
+
|
|
280
|
+
// SessionEnd — auto-checkpoint on session close (fast timeout to avoid cancellation)
|
|
281
|
+
settings.hooks.SessionEnd = [{
|
|
282
|
+
matcher: '',
|
|
283
|
+
hooks: [{
|
|
284
|
+
type: 'command',
|
|
285
|
+
command: `curl -sf -X POST ${hookUrl} -H "Content-Type: application/json" -H "Authorization: Bearer ${hookSecret}" -d "{\\"hook_event_name\\":\\"SessionEnd\\"}" --max-time 2`,
|
|
286
|
+
timeout: 3,
|
|
287
|
+
}],
|
|
288
|
+
}];
|
|
289
|
+
|
|
290
|
+
// Ensure settings directory exists
|
|
291
|
+
const settingsDir = dirname(settingsPath);
|
|
292
|
+
if (!existsSync(settingsDir)) {
|
|
293
|
+
mkdirSync(settingsDir, { recursive: true });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
297
|
+
hookAction = ` Hooks: Stop (memory reminder) + PreCompact + SessionEnd → auto-checkpoint (port ${hookPort})`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const scope = isGlobal ? 'globally (all projects)' : cwd;
|
|
301
|
+
console.log(`
|
|
302
|
+
AWM configured ${isGlobal ? 'globally' : 'for: ' + cwd}
|
|
303
|
+
|
|
304
|
+
Agent ID: ${agentId}
|
|
305
|
+
DB path: ${dbPath}
|
|
306
|
+
MCP config: ${mcpJsonPath}
|
|
307
|
+
Hook port: ${hookPort}
|
|
308
|
+
Hook secret: ${hookSecret.slice(0, 8)}...
|
|
309
|
+
${claudeMdAction}
|
|
310
|
+
${hookAction}
|
|
311
|
+
|
|
312
|
+
Next steps:
|
|
313
|
+
1. Restart Claude Code to pick up the MCP server
|
|
314
|
+
2. The memory tools will appear automatically
|
|
315
|
+
3. Hooks auto-checkpoint on context compaction and session end${isGlobal ? '\n 4. One brain across all your projects — no per-project setup needed' : ''}
|
|
316
|
+
`.trim());
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ─── MCP ──────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
async function mcp() {
|
|
322
|
+
// Dynamic import to avoid loading heavy deps for setup/health commands
|
|
323
|
+
await import('./mcp.js');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ─── SERVE ──────────────────────────────────────
|
|
327
|
+
|
|
328
|
+
async function serve() {
|
|
329
|
+
// Parse --port flag
|
|
330
|
+
for (let i = 1; i < args.length; i++) {
|
|
331
|
+
if (args[i] === '--port' && args[i + 1]) {
|
|
332
|
+
process.env.AWM_PORT = args[++i];
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
await import('./index.js');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ─── HEALTH ──────────────────────────────────────
|
|
339
|
+
|
|
340
|
+
function health() {
|
|
341
|
+
let port = '8400';
|
|
342
|
+
for (let i = 1; i < args.length; i++) {
|
|
343
|
+
if (args[i] === '--port' && args[i + 1]) {
|
|
344
|
+
port = args[++i];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
const result = execSync(`curl -sf http://localhost:${port}/health`, {
|
|
350
|
+
encoding: 'utf8',
|
|
351
|
+
timeout: 5000,
|
|
352
|
+
});
|
|
353
|
+
const data = JSON.parse(result);
|
|
354
|
+
console.log(`OK — v${data.version} (${data.timestamp})`);
|
|
355
|
+
} catch {
|
|
356
|
+
console.error(`Cannot reach AWM server on port ${port}`);
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ─── Dispatch ──────────────────────────────────────
|
|
362
|
+
|
|
363
|
+
switch (command) {
|
|
364
|
+
case 'setup':
|
|
365
|
+
setup();
|
|
366
|
+
break;
|
|
367
|
+
case 'mcp':
|
|
368
|
+
mcp();
|
|
369
|
+
break;
|
|
370
|
+
case 'serve':
|
|
371
|
+
serve();
|
|
372
|
+
break;
|
|
373
|
+
case 'health':
|
|
374
|
+
health();
|
|
375
|
+
break;
|
|
376
|
+
case '--help':
|
|
377
|
+
case '-h':
|
|
378
|
+
case undefined:
|
|
379
|
+
printUsage();
|
|
380
|
+
break;
|
|
381
|
+
default:
|
|
382
|
+
console.error(`Unknown command: ${command}`);
|
|
383
|
+
printUsage();
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|