@memstack/cli 0.8.0 → 0.8.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 +46 -3
- package/dist/cli.js +219 -34
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @memstack/cli
|
|
2
2
|
|
|
3
|
-
CLI for MemStack — shell-based agent memory.
|
|
3
|
+
CLI for MemStack — shell-based agent memory. Connect Claude Code and Codex to one shared memory per project, or use it from bash scripts, CI pipelines, or any subprocess-capable agent.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,9 +10,41 @@ npm install -g @memstack/cli
|
|
|
10
10
|
npx @memstack/cli [command]
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## Claude Code and Codex
|
|
14
|
+
|
|
15
|
+
Give both agents one persistent memory per project. MemStack never installs
|
|
16
|
+
storage drivers; install the one for your store with the MCP server:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g @memstack/cli @memstack/mcp better-sqlite3@^11.10.0
|
|
20
|
+
memstack init # LLM provider + store → ~/.memstack/config.json
|
|
21
|
+
memstack connect claude-code
|
|
22
|
+
memstack connect codex # then approve the MemStack hook once in Codex with /hooks
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Command | What it does |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `init` | Choose an LLM provider and store; verifies the key with a real request. Non-interactive: `--provider openai-compatible\|anthropic --base-url <url> --model <name> --api-key-env <VAR> --store <type> [--path <file> \| --url <url>] --yes`. |
|
|
28
|
+
| `connect <claude-code\|codex>` | Registers your installed `memstack-mcp`, adds a session-start hook that loads project memories, and for Codex a marked block in `~/.codex/AGENTS.md`. Checks the server first and undoes everything if a step fails. `--dry-run`, `--no-hooks`, `--no-agents-md`. |
|
|
29
|
+
| `disconnect <claude-code\|codex>` | Removes everything `connect` added, restoring each file exactly. Memories are kept. |
|
|
30
|
+
| `status` | Config, storage, the current project, and each agent's connection, hook, and guidance. |
|
|
31
|
+
| `doctor` | Diagnoses problems and prints the fix for each. `--live` tests the LLM key. Exits non-zero when it finds a problem. |
|
|
32
|
+
| `memories [query]` | Lists or searches the current project's memories. `--global`, `--limit <n>`, `--delete <id>`. |
|
|
33
|
+
| `project` | Shows the current project ID. `project pin <id>` writes `.memstack.json`; `project merge <old-id>` moves memories from an old ID. |
|
|
34
|
+
|
|
35
|
+
These commands print readable text, not JSON, and read
|
|
36
|
+
`~/.memstack/config.json` (or `$MEMSTACK_HOME/config.json`) overlaid with the
|
|
37
|
+
environment variables below. Projects are identified by the repository's
|
|
38
|
+
first commit, so clones, worktrees, and renamed remotes share memories. See
|
|
39
|
+
[Harness Memory](https://github.com/isiomaC/memstack#harness-memory-claude-code--codex)
|
|
40
|
+
for details.
|
|
41
|
+
|
|
13
42
|
## Configuration
|
|
14
43
|
|
|
15
|
-
|
|
44
|
+
Every command reads `~/.memstack/config.json` (written by `memstack init`),
|
|
45
|
+
overlaid with these environment variables: any LLM variable replaces the
|
|
46
|
+
file's LLM settings, and `MEMSTACK_STORAGE` replaces its storage settings.
|
|
47
|
+
`memstack status` shows which source is in use. Same scheme as @memstack/mcp.
|
|
16
48
|
|
|
17
49
|
| Variable | Purpose | Default |
|
|
18
50
|
|---|---|---|
|
|
@@ -29,7 +61,18 @@ All via environment variables. Same scheme as @memstack/mcp.
|
|
|
29
61
|
|
|
30
62
|
## Commands
|
|
31
63
|
|
|
32
|
-
|
|
64
|
+
The memory commands below print JSON to stdout. Errors go to stderr.
|
|
65
|
+
`memstack <command> --help` prints a command's flags; `memstack --version`
|
|
66
|
+
prints the installed version.
|
|
67
|
+
|
|
68
|
+
Inside a code repository, `--project` can replace `--actor` on any of them. It
|
|
69
|
+
uses the repository's project, the same memory Claude Code and Codex share
|
|
70
|
+
through `memstack connect`:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
memstack store --project --content "Deploy with fly deploy" --type instruction
|
|
74
|
+
memstack retrieve --project --query "deploy"
|
|
75
|
+
```
|
|
33
76
|
|
|
34
77
|
### store
|
|
35
78
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { parseArgs } from 'util';
|
|
3
3
|
import { readFile, writeFile } from 'fs/promises';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { projectNamespace, MemStack, HarnessMemory, defaultRecallNamespaces, AnthropicLLMAdapter, OpenAILLMAdapter, OpenAIEmbeddingAdapter, RedisStorageAdapter, SQLiteStorageAdapter, PostgresStorageAdapter, DiskStorageAdapter, MarkdownStorageAdapter, InMemoryStorageAdapter } from '@memstack/core';
|
|
6
|
+
import { realpathSync, existsSync, mkdirSync, readFileSync, writeFileSync, statSync, chmodSync, renameSync, rmSync, copyFileSync, constants } from 'fs';
|
|
6
7
|
import { homedir } from 'os';
|
|
7
|
-
import { join,
|
|
8
|
-
import { execFile,
|
|
8
|
+
import { resolve, join, dirname, delimiter, basename } from 'path';
|
|
9
|
+
import { execFile, execFileSync, spawn } from 'child_process';
|
|
9
10
|
import { createHash } from 'crypto';
|
|
10
11
|
import { createInterface } from 'readline/promises';
|
|
11
12
|
|
|
@@ -262,9 +263,6 @@ async function buildStorageAdapter(env) {
|
|
|
262
263
|
throw new Error(`Unknown MEMSTACK_STORAGE: "${storage}". Supported: memory, markdown, disk, postgres, sqlite, redis`);
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
|
-
async function loadConfigFromEnv() {
|
|
266
|
-
return buildConfig(process.env);
|
|
267
|
-
}
|
|
268
266
|
async function loadConfig(options = {}) {
|
|
269
267
|
return buildConfig(mergeConfigFile(readConfigFile(options.configPath), process.env));
|
|
270
268
|
}
|
|
@@ -304,7 +302,7 @@ async function buildConfig(env) {
|
|
|
304
302
|
|
|
305
303
|
// src/config.ts
|
|
306
304
|
async function loadConfig2() {
|
|
307
|
-
const { config } = await
|
|
305
|
+
const { config } = await loadConfig();
|
|
308
306
|
return config;
|
|
309
307
|
}
|
|
310
308
|
var createRunner = (env = process.env) => (command, args) => new Promise((resolve2) => {
|
|
@@ -337,9 +335,9 @@ function claudeCodeAdapter(options = {}) {
|
|
|
337
335
|
// Also on clear and compact, so the memories survive /clear and context compaction.
|
|
338
336
|
sessionHook: { path: settingsPath, matcher: "startup|resume|clear|compact" },
|
|
339
337
|
async inspect() {
|
|
340
|
-
const
|
|
341
|
-
const state = { installed:
|
|
342
|
-
if (state.installed) state.version =
|
|
338
|
+
const version2 = await runner(binary, ["--version"]);
|
|
339
|
+
const state = { installed: version2.code === 0, configPath };
|
|
340
|
+
if (state.installed) state.version = version2.stdout.trim().split(/\s+/)[0];
|
|
343
341
|
state.entry = readEntry(configPath);
|
|
344
342
|
return state;
|
|
345
343
|
},
|
|
@@ -403,7 +401,7 @@ var CODEX_INSTRUCTIONS = [
|
|
|
403
401
|
"## Project memory (MemStack)",
|
|
404
402
|
"",
|
|
405
403
|
"When the user asks you to remember something, save it with the memstack `memory_store` tool. Do not only acknowledge it, and do not write it into project files unless the user asks for a file change.",
|
|
406
|
-
"
|
|
404
|
+
"At the start of a task, and before answering questions about this project's conventions, decisions, or the user's preferences, check `memory_retrieve`: it holds what earlier sessions, in Codex or other agents, were told to remember."
|
|
407
405
|
].join("\n");
|
|
408
406
|
function renderBlock(body, harness) {
|
|
409
407
|
return `${BEGIN} (managed by \`memstack connect ${harness}\`; removed by \`memstack disconnect ${harness}\`) -->
|
|
@@ -461,10 +459,10 @@ function codexAdapter(options = {}) {
|
|
|
461
459
|
instructions: { path: join(home, "AGENTS.md"), body: CODEX_INSTRUCTIONS },
|
|
462
460
|
sessionHook: { path: join(home, "hooks.json"), matcher: "startup|resume" },
|
|
463
461
|
async inspect() {
|
|
464
|
-
const
|
|
465
|
-
const state = { installed:
|
|
462
|
+
const version2 = await runner(binary, ["--version"]);
|
|
463
|
+
const state = { installed: version2.code === 0, configPath };
|
|
466
464
|
if (!state.installed) return state;
|
|
467
|
-
state.version =
|
|
465
|
+
state.version = version2.stdout.trim().split(/\s+/).pop();
|
|
468
466
|
const got = await runner(binary, ["mcp", "get", SERVER_NAME, "--json"]);
|
|
469
467
|
if (got.code !== 0) {
|
|
470
468
|
if (/No MCP server named/i.test(got.stderr + got.stdout)) return state;
|
|
@@ -978,9 +976,19 @@ async function disconnect(ids, flags) {
|
|
|
978
976
|
var label = (name) => (name ? `${name}:` : "").padEnd(14);
|
|
979
977
|
async function status() {
|
|
980
978
|
const file = readConfigFile();
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
979
|
+
const fromEnv = envOverrides();
|
|
980
|
+
const config = file ? configFilePath() : fromEnv.llm || fromEnv.storage ? "none, using environment variables" : "none (run `memstack init`)";
|
|
981
|
+
out(`${label("Config")}${config}`);
|
|
982
|
+
const env = mergeConfigFile(file, process.env);
|
|
983
|
+
const envNote = (overridden) => overridden ? " (from environment variables)" : "";
|
|
984
|
+
if (env.OPENAI_API_KEY || env.ANTHROPIC_API_KEY) {
|
|
985
|
+
const provider = env.ANTHROPIC_API_KEY ? "anthropic" : "openai-compatible";
|
|
986
|
+
const baseURL = provider === "openai-compatible" ? env.MEMSTACK_OPENAI_BASE_URL : void 0;
|
|
987
|
+
out(`${label("LLM")}${provider}${env.MEMSTACK_LLM_MODEL ? ` (${env.MEMSTACK_LLM_MODEL})` : ""}${baseURL ? ` at ${baseURL}` : ""}${envNote(fromEnv.llm)}`);
|
|
988
|
+
} else if (file || fromEnv.llm) {
|
|
989
|
+
out(`${label("LLM")}no API key (run \`memstack init\`)`);
|
|
990
|
+
}
|
|
991
|
+
if (env.MEMSTACK_STORAGE) out(`${label("Storage")}${describeStorage(env)}${envNote(fromEnv.storage)}`);
|
|
984
992
|
const project = resolveProject();
|
|
985
993
|
out(`${label("Project")}${project.id} (${describeSource(project.source)}: ${project.key})`);
|
|
986
994
|
const mcp = findMemstackMcp();
|
|
@@ -1008,6 +1016,20 @@ async function status() {
|
|
|
1008
1016
|
}
|
|
1009
1017
|
return 0;
|
|
1010
1018
|
}
|
|
1019
|
+
function envOverrides() {
|
|
1020
|
+
return {
|
|
1021
|
+
llm: ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "MEMSTACK_OPENAI_BASE_URL", "MEMSTACK_LLM_MODEL"].some((key) => process.env[key] !== void 0),
|
|
1022
|
+
storage: process.env.MEMSTACK_STORAGE !== void 0
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
function describeStorage(env) {
|
|
1026
|
+
const type = env.MEMSTACK_STORAGE;
|
|
1027
|
+
if (type === "memory") return "memory (lost when the process exits)";
|
|
1028
|
+
const path = type === "sqlite" ? env.SQLITE_PATH : type === "disk" || type === "markdown" ? env.MEMSTACK_DIR : void 0;
|
|
1029
|
+
if (path) return `${type} at ${path}`;
|
|
1030
|
+
const url = type === "postgres" ? env.DATABASE_URL : type === "redis" ? env.REDIS_URL : void 0;
|
|
1031
|
+
return url ? `${type} (URL configured)` : type;
|
|
1032
|
+
}
|
|
1011
1033
|
function hookState(adapter, mcp) {
|
|
1012
1034
|
const current = readHook(adapter.sessionHook.path);
|
|
1013
1035
|
if (!current) return "missing";
|
|
@@ -1026,6 +1048,7 @@ async function doctor(flags) {
|
|
|
1026
1048
|
try {
|
|
1027
1049
|
file = readConfigFile();
|
|
1028
1050
|
if (file) pass(`Config file ${configFilePath()}`);
|
|
1051
|
+
else if (envOverrides().llm || envOverrides().storage) pass(`Using environment variables (no config file at ${configFilePath()})`);
|
|
1029
1052
|
else warn(`No config file at ${configFilePath()}; run \`memstack init\`.`);
|
|
1030
1053
|
} catch (error) {
|
|
1031
1054
|
fail2(error.message);
|
|
@@ -1227,6 +1250,149 @@ function createPrompter() {
|
|
|
1227
1250
|
};
|
|
1228
1251
|
}
|
|
1229
1252
|
|
|
1253
|
+
// src/help.ts
|
|
1254
|
+
var ACTOR = ` --actor <id> Actor ID that groups related memories
|
|
1255
|
+
--project Use this repository's project instead of --actor, the same
|
|
1256
|
+
memories Claude Code and Codex see through \`memstack connect\``;
|
|
1257
|
+
var MEMORY_ENV = `
|
|
1258
|
+
Reads ~/.memstack/config.json (written by \`memstack init\`); environment variables
|
|
1259
|
+
such as OPENAI_API_KEY and MEMSTACK_STORAGE override it. Prints JSON.
|
|
1260
|
+
`;
|
|
1261
|
+
var COMMAND_HELP = {
|
|
1262
|
+
init: `memstack init [flags]
|
|
1263
|
+
|
|
1264
|
+
Choose an LLM provider and a store, check the key with a real request, and write
|
|
1265
|
+
~/.memstack/config.json (readable only by you). Prompts for anything not given
|
|
1266
|
+
as a flag; with --yes or without a terminal it uses flags and existing values.
|
|
1267
|
+
|
|
1268
|
+
--provider <name> openai-compatible (default) or anthropic
|
|
1269
|
+
--base-url <url> OpenAI-compatible endpoint, e.g. https://api.deepseek.com
|
|
1270
|
+
--model <name> Model override
|
|
1271
|
+
--api-key-env <VAR> Read the API key from this environment variable
|
|
1272
|
+
--store <type> Storage type (default: sqlite)
|
|
1273
|
+
--path <path> File or directory for sqlite, disk, or markdown
|
|
1274
|
+
--url <url> Connection URL for postgres, redis, or mongodb
|
|
1275
|
+
--yes Don't prompt
|
|
1276
|
+
`,
|
|
1277
|
+
connect: `memstack connect <claude-code|codex> [flags]
|
|
1278
|
+
|
|
1279
|
+
Register MemStack's MCP server with a harness, add a session-start hook that
|
|
1280
|
+
loads the project's memories, and for Codex add a marked block to
|
|
1281
|
+
~/.codex/AGENTS.md. Checks the server first and undoes every change if a step
|
|
1282
|
+
fails. Safe to run again.
|
|
1283
|
+
|
|
1284
|
+
--dry-run Show what would change without changing it
|
|
1285
|
+
--no-hooks Skip the session-start hook
|
|
1286
|
+
--no-agents-md Skip the Codex AGENTS.md block
|
|
1287
|
+
`,
|
|
1288
|
+
disconnect: `memstack disconnect <claude-code|codex> [flags]
|
|
1289
|
+
|
|
1290
|
+
Remove everything \`memstack connect\` added. Memories are kept.
|
|
1291
|
+
|
|
1292
|
+
--dry-run Show what would change without changing it
|
|
1293
|
+
`,
|
|
1294
|
+
status: `memstack status
|
|
1295
|
+
|
|
1296
|
+
Show where configuration comes from (config file or environment variables), the
|
|
1297
|
+
LLM and storage in use, this repository's project ID, and each harness's
|
|
1298
|
+
connection, guidance, and session-start hook.
|
|
1299
|
+
`,
|
|
1300
|
+
doctor: `memstack doctor [flags]
|
|
1301
|
+
|
|
1302
|
+
Check the configuration, storage driver, MCP server, and harness connections,
|
|
1303
|
+
and print a fix for each problem. Exits non-zero when it finds any.
|
|
1304
|
+
|
|
1305
|
+
--live Also send a test request to the LLM provider
|
|
1306
|
+
`,
|
|
1307
|
+
memories: `memstack memories [query] [flags]
|
|
1308
|
+
|
|
1309
|
+
List this repository's memories (project and global), or search them.
|
|
1310
|
+
|
|
1311
|
+
--global Only global memories
|
|
1312
|
+
--limit <n> Maximum to show (default: 20)
|
|
1313
|
+
--delete <id> Delete one memory
|
|
1314
|
+
`,
|
|
1315
|
+
project: `memstack project [pin <id> | merge <old-id>]
|
|
1316
|
+
|
|
1317
|
+
memstack project Show this repository's project ID and where it comes from
|
|
1318
|
+
memstack project pin <id> Write .memstack.json so every clone uses <id>
|
|
1319
|
+
memstack project merge <old> Move memories stored under an old project ID to this one
|
|
1320
|
+
`,
|
|
1321
|
+
store: `memstack store --content <text> (--actor <id> | --project) [flags]
|
|
1322
|
+
|
|
1323
|
+
${ACTOR}
|
|
1324
|
+
--content <text> Memory text (required)
|
|
1325
|
+
--type <type> interaction (default), summary, observation, fact, reflection,
|
|
1326
|
+
preference, decision, or instruction
|
|
1327
|
+
--importance <n> 0.0 to 1.0
|
|
1328
|
+
--tags <a,b> Comma-separated tags
|
|
1329
|
+
${MEMORY_ENV}`,
|
|
1330
|
+
retrieve: `memstack retrieve (--actor <id> | --project) [flags]
|
|
1331
|
+
|
|
1332
|
+
${ACTOR}
|
|
1333
|
+
--query <text> Search text
|
|
1334
|
+
--strategy <name> recent (default), important, semantic, or hybrid
|
|
1335
|
+
--limit <n> Default: 10
|
|
1336
|
+
--tags <a,b> Only memories with these tags
|
|
1337
|
+
--created-after <iso> e.g. 2026-01-01T00:00:00Z
|
|
1338
|
+
--created-before <iso>
|
|
1339
|
+
${MEMORY_ENV}`,
|
|
1340
|
+
context: `memstack context (--actor <id> | --project) [flags]
|
|
1341
|
+
|
|
1342
|
+
Compile memories into a token-budgeted system prompt.
|
|
1343
|
+
|
|
1344
|
+
${ACTOR}
|
|
1345
|
+
--max-tokens <n> Token budget (default: 2000)
|
|
1346
|
+
${MEMORY_ENV}`,
|
|
1347
|
+
summarize: `memstack summarize (--actor <id> | --project) [flags]
|
|
1348
|
+
|
|
1349
|
+
Compress old interactions into summaries.
|
|
1350
|
+
|
|
1351
|
+
${ACTOR}
|
|
1352
|
+
--older-than <d> Only memories older than this, e.g. 7d, 24h, 30m
|
|
1353
|
+
${MEMORY_ENV}`,
|
|
1354
|
+
prune: `memstack prune (--actor <id> | --project) [flags]
|
|
1355
|
+
|
|
1356
|
+
${ACTOR}
|
|
1357
|
+
--type <name> byAge (default), byImportance, or byCount
|
|
1358
|
+
--max-age <d> For byAge, e.g. 30d
|
|
1359
|
+
--min-importance <n> For byImportance
|
|
1360
|
+
--max-count <n> For byCount: keep the top n
|
|
1361
|
+
--dry-run Show what would be removed
|
|
1362
|
+
${MEMORY_ENV}`,
|
|
1363
|
+
purge: `memstack purge (--actor <id> | --project)
|
|
1364
|
+
|
|
1365
|
+
Delete every memory for the actor.
|
|
1366
|
+
|
|
1367
|
+
${ACTOR}
|
|
1368
|
+
${MEMORY_ENV}`,
|
|
1369
|
+
merge: `memstack merge --ids <id,id,...>
|
|
1370
|
+
|
|
1371
|
+
Merge several memories into one.
|
|
1372
|
+
${MEMORY_ENV}`,
|
|
1373
|
+
stats: `memstack stats [--actor <id> | --project]
|
|
1374
|
+
|
|
1375
|
+
Memory counts and diagnostics; all actors when neither flag is given.
|
|
1376
|
+
${MEMORY_ENV}`,
|
|
1377
|
+
delete: `memstack delete --id <id>
|
|
1378
|
+
${MEMORY_ENV}`,
|
|
1379
|
+
health: `memstack health
|
|
1380
|
+
|
|
1381
|
+
Check storage, LLM, and embedding connectivity.
|
|
1382
|
+
${MEMORY_ENV}`,
|
|
1383
|
+
export: `memstack export [--actor <id> | --project] [--out <file>]
|
|
1384
|
+
|
|
1385
|
+
Export memories as a JSON snapshot; all actors when neither flag is given.
|
|
1386
|
+
${MEMORY_ENV}`,
|
|
1387
|
+
import: `memstack import (--actor <id> | --project) --file <file>
|
|
1388
|
+
|
|
1389
|
+
Import memories from a JSON snapshot.
|
|
1390
|
+
|
|
1391
|
+
${ACTOR}
|
|
1392
|
+
--file <file> Snapshot path (required)
|
|
1393
|
+
${MEMORY_ENV}`
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1230
1396
|
// src/cli.ts
|
|
1231
1397
|
process.stdout.on("error", (error) => {
|
|
1232
1398
|
if (error.code === "EPIPE") process.exit(0);
|
|
@@ -1268,24 +1434,43 @@ async function main() {
|
|
|
1268
1434
|
global: { type: "boolean" },
|
|
1269
1435
|
delete: { type: "string" },
|
|
1270
1436
|
"no-agents-md": { type: "boolean" },
|
|
1271
|
-
"no-hooks": { type: "boolean" }
|
|
1437
|
+
"no-hooks": { type: "boolean" },
|
|
1438
|
+
project: { type: "boolean" },
|
|
1439
|
+
help: { type: "boolean", short: "h" },
|
|
1440
|
+
version: { type: "boolean", short: "v" }
|
|
1272
1441
|
}
|
|
1273
1442
|
});
|
|
1443
|
+
if (values.version) {
|
|
1444
|
+
process.stdout.write(`${version()}
|
|
1445
|
+
`);
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1274
1448
|
const command = positionals[0];
|
|
1449
|
+
if (command === "help" || values.help) {
|
|
1450
|
+
const topic = command === "help" ? positionals[1] : command;
|
|
1451
|
+
if (topic && !COMMAND_HELP[topic]) fail(`Unknown command: ${topic}. Run memstack --help for the list.`);
|
|
1452
|
+
process.stdout.write(topic ? COMMAND_HELP[topic] : USAGE);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1275
1455
|
if (!command) {
|
|
1276
|
-
|
|
1456
|
+
process.stderr.write(USAGE);
|
|
1277
1457
|
process.exit(1);
|
|
1278
1458
|
}
|
|
1279
1459
|
if (HARNESS_COMMANDS.includes(command)) {
|
|
1280
1460
|
process.exitCode = await runHarnessCommand(command, positionals.slice(1), values);
|
|
1281
1461
|
return;
|
|
1282
1462
|
}
|
|
1463
|
+
if (!COMMAND_HELP[command]) fail(`Unknown command: ${command}. Run memstack --help for the list.`);
|
|
1464
|
+
if (values.project) {
|
|
1465
|
+
if (values.actor) fail("Use either --actor or --project, not both");
|
|
1466
|
+
values.actor = projectNamespace(resolveProject().id);
|
|
1467
|
+
}
|
|
1283
1468
|
const config = await loadConfig2();
|
|
1284
1469
|
const ms = new MemStack(config);
|
|
1285
1470
|
let result;
|
|
1286
1471
|
switch (command) {
|
|
1287
1472
|
case "store": {
|
|
1288
|
-
if (!values.actor) fail("--actor is required");
|
|
1473
|
+
if (!values.actor) fail("--actor (or --project) is required");
|
|
1289
1474
|
if (!values.content) fail("--content is required");
|
|
1290
1475
|
const importance = values.importance ? Math.max(0, Math.min(1, Number(values.importance))) : void 0;
|
|
1291
1476
|
result = await ms.memory.store({
|
|
@@ -1298,7 +1483,7 @@ async function main() {
|
|
|
1298
1483
|
break;
|
|
1299
1484
|
}
|
|
1300
1485
|
case "retrieve": {
|
|
1301
|
-
if (!values.actor) fail("--actor is required");
|
|
1486
|
+
if (!values.actor) fail("--actor (or --project) is required");
|
|
1302
1487
|
result = await ms.memory.retrieve({
|
|
1303
1488
|
actorId: values.actor,
|
|
1304
1489
|
query: values.query,
|
|
@@ -1311,7 +1496,7 @@ async function main() {
|
|
|
1311
1496
|
break;
|
|
1312
1497
|
}
|
|
1313
1498
|
case "context": {
|
|
1314
|
-
if (!values.actor) fail("--actor is required");
|
|
1499
|
+
if (!values.actor) fail("--actor (or --project) is required");
|
|
1315
1500
|
result = await ms.memory.compileContext({
|
|
1316
1501
|
actorId: String(values.actor),
|
|
1317
1502
|
maxTokens: values["max-tokens"] ? Number(values["max-tokens"]) : void 0
|
|
@@ -1319,7 +1504,7 @@ async function main() {
|
|
|
1319
1504
|
break;
|
|
1320
1505
|
}
|
|
1321
1506
|
case "summarize": {
|
|
1322
|
-
if (!values.actor) fail("--actor is required");
|
|
1507
|
+
if (!values.actor) fail("--actor (or --project) is required");
|
|
1323
1508
|
const durationMs = values["older-than"] ? parseDuration(String(values["older-than"])) : void 0;
|
|
1324
1509
|
const olderThan = durationMs ? new Date(Date.now() - durationMs) : void 0;
|
|
1325
1510
|
result = await ms.memory.summarize({
|
|
@@ -1329,7 +1514,7 @@ async function main() {
|
|
|
1329
1514
|
break;
|
|
1330
1515
|
}
|
|
1331
1516
|
case "prune": {
|
|
1332
|
-
if (!values.actor) fail("--actor is required");
|
|
1517
|
+
if (!values.actor) fail("--actor (or --project) is required");
|
|
1333
1518
|
const VALID_TYPES = ["byAge", "byImportance", "byCount", "byType", "custom", "compose"];
|
|
1334
1519
|
const pruneType = values.type ?? "byAge";
|
|
1335
1520
|
if (!VALID_TYPES.includes(String(pruneType))) {
|
|
@@ -1350,7 +1535,7 @@ async function main() {
|
|
|
1350
1535
|
break;
|
|
1351
1536
|
}
|
|
1352
1537
|
case "purge": {
|
|
1353
|
-
if (!values.actor) fail("--actor is required");
|
|
1538
|
+
if (!values.actor) fail("--actor (or --project) is required");
|
|
1354
1539
|
result = await ms.memory.purgeActor(String(values.actor));
|
|
1355
1540
|
break;
|
|
1356
1541
|
}
|
|
@@ -1385,7 +1570,7 @@ async function main() {
|
|
|
1385
1570
|
break;
|
|
1386
1571
|
}
|
|
1387
1572
|
case "import": {
|
|
1388
|
-
if (!values.actor) fail("--actor is required");
|
|
1573
|
+
if (!values.actor) fail("--actor (or --project) is required");
|
|
1389
1574
|
if (!values.file) fail("--file is required");
|
|
1390
1575
|
const raw = await readFile(String(values.file), "utf-8");
|
|
1391
1576
|
let snapshot2 = JSON.parse(raw);
|
|
@@ -1400,8 +1585,7 @@ async function main() {
|
|
|
1400
1585
|
break;
|
|
1401
1586
|
}
|
|
1402
1587
|
default:
|
|
1403
|
-
|
|
1404
|
-
process.exit(1);
|
|
1588
|
+
fail(`Unknown command: ${command}. Run memstack --help for the list.`);
|
|
1405
1589
|
}
|
|
1406
1590
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1407
1591
|
await ms.close();
|
|
@@ -1427,8 +1611,10 @@ function parseDuration(input) {
|
|
|
1427
1611
|
return value;
|
|
1428
1612
|
}
|
|
1429
1613
|
}
|
|
1430
|
-
function
|
|
1431
|
-
|
|
1614
|
+
function version() {
|
|
1615
|
+
return createRequire(import.meta.url)("../package.json").version;
|
|
1616
|
+
}
|
|
1617
|
+
var USAGE = `memstack <command> [flags]
|
|
1432
1618
|
|
|
1433
1619
|
Agent harnesses (Claude Code, Codex):
|
|
1434
1620
|
init Choose an LLM provider and store; writes ~/.memstack/config.json
|
|
@@ -1455,9 +1641,8 @@ Commands:
|
|
|
1455
1641
|
export Export all memories
|
|
1456
1642
|
import Import memories from a JSON snapshot
|
|
1457
1643
|
|
|
1458
|
-
Run memstack <command> --help for flags.
|
|
1459
|
-
|
|
1460
|
-
}
|
|
1644
|
+
Run memstack <command> --help for flags, or memstack --version.
|
|
1645
|
+
`;
|
|
1461
1646
|
function fail(message) {
|
|
1462
1647
|
process.stderr.write(`Error: ${message}
|
|
1463
1648
|
`);
|