myagentmemory 0.4.11 → 0.4.13
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 +83 -38
- package/dist/cli.js +60 -67
- package/dist/core.d.ts +21 -1
- package/dist/core.js +275 -50
- package/package.json +29 -11
- package/scripts/install-skills.sh +4 -1
- package/scripts/postinstall.cjs +23 -4
- package/src/cli.ts +64 -78
- package/src/core.ts +290 -50
- package/dist/agent-memory +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
# agent-memory
|
|
2
2
|
|
|
3
|
-
Persistent memory for coding agents
|
|
3
|
+
**Persistent memory for AI coding agents.** Give [Claude Code](https://claude.ai/code), [OpenAI Codex](https://github.com/openai/codex), [Cursor](https://cursor.com), and Agent (Cursor CLI) a memory that survives across sessions — long-term facts, daily logs, topic notes, and a scratchpad checklist, stored as plain markdown and searchable with [qmd](https://github.com/tobi/qmd)-powered semantic search.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/myagentmemory)
|
|
6
|
+
[](https://www.npmjs.com/package/myagentmemory)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://jayzeng.github.io/agentmemory/)
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
[Website and quickstart](https://jayzeng.github.io/agentmemory/) · [Install](#installation) · [CLI commands](#cli-commands) · [How it works](#how-it-works)
|
|
11
|
+
|
|
12
|
+
## Why agent-memory?
|
|
13
|
+
|
|
14
|
+
Coding agents forget everything between sessions. `agent-memory` gives them a durable, local-first memory so they stop re-learning your stack, your preferences, and past decisions on every run.
|
|
15
|
+
|
|
16
|
+
- **Persistent project memory** — decisions, preferences, and project context carry across sessions instead of starting cold.
|
|
17
|
+
- **Plain Markdown, local-first** — every memory is a readable, git-friendly file on disk. No database, cloud service, or lock-in.
|
|
18
|
+
- **Optional semantic search** — [qmd](https://github.com/tobi/qmd) adds keyword, semantic, and hybrid search across memory files.
|
|
19
|
+
- **Explicit retrieval** — skills load base context at session start and search for related memories when a task needs them.
|
|
20
|
+
- **Shared across agents** — Claude Code, Codex, Cursor, and Agent can use the same store.
|
|
21
|
+
|
|
22
|
+
> **Naming:** `agentmemory` is the GitHub repo (and Homebrew tap), `myagentmemory` is the npm package, and `agent-memory` is the installed CLI binary. Also known as *coding agent memory* or *AI coding memory*.
|
|
23
|
+
|
|
24
|
+
### Product boundary
|
|
25
|
+
|
|
26
|
+
AgentMemory is a local Markdown store with a CLI, optional qmd search, and agent skills. It is not a Python SDK, vector database, or knowledge graph. The Markdown files remain the source of truth.
|
|
8
27
|
|
|
9
28
|
## Installation
|
|
10
29
|
|
|
@@ -13,7 +32,7 @@ Long-term facts, daily logs, topic/event notes, and a scratchpad checklist store
|
|
|
13
32
|
brew tap jayzeng/agentmemory https://github.com/jayzeng/agentmemory
|
|
14
33
|
brew install jayzeng/agentmemory/agent-memory
|
|
15
34
|
|
|
16
|
-
# Install the CLI globally
|
|
35
|
+
# Install the portable CLI globally (Node.js 20+; macOS, Linux, or Windows)
|
|
17
36
|
npm install -g myagentmemory
|
|
18
37
|
|
|
19
38
|
# If you hit SSL errors due to corporate MITM/inspection, try:
|
|
@@ -33,11 +52,9 @@ agent-memory install-skills
|
|
|
33
52
|
agent-memory uninstall-skills
|
|
34
53
|
```
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
If you're on Pi and prefer a native extension, use `pi-memory` (https://github.com/jayzeng/pi-memory) instead of installing this skill. The CLI + skill workflow here is the cross-platform alternative, and works fine on Pi without any extension.
|
|
55
|
+
The npm package installs a platform-neutral Node.js executable. The optional Homebrew and `build:cli` paths use a native binary built for the current platform.
|
|
39
56
|
|
|
40
|
-
|
|
57
|
+
`install-skills` writes a SKILL.md into each agent's config directory:
|
|
41
58
|
- `~/.claude/skills/agent-memory/SKILL.md` — Claude Code skill
|
|
42
59
|
- `~/.codex/skills/agent-memory/SKILL.md` — Codex skill
|
|
43
60
|
- `~/.cursor/skills/agent-memory/SKILL.md` — Cursor skill
|
|
@@ -47,6 +64,10 @@ This installs:
|
|
|
47
64
|
- `%USERPROFILE%\.cursor\skills\agent-memory\SKILL.md` — Cursor skill (Windows)
|
|
48
65
|
- `%USERPROFILE%\.agents\skills\agent-memory\SKILL.md` — Agent CLI skill (Windows)
|
|
49
66
|
|
|
67
|
+
### Pi users
|
|
68
|
+
|
|
69
|
+
If you're on Pi and prefer a native extension, use `pi-memory` (https://github.com/jayzeng/pi-memory) instead of installing this skill. The CLI + skill workflow here is the cross-platform alternative, and works fine on Pi without any extension.
|
|
70
|
+
|
|
50
71
|
### Optional: Enable search with qmd
|
|
51
72
|
|
|
52
73
|
When qmd is installed, the collection is automatically set up via `agent-memory init`.
|
|
@@ -65,22 +86,21 @@ Without qmd, all core tools (write/read/scratchpad) work normally. Only `memory_
|
|
|
65
86
|
## Architecture
|
|
66
87
|
|
|
67
88
|
```
|
|
68
|
-
|
|
69
|
-
│ src/core.ts
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
│
|
|
77
|
-
│
|
|
78
|
-
│
|
|
79
|
-
│
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
`agent-memory` that invoke CLI
|
|
89
|
+
┌───────────────┐
|
|
90
|
+
│ src/core.ts │ ← all logic: paths, truncation, scratchpad,
|
|
91
|
+
└───────┬───────┘ context builder, qmd, tool functions
|
|
92
|
+
│
|
|
93
|
+
┌────┴─────┐
|
|
94
|
+
▼ ▼
|
|
95
|
+
┌─────────┐ ┌─────────────────────────┐
|
|
96
|
+
│ src/ │ │ skills/ │
|
|
97
|
+
│ cli.ts │ │ ├─ claude-code/SKILL.md │
|
|
98
|
+
│ │ │ ├─ codex/SKILL.md │
|
|
99
|
+
│ │ │ ├─ cursor/SKILL.md │
|
|
100
|
+
│ │ │ └─ agent/SKILL.md │
|
|
101
|
+
└─────────┘ └─────────────────────────┘
|
|
102
|
+
CLI command instruction files
|
|
103
|
+
`agent-memory` that invoke the CLI
|
|
84
104
|
```
|
|
85
105
|
|
|
86
106
|
The memory directory defaults to `~/.agent-memory/`. Override with `AGENT_MEMORY_DIR` env var or `--dir` flag.
|
|
@@ -89,8 +109,8 @@ The memory directory defaults to `~/.agent-memory/`. Override with `AGENT_MEMORY
|
|
|
89
109
|
|
|
90
110
|
| Command | Purpose |
|
|
91
111
|
|---------|---------|
|
|
92
|
-
| `agent-memory context [--no-search]` | Build
|
|
93
|
-
| `agent-memory write --target <long_term\|daily\|topic> --content <text> [--mode append\|overwrite] [--topic <name>] [--date YYYY-MM-DD]` | Write to memory files |
|
|
112
|
+
| `agent-memory context [--query <text>] [--no-search]` | Build context and optionally include qmd matches for a query |
|
|
113
|
+
| `agent-memory write --target <long_term\|daily\|topic> --content <text> [--mode append\|overwrite] [--source-uri <uri>] [--topic <name>] [--date YYYY-MM-DD]` | Write to memory files with optional provenance |
|
|
94
114
|
| `agent-memory read --target <long_term\|scratchpad\|daily\|list\|topic\|topics> [--date YYYY-MM-DD] [--topic <name>]` | Read memory files |
|
|
95
115
|
| `agent-memory scratchpad <add\|done\|undo\|clear_done\|list> [--text <text>]` | Manage checklist |
|
|
96
116
|
| `agent-memory search --query <text> [--mode keyword\|semantic\|deep] [--limit N]` | Search via qmd |
|
|
@@ -115,14 +135,14 @@ If the first search doesn't find what you need, try rephrasing or switching mode
|
|
|
115
135
|
|
|
116
136
|
```
|
|
117
137
|
~/.agent-memory/
|
|
118
|
-
MEMORY.md
|
|
119
|
-
SCRATCHPAD.md
|
|
138
|
+
MEMORY.md # Curated long-term memory
|
|
139
|
+
SCRATCHPAD.md # Checklist of things to fix/remember
|
|
120
140
|
daily/
|
|
121
|
-
2026-02-15.md
|
|
141
|
+
2026-02-15.md # Daily append-only log
|
|
122
142
|
2026-02-14.md
|
|
123
143
|
...
|
|
124
144
|
topics/
|
|
125
|
-
auth.md
|
|
145
|
+
auth.md # Topic/event log linked back to daily entries
|
|
126
146
|
```
|
|
127
147
|
|
|
128
148
|
## Topic notes
|
|
@@ -139,25 +159,31 @@ agent-memory read --target topics
|
|
|
139
159
|
|
|
140
160
|
### Context injection
|
|
141
161
|
|
|
142
|
-
|
|
162
|
+
The context builder emits the following sections in priority order. Installed skills load base context at session start; callers can optionally supply `--query` to add relevant qmd results:
|
|
143
163
|
|
|
144
164
|
1. **Open scratchpad items** (up to 2K chars)
|
|
145
165
|
2. **Recent topic entries** (up to 2K chars) — most recent topic notes with backlinks
|
|
146
|
-
3. **Today's daily log** (up to 3K chars, tail)
|
|
166
|
+
3. **Today's daily log** (up to 3K chars, head + tail)
|
|
147
167
|
4. **Relevant memories via qmd search** (up to 2.5K chars) — searches using the user's current prompt to surface related past context
|
|
148
168
|
5. **MEMORY.md** (up to 4K chars, middle-truncated)
|
|
149
169
|
6. **Yesterday's daily log** (up to 3K chars, tail — lowest priority, trimmed first)
|
|
150
170
|
|
|
151
|
-
Total
|
|
171
|
+
Total output, including headings and truncation notices, is hard-capped at 16,000 characters. Explicitly untrusted, expired, superseded, revoked, or retired blocks are excluded; legacy secret-like values are redacted before injection. When qmd is unavailable, the relevant-memory step is skipped and the rest still works.
|
|
152
172
|
|
|
153
|
-
|
|
173
|
+
Claude Code loads base context through the skill's shell injection. Codex, Cursor, and Agent run the same base command at session start. The bundled skills use explicit search when a task relates to prior work; they make no host-level guarantee of automatic retrieval.
|
|
154
174
|
|
|
155
175
|
### Selective injection
|
|
156
176
|
|
|
157
|
-
When qmd is available, the
|
|
177
|
+
When qmd is available and `context --query` is supplied, the CLI sanitizes the query, limits it to 200 characters, and includes the top three keyword results with the standard context. Programmatic integrations should spawn the CLI with an argument array so query text is not evaluated by a shell.
|
|
158
178
|
|
|
159
179
|
The search has a 3-second timeout and fails silently. If qmd is down or the query returns nothing, injection falls back to the standard behavior.
|
|
160
180
|
|
|
181
|
+
### Provenance, temporal state, and secret screening
|
|
182
|
+
|
|
183
|
+
`write --source-uri <uri>` stores an addressable `Source:` line with the entry. Plain-Markdown compatibility is retained: complete write entries containing standalone header metadata lines such as `Trust: untrusted`, `Status: expired`, `Status: superseded`, `Status: revoked`, or `Status: retired` are kept on disk but omitted from direct, distilled, and auto-retrieved agent context. A standalone past `Valid until: YYYY-MM-DD` line is also honored. These phrases inside ordinary prose are not treated as metadata.
|
|
184
|
+
|
|
185
|
+
Writes screen a bounded set of high-confidence credential shapes and replace matching values with `[REDACTED_SECRET]` before persistence. Context rendering applies the same screening to legacy files. This is defense in depth, not a secrets vault; avoid passing real credentials in command arguments or memory content.
|
|
186
|
+
|
|
161
187
|
### Tags and links
|
|
162
188
|
|
|
163
189
|
Use `#tags` and `[[wiki-links]]` in memory content to improve searchability:
|
|
@@ -192,6 +218,14 @@ These are content conventions, not enforced metadata. qmd's full-text indexing m
|
|
|
192
218
|
# Unit tests (no LLM, no qmd — fast, deterministic)
|
|
193
219
|
bun test test/unit.test.ts
|
|
194
220
|
bun test test/cli.test.ts
|
|
221
|
+
|
|
222
|
+
# External-feedback dataset and deterministic capability probes
|
|
223
|
+
bun run build:eval
|
|
224
|
+
bun run test:eval
|
|
225
|
+
bun run eval:feedback
|
|
226
|
+
|
|
227
|
+
# Optional: add isolated live qmd multilingual retrieval probes
|
|
228
|
+
bun run eval:feedback --live-qmd
|
|
195
229
|
```
|
|
196
230
|
|
|
197
231
|
### Test levels
|
|
@@ -200,6 +234,7 @@ bun test test/cli.test.ts
|
|
|
200
234
|
|-------|------|-------------|---------------|
|
|
201
235
|
| Unit | `test/unit.test.ts` | None | Utilities, scratchpad parsing, context builder, qmd helpers, tool functions |
|
|
202
236
|
| CLI | `test/cli.test.ts` | None | CLI commands, subprocess integration |
|
|
237
|
+
| Feedback eval | `test/eval.test.ts`, `eval/` | qmd optional | External feedback, capability gaps, multilingual retrieval, and qualitative boundaries |
|
|
203
238
|
|
|
204
239
|
## Development
|
|
205
240
|
|
|
@@ -218,7 +253,7 @@ agent-memory install-skills
|
|
|
218
253
|
|
|
219
254
|
```bash
|
|
220
255
|
# Confirm package name is available
|
|
221
|
-
npm view
|
|
256
|
+
npm view myagentmemory
|
|
222
257
|
|
|
223
258
|
# Bump version (choose patch/minor/major)
|
|
224
259
|
npm version patch
|
|
@@ -227,13 +262,23 @@ npm version patch
|
|
|
227
262
|
npm publish --access public
|
|
228
263
|
```
|
|
229
264
|
|
|
265
|
+
### Repository assets (maintainers)
|
|
266
|
+
|
|
267
|
+
- **Social preview image:** `.github/assets/social-preview.png` (1280×640)
|
|
268
|
+
- **Release notes template:** `.github/release.yml` (used by GitHub auto-generated release notes)
|
|
269
|
+
- **Landing page source:** `docs/index.html` (deployed by `.github/workflows/deploy-pages.yml`)
|
|
270
|
+
|
|
271
|
+
## Acknowledgments
|
|
272
|
+
|
|
273
|
+
Inspired by [skyfallsin/pi-mem](https://github.com/skyfallsin/pi-mem). Semantic search is powered by [qmd](https://github.com/tobi/qmd).
|
|
274
|
+
|
|
230
275
|
## Changelog
|
|
231
276
|
|
|
232
|
-
### 0.
|
|
277
|
+
### 0.4.12
|
|
233
278
|
|
|
234
279
|
- **Removed pi extension**: Removed `index.ts` and all pi-specific code (`@mariozechner/pi-ai`, `@mariozechner/pi-coding-agent`, `@sinclair/typebox` peer dependencies).
|
|
235
280
|
- **Standalone tool functions**: Extracted `memoryWrite()`, `memoryRead()`, `scratchpadAction()`, `memorySearch()` into `src/core.ts` as standalone functions usable without any framework.
|
|
236
|
-
- **Renamed package**: `pi-memory` → `agent-memory`.
|
|
281
|
+
- **Renamed package**: `pi-memory` → `myagentmemory` (npm); the CLI binary is `agent-memory`.
|
|
237
282
|
- **Renamed env var**: `PI_MEMORY_QMD_UPDATE` → `AGENT_MEMORY_QMD_UPDATE` (old name still works as fallback).
|
|
238
283
|
- **Default memory directory**: Now always `~/.agent-memory/`.
|
|
239
284
|
- **Removed pi-specific tests**: Deleted `test/e2e.ts`, `test/eval-recall.ts`, `test/unit.ts`.
|
package/dist/cli.js
CHANGED
|
@@ -18,8 +18,17 @@
|
|
|
18
18
|
* --json Machine-readable JSON output
|
|
19
19
|
*/
|
|
20
20
|
import * as fs from "node:fs";
|
|
21
|
-
import { _setBaseDir, buildMemoryContext, checkCollection, dailyPath, detectQmd, distilMemories, ensureDirs, ensureQmdAvailableForSync, ensureQmdAvailableForUpdate, getCollectionName, getDailyDir, getMemoryDir, getMemoryFile, getQmdEmbedMode, getQmdHealth, getQmdResultPath, getQmdResultText, getScratchpadFile, getTopicsDir, installSkills, nowTimestamp, parseScratchpad, readFileSafe, runQmdEmbedDetached, runQmdSearch, runQmdSync, runQmdUpdateNow, scheduleQmdUpdate, searchRelevantMemories, serializeScratchpad, setupQmdCollection, slugifyTopic, todayStr, topicPath, uninstallSkills, } from "./core.js";
|
|
22
|
-
|
|
21
|
+
import { _setBaseDir, buildMemoryContext, checkCollection, dailyPath, detectQmd, distilMemories, ensureDirs, ensureQmdAvailableForSync, ensureQmdAvailableForUpdate, getCollectionName, getDailyDir, getMemoryDir, getMemoryFile, getQmdEmbedMode, getQmdHealth, getQmdResultPath, getQmdResultText, getScratchpadFile, getTopicsDir, installSkills, memoryWrite, nowTimestamp, parseScratchpad, probeEmbeddings, readFileSafe, redactSecrets, runQmdEmbedDetached, runQmdSearch, runQmdSync, runQmdUpdateNow, scheduleQmdUpdate, searchRelevantMemories, serializeScratchpad, setupQmdCollection, slugifyTopic, todayStr, topicPath, uninstallSkills, } from "./core.js";
|
|
22
|
+
function readPackageVersion() {
|
|
23
|
+
try {
|
|
24
|
+
const packageJson = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
25
|
+
return typeof packageJson.version === "string" ? packageJson.version : "dev";
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return "dev";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : readPackageVersion();
|
|
23
32
|
function parseArgs(argv) {
|
|
24
33
|
const flags = {};
|
|
25
34
|
const positional = [];
|
|
@@ -83,8 +92,11 @@ function exitError(message, json) {
|
|
|
83
92
|
async function cmdContext(flags) {
|
|
84
93
|
const json = hasFlag(flags, "json");
|
|
85
94
|
const noSearch = hasFlag(flags, "no-search");
|
|
95
|
+
const query = getFlag(flags, "query") ?? "";
|
|
86
96
|
ensureDirs();
|
|
87
|
-
|
|
97
|
+
if (!noSearch && query)
|
|
98
|
+
await ensureQmdAvailableForSync();
|
|
99
|
+
const searchResults = noSearch ? "" : await searchRelevantMemories(query);
|
|
88
100
|
const context = buildMemoryContext(searchResults);
|
|
89
101
|
if (json) {
|
|
90
102
|
output({ context, directory: getMemoryDir() }, true);
|
|
@@ -102,68 +114,28 @@ async function cmdWrite(flags) {
|
|
|
102
114
|
const mode = getFlag(flags, "mode") ?? "append";
|
|
103
115
|
const topic = getFlag(flags, "topic");
|
|
104
116
|
const date = getFlag(flags, "date");
|
|
117
|
+
const sourceUri = getFlag(flags, "source-uri");
|
|
105
118
|
if (!["long_term", "daily", "topic"].includes(target)) {
|
|
106
119
|
exitError("--target must be 'long_term', 'daily', or 'topic' (default: daily)", json);
|
|
107
120
|
}
|
|
121
|
+
if (!["append", "overwrite"].includes(mode)) {
|
|
122
|
+
exitError("--mode must be 'append' or 'overwrite'", json);
|
|
123
|
+
}
|
|
108
124
|
if (!content) {
|
|
109
125
|
exitError("--content is required", json);
|
|
110
126
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
? { ok: true, path: filePath, target, mode: "append", timestamp: ts }
|
|
124
|
-
: `Appended to daily log: ${filePath}`, json);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (target === "topic") {
|
|
128
|
-
if (!topic) {
|
|
129
|
-
exitError("--topic is required when --target is 'topic'", json);
|
|
130
|
-
}
|
|
131
|
-
const slug = slugifyTopic(topic);
|
|
132
|
-
if (!slug) {
|
|
133
|
-
exitError("--topic must include at least one letter or number", json);
|
|
134
|
-
}
|
|
135
|
-
const filePath = topicPath(slug);
|
|
136
|
-
const existing = readFileSafe(filePath) ?? "";
|
|
137
|
-
const linkDate = date?.trim() || todayStr();
|
|
138
|
-
const header = `# Topic: ${topic}\n\n<!-- created: ${ts} [${sid}] -->\n`;
|
|
139
|
-
const separator = existing.trim() ? "\n\n" : "";
|
|
140
|
-
const base = existing.trim() ? existing : header.trimEnd();
|
|
141
|
-
const stamped = `<!-- ${ts} [${sid}] -->\n${content.trim()}\nDaily: [[${linkDate}]]`;
|
|
142
|
-
fs.writeFileSync(filePath, `${base}${separator}${stamped}`, "utf-8");
|
|
143
|
-
await ensureQmdAvailableForUpdate();
|
|
144
|
-
scheduleQmdUpdate();
|
|
145
|
-
output(json
|
|
146
|
-
? { ok: true, path: filePath, target, mode: "append", timestamp: ts, topic, slug, date: linkDate }
|
|
147
|
-
: `Appended to topic: ${filePath}`, json);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
// long_term
|
|
151
|
-
const memFile = getMemoryFile();
|
|
152
|
-
const existing = readFileSafe(memFile) ?? "";
|
|
153
|
-
if (mode === "overwrite") {
|
|
154
|
-
const stamped = `<!-- last updated: ${ts} [${sid}] -->\n${content}`;
|
|
155
|
-
fs.writeFileSync(memFile, stamped, "utf-8");
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
const separator = existing.trim() ? "\n\n" : "";
|
|
159
|
-
const stamped = `<!-- ${ts} [${sid}] -->\n${content}`;
|
|
160
|
-
fs.writeFileSync(memFile, existing + separator + stamped, "utf-8");
|
|
161
|
-
}
|
|
162
|
-
await ensureQmdAvailableForUpdate();
|
|
163
|
-
scheduleQmdUpdate();
|
|
164
|
-
output(json
|
|
165
|
-
? { ok: true, path: memFile, target, mode, timestamp: ts }
|
|
166
|
-
: `${mode === "overwrite" ? "Overwrote" : "Appended to"} MEMORY.md`, json);
|
|
127
|
+
const result = await memoryWrite({
|
|
128
|
+
target: target,
|
|
129
|
+
content,
|
|
130
|
+
mode: mode,
|
|
131
|
+
sessionId: "cli",
|
|
132
|
+
topic,
|
|
133
|
+
date,
|
|
134
|
+
sourceUri,
|
|
135
|
+
});
|
|
136
|
+
if (result.isError)
|
|
137
|
+
exitError(result.text.replace(/^Error:\s*/, ""), json);
|
|
138
|
+
output(json ? { ok: true, ...result.details } : result.text.split("\n\n", 1)[0], json);
|
|
167
139
|
}
|
|
168
140
|
async function cmdRead(flags) {
|
|
169
141
|
const json = hasFlag(flags, "json");
|
|
@@ -270,7 +242,11 @@ async function cmdScratchpad(flags, positional) {
|
|
|
270
242
|
ensureDirs();
|
|
271
243
|
const spFile = getScratchpadFile();
|
|
272
244
|
const existing = readFileSafe(spFile) ?? "";
|
|
273
|
-
let items = parseScratchpad(existing)
|
|
245
|
+
let items = parseScratchpad(existing).map((item) => ({
|
|
246
|
+
...item,
|
|
247
|
+
text: redactSecrets(item.text).content,
|
|
248
|
+
meta: redactSecrets(item.meta).content,
|
|
249
|
+
}));
|
|
274
250
|
if (action === "list") {
|
|
275
251
|
if (items.length === 0) {
|
|
276
252
|
output(json ? { items: [], count: 0, open: 0 } : "Scratchpad is empty.", json);
|
|
@@ -292,11 +268,12 @@ async function cmdScratchpad(flags, positional) {
|
|
|
292
268
|
if (!text)
|
|
293
269
|
exitError("--text is required for add", json);
|
|
294
270
|
const ts = nowTimestamp();
|
|
295
|
-
|
|
271
|
+
const safeText = redactSecrets(text).content;
|
|
272
|
+
items.push({ done: false, text: safeText, meta: `<!-- ${ts} [cli] -->` });
|
|
296
273
|
fs.writeFileSync(spFile, serializeScratchpad(items), "utf-8");
|
|
297
274
|
await ensureQmdAvailableForUpdate();
|
|
298
275
|
scheduleQmdUpdate();
|
|
299
|
-
output(json ? { ok: true, action, text } : `Added: - [ ] ${
|
|
276
|
+
output(json ? { ok: true, action, text: safeText } : `Added: - [ ] ${safeText}`, json);
|
|
300
277
|
return;
|
|
301
278
|
}
|
|
302
279
|
if (action === "done" || action === "undo") {
|
|
@@ -561,11 +538,18 @@ async function cmdStatus(flags) {
|
|
|
561
538
|
const qmdFound = await detectQmd();
|
|
562
539
|
let hasCollection = false;
|
|
563
540
|
let health = null;
|
|
541
|
+
let embeddings = "n/a";
|
|
564
542
|
if (qmdFound) {
|
|
565
543
|
hasCollection = await checkCollection();
|
|
566
544
|
if (hasCollection) {
|
|
567
545
|
await ensureQmdAvailableForSync();
|
|
568
546
|
health = await getQmdHealth();
|
|
547
|
+
// A live semantic probe confirms embeddings are actually usable, but
|
|
548
|
+
// it costs a real qmd query (and a possible model load), so it's
|
|
549
|
+
// opt-in — the cheap pending-embed count below covers the common case.
|
|
550
|
+
if (hasFlag(flags, "probe")) {
|
|
551
|
+
embeddings = await probeEmbeddings();
|
|
552
|
+
}
|
|
569
553
|
}
|
|
570
554
|
}
|
|
571
555
|
const embedMode = getQmdEmbedMode();
|
|
@@ -588,6 +572,7 @@ async function cmdStatus(flags) {
|
|
|
588
572
|
available: qmdFound,
|
|
589
573
|
collection: hasCollection ? getCollectionName() : null,
|
|
590
574
|
health,
|
|
575
|
+
embeddings,
|
|
591
576
|
},
|
|
592
577
|
embedMode,
|
|
593
578
|
}, true);
|
|
@@ -617,6 +602,14 @@ async function cmdStatus(flags) {
|
|
|
617
602
|
console.log(`qmd: available`);
|
|
618
603
|
console.log(`Collection '${getCollectionName()}': ${hasCollection ? "configured" : "not configured — run: agent-memory init"}`);
|
|
619
604
|
console.log(`Embed mode: ${embedMode}`);
|
|
605
|
+
if (hasCollection && embeddings !== "n/a") {
|
|
606
|
+
const embLabel = embeddings === "ready"
|
|
607
|
+
? "ready"
|
|
608
|
+
: embeddings === "missing"
|
|
609
|
+
? "missing — run: agent-memory sync"
|
|
610
|
+
: "unknown (could not verify within probe timeout)";
|
|
611
|
+
console.log(`Embeddings (semantic/deep search): ${embLabel}`);
|
|
612
|
+
}
|
|
620
613
|
if (health) {
|
|
621
614
|
if (health.totalFiles !== null)
|
|
622
615
|
console.log(`Files indexed: ${health.totalFiles}`);
|
|
@@ -671,15 +664,15 @@ Commands:
|
|
|
671
664
|
version Show binary version
|
|
672
665
|
install-skills Install (or --uninstall) bundled skills
|
|
673
666
|
uninstall-skills Uninstall bundled skills
|
|
674
|
-
context Build
|
|
675
|
-
write Write to memory files (default: daily)
|
|
667
|
+
context Build context; optionally retrieve memories with --query
|
|
668
|
+
write Write to memory files (default: daily; optional --source-uri)
|
|
676
669
|
read Read memory files
|
|
677
670
|
scratchpad Manage checklist items
|
|
678
671
|
search Search across memory files (requires qmd)
|
|
679
672
|
distil Generate compact MEMORY.md index from daily logs + topics
|
|
680
673
|
sync Re-index and embed all files (requires qmd)
|
|
681
674
|
init Initialize memory directory and qmd collection
|
|
682
|
-
status Show configuration and status
|
|
675
|
+
status Show configuration and status (--probe for a live embeddings check)
|
|
683
676
|
|
|
684
677
|
Global flags:
|
|
685
678
|
--dir <path> Override memory directory
|
|
@@ -688,7 +681,7 @@ Global flags:
|
|
|
688
681
|
Examples:
|
|
689
682
|
agent-memory init
|
|
690
683
|
agent-memory write --content "Fixed auth bug in login flow"
|
|
691
|
-
agent-memory write --target long_term --content "User prefers dark mode"
|
|
684
|
+
agent-memory write --target long_term --content "User prefers dark mode" --source-uri "session://agent/turn/12"
|
|
692
685
|
agent-memory write --target topic --topic "auth" --content "Rolled JWT refresh to edge"
|
|
693
686
|
agent-memory read --target long_term
|
|
694
687
|
agent-memory read --target daily --date 2026-02-15
|
|
@@ -700,7 +693,7 @@ Examples:
|
|
|
700
693
|
agent-memory scratchpad done --text "PR #42"
|
|
701
694
|
agent-memory search --query "database choice" --mode keyword
|
|
702
695
|
agent-memory distil --dry-run
|
|
703
|
-
agent-memory context --
|
|
696
|
+
agent-memory context --query "database choice"
|
|
704
697
|
agent-memory sync
|
|
705
698
|
agent-memory status --json`);
|
|
706
699
|
}
|
package/dist/core.d.ts
CHANGED
|
@@ -40,6 +40,13 @@ export interface PreviewResult {
|
|
|
40
40
|
previewLines: number;
|
|
41
41
|
previewChars: number;
|
|
42
42
|
}
|
|
43
|
+
/** Redact common credential shapes before content reaches disk or agent context. */
|
|
44
|
+
export declare function redactSecrets(content: string): {
|
|
45
|
+
content: string;
|
|
46
|
+
redacted: boolean;
|
|
47
|
+
};
|
|
48
|
+
/** Apply trust, lifecycle, and secret policy to complete logical write entries. */
|
|
49
|
+
export declare function filterMemoryForContext(content: string, now?: Date): string;
|
|
43
50
|
export declare function truncateLines(lines: string[], maxLines: number, mode: TruncateMode): {
|
|
44
51
|
lines: string[];
|
|
45
52
|
truncated: boolean;
|
|
@@ -166,6 +173,7 @@ export declare function searchRelevantMemories(prompt: string): Promise<string>;
|
|
|
166
173
|
export interface QmdSearchResult {
|
|
167
174
|
path?: string;
|
|
168
175
|
file?: string;
|
|
176
|
+
context?: string;
|
|
169
177
|
score?: number;
|
|
170
178
|
content?: string;
|
|
171
179
|
chunk?: string;
|
|
@@ -175,10 +183,21 @@ export interface QmdSearchResult {
|
|
|
175
183
|
}
|
|
176
184
|
export declare function getQmdResultPath(r: QmdSearchResult): string | undefined;
|
|
177
185
|
export declare function getQmdResultText(r: QmdSearchResult): string;
|
|
178
|
-
export declare function runQmdSearch(mode: "keyword" | "semantic" | "deep", query: string, limit: number
|
|
186
|
+
export declare function runQmdSearch(mode: "keyword" | "semantic" | "deep", query: string, limit: number, options?: {
|
|
187
|
+
signal?: AbortSignal;
|
|
188
|
+
}): Promise<{
|
|
179
189
|
results: QmdSearchResult[];
|
|
180
190
|
stderr: string;
|
|
181
191
|
}>;
|
|
192
|
+
/**
|
|
193
|
+
* Best-effort check of whether vector embeddings are actually usable for
|
|
194
|
+
* semantic/deep search right now. Runs a tiny semantic probe and looks for
|
|
195
|
+
* qmd's "need embeddings" warning. Bounded by a short timeout because the very
|
|
196
|
+
* first semantic query can trigger a model download — returns "unknown" rather
|
|
197
|
+
* than blocking on it. "ready" means the probe ran without the warning; it does
|
|
198
|
+
* not prove the index has content.
|
|
199
|
+
*/
|
|
200
|
+
export declare function probeEmbeddings(): Promise<"ready" | "missing" | "unknown">;
|
|
182
201
|
export interface ToolResult {
|
|
183
202
|
text: string;
|
|
184
203
|
details: Record<string, unknown>;
|
|
@@ -191,6 +210,7 @@ export declare function memoryWrite(params: {
|
|
|
191
210
|
sessionId?: string;
|
|
192
211
|
topic?: string;
|
|
193
212
|
date?: string;
|
|
213
|
+
sourceUri?: string;
|
|
194
214
|
}): Promise<ToolResult>;
|
|
195
215
|
export declare function scratchpadAction(params: {
|
|
196
216
|
action: "add" | "done" | "undo" | "clear_done" | "list";
|