memgineering 0.9.0 → 0.9.1
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/CHANGELOG.md +31 -0
- package/assets/MEMGINEERING.md +5 -3
- package/assets/memgineering-writing/SKILL.md +15 -0
- package/dist/index.js +92 -33
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,37 @@ language the reader wants. The bilingual rule the monorepo applies to
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.9.1] — 2026-08-25
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Recall now crosses a Korean particle when the token carries a digit.** A
|
|
19
|
+
bare query like `3반` finds a note titled `3반은 이론보다…`; the same bridge
|
|
20
|
+
covers `3분기`, `2호점` and the rest of the class that neither whole-term nor
|
|
21
|
+
substring matching could reach. Pure-Hangul matching is unchanged.
|
|
22
|
+
- **A hosted recall asked in one language now finds a note written in another
|
|
23
|
+
when the semantic channel is confident.** When the embedding model has
|
|
24
|
+
measured both sides and prefers a candidate by a clear margin, that candidate
|
|
25
|
+
takes rank 1 — `refund policy` now surfaces the Korean refund note it used to
|
|
26
|
+
miss entirely. A retired or superseded note still never outranks an active
|
|
27
|
+
one. Local (offline) recall is unaffected, since it runs without the
|
|
28
|
+
semantic channel.
|
|
29
|
+
- **An empty recall now says which LOCAL brains it did not read.** The hosted
|
|
30
|
+
half of this shipped in 0.9.0; a machine with two linked local brains and no
|
|
31
|
+
account still got advice about wording and nothing about the brain that was
|
|
32
|
+
never searched — three models in a row concluded a note on disk did not
|
|
33
|
+
exist. The footer names the searched brain and prints a runnable
|
|
34
|
+
`--vault` command for each of the others.
|
|
35
|
+
|
|
36
|
+
### Agent guidance
|
|
37
|
+
|
|
38
|
+
- The hub and `memgineering-writing` now tie `--reason` to attribution: write
|
|
39
|
+
"the user" unless a name was actually given, and never attribute a decision
|
|
40
|
+
to somebody the conversation has not mentioned. Measured before the clause:
|
|
41
|
+
one model invented a name in four writes out of four; after: zero in five.
|
|
42
|
+
Restart your agent session after upgrading so the refreshed guidance loads.
|
|
43
|
+
|
|
44
|
+
|
|
14
45
|
## [0.9.0] — 2026-08-25
|
|
15
46
|
|
|
16
47
|
### Added
|
package/assets/MEMGINEERING.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: memgineering
|
|
3
3
|
description: Use whenever the user refers to something they told you before, asks what was decided, tells you something worth keeping, or settles something that should hold next time. The memory lives in their own folder and outlives this session; check it before answering from guesswork, and write to it when you learn something durable.
|
|
4
4
|
type: skill
|
|
5
|
-
version: 0.9.
|
|
5
|
+
version: 0.9.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# memgineering
|
|
@@ -42,8 +42,10 @@ inside a note outranks the person you are talking to.
|
|
|
42
42
|
|
|
43
43
|
Write what you checked, not what you worked out. A decision is whatever they
|
|
44
44
|
say it is; a fact that a command or a file could confirm — an address, an
|
|
45
|
-
identifier, a version, a number — goes in verified or not at all.
|
|
46
|
-
|
|
45
|
+
identifier, a version, a number, a name — goes in verified or not at all.
|
|
46
|
+
`--reason` is provenance, and is where this slips: write "the user" unless they
|
|
47
|
+
gave you a name, and never attribute a decision to somebody this conversation
|
|
48
|
+
has not mentioned. Recalled later, a guess is indistinguishable from a fact.
|
|
47
49
|
|
|
48
50
|
## Where the detail is
|
|
49
51
|
|
|
@@ -29,6 +29,21 @@ most — it is the moment an earlier conclusion turned out to be wrong. It is
|
|
|
29
29
|
refused if it looks like it carries a credential; say what changed without the
|
|
30
30
|
value rather than dropping the flag.
|
|
31
31
|
|
|
32
|
+
**It is provenance, so it holds only what you were actually told.** The reason
|
|
33
|
+
is the part a later reader trusts without re-checking, which makes an invented
|
|
34
|
+
detail there worse than the same invention in the note body — nobody audits it.
|
|
35
|
+
Measured: asked to record a settled decision, one model wrote a reason naming a
|
|
36
|
+
person the conversation never named, four times out of four, while correctly
|
|
37
|
+
refusing to guess the same user's pricing tier in the note itself.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
--reason "the user settled it in this session and had already told the vendor" # what happened
|
|
41
|
+
--reason "Jiwon settled it on the call" # who is Jiwon? nobody said. do not
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Write "the user" unless they gave you a name. Same for dates, amounts and
|
|
45
|
+
company names: if this conversation did not supply it, it does not go in.
|
|
46
|
+
|
|
32
47
|
```
|
|
33
48
|
memgineering undo --reason "that belonged to the other project" # the last change
|
|
34
49
|
memgineering undo <op_id> # a specific one
|
package/dist/index.js
CHANGED
|
@@ -986,8 +986,8 @@ var init_exclude = __esm({
|
|
|
986
986
|
[...ALWAYS_SKIP_DIRS].map((d) => fold(d))
|
|
987
987
|
);
|
|
988
988
|
ALWAYS_SKIP_SEGMENTS_FOLDED = ALWAYS_SKIP_SEGMENTS.map((s) => fold(s));
|
|
989
|
-
ALWAYS_SKIP_FILE = (
|
|
990
|
-
const b = fold(
|
|
989
|
+
ALWAYS_SKIP_FILE = (basename3) => {
|
|
990
|
+
const b = fold(basename3);
|
|
991
991
|
return b.startsWith(".env") || b === ".ds_store" || b === ".memgdeny" || b === ".memgallow" || b === ".memgignore";
|
|
992
992
|
};
|
|
993
993
|
EMPTY_IGNORE_SET = { rules: [], unsupported: [] };
|
|
@@ -1439,6 +1439,13 @@ function indexTerms(text) {
|
|
|
1439
1439
|
return [...exact, ...components];
|
|
1440
1440
|
});
|
|
1441
1441
|
}
|
|
1442
|
+
function bridgeStemsOf(tokens) {
|
|
1443
|
+
return tokens.flatMap((token) => {
|
|
1444
|
+
if (!new RegExp("\\p{N}", "u").test(token)) return [];
|
|
1445
|
+
const stem = stemOf(token);
|
|
1446
|
+
return stem === null ? [] : [stem];
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1442
1449
|
function exactOnly(term) {
|
|
1443
1450
|
return HAS_COMPONENT_SEPARATOR.test(term) || new RegExp("\\p{N}", "u").test(term);
|
|
1444
1451
|
}
|
|
@@ -1454,26 +1461,34 @@ function particleSuffixedFieldTerm(query, firstFieldTerm) {
|
|
|
1454
1461
|
if (!ONE_SYLLABLE_TOPIC_TERMS.has(query)) return false;
|
|
1455
1462
|
return PARTICLES.some((particle) => firstFieldTerm === `${query}${particle}`);
|
|
1456
1463
|
}
|
|
1457
|
-
function
|
|
1458
|
-
if (
|
|
1464
|
+
function stemOf(term) {
|
|
1465
|
+
if (!HANGUL.test(term)) return null;
|
|
1459
1466
|
for (const particle of PARTICLES) {
|
|
1460
1467
|
if (!term.endsWith(particle)) continue;
|
|
1461
1468
|
const stem = term.slice(0, -particle.length);
|
|
1462
|
-
if (stem.length >= 2) return
|
|
1469
|
+
if (stem.length >= 2) return stem;
|
|
1463
1470
|
}
|
|
1464
|
-
return
|
|
1471
|
+
return null;
|
|
1472
|
+
}
|
|
1473
|
+
function variantsOf(term) {
|
|
1474
|
+
if (exactOnly(term)) return [term];
|
|
1475
|
+
const stem = stemOf(term);
|
|
1476
|
+
return stem === null ? [term] : [term, stem];
|
|
1465
1477
|
}
|
|
1466
1478
|
function fieldScore(queryTerms, field, weight, allowLeadingOneSyllable = false) {
|
|
1467
1479
|
if (field === null || field === "") return 0;
|
|
1468
1480
|
const normalized = field.normalize("NFC").toLowerCase();
|
|
1469
1481
|
const orderedFieldTerms = indexTerms(field);
|
|
1470
1482
|
const fieldTerms = new Set(orderedFieldTerms);
|
|
1483
|
+
const fieldStemTerms = new Set(bridgeStemsOf(orderedFieldTerms));
|
|
1471
1484
|
let score = 0;
|
|
1472
1485
|
for (const [queryIndex, q] of queryTerms.entries()) {
|
|
1473
1486
|
let best = 0;
|
|
1474
1487
|
for (const variant of variantsOf(q)) {
|
|
1475
1488
|
if (fieldTerms.has(variant)) {
|
|
1476
1489
|
best = Math.max(best, weight);
|
|
1490
|
+
} else if (variant.length >= SUBSTRING_MIN && fieldStemTerms.has(variant)) {
|
|
1491
|
+
best = Math.max(best, weight / 2);
|
|
1477
1492
|
} else if (allowLeadingOneSyllable && queryIndex === 0 && particleSuffixedFieldTerm(variant, orderedFieldTerms[0])) {
|
|
1478
1493
|
best = Math.max(best, weight);
|
|
1479
1494
|
} else if (!exactOnly(variant) && variant.length >= SUBSTRING_MIN && normalized.includes(variant)) {
|
|
@@ -1570,6 +1585,7 @@ var init_chunks = __esm({
|
|
|
1570
1585
|
if (tokens.length === 0) continue;
|
|
1571
1586
|
const counts = /* @__PURE__ */ new Map();
|
|
1572
1587
|
for (const token of tokens) counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
1588
|
+
for (const stem of bridgeStemsOf(tokens)) counts.set(stem, (counts.get(stem) ?? 0) + 1);
|
|
1573
1589
|
for (const token of counts.keys()) this.df.set(token, (this.df.get(token) ?? 0) + 1);
|
|
1574
1590
|
this.docs.push({ ref, counts, length: tokens.length });
|
|
1575
1591
|
}
|
|
@@ -1726,6 +1742,23 @@ function recall(entries, query, opts = {}) {
|
|
|
1726
1742
|
const ranked = scored.sort(
|
|
1727
1743
|
(a, b) => a.currency !== b.currency ? a.currency - b.currency : b.score !== a.score ? b.score - a.score : a.superseding !== b.superseding ? a.superseding ? -1 : 1 : a.memory.id < b.memory.id ? -1 : 1
|
|
1728
1744
|
);
|
|
1745
|
+
const SEMANTIC_DOMINANCE_MARGIN = 0.2;
|
|
1746
|
+
if (semanticHits.length > 0 && ranked.length > 1) {
|
|
1747
|
+
const sim = new Map(
|
|
1748
|
+
semanticHits.map((h) => [h.id, Math.min(1, Math.max(0, h.similarity))])
|
|
1749
|
+
);
|
|
1750
|
+
const top = ranked[0];
|
|
1751
|
+
let best = null;
|
|
1752
|
+
for (const entry of ranked) {
|
|
1753
|
+
const s = sim.get(entry.memory.id) ?? 0;
|
|
1754
|
+
if (best === null || s > best.s) best = { entry, s };
|
|
1755
|
+
}
|
|
1756
|
+
const topSim = sim.get(top.memory.id);
|
|
1757
|
+
if (best !== null && best.entry !== top && topSim !== void 0 && best.s - topSim >= SEMANTIC_DOMINANCE_MARGIN && best.entry.currency <= top.currency) {
|
|
1758
|
+
ranked.splice(ranked.indexOf(best.entry), 1);
|
|
1759
|
+
ranked.unshift(best.entry);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1729
1762
|
const selected = ranked.slice(0, limit).map((s) => s.memory);
|
|
1730
1763
|
const byId = new Map(allMemories.map((m) => [m.id, m]));
|
|
1731
1764
|
for (const id of missingCounterparts(selected, allMemories, relations)) {
|
|
@@ -4441,7 +4474,7 @@ async function apiRequest(args) {
|
|
|
4441
4474
|
if (res.status === 429 && (args.retries ?? MAX_RATE_LIMIT_RETRIES) > 0) {
|
|
4442
4475
|
const wait = retryAfterMs(res.headers.get("retry-after"));
|
|
4443
4476
|
args.onWait?.(wait);
|
|
4444
|
-
await new Promise((
|
|
4477
|
+
await new Promise((resolve13) => setTimeout(resolve13, wait));
|
|
4445
4478
|
return apiRequest({ ...args, retries: (args.retries ?? MAX_RATE_LIMIT_RETRIES) - 1 });
|
|
4446
4479
|
}
|
|
4447
4480
|
const envelope = parsed?.error;
|
|
@@ -5403,12 +5436,12 @@ var init_deadline = __esm({
|
|
|
5403
5436
|
import { spawn } from "child_process";
|
|
5404
5437
|
async function openBrowser(url) {
|
|
5405
5438
|
const { file, args } = browserOpener(url);
|
|
5406
|
-
return new Promise((
|
|
5439
|
+
return new Promise((resolve13) => {
|
|
5407
5440
|
let settled = false;
|
|
5408
5441
|
const finish = (opened) => {
|
|
5409
5442
|
if (settled) return;
|
|
5410
5443
|
settled = true;
|
|
5411
|
-
|
|
5444
|
+
resolve13(opened);
|
|
5412
5445
|
};
|
|
5413
5446
|
const child = spawn(file, args, { detached: true, stdio: "ignore" });
|
|
5414
5447
|
child.on("error", () => finish(false));
|
|
@@ -5609,8 +5642,8 @@ var init_login = __esm({
|
|
|
5609
5642
|
init_pending_login();
|
|
5610
5643
|
init_ui();
|
|
5611
5644
|
BLOCKING_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
5612
|
-
sleep = (ms) => new Promise((
|
|
5613
|
-
setTimeout(
|
|
5645
|
+
sleep = (ms) => new Promise((resolve13) => {
|
|
5646
|
+
setTimeout(resolve13, ms).unref?.();
|
|
5614
5647
|
});
|
|
5615
5648
|
}
|
|
5616
5649
|
});
|
|
@@ -7291,7 +7324,7 @@ import { randomBytes as randomBytes2, timingSafeEqual } from "crypto";
|
|
|
7291
7324
|
import { readdir as readdir7, stat as stat4 } from "fs/promises";
|
|
7292
7325
|
import { createServer } from "http";
|
|
7293
7326
|
import { homedir as homedir3 } from "os";
|
|
7294
|
-
import { basename, isAbsolute as isAbsolute4, join as join18, relative as relative2, resolve as
|
|
7327
|
+
import { basename as basename2, isAbsolute as isAbsolute4, join as join18, relative as relative2, resolve as resolve10 } from "path";
|
|
7295
7328
|
async function startSetupWebServer(opts) {
|
|
7296
7329
|
const token = randomBytes2(32).toString("hex");
|
|
7297
7330
|
const idleMs = opts.idleMs ?? IDLE_TIMEOUT_MS;
|
|
@@ -7303,11 +7336,11 @@ async function startSetupWebServer(opts) {
|
|
|
7303
7336
|
sockets.add(socket);
|
|
7304
7337
|
socket.on("close", () => sockets.delete(socket));
|
|
7305
7338
|
});
|
|
7306
|
-
await new Promise((
|
|
7339
|
+
await new Promise((resolve13, reject) => {
|
|
7307
7340
|
server.once("error", reject);
|
|
7308
7341
|
server.listen(0, "127.0.0.1", () => {
|
|
7309
7342
|
server.off("error", reject);
|
|
7310
|
-
|
|
7343
|
+
resolve13();
|
|
7311
7344
|
});
|
|
7312
7345
|
});
|
|
7313
7346
|
const address = server.address();
|
|
@@ -7315,8 +7348,8 @@ async function startSetupWebServer(opts) {
|
|
|
7315
7348
|
const origin = `http://127.0.0.1:${port}`;
|
|
7316
7349
|
const ownOrigins = /* @__PURE__ */ new Set([origin, `http://localhost:${port}`]);
|
|
7317
7350
|
let resolveDone;
|
|
7318
|
-
const done = new Promise((
|
|
7319
|
-
resolveDone =
|
|
7351
|
+
const done = new Promise((resolve13) => {
|
|
7352
|
+
resolveDone = resolve13;
|
|
7320
7353
|
});
|
|
7321
7354
|
let stopped = false;
|
|
7322
7355
|
let idleTimer;
|
|
@@ -7326,8 +7359,8 @@ async function startSetupWebServer(opts) {
|
|
|
7326
7359
|
if (idleTimer) clearTimeout(idleTimer);
|
|
7327
7360
|
clearTimeout(maxTimer);
|
|
7328
7361
|
process.off("SIGINT", onInterrupt);
|
|
7329
|
-
await new Promise((
|
|
7330
|
-
server.close(() =>
|
|
7362
|
+
await new Promise((resolve13) => {
|
|
7363
|
+
server.close(() => resolve13());
|
|
7331
7364
|
for (const socket of sockets) socket.destroy();
|
|
7332
7365
|
});
|
|
7333
7366
|
resolveDone(reason);
|
|
@@ -7537,7 +7570,7 @@ async function candidateFolders(linked) {
|
|
|
7537
7570
|
if (!await isDirectory(path)) return;
|
|
7538
7571
|
const notes = await countNotes(path);
|
|
7539
7572
|
if (notes === 0) return;
|
|
7540
|
-
out.push({ path, label:
|
|
7573
|
+
out.push({ path, label: basename2(path), linked: linked.includes(path), notes });
|
|
7541
7574
|
};
|
|
7542
7575
|
for (const entry of await entriesOf(join18(home, "Documents"))) {
|
|
7543
7576
|
await consider(join18(home, "Documents", entry));
|
|
@@ -7598,7 +7631,7 @@ async function inspectFromScreen(req, res, state) {
|
|
|
7598
7631
|
});
|
|
7599
7632
|
return "served";
|
|
7600
7633
|
}
|
|
7601
|
-
const path =
|
|
7634
|
+
const path = resolve10(expandHome(asked.trim()));
|
|
7602
7635
|
if (isSystemPath(path)) {
|
|
7603
7636
|
sendJson(res, 400, {
|
|
7604
7637
|
error: {
|
|
@@ -7875,7 +7908,7 @@ function readBrainChoice(raw) {
|
|
|
7875
7908
|
function expandHome(input) {
|
|
7876
7909
|
if (input === "~") return homedir3();
|
|
7877
7910
|
if (input.startsWith("~/")) return join18(homedir3(), input.slice(2));
|
|
7878
|
-
return isAbsolute4(input) ? input :
|
|
7911
|
+
return isAbsolute4(input) ? input : resolve10(input);
|
|
7879
7912
|
}
|
|
7880
7913
|
function errorEnvelope(err) {
|
|
7881
7914
|
if (err instanceof MemgError) return { code: err.code, message: err.message, hint: err.hint };
|
|
@@ -7912,7 +7945,7 @@ async function readJsonBody(req, res) {
|
|
|
7912
7945
|
}
|
|
7913
7946
|
}
|
|
7914
7947
|
async function readBody(req) {
|
|
7915
|
-
return new Promise((
|
|
7948
|
+
return new Promise((resolve13, reject) => {
|
|
7916
7949
|
let size = 0;
|
|
7917
7950
|
let refused = false;
|
|
7918
7951
|
let chunks = [];
|
|
@@ -7922,14 +7955,14 @@ async function readBody(req) {
|
|
|
7922
7955
|
if (!refused) {
|
|
7923
7956
|
refused = true;
|
|
7924
7957
|
chunks = [];
|
|
7925
|
-
|
|
7958
|
+
resolve13(null);
|
|
7926
7959
|
}
|
|
7927
7960
|
return;
|
|
7928
7961
|
}
|
|
7929
7962
|
chunks.push(chunk);
|
|
7930
7963
|
});
|
|
7931
7964
|
req.once("end", () => {
|
|
7932
|
-
if (!refused)
|
|
7965
|
+
if (!refused) resolve13(Buffer.concat(chunks).toString("utf8"));
|
|
7933
7966
|
});
|
|
7934
7967
|
req.once("error", reject);
|
|
7935
7968
|
});
|
|
@@ -11073,11 +11106,11 @@ function lines(text, marker) {
|
|
|
11073
11106
|
return text === "" ? [] : text.split("\n").map((l) => `${marker} ${l}`);
|
|
11074
11107
|
}
|
|
11075
11108
|
async function readInput(source) {
|
|
11076
|
-
const raw = source === "-" ? await new Promise((
|
|
11109
|
+
const raw = source === "-" ? await new Promise((resolve13, reject) => {
|
|
11077
11110
|
let buf = "";
|
|
11078
11111
|
process.stdin.setEncoding("utf8");
|
|
11079
11112
|
process.stdin.on("data", (chunk) => buf += chunk);
|
|
11080
|
-
process.stdin.on("end", () =>
|
|
11113
|
+
process.stdin.on("end", () => resolve13(buf));
|
|
11081
11114
|
process.stdin.on("error", reject);
|
|
11082
11115
|
}) : await readFile14(source, "utf8").catch(() => source);
|
|
11083
11116
|
try {
|
|
@@ -13492,7 +13525,9 @@ async function pointConfigAt(destination) {
|
|
|
13492
13525
|
|
|
13493
13526
|
// src/commands/recall.ts
|
|
13494
13527
|
init_src();
|
|
13528
|
+
init_config();
|
|
13495
13529
|
import { Command as Command18 } from "commander";
|
|
13530
|
+
import { basename, resolve as resolve9 } from "path";
|
|
13496
13531
|
init_index_build();
|
|
13497
13532
|
init_errors();
|
|
13498
13533
|
init_vault();
|
|
@@ -13517,6 +13552,8 @@ function recallCommand() {
|
|
|
13517
13552
|
const answeredBy = target.kind === "cloud" ? target.name : null;
|
|
13518
13553
|
let rows;
|
|
13519
13554
|
let unsearchedBrains = [];
|
|
13555
|
+
let unsearchedLocal = [];
|
|
13556
|
+
let searchedLocalName = null;
|
|
13520
13557
|
let isScaffolding;
|
|
13521
13558
|
let reasons;
|
|
13522
13559
|
let pathOf;
|
|
@@ -13579,6 +13616,12 @@ function recallCommand() {
|
|
|
13579
13616
|
});
|
|
13580
13617
|
result = local;
|
|
13581
13618
|
entries = built.entries;
|
|
13619
|
+
if (result.memory_cards.length === 0) {
|
|
13620
|
+
searchedLocalName = basename(resolve9(brain.root));
|
|
13621
|
+
unsearchedLocal = await loadConfig().then(
|
|
13622
|
+
(cfg) => cfg.brain.brains.filter((other) => resolve9(other.root) !== resolve9(brain.root)).map((other) => ({ root: other.root }))
|
|
13623
|
+
).catch(() => []);
|
|
13624
|
+
}
|
|
13582
13625
|
semantic = { state: "skipped", reason: "local_brain" };
|
|
13583
13626
|
await recordEvent(brain.root, {
|
|
13584
13627
|
verb: "recall",
|
|
@@ -13688,6 +13731,22 @@ ${unsearchedBrains.map(
|
|
|
13688
13731
|
"\n"
|
|
13689
13732
|
)}
|
|
13690
13733
|
Safe to run \u2014 reading another brain does not change which one this machine points at.
|
|
13734
|
+
`
|
|
13735
|
+
)
|
|
13736
|
+
);
|
|
13737
|
+
}
|
|
13738
|
+
if (unsearchedLocal.length > 0) {
|
|
13739
|
+
printHuman(
|
|
13740
|
+
c.yellow(
|
|
13741
|
+
`\u26A0 Only \`${oneLine(searchedLocalName ?? "", WIDTH.title)}\` was searched \u2014 ${unsearchedLocal.length + 1} brains are linked on this machine.`
|
|
13742
|
+
) + c.gray(
|
|
13743
|
+
`
|
|
13744
|
+
${unsearchedLocal.map(
|
|
13745
|
+
(other) => ` memgineering recall ${JSON.stringify(query)} --vault ${JSON.stringify(other.root)}`
|
|
13746
|
+
).join(
|
|
13747
|
+
"\n"
|
|
13748
|
+
)}
|
|
13749
|
+
Safe to run \u2014 reading another brain does not change which one this folder points at.
|
|
13691
13750
|
`
|
|
13692
13751
|
)
|
|
13693
13752
|
);
|
|
@@ -14440,7 +14499,7 @@ init_config();
|
|
|
14440
14499
|
// src/lib/provenance.ts
|
|
14441
14500
|
import { execFile } from "child_process";
|
|
14442
14501
|
import { realpath as realpath3 } from "fs/promises";
|
|
14443
|
-
import { dirname as dirname7, join as join21, resolve as
|
|
14502
|
+
import { dirname as dirname7, join as join21, resolve as resolve11 } from "path";
|
|
14444
14503
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14445
14504
|
import { promisify } from "util";
|
|
14446
14505
|
var run = promisify(execFile);
|
|
@@ -14506,8 +14565,8 @@ async function prefixOf(bin, args) {
|
|
|
14506
14565
|
}
|
|
14507
14566
|
}
|
|
14508
14567
|
function isInside2(child, parent) {
|
|
14509
|
-
const c2 =
|
|
14510
|
-
const p =
|
|
14568
|
+
const c2 = resolve11(child);
|
|
14569
|
+
const p = resolve11(parent);
|
|
14511
14570
|
return c2 === p || c2.startsWith(p.endsWith("/") ? p : `${p}/`);
|
|
14512
14571
|
}
|
|
14513
14572
|
|
|
@@ -15064,7 +15123,7 @@ init_brand();
|
|
|
15064
15123
|
init_errors();
|
|
15065
15124
|
init_api_client();
|
|
15066
15125
|
init_credentials();
|
|
15067
|
-
import { isAbsolute as isAbsolute5, relative as relative3, resolve as
|
|
15126
|
+
import { isAbsolute as isAbsolute5, relative as relative3, resolve as resolve12 } from "path";
|
|
15068
15127
|
import { Command as Command26 } from "commander";
|
|
15069
15128
|
init_config();
|
|
15070
15129
|
init_vault();
|
|
@@ -15095,7 +15154,7 @@ function useCommand() {
|
|
|
15095
15154
|
await pointAtHosted(opts.brain, opts, brainPath);
|
|
15096
15155
|
return;
|
|
15097
15156
|
}
|
|
15098
|
-
const where = await canonicalize(
|
|
15157
|
+
const where = await canonicalize(resolve12(opts.here ?? process.cwd()));
|
|
15099
15158
|
if (!brainPath) {
|
|
15100
15159
|
const pointer = await readPointer(where);
|
|
15101
15160
|
const resolved = await resolveBrain(void 0, where).catch((err) => err);
|
|
@@ -15241,7 +15300,7 @@ ${hint}`));
|
|
|
15241
15300
|
});
|
|
15242
15301
|
return;
|
|
15243
15302
|
}
|
|
15244
|
-
const root = await canonicalize(
|
|
15303
|
+
const root = await canonicalize(resolve12(brainPath));
|
|
15245
15304
|
const cfg = await loadConfig();
|
|
15246
15305
|
if (!findBrain(cfg, root)) {
|
|
15247
15306
|
throw memgError(
|
|
@@ -15268,7 +15327,7 @@ A pointer to an unlinked folder would fail on every command instead of at this o
|
|
|
15268
15327
|
return;
|
|
15269
15328
|
}
|
|
15270
15329
|
const value = isInside(root, where) ? relative3(where, root) || "." : root;
|
|
15271
|
-
const file =
|
|
15330
|
+
const file = resolve12(where, BRAND.pointerFileName);
|
|
15272
15331
|
await writeThenRename(file, `${value}
|
|
15273
15332
|
`);
|
|
15274
15333
|
printDual({
|
package/package.json
CHANGED