memgineering 0.7.0 → 0.7.2

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,73 @@ language the reader wants. The bilingual rule the monorepo applies to
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.7.2] — 2026-08-18
15
+
16
+ Everyone who runs this CLI is an agent, and an agent can only act on what a
17
+ command actually prints. Three places were telling the user's agent nothing.
18
+
19
+ ### Added
20
+
21
+ - **Any command run before setup finishes now says so, in markdown, on stderr**
22
+ — what is missing, the two questions to ask, and the command to run, addressed
23
+ to the agent rather than to the person. The same text has been in the install
24
+ script all along and reached nobody: npm swallows lifecycle-script output on
25
+ `npm i -g`, which is the exact command the README gives. Measured — npm 11.13
26
+ showed it 0 times without `--foreground-scripts` and once with; npm 12 never.
27
+ So the CLI says it itself, where nothing can suppress it, and stops saying it
28
+ the moment the step is done.
29
+
30
+ It goes to stderr because stdout under `--json` is one parseable document, and
31
+ a preamble there would break every caller that parses it.
32
+
33
+ - **When setup has run and no brain is connected**, the same channel says that
34
+ instead, with `link` and `init` side by side and a note that an existing folder
35
+ needs no index and no frontmatter of its own — plain markdown is a brain.
36
+
37
+ ### Changed
38
+
39
+ - **`setup` now signs in by default**, on both the flag path and the interactive
40
+ one. It opens no browser: a pairing code is printed for the agent to show its
41
+ user, and nothing exists on the server until they approve it. `--no-login` is
42
+ the one flag that says "this machine only", and setup still reports which way
43
+ it went.
44
+
45
+ The old default skipped the account unless asked, to avoid sending anyone to a
46
+ consent screen they had not chosen. Separating the account from the browser
47
+ keeps that, and drops what it cost: an install that never mentioned accounts,
48
+ and a user who discovered weeks later that their memory did not follow them.
49
+
50
+ ### Agent guidance
51
+
52
+ - `memgineering-setup` now covers the sign-in default, what the pairing code
53
+ means, and the two ways to hand setup to someone who would rather do it
54
+ themselves (`--human` in your terminal, `--web` in their browser). **Restart
55
+ your agent session after upgrading** so the updated guidance loads.
56
+
57
+ ## [0.7.1] — 2026-08-17
58
+
59
+ ### Changed
60
+
61
+ - **The semantic-search figures 0.7.0 quoted were measured on the set it was
62
+ tuned against.** An independent set of 70 questions — written by someone
63
+ reading only the notes and never a search result, with the expected note fixed
64
+ from what they had just read — scores lower on the same brain and the same
65
+ server: the right note is in the top five for **72.9%** of them and ranked
66
+ first for **45.7%**, and for a question asked across a language, **61.1%** and
67
+ **33.3%**. Text search alone scores 47.1% / 35.7% overall and 5.6% / 0% across
68
+ a language on that set, so meaning still moves every category it touches, and
69
+ cross-language remains the largest move either set shows. Both sets are real
70
+ measurements; only the tuned one had been reported. Expect the lower pair from
71
+ a question nobody tuned for.
72
+
73
+ ### Fixed
74
+
75
+ - `recall --help` no longer offers `--detail summary`, `chunks`, and `full` as
76
+ though a hosted brain could answer them. It now says which two levels a hosted
77
+ brain does answer, and points at a brain in a folder or `open <handle>` for the
78
+ deeper ones. The refusal was already correct; the help text described a
79
+ different product.
80
+
14
81
  ## [0.7.0] — 2026-08-16
15
82
 
16
83
  Recall reads what a note says, not only what it is called. And the pile of
@@ -181,6 +248,7 @@ that goes one approved change at a time.
181
248
  the shape of its answers without changing its name.
182
249
 
183
250
  ### Fixed
251
+
184
252
  - **`push` now reports the notes it never sent.** A note held back by the name
185
253
  deny list or by an ignore rule was dropped before the upload and appeared in
186
254
  no summary — a 277-note brain reported `276 note(s) uploaded` and said nothing
