myagentmemory 0.4.8 → 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) {
@@ -520,7 +616,7 @@ async function cmdInit(flags: Record<string, string | boolean>) {
520
616
  );
521
617
  } else {
522
618
  console.log(`Memory directory: ${dir}`);
523
- console.log(` MEMORY.md, SCRATCHPAD.md, daily/ created.`);
619
+ console.log(` MEMORY.md, SCRATCHPAD.md, daily/, topics/ created.`);
524
620
  if (qmdFound) {
525
621
  if (collectionCreated) {
526
622
  console.log(` qmd collection '${getCollectionName()}' created.`);
@@ -548,6 +644,7 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
548
644
  const memFile = getMemoryFile();
549
645
  const spFile = getScratchpadFile();
550
646
  const dailyDir = getDailyDir();
647
+ const topicsDir = getTopicsDir();
551
648
 
552
649
  const memContent = readFileSafe(memFile);
553
650
  const spContent = readFileSafe(spFile);
@@ -558,6 +655,12 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
558
655
  } catch {
559
656
  // directory may not exist
560
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
+ }
561
664
 
562
665
  const qmdFound = await detectQmd();
563
666
  let hasCollection = false;
@@ -587,6 +690,7 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
587
690
  openItems: spContent ? parseScratchpad(spContent).filter((i) => !i.done).length : 0,
588
691
  },
589
692
  dailyLogs: dailyCount,
693
+ topics: topicCount,
590
694
  qmd: {
591
695
  available: qmdFound,
592
696
  collection: hasCollection ? getCollectionName() : null,
@@ -613,6 +717,7 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
613
717
  console.log("SCRATCHPAD.md: not created yet");
614
718
  }
615
719
  console.log(`Daily logs: ${dailyCount} file(s)`);
720
+ console.log(`Topics: ${topicCount} file(s)`);
616
721
  console.log("");
617
722
  if (qmdFound) {
618
723
  console.log(`qmd: available`);
@@ -635,6 +740,33 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
635
740
  }
636
741
  }
637
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
+
638
770
  // ---------------------------------------------------------------------------
639
771
  // Usage
640
772
  // ---------------------------------------------------------------------------
@@ -647,12 +779,14 @@ Usage:
647
779
 
648
780
  Commands:
649
781
  version Show binary version
650
- install-skills Install bundled skills into local agent directories
782
+ install-skills Install (or --uninstall) bundled skills
783
+ uninstall-skills Uninstall bundled skills
651
784
  context Build & print context injection string
652
- write Write to memory files
785
+ write Write to memory files (default: daily)
653
786
  read Read memory files
654
787
  scratchpad Manage checklist items
655
788
  search Search across memory files (requires qmd)
789
+ distil Generate compact MEMORY.md index from daily logs + topics
656
790
  sync Re-index and embed all files (requires qmd)
657
791
  init Initialize memory directory and qmd collection
658
792
  status Show configuration and status
@@ -663,15 +797,19 @@ Global flags:
663
797
 
664
798
  Examples:
665
799
  agent-memory init
800
+ agent-memory write --content "Fixed auth bug in login flow"
666
801
  agent-memory write --target long_term --content "User prefers dark mode"
667
- 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"
668
803
  agent-memory read --target long_term
669
804
  agent-memory read --target daily --date 2026-02-15
670
805
  agent-memory read --target list
806
+ agent-memory read --target topic --topic "auth"
807
+ agent-memory read --target topics
671
808
  agent-memory scratchpad add --text "Review PR #42"
672
809
  agent-memory scratchpad list
673
810
  agent-memory scratchpad done --text "PR #42"
674
811
  agent-memory search --query "database choice" --mode keyword
812
+ agent-memory distil --dry-run
675
813
  agent-memory context --no-search
676
814
  agent-memory sync
677
815
  agent-memory status --json`);
@@ -720,6 +858,13 @@ async function main() {
720
858
  case "install-skills":
721
859
  cmdInstallSkills(flags);
722
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;
723
868
  case "sync":
724
869
  await cmdSync(flags);
725
870
  break;