@kevin0181/memoc 1.0.3 → 1.0.4
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/cli.js +79 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npx @kevin0181/memoc init
|
|
|
12
12
|
|
|
13
13
|
Run inside your project directory. Detects your stack automatically and generates everything agents need.
|
|
14
14
|
|
|
15
|
-
`init` also creates PATH helpers so agents can keep using memoc even when the global/npm bin is not on PATH. It installs a
|
|
15
|
+
`init` also creates PATH helpers so agents can keep using memoc even when the global/npm bin is not on PATH. It installs a `memoc` launcher into a writable directory already on the current PATH when possible, then also installs a user-local launcher and registers that launcher directory on Windows, macOS, and Linux.
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
# PowerShell
|
package/bin/cli.js
CHANGED
|
@@ -255,22 +255,26 @@ function ensurePathHelpers(dir, mark) {
|
|
|
255
255
|
|
|
256
256
|
function ensureUserLauncher(mark) {
|
|
257
257
|
const userBin = defaultUserBinDir();
|
|
258
|
+
writeLaunchers(userBin, mark, 'user bin');
|
|
259
|
+
return userBin;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function writeLaunchers(binDir, mark, label) {
|
|
258
263
|
const files = [
|
|
259
|
-
[path.join(
|
|
260
|
-
[path.join(
|
|
261
|
-
[path.join(
|
|
264
|
+
[path.join(binDir, 'memoc.cmd'), tplMemocCmdWrapper, false],
|
|
265
|
+
[path.join(binDir, 'memoc.ps1'), tplMemocPs1Wrapper, false],
|
|
266
|
+
[path.join(binDir, 'memoc'), tplMemocShWrapper, true],
|
|
262
267
|
];
|
|
263
268
|
|
|
264
269
|
for (const [fp, tpl, executable] of files) {
|
|
265
270
|
const added = ensure(fp, tpl());
|
|
266
271
|
if (executable) chmodExecutable(fp);
|
|
267
|
-
mark(added ? 'add' : 'skip',
|
|
272
|
+
mark(added ? 'add' : 'skip', `${label} ${path.basename(fp)}`);
|
|
268
273
|
}
|
|
269
|
-
|
|
270
|
-
return userBin;
|
|
271
274
|
}
|
|
272
275
|
|
|
273
276
|
function ensurePathRegistration(dir, mark) {
|
|
277
|
+
ensureCurrentPathLauncher(mark);
|
|
274
278
|
const binDir = ensureUserLauncher(mark);
|
|
275
279
|
const pathSep = path.delimiter;
|
|
276
280
|
|
|
@@ -319,6 +323,75 @@ function ensurePathRegistration(dir, mark) {
|
|
|
319
323
|
}
|
|
320
324
|
}
|
|
321
325
|
|
|
326
|
+
function ensureCurrentPathLauncher(mark) {
|
|
327
|
+
const target = findWritablePathDir();
|
|
328
|
+
if (!target) {
|
|
329
|
+
mark('skip', 'active PATH launcher (no writable PATH directory found)');
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
writeLaunchers(target, mark, 'active PATH');
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function findWritablePathDir() {
|
|
337
|
+
const dirs = [...new Set((process.env.PATH || '').split(path.delimiter).filter(Boolean))];
|
|
338
|
+
const npmBin = npmGlobalBinDir();
|
|
339
|
+
const ranked = dirs
|
|
340
|
+
.filter(d => !isVolatilePathDir(d))
|
|
341
|
+
.filter(d => {
|
|
342
|
+
try { return fs.existsSync(d) && fs.statSync(d).isDirectory() && canWriteDir(d); }
|
|
343
|
+
catch { return false; }
|
|
344
|
+
})
|
|
345
|
+
.sort((a, b) => pathRank(a, npmBin) - pathRank(b, npmBin));
|
|
346
|
+
return ranked[0] || null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function pathRank(dir, npmBin) {
|
|
350
|
+
if (npmBin && samePath(dir, npmBin)) return 0;
|
|
351
|
+
const lower = dir.toLowerCase();
|
|
352
|
+
for (const root of userWritableRoots()) {
|
|
353
|
+
if (root && lower.startsWith(root.toLowerCase())) return 1;
|
|
354
|
+
}
|
|
355
|
+
return 5;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function userWritableRoots() {
|
|
359
|
+
return [
|
|
360
|
+
process.env.APPDATA,
|
|
361
|
+
process.env.LOCALAPPDATA,
|
|
362
|
+
process.env.HOME,
|
|
363
|
+
process.env.USERPROFILE,
|
|
364
|
+
].filter(Boolean).map(p => path.resolve(p));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function npmGlobalBinDir() {
|
|
368
|
+
try {
|
|
369
|
+
const prefix = require('child_process').execFileSync('npm', ['config', 'get', 'prefix'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
370
|
+
if (!prefix) return null;
|
|
371
|
+
return currentPlatform() === 'win32' ? prefix : path.join(prefix, 'bin');
|
|
372
|
+
} catch {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function isVolatilePathDir(dir) {
|
|
378
|
+
const lower = dir.toLowerCase();
|
|
379
|
+
return lower.includes(`${path.sep}_npx${path.sep}`) ||
|
|
380
|
+
lower.includes(`${path.sep}node_modules${path.sep}.bin`) ||
|
|
381
|
+
lower.includes(`${path.sep}npm-cache${path.sep}_npx${path.sep}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function canWriteDir(dir) {
|
|
385
|
+
const probe = path.join(dir, `.memoc-write-test-${process.pid}-${Date.now()}`);
|
|
386
|
+
try {
|
|
387
|
+
fs.writeFileSync(probe, '');
|
|
388
|
+
fs.unlinkSync(probe);
|
|
389
|
+
return true;
|
|
390
|
+
} catch {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
322
395
|
function ensureUnixPathRegistration(binDir) {
|
|
323
396
|
if (process.env.MEMOC_SKIP_PATH_REGISTER === '1') return false;
|
|
324
397
|
|