memoir-cli 3.16.2 → 3.16.3
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/package.json +1 -1
- package/src/mcp.js +21 -8
- package/src/memory/scope.js +15 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoir-cli",
|
|
3
|
-
"version": "3.16.
|
|
3
|
+
"version": "3.16.3",
|
|
4
4
|
"mcpName": "io.github.camgitt/memoir",
|
|
5
5
|
"description": "Portable, project-scoped memory and session handoffs for coding agents. Readable files, MCP recall, and optional user-passphrase encrypted backups.",
|
|
6
6
|
"main": "src/index.js",
|
package/src/mcp.js
CHANGED
|
@@ -483,15 +483,28 @@ server.tool(
|
|
|
483
483
|
|
|
484
484
|
server.tool(
|
|
485
485
|
'memoir_session',
|
|
486
|
-
'Show the current session state — goals, next actions, open questions, recent decisions, recent sessions across machines. Use this to catch up at the start of a session, or when you need to orient yourself on what was decided.',
|
|
487
|
-
{
|
|
488
|
-
|
|
489
|
-
|
|
486
|
+
'Show the current session state — goals, next actions, open questions, recent decisions, recent sessions across machines. Use this to catch up at the start of a session, or when you need to orient yourself on what was decided. Pass project to narrow it to one project, or tag to narrow it to items whose text carries a [tag] prefix.',
|
|
487
|
+
{
|
|
488
|
+
project: z.string().optional().describe('Project directory or identity. Defaults to the current working project.'),
|
|
489
|
+
tag: z.string().optional().describe('Show only items whose text starts with a bracketed tag, e.g. "gathered" matches "[gathered — CAM] …". Case-insensitive.'),
|
|
490
|
+
},
|
|
491
|
+
async ({ project, tag }) => {
|
|
492
|
+
const state = sessionView(await readSession(), project ? { project } : {});
|
|
490
493
|
const machine = await getMachineId();
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const
|
|
494
|
-
|
|
494
|
+
// Untagged items are visible in every project by design; a [tag] prefix in
|
|
495
|
+
// the text is the only project signal many sessions actually carry.
|
|
496
|
+
const tagged = items => {
|
|
497
|
+
if (!tag) return items;
|
|
498
|
+
const want = tag.toLowerCase().replace(/^\[|\]$/g, '');
|
|
499
|
+
return items.filter(i => {
|
|
500
|
+
const m = String(i?.text || '').match(/^\s*\[([^\]]+)\]/);
|
|
501
|
+
return m && m[1].toLowerCase().includes(want);
|
|
502
|
+
});
|
|
503
|
+
};
|
|
504
|
+
const goals = tagged(state.current.goals).map(g => `- ${g.text}`).join('\n') || '(none)';
|
|
505
|
+
const nexts = tagged(state.current.next_actions).map(n => `- [ ] ${n.text}`).join('\n') || '(none)';
|
|
506
|
+
const questions = tagged(state.current.open_questions).map(q => `- ${q.text}`).join('\n') || '(none)';
|
|
507
|
+
const decisions = tagged(state.current.decisions.filter(d => visibleMemory(d, project ? { project } : {}))).slice(0, 5).map(d => {
|
|
495
508
|
let line = `- ${d.text}`;
|
|
496
509
|
if (d.why) line += ` — *${d.why}*`;
|
|
497
510
|
return line;
|
package/src/memory/scope.js
CHANGED
|
@@ -5,6 +5,14 @@ import crypto from 'crypto';
|
|
|
5
5
|
import { execFileSync } from 'child_process';
|
|
6
6
|
|
|
7
7
|
const cache = new Map();
|
|
8
|
+
|
|
9
|
+
// Records written before the home directory got an explicit key carry the hash
|
|
10
|
+
// of the empty string. Treat that as an alias for home so they stay visible.
|
|
11
|
+
const LEGACY_HOME_ID = 'local:' + crypto.createHash('sha256').update('').digest('hex').slice(0, 32);
|
|
12
|
+
export function canonicalIdentity(id) {
|
|
13
|
+
return id === LEGACY_HOME_ID ? projectIdentity(os.homedir()) : id;
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
export function projectIdentity(project = process.env.MEMOIR_PROJECT_ROOT || process.cwd()) {
|
|
9
17
|
if (project === 'shared') return 'shared';
|
|
10
18
|
if (/^(git|local):[a-f0-9]{32}$/.test(project)) return project;
|
|
@@ -14,7 +22,11 @@ export function projectIdentity(project = process.env.MEMOIR_PROJECT_ROOT || pro
|
|
|
14
22
|
if (old && Date.now() - old.at < 60_000) return old.id;
|
|
15
23
|
let home = os.homedir();
|
|
16
24
|
try { home = fs.realpathSync(home); } catch {}
|
|
17
|
-
|
|
25
|
+
// The home directory itself is a legitimate working root for people who run
|
|
26
|
+
// their agent from ~. path.relative(home, home) is '', and hashing '' gives
|
|
27
|
+
// every such user the same degenerate id (sha256 of the empty string), so
|
|
28
|
+
// name it explicitly instead.
|
|
29
|
+
let key = path.relative(home, absolute).replace(/\\/g, '/') || '~';
|
|
18
30
|
let kind = 'local';
|
|
19
31
|
try {
|
|
20
32
|
const remote = execFileSync('git', ['config', '--get', 'remote.origin.url'], { cwd: absolute, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 }).trim();
|
|
@@ -44,9 +56,9 @@ export function memoryVisibility({ project = process.env.MEMOIR_PROJECT_ROOT ||
|
|
|
44
56
|
if (item.claudeProjectKey !== currentKey && item.claudeProjectKey !== sharedKey) return false;
|
|
45
57
|
}
|
|
46
58
|
if (allProjects || !item.project || item.project === 'shared') return true;
|
|
47
|
-
activeId ??= projectIdentity(project);
|
|
59
|
+
activeId ??= canonicalIdentity(projectIdentity(project));
|
|
48
60
|
const key = String(item.project);
|
|
49
|
-
if (!identities.has(key)) identities.set(key, projectIdentity(key));
|
|
61
|
+
if (!identities.has(key)) identities.set(key, canonicalIdentity(projectIdentity(key)));
|
|
50
62
|
return identities.get(key) === activeId;
|
|
51
63
|
};
|
|
52
64
|
}
|