Skip to content

Commit 75af3e8

Browse files
committed
feat(memory): # quick-add memory from the input line
Typing `# always run the linter` (or `#feedback: …` to pick the bucket) saves a memory without spending an agent turn — bypasses the busy queue since it's a local write. Mirrors Claude Code's # shortcut.
1 parent 77fa837 commit 75af3e8

2 files changed

Lines changed: 76 additions & 0 deletions

File tree

src/memory/quick-add.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { mkdtempSync, rmSync } from "node:fs";
2+
import { tmpdir } from "node:os";
3+
import { join } from "node:path";
4+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
5+
import { quickAddMemory } from "./quick-add.js";
6+
import { MemoryStore } from "./store.js";
7+
8+
describe("quickAddMemory", () => {
9+
let cwd: string;
10+
let store: MemoryStore;
11+
12+
beforeEach(() => {
13+
cwd = mkdtempSync(join(tmpdir(), "quickmem-"));
14+
store = new MemoryStore({ cwd });
15+
});
16+
afterEach(() => {
17+
rmSync(cwd, { recursive: true, force: true });
18+
});
19+
20+
it("saves a user memory from a bare # line", () => {
21+
const rec = quickAddMemory(store, "# always run the linter before committing");
22+
expect(rec.type).toBe("user");
23+
expect(rec.body).toBe("always run the linter before committing");
24+
expect(store.list()).toHaveLength(1);
25+
});
26+
27+
it("honors a #<type>: prefix", () => {
28+
const rec = quickAddMemory(store, "#feedback: prefer small focused commits");
29+
expect(rec.type).toBe("feedback");
30+
expect(rec.body).toBe("prefer small focused commits");
31+
});
32+
33+
it("derives a kebab slug filename and a clipped name", () => {
34+
const long = `# ${"word ".repeat(30)}`;
35+
const rec = quickAddMemory(store, long);
36+
expect(rec.filename).toMatch(/\.md$/);
37+
expect(rec.name.endsWith("…")).toBe(true);
38+
});
39+
40+
it("two quick-adds don't collide on filename", () => {
41+
quickAddMemory(store, "# note one");
42+
quickAddMemory(store, "# note two");
43+
expect(store.list()).toHaveLength(2);
44+
});
45+
});

src/memory/quick-add.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { MemoryStore } from "./store.js";
2+
import type { MemoryRecord, MemoryType } from "./types.js";
3+
4+
/**
5+
* Quick-add a memory from a `#`-prefixed input line. The user types
6+
* `# always run the linter before committing` and it lands as a memory
7+
* file without spending an agent turn. An optional `#<type>:` prefix
8+
* picks the bucket (`#feedback: …`, `#project: …`); default is `user`.
9+
*/
10+
11+
const TYPE_PREFIX = /^#\s*(user|feedback|project|reference)\s*:\s*/i;
12+
13+
export function quickAddMemory(store: MemoryStore, raw: string): MemoryRecord {
14+
const body = raw.replace(/^#/, "").trim();
15+
let type: MemoryType = "user";
16+
let text = body;
17+
const typed = raw.match(TYPE_PREFIX);
18+
if (typed) {
19+
type = typed[1].toLowerCase() as MemoryType;
20+
text = raw.slice(typed[0].length).trim();
21+
}
22+
const name = text.length > 60 ? `${text.slice(0, 57)}…` : text;
23+
const slug =
24+
text
25+
.toLowerCase()
26+
.replace(/[^a-z0-9]+/g, "-")
27+
.replace(/^-+|-+$/g, "")
28+
.slice(0, 40) || "note";
29+
const filename = `${slug}-${Date.now().toString(36)}.md`;
30+
return store.save({ filename, name, description: name, type, body: text });
31+
}

0 commit comments

Comments
 (0)