@kevin0181/memoc 1.0.3 → 1.0.5
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 +96 -11
- 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
|
@@ -209,15 +209,15 @@ function write(filePath, content) {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
function tplMemocCmdWrapper() {
|
|
212
|
-
return `@echo off\r\
|
|
212
|
+
return `@echo off\r\nnpm exec --yes --package "@kevin0181/memoc" -- memoc %*\r\n`;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
function tplMemocPs1Wrapper() {
|
|
216
|
-
return `
|
|
216
|
+
return `npm exec --yes --package '@kevin0181/memoc' -- memoc @args\nexit $LASTEXITCODE\n`;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
function tplMemocShWrapper() {
|
|
220
|
-
return `#!/bin/sh\nexec
|
|
220
|
+
return `#!/bin/sh\nexec npm exec --yes --package '@kevin0181/memoc' -- memoc "$@"\n`;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
function defaultUserBinDir() {
|
|
@@ -247,30 +247,46 @@ function ensurePathHelpers(dir, mark) {
|
|
|
247
247
|
|
|
248
248
|
for (const [fp, tpl, executable] of files) {
|
|
249
249
|
const rel = path.relative(dir, fp);
|
|
250
|
-
const added =
|
|
250
|
+
const added = writeIfChanged(fp, tpl());
|
|
251
251
|
if (executable) chmodExecutable(fp);
|
|
252
|
-
mark(added
|
|
252
|
+
mark(added, rel);
|
|
253
253
|
}
|
|
254
254
|
}
|
|
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
|
-
const added =
|
|
270
|
+
const added = writeIfChanged(fp, tpl());
|
|
266
271
|
if (executable) chmodExecutable(fp);
|
|
267
|
-
mark(added
|
|
272
|
+
mark(added, `${label} ${path.basename(fp)}`);
|
|
268
273
|
}
|
|
274
|
+
}
|
|
269
275
|
|
|
270
|
-
|
|
276
|
+
function writeIfChanged(filePath, content) {
|
|
277
|
+
if (!fs.existsSync(filePath)) {
|
|
278
|
+
write(filePath, content);
|
|
279
|
+
return 'add';
|
|
280
|
+
}
|
|
281
|
+
try {
|
|
282
|
+
if (fs.readFileSync(filePath, 'utf8') === content) return 'skip';
|
|
283
|
+
} catch {}
|
|
284
|
+
write(filePath, content);
|
|
285
|
+
return 'update';
|
|
271
286
|
}
|
|
272
287
|
|
|
273
288
|
function ensurePathRegistration(dir, mark) {
|
|
289
|
+
ensureCurrentPathLauncher(mark);
|
|
274
290
|
const binDir = ensureUserLauncher(mark);
|
|
275
291
|
const pathSep = path.delimiter;
|
|
276
292
|
|
|
@@ -319,6 +335,75 @@ function ensurePathRegistration(dir, mark) {
|
|
|
319
335
|
}
|
|
320
336
|
}
|
|
321
337
|
|
|
338
|
+
function ensureCurrentPathLauncher(mark) {
|
|
339
|
+
const target = findWritablePathDir();
|
|
340
|
+
if (!target) {
|
|
341
|
+
mark('skip', 'active PATH launcher (no writable PATH directory found)');
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
writeLaunchers(target, mark, 'active PATH');
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function findWritablePathDir() {
|
|
349
|
+
const dirs = [...new Set((process.env.PATH || '').split(path.delimiter).filter(Boolean))];
|
|
350
|
+
const npmBin = npmGlobalBinDir();
|
|
351
|
+
const ranked = dirs
|
|
352
|
+
.filter(d => !isVolatilePathDir(d))
|
|
353
|
+
.filter(d => {
|
|
354
|
+
try { return fs.existsSync(d) && fs.statSync(d).isDirectory() && canWriteDir(d); }
|
|
355
|
+
catch { return false; }
|
|
356
|
+
})
|
|
357
|
+
.sort((a, b) => pathRank(a, npmBin) - pathRank(b, npmBin));
|
|
358
|
+
return ranked[0] || null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function pathRank(dir, npmBin) {
|
|
362
|
+
if (npmBin && samePath(dir, npmBin)) return 0;
|
|
363
|
+
const lower = dir.toLowerCase();
|
|
364
|
+
for (const root of userWritableRoots()) {
|
|
365
|
+
if (root && lower.startsWith(root.toLowerCase())) return 1;
|
|
366
|
+
}
|
|
367
|
+
return 5;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function userWritableRoots() {
|
|
371
|
+
return [
|
|
372
|
+
process.env.APPDATA,
|
|
373
|
+
process.env.LOCALAPPDATA,
|
|
374
|
+
process.env.HOME,
|
|
375
|
+
process.env.USERPROFILE,
|
|
376
|
+
].filter(Boolean).map(p => path.resolve(p));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function npmGlobalBinDir() {
|
|
380
|
+
try {
|
|
381
|
+
const prefix = require('child_process').execFileSync('npm', ['config', 'get', 'prefix'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
382
|
+
if (!prefix) return null;
|
|
383
|
+
return currentPlatform() === 'win32' ? prefix : path.join(prefix, 'bin');
|
|
384
|
+
} catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function isVolatilePathDir(dir) {
|
|
390
|
+
const lower = dir.toLowerCase();
|
|
391
|
+
return lower.includes(`${path.sep}_npx${path.sep}`) ||
|
|
392
|
+
lower.includes(`${path.sep}node_modules${path.sep}.bin`) ||
|
|
393
|
+
lower.includes(`${path.sep}npm-cache${path.sep}_npx${path.sep}`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function canWriteDir(dir) {
|
|
397
|
+
const probe = path.join(dir, `.memoc-write-test-${process.pid}-${Date.now()}`);
|
|
398
|
+
try {
|
|
399
|
+
fs.writeFileSync(probe, '');
|
|
400
|
+
fs.unlinkSync(probe);
|
|
401
|
+
return true;
|
|
402
|
+
} catch {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
322
407
|
function ensureUnixPathRegistration(binDir) {
|
|
323
408
|
if (process.env.MEMOC_SKIP_PATH_REGISTER === '1') return false;
|
|
324
409
|
|