myagentmemory 0.4.3 → 0.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # agent-memory
2
2
 
3
- Persistent memory for coding agents — [Claude Code](https://claude.ai/code) and [OpenAI Codex](https://github.com/openai/codex). Semantic search powered by [qmd](https://github.com/tobi/qmd).
3
+ Persistent memory for coding agents — [Claude Code](https://claude.ai/code), [OpenAI Codex](https://github.com/openai/codex), Cursor, and Agent (Cursor CLI). Semantic search powered by [qmd](https://github.com/tobi/qmd).
4
4
 
5
5
  Thanks to https://github.com/skyfallsin/pi-mem for inspiration.
6
6
 
@@ -12,6 +12,9 @@ Long-term facts, daily logs, and a scratchpad checklist stored as plain markdown
12
12
  # Install the CLI globally
13
13
  npm install -g myagentmemory
14
14
 
15
+ # If you hit SSL errors due to corporate MITM/inspection, try:
16
+ # npm config set strict-ssl false
17
+
15
18
  # Or build from source
16
19
  bun run build:cli
17
20
  # => produces dist/agent-memory
@@ -19,7 +22,7 @@ bun run build:cli
19
22
  # Initialize memory directory
20
23
  agent-memory init
21
24
 
22
- # Install skill files for Claude Code and Codex
25
+ # Install skill files for Claude Code, Codex, Cursor, and Agent
23
26
  bash scripts/install-skills.sh
24
27
  pwsh -File scripts/install-skills.ps1
25
28
  ```
@@ -27,8 +30,12 @@ pwsh -File scripts/install-skills.ps1
27
30
  This installs:
28
31
  - `~/.claude/skills/agent-memory/SKILL.md` — Claude Code skill
29
32
  - `~/.codex/skills/agent-memory/SKILL.md` — Codex skill
33
+ - `~/.cursor/skills/agent-memory/SKILL.md` — Cursor skill
34
+ - `~/.agents/skills/agent-memory/SKILL.md` — Agent CLI skill (Cursor)
30
35
  - `%USERPROFILE%\.claude\skills\agent-memory\SKILL.md` — Claude Code skill (Windows)
31
36
  - `%USERPROFILE%\.codex\skills\agent-memory\SKILL.md` — Codex skill (Windows)
37
+ - `%USERPROFILE%\.cursor\skills\agent-memory\SKILL.md` — Cursor skill (Windows)
38
+ - `%USERPROFILE%\.agents\skills\agent-memory\SKILL.md` — Agent CLI skill (Windows)
32
39
 
33
40
  ### Optional: Enable search with qmd
34
41
 
@@ -58,7 +65,9 @@ Without qmd, all core tools (write/read/scratchpad) work normally. Only `memory_
58
65
  ┌──────────┐ ┌──────────────┐
59
66
  │ src/ │ │ skills/ │
60
67
  │ cli.ts │ │ ├─ claude-code/SKILL.md
61
- │ │ │ └─ codex/SKILL.md
68
+ │ │ │ ├─ codex/SKILL.md
69
+ │ │ │ ├─ cursor/SKILL.md
70
+ │ │ │ └─ agent/SKILL.md
62
71
  └──────────┘ └──────────────┘
63
72
  CLI binary instruction files
64
73
  `agent-memory` that invoke CLI
@@ -116,7 +125,7 @@ Before every agent turn, the following are injected into the system prompt (in p
116
125
 
117
126
  Total injection is capped at 16K chars. When qmd is unavailable, step 3 is skipped and the rest works as before.
118
127
 
119
- For Claude Code, context is injected via the `!`agent-memory context`` syntax in the SKILL.md. For Codex, the agent runs `agent-memory context` at session start.
128
+ For Claude Code, context is injected via the `!`agent-memory context`` syntax in the SKILL.md. For Codex, Cursor, and Agent, the agent runs `agent-memory context` at session start.
120
129
 
121
130
  ### Selective injection
122
131
 
package/dist/agent-memory CHANGED
Binary file
package/dist/cli.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  * agent-memory CLI
4
4
  *
5
5
  * Subcommands:
6
+ * version — Print binary version
6
7
  * context — Build & print context injection string to stdout
7
8
  * write — Write to memory files
8
9
  * read — Read memory files
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@
3
3
  * agent-memory CLI
4
4
  *
5
5
  * Subcommands:
6
+ * version — Print binary version
6
7
  * context — Build & print context injection string to stdout
7
8
  * write — Write to memory files
8
9
  * read — Read memory files
@@ -16,7 +17,8 @@
16
17
  * --json Machine-readable JSON output
17
18
  */
18
19
  import * as fs from "node:fs";
19
- import { _setBaseDir, buildMemoryContext, checkCollection, dailyPath, detectQmd, ensureDirs, ensureQmdAvailableForUpdate, getCollectionName, getDailyDir, getMemoryDir, getMemoryFile, getQmdResultPath, getQmdResultText, getScratchpadFile, nowTimestamp, parseScratchpad, readFileSafe, runQmdSearch, scheduleQmdUpdate, searchRelevantMemories, serializeScratchpad, setupQmdCollection, todayStr, } from "./core.js";
20
+ const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "dev";
21
+ import { _setBaseDir, buildMemoryContext, checkCollection, dailyPath, detectQmd, ensureDirs, ensureQmdAvailableForSync, ensureQmdAvailableForUpdate, getCollectionName, getDailyDir, getMemoryDir, getMemoryFile, getQmdEmbedMode, getQmdHealth, getQmdResultPath, getQmdResultText, getScratchpadFile, nowTimestamp, parseScratchpad, readFileSafe, runQmdEmbedDetached, runQmdSearch, runQmdSync, runQmdUpdateNow, scheduleQmdUpdate, searchRelevantMemories, serializeScratchpad, setupQmdCollection, todayStr, } from "./core.js";
20
22
  function parseArgs(argv) {
21
23
  const flags = {};
22
24
  const positional = [];
@@ -319,18 +321,60 @@ async function cmdSearch(flags) {
319
321
  exitError(`Search failed: ${err instanceof Error ? err.message : String(err)}`, json);
320
322
  }
321
323
  }
324
+ async function cmdSync(flags) {
325
+ const json = hasFlag(flags, "json");
326
+ ensureDirs();
327
+ const qmdFound = await ensureQmdAvailableForSync();
328
+ if (!qmdFound) {
329
+ exitError("qmd is not installed. Install: bun install -g https://github.com/tobi/qmd", json);
330
+ }
331
+ const collName = getCollectionName();
332
+ const hasCollection = await checkCollection(collName);
333
+ if (!hasCollection) {
334
+ exitError(`qmd collection '${collName}' not found. Run: agent-memory init`, json);
335
+ }
336
+ const result = await runQmdSync();
337
+ if (json) {
338
+ output({ ok: result.updateOk && result.embedOk, updateOk: result.updateOk, embedOk: result.embedOk }, true);
339
+ }
340
+ else {
341
+ if (result.updateOk) {
342
+ console.log("qmd update: ok");
343
+ }
344
+ else {
345
+ console.log("qmd update: failed");
346
+ }
347
+ if (result.embedOk) {
348
+ console.log("qmd embed: ok");
349
+ }
350
+ else {
351
+ console.log("qmd embed: failed");
352
+ }
353
+ if (result.updateOk && result.embedOk) {
354
+ console.log("\nIndex fully synced.");
355
+ }
356
+ }
357
+ }
322
358
  async function cmdInit(flags) {
323
359
  const json = hasFlag(flags, "json");
324
360
  ensureDirs();
325
361
  const dir = getMemoryDir();
326
362
  const qmdFound = await detectQmd();
327
363
  let collectionCreated = false;
364
+ let indexUpdated = false;
365
+ let embedStarted = false;
328
366
  if (qmdFound) {
329
367
  const collName = getCollectionName();
330
368
  const hasCollection = await checkCollection(collName);
331
369
  if (!hasCollection) {
332
370
  collectionCreated = await setupQmdCollection();
333
371
  }
372
+ // Run initial index update + start background embed
373
+ await ensureQmdAvailableForUpdate();
374
+ await runQmdUpdateNow();
375
+ indexUpdated = true;
376
+ const child = runQmdEmbedDetached();
377
+ embedStarted = child !== null;
334
378
  }
335
379
  if (json) {
336
380
  output({
@@ -338,6 +382,8 @@ async function cmdInit(flags) {
338
382
  directory: dir,
339
383
  qmd: qmdFound,
340
384
  collectionCreated,
385
+ indexUpdated,
386
+ embedStarted,
341
387
  }, true);
342
388
  }
343
389
  else {
@@ -350,6 +396,12 @@ async function cmdInit(flags) {
350
396
  else {
351
397
  console.log(` qmd collection '${getCollectionName()}' already exists.`);
352
398
  }
399
+ if (indexUpdated) {
400
+ console.log(` Index updated.`);
401
+ }
402
+ if (embedStarted) {
403
+ console.log(` Embedding started in background.`);
404
+ }
353
405
  }
354
406
  else {
355
407
  console.log(` qmd not found — search features unavailable.`);
@@ -375,9 +427,15 @@ async function cmdStatus(flags) {
375
427
  }
376
428
  const qmdFound = await detectQmd();
377
429
  let hasCollection = false;
430
+ let health = null;
378
431
  if (qmdFound) {
379
432
  hasCollection = await checkCollection();
433
+ if (hasCollection) {
434
+ await ensureQmdAvailableForSync();
435
+ health = await getQmdHealth();
436
+ }
380
437
  }
438
+ const embedMode = getQmdEmbedMode();
381
439
  if (json) {
382
440
  output({
383
441
  directory: dir,
@@ -395,7 +453,9 @@ async function cmdStatus(flags) {
395
453
  qmd: {
396
454
  available: qmdFound,
397
455
  collection: hasCollection ? getCollectionName() : null,
456
+ health,
398
457
  },
458
+ embedMode,
399
459
  }, true);
400
460
  }
401
461
  else {
@@ -421,6 +481,19 @@ async function cmdStatus(flags) {
421
481
  if (qmdFound) {
422
482
  console.log(`qmd: available`);
423
483
  console.log(`Collection '${getCollectionName()}': ${hasCollection ? "configured" : "not configured — run: agent-memory init"}`);
484
+ console.log(`Embed mode: ${embedMode}`);
485
+ if (health) {
486
+ if (health.totalFiles !== null)
487
+ console.log(`Files indexed: ${health.totalFiles}`);
488
+ if (health.vectorsEmbedded !== null)
489
+ console.log(`Vectors embedded: ${health.vectorsEmbedded}`);
490
+ if (health.pendingEmbed !== null && health.pendingEmbed > 0) {
491
+ console.log(`Pending embeds: ${health.pendingEmbed}`);
492
+ console.log(` run: agent-memory sync`);
493
+ }
494
+ if (health.lastUpdated)
495
+ console.log(`Last updated: ${health.lastUpdated}`);
496
+ }
424
497
  }
425
498
  else {
426
499
  console.log("qmd: not installed");
@@ -437,11 +510,13 @@ Usage:
437
510
  agent-memory <command> [options]
438
511
 
439
512
  Commands:
513
+ version Show binary version
440
514
  context Build & print context injection string
441
515
  write Write to memory files
442
516
  read Read memory files
443
517
  scratchpad Manage checklist items
444
518
  search Search across memory files (requires qmd)
519
+ sync Re-index and embed all files (requires qmd)
445
520
  init Initialize memory directory and qmd collection
446
521
  status Show configuration and status
447
522
 
@@ -461,6 +536,7 @@ Examples:
461
536
  agent-memory scratchpad done --text "PR #42"
462
537
  agent-memory search --query "database choice" --mode keyword
463
538
  agent-memory context --no-search
539
+ agent-memory sync
464
540
  agent-memory status --json`);
465
541
  }
466
542
  // ---------------------------------------------------------------------------
@@ -474,6 +550,10 @@ async function main() {
474
550
  if (dir) {
475
551
  _setBaseDir(dir);
476
552
  }
553
+ if (command === "version" || hasFlag(flags, "version")) {
554
+ output(json ? { version: VERSION } : VERSION, json);
555
+ return;
556
+ }
477
557
  if (!command || command === "help" || hasFlag(flags, "help")) {
478
558
  printUsage();
479
559
  return;
@@ -494,6 +574,9 @@ async function main() {
494
574
  case "search":
495
575
  await cmdSearch(flags);
496
576
  break;
577
+ case "sync":
578
+ await cmdSync(flags);
579
+ break;
497
580
  case "init":
498
581
  await cmdInit(flags);
499
582
  break;
package/dist/core.d.ts CHANGED
@@ -4,7 +4,8 @@
4
4
  * Core logic for agent-memory CLI and skills.
5
5
  * Zero pi peer dependencies — only node:fs, node:path, node:child_process.
6
6
  */
7
- import { execFile } from "node:child_process";
7
+ import type { ChildProcess } from "node:child_process";
8
+ import { execFile, spawn } from "node:child_process";
8
9
  /** Override base directory (for testing or platform-specific defaults). */
9
10
  export declare function _setBaseDir(baseDir: string): void;
10
11
  /** Reset to default paths. */
@@ -59,10 +60,15 @@ export declare function parseScratchpad(content: string): ScratchpadItem[];
59
60
  export declare function serializeScratchpad(items: ScratchpadItem[]): string;
60
61
  export declare function buildMemoryContext(searchResults?: string): string;
61
62
  type ExecFileFn = typeof execFile;
63
+ type SpawnFn = typeof spawn;
62
64
  /** Override execFile implementation (for testing). */
63
65
  export declare function _setExecFileForTest(fn: ExecFileFn): void;
64
66
  /** Reset execFile implementation (for testing). */
65
67
  export declare function _resetExecFileForTest(): void;
68
+ /** Override spawn implementation (for testing). */
69
+ export declare function _setSpawnForTest(fn: SpawnFn): void;
70
+ /** Reset spawn implementation (for testing). */
71
+ export declare function _resetSpawnForTest(): void;
66
72
  /** Set qmd availability flag (for testing). */
67
73
  export declare function _setQmdAvailable(value: boolean): void;
68
74
  /** Get current qmd availability flag. */
@@ -71,6 +77,10 @@ export declare function _getQmdAvailable(): boolean;
71
77
  export declare function _getUpdateTimer(): ReturnType<typeof setTimeout> | null;
72
78
  /** Clear the update timer (for testing). */
73
79
  export declare function _clearUpdateTimer(): void;
80
+ /** Get current embed timer (for testing). */
81
+ export declare function _getEmbedTimer(): ReturnType<typeof setTimeout> | null;
82
+ /** Clear the embed timer (for testing). */
83
+ export declare function _clearEmbedTimer(): void;
74
84
  /** Get the current QMD collection name. */
75
85
  export declare function getCollectionName(): string;
76
86
  /** Set the QMD collection name (for platform-specific overrides). */
@@ -83,8 +93,28 @@ export declare function detectQmd(): Promise<boolean>;
83
93
  export declare function checkCollection(name?: string): Promise<boolean>;
84
94
  export declare function getQmdUpdateMode(): "background" | "manual" | "off";
85
95
  export declare function ensureQmdAvailableForUpdate(): Promise<boolean>;
96
+ export declare function getQmdEmbedMode(): "background" | "manual" | "off";
97
+ export declare function runQmdEmbedDetached(): ChildProcess | null;
98
+ export declare function scheduleQmdEmbed(): void;
86
99
  export declare function scheduleQmdUpdate(): void;
87
100
  export declare function runQmdUpdateNow(): Promise<void>;
101
+ export declare function runQmdEmbedNow(): Promise<boolean>;
102
+ export declare function ensureQmdAvailableForSync(): Promise<boolean>;
103
+ export declare function runQmdSync(): Promise<{
104
+ updateOk: boolean;
105
+ embedOk: boolean;
106
+ }>;
107
+ export interface QmdHealthInfo {
108
+ totalFiles: number | null;
109
+ vectorsEmbedded: number | null;
110
+ pendingEmbed: number | null;
111
+ lastUpdated: string | null;
112
+ collectionFiles: number | null;
113
+ collectionUpdated: string | null;
114
+ embedMode: string;
115
+ }
116
+ export declare function parseQmdStatus(stdout: string, collectionName: string): QmdHealthInfo;
117
+ export declare function getQmdHealth(): Promise<QmdHealthInfo | null>;
88
118
  /** Search for memories relevant to the user's prompt. Returns formatted markdown or empty string on error. */
89
119
  export declare function searchRelevantMemories(prompt: string): Promise<string>;
90
120
  export interface QmdSearchResult {
package/dist/core.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Core logic for agent-memory CLI and skills.
5
5
  * Zero pi peer dependencies — only node:fs, node:path, node:child_process.
6
6
  */
7
- import { execFile } from "node:child_process";
7
+ import { execFile, spawn } from "node:child_process";
8
8
  import * as fs from "node:fs";
9
9
  import * as path from "node:path";
10
10
  // ---------------------------------------------------------------------------
@@ -280,8 +280,10 @@ export function buildMemoryContext(searchResults) {
280
280
  return context;
281
281
  }
282
282
  let execFileFn = execFile;
283
+ let spawnFn = spawn;
283
284
  let qmdAvailable = false;
284
285
  let updateTimer = null;
286
+ let embedTimer = null;
285
287
  /** QMD collection name — configurable per platform. */
286
288
  let QMD_COLLECTION_NAME = "agent-memory";
287
289
  /** Override execFile implementation (for testing). */
@@ -292,6 +294,14 @@ export function _setExecFileForTest(fn) {
292
294
  export function _resetExecFileForTest() {
293
295
  execFileFn = execFile;
294
296
  }
297
+ /** Override spawn implementation (for testing). */
298
+ export function _setSpawnForTest(fn) {
299
+ spawnFn = fn;
300
+ }
301
+ /** Reset spawn implementation (for testing). */
302
+ export function _resetSpawnForTest() {
303
+ spawnFn = spawn;
304
+ }
295
305
  /** Set qmd availability flag (for testing). */
296
306
  export function _setQmdAvailable(value) {
297
307
  qmdAvailable = value;
@@ -311,6 +321,17 @@ export function _clearUpdateTimer() {
311
321
  updateTimer = null;
312
322
  }
313
323
  }
324
+ /** Get current embed timer (for testing). */
325
+ export function _getEmbedTimer() {
326
+ return embedTimer;
327
+ }
328
+ /** Clear the embed timer (for testing). */
329
+ export function _clearEmbedTimer() {
330
+ if (embedTimer) {
331
+ clearTimeout(embedTimer);
332
+ embedTimer = null;
333
+ }
334
+ }
314
335
  /** Get the current QMD collection name. */
315
336
  export function getCollectionName() {
316
337
  return QMD_COLLECTION_NAME;
@@ -425,6 +446,39 @@ export async function ensureQmdAvailableForUpdate() {
425
446
  qmdAvailable = await detectQmd();
426
447
  return qmdAvailable;
427
448
  }
449
+ export function getQmdEmbedMode() {
450
+ const mode = (process.env.AGENT_MEMORY_QMD_EMBED ?? "background").toLowerCase();
451
+ if (mode === "manual" || mode === "off" || mode === "background") {
452
+ return mode;
453
+ }
454
+ return "background";
455
+ }
456
+ export function runQmdEmbedDetached() {
457
+ if (!qmdAvailable)
458
+ return null;
459
+ const child = spawnFn("qmd", ["embed"], {
460
+ detached: true,
461
+ stdio: "ignore",
462
+ });
463
+ child.unref();
464
+ return child;
465
+ }
466
+ export function scheduleQmdEmbed() {
467
+ if (getQmdEmbedMode() !== "background")
468
+ return;
469
+ if (!qmdAvailable)
470
+ return;
471
+ if (embedTimer)
472
+ clearTimeout(embedTimer);
473
+ embedTimer = setTimeout(() => {
474
+ embedTimer = null;
475
+ runQmdEmbedDetached();
476
+ }, 5_000);
477
+ // Don't prevent process exit while waiting to embed
478
+ if (embedTimer && typeof embedTimer === "object" && "unref" in embedTimer) {
479
+ embedTimer.unref();
480
+ }
481
+ }
428
482
  export function scheduleQmdUpdate() {
429
483
  if (getQmdUpdateMode() !== "background")
430
484
  return;
@@ -434,7 +488,9 @@ export function scheduleQmdUpdate() {
434
488
  clearTimeout(updateTimer);
435
489
  updateTimer = setTimeout(() => {
436
490
  updateTimer = null;
437
- execFileFn("qmd", ["update"], { timeout: 30_000 }, () => { });
491
+ execFileFn("qmd", ["update"], { timeout: 30_000 }, () => {
492
+ scheduleQmdEmbed();
493
+ });
438
494
  }, 500);
439
495
  }
440
496
  export async function runQmdUpdateNow() {
@@ -446,6 +502,89 @@ export async function runQmdUpdateNow() {
446
502
  execFileFn("qmd", ["update"], { timeout: 30_000 }, () => resolve());
447
503
  });
448
504
  }
505
+ export async function runQmdEmbedNow() {
506
+ if (!qmdAvailable)
507
+ return false;
508
+ return new Promise((resolve) => {
509
+ execFileFn("qmd", ["embed"], { timeout: 120_000 }, (err) => {
510
+ resolve(!err);
511
+ });
512
+ });
513
+ }
514
+ export async function ensureQmdAvailableForSync() {
515
+ if (qmdAvailable)
516
+ return true;
517
+ qmdAvailable = await detectQmd();
518
+ return qmdAvailable;
519
+ }
520
+ export async function runQmdSync() {
521
+ if (!qmdAvailable)
522
+ return { updateOk: false, embedOk: false };
523
+ const updateOk = await new Promise((resolve) => {
524
+ execFileFn("qmd", ["update"], { timeout: 30_000 }, (err) => {
525
+ resolve(!err);
526
+ });
527
+ });
528
+ const embedOk = await runQmdEmbedNow();
529
+ return { updateOk, embedOk };
530
+ }
531
+ export function parseQmdStatus(stdout, collectionName) {
532
+ const result = {
533
+ totalFiles: null,
534
+ vectorsEmbedded: null,
535
+ pendingEmbed: null,
536
+ lastUpdated: null,
537
+ collectionFiles: null,
538
+ collectionUpdated: null,
539
+ embedMode: getQmdEmbedMode(),
540
+ };
541
+ if (!stdout.trim())
542
+ return result;
543
+ // Total files across all collections
544
+ const totalFilesMatch = stdout.match(/(\d+)\s+(?:total\s+)?files?/i);
545
+ if (totalFilesMatch) {
546
+ result.totalFiles = Number.parseInt(totalFilesMatch[1], 10);
547
+ }
548
+ // Vectors / embeddings
549
+ const vectorsMatch = stdout.match(/(\d+)\s+(?:vectors?|embeddings?)/i);
550
+ if (vectorsMatch) {
551
+ result.vectorsEmbedded = Number.parseInt(vectorsMatch[1], 10);
552
+ }
553
+ // Pending embed
554
+ const pendingMatch = stdout.match(/(\d+)\s+pending/i);
555
+ if (pendingMatch) {
556
+ result.pendingEmbed = Number.parseInt(pendingMatch[1], 10);
557
+ }
558
+ // If we have totalFiles and vectorsEmbedded but no explicit pending, infer it
559
+ if (result.pendingEmbed === null && result.totalFiles !== null && result.vectorsEmbedded !== null) {
560
+ result.pendingEmbed = Math.max(0, result.totalFiles - result.vectorsEmbedded);
561
+ }
562
+ // Last updated timestamp
563
+ const updatedMatch = stdout.match(/(?:last\s+)?updated:?\s+(\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}[:\d]*)/i);
564
+ if (updatedMatch) {
565
+ result.lastUpdated = updatedMatch[1];
566
+ }
567
+ // Collection-specific info — look for collection name section
568
+ const collectionPattern = new RegExp(`${collectionName}[:\\s]*(?:.*?)(\\d+)\\s+files?`, "i");
569
+ const collMatch = stdout.match(collectionPattern);
570
+ if (collMatch) {
571
+ result.collectionFiles = Number.parseInt(collMatch[1], 10);
572
+ }
573
+ return result;
574
+ }
575
+ export async function getQmdHealth() {
576
+ if (!qmdAvailable)
577
+ return null;
578
+ return new Promise((resolve) => {
579
+ execFileFn("qmd", ["status"], { timeout: 10_000 }, (err, stdout) => {
580
+ if (err) {
581
+ resolve(null);
582
+ return;
583
+ }
584
+ resolve(parseQmdStatus(stdout ?? "", QMD_COLLECTION_NAME));
585
+ });
586
+ });
587
+ }
449
588
  /** Search for memories relevant to the user's prompt. Returns formatted markdown or empty string on error. */
450
589
  export async function searchRelevantMemories(prompt) {
451
590
  if (!qmdAvailable || !prompt.trim())
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "myagentmemory",
3
- "version": "0.4.3",
4
- "description": "Persistent memory for coding agents (Claude Code, Codex) with qmd-powered semantic search across daily logs, long-term memory, and scratchpad",
3
+ "version": "0.4.5",
4
+ "description": "Persistent memory for coding agents (Claude Code, Codex, Cursor, Agent) with qmd-powered semantic search across daily logs, long-term memory, and scratchpad",
5
5
  "main": "./dist/core.js",
6
6
  "types": "./dist/core.d.ts",
7
7
  "exports": {
@@ -22,6 +22,8 @@
22
22
  "daily-log",
23
23
  "claude-code",
24
24
  "codex",
25
+ "cursor",
26
+ "agent",
25
27
  "agent-memory",
26
28
  "coding-agent"
27
29
  ],
@@ -50,7 +52,7 @@
50
52
  "postinstall": "node scripts/postinstall.cjs",
51
53
  "build": "tsc -p tsconfig.json --noEmit",
52
54
  "build:lib": "tsc -p tsconfig.build.json",
53
- "build:cli": "bun build src/cli.ts --compile --outfile dist/agent-memory",
55
+ "build:cli": "bun build src/cli.ts --compile --outfile dist/agent-memory --define __VERSION__=\"'$(node -p \"require('./package.json').version\")'\"",
54
56
  "prepack": "npm run build:lib && bun run build:cli",
55
57
  "lint": "biome check .",
56
58
  "test": "bun test test/unit.test.ts",
@@ -60,6 +62,7 @@
60
62
  },
61
63
  "devDependencies": {
62
64
  "@biomejs/biome": "^2.4.0",
65
+ "@types/node": "^25.3.0",
63
66
  "tsx": "^4.0.0",
64
67
  "typescript": "^5.9.3"
65
68
  },
@@ -3,28 +3,34 @@ $ErrorActionPreference = "Stop"
3
3
  $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
4
4
  $ProjectDir = Split-Path -Parent $ScriptDir
5
5
 
6
- # Claude Code skill
7
- $ClaudeSkillDir = Join-Path $env:USERPROFILE ".claude\\skills\\agent-memory"
8
- $ClaudeSkillSrc = Join-Path $ProjectDir "skills\\claude-code\\SKILL.md"
9
- if (Test-Path $ClaudeSkillSrc) {
10
- New-Item -ItemType Directory -Force -Path $ClaudeSkillDir | Out-Null
11
- Copy-Item -Force $ClaudeSkillSrc (Join-Path $ClaudeSkillDir "SKILL.md")
12
- Write-Host "Installed Claude Code skill: $ClaudeSkillDir\\SKILL.md"
13
- } else {
14
- Write-Host "Skipping Claude Code skill (skills\\claude-code\\SKILL.md not found)"
15
- }
6
+ function Install-Skill {
7
+ param(
8
+ [string]$Label,
9
+ [string]$SrcDir,
10
+ [string]$DestDir,
11
+ [string]$HomeMarker
12
+ )
16
13
 
17
- # Codex skill
18
- $CodexSkillDir = Join-Path $env:USERPROFILE ".codex\\skills\\agent-memory"
19
- $CodexSkillSrc = Join-Path $ProjectDir "skills\\codex\\SKILL.md"
20
- if (Test-Path $CodexSkillSrc) {
21
- New-Item -ItemType Directory -Force -Path $CodexSkillDir | Out-Null
22
- Copy-Item -Force $CodexSkillSrc (Join-Path $CodexSkillDir "SKILL.md")
23
- Write-Host "Installed Codex skill: $CodexSkillDir\\SKILL.md"
24
- } else {
25
- Write-Host "Skipping Codex skill (skills\\codex\\SKILL.md not found)"
14
+ if (-not (Test-Path $HomeMarker)) {
15
+ Write-Host "Skipping $Label ($HomeMarker not found)"
16
+ return
17
+ }
18
+
19
+ $SkillSrc = Join-Path $SrcDir "SKILL.md"
20
+ if (Test-Path $SkillSrc) {
21
+ New-Item -ItemType Directory -Force -Path $DestDir | Out-Null
22
+ Copy-Item -Force $SkillSrc (Join-Path $DestDir "SKILL.md")
23
+ Write-Host "Installed $Label: $DestDir\\SKILL.md"
24
+ } else {
25
+ Write-Host "Skipping $Label ($SkillSrc not found)"
26
+ }
26
27
  }
27
28
 
29
+ Install-Skill -Label "Claude Code skill" -SrcDir (Join-Path $ProjectDir "skills\\claude-code") -DestDir (Join-Path $env:USERPROFILE ".claude\\skills\\agent-memory") -HomeMarker (Join-Path $env:USERPROFILE ".claude")
30
+ Install-Skill -Label "Codex skill" -SrcDir (Join-Path $ProjectDir "skills\\codex") -DestDir (Join-Path $env:USERPROFILE ".codex\\skills\\agent-memory") -HomeMarker (Join-Path $env:USERPROFILE ".codex")
31
+ Install-Skill -Label "Cursor skill" -SrcDir (Join-Path $ProjectDir "skills\\cursor") -DestDir (Join-Path $env:USERPROFILE ".cursor\\skills\\agent-memory") -HomeMarker (Join-Path $env:USERPROFILE ".cursor")
32
+ Install-Skill -Label "Agent CLI skill" -SrcDir (Join-Path $ProjectDir "skills\\agent") -DestDir (Join-Path $env:USERPROFILE ".agents\\skills\\agent-memory") -HomeMarker (Join-Path $env:USERPROFILE ".agents")
33
+
28
34
  Write-Host ""
29
35
  Write-Host "Done."
30
36
  Write-Host ""
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Install agent-memory skills for Claude Code and Codex CLI.
2
+ # Install agent-memory skills for Claude Code, Codex, Cursor, and Agent CLI.
3
3
  # Usage: bash scripts/install-skills.sh
4
4
 
5
5
  set -euo pipefail
@@ -7,25 +7,30 @@ set -euo pipefail
7
7
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
8
  PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
9
9
 
10
- # Claude Code skill
11
- CLAUDE_SKILL_DIR="$HOME/.claude/skills/agent-memory"
12
- if [ -d "$PROJECT_DIR/skills/claude-code" ]; then
13
- mkdir -p "$CLAUDE_SKILL_DIR"
14
- cp "$PROJECT_DIR/skills/claude-code/SKILL.md" "$CLAUDE_SKILL_DIR/SKILL.md"
15
- echo "Installed Claude Code skill: $CLAUDE_SKILL_DIR/SKILL.md"
16
- else
17
- echo "Skipping Claude Code skill (skills/claude-code/ not found)"
18
- fi
10
+ install_skill() {
11
+ local label="$1"
12
+ local src_dir="$2"
13
+ local dest_dir="$3"
14
+ local home_marker="$4"
19
15
 
20
- # Codex skill
21
- CODEX_SKILL_DIR="$HOME/.codex/skills/agent-memory"
22
- if [ -d "$PROJECT_DIR/skills/codex" ]; then
23
- mkdir -p "$CODEX_SKILL_DIR"
24
- cp "$PROJECT_DIR/skills/codex/SKILL.md" "$CODEX_SKILL_DIR/SKILL.md"
25
- echo "Installed Codex skill: $CODEX_SKILL_DIR/SKILL.md"
26
- else
27
- echo "Skipping Codex skill (skills/codex/ not found)"
28
- fi
16
+ if [ ! -d "$home_marker" ]; then
17
+ echo "Skipping $label ($home_marker not found)"
18
+ return
19
+ fi
20
+
21
+ if [ -d "$src_dir" ]; then
22
+ mkdir -p "$dest_dir"
23
+ cp "$src_dir/SKILL.md" "$dest_dir/SKILL.md"
24
+ echo "Installed $label: $dest_dir/SKILL.md"
25
+ else
26
+ echo "Skipping $label ($src_dir not found)"
27
+ fi
28
+ }
29
+
30
+ install_skill "Claude Code skill" "$PROJECT_DIR/skills/claude-code" "$HOME/.claude/skills/agent-memory" "$HOME/.claude"
31
+ install_skill "Codex skill" "$PROJECT_DIR/skills/codex" "$HOME/.codex/skills/agent-memory" "$HOME/.codex"
32
+ install_skill "Cursor skill" "$PROJECT_DIR/skills/cursor" "$HOME/.cursor/skills/agent-memory" "$HOME/.cursor"
33
+ install_skill "Agent CLI skill" "$PROJECT_DIR/skills/agent" "$HOME/.agents/skills/agent-memory" "$HOME/.agents"
29
34
 
30
35
  echo ""
31
36
  echo "Done."