memoir-cli 3.16.0 → 3.16.2
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 +1 -1
- package/bin/memoir.js +8 -34
- package/package.json +1 -1
- package/src/commands/update.js +228 -0
- package/src/session/inject.js +5 -2
package/README.md
CHANGED
|
@@ -144,7 +144,7 @@ Common secret filenames and detected patterns are omitted and listed in the mani
|
|
|
144
144
|
| Surface | Capability | Verification boundary |
|
|
145
145
|
|---|---|---|
|
|
146
146
|
| Claude Code | Project MCP setup, Memoir tools, instruction import/export | JSON preservation and real server startup tested |
|
|
147
|
-
| Codex | Project TOML MCP setup, Memoir tools, AGENTS.md import/export | TOML round trip and server startup tested |
|
|
147
|
+
| Codex | Project TOML MCP setup, Memoir tools, AGENTS.md import/export, pinned session block in `~/.codex/AGENTS.md` | TOML round trip and server startup tested |
|
|
148
148
|
| Cursor | Project MCP setup, Memoir tools, rule import/export | JSON preservation and server startup tested |
|
|
149
149
|
| Other existing adapters | Selected memory/config import/export | Adapter fixtures, not full native session continuity |
|
|
150
150
|
| Generic MCP client | Memoir tool contract | Real stdio tests |
|
package/bin/memoir.js
CHANGED
|
@@ -45,6 +45,7 @@ import { whyCommand } from '../src/commands/why.js';
|
|
|
45
45
|
import { forgetCommand } from '../src/commands/forget.js';
|
|
46
46
|
import { recallCommand } from '../src/commands/recall.js';
|
|
47
47
|
import { autoRefreshCommand } from '../src/commands/auto-refresh.js';
|
|
48
|
+
import { updateCommand } from '../src/commands/update.js';
|
|
48
49
|
import { validateCommand } from '../src/commands/validate.js';
|
|
49
50
|
import { hooksInstallCommand, hooksUninstallCommand, hooksStatusCommand } from '../src/commands/hooks.js';
|
|
50
51
|
import { capture as track, telemetryCommand } from '../src/telemetry.js';
|
|
@@ -395,43 +396,16 @@ program
|
|
|
395
396
|
|
|
396
397
|
program
|
|
397
398
|
.command('update')
|
|
398
|
-
.description('Update memoir
|
|
399
|
-
.
|
|
399
|
+
.description('Update the copy of memoir that is running (npm global, bun global, or a project-local install)')
|
|
400
|
+
.option('--dry-run', 'Show which install would be updated and the exact command, without running it')
|
|
401
|
+
.action(async (options) => {
|
|
400
402
|
try {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const latest = data.version;
|
|
404
|
-
|
|
405
|
-
const currentParts = VERSION.split('.').map(Number);
|
|
406
|
-
const latestParts = String(latest).split('.').map(Number);
|
|
407
|
-
const newer = latestParts.some((n, i) => n > currentParts[i] && latestParts.slice(0, i).every((v, j) => v === currentParts[j]));
|
|
408
|
-
if (!newer) {
|
|
409
|
-
console.log('\n' + boxen(
|
|
410
|
-
chalk.green('✔ Already up to date!') + '\n' +
|
|
411
|
-
chalk.gray(`v${VERSION}`),
|
|
412
|
-
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderStyle: 'round', borderColor: 'green', dimBorder: true }
|
|
413
|
-
) + '\n');
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
console.log('\n' + chalk.cyan(`Updating memoir ${VERSION} → ${chalk.green.bold(latest)}...`) + '\n');
|
|
418
|
-
|
|
419
|
-
const { execSync } = await import('child_process');
|
|
420
|
-
// Always use npm — bun installs to a different location and can cause PATH conflicts
|
|
421
|
-
const cmd = 'npm install -g memoir-cli';
|
|
422
|
-
|
|
423
|
-
execSync(cmd, { stdio: 'inherit' });
|
|
424
|
-
|
|
425
|
-
console.log('\n' + boxen(
|
|
426
|
-
gradient.pastel(' Updated! ') + '\n\n' +
|
|
427
|
-
chalk.white(`memoir ${VERSION} → ${chalk.green.bold(latest)}`),
|
|
428
|
-
{ padding: 1, borderStyle: 'round', borderColor: 'green', dimBorder: true }
|
|
429
|
-
) + '\n');
|
|
430
|
-
process.exit(0); // Exit immediately — old process still has old VERSION
|
|
403
|
+
await updateCommand({ dryRun: options.dryRun }, { currentVersion: VERSION });
|
|
404
|
+
process.exit(0); // Exit immediately — this process still holds the old VERSION
|
|
431
405
|
} catch (err) {
|
|
432
406
|
console.error(chalk.red('\n✖ Update failed:'), err.message);
|
|
433
|
-
console.log(chalk.gray('
|
|
434
|
-
process.exit(1);
|
|
407
|
+
console.log(chalk.gray('See which copy is running: ') + chalk.cyan('memoir update --dry-run'));
|
|
408
|
+
process.exit(err.exitCode || 1);
|
|
435
409
|
}
|
|
436
410
|
});
|
|
437
411
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoir-cli",
|
|
3
|
-
"version": "3.16.
|
|
3
|
+
"version": "3.16.2",
|
|
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",
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// memoir update — upgrade the copy of memoir that is actually running.
|
|
2
|
+
//
|
|
3
|
+
// Before this module, `memoir update` ran `npm install -g memoir-cli`
|
|
4
|
+
// unconditionally and printed "Updated!" from the registry version. On a
|
|
5
|
+
// machine with more than one install (a project-local copy first on PATH, a
|
|
6
|
+
// Homebrew/npm global second, a bun global) that upgraded a copy the shell
|
|
7
|
+
// never runs, the running copy kept showing the update banner, and the
|
|
8
|
+
// Claude Code hooks kept executing the old version.
|
|
9
|
+
//
|
|
10
|
+
// Now: resolve the running install from the executing script, classify it,
|
|
11
|
+
// upgrade that install with the matching package manager, verify the version
|
|
12
|
+
// on disk afterwards, and say plainly when another copy on PATH shadows it.
|
|
13
|
+
|
|
14
|
+
import fs from 'fs';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import os from 'os';
|
|
17
|
+
import { execFileSync } from 'child_process';
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
import boxen from 'boxen';
|
|
20
|
+
|
|
21
|
+
export const PACKAGE_NAME = 'memoir-cli';
|
|
22
|
+
|
|
23
|
+
// Numeric semver compare. Non-numeric parts (tags, garbage) compare as 0 so a
|
|
24
|
+
// malformed registry answer never triggers an "update".
|
|
25
|
+
export function isNewer(candidate, current) {
|
|
26
|
+
const parse = v => String(v || '').split('.').slice(0, 3).map(n => { const x = parseInt(n, 10); return Number.isFinite(x) ? x : 0; });
|
|
27
|
+
const [a1, a2, a3] = parse(candidate);
|
|
28
|
+
const [b1, b2, b3] = parse(current);
|
|
29
|
+
return a1 > b1 || (a1 === b1 && a2 > b2) || (a1 === b1 && a2 === b2 && a3 > b3);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readJson(file) {
|
|
33
|
+
try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch { return null; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function readInstalledVersion(root) {
|
|
37
|
+
const pkg = root ? readJson(path.join(root, 'package.json')) : null;
|
|
38
|
+
return pkg && pkg.name === PACKAGE_NAME ? String(pkg.version || '') : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Walk up from a script (bin/memoir.js, src/mcp.js …) to the memoir-cli
|
|
42
|
+
// package directory. Follows symlinks first so `~/.bun/bin/memoir` resolves
|
|
43
|
+
// to the package it points at, not the shim directory.
|
|
44
|
+
export function findPackageRoot(startPath) {
|
|
45
|
+
let real;
|
|
46
|
+
try { real = fs.realpathSync(startPath); } catch { return null; }
|
|
47
|
+
let dir = fs.existsSync(real) && fs.statSync(real).isDirectory() ? real : path.dirname(real);
|
|
48
|
+
for (;;) {
|
|
49
|
+
const pkg = readJson(path.join(dir, 'package.json'));
|
|
50
|
+
if (pkg && pkg.name === PACKAGE_NAME) return dir;
|
|
51
|
+
const parent = path.dirname(dir);
|
|
52
|
+
if (parent === dir) return null;
|
|
53
|
+
dir = parent;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function samePath(a, b) {
|
|
58
|
+
const norm = p => { try { return fs.realpathSync(p); } catch { return path.resolve(p); } };
|
|
59
|
+
return norm(a) === norm(b);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Where is this install, and who owns it?
|
|
63
|
+
// source — a git checkout (`.git` present): upgrading means `git pull`
|
|
64
|
+
// npm-global — `<npm root -g>/memoir-cli`
|
|
65
|
+
// bun-global — under bun's global node_modules
|
|
66
|
+
// local — `<project>/node_modules/memoir-cli` (a dependency of <project>)
|
|
67
|
+
// unknown — anything else; fall back to npm -g with a warning
|
|
68
|
+
export function classifyInstall(root, { npmGlobalRoot = null, bunGlobalRoot = null } = {}) {
|
|
69
|
+
if (!root) return { kind: 'unknown', root: null };
|
|
70
|
+
root = path.resolve(root);
|
|
71
|
+
if (fs.existsSync(path.join(root, '.git'))) return { kind: 'source', root };
|
|
72
|
+
if (npmGlobalRoot && samePath(root, path.join(npmGlobalRoot, PACKAGE_NAME))) return { kind: 'npm-global', root };
|
|
73
|
+
if (bunGlobalRoot && (samePath(root, path.join(bunGlobalRoot, PACKAGE_NAME)) || root.startsWith(path.resolve(bunGlobalRoot) + path.sep))) return { kind: 'bun-global', root };
|
|
74
|
+
const parent = path.dirname(root);
|
|
75
|
+
if (path.basename(parent) === 'node_modules') {
|
|
76
|
+
const project = path.dirname(parent);
|
|
77
|
+
return { kind: 'local', root, project, hasManifest: fs.existsSync(path.join(project, 'package.json')) };
|
|
78
|
+
}
|
|
79
|
+
return { kind: 'unknown', root };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// The exact command that upgrades *this* install. `source` has no command:
|
|
83
|
+
// the right move is a git pull in the checkout, which we never run for the user.
|
|
84
|
+
export function planUpdate(install, latest) {
|
|
85
|
+
const spec = `${PACKAGE_NAME}@${latest}`;
|
|
86
|
+
switch (install.kind) {
|
|
87
|
+
case 'source':
|
|
88
|
+
return { kind: install.kind, command: null, args: [], cwd: install.root, description: `running from a source checkout at ${install.root}; run \`git pull\` there` };
|
|
89
|
+
case 'npm-global':
|
|
90
|
+
return { kind: install.kind, command: 'npm', args: ['install', '-g', spec], cwd: undefined, description: `npm global install at ${install.root}` };
|
|
91
|
+
case 'bun-global':
|
|
92
|
+
return { kind: install.kind, command: 'bun', args: ['add', '-g', spec], cwd: undefined, description: `bun global install at ${install.root}` };
|
|
93
|
+
case 'local': {
|
|
94
|
+
// A dependency of <project>/package.json: update it there so the
|
|
95
|
+
// manifest, the lockfile and the copy on disk agree. Without a manifest
|
|
96
|
+
// there is nothing to record, so do not create one.
|
|
97
|
+
const args = ['install', spec, '--no-audit', '--no-fund'];
|
|
98
|
+
if (!install.hasManifest) args.push('--no-save');
|
|
99
|
+
return { kind: install.kind, command: 'npm', args, cwd: install.project, description: `project-local install at ${install.root} (dependency of ${install.project})` };
|
|
100
|
+
}
|
|
101
|
+
default:
|
|
102
|
+
return { kind: 'unknown', command: 'npm', args: ['install', '-g', spec], cwd: undefined, description: install.root ? `unrecognised install layout at ${install.root}; trying npm -g` : 'could not locate the running install; trying npm -g' };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Every `memoir` executable on PATH, in shell resolution order, mapped to the
|
|
107
|
+
// package it runs. The first entry is what the shell executes for `memoir`.
|
|
108
|
+
export function listPathInstalls({ PATH = process.env.PATH || '', bin = 'memoir' } = {}) {
|
|
109
|
+
const out = [];
|
|
110
|
+
const seen = new Set();
|
|
111
|
+
for (const dir of PATH.split(path.delimiter).filter(Boolean)) {
|
|
112
|
+
const candidate = path.join(dir, bin);
|
|
113
|
+
let stat;
|
|
114
|
+
try { stat = fs.statSync(candidate); } catch { continue; }
|
|
115
|
+
if (!stat.isFile()) continue;
|
|
116
|
+
const root = findPackageRoot(candidate);
|
|
117
|
+
const key = root || candidate;
|
|
118
|
+
if (seen.has(key)) continue;
|
|
119
|
+
seen.add(key);
|
|
120
|
+
out.push({ bin: candidate, root, version: readInstalledVersion(root) });
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Plain-language warnings when the shell would run a different copy than the
|
|
126
|
+
// one we just updated (or when other copies exist and will drift).
|
|
127
|
+
export function shadowWarnings(runningRoot, installs) {
|
|
128
|
+
const warnings = [];
|
|
129
|
+
if (!installs.length) return warnings;
|
|
130
|
+
const first = installs[0];
|
|
131
|
+
const same = (a, b) => a && b && samePath(a, b);
|
|
132
|
+
if (first.root && !same(first.root, runningRoot)) {
|
|
133
|
+
warnings.push(`Your shell runs \`memoir\` from ${first.bin} (v${first.version || '?'}), not the copy just updated. Remove that one or update it too, or the old version keeps running.`);
|
|
134
|
+
}
|
|
135
|
+
const others = installs.filter(i => i.root && !same(i.root, runningRoot) && i !== first);
|
|
136
|
+
for (const other of others) warnings.push(`Another copy is on PATH at ${other.bin} (v${other.version || '?'}); it was not updated.`);
|
|
137
|
+
return warnings;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function probeNpmGlobalRoot() {
|
|
141
|
+
try { return execFileSync('npm', ['root', '-g'], { encoding: 'utf8', timeout: 15000, stdio: ['ignore', 'pipe', 'ignore'] }).trim() || null; }
|
|
142
|
+
catch { return null; }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function defaultBunGlobalRoot() {
|
|
146
|
+
const bunHome = process.env.BUN_INSTALL || path.join(os.homedir(), '.bun');
|
|
147
|
+
return path.join(bunHome, 'install', 'global', 'node_modules');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function fetchLatestFromRegistry() {
|
|
151
|
+
const controller = new AbortController();
|
|
152
|
+
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
153
|
+
try {
|
|
154
|
+
const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { signal: controller.signal });
|
|
155
|
+
const data = await res.json();
|
|
156
|
+
return String(data.version || '');
|
|
157
|
+
} finally { clearTimeout(timeout); }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Resolve the running install once; exported so `doctor`/tests can reuse it.
|
|
161
|
+
export function resolveRunningInstall({ scriptPath = process.argv[1], npmGlobalRoot, bunGlobalRoot } = {}) {
|
|
162
|
+
const root = findPackageRoot(scriptPath);
|
|
163
|
+
return classifyInstall(root, {
|
|
164
|
+
npmGlobalRoot: npmGlobalRoot === undefined ? probeNpmGlobalRoot() : npmGlobalRoot,
|
|
165
|
+
bunGlobalRoot: bunGlobalRoot === undefined ? defaultBunGlobalRoot() : bunGlobalRoot,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function updateCommand(options = {}, deps = {}) {
|
|
170
|
+
const { dryRun = false } = options;
|
|
171
|
+
const {
|
|
172
|
+
currentVersion,
|
|
173
|
+
scriptPath = process.argv[1],
|
|
174
|
+
fetchLatest = fetchLatestFromRegistry,
|
|
175
|
+
run = (cmd, args, cwd) => execFileSync(cmd, args, { stdio: 'inherit', cwd }),
|
|
176
|
+
PATH = process.env.PATH,
|
|
177
|
+
log = console.log,
|
|
178
|
+
} = deps;
|
|
179
|
+
|
|
180
|
+
const latest = process.env.MEMOIR_UPDATE_LATEST || await fetchLatest();
|
|
181
|
+
if (!latest) throw new Error('could not read the latest version from the npm registry');
|
|
182
|
+
|
|
183
|
+
const install = resolveRunningInstall({ scriptPath, npmGlobalRoot: deps.npmGlobalRoot, bunGlobalRoot: deps.bunGlobalRoot });
|
|
184
|
+
const installs = listPathInstalls({ PATH });
|
|
185
|
+
const warnings = shadowWarnings(install.root, installs);
|
|
186
|
+
|
|
187
|
+
if (!isNewer(latest, currentVersion)) {
|
|
188
|
+
log('\n' + boxen(chalk.green('✔ Already up to date!') + '\n' + chalk.gray(`v${currentVersion} · ${install.root || 'install location unknown'}`),
|
|
189
|
+
{ padding: { top: 0, bottom: 0, left: 1, right: 1 }, borderStyle: 'round', borderColor: 'green', dimBorder: true }) + '\n');
|
|
190
|
+
for (const w of warnings) log(chalk.yellow('⚠ ') + w);
|
|
191
|
+
return { updated: false, install, latest, warnings };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const plan = planUpdate(install, latest);
|
|
195
|
+
log('\n' + chalk.cyan(`Updating memoir ${currentVersion} → ${chalk.green.bold(latest)}`) + chalk.gray(` — ${plan.description}`) + '\n');
|
|
196
|
+
|
|
197
|
+
if (plan.kind === 'source') {
|
|
198
|
+
log(chalk.yellow('This copy is a git checkout; memoir will not pull it for you.') + '\n' + chalk.gray('Run: ') + chalk.cyan(`git -C ${plan.cwd} pull`) + '\n');
|
|
199
|
+
for (const w of warnings) log(chalk.yellow('⚠ ') + w);
|
|
200
|
+
return { updated: false, install, latest, plan, warnings };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const commandLine = [plan.command, ...plan.args].join(' ') + (plan.cwd ? chalk.gray(` (in ${plan.cwd})`) : '');
|
|
204
|
+
if (dryRun) {
|
|
205
|
+
log(chalk.gray('Would run: ') + chalk.cyan(commandLine) + '\n');
|
|
206
|
+
for (const w of warnings) log(chalk.yellow('⚠ ') + w);
|
|
207
|
+
return { updated: false, dryRun: true, install, latest, plan, warnings };
|
|
208
|
+
}
|
|
209
|
+
if (plan.kind === 'unknown') log(chalk.yellow('⚠ ') + plan.description);
|
|
210
|
+
|
|
211
|
+
await run(plan.command, plan.args, plan.cwd);
|
|
212
|
+
|
|
213
|
+
const now = install.root ? readInstalledVersion(install.root) : null;
|
|
214
|
+
const ok = now === latest || (install.root == null && plan.kind === 'unknown');
|
|
215
|
+
if (!ok) {
|
|
216
|
+
log('\n' + chalk.red('✖ The install did not change: ') + chalk.white(`${install.root} still reports v${now || '?'}`) + '\n' +
|
|
217
|
+
chalk.gray('Try manually: ') + chalk.cyan(commandLine) + '\n');
|
|
218
|
+
for (const w of warnings) log(chalk.yellow('⚠ ') + w);
|
|
219
|
+
const err = new Error(`update ran but ${install.root} still reports v${now || '?'}`);
|
|
220
|
+
err.exitCode = 1;
|
|
221
|
+
throw err;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
log('\n' + boxen(chalk.white(`memoir ${currentVersion} → ${chalk.green.bold(latest)}`) + '\n' + chalk.gray(install.root || ''),
|
|
225
|
+
{ padding: 1, borderStyle: 'round', borderColor: 'green', dimBorder: true }) + '\n');
|
|
226
|
+
for (const w of warnings) log(chalk.yellow('⚠ ') + w);
|
|
227
|
+
return { updated: true, install, latest, plan, warnings };
|
|
228
|
+
}
|
package/src/session/inject.js
CHANGED
|
@@ -28,6 +28,7 @@ const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
|
28
28
|
// Cursor: ~/.cursor/rules/memoir-session.mdc (global user rules — auto-loaded)
|
|
29
29
|
// Windsurf: {AppSupport}/Windsurf/User/memoir-session.md (user-global instructions)
|
|
30
30
|
// Gemini: ~/.gemini/GEMINI.md (user-global)
|
|
31
|
+
// Codex: ~/.codex/AGENTS.md (user-global; Codex CLI reads it before any repo AGENTS.md)
|
|
31
32
|
export const INJECTION_TARGETS = {
|
|
32
33
|
claude: path.join(home, '.claude', 'CLAUDE.md'),
|
|
33
34
|
cursor: path.join(home, '.cursor', 'rules', 'memoir-session.mdc'),
|
|
@@ -35,6 +36,7 @@ export const INJECTION_TARGETS = {
|
|
|
35
36
|
? path.join(appData, 'Windsurf', 'User', 'memoir-session.md')
|
|
36
37
|
: path.join(home, 'Library', 'Application Support', 'Windsurf', 'User', 'memoir-session.md'),
|
|
37
38
|
gemini: path.join(home, '.gemini', 'GEMINI.md'),
|
|
39
|
+
codex: path.join(home, '.codex', 'AGENTS.md'),
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
// Returns the target paths whose parent infrastructure exists — i.e. the tool
|
|
@@ -48,6 +50,7 @@ export function detectAvailableTargets() {
|
|
|
48
50
|
? path.join(appData, 'Windsurf', 'User')
|
|
49
51
|
: path.join(home, 'Library', 'Application Support', 'Windsurf', 'User'),
|
|
50
52
|
gemini: path.join(home, '.gemini'),
|
|
53
|
+
codex: path.join(home, '.codex'),
|
|
51
54
|
};
|
|
52
55
|
const available = {};
|
|
53
56
|
for (const [name, dir] of Object.entries(detectors)) {
|
|
@@ -83,13 +86,13 @@ export async function injectInto(targetPath, renderedBlock) {
|
|
|
83
86
|
await writeSafeFile(path.dirname(targetPath), path.basename(targetPath), updated);
|
|
84
87
|
|
|
85
88
|
// One event per target (this function is called once per detected tool —
|
|
86
|
-
// up to ~
|
|
89
|
+
// up to ~5x for a single session update). Deliberate: each call here IS a
|
|
87
90
|
// successful write to one specific target file, and the payload is tiny
|
|
88
91
|
// (just the filename, no content), so per-tool visibility is worth the 4x
|
|
89
92
|
// over collapsing to one event per "session update." injectInto() only
|
|
90
93
|
// receives a raw path (callers loop over detectAvailableTargets() by
|
|
91
94
|
// value, discarding the tool-name key), so the filename itself
|
|
92
|
-
// (CLAUDE.md / memoir-session.mdc / memoir-session.md / GEMINI.md) is
|
|
95
|
+
// (CLAUDE.md / memoir-session.mdc / memoir-session.md / GEMINI.md / AGENTS.md) is
|
|
93
96
|
// what's actually available here without a larger refactor.
|
|
94
97
|
await appendEvent('memory_written', { target: path.basename(targetPath) });
|
|
95
98
|
|