memgineering 0.10.0 → 0.11.0

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 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.11.0] — 2026-08-26
15
+
16
+ ### Added
17
+
18
+ - **`memgineering rename <new-name> [--brain <name-or-id>]` — a wrong name is
19
+ one command, not an orphan.** Renames a hosted brain in place: notes, ledger
20
+ and id untouched, this machine's pointer healed, other machines picking the
21
+ new name up on their next command. A server that predates renaming is told
22
+ apart from a missing brain and says which side needs updating.
23
+ - **`memgineering delete-brain <name-or-id> --confirm "<exact name>"`.**
24
+ Deletes a hosted brain — every note, its ledger, its measurements — behind a
25
+ three-way gate: an exact-name `--confirm`, a retype-the-name prompt when a
26
+ person is at the terminal, and a hard refusal when nobody can answer.
27
+ Deleting the brain this machine reads also clears the pointer, and says so.
28
+ The server route existed since 0.6; the CLI now exposes it.
29
+
30
+ ### Changed
31
+
32
+ - `create`'s follow-up and the `brains` listing hints now name `rename` (and,
33
+ on the listing, `delete-brain`) so an agent mid-task finds the right verb
34
+ instead of inventing a copy-rename that strands an orphan brain — the
35
+ failure measured 3/3 in round 9.
36
+
37
+ ### Agent guidance
38
+
39
+ - The `memgineering-setup` skill gained "Renaming and deleting a hosted
40
+ brain": rename-not-delete for wrong names, the delete confirm contract
41
+ (only on the person's explicit ask, never to tidy up), and `unlink --brain`
42
+ as the non-destructive sibling. Skill trigger phrases now include renaming
43
+ and deleting. Restart your agent session after upgrading.
44
+
14
45
  ## [0.10.0] — 2026-08-26
15
46
 
16
47
  ### Added
@@ -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.10.0
5
+ version: 0.11.0
6
6
  ---
7
7
 
8
8
  # memgineering
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: memgineering-setup
3
- description: Use when installing or configuring memgineering, when moving a brain between a machine and an account, or whenever the user asks WHICH of their AI tools can see this memory. Triggers include "set up memgineering", "connect my notes", "does my other AI see this too", "which of my tools can read this", "will it work in the app on my phone", "no brain is linked".
3
+ description: Use when installing or configuring memgineering, when moving a brain between a machine and an account, when renaming or deleting a hosted brain, or whenever the user asks WHICH of their AI tools can see this memory. Triggers include "set up memgineering", "connect my notes", "rename my brain", "delete that brain", "does my other AI see this too", "which of my tools can read this", "will it work in the app on my phone", "no brain is linked".
4
4
  type: skill
5
5
  allowed-tools: Bash(memgineering:*)
6
6
  ---
@@ -163,6 +163,25 @@ A name the account already holds is refused with nothing written — `use
163
163
  exception is a brain holding nothing but its own untouched scaffolding: that
164
164
  is an interrupted `create`, and running the same command again finishes it.
165
165
 
166
+ ### Renaming and deleting a hosted brain
167
+
168
+ **A wrongly-named brain is a rename, not a delete.** `memgineering rename
169
+ <new name>` changes what the brain this machine reads is called, in place —
170
+ notes, ledger and id untouched; `--brain <name-or-id>` renames another one.
171
+ Other machines pick the new name up on their own next command; nothing on
172
+ them breaks, because pointers hold the id. Never create a second brain to
173
+ fix a name.
174
+
175
+ `memgineering delete-brain <name-or-id> --confirm "<exact name>"` is the
176
+ destructive one: the brain, every note in it, its ledger and its
177
+ measurements, gone from the server, not recoverable. Run it ONLY when the
178
+ person has explicitly asked for THIS brain to be deleted, and take the
179
+ confirm name from what they said — never type it for them from a listing to
180
+ tidy an account up. Without `--confirm` in a real terminal it asks the
181
+ person to retype the brain's name; with no terminal it refuses rather than
182
+ deciding. `unlink --brain` is the gentle sibling: it only stops this machine
183
+ reading a brain and deletes nothing.
184
+
166
185
  **They already keep notes in a folder on this machine** — carry them up
167
186
  instead of starting empty:
168
187
 
package/dist/index.js CHANGED
@@ -5171,8 +5171,8 @@ var init_init = __esm({
5171
5171
  // src/commands/link.ts
5172
5172
  import { readFile as readFile16 } from "fs/promises";
5173
5173
  import { join as join15 } from "path";
5174
- import { Command as Command9 } from "commander";
5175
- import prompts from "prompts";
5174
+ import { Command as Command11 } from "commander";
5175
+ import prompts2 from "prompts";
5176
5176
  function sameBasename(all, entry) {
5177
5177
  const base = (entry.split("/").pop() ?? entry).normalize("NFC").toLowerCase();
5178
5178
  return all.filter((p) => (p.split("/").pop() ?? "").normalize("NFC").toLowerCase() === base);
@@ -5194,7 +5194,7 @@ function spreadAcrossFolders(rows, max) {
5194
5194
  return picked;
5195
5195
  }
5196
5196
  function linkCommand() {
5197
- return new Command9("link").description("read a notes folder as a brain \u2014 audits its rules first").argument("<path>", "path to the notes folder").option("--yes", "skip the confirmation").option("--dry-run", "show exactly what would be stored, then stop").option(
5197
+ return new Command11("link").description("read a notes folder as a brain \u2014 audits its rules first").argument("<path>", "path to the notes folder").option("--yes", "skip the confirmation").option("--dry-run", "show exactly what would be stored, then stop").option(
5198
5198
  "--observations <dir>",
5199
5199
  "where `remember` writes, relative to the folder",
5200
5200
  DEFAULT_OBSERVATIONS_DIR
@@ -5520,7 +5520,7 @@ Brain: ${root}`));
5520
5520
  "\n This tool writes into your notes without asking each time \u2014 new\n observations, and changes to the memory block of a note. Every one is\n recorded and reversible: memgineering log \xB7 memgineering undo"
5521
5521
  )
5522
5522
  );
5523
- const { ok } = await prompts({
5523
+ const { ok } = await prompts2({
5524
5524
  type: "confirm",
5525
5525
  name: "ok",
5526
5526
  message: "Build the index for this folder?",
@@ -5601,9 +5601,9 @@ var init_open_browser = __esm({
5601
5601
  });
5602
5602
 
5603
5603
  // src/commands/login.ts
5604
- import { Command as Command14 } from "commander";
5604
+ import { Command as Command16 } from "commander";
5605
5605
  function loginCommand() {
5606
- return new Command14("login").description("sign in to a hosted brain").option(
5606
+ return new Command16("login").description("sign in to a hosted brain").option(
5607
5607
  "--emit-only",
5608
5608
  "start the sign-in, open the browser, and return immediately \u2014 THE path an agent should use on a user\u2019s behalf; the next command claims the token automatically. Prefer this over the plain form, whose wait for a human click will outlast an agent\u2019s command timeout"
5609
5609
  ).option("--no-browser", "do not try to open a browser \u2014 just print the link").option("--print-url", "print the approval link even when a browser opened").addHelpText(
@@ -5962,7 +5962,7 @@ var init_setup_brain = __esm({
5962
5962
  });
5963
5963
 
5964
5964
  // src/commands/setup-signin.ts
5965
- import prompts2 from "prompts";
5965
+ import prompts3 from "prompts";
5966
5966
  async function runSignInStep(opts) {
5967
5967
  const summary = await signInSummary();
5968
5968
  if (summary.signedIn) return { status: "signed-in", displayName: summary.displayName };
@@ -5989,7 +5989,7 @@ async function runSignInStep(opts) {
5989
5989
  }
5990
5990
  if (opts.interactive) {
5991
5991
  printHuman(c.bold("\nmemgineering \u2014 setup\n"));
5992
- const { wants } = await prompts2({
5992
+ const { wants } = await prompts3({
5993
5993
  type: "toggle",
5994
5994
  name: "wants",
5995
5995
  message: "Sign in to a memgineering account first?",
@@ -8364,11 +8364,11 @@ import { createHash as createHash8 } from "crypto";
8364
8364
  import { access, mkdir as mkdir13, readFile as readFile19 } from "fs/promises";
8365
8365
  import { dirname as dirname5, join as join19 } from "path";
8366
8366
  import { fileURLToPath } from "url";
8367
- import { Command as Command23 } from "commander";
8368
- import prompts3 from "prompts";
8367
+ import { Command as Command25 } from "commander";
8368
+ import prompts4 from "prompts";
8369
8369
  import { homedir as homedir4 } from "os";
8370
8370
  function setupCommand() {
8371
- return new Command23("setup").description("install into your agents, and point at a brain (--dry-run to only report)").option("--agent", "non-interactive: take every answer from flags").option("--human", "interactive: choose from a list").option("--web", "open a screen in the browser and answer it there").option("--print-url", "with --web: print the URL instead of opening a browser").option(
8371
+ return new Command25("setup").description("install into your agents, and point at a brain (--dry-run to only report)").option("--agent", "non-interactive: take every answer from flags").option("--human", "interactive: choose from a list").option("--web", "open a screen in the browser and answer it there").option("--print-url", "with --web: print the URL instead of opening a browser").option(
8372
8372
  "--tools <list>",
8373
8373
  "comma-separated: claude, codex, grok, gemini (default: all detected)"
8374
8374
  ).option("--auto-update <choice>", "on | off (default: on, recommended)").option(
@@ -8584,7 +8584,7 @@ function setupCommand() {
8584
8584
  }
8585
8585
  });
8586
8586
  if (mode.kind === "human" && brains.length === 0 && !opts.dryRun && isInteractive()) {
8587
- const { path } = await prompts3({
8587
+ const { path } = await prompts4({
8588
8588
  type: "text",
8589
8589
  name: "path",
8590
8590
  message: "Path to your notes folder (blank to skip)"
@@ -8705,7 +8705,7 @@ async function resolveMode(opts) {
8705
8705
  return { kind: "human", chosen: true };
8706
8706
  }
8707
8707
  if (!isInteractive() || isJsonMode()) return { kind: "agent", chosen: false };
8708
- const { mode } = await prompts3({
8708
+ const { mode } = await prompts4({
8709
8709
  type: "select",
8710
8710
  name: "mode",
8711
8711
  message: "How would you like to set this up?",
@@ -8762,7 +8762,7 @@ async function askHuman(detected, opts) {
8762
8762
  printHuman(c.bold("\nmemgineering \u2014 setup\n"));
8763
8763
  printHuman(c.gray(" Found: ") + detected.map((t) => t.label).join(", "));
8764
8764
  printHuman("");
8765
- const answers = await prompts3([
8765
+ const answers = await prompts4([
8766
8766
  {
8767
8767
  type: "multiselect",
8768
8768
  name: "tools",
@@ -9117,7 +9117,7 @@ For a guide: put ${BRAND.hubMarkerStart} and ${BRAND.hubMarkerEnd} back around t
9117
9117
  });
9118
9118
 
9119
9119
  // src/program.ts
9120
- import { Command as Command29 } from "commander";
9120
+ import { Command as Command31 } from "commander";
9121
9121
 
9122
9122
  // src/help.ts
9123
9123
  init_ui();
@@ -9141,7 +9141,17 @@ var COMMAND_GROUPS = [
9141
9141
  {
9142
9142
  title: "ACCOUNT",
9143
9143
  blurb: "optional \u2014 connect a hosted brain; everything above works without it",
9144
- commands: ["login", "logout", "whoami", "brains", "create", "push", "pull"]
9144
+ commands: [
9145
+ "login",
9146
+ "logout",
9147
+ "whoami",
9148
+ "brains",
9149
+ "create",
9150
+ "rename",
9151
+ "delete-brain",
9152
+ "push",
9153
+ "pull"
9154
+ ]
9145
9155
  },
9146
9156
  {
9147
9157
  title: "SYSTEM",
@@ -12407,7 +12417,7 @@ async function refuseUnlessInterrupted(brain, name) {
12407
12417
  );
12408
12418
  }
12409
12419
  function nextStep2(created, filledCount) {
12410
- const ask = "Run `memgineering onboard` \u2014 it prints what to ask, in their words. Do not invent answers; a guessed BOUNDARIES file is worse than an empty one, because the next agent reads it as something they said.";
12420
+ const ask = "Run `memgineering onboard` \u2014 it prints what to ask, in their words. Do not invent answers; a guessed BOUNDARIES file is worse than an empty one, because the next agent reads it as something they said. If this name turns out wrong, `memgineering rename <new name>` renames it in place \u2014 never create a second brain to fix a name.";
12411
12421
  return created ? `${BASE_COUNT - filledCount} of ${BASE_COUNT} base files are still empty templates. ${ask}` : `Some base files may still be empty templates \u2014 the ones already there were left as they were. ${ask}`;
12412
12422
  }
12413
12423
  function printPointed(repoint, name) {
@@ -12435,11 +12445,396 @@ This machine now reads \u201C${shown}\u201D.
12435
12445
  );
12436
12446
  }
12437
12447
 
12448
+ // src/commands/rename.ts
12449
+ init_errors();
12450
+ init_api_client();
12451
+ init_cloud_brain();
12452
+ import { Command as Command9 } from "commander";
12453
+ init_config();
12454
+ init_ui();
12455
+ function renameCommand() {
12456
+ return new Command9("rename").description("change what a hosted brain is called, in place").argument("<new-name>", "what to call it instead").option("--brain <name>", "which hosted brain to rename, by its name or id").option("--dry-run", "say what would be renamed and send nothing").addHelpText(
12457
+ "after",
12458
+ "\nRenames the brain this machine reads unless `--brain` names another one.\nNothing is uploaded, downloaded or deleted: the notes, the history and every\npointer at this brain resolve by id, so only the label changes."
12459
+ ).action(async (rawName, opts) => {
12460
+ if (rawName.startsWith("--")) {
12461
+ throw memgError(
12462
+ "invalid_input",
12463
+ `\`rename\` was given the flag \`${oneLine(rawName, 32)}\` as a brain name`,
12464
+ "Nothing was renamed. The first argument is the NEW NAME for the brain, and the\nflag written there was read as that name. Put the name first \u2014 `memgineering\nrename <new name> --brain <name-or-id>` \u2014 and pass the other flag after it."
12465
+ );
12466
+ }
12467
+ const named = opts.brain;
12468
+ if (named !== void 0 && named.startsWith("--")) {
12469
+ throw memgError(
12470
+ "invalid_input",
12471
+ `\`--brain\` was given the flag \`${oneLine(named, WIDTH.title)}\` as its name`,
12472
+ "Nothing was renamed. Put the hosted brain name or id immediately after `--brain`, then pass the other flag separately. The new name is the first argument, not this one."
12473
+ );
12474
+ }
12475
+ const name = rawName.replace(/\s+/g, " ").trim();
12476
+ if (name === "") {
12477
+ throw memgError(
12478
+ "invalid_input",
12479
+ "a brain needs a name",
12480
+ "Nothing was renamed. `memgineering rename <new name>` needs the new name as its first argument \u2014 it is what the user will see in `memgineering brains` and how they tell two brains apart."
12481
+ );
12482
+ }
12483
+ const server = apiUrl();
12484
+ const pointer = (await loadConfig()).brain.cloud;
12485
+ const pointed = pointer !== void 0 && pointer.api_url === server ? pointer : void 0;
12486
+ if (opts.dryRun === true) {
12487
+ printDual({
12488
+ json: {
12489
+ dry_run: true,
12490
+ would_rename_to: name,
12491
+ api_url: server,
12492
+ // The unresolved flag value is echoed rather than looked up: which
12493
+ // brain it names is a question only the server can answer, and this
12494
+ // run does not ask it anything.
12495
+ target: named !== void 0 ? { source: "flag", requested: named, id: null, name: null } : pointed === void 0 ? null : { source: "pointer", requested: null, id: pointed.id, name: pointed.name }
12496
+ },
12497
+ human: () => {
12498
+ printHuman(`## rename (dry run)
12499
+ `);
12500
+ if (named !== void 0) {
12501
+ printHuman(
12502
+ `whichever brain \`--brain ${oneLine(named, WIDTH.title)}\` turns out to name \u2192 \u201C${oneLine(name, WIDTH.title)}\u201D on \`${server}\`
12503
+ `
12504
+ );
12505
+ } else if (pointed !== void 0) {
12506
+ printHuman(
12507
+ `\u201C${oneLine(pointed.name, WIDTH.title)}\u201D \u2192 \u201C${oneLine(name, WIDTH.title)}\u201D on \`${server}\`
12508
+ ` + c.gray(
12509
+ ` (${oneLine(pointed.id, WIDTH.handle)} \u2014 the brain this machine reads)
12510
+ `
12511
+ )
12512
+ );
12513
+ } else {
12514
+ printHuman(
12515
+ c.yellow("nothing to rename: this machine is not reading a hosted brain\n") + c.gray(" A real run would refuse. `--brain <name-or-id>` picks one instead.\n")
12516
+ );
12517
+ }
12518
+ printHuman(
12519
+ c.gray(
12520
+ "\nNothing was sent, and nothing was asked of the server \u2014 including whether\nthis account already holds a brain by that name, which is the one thing a\nreal run finds out. Run without --dry-run to find out."
12521
+ )
12522
+ );
12523
+ }
12524
+ });
12525
+ return;
12526
+ }
12527
+ const { target, token, viaPointer } = await resolveTarget2({
12528
+ named,
12529
+ pointed,
12530
+ pointer,
12531
+ server
12532
+ });
12533
+ const renamed = await renameCloudBrain({ token, server, id: target.id, name, viaPointer });
12534
+ const pointerUpdated = pointed !== void 0 && pointed.id === renamed.id;
12535
+ if (pointerUpdated) {
12536
+ await pointConfigAt({ brainId: renamed.id, name: renamed.name, apiUrl: server });
12537
+ }
12538
+ const changed = renamed.renamed_from !== null;
12539
+ const next = nextStep3(renamed);
12540
+ printDual({
12541
+ json: {
12542
+ brain: { id: renamed.id, name: renamed.name, api_url: server },
12543
+ renamed_from: renamed.renamed_from,
12544
+ /** False when the brain already carried this name — a repeat, not a failure. */
12545
+ changed,
12546
+ /** Whether this machine's own pointer was rewritten to carry the new name. */
12547
+ pointer_updated: pointerUpdated,
12548
+ next
12549
+ },
12550
+ human: () => {
12551
+ printHuman(
12552
+ c.green(
12553
+ changed ? `\u2713 renamed \u201C${oneLine(renamed.renamed_from ?? "", WIDTH.title)}\u201D \u2192 \u201C${oneLine(renamed.name, WIDTH.title)}\u201D` : `\u2713 \u201C${oneLine(renamed.name, WIDTH.title)}\u201D is already what it is called \u2014 nothing changed`
12554
+ )
12555
+ );
12556
+ if (changed) {
12557
+ printHuman(
12558
+ c.gray(" the notes, the history and every id are untouched \u2014 only the label moved")
12559
+ );
12560
+ }
12561
+ if (pointerUpdated) {
12562
+ printHuman(c.gray(" this machine reads this brain, and now calls it that too"));
12563
+ } else if (pointed !== void 0) {
12564
+ printHuman(
12565
+ c.gray(`
12566
+ This machine still reads \u201C${oneLine(pointed.name, WIDTH.title)}\u201D.`)
12567
+ );
12568
+ }
12569
+ printHuman(c.gray(`
12570
+ ${next}`));
12571
+ }
12572
+ });
12573
+ });
12574
+ }
12575
+ async function resolveTarget2(args) {
12576
+ const { named, pointed, pointer, server } = args;
12577
+ if (named === void 0) {
12578
+ if (pointed === void 0) throw notPointed(pointer, server);
12579
+ const token2 = await tokenFor(server);
12580
+ return { target: { id: pointed.id, name: pointed.name }, token: token2, viaPointer: true };
12581
+ }
12582
+ const token = await tokenFor(server);
12583
+ const brains = await listCloudBrains(token, server);
12584
+ const found = findCloudBrain(brains, named);
12585
+ if (!found) throw noSuchBrain(named, brains);
12586
+ return { target: { id: found.id, name: found.name }, token, viaPointer: false };
12587
+ }
12588
+ async function tokenFor(server) {
12589
+ const credentials = await requireCredentials(server).catch((err) => {
12590
+ if (err instanceof MemgError && err.code === "not_signed_in") {
12591
+ throw memgError(
12592
+ "not_signed_in",
12593
+ "renaming a hosted brain needs the account it lives on, and this machine is not signed in",
12594
+ "Nothing was renamed. `memgineering login --emit-only`, show the user the code it\nprints, then run this again \u2014 the token is claimed once they approve."
12595
+ );
12596
+ }
12597
+ throw err;
12598
+ });
12599
+ return credentials.token;
12600
+ }
12601
+ async function renameCloudBrain(args) {
12602
+ const { token, server, id, name, viaPointer } = args;
12603
+ try {
12604
+ const res = await apiRequest({
12605
+ method: "PATCH",
12606
+ path: `/v1/brains/${encodeURIComponent(id)}`,
12607
+ token,
12608
+ baseUrl: server,
12609
+ body: { name }
12610
+ });
12611
+ return res.body;
12612
+ } catch (err) {
12613
+ if (!(err instanceof MemgError)) throw err;
12614
+ if (err.code === "not_found") {
12615
+ throw new MemgError({
12616
+ code: err.code,
12617
+ message: `${server} answered as if \`PATCH /v1/brains/:id\` does not exist \u2014 it predates renaming`,
12618
+ hint: "Nothing was renamed, and the brain and its name are unchanged. Nothing in this CLI will change that: the server needs its brain-api updated to a version that has the route. Every other verb still works against it, and `memgineering brains` lists what the account holds."
12619
+ });
12620
+ }
12621
+ if (err.code === "brain_not_found" && viaPointer) {
12622
+ throw new MemgError({
12623
+ code: err.code,
12624
+ message: err.message,
12625
+ hint: `Nothing was renamed. This machine is pointed at a brain this account no longer has \u2014 deleted somewhere else, or made on a different account. \`memgineering brains\` lists what is actually there and \`memgineering use --brain <name-or-id>\` points this machine at one of them; then run this again. The server said: ${err.hint}`
12626
+ });
12627
+ }
12628
+ throw err;
12629
+ }
12630
+ }
12631
+ function notPointed(pointer, server) {
12632
+ return memgError(
12633
+ "invalid_input",
12634
+ pointer === void 0 ? "this machine is not reading a hosted brain" : `this machine is not reading a hosted brain on ${server} \u2014 its pointer names one on ${pointer.api_url}`,
12635
+ "Nothing was renamed. `memgineering brains` lists the account\u2019s brains; rename one with `memgineering rename <new name> --brain <name-or-id>`, or point this machine first with `memgineering use --brain <name>`."
12636
+ );
12637
+ }
12638
+ function noSuchBrain(named, brains) {
12639
+ const available = brains.map((brain) => ` ${oneLine(brain.name, WIDTH.title)} (${oneLine(brain.id, WIDTH.handle)})`).join("\n");
12640
+ return memgError(
12641
+ "invalid_input",
12642
+ `no hosted brain called "${oneLine(named, WIDTH.title)}"`,
12643
+ brains.length === 0 ? "Nothing was renamed. This account holds no hosted brains at all, so there is nothing here to rename \u2014 `memgineering create <name>` makes one." : `Nothing was renamed. This account has:
12644
+ ${available}
12645
+ Use one of those names or ids after \`--brain\`. The NEW name is the first argument.`
12646
+ );
12647
+ }
12648
+ function nextStep3(renamed) {
12649
+ if (renamed.renamed_from === null) {
12650
+ return `Nothing changed: \u201C${oneLine(renamed.name, WIDTH.title)}\u201D is already what this brain is called. If a different brain was meant, \`memgineering brains\` lists the account's with their ids.`;
12651
+ }
12652
+ return `Other machines that point at this brain keep showing the old name until their next command against it; nothing on them breaks \u2014 the pointer holds the id. \`--brain "${oneLine(renamed.renamed_from, WIDTH.title)}"\` no longer matches anywhere; use the new name or the id.`;
12653
+ }
12654
+
12655
+ // src/commands/delete-brain.ts
12656
+ init_errors();
12657
+ init_api_client();
12658
+ init_cloud_brain();
12659
+ init_config();
12660
+ init_ui();
12661
+ import { Command as Command10 } from "commander";
12662
+ import prompts from "prompts";
12663
+ function deleteBrainCommand() {
12664
+ return new Command10("delete-brain").description("delete a hosted brain and everything in it, for good").argument("<name-or-id>", "which hosted brain to delete, by its name or id").option("--confirm <exact-name>", "the brain\u2019s name, typed exactly, authorising the deletion").addHelpText(
12665
+ "after",
12666
+ "\nIrreversible, and server-side: the brain, every note in it, its ledger and its\nmeasurements. `memgineering unlink --brain` is the other thing \u2014 it only stops\nthis machine reading a brain and deletes nothing. Run this ONLY when the person\nhas explicitly asked for this brain to be deleted, and pass --confirm with the\nexact name. A wrongly-named brain is a rename, not a delete."
12667
+ ).action(async (named, opts) => {
12668
+ if (named.startsWith("--")) {
12669
+ throw memgError(
12670
+ "invalid_input",
12671
+ `\`delete-brain\` was given the flag \`${oneLine(named, 32)}\` as a brain name`,
12672
+ 'Nothing was deleted. The first argument is WHICH BRAIN to delete, and the flag written there was read as its name. Put the name or id first \u2014 `memgineering delete-brain <name-or-id> --confirm "<name>"`. If a brain really is called that, pass its id instead; `memgineering brains` lists them.'
12673
+ );
12674
+ }
12675
+ const confirmFlag = opts.confirm;
12676
+ if (confirmFlag !== void 0 && confirmFlag.startsWith("--")) {
12677
+ throw memgError(
12678
+ "invalid_input",
12679
+ `\`--confirm\` was given the flag \`${oneLine(confirmFlag, WIDTH.title)}\` as a name`,
12680
+ "Nothing was deleted. `--confirm` takes the brain\u2019s own name, typed exactly, and the flag written after it was read as that name. Put the name immediately after `--confirm`, then pass the other flag separately."
12681
+ );
12682
+ }
12683
+ const server = apiUrl();
12684
+ const token = await tokenFor2(server);
12685
+ const brains = await listCloudBrains(token, server);
12686
+ const target = findCloudBrain(brains, named);
12687
+ if (!target) throw noSuchBrain2(named, brains);
12688
+ const authorised = await authorise(target, confirmFlag);
12689
+ if (authorised === void 0) {
12690
+ printHuman(c.yellow("Cancelled \u2014 nothing was deleted."));
12691
+ return;
12692
+ }
12693
+ const deleted = await deleteCloudBrain({ token, server, id: target.id, authorised });
12694
+ const cfg = await loadConfig();
12695
+ const pointer = cfg.brain.cloud;
12696
+ const pointerCleared = pointer !== void 0 && pointer.id === deleted.deleted && pointer.api_url === server;
12697
+ if (pointerCleared) {
12698
+ await saveConfig({
12699
+ ...cfg,
12700
+ brain: {
12701
+ brains: cfg.brain.brains,
12702
+ ...cfg.brain.default === void 0 ? {} : { default: cfg.brain.default }
12703
+ }
12704
+ });
12705
+ }
12706
+ const hint = deleted.hint ?? "The brain, its notes and its ledger are gone. `memgineering brains` lists what is left.";
12707
+ printDual({
12708
+ json: {
12709
+ deleted: { id: deleted.deleted, name: deleted.name },
12710
+ /** Null when the server said nothing — which is not the same as zero. */
12711
+ notes_deleted: deleted.notes_deleted ?? null,
12712
+ /** Whether this machine had been reading it, and has now stopped. */
12713
+ pointer_cleared: pointerCleared,
12714
+ hint
12715
+ },
12716
+ human: () => {
12717
+ printHuman(
12718
+ c.green(`\u2713 deleted \u201C${oneLine(deleted.name, WIDTH.title)}\u201D`) + c.gray(` (${oneLine(deleted.deleted, WIDTH.handle)})`)
12719
+ );
12720
+ printHuman(
12721
+ c.gray(
12722
+ deleted.notes_deleted === void 0 ? " its notes, its ledger and its measurements went with it" : ` ${deleted.notes_deleted} note(s) went with it, along with its ledger and its measurements`
12723
+ )
12724
+ );
12725
+ if (pointerCleared) {
12726
+ printHuman(
12727
+ c.yellow(
12728
+ `
12729
+ \u26A0 THIS MACHINE NO LONGER READS A HOSTED BRAIN \u2014 it was reading \u201C${oneLine(deleted.name, WIDTH.title)}\u201D, which is now deleted.`
12730
+ )
12731
+ );
12732
+ printHuman(
12733
+ c.gray(
12734
+ " `memgineering brains` lists what is left; `use --brain <name>` points at one.\n `recall --local` still reads a folder, if one is linked."
12735
+ )
12736
+ );
12737
+ }
12738
+ printHuman(c.gray(`
12739
+ ${hint}`));
12740
+ }
12741
+ });
12742
+ });
12743
+ }
12744
+ async function tokenFor2(server) {
12745
+ const credentials = await requireCredentials(server).catch((err) => {
12746
+ if (err instanceof MemgError && err.code === "not_signed_in") {
12747
+ throw memgError(
12748
+ "not_signed_in",
12749
+ "deleting a hosted brain needs the account it lives on, and this machine is not signed in",
12750
+ "Nothing was deleted. `memgineering login --emit-only`, show the user the code it\nprints, then run this again \u2014 the token is claimed once they approve."
12751
+ );
12752
+ }
12753
+ throw err;
12754
+ });
12755
+ return credentials.token;
12756
+ }
12757
+ async function authorise(target, confirmFlag) {
12758
+ if (confirmFlag !== void 0) {
12759
+ if (confirmFlag !== target.name) throw confirmationMismatch(target);
12760
+ return confirmFlag;
12761
+ }
12762
+ if (!isInteractive()) throw cannotAsk(target);
12763
+ return retypeTheName(target);
12764
+ }
12765
+ function confirmationMismatch(target) {
12766
+ const name = oneLine(target.name, WIDTH.title);
12767
+ const removes = target.note_count === void 0 ? "Deleting it removes its notes, its ledger and its measurements, and none of that comes back." : `Deleting it removes its ${target.note_count} note(s), its ledger and its measurements, and none of that comes back.`;
12768
+ return memgError(
12769
+ "invalid_input",
12770
+ "the confirmation does not match this brain\u2019s name",
12771
+ `Nothing was deleted. The brain is called \u201C${name}\u201D \u2014 exactly that, including case and spacing. ${removes} Pass --confirm "${name}" only if the person explicitly asked for THIS brain to be deleted.`
12772
+ );
12773
+ }
12774
+ function cannotAsk(target) {
12775
+ const name = oneLine(target.name, WIDTH.title);
12776
+ return memgError(
12777
+ "invalid_input",
12778
+ "cannot ask for confirmation here \u2014 this is not an interactive terminal",
12779
+ `Nothing was deleted. Two ways forward: run this in a terminal the person can see and let them type the name; or, once they have explicitly said \u201Cdelete ${name}\u201D, pass --confirm "${name}". Deciding for them is the one thing this gate exists to prevent.`
12780
+ );
12781
+ }
12782
+ async function retypeTheName(target) {
12783
+ printHuman(c.bold(`
12784
+ Delete \u201C${oneLine(target.name, WIDTH.title)}\u201D?`));
12785
+ printHuman(c.gray(` id ${oneLine(target.id, WIDTH.handle)}`));
12786
+ printHuman(
12787
+ c.gray(
12788
+ target.note_count === void 0 ? " notes (this server does not say how many)" : ` notes ${target.note_count}`
12789
+ )
12790
+ );
12791
+ printHuman(
12792
+ c.yellow(
12793
+ "\n This deletes the brain on the server \u2014 every note in it, its ledger and\n its measurements. None of it comes back, and no other machine holds a copy."
12794
+ )
12795
+ );
12796
+ printHuman(
12797
+ c.gray(
12798
+ "\n `memgineering unlink --brain` is the other thing: it stops this machine\n reading a brain and deletes nothing. A wrongly-named brain is a rename."
12799
+ )
12800
+ );
12801
+ const { typed } = await prompts({
12802
+ type: "text",
12803
+ name: "typed",
12804
+ message: "Type the brain\u2019s name exactly to delete it"
12805
+ });
12806
+ return typed === target.name ? typed : void 0;
12807
+ }
12808
+ function noSuchBrain2(named, brains) {
12809
+ const available = brains.map((brain) => {
12810
+ const count = brain.note_count === void 0 ? "" : `, ${brain.note_count} notes`;
12811
+ return ` ${oneLine(brain.name, WIDTH.title)} (${oneLine(brain.id, WIDTH.handle)}${count})`;
12812
+ }).join("\n");
12813
+ return memgError(
12814
+ "invalid_input",
12815
+ `no brain called \u201C${oneLine(named, WIDTH.title)}\u201D on this account`,
12816
+ brains.length === 0 ? "Nothing was deleted. This account holds no hosted brains at all. If it was already deleted, that is what is left: nothing." : `Nothing was deleted. This account has:
12817
+ ${available}
12818
+ If it was already deleted, the list above is what is left.`
12819
+ );
12820
+ }
12821
+ async function deleteCloudBrain(args) {
12822
+ const { token, server, id, authorised } = args;
12823
+ const res = await apiRequest({
12824
+ method: "DELETE",
12825
+ path: `/v1/brains/${encodeURIComponent(id)}`,
12826
+ token,
12827
+ baseUrl: server,
12828
+ body: { confirm: authorised }
12829
+ });
12830
+ return res.body;
12831
+ }
12832
+
12438
12833
  // src/commands/curation.ts
12439
12834
  init_src();
12440
12835
  import { stat as stat2 } from "fs/promises";
12441
12836
  import { resolve as resolve6 } from "path";
12442
- import { Command as Command10 } from "commander";
12837
+ import { Command as Command12 } from "commander";
12443
12838
 
12444
12839
  // ../../packages/storage-adapter/src/local-file/ignore-file.ts
12445
12840
  init_atomic();
@@ -12541,7 +12936,7 @@ init_vault();
12541
12936
  init_ui();
12542
12937
  init_link();
12543
12938
  function lifecycleCommand(action) {
12544
- return new Command10(action).description(
12939
+ return new Command12(action).description(
12545
12940
  action === "retire" ? "mark a memory as no longer current \u2014 the note itself is not changed" : "mark a retired memory as current again"
12546
12941
  ).argument("<ref>", "handle, id, path, or exact title").option("--reason <text>", "why \u2014 kept in the ledger").option("--vault <path>", "which brain the memory is in").option("--local", "use the brain on this machine, even when signed in to a hosted one").action(async (ref, opts) => {
12547
12942
  const rationale = sanitizeRationale(opts.reason);
@@ -12624,7 +13019,7 @@ Undo: \`${undoHint(record.op_id, opts.vault)}\``));
12624
13019
  });
12625
13020
  }
12626
13021
  function ignoreCommand(mode) {
12627
- return new Command10(mode).description(
13022
+ return new Command12(mode).description(
12628
13023
  mode === "exclude" ? "stop reading a note entirely \u2014 the note itself is not changed" : "read a note again after excluding it"
12629
13024
  ).argument("<path>", "path inside the brain, e.g. decisions/launch-date.md").option("--reason <text>", "why \u2014 kept in the ledger").option("--vault <path>", "which brain the note is in").option("--local", "use the brain on this machine, even when signed in to a hosted one").action(async (path, opts) => {
12630
13025
  const rationale = sanitizeRationale(opts.reason);
@@ -12765,13 +13160,13 @@ var excludeCommand = () => ignoreCommand("exclude");
12765
13160
  var unexcludeCommand = () => ignoreCommand("unexclude");
12766
13161
 
12767
13162
  // src/commands/evidence.ts
12768
- import { Command as Command11 } from "commander";
13163
+ import { Command as Command13 } from "commander";
12769
13164
  init_index_build();
12770
13165
  init_cloud_brain();
12771
13166
  init_vault();
12772
13167
  init_ui();
12773
13168
  function evidenceCommand() {
12774
- return new Command11("evidence").description("how much a memory has been used, and why it last changed").argument("<ref>", "handle, id, path, or exact title").option("--vault <path>", "which brain to read").option("--local", "read the brain on this machine, even when signed in to a hosted one").option(
13169
+ return new Command13("evidence").description("how much a memory has been used, and why it last changed").argument("<ref>", "handle, id, path, or exact title").option("--vault <path>", "which brain to read").option("--local", "read the brain on this machine, even when signed in to a hosted one").option(
12775
13170
  "--brain <name>",
12776
13171
  "read from one existing hosted brain by name or id, without changing the pointer"
12777
13172
  ).action(async (ref, opts) => {
@@ -13025,7 +13420,7 @@ init_config();
13025
13420
  import { createHash as createHash7 } from "crypto";
13026
13421
  import { mkdir as mkdir11, readdir as readdir6, stat as stat3, unlink as unlink3, writeFile as writeFile9 } from "fs/promises";
13027
13422
  import { dirname as dirname3, isAbsolute as isAbsolute2, join as join16, resolve as resolve7 } from "path";
13028
- import { Command as Command12 } from "commander";
13423
+ import { Command as Command14 } from "commander";
13029
13424
 
13030
13425
  // src/lib/rules.ts
13031
13426
  init_src();
@@ -13067,7 +13462,7 @@ init_deadline();
13067
13462
  init_cloud_brain();
13068
13463
  init_ui();
13069
13464
  function guardCommand() {
13070
- return new Command12("guard").description("hook entry \u2014 show the user\u2019s rules before the first edit of a session").option("--context-dir <path>", "where the work is happening, when stdin does not say").option("--at <event>", "pre-tool-use | stop", "pre-tool-use").action(async (opts) => {
13465
+ return new Command14("guard").description("hook entry \u2014 show the user\u2019s rules before the first edit of a session").option("--context-dir <path>", "where the work is happening, when stdin does not say").option("--at <event>", "pre-tool-use | stop", "pre-tool-use").action(async (opts) => {
13071
13466
  try {
13072
13467
  const out = await withDeadline(HOOK_DEADLINE_MS, async () => {
13073
13468
  const payload = await readPayload();
@@ -13251,12 +13646,12 @@ init_link();
13251
13646
  // src/commands/log.ts
13252
13647
  init_walk();
13253
13648
  import { lstat as lstat4, readFile as readFile17 } from "fs/promises";
13254
- import { Command as Command13 } from "commander";
13649
+ import { Command as Command15 } from "commander";
13255
13650
  init_cloud_brain();
13256
13651
  init_vault();
13257
13652
  init_ui();
13258
13653
  function logCommand() {
13259
- return new Command13("log").description("what changed in this brain, and what can still be undone").option("--limit <n>", "how many operations", (v) => parseInt(v, 10), 20).option("--verbose", "include hashes and reverse patches").option("--vault <path>", "which brain to read").option("--local", "read the brain on this machine, even when signed in to a hosted one").option(
13654
+ return new Command15("log").description("what changed in this brain, and what can still be undone").option("--limit <n>", "how many operations", (v) => parseInt(v, 10), 20).option("--verbose", "include hashes and reverse patches").option("--vault <path>", "which brain to read").option("--local", "read the brain on this machine, even when signed in to a hosted one").option(
13260
13655
  "--brain <name>",
13261
13656
  "read one existing hosted brain by name or id, without changing the pointer"
13262
13657
  ).action(async (opts) => {
@@ -13493,9 +13888,9 @@ init_api_client();
13493
13888
  init_credentials();
13494
13889
  init_pending_login();
13495
13890
  init_ui();
13496
- import { Command as Command15 } from "commander";
13891
+ import { Command as Command17 } from "commander";
13497
13892
  function logoutCommand() {
13498
- return new Command15("logout").description("sign out of your account \u2014 the pointer and your notes stay").option("--all", "revoke every token on the account, not just this machine\u2019s").option(
13893
+ return new Command17("logout").description("sign out of your account \u2014 the pointer and your notes stay").option("--all", "revoke every token on the account, not just this machine\u2019s").option(
13499
13894
  "--local",
13500
13895
  "forget the token here without telling the server \u2014 only for a server you cannot reach"
13501
13896
  ).action(async (opts) => {
@@ -13590,12 +13985,12 @@ function logoutCommand() {
13590
13985
 
13591
13986
  // src/commands/onboard.ts
13592
13987
  init_src();
13593
- import { Command as Command16 } from "commander";
13988
+ import { Command as Command18 } from "commander";
13594
13989
  init_cloud_brain();
13595
13990
  init_vault();
13596
13991
  init_ui();
13597
13992
  function onboardCommand() {
13598
- return new Command16("onboard").description("what to ask so the brain knows who it belongs to").option("--vault <path>", "which brain").option("--local", "use the brain on this machine, even when signed in to a hosted one").option(
13993
+ return new Command18("onboard").description("what to ask so the brain knows who it belongs to").option("--vault <path>", "which brain").option("--local", "use the brain on this machine, even when signed in to a hosted one").option(
13599
13994
  "--brain <name>",
13600
13995
  "read one existing hosted brain by name or id, without changing the pointer"
13601
13996
  ).action(async (opts) => {
@@ -13741,9 +14136,9 @@ init_cloud_brain();
13741
14136
  init_ui();
13742
14137
  import { mkdir as mkdir12, readFile as readFile18, writeFile as writeFile10 } from "fs/promises";
13743
14138
  import { dirname as dirname4, isAbsolute as isAbsolute3, join as join17, resolve as resolve8, sep as sep2 } from "path";
13744
- import { Command as Command17 } from "commander";
14139
+ import { Command as Command19 } from "commander";
13745
14140
  function pullCommand() {
13746
- return new Command17("pull").description("download your hosted brain to a folder on this machine").argument("[destination]", "folder to write into (created if missing)").option("--brain <name>", "which hosted brain to download").option("--dry-run", "say what would be written and write nothing").action(async (destination, opts) => {
14141
+ return new Command19("pull").description("download your hosted brain to a folder on this machine").argument("[destination]", "folder to write into (created if missing)").option("--brain <name>", "which hosted brain to download").option("--dry-run", "say what would be written and write nothing").action(async (destination, opts) => {
13747
14142
  const { brain: source, others } = await resolveSource(opts.brain);
13748
14143
  const target = resolve8(destination ?? `./${oneLine(source.name, WIDTH.title)}`);
13749
14144
  const first = await cloudListNotes(source, { limit: 1 });
@@ -13947,7 +14342,7 @@ init_errors();
13947
14342
  init_config();
13948
14343
  init_api_client();
13949
14344
  init_cloud_brain();
13950
- import { Command as Command18 } from "commander";
14345
+ import { Command as Command20 } from "commander";
13951
14346
  init_vault();
13952
14347
  init_ui();
13953
14348
  var HELD_BACK_WORDING = {
@@ -13956,7 +14351,7 @@ var HELD_BACK_WORDING = {
13956
14351
  };
13957
14352
  var printHeldBack2 = (held) => printHeldBack(held, HELD_BACK_WORDING);
13958
14353
  function pushCommand() {
13959
- return new Command18("push").description("upload the brain on this machine to your hosted one").option("--vault <path>", "which local brain to upload").option("--brain <name>", "the hosted brain to upload into (created if it does not exist)").option("--dry-run", "list what would be uploaded and send nothing").action(async (opts) => {
14354
+ return new Command20("push").description("upload the brain on this machine to your hosted one").option("--vault <path>", "which local brain to upload").option("--brain <name>", "the hosted brain to upload into (created if it does not exist)").option("--dry-run", "list what would be uploaded and send nothing").action(async (opts) => {
13960
14355
  const { brain: local } = await resolveBrain(opts.vault);
13961
14356
  const adapter = await openBrainOrExplain(local.root);
13962
14357
  const scan = await adapter.scan();
@@ -14136,7 +14531,7 @@ async function resolveDestination(named, root) {
14136
14531
  // src/commands/recall.ts
14137
14532
  init_src();
14138
14533
  init_config();
14139
- import { Command as Command19 } from "commander";
14534
+ import { Command as Command21 } from "commander";
14140
14535
  import { basename, resolve as resolve9 } from "path";
14141
14536
  init_index_build();
14142
14537
  init_cloud_brain();
@@ -14144,7 +14539,7 @@ init_errors();
14144
14539
  init_vault();
14145
14540
  init_ui();
14146
14541
  function recallCommand() {
14147
- return new Command19("recall").description("recall memory cards for a question").argument("<query>", "what you want to remember").option("--vault <path>", "which brain to search").option("--local", "search the brain on this machine, even when signed in to a hosted one").option(
14542
+ return new Command21("recall").description("recall memory cards for a question").argument("<query>", "what you want to remember").option("--vault <path>", "which brain to search").option("--local", "search the brain on this machine, even when signed in to a hosted one").option(
14148
14543
  "--brain <name>",
14149
14544
  "search one existing hosted brain by name or id, without changing the pointer"
14150
14545
  ).option("--scope <scope>", "restrict to one scope").option("--limit <n>", "max cards", (v) => parseInt(v, 10), 8).option(
@@ -14611,14 +15006,14 @@ function scriptsIn(text) {
14611
15006
  }
14612
15007
 
14613
15008
  // src/commands/remember.ts
14614
- import { Command as Command20 } from "commander";
15009
+ import { Command as Command22 } from "commander";
14615
15010
  init_src();
14616
15011
  init_errors();
14617
15012
  init_cloud_brain();
14618
15013
  init_vault();
14619
15014
  init_ui();
14620
15015
  function rememberCommand() {
14621
- return new Command20("remember").description("write something down now, with no approval step").argument("<text>", "what to remember").option("--vault <path>", "which brain to write to").option("--local", "write to the brain on this machine, even when signed in to a hosted one").option("--scope <scope>", "what this is about \u2014 a project, an area").option(
15016
+ return new Command22("remember").description("write something down now, with no approval step").argument("<text>", "what to remember").option("--vault <path>", "which brain to write to").option("--local", "write to the brain on this machine, even when signed in to a hosted one").option("--scope <scope>", "what this is about \u2014 a project, an area").option(
14622
15017
  "--rule",
14623
15018
  "this is a decision that binds what you do, not a fact to look up (shown before edits)"
14624
15019
  ).option(
@@ -14793,13 +15188,13 @@ function yamlString(value) {
14793
15188
  // src/commands/resurface.ts
14794
15189
  init_deadline();
14795
15190
  init_src();
14796
- import { Command as Command21 } from "commander";
15191
+ import { Command as Command23 } from "commander";
14797
15192
  init_index_build();
14798
15193
  init_cloud_brain();
14799
15194
  init_vault();
14800
15195
  init_ui();
14801
15196
  function resurfaceCommand() {
14802
- return new Command21("resurface").description("what is worth having in view right now, without being asked").option("--context-dir <path>", "where the work is happening (default: cwd)").option("--limit <n>", "how many cards", (v) => parseInt(v, 10), 5).option("--vault <path>", "which brain").option("--local", "use the brain on this machine, even when signed in to a hosted one").option(
15197
+ return new Command23("resurface").description("what is worth having in view right now, without being asked").option("--context-dir <path>", "where the work is happening (default: cwd)").option("--limit <n>", "how many cards", (v) => parseInt(v, 10), 5).option("--vault <path>", "which brain").option("--local", "use the brain on this machine, even when signed in to a hosted one").option(
14803
15198
  "--brain <name>",
14804
15199
  "read one existing hosted brain by name or id, without changing the pointer"
14805
15200
  ).option(
@@ -14984,11 +15379,11 @@ exists only for a brain on this machine.`
14984
15379
  }
14985
15380
 
14986
15381
  // src/commands/rules.ts
14987
- import { Command as Command22 } from "commander";
15382
+ import { Command as Command24 } from "commander";
14988
15383
  init_cloud_brain();
14989
15384
  init_ui();
14990
15385
  function rulesCommand() {
14991
- return new Command22("rules").description("the user\u2019s standing decisions \u2014 what binds you here").option("--vault <path>", "which brain").option("--local", "use the brain on this machine, even when signed in to a hosted one").option(
15386
+ return new Command24("rules").description("the user\u2019s standing decisions \u2014 what binds you here").option("--vault <path>", "which brain").option("--local", "use the brain on this machine, even when signed in to a hosted one").option(
14992
15387
  "--brain <name>",
14993
15388
  "read one existing hosted brain by name or id, without changing the pointer"
14994
15389
  ).action(async (opts) => {
@@ -15090,7 +15485,7 @@ Retire what no longer decides anything: \`memgineering retire <handle>\`.` : "Th
15090
15485
  init_setup();
15091
15486
 
15092
15487
  // src/commands/update.ts
15093
- import { Command as Command24 } from "commander";
15488
+ import { Command as Command26 } from "commander";
15094
15489
 
15095
15490
  // src/version.ts
15096
15491
  import { readFileSync } from "fs";
@@ -15338,7 +15733,7 @@ function shortMessage(err) {
15338
15733
  // src/commands/update.ts
15339
15734
  init_ui();
15340
15735
  function updateCommand() {
15341
- return new Command24("update").description("update memgineering itself").option("--check", "report what is available and change nothing").action(async (opts) => {
15736
+ return new Command26("update").description("update memgineering itself").option("--check", "report what is available and change nothing").action(async (opts) => {
15342
15737
  const status = await checkForUpdate(VERSION);
15343
15738
  const provenance = await detectProvenance();
15344
15739
  const previous = await readUpdateState();
@@ -15447,14 +15842,14 @@ function reportBelowMinimum(below) {
15447
15842
  init_src();
15448
15843
  import { readFile as readFile21, rm as rm8 } from "fs/promises";
15449
15844
  import { join as join23 } from "path";
15450
- import { Command as Command25 } from "commander";
15845
+ import { Command as Command27 } from "commander";
15451
15846
  init_atomic();
15452
15847
  init_errors();
15453
15848
  init_cloud_brain();
15454
15849
  init_vault();
15455
15850
  init_ui();
15456
15851
  function undoCommand() {
15457
- return new Command25("undo").description("take back the last change, or a named one").argument("[op]", "operation id (default: the most recent)").option("--reason <text>", "why it is being taken back \u2014 kept in the ledger").option("--vault <path>", "which brain to act on").option("--local", "act on the brain on this machine, even when signed in to a hosted one").action(
15852
+ return new Command27("undo").description("take back the last change, or a named one").argument("[op]", "operation id (default: the most recent)").option("--reason <text>", "why it is being taken back \u2014 kept in the ledger").option("--vault <path>", "which brain to act on").option("--local", "act on the brain on this machine, even when signed in to a hosted one").action(
15458
15853
  async (opId, opts) => {
15459
15854
  const rationale = sanitizeRationale(opts.reason);
15460
15855
  const where = await resolveTarget({
@@ -15658,11 +16053,11 @@ init_api_client();
15658
16053
  init_cloud_brain();
15659
16054
  init_config();
15660
16055
  init_ui();
15661
- import { Command as Command26 } from "commander";
16056
+ import { Command as Command28 } from "commander";
15662
16057
  import stringWidth from "string-width";
15663
16058
  var NAME_WIDTH2 = 48;
15664
16059
  function brainsCommand() {
15665
- return new Command26("brains").description("list the hosted brains on your account, and which one this machine reads").addHelpText(
16060
+ return new Command28("brains").description("list the hosted brains on your account, and which one this machine reads").addHelpText(
15666
16061
  "after",
15667
16062
  "\nReads only. `memgineering use --brain <name>` points this machine at one of\nthem. `memgineering create <name>` makes a new empty one, and\n`push --brain <name>` makes one from a folder on this machine."
15668
16063
  ).action(async () => {
@@ -15704,7 +16099,7 @@ function brainsCommand() {
15704
16099
  // Named at the top level too, so a caller does not have to scan the
15705
16100
  // array to answer the question it most often has.
15706
16101
  current: currentId,
15707
- hint: currentId === null ? "This machine is not pointed at any of them. `memgineering use --brain <name>` picks one \u2014 it moves the pointer and uploads nothing." : "Switch with `memgineering use --brain <name>`, which moves the pointer and uploads nothing. `memgineering create <name>` makes a new empty brain; `push --brain <name>` makes one from a folder on this machine."
16102
+ hint: currentId === null ? "This machine is not pointed at any of them. `memgineering use --brain <name>` picks one \u2014 it moves the pointer and uploads nothing." : 'Switch with `memgineering use --brain <name>`, which moves the pointer and uploads nothing. `memgineering create <name>` makes a new empty brain; `push --brain <name>` makes one from a folder on this machine. `memgineering rename <new name>` renames the one this machine reads; `memgineering delete-brain <name> --confirm "<name>"` permanently deletes one \u2014 only on the owner\'s explicit ask.'
15708
16103
  },
15709
16104
  human: () => {
15710
16105
  printHuman(
@@ -15725,7 +16120,7 @@ function brainsCommand() {
15725
16120
  printHuman("");
15726
16121
  printHuman(
15727
16122
  c.gray(
15728
- currentId === null ? " This machine is not pointed at any of them.\n `memgineering use --brain <name>` picks one \u2014 nothing is uploaded." : " `memgineering use --brain <name>` switches \u2014 nothing is uploaded.\n `memgineering create <name>` makes a new empty one; `push --brain <name>`\n makes one from a folder on this machine."
16123
+ currentId === null ? " This machine is not pointed at any of them.\n `memgineering use --brain <name>` picks one \u2014 nothing is uploaded." : ' `memgineering use --brain <name>` switches \u2014 nothing is uploaded.\n `memgineering create <name>` makes a new empty one; `push --brain <name>`\n makes one from a folder on this machine.\n `memgineering rename <new name>` renames the one this machine reads;\n `delete-brain <name> --confirm "<name>"` deletes one \u2014 only on the owner\'s\n explicit ask.'
15729
16124
  )
15730
16125
  );
15731
16126
  }
@@ -15742,7 +16137,7 @@ init_credentials();
15742
16137
  init_cloud_brain();
15743
16138
  init_config();
15744
16139
  import { isAbsolute as isAbsolute5, relative as relative3, resolve as resolve12 } from "path";
15745
- import { Command as Command27 } from "commander";
16140
+ import { Command as Command29 } from "commander";
15746
16141
  init_vault();
15747
16142
  init_ui();
15748
16143
  function lostDefaultLines(missing) {
@@ -15761,7 +16156,7 @@ var VIA_MEANS = {
15761
16156
  default: "because it is your default brain (`memgineering use --default <path>` changes it)"
15762
16157
  };
15763
16158
  function useCommand() {
15764
- return new Command27("use").description(
16159
+ return new Command29("use").description(
15765
16160
  "say which brain answers here \u2014 `--brain <name>` for the whole machine, or a path to bind this folder"
15766
16161
  ).argument("[brain]", "path to the brain \u2014 omit to show what this folder resolves to").option("--brain <name>", "point this machine at a hosted brain, by its name or id").option("--here <dir>", "write the pointer in this directory instead of the cwd").option(
15767
16162
  "--default",
@@ -16069,9 +16464,9 @@ async function pointAtHosted(named, opts, path) {
16069
16464
  // src/commands/whoami.ts
16070
16465
  init_api_client();
16071
16466
  init_ui();
16072
- import { Command as Command28 } from "commander";
16467
+ import { Command as Command30 } from "commander";
16073
16468
  function whoamiCommand() {
16074
- return new Command28("whoami").description("show which account this machine is signed in as").action(async () => {
16469
+ return new Command30("whoami").description("show which account this machine is signed in as").action(async () => {
16075
16470
  const base = apiUrl();
16076
16471
  const credentials = await requireCredentials(base);
16077
16472
  const res = await apiRequest({
@@ -16206,7 +16601,7 @@ async function currentNextStep() {
16206
16601
  init_ui();
16207
16602
  init_brand();
16208
16603
  function buildProgram() {
16209
- const program = new Command29("memgineering").version(VERSION).description("memgineering \u2014 one memory for the AI you connect").option("--json", "emit JSON to stdout instead of markdown", false).hook("preAction", async (thisCommand, actionCommand) => {
16604
+ const program = new Command31("memgineering").version(VERSION).description("memgineering \u2014 one memory for the AI you connect").option("--json", "emit JSON to stdout instead of markdown", false).hook("preAction", async (thisCommand, actionCommand) => {
16210
16605
  const opts = thisCommand.optsWithGlobals();
16211
16606
  if (opts.json || process.env[BRAND.jsonEnvVar] === "1") setJsonMode(true);
16212
16607
  await announceNextStep(actionCommand.name());
@@ -16229,6 +16624,8 @@ function buildProgram() {
16229
16624
  program.addCommand(useCommand());
16230
16625
  program.addCommand(brainsCommand());
16231
16626
  program.addCommand(createCommand());
16627
+ program.addCommand(renameCommand());
16628
+ program.addCommand(deleteBrainCommand());
16232
16629
  program.addCommand(unlinkCommand());
16233
16630
  program.addCommand(reindexCommand());
16234
16631
  program.addCommand(syncRulesCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memgineering",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "private": false,
5
5
  "description": "One memory for the AI you connect. Recall, remember, and revise a brain your agents share \u2014 stored in your own folder.",
6
6
  "license": "Apache-2.0",