agent-working-memory 0.14.6 → 0.15.5
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/.claude-plugin/marketplace.json +17 -0
- package/README.md +67 -3
- package/dist/adapters/claude-code.d.ts +9 -0
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +1 -1
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/common.d.ts +4 -1
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +2 -2
- package/dist/adapters/common.js.map +1 -1
- package/dist/cli.js +54 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/write-pipeline.js +17 -0
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/storage/factory.d.ts.map +1 -1
- package/dist/storage/factory.js +20 -8
- package/dist/storage/factory.js.map +1 -1
- package/package.json +9 -3
- package/plugin/.claude-plugin/plugin.json +34 -0
- package/plugin/README.md +36 -0
- package/plugin/bin/awm-mcp-launcher.cjs +109 -0
- package/plugin/hooks/awm-checkpoint.cjs +55 -0
- package/plugin/hooks/awm-db-mutation-reminder.cjs +25 -0
- package/plugin/hooks/awm-find-sidecar.cjs +145 -0
- package/plugin/hooks/awm-prime.cjs +97 -0
- package/plugin/hooks/hooks.json +66 -0
- package/plugin/skills/awm-memory/SKILL.md +445 -0
- package/src/adapters/claude-code.ts +1 -1
- package/src/adapters/common.ts +5 -3
- package/src/cli.ts +58 -1
- package/src/core/write-pipeline.ts +18 -0
- package/src/plugin/awm-mcp-launcher.cjs +109 -0
- package/src/storage/factory.ts +19 -7
package/src/cli.ts
CHANGED
|
@@ -47,6 +47,7 @@ AgentWorkingMemory — Cognitive memory for AI agents
|
|
|
47
47
|
|
|
48
48
|
Usage:
|
|
49
49
|
awm setup [target] [options] Configure AWM for an AI CLI
|
|
50
|
+
awm plugin Install as a Claude Code plugin (local, no GitHub)
|
|
50
51
|
awm doctor [target|--all] Validate AWM integrations
|
|
51
52
|
awm mcp Start MCP server (stdio)
|
|
52
53
|
awm serve [--port <port>] Start HTTP API server
|
|
@@ -145,7 +146,8 @@ async function setup() {
|
|
|
145
146
|
// Build context. A previous install's values survive unless a flag overrides them
|
|
146
147
|
// (0.14.6) — an upgrade must never repoint the database or rename the agent.
|
|
147
148
|
const existingEnv = adapter.readExistingEnv?.(isGlobal, process.cwd()) ?? null;
|
|
148
|
-
|
|
149
|
+
// `awm setup claude-code` -> client=claude-code, cursor -> client=cursor, and so on.
|
|
150
|
+
const ctx = buildSetupContext({ agentId, dbPath, isGlobal, hookPort, hookPortRange, installPrime, existingEnv, client: target });
|
|
149
151
|
|
|
150
152
|
// Run adapter
|
|
151
153
|
const configAction = adapter.writeMcpConfig(ctx);
|
|
@@ -172,6 +174,58 @@ Next steps:
|
|
|
172
174
|
`.trim());
|
|
173
175
|
}
|
|
174
176
|
|
|
177
|
+
// ─── PLUGIN ──────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Print the two lines that install AWM as a Claude Code plugin FROM THIS INSTALL.
|
|
181
|
+
*
|
|
182
|
+
* Most users are on Windows and will never clone the repository, so
|
|
183
|
+
* `/plugin marketplace add CompleteIdeas/agent-working-memory` — which needs GitHub — is the
|
|
184
|
+
* wrong default for them. npm has already put a complete marketplace on their disk; they
|
|
185
|
+
* just have no way to know where it is. This tells them, with the real resolved path.
|
|
186
|
+
*/
|
|
187
|
+
function pluginInfo(): void {
|
|
188
|
+
// dist/cli.js -> package root
|
|
189
|
+
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
190
|
+
const marketplace = join(pkgRoot, '.claude-plugin', 'marketplace.json');
|
|
191
|
+
|
|
192
|
+
if (!existsSync(marketplace)) {
|
|
193
|
+
console.log(`
|
|
194
|
+
AWM ${VERSION} — plugin not found in this install
|
|
195
|
+
|
|
196
|
+
Looked for: ${marketplace}
|
|
197
|
+
|
|
198
|
+
This happens when AWM is running from a source checkout that has not been built,
|
|
199
|
+
or from a package published before the plugin was bundled (< 0.15.3).
|
|
200
|
+
|
|
201
|
+
From a checkout: npm run build && npm run build:plugin
|
|
202
|
+
Otherwise: npm install -g agent-working-memory@latest
|
|
203
|
+
`.trimStart());
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log(`
|
|
208
|
+
AWM ${VERSION} — install as a Claude Code plugin
|
|
209
|
+
|
|
210
|
+
Paste these two lines into Claude Code:
|
|
211
|
+
|
|
212
|
+
/plugin marketplace add ${pkgRoot}
|
|
213
|
+
/plugin install awm@agent-working-memory
|
|
214
|
+
|
|
215
|
+
Then restart Claude Code. You get 19 memory tools, the session hooks, and the
|
|
216
|
+
usage guidance as a skill.
|
|
217
|
+
|
|
218
|
+
No GitHub access needed — the marketplace is this npm install, already on disk.
|
|
219
|
+
|
|
220
|
+
The store defaults to the same file \`awm setup --global\` uses, so a plugin
|
|
221
|
+
install and a CLI install share one memory rather than quietly keeping two.
|
|
222
|
+
|
|
223
|
+
Check it worked: awm doctor claude-code
|
|
224
|
+
More: https://github.com/CompleteIdeas/agent-working-memory/blob/master/docs/plugin.md
|
|
225
|
+
`.trimStart());
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
175
229
|
// ─── DOCTOR ──────────────────────────────────────
|
|
176
230
|
|
|
177
231
|
async function doctor() {
|
|
@@ -803,6 +857,9 @@ switch (command) {
|
|
|
803
857
|
case 'setup':
|
|
804
858
|
await setup();
|
|
805
859
|
break;
|
|
860
|
+
case 'plugin':
|
|
861
|
+
pluginInfo();
|
|
862
|
+
break;
|
|
806
863
|
case 'doctor':
|
|
807
864
|
await doctor();
|
|
808
865
|
break;
|
|
@@ -491,6 +491,24 @@ async function createNewEngram(
|
|
|
491
491
|
|
|
492
492
|
const tags = [...(input.tags ?? [])];
|
|
493
493
|
if (isLowSalience && !tags.includes('low-salience')) tags.push('low-salience');
|
|
494
|
+
|
|
495
|
+
// Which CLIENT wrote this: `client=claude-code`, `client=claude-desktop`, `client=cursor`.
|
|
496
|
+
//
|
|
497
|
+
// Deliberately not called `surface`: memory_whoami already reports a `surface` field
|
|
498
|
+
// meaning the TRANSPORT (mcp | http), and one word for two concepts is how you lose an
|
|
499
|
+
// hour six months from now. Transport is how the request arrived; client is which
|
|
500
|
+
// application it arrived from.
|
|
501
|
+
// AWM_CLIENT is set by the thing that launches the server (the Claude Code plugin, the
|
|
502
|
+
// Desktop extension, `awm setup`), never by the caller, so it records where a memory came
|
|
503
|
+
// from rather than what the writer claims.
|
|
504
|
+
//
|
|
505
|
+
// Captured by default rather than behind a flag, because provenance is one-way: a tag can
|
|
506
|
+
// be ignored or stripped later, but the origin of a memory written without one is gone for
|
|
507
|
+
// good. An explicit `client=` in the caller's tags always wins.
|
|
508
|
+
const client = (process.env.AWM_CLIENT ?? '').trim();
|
|
509
|
+
if (client && !tags.some(t => t.toLowerCase().startsWith('client='))) {
|
|
510
|
+
tags.push(`client=${client}`);
|
|
511
|
+
}
|
|
494
512
|
|
|
495
513
|
// Auto-tag meta-tags (default-OFF, AWM_AUTOTAG=1). extractMetaTags emits
|
|
496
514
|
// `entity:<ProperNoun>` and `cat:<category>` tags from concept+content. These
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
//
|
|
5
|
+
// Entry point for the Claude Code plugin's MCP server.
|
|
6
|
+
//
|
|
7
|
+
// This is a REAL source file, not a string inside the generator, because it is real code:
|
|
8
|
+
// it gets syntax-checked, linted and tested like everything else. `npm run build:plugin`
|
|
9
|
+
// copies it to plugin/bin/ and substitutes __AWM_VERSION__.
|
|
10
|
+
//
|
|
11
|
+
// Two jobs a bare `mcpServers` entry in plugin.json cannot do:
|
|
12
|
+
//
|
|
13
|
+
// 1. Default the store to ~/.awm/memory.db — the SAME file `awm setup --global` writes.
|
|
14
|
+
// Without this the plugin opens its own empty database and "your memory follows you
|
|
15
|
+
// between Claude Code, Desktop and Cowork" is simply false.
|
|
16
|
+
//
|
|
17
|
+
// 2. Find the server. A plugin does not know whether the package is installed globally,
|
|
18
|
+
// sits in a local node_modules, or is a checkout on disk.
|
|
19
|
+
//
|
|
20
|
+
// Fails loudly on stderr. A silent exit here looks like "the memory tools just aren't there",
|
|
21
|
+
// which is the least debuggable failure this could have.
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const { join, resolve, sep } = require('node:path');
|
|
25
|
+
const { homedir } = require('node:os');
|
|
26
|
+
const { existsSync } = require('node:fs');
|
|
27
|
+
const { pathToFileURL } = require('node:url');
|
|
28
|
+
const { spawn, execFileSync } = require('node:child_process');
|
|
29
|
+
|
|
30
|
+
const NL = '\n';
|
|
31
|
+
|
|
32
|
+
// Claude Desktop substitutes user_config values into env, and a field the user cleared
|
|
33
|
+
// arrives as an empty string rather than being absent. An empty AWM_AGENT_ID would override
|
|
34
|
+
// the server's directory-derived default with nothing, so drop empties before anything reads
|
|
35
|
+
// them and let the normal defaults apply.
|
|
36
|
+
for (const k of ['AWM_DB_PATH', 'AWM_AGENT_ID', 'AWM_HOOK_PORT', 'AWM_PACKAGE_ROOT']) {
|
|
37
|
+
if (process.env[k] !== undefined && String(process.env[k]).trim() === '') delete process.env[k];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// An explicit AWM_DB_PATH always wins, so per-project pools still work.
|
|
41
|
+
if (!process.env.AWM_DB_PATH) {
|
|
42
|
+
process.env.AWM_DB_PATH = join(homedir(), '.awm', 'memory.db').split(sep).join('/');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const tried = [];
|
|
46
|
+
function candidate(p) {
|
|
47
|
+
if (!p) return null;
|
|
48
|
+
tried.push(p);
|
|
49
|
+
return existsSync(p) ? p : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// In priority order. npx is last: on a cold cache it rebuilds better-sqlite3, which takes
|
|
53
|
+
// minutes and is indistinguishable from a hang.
|
|
54
|
+
let found =
|
|
55
|
+
// 1. An explicit override.
|
|
56
|
+
candidate(process.env.AWM_PACKAGE_ROOT && join(process.env.AWM_PACKAGE_ROOT, 'dist', 'mcp.js')) ||
|
|
57
|
+
// 2. The plugin sitting inside an AWM checkout (plugin/bin -> ../../dist).
|
|
58
|
+
candidate(resolve(__dirname, '..', '..', 'dist', 'mcp.js')) ||
|
|
59
|
+
// 3. A local dependency of the project being worked on.
|
|
60
|
+
candidate(join(process.cwd(), 'node_modules', 'agent-working-memory', 'dist', 'mcp.js'));
|
|
61
|
+
|
|
62
|
+
// 4. A global install. require.resolve does not search global roots, so ask npm once.
|
|
63
|
+
if (!found) {
|
|
64
|
+
try {
|
|
65
|
+
const isWin = process.platform === 'win32';
|
|
66
|
+
const root = execFileSync(isWin ? 'npm.cmd' : 'npm', ['root', '-g'], {
|
|
67
|
+
encoding: 'utf-8',
|
|
68
|
+
shell: isWin,
|
|
69
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
70
|
+
}).trim();
|
|
71
|
+
found = candidate(root && join(root, 'agent-working-memory', 'dist', 'mcp.js'));
|
|
72
|
+
} catch {
|
|
73
|
+
/* npm not on PATH — fall through */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 5. Anywhere node can resolve it from here.
|
|
78
|
+
if (!found) {
|
|
79
|
+
try {
|
|
80
|
+
found = require.resolve('agent-working-memory/dist/mcp.js');
|
|
81
|
+
} catch {
|
|
82
|
+
/* not installed where this script can see it */
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (found) {
|
|
87
|
+
// dist/ is ESM ("type": "module"), so import() — require() throws ERR_REQUIRE_ESM.
|
|
88
|
+
import(pathToFileURL(found).href).catch((e) => {
|
|
89
|
+
process.stderr.write('[awm] failed to load ' + found + ': ' + e.message + NL);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
// Last resort, pinned so the tools match the hook scripts shipped beside them.
|
|
94
|
+
// shell:true on Windows: spawning npx.cmd directly throws EINVAL since Node 18.20/20.12.
|
|
95
|
+
const isWin = process.platform === 'win32';
|
|
96
|
+
const child = spawn(
|
|
97
|
+
isWin ? 'npx.cmd' : 'npx',
|
|
98
|
+
['-y', '-p', 'agent-working-memory@__AWM_VERSION__', 'awm', 'mcp'],
|
|
99
|
+
{ stdio: 'inherit', env: process.env, shell: isWin }
|
|
100
|
+
);
|
|
101
|
+
child.on('error', (e) => {
|
|
102
|
+
process.stderr.write('[awm] could not start the MCP server: ' + e.message + NL);
|
|
103
|
+
process.stderr.write('[awm] looked in:' + NL);
|
|
104
|
+
for (const t of tried) process.stderr.write(' ' + t + NL);
|
|
105
|
+
process.stderr.write('[awm] fix: npm install -g agent-working-memory' + NL);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
});
|
|
108
|
+
child.on('exit', (code) => process.exit(code == null ? 0 : code));
|
|
109
|
+
}
|
package/src/storage/factory.ts
CHANGED
|
@@ -49,13 +49,25 @@ export type StoreBackend = 'sqlite' | 'pglite' | 'postgres';
|
|
|
49
49
|
function detectBackendFromDisk(): StoreBackend | null {
|
|
50
50
|
// If AWM_DB_PATH is set, infer from its shape.
|
|
51
51
|
const explicitPath = process.env.AWM_DB_PATH;
|
|
52
|
-
if (explicitPath
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
if (explicitPath) {
|
|
53
|
+
if (existsSync(explicitPath)) {
|
|
54
|
+
try {
|
|
55
|
+
const stat = statSync(explicitPath);
|
|
56
|
+
if (stat.isDirectory()) return 'pglite'; // PGlite uses a directory
|
|
57
|
+
if (stat.isFile()) return 'sqlite'; // SQLite is a single file
|
|
58
|
+
} catch { /* fall through to the default below */ }
|
|
59
|
+
}
|
|
60
|
+
// The caller named a path that does not exist yet — a brand-new store. Default it to
|
|
61
|
+
// SQLite and STOP: falling through to the cwd conventions below would pick the backend
|
|
62
|
+
// from files that have nothing to do with the path that was asked for.
|
|
63
|
+
//
|
|
64
|
+
// Not hypothetical. Running from a checkout containing a `memory-pglite/` directory
|
|
65
|
+
// made a fresh AWM_DB_PATH=/tmp/new-store.db resolve to PGlite, which is
|
|
66
|
+
// single-process — so two surfaces pointed at 'the same store' would have had the
|
|
67
|
+
// second refuse to start. Found by the cross-surface end-to-end test.
|
|
68
|
+
// PGlite at a new path is still reachable via AWM_STORE_BACKEND=pglite.
|
|
69
|
+
return 'sqlite';
|
|
70
|
+
}
|
|
59
71
|
|
|
60
72
|
// Otherwise look for the conventional defaults in cwd.
|
|
61
73
|
// PGlite directory wins over SQLite file when both exist — assume the
|