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 +31 -0
- package/assets/MEMGINEERING.md +1 -1
- package/assets/memgineering-setup/SKILL.md +20 -1
- package/dist/index.js +452 -55
- 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.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
|
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.
|
|
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
|
|
5175
|
-
import
|
|
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
|
|
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
|
|
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
|
|
5604
|
+
import { Command as Command16 } from "commander";
|
|
5605
5605
|
function loginCommand() {
|
|
5606
|
-
return new
|
|
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
|
|
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
|
|
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
|
|
8368
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: [
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
13891
|
+
import { Command as Command17 } from "commander";
|
|
13497
13892
|
function logoutCommand() {
|
|
13498
|
-
return new
|
|
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
|
|
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
|
|
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
|
|
14139
|
+
import { Command as Command19 } from "commander";
|
|
13745
14140
|
function pullCommand() {
|
|
13746
|
-
return new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
15382
|
+
import { Command as Command24 } from "commander";
|
|
14988
15383
|
init_cloud_brain();
|
|
14989
15384
|
init_ui();
|
|
14990
15385
|
function rulesCommand() {
|
|
14991
|
-
return new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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." :
|
|
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." :
|
|
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
|
|
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
|
|
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
|
|
16467
|
+
import { Command as Command30 } from "commander";
|
|
16073
16468
|
function whoamiCommand() {
|
|
16074
|
-
return new
|
|
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
|
|
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