@@ -205,7 +273,6 @@ that goes one approved change at a time.
205
273
  held each note, instead of pointing at `memgineering reindex` — which reports
206
274
  the name and content screens and says nothing about an ignore rule.
207
275
 
208
-
209
276
  - **A `supersedes:` written as a single value is no longer ignored.**
210
277
  `supersedes: mem-abc` is what somebody writes when one memory replaces one
211
278
  other, and it parsed to nothing at all — no relation, no complaint, no way to
@@ -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.7.0
5
+ version: 0.7.2
6
6
  ---
7
7
 
8
8
  # memgineering
@@ -38,6 +38,24 @@ memgineering setup --agent \
38
38
 
39
39
  `--dry-run` reports what would change without changing it.
40
40
 
41
+ **The account is the default, and the browser is not.** `setup --agent` starts a
42
+ sign-in on its own: it prints a short pairing code and returns immediately,
43
+ opening nothing. Show the user the code — they approve it in their browser while
44
+ you keep working, and the next command picks the token up. Nothing exists on the
45
+ server until they approve.
46
+
47
+ `--no-login` is how someone says "this machine only". Offer it, do not assume it:
48
+ a brain that lives in a folder here cannot follow them to their phone or their
49
+ next laptop, and that is discovered weeks later, by which time the notes are in
50
+ the wrong place. Ask which they want in one sentence before you run anything.
51
+
52
+ **If they would rather set it up themselves**, hand them a screen instead of
53
+ doing it: `setup --human` draws a checkbox list in your terminal, and
54
+ `setup --web` opens a page in their own browser (`--web --print-url` when you
55
+ cannot open one for them). Both ask the same questions `--agent` takes from
56
+ flags, and `--web` is the one to reach for when the user is not reading your
57
+ terminal at all.
58
+
41
59
  **Ask before setting policy for them.** Auto-update means the tool updates
42
60
  itself; `--hook on` means note summaries and their standing rules reach an agent
43
61
  unasked. Recommend both, but they are theirs to decide.
package/dist/index.js CHANGED
@@ -4962,17 +4962,35 @@ async function runSignInStep(opts) {
4962
4962
  if (summary.pendingUserCode) return { status: "pending", userCode: summary.pendingUserCode };
4963
4963
  if (opts.login === false) return { status: "skipped" };
4964
4964
  if (opts.dryRun) {
4965
- const wouldAsk = opts.interactive || opts.login === true;
4966
- return wouldAsk ? { status: "would-start" } : { status: "skipped" };
4965
+ return { status: "would-start" };
4966
+ }
4967
+ if (!opts.interactive && opts.login === void 0) {
4968
+ try {
4969
+ const { pending, opened } = await beginSignIn({ browser: false });
4970
+ return {
4971
+ status: "started",
4972
+ userCode: pending.user_code,
4973
+ verificationUrl: pending.verification_url,
4974
+ opened
4975
+ };
4976
+ } catch (err) {
4977
+ return {
4978
+ status: "unavailable",
4979
+ because: err instanceof MemgError ? err.message : String(err)
4980
+ };
4981
+ }
4967
4982
  }
4968
- if (!opts.interactive && opts.login !== true) return { status: "skipped" };
4969
4983
  if (opts.interactive) {
4970
4984
  printHuman(c.bold("\nmemgineering \u2014 setup\n"));
4971
4985
  const { wants } = await prompts2({
4972
4986
  type: "toggle",
4973
4987
  name: "wants",
4974
4988
  message: "Sign in to a memgineering account first?",
4975
- initial: false,
4989
+ // Default yes, to match the non-interactive path. Someone at a prompt can
4990
+ // see both answers and press either; the default is which one costs them
4991
+ // nothing to accept, and syncing is the one they cannot add retroactively
4992
+ // to notes they have already written somewhere else.
4993
+ initial: true,
4976
4994
  active: "sign in",
4977
4995
  inactive: "later"
4978
4996
  });
@@ -12424,7 +12442,10 @@ init_errors();
12424
12442
  init_vault();
12425
12443
  init_ui();
12426
12444
  function recallCommand() {
12427
- return new Command18("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("--scope <scope>", "restrict to one scope").option("--limit <n>", "max cards", (v) => parseInt(v, 10), 8).option("--detail <level>", "title | card | summary | chunks | full (default: card)").option("--depth <depth>", "exact | standard (default: standard, which also searches bodies)").option("--budget <tokens>", "max estimated size of the answer", (v) => parseInt(v, 10)).action(
12445
+ return new Command18("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("--scope <scope>", "restrict to one scope").option("--limit <n>", "max cards", (v) => parseInt(v, 10), 8).option(
12446
+ "--detail <level>",
12447
+ "title | card | summary | chunks | full (default: card) \u2014 a hosted brain answers title and card only; deeper needs a brain in a folder, or `open <handle>`"
12448
+ ).option("--depth <depth>", "exact | standard (default: standard, which also searches bodies)").option("--budget <tokens>", "max estimated size of the answer", (v) => parseInt(v, 10)).action(
12428
12449
  async (query, opts) => {
12429
12450
  const detail = parseDetail(opts.detail, "card");
12430
12451
  const depth = parseDepth(opts.depth);
@@ -14083,13 +14104,83 @@ function whoamiCommand() {
14083
14104
  });
14084
14105
  }
14085
14106
 
14107
+ // src/lib/next-step.ts
14108
+ init_config();
14109
+ function isSetUp(cfg) {
14110
+ return Object.keys(cfg.system.installed).length > 0;
14111
+ }
14112
+ var FINISH_SETUP = `## memgineering is installed. It is NOT set up yet.
14113
+
14114
+ **TO THE AGENT READING THIS** \u2014 finish it now, in this same turn. Do not print
14115
+ these commands for your user and stop: they are yours to run.
14116
+
14117
+ Ask them TWO short questions, in their language, then run setup yourself.
14118
+
14119
+ 1. **Where should the memory live?**
14120
+ "Just on this machine, or synced to an account so every device and every
14121
+ tool you use sees the same brain?"
14122
+
14123
+ - this machine \u2192 add \`--no-login\`
14124
+ - synced (default) \u2192 no extra flag
14125
+
14126
+ 2. **Who picks the rest of the settings?**
14127
+ "Shall I set it up, or would you rather choose on a screen?"
14128
+
14129
+ - you do it \u2192 \`memgineering setup --agent\`
14130
+ - they choose \u2192 \`memgineering setup --web\`
14131
+
14132
+ \`\`\`bash
14133
+ memgineering setup --agent # synced, you answer the rest
14134
+ memgineering setup --agent --no-login # this machine only
14135
+ memgineering setup --web # they answer on a screen
14136
+ \`\`\`
14137
+
14138
+ Signing in prints a short code \u2014 show it to them and they approve it in the
14139
+ browser. Setup does not wait, and the next command collects the token, so you
14140
+ keep going in the same turn.
14141
+
14142
+ Then read \`memgineering-setup\` for what to do with the brain it points at.`;
14143
+ var CONNECT_A_BRAIN = `## memgineering is set up, and no brain is connected.
14144
+
14145
+ **TO THE AGENT READING THIS** \u2014 a brain is a folder of markdown notes. Until one
14146
+ is connected there is nothing to recall and nowhere to remember. Pick with your
14147
+ user, then run it yourself:
14148
+
14149
+ \`\`\`bash
14150
+ memgineering link <their-notes-folder> # they already keep notes
14151
+ memgineering init <path> # they do not \u2014 creates one, laid out
14152
+ \`\`\`
14153
+
14154
+ \`link\` reports what would be stored before anything is indexed. An existing
14155
+ folder needs no index and no frontmatter of its own \u2014 plain markdown is a brain.
14156
+
14157
+ Then run \`memgineering onboard\`: it prints what to ask so the brain knows whose
14158
+ it is. A brain whose \`01_BASE/\` is still template text answers almost nothing.
14159
+
14160
+ Read \`memgineering-setup\` for accounts and which of their tools this reaches.`;
14161
+ function nextStepFor(cfg) {
14162
+ if (!isSetUp(cfg)) return { id: "finish_setup", markdown: FINISH_SETUP };
14163
+ if (cfg.brain.brains.length === 0 && cfg.brain.cloud === void 0) {
14164
+ return { id: "connect_a_brain", markdown: CONNECT_A_BRAIN };
14165
+ }
14166
+ return null;
14167
+ }
14168
+ async function currentNextStep() {
14169
+ try {
14170
+ return nextStepFor(await loadConfig());
14171
+ } catch {
14172
+ return null;
14173
+ }
14174
+ }
14175
+
14086
14176
  // src/program.ts
14087
14177
  init_ui();
14088
14178
  init_brand();
14089
14179
  function buildProgram() {
14090
- const program = new Command27("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", (thisCommand) => {
14180
+ const program = new Command27("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) => {
14091
14181
  const opts = thisCommand.optsWithGlobals();
14092
14182
  if (opts.json || process.env[BRAND.jsonEnvVar] === "1") setJsonMode(true);
14183
+ await announceNextStep(actionCommand.name());
14093
14184
  });
14094
14185
  configureGroupedHelp(program);
14095
14186
  program.addCommand(recallCommand());
@@ -14126,6 +14217,16 @@ function buildProgram() {
14126
14217
  makeParseErrorsActionable(program);
14127
14218
  return program;
14128
14219
  }
14220
+ var WITHOUT_NEXT_STEP = /* @__PURE__ */ new Set(["setup", "login", "logout", "whoami", "update", "guard"]);
14221
+ async function announceNextStep(commandName) {
14222
+ if (WITHOUT_NEXT_STEP.has(commandName)) return;
14223
+ const step = await currentNextStep();
14224
+ if (step === null) return;
14225
+ process.stderr.write(`
14226
+ ${step.markdown}
14227
+
14228
+ `);
14229
+ }
14129
14230
  function makeParseErrorsActionable(program) {
14130
14231
  const decorate = (cmd) => {
14131
14232
  cmd.showSuggestionAfterError(true);
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "memgineering",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "private": false,
5
- "description": "One memory for the AI you connect. Recall, remember, and revise a brain your agents share \u2014 stored in your own folder.",
5
+ "description": "One memory for the AI you connect. Recall, remember, and revise a brain your agents share stored in your own folder.",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://memgineering.com",
8
8
  "repository": {
@@ -42,8 +42,8 @@ if (process.env['npm_config_global'] === 'true') {
42
42
  ' "Just on this machine, or synced to an account so every device',
43
43
  ' and every tool you use sees the same brain?"',
44
44
  '',
45
- ' this machine -> no extra flag',
46
- ' synced -> add --login',
45
+ ' this machine -> add --no-login',
46
+ ' synced -> no extra flag, this is the default',
47
47
  '',
48
48
  ' 2. Who picks the rest of the settings?',
49
49
  ' "Shall I set it up, or would you rather choose on a screen?"',
@@ -51,14 +51,16 @@ if (process.env['npm_config_global'] === 'true') {
51
51
  ' you do it -> memgineering setup --agent',
52
52
  ' they choose -> memgineering setup --web',
53
53
  '',
54
- 'So: `memgineering setup --agent --login`, or `--agent`, or `--web',
55
- '--login`, or `--web`. Question 1 matters most — WITHOUT `--login`,',
56
- 'setup makes a folder on this machine and never mentions accounts, and',
57
- 'the user finds out later that their memory does not follow them.',
54
+ 'So: `memgineering setup --agent`, or `--agent --no-login`, or `--web`.',
55
+ 'Question 1 matters most — WITH `--no-login`, setup makes a folder on this',
56
+ 'machine and never mentions accounts again, and the user finds out later',
57
+ 'that their memory does not follow them.',
58
58
  '',
59
- '`--login` prints a short code: show it to them and they approve it in',
60
- 'the browser. Setup does not wait, and the next command collects the',
61
- 'token, so you keep going in the same turn.',
59
+ 'The default prints a short code and opens nothing: show the code to them',
60
+ 'and they approve it in the browser. Setup does not wait, and the next',
61
+ 'command collects the token, so you keep going in the same turn.',
62
+ '',
63
+ 'Then read the `memgineering-setup` skill for what to do with the brain.',
62
64
  '',
63
65
  ].join('\n'),
64
66
  );