myagentmemory 0.4.7 → 0.4.10

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/src/cli.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Subcommands:
6
6
  * version — Print binary version
7
- * install-skills — Install SKILL.md files into local agent directories
7
+ * install-skills — Install (or --uninstall) SKILL.md files into local agent directories
8
8
  * context — Build & print context injection string to stdout
9
9
  * write — Write to memory files
10
10
  * read — Read memory files
@@ -20,15 +20,13 @@
20
20
 
21
21
  import * as fs from "node:fs";
22
22
 
23
- declare const __VERSION__: string;
24
- const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "dev";
25
-
26
23
  import {
27
24
  _setBaseDir,
28
25
  buildMemoryContext,
29
26
  checkCollection,
30
27
  dailyPath,
31
28
  detectQmd,
29
+ distilMemories,
32
30
  ensureDirs,
33
31
  ensureQmdAvailableForSync,
34
32
  ensureQmdAvailableForUpdate,
@@ -41,6 +39,7 @@ import {
41
39
  getQmdResultPath,
42
40
  getQmdResultText,
43
41
  getScratchpadFile,
42
+ getTopicsDir,
44
43
  installSkills,
45
44
  nowTimestamp,
46
45
  parseScratchpad,
@@ -53,9 +52,15 @@ import {
53
52
  searchRelevantMemories,
54
53
  serializeScratchpad,
55
54
  setupQmdCollection,
55
+ slugifyTopic,
56
56
  todayStr,
57
+ topicPath,
58
+ uninstallSkills,
57
59
  } from "./core.js";
58
60
 
61
+ declare const __VERSION__: string;
62
+ const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "dev";
63
+
59
64
  // ---------------------------------------------------------------------------
60
65
  // Arg parsing (no external deps)
61
66
  // ---------------------------------------------------------------------------
@@ -151,12 +156,14 @@ async function cmdContext(flags: Record<string, string | boolean>) {
151
156
 
152
157
  async function cmdWrite(flags: Record<string, string | boolean>) {
153
158
  const json = hasFlag(flags, "json");
154
- const target = getFlag(flags, "target");
159
+ const target = getFlag(flags, "target") ?? "daily";
155
160
  const content = getFlag(flags, "content");
156
161
  const mode = getFlag(flags, "mode") ?? "append";
162
+ const topic = getFlag(flags, "topic");
163
+ const date = getFlag(flags, "date");
157
164
 
158
- if (!target || !["long_term", "daily"].includes(target)) {
159
- exitError("--target must be 'long_term' or 'daily'", json);
165
+ if (!["long_term", "daily", "topic"].includes(target)) {
166
+ exitError("--target must be 'long_term', 'daily', or 'topic' (default: daily)", json);
160
167
  }
161
168
  if (!content) {
162
169
  exitError("--content is required", json);
@@ -183,6 +190,33 @@ async function cmdWrite(flags: Record<string, string | boolean>) {
183
190
  return;
184
191
  }
185
192
 
193
+ if (target === "topic") {
194
+ if (!topic) {
195
+ exitError("--topic is required when --target is 'topic'", json);
196
+ }
197
+ const slug = slugifyTopic(topic);
198
+ if (!slug) {
199
+ exitError("--topic must include at least one letter or number", json);
200
+ }
201
+ const filePath = topicPath(slug);
202
+ const existing = readFileSafe(filePath) ?? "";
203
+ const linkDate = date?.trim() || todayStr();
204
+ const header = `# Topic: ${topic}\n\n<!-- created: ${ts} [${sid}] -->\n`;
205
+ const separator = existing.trim() ? "\n\n" : "";
206
+ const base = existing.trim() ? existing : header.trimEnd();
207
+ const stamped = `<!-- ${ts} [${sid}] -->\n${content.trim()}\nDaily: [[${linkDate}]]`;
208
+ fs.writeFileSync(filePath, `${base}${separator}${stamped}`, "utf-8");
209
+ await ensureQmdAvailableForUpdate();
210
+ scheduleQmdUpdate();
211
+ output(
212
+ json
213
+ ? { ok: true, path: filePath, target, mode: "append", timestamp: ts, topic, slug, date: linkDate }
214
+ : `Appended to topic: ${filePath}`,
215
+ json,
216
+ );
217
+ return;
218
+ }
219
+
186
220
  // long_term
187
221
  const memFile = getMemoryFile();
188
222
  const existing = readFileSafe(memFile) ?? "";
@@ -209,9 +243,10 @@ async function cmdRead(flags: Record<string, string | boolean>) {
209
243
  const json = hasFlag(flags, "json");
210
244
  const target = getFlag(flags, "target");
211
245
  const date = getFlag(flags, "date");
246
+ const topic = getFlag(flags, "topic");
212
247
 
213
- if (!target || !["long_term", "scratchpad", "daily", "list"].includes(target)) {
214
- exitError("--target must be 'long_term', 'scratchpad', 'daily', or 'list'", json);
248
+ if (!target || !["long_term", "scratchpad", "daily", "list", "topic", "topics"].includes(target)) {
249
+ exitError("--target must be 'long_term', 'scratchpad', 'daily', 'list', 'topic', or 'topics'", json);
215
250
  }
216
251
 
217
252
  ensureDirs();
@@ -248,6 +283,41 @@ async function cmdRead(flags: Record<string, string | boolean>) {
248
283
  return;
249
284
  }
250
285
 
286
+ if (target === "topics") {
287
+ try {
288
+ const files = fs
289
+ .readdirSync(getTopicsDir())
290
+ .filter((f) => f.endsWith(".md"))
291
+ .sort()
292
+ .reverse();
293
+ if (json) {
294
+ output({ files }, true);
295
+ } else if (files.length === 0) {
296
+ console.log("No topics found.");
297
+ } else {
298
+ console.log(`Topics:\n${files.map((f) => `- ${f}`).join("\n")}`);
299
+ }
300
+ } catch {
301
+ output(json ? { files: [] } : "No topics directory.", json);
302
+ }
303
+ return;
304
+ }
305
+
306
+ if (target === "topic") {
307
+ if (!topic) {
308
+ exitError("--topic is required when --target is 'topic'", json);
309
+ }
310
+ const slug = slugifyTopic(topic);
311
+ const filePath = topicPath(slug);
312
+ const content = readFileSafe(filePath);
313
+ if (!content) {
314
+ output(json ? { content: null, topic } : `No topic file found for ${topic}.`, json);
315
+ return;
316
+ }
317
+ output(json ? { content, topic, slug, path: filePath } : content, json);
318
+ return;
319
+ }
320
+
251
321
  if (target === "scratchpad") {
252
322
  const content = readFileSafe(getScratchpadFile());
253
323
  if (!content?.trim()) {
@@ -402,6 +472,32 @@ async function cmdSearch(flags: Record<string, string | boolean>) {
402
472
 
403
473
  function cmdInstallSkills(flags: Record<string, string | boolean>) {
404
474
  const json = hasFlag(flags, "json");
475
+ const uninstall = hasFlag(flags, "uninstall");
476
+
477
+ if (uninstall) {
478
+ const report = uninstallSkills();
479
+
480
+ if (!report.ok) {
481
+ exitError(report.error ?? "Failed to uninstall skills.", json);
482
+ }
483
+
484
+ if (json) {
485
+ output(report, true);
486
+ return;
487
+ }
488
+
489
+ for (const item of report.removed) {
490
+ console.log(`Uninstalled ${item.label}: ${item.path}`);
491
+ }
492
+ for (const item of report.skipped) {
493
+ console.log(`Skipping ${item.label} (${item.reason})`);
494
+ }
495
+ if (report.removed.length === 0) {
496
+ console.log("No skills were installed.");
497
+ }
498
+ return;
499
+ }
500
+
405
501
  const report = installSkills();
406
502
 
407
503
  if (!report.ok) {
@@ -413,7 +509,15 @@ function cmdInstallSkills(flags: Record<string, string | boolean>) {
413
509
  return;
414
510
  }
415
511
 
416
- if (report.detected.length === 0) {
512
+ if (report.checked.length > 0) {
513
+ for (const item of report.checked) {
514
+ if (item.status === "detected") {
515
+ console.log(`Detecting ${item.label}... found`);
516
+ } else {
517
+ console.log(`Detecting ${item.label}... not found (${item.reason ?? "unknown"})`);
518
+ }
519
+ }
520
+ } else if (report.detected.length === 0) {
417
521
  console.log("No supported agent installations detected.");
418
522
  } else {
419
523
  const detectedLabels = report.detected.map((item) => item.label).join(", ");
@@ -512,7 +616,7 @@ async function cmdInit(flags: Record<string, string | boolean>) {
512
616
  );
513
617
  } else {
514
618
  console.log(`Memory directory: ${dir}`);
515
- console.log(` MEMORY.md, SCRATCHPAD.md, daily/ created.`);
619
+ console.log(` MEMORY.md, SCRATCHPAD.md, daily/, topics/ created.`);
516
620
  if (qmdFound) {
517
621
  if (collectionCreated) {
518
622
  console.log(` qmd collection '${getCollectionName()}' created.`);
@@ -540,6 +644,7 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
540
644
  const memFile = getMemoryFile();
541
645
  const spFile = getScratchpadFile();
542
646
  const dailyDir = getDailyDir();
647
+ const topicsDir = getTopicsDir();
543
648
 
544
649
  const memContent = readFileSafe(memFile);
545
650
  const spContent = readFileSafe(spFile);
@@ -550,6 +655,12 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
550
655
  } catch {
551
656
  // directory may not exist
552
657
  }
658
+ let topicCount = 0;
659
+ try {
660
+ topicCount = fs.readdirSync(topicsDir).filter((f) => f.endsWith(".md")).length;
661
+ } catch {
662
+ // directory may not exist
663
+ }
553
664
 
554
665
  const qmdFound = await detectQmd();
555
666
  let hasCollection = false;
@@ -579,6 +690,7 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
579
690
  openItems: spContent ? parseScratchpad(spContent).filter((i) => !i.done).length : 0,
580
691
  },
581
692
  dailyLogs: dailyCount,
693
+ topics: topicCount,
582
694
  qmd: {
583
695
  available: qmdFound,
584
696
  collection: hasCollection ? getCollectionName() : null,
@@ -605,6 +717,7 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
605
717
  console.log("SCRATCHPAD.md: not created yet");
606
718
  }
607
719
  console.log(`Daily logs: ${dailyCount} file(s)`);
720
+ console.log(`Topics: ${topicCount} file(s)`);
608
721
  console.log("");
609
722
  if (qmdFound) {
610
723
  console.log(`qmd: available`);
@@ -627,6 +740,33 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
627
740
  }
628
741
  }
629
742
 
743
+ async function cmdDistil(flags: Record<string, string | boolean>) {
744
+ const json = hasFlag(flags, "json");
745
+ const dryRun = hasFlag(flags, "dry-run");
746
+
747
+ const result = await distilMemories({ dryRun });
748
+
749
+ if (json) {
750
+ output(result, true);
751
+ } else {
752
+ if (result.totalEntries === 0) {
753
+ console.log(result.output.trim());
754
+ return;
755
+ }
756
+ if (dryRun) {
757
+ console.log("--- Dry run (MEMORY.md not modified) ---\n");
758
+ }
759
+ console.log(result.output.trim());
760
+ console.log("");
761
+ console.log(
762
+ `Distilled ${result.totalEntries} entries from ${result.totalDailyFiles} daily file(s) and ${result.totalTopicFiles} topic file(s), ${result.totalTags} tag(s).`,
763
+ );
764
+ if (!dryRun) {
765
+ console.log("MEMORY.md updated.");
766
+ }
767
+ }
768
+ }
769
+
630
770
  // ---------------------------------------------------------------------------
631
771
  // Usage
632
772
  // ---------------------------------------------------------------------------
@@ -639,12 +779,14 @@ Usage:
639
779
 
640
780
  Commands:
641
781
  version Show binary version
642
- install-skills Install bundled skills into local agent directories
782
+ install-skills Install (or --uninstall) bundled skills
783
+ uninstall-skills Uninstall bundled skills
643
784
  context Build & print context injection string
644
- write Write to memory files
785
+ write Write to memory files (default: daily)
645
786
  read Read memory files
646
787
  scratchpad Manage checklist items
647
788
  search Search across memory files (requires qmd)
789
+ distil Generate compact MEMORY.md index from daily logs + topics
648
790
  sync Re-index and embed all files (requires qmd)
649
791
  init Initialize memory directory and qmd collection
650
792
  status Show configuration and status
@@ -655,15 +797,19 @@ Global flags:
655
797
 
656
798
  Examples:
657
799
  agent-memory init
800
+ agent-memory write --content "Fixed auth bug in login flow"
658
801
  agent-memory write --target long_term --content "User prefers dark mode"
659
- agent-memory write --target daily --content "Fixed auth bug in login flow"
802
+ agent-memory write --target topic --topic "auth" --content "Rolled JWT refresh to edge"
660
803
  agent-memory read --target long_term
661
804
  agent-memory read --target daily --date 2026-02-15
662
805
  agent-memory read --target list
806
+ agent-memory read --target topic --topic "auth"
807
+ agent-memory read --target topics
663
808
  agent-memory scratchpad add --text "Review PR #42"
664
809
  agent-memory scratchpad list
665
810
  agent-memory scratchpad done --text "PR #42"
666
811
  agent-memory search --query "database choice" --mode keyword
812
+ agent-memory distil --dry-run
667
813
  agent-memory context --no-search
668
814
  agent-memory sync
669
815
  agent-memory status --json`);
@@ -712,6 +858,13 @@ async function main() {
712
858
  case "install-skills":
713
859
  cmdInstallSkills(flags);
714
860
  break;
861
+ case "uninstall-skills":
862
+ cmdInstallSkills({ ...flags, uninstall: true });
863
+ break;
864
+ case "distil":
865
+ case "distill":
866
+ await cmdDistil(flags);
867
+ break;
715
868
  case "sync":
716
869
  await cmdSync(flags);
717
870
  break;