@kevin0181/memoc 1.0.4 → 1.0.6

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.
Files changed (2) hide show
  1. package/bin/cli.js +74 -19
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -208,16 +208,16 @@ function write(filePath, content) {
208
208
  fs.writeFileSync(filePath, content, 'utf8');
209
209
  }
210
210
 
211
- function tplMemocCmdWrapper() {
212
- return `@echo off\r\nnpx @kevin0181/memoc %*\r\n`;
211
+ function tplMemocCmdWrapper(cliPath = runtimeCliPath()) {
212
+ return `@echo off\r\nnode "${escapeCmdPath(cliPath)}" %*\r\n`;
213
213
  }
214
214
 
215
- function tplMemocPs1Wrapper() {
216
- return `npx @kevin0181/memoc @args\nexit $LASTEXITCODE\n`;
215
+ function tplMemocPs1Wrapper(cliPath = runtimeCliPath()) {
216
+ return `& node ${psSingleQuote(cliPath)} @args\nexit $LASTEXITCODE\n`;
217
217
  }
218
218
 
219
- function tplMemocShWrapper() {
220
- return `#!/bin/sh\nexec npx @kevin0181/memoc "$@"\n`;
219
+ function tplMemocShWrapper(cliPath = runtimeCliPath()) {
220
+ return `#!/bin/sh\nexec node ${shellSingleQuote(cliPath)} "$@"\n`;
221
221
  }
222
222
 
223
223
  function defaultUserBinDir() {
@@ -228,6 +228,18 @@ function defaultUserBinDir() {
228
228
  return path.join(process.env.HOME || process.cwd(), '.local', 'bin');
229
229
  }
230
230
 
231
+ function defaultRuntimeDir() {
232
+ if (process.env.MEMOC_RUNTIME_DIR) return process.env.MEMOC_RUNTIME_DIR;
233
+ if (currentPlatform() === 'win32') {
234
+ return path.join(process.env.LOCALAPPDATA || path.join(process.env.USERPROFILE || process.cwd(), 'AppData', 'Local'), 'memoc', 'runtime');
235
+ }
236
+ return path.join(process.env.HOME || process.cwd(), '.local', 'share', 'memoc', 'runtime');
237
+ }
238
+
239
+ function runtimeCliPath() {
240
+ return path.join(defaultRuntimeDir(), 'bin', 'cli.js');
241
+ }
242
+
231
243
  function tplEnvPs1() {
232
244
  return `$memocBin = Join-Path $PSScriptRoot 'bin'\n$parts = $env:PATH -split [IO.Path]::PathSeparator\nif ($parts -notcontains $memocBin) {\n $env:PATH = \"$memocBin$([IO.Path]::PathSeparator)$env:PATH\"\n}\n`;
233
245
  }
@@ -237,42 +249,55 @@ function tplEnvSh() {
237
249
  }
238
250
 
239
251
  function ensurePathHelpers(dir, mark) {
252
+ const cliPath = ensureRuntimeInstall(mark);
240
253
  const files = [
241
- [path.join(dir, '.memoc', 'bin', 'memoc.cmd'), tplMemocCmdWrapper, false],
242
- [path.join(dir, '.memoc', 'bin', 'memoc.ps1'), tplMemocPs1Wrapper, false],
243
- [path.join(dir, '.memoc', 'bin', 'memoc'), tplMemocShWrapper, true],
254
+ [path.join(dir, '.memoc', 'bin', 'memoc.cmd'), () => tplMemocCmdWrapper(cliPath), false],
255
+ [path.join(dir, '.memoc', 'bin', 'memoc.ps1'), () => tplMemocPs1Wrapper(cliPath), false],
256
+ [path.join(dir, '.memoc', 'bin', 'memoc'), () => tplMemocShWrapper(cliPath), true],
244
257
  [path.join(dir, '.memoc', 'env.ps1'), tplEnvPs1, false],
245
258
  [path.join(dir, '.memoc', 'env.sh'), tplEnvSh, true],
246
259
  ];
247
260
 
248
261
  for (const [fp, tpl, executable] of files) {
249
262
  const rel = path.relative(dir, fp);
250
- const added = ensure(fp, tpl());
263
+ const added = writeIfChanged(fp, tpl());
251
264
  if (executable) chmodExecutable(fp);
252
- mark(added ? 'add' : 'skip', rel);
265
+ mark(added, rel);
253
266
  }
254
267
  }
255
268
 
256
269
  function ensureUserLauncher(mark) {
257
270
  const userBin = defaultUserBinDir();
258
- writeLaunchers(userBin, mark, 'user bin');
271
+ writeLaunchers(userBin, mark, 'user bin', ensureRuntimeInstall(mark));
259
272
  return userBin;
260
273
  }
261
274
 
262
- function writeLaunchers(binDir, mark, label) {
275
+ function writeLaunchers(binDir, mark, label, cliPath = ensureRuntimeInstall(mark)) {
263
276
  const files = [
264
- [path.join(binDir, 'memoc.cmd'), tplMemocCmdWrapper, false],
265
- [path.join(binDir, 'memoc.ps1'), tplMemocPs1Wrapper, false],
266
- [path.join(binDir, 'memoc'), tplMemocShWrapper, true],
277
+ [path.join(binDir, 'memoc.cmd'), () => tplMemocCmdWrapper(cliPath), false],
278
+ [path.join(binDir, 'memoc.ps1'), () => tplMemocPs1Wrapper(cliPath), false],
279
+ [path.join(binDir, 'memoc'), () => tplMemocShWrapper(cliPath), true],
267
280
  ];
268
281
 
269
282
  for (const [fp, tpl, executable] of files) {
270
- const added = ensure(fp, tpl());
283
+ const added = writeIfChanged(fp, tpl());
271
284
  if (executable) chmodExecutable(fp);
272
- mark(added ? 'add' : 'skip', `${label} ${path.basename(fp)}`);
285
+ mark(added, `${label} ${path.basename(fp)}`);
273
286
  }
274
287
  }
275
288
 
289
+ function writeIfChanged(filePath, content) {
290
+ if (!fs.existsSync(filePath)) {
291
+ write(filePath, content);
292
+ return 'add';
293
+ }
294
+ try {
295
+ if (fs.readFileSync(filePath, 'utf8') === content) return 'skip';
296
+ } catch {}
297
+ write(filePath, content);
298
+ return 'update';
299
+ }
300
+
276
301
  function ensurePathRegistration(dir, mark) {
277
302
  ensureCurrentPathLauncher(mark);
278
303
  const binDir = ensureUserLauncher(mark);
@@ -329,10 +354,32 @@ function ensureCurrentPathLauncher(mark) {
329
354
  mark('skip', 'active PATH launcher (no writable PATH directory found)');
330
355
  return false;
331
356
  }
332
- writeLaunchers(target, mark, 'active PATH');
357
+ writeLaunchers(target, mark, 'active PATH', ensureRuntimeInstall(mark));
333
358
  return true;
334
359
  }
335
360
 
361
+ function ensureRuntimeInstall(mark) {
362
+ const runtimeDir = defaultRuntimeDir();
363
+ const sourceRoot = path.join(__dirname, '..');
364
+ const files = [
365
+ [path.join(sourceRoot, 'bin', 'cli.js'), path.join(runtimeDir, 'bin', 'cli.js')],
366
+ [path.join(sourceRoot, 'package.json'), path.join(runtimeDir, 'package.json')],
367
+ ];
368
+
369
+ for (const [src, dest] of files) {
370
+ try {
371
+ const content = fs.readFileSync(src, 'utf8');
372
+ const changed = writeIfChanged(dest, content);
373
+ mark(changed, `runtime ${path.relative(runtimeDir, dest)}`);
374
+ } catch {
375
+ mark('skip', `runtime ${path.basename(dest)} unavailable`);
376
+ }
377
+ }
378
+
379
+ chmodExecutable(path.join(runtimeDir, 'bin', 'cli.js'));
380
+ return path.join(runtimeDir, 'bin', 'cli.js');
381
+ }
382
+
336
383
  function findWritablePathDir() {
337
384
  const dirs = [...new Set((process.env.PATH || '').split(path.delimiter).filter(Boolean))];
338
385
  const npmBin = npmGlobalBinDir();
@@ -435,6 +482,14 @@ function shellSingleQuote(value) {
435
482
  return `'${String(value).replace(/'/g, `'\\''`)}'`;
436
483
  }
437
484
 
485
+ function psSingleQuote(value) {
486
+ return `'${String(value).replace(/'/g, "''")}'`;
487
+ }
488
+
489
+ function escapeCmdPath(value) {
490
+ return String(value).replace(/"/g, '""');
491
+ }
492
+
438
493
  function samePath(a, b) {
439
494
  if (!a || !b) return false;
440
495
  const norm = p => path.resolve(p).toLowerCase().replace(/[\\/]+$/, '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevin0181/memoc",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Give AI agents a memory. Scaffolds session-to-session context for Claude Code, Codex, Cursor, and more.",
5
5
  "keywords": [
6
6
  "ai",