xmemcli 0.0.4__tar.gz → 0.0.7__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xmemcli
3
- Version: 0.0.4
3
+ Version: 0.0.7
4
4
  Summary: Command-line client for the xmemory HTTP API: PKCE auth, instances, read/write, schema and XMD tools.
5
5
  Keywords: api,cli,llm,memory,xmemory
6
6
  Requires-Python: <4,>=3.12
@@ -22,12 +22,20 @@ xmemcli read <query>
22
22
  xmemcli write <text>
23
23
  xmemcli write-status <write_id> [<id> …]
24
24
  xmemcli quota
25
+ xmemcli binding list [--autoload]
26
+ xmemcli binding add <instance_id> [--tier autoload|available|off] [--scope project|user]
27
+ xmemcli binding remove <instance_id> [--scope project|user]
28
+ xmemcli context [--text] [--max-tokens N] [--no-live-state]
25
29
  ```
26
30
 
27
- Global flags / env: `--api-key` / `$XMEM_API_KEY`, `--instance-id` / `$XMEM_INSTANCE_ID`, `--api-url` / `$XMEM_API_URL`, `--rc-dir` / `$XMEM_RC_DIR`, `--json`, `--verbose`. See `xmemcli help options`.
31
+ Global flags / env: `--api-key` / `$XMEM_API_KEY`, `--instance-id` / `$XMEM_INSTANCE_ID`, `--api-url` / `$XMEM_API_URL`, `--rc-dir` / `$XMEM_RC_DIR`, `--binding-dir` / `$XMEM_BINDING_DIR`, `--json`, `--verbose`. See `xmemcli help options`.
28
32
 
29
33
  Credential resolution: `--api-key` → `$XMEM_API_KEY` → nearest `.xmemrc.json` (walk up from `--rc-dir` toward `$HOME`). Origin: `--api-url` → `$XMEM_API_URL` → rc `api_url` → default.
30
34
 
35
+ `context` is the exception: it runs unattended from a session-start hook, with no person and no chosen directory behind it, so it resolves the credential from `$HOME` **only** — a project `.xmemrc.json` is used only when `--api-key` / `$XMEM_API_KEY` or `--rc-dir` / `$XMEM_RC_DIR` names it explicitly. That stops a cloned repository shipping the key and origin its own context is fetched with.
36
+
37
+ Two local files, same discovery rule, different jobs: `.xmemrc.json` holds the credential (`0600`, never committed, nearest wins); `.xmemory.json` holds the instance binding (`0644`, *meant* to be committed at project scope, every scope merged). `binding` commands are local-only — no network, no credential.
38
+
31
39
  ## Read / write modes
32
40
 
33
41
  | CLI | API field |
@@ -43,7 +51,7 @@ Credential resolution: `--api-key` → `$XMEM_API_KEY` → nearest `.xmemrc.json
43
51
 
44
52
  ## Curated output
45
53
 
46
- The CLI does not echo raw API payloads. Server internals (`sql`, `diff_plan`, extraction trees) are never emitted. `--verbose` adds `trace_id` and `console_url` when present.
54
+ The CLI does not echo raw API payloads. Server internals (`sql`, `diff_plan`, extraction trees) are never emitted. Read responses keep `pending_suggestions` (schema-evolution hint). `--verbose` adds `trace_id` and `console_url` when present.
47
55
 
48
56
  ## Output personality — JSON vs pretty
49
57
 
@@ -89,7 +97,7 @@ xmemcli version
89
97
  xmemcli auth login
90
98
  ```
91
99
 
92
- Version: **0.0.4**.
100
+ Version: **0.0.7**.
93
101
 
94
102
 
95
103
  ---
@@ -133,7 +141,7 @@ AI-assisted IDE ── xmemcli read/write ──► CLI resolves key; never hand
133
141
 
134
142
  Project-local **`.xmemrc.json`**: `{email, api_url, api_key}`. Found by walking up from `--rc-dir` / CWD, ceiling `$HOME`.
135
143
 
136
- Resolution order: `--api-key` → `$XMEM_API_KEY` → `.xmemrc.json`. Origin: `--api-url` → `$XMEM_API_URL` → rc `api_url` → default. Explicit key + mismatched rc origin is rejected (localhost-key-at-prod guard).
144
+ Resolution order: `--api-key` → `$XMEM_API_KEY` → `.xmemrc.json`. Origin: `--api-url` → `$XMEM_API_URL` → rc `api_url` → default — **except** when the key is explicit (`--api-key` / `$XMEM_API_KEY`): the rc's `api_url` is skipped and the origin falls back to the built-in default unless you also set `--api-url` / `$XMEM_API_URL`. The localhost-key-at-prod guard applies when the key comes from the rc and an explicit `--api-url` / `$XMEM_API_URL` mismatches that rc origin.
137
145
 
138
146
  ## Identity — `/whoami`
139
147
 
@@ -159,7 +167,7 @@ Fixed port **9636** ("xmem" on phone keypad) allows SSH/devcontainer forwarding;
159
167
 
160
168
  # `xmemcli` — capabilities reference
161
169
 
162
- **Version:** `0.0.4` (see `xmemcli version`)
170
+ **Version:** `0.0.7` (see `xmemcli version`)
163
171
 
164
172
  `xmemcli` is the command-line client for the **xmemory HTTP API**. It is **human-governed for key acquisition**; an AI-assisted IDE may run reads, writes, and inventory once credentials exist. Humans acquire API keys and change quota limits in the **Console UI**.
165
173
 
@@ -203,6 +211,7 @@ Permission-filtered view of what this API key can see.
203
211
  |---------|-------------|
204
212
  | **`instance get`** `<instance_id>` | Fetch one instance |
205
213
  | **`instance create`** | Create from XMD/JSON schema. Required `--name`; schema via `--schema-file` or `--schema-stdin`. |
214
+ | **`instance setup`** `[instance_id]` `[--format agent\|project]` | How to connect this instance to an agent, ordered for where it is likely to be used. Prints the same steps the `get_setup_instructions` MCP tool gives an agent, so a person and the agent beside them read the same thing. `--format project` additionally prints the shared setup files to commit; each teammate still approves the install and signs in once. Carries no credential. |
206
215
 
207
216
  ---
208
217
 
@@ -219,6 +228,9 @@ XMD (xmemory Data) is the typed schema format for instances. These commands mirr
219
228
  | Preview migration | **`schema dry-run`** `[instance_id]` | Same flags as `schema update`; returns planned DDL without applying. |
220
229
  | List migrations | **`schema migrations list`** `[instance_id]` | Newest first. `--limit`, `--before-id`, `--include-yaml`. |
221
230
  | Fetch one migration | **`schema migrations get`** `[instance_id]` `<migration_id>` | Optional positional instance id; else `--instance-id` / `$XMEM_INSTANCE_ID`. `--include-yaml`. |
231
+ | Review suggestions | **`schema suggestions review`** `[instance_id]` | Suggestion engine: consolidated proposal from read-traffic gaps. |
232
+ | Decide suggestions | **`schema suggestions decide`** `[instance_id]` | `--proposal-version` (required) + `--accept`/`--reject`/`--defer`/`--file`. |
233
+ | Apply suggestions | **`schema suggestions apply`** `[instance_id]` | `--proposal-version` plus mandatory `--confirm-destructive`; preview removals in Console first. |
222
234
  | Enhance bound schema | **`xmd enhance --from-instance`** `<description>` | Uses live schema; requires instance id. |
223
235
  | Synthesize schema | **`xmd generate`** `<description>` | Cluster-scoped LLM synthesis. `-o` writes YAML. |
224
236
  | Enhance from file | **`xmd enhance`** `[schema_file]` `<description>` | From a file, or `--from-instance` for bound enhance. `-o`, `--plan-output`. |
@@ -226,7 +238,7 @@ XMD (xmemory Data) is the typed schema format for instances. These commands mirr
226
238
  | Validate schema | **`xmd validate`** / **`validate-yaml`** / **`validate-json`** | Cluster-scoped server validation. |
227
239
  | Convert JSON Schema | **`xmd convert`** | JSON Schema → XMD YAML via the API (`validate_json_schema`). |
228
240
 
229
- **Not exposed in CLI** (Console / suggestion-engine only today): pending schema suggestions review/apply.
241
+ **Not exposed in CLI** (Console-only today): the suggestions before/after YAML **preview**.
230
242
 
231
243
  #### Typical workflow
232
244
 
@@ -246,6 +258,11 @@ xmemcli schema update INST_ID --schema-file next.yml --migration-plan plan.json
246
258
  # 4. Inspect history
247
259
  xmemcli schema migrations list INST_ID
248
260
  xmemcli schema migrations get INST_ID MIGRATION_UUID
261
+
262
+ # 5. Suggestion engine (from real read traffic) — confirm with the user before decide/apply
263
+ xmemcli schema suggestions review INST_ID
264
+ xmemcli schema suggestions decide INST_ID --proposal-version PV --accept FP1 --accept FP2
265
+ xmemcli schema suggestions apply INST_ID --proposal-version NEXT_PV --confirm-destructive
249
266
  ```
250
267
 
251
268
  #### Commands
@@ -257,6 +274,9 @@ xmemcli schema migrations get INST_ID MIGRATION_UUID
257
274
  | **`schema dry-run`** `[instance_id]` | Preview migration (same schema/plan flags as update). |
258
275
  | **`schema migrations list`** `[instance_id]` | Applied migrations, newest first. `--limit` (default 50), `--before-id`, `--include-yaml`. |
259
276
  | **`schema migrations get`** `[instance_id]` `<migration_id>` | One record. Optional positional instance id; else global instance flags. `--include-yaml`. |
277
+ | **`schema suggestions review`** `[instance_id]` | Consolidated proposal from read-traffic gaps. Returns `proposal_version` + items. |
278
+ | **`schema suggestions decide`** `[instance_id]` | Record accept/reject/defer. Requires `--proposal-version` plus fingerprints or `--file`. |
279
+ | **`schema suggestions apply`** `[instance_id]` | Apply accepted items as one migration. Requires `--proposal-version` and `--confirm-destructive`. |
260
280
  | **`xmd generate`** `<description>` | LLM-synthesize XMD. Optional `-o`. Requires cluster resolution. |
261
281
  | **`xmd enhance`** `[schema_file]` `<description>` | LLM-augment schema from file, or **`--from-instance`** for live schema. `-o`, `--plan-output`. |
262
282
  | **`xmd validate`** `<schema_file>` | Validate; YAML vs JSON inferred from `.json` extension. |
@@ -268,6 +288,99 @@ Instance id for `schema *` commands: positional `[instance_id]`, else **`--insta
268
288
 
269
289
  ---
270
290
 
291
+ ### Binding (local only — no network, no credential)
292
+
293
+ Which instances an agent working in **this directory** should know about, and how eagerly to
294
+ engage each one. Stored in `.xmemory.json`, discovered by walking **up** from `--binding-dir` /
295
+ `$XMEM_BINDING_DIR` (or the cwd), ceilinged at `$HOME` — the same rule `.xmemrc.json` has always
296
+ used, and **no git anywhere**. From a start outside `$HOME` the walk checks that directory and
297
+ stops, rather than climbing into a shared parent like `/work` or `/tmp` where another user's file
298
+ could be planted. A session-start hook passes the project root explicitly, so it never depends on
299
+ the walk at all.
300
+ `~/.xmemory.json` is consulted as the outermost scope whenever `$HOME` is the user's own rather than part of the tree being read. Where the session root *is* the home directory — a dotfiles repository, a devcontainer or an archive unpacked as `$HOME` — that file is treated as project scope like any other file inside the tree.
301
+
302
+ Unlike the rc — where the nearest file simply wins — every scope is **merged**, git-config style:
303
+ `~/.xmemory.json` supplies personal instances, a repository's committed `.xmemory.json` adds the
304
+ team's, and the nearer file wins **field by field** on any instance both mention.
305
+
306
+ | Command | Description |
307
+ |---------|-------------|
308
+ | **`binding list`** | Merged view for this directory. `--autoload` lists only what a session-start hook would pull. |
309
+ | **`binding add <id>`** | Bind an instance, or update the binding it already has. `--name`, `--purpose`, `--tier`, `--engage` (repeatable), `--scope`. |
310
+ | **`binding remove <id>`** | Unbind from **one** scope. To silence an instance inherited from a wider scope, bind it here with `--tier off` instead. |
311
+ | **`context`** | Session-start pack for the `autoload`-tiered bindings here. `--text` prints the pack alone, `--max-tokens`, `--no-live-state`, `--timeout`. The only one of these that needs a credential. |
312
+
313
+ **Tiers:** `autoload` (pull its context every session) · `available` (default; engage on demand) ·
314
+ `off` (bound but dormant).
315
+
316
+ **Scopes:** `--scope project` (default) writes the committable file — at an existing nearer
317
+ binding, else the git work-tree root **when git can say and the reader would actually look
318
+ there**, else the cwd. git only ever suggests: no git, or a directory that is not a checkout, is
319
+ an ordinary state and never blocks a write. The one refusal left is when the target would resolve
320
+ onto the home directory, which would write the user-scope file while calling it a project one.
321
+ `--scope user` always writes `~/.xmemory.json`.
322
+
323
+ `context` makes no request when nothing is tiered `autoload`, and resolves no credential
324
+ either — a hook running on every session start must not error in a project that was never
325
+ going to fetch anything. A server without the endpoint is treated as nothing to inject, so a
326
+ client ahead of its deployment degrades quietly instead of failing every session, as does a
327
+ binding this build cannot parse.
328
+
329
+ **`--timeout` bounds the whole command.** Not the request — *everything*: binding discovery,
330
+ the credential preflight, the git provenance lookup, name resolution, TLS, response headers and
331
+ body. It is a wall-clock limit, because it is the only bound a session-start hook has (POSIX
332
+ `sh` has no portable `timeout`) and a bound with an exception in it is not one. Expiry degrades
333
+ like every other failure: exit **0**, empty pack, one `warning:` line naming the budget.
334
+
335
+ `--text` prints the pack and nothing else, for a caller that will **wrap** it. Do not point a
336
+ hook at it directly: a hook's stdout is parsed as its control document on some events, so a
337
+ pack that happened to be valid JSON would be read as control fields rather than injected. A
338
+ companion editor-integration plugin wraps this output in the documented hook envelope its own
339
+ client expects; it ships from a separate repository and is not part of this one.
340
+
341
+ **No secrets.** An instance id is not a credential, so a *project* `.xmemory.json` is written
342
+ `0644` and is *meant* to be committed — the opposite of `.xmemrc.json` (`0600`, never
343
+ committed). `~/.xmemory.json` is written `0600`: it stays on one machine, and on a shared host
344
+ its instance names and purposes are nobody else's business.
345
+
346
+ **Scope precedence has one exception: your own entries are a ceiling.** A nearer scope normally
347
+ wins field by field, but a project file arrives by `git clone` and is the less trusted of the
348
+ two. So for any instance `~/.xmemory.json` lists, a committed file may make it **less** eager,
349
+ never more:
350
+
351
+ - **Tier.** Ranked `autoload` > `available` > `off`. A project may take your `autoload` down to
352
+ `available` or `off`; it can never raise `off` to `available`, or `available` to `autoload`.
353
+ **An omitted `tier` in your file means `available` and binds as one** — so `binding add --scope
354
+ user <id>` with no `--tier` caps that instance at `available` everywhere. If you want a
355
+ project's `autoload` to stand, say `--tier autoload` in your own file: your entry states the
356
+ most eager you are willing to be, not merely that you know the id.
357
+ - **`engage`.** Replaced by a nearer scope for every other instance, but not for one of yours —
358
+ otherwise a committed file could rewrite the cues attached to your own instance, which is a
359
+ quieter version of raising its tier.
360
+
361
+ Everything else (`name`, `purpose`) still merges nearest-wins, and a project may always add
362
+ detail to an instance you listed.
363
+
364
+ **File format** (v1). `id` is required; everything else is optional. Unknown keys are rejected,
365
+ so a typo fails loudly rather than being ignored.
366
+
367
+ ```json
368
+ {
369
+ "version": 1,
370
+ "instances": [
371
+ {
372
+ "id": "9c421cd4-77ab-4a51-a7e3-b8fd5d09d394",
373
+ "name": "Team Knowledge",
374
+ "purpose": "shared dev conventions",
375
+ "tier": "autoload",
376
+ "engage": ["a convention is learned or corrected"]
377
+ }
378
+ ]
379
+ }
380
+ ```
381
+
382
+ ---
383
+
271
384
  ### Auth & meta
272
385
 
273
386
  | Command | Description |
@@ -275,9 +388,30 @@ Instance id for `schema *` commands: positional `[instance_id]`, else **`--insta
275
388
  | **`auth login`** | PKCE via Console `/xmemcli/<challenge>` → `.xmemrc.json`. `--no-browser`, `--timeout`, `--console-url`. |
276
389
  | **`auth logout`** | Remove active `.xmemrc.json` |
277
390
  | **`auth status`** | Call `/whoami`. No key → exit 0; bad key → auth error; good key → email + prefix. |
391
+ | **`mcp <instance-id>`** | Serve one instance to an MCP client over stdio, using this CLI's credential. Not for humans — see below. |
278
392
  | **`version`** | CLI release version (no network) |
279
393
  | **`status`** | Local health: version + whether `.xmemrc.json` exists (no network) |
280
394
 
395
+ **`mcp` is a transport, not a command you run.** An MCP client starts it and speaks
396
+ JSON-RPC to it over stdio; it forwards each frame to `<mcp-url>/instance/<id>` with this
397
+ CLI's credential attached, and writes the answer back. Register it with
398
+ `claude mcp add <name> -- xmemcli mcp <instance-id>` (or the equivalent for another
399
+ client).
400
+
401
+ It exists because an MCP client can reference an environment variable in an
402
+ `Authorization` header but cannot read `.xmemrc.json`. Reading the credential per
403
+ connection means nothing is captured when the server is registered, so a configuration
404
+ written today still works in a session opened next week with an empty environment.
405
+
406
+ Two consequences worth knowing:
407
+
408
+ - **stdout is the protocol.** Alone among these commands it emits no JSON document and
409
+ honours neither `--json` nor the exit-code table above; its exit code is a process
410
+ status. Diagnostics go to stderr.
411
+ - **Signed out is reported, not fatal.** Every request is answered with a JSON-RPC error
412
+ naming `auth login`, because a client that is told only "the server failed to start"
413
+ gives the reader nothing to act on. Exit code and stage do not apply.
414
+
281
415
  **Console `/xmemcli` routing:**
282
416
 
283
417
  - **Onboarding** → silent onboarding-key handoff to the CLI (no overlay, no button click).
@@ -302,13 +436,60 @@ Quota **increases** are Console-only — see [What `xmemcli` does not do](#what-
302
436
  |------|---------|
303
437
  | **0** | Success |
304
438
  | **2** | Usage error (bad flags, unknown `help` topic, missing required args) |
305
- | **3** | HTTP / request failure (includes unwritable `-o` paths) |
306
- | **4** | Auth error (missing or rejected API key) |
439
+ | **3** | HTTP / request failure — see [HTTP status mapping](#http-status--exit-code-and-json-stage) (includes permission denied, other API errors, and unwritable `-o` paths) |
440
+ | **4** | Auth error — invalid or missing API key (`401`; `stage: "auth"`) |
307
441
  | **5** | Timeout |
308
- | **6** | Quota exhausted (API quota-exceeded response) |
442
+ | **6** | Quota exhausted (API quota-exceeded response; `stage: "quota"`) |
309
443
  | **7** | Quota headroom below `--fail-if-under` |
310
444
  | **130** | Interrupted (Ctrl+C) |
311
445
 
446
+ ### HTTP status → exit code and JSON `stage`
447
+
448
+ `xmemcli` mirrors the API's **401 vs 403** distinction on the process exit code and on the machine-readable `stage` field. This is the single contract for **every** command that performs an HTTP call (read/write, `org list *`, schema/XMD, quota, `auth status`, …) — with one deliberate exception, below.
449
+
450
+ **`context` is exempt, by design** — from the release that introduces `context`, which is the
451
+ first one where this carve-out means anything. It is the one command nobody invokes: a
452
+ session-start hook runs it unattended, and a non-zero exit there costs the user their session
453
+ rather than their context. So **every** HTTP outcome — 401 and 403 included — exits **0** with an
454
+ empty context and an explanatory `warnings[]` entry. Use `auth status` to tell a credential
455
+ problem apart from an empty binding; `context` will not.
456
+
457
+ The exemption goes further than HTTP status. Any runtime failure inside `context` — a malformed
458
+ binding, an unreadable response shape, a timeout, a bug nobody anticipated — exits **0** with an
459
+ empty pack and a `warning:` line naming the cause. This is enforced in code rather than by
460
+ enumerating failure modes, so an unforeseen one degrades like the rest.
461
+
462
+ **And it is exempt from the one-JSON-document rule on stdout.** Every other command emits a JSON
463
+ error document in machine mode, including on usage errors. A piped, non-`--json` `context` whose
464
+ arguments fail to parse writes **zero bytes to stdout** and puts its reason on stderr instead,
465
+ because a hook discards stdout at exit **2** — so a JSON error document there is a session start
466
+ failing with no stated cause anywhere the user can see it. Usage and parse errors are the only
467
+ paths that still exit **2**; they are a mistake in the hook's own configuration, not a runtime
468
+ condition, and the hook author is the one reading stderr.
469
+
470
+ #### Quick reference
471
+
472
+ | API HTTP status | Shell exit code | JSON `stage` | Meaning |
473
+ |-----------------|-----------------|--------------|---------|
474
+ | **401** | **4** | `auth` | **Bad credential** — missing, invalid, expired, or inactive API key |
475
+ | **403** | **3** | `permission` | **Bad authorization** — key is valid but this key/role cannot perform the operation |
476
+ | **Other 4xx/5xx** | **3** | `request` | Request failed for another reason (404, 422, 5xx, …) |
477
+ | **402** (quota exhausted) | **6** | `quota` | Quota window exhausted — human must extend quota in Console |
478
+
479
+ #### How to interpret failures
480
+
481
+ 1. **Exit 4 / `stage: "auth"` / HTTP 401** → fix the key: login again, rotate the key, check `$XMEM_API_KEY` / `.xmemrc.json`, confirm `--api-url` points at the environment that issued the key.
482
+ 2. **Exit 3 / `stage: "permission"` / HTTP 403** → the key works, but the operation needs a broader API role (for example `data.write`, `instance.generate_schema`, `cluster.get_own`). Do **not** treat this as a wrong password.
483
+ 3. **Exit 3 / `stage: "request"`** → other HTTP failure (instance not found, validation error, server error). Read the error message and HTTP status in the JSON body.
484
+
485
+ Some commands attach a more specific `stage` (`validate`, `usage`, `limit`, …) while still using exit **3** unless the row above applies. The `schema suggestions` commands add their own stages: `stale_proposal` (re-run review for a fresh `proposal_version`), `invalid_decision`, `dependency` (accept the dangling dependencies first), `not_initialised` (submit a write first), and `apply`; `review` reports an `evolution_in_progress` condition as a successful status with exit 0.
486
+
487
+ **Envelope errors** (HTTP **200** with non-empty `errors[]`) are not HTTP 401/403; they typically exit **3** with `stage: "request"`.
488
+
489
+ **Breaking change in 0.0.5:** HTTP **403** previously exited **4** (`auth`); it now exits **3** (`permission`). Scripts that treated exit **4** as “any auth-layer failure” must check `stage` or handle exit **3** for permission denials.
490
+
491
+ See also [`ACCOUNTS_AUTH.md`](../ACCOUNTS_AUTH.md) for the server-side 401/403/404 rules.
492
+
312
493
  ---
313
494
 
314
495
  ## Help
@@ -341,6 +522,10 @@ xmemcli help schema dry-run
341
522
  xmemcli help schema migrations
342
523
  xmemcli help schema migrations list
343
524
  xmemcli help schema migrations get
525
+ xmemcli help schema suggestions
526
+ xmemcli help schema suggestions review
527
+ xmemcli help schema suggestions decide
528
+ xmemcli help schema suggestions apply
344
529
  xmemcli help xmd
345
530
  xmemcli help xmd generate
346
531
  xmemcli help xmd enhance
@@ -363,7 +548,7 @@ Nested topics work at any level that exists in the parser (`help org`, `help org
363
548
  |------|-------------------------|
364
549
  | **Quota increases** | No `quota request-increase`. Observe with `xmemcli quota`; **raise limits in Billing / Quotas UX**. |
365
550
  | **Routine API key creation** | Post-onboarding: API Keys UI (CLI opens that flow via `/xmemcli`). |
366
- | **Billing, plans, trial paywall** | Console only |
551
+ | **Billing & plans** | Console only |
367
552
  | **Users, roles, org admin** | Console only |
368
553
  | **Instance chat / trace inspection** | Console only |
369
554
  | **Cluster create/delete** | CLI lists clusters; provisioning in Console |
@@ -385,12 +570,23 @@ These apply **before** any subcommand (`xmemcli help options`).
385
570
  | `--api-key` | `XMEM_API_KEY` | Bearer API key |
386
571
  | `--instance-id` | `XMEM_INSTANCE_ID` | Default instance for read/write and cluster derivation |
387
572
  | `--rc-dir` | `XMEM_RC_DIR` | Where `auth login` writes `.xmemrc.json`; reads walk **up** toward `$HOME` |
573
+ | `--binding-dir` | `XMEM_BINDING_DIR` | Where `binding` commands look for `.xmemory.json`; reads walk **up** toward `$HOME` and **merge** every scope |
388
574
  | `--json` | — | Force one JSON document on stdout |
389
575
  | `--verbose` | — | Add `trace_id` / `console_url` when present (never SQL, diff plans, or internals) |
390
576
 
391
577
  **Credential resolution:** `--api-key` → `$XMEM_API_KEY` → nearest `.xmemrc.json` → *Not logged in — run `xmemcli auth login` first.*
392
578
 
393
- **Origin resolution:** `--api-url` → `$XMEM_API_URL` → rc `api_url` → default.
579
+ **`context` is the one exception, and it is a security boundary.** Every other command walks up
580
+ from the current directory for the nearest `.xmemrc.json`, which is deliberate: a person chose
581
+ that directory and ran the command in it. `context` has neither — a session-start hook runs it
582
+ unattended, in whatever tree the editor happened to open — so it resolves the credential from
583
+ **`$HOME` only**, and consults a project `.xmemrc.json` only when the caller named that source
584
+ explicitly with `--api-key` / `$XMEM_API_KEY` or `--rc-dir` / `$XMEM_RC_DIR`. Without the pin, a
585
+ cloned repository could ship a key *and* an origin and have both used automatically. This is a
586
+ rule rather than a detector: earlier versions tried to *detect* a checkout-supplied credential
587
+ and each delivery mechanism turned out to be a fresh bypass.
588
+
589
+ **Origin resolution:** `--api-url` → `$XMEM_API_URL` → rc `api_url` → default — **with one escape hatch**: when the key comes from `--api-key` / `$XMEM_API_KEY` (not from the rc), the rc's `api_url` is **not** consulted and the origin falls back to the built-in default (`https://api.xmemory.ai`) unless you also set `--api-url` / `$XMEM_API_URL`. That keeps an explicitly supplied key from being silently paired with an unrelated origin in a project `.xmemrc.json`. If `$XMEM_API_KEY` is set without `$XMEM_API_URL`, the CLI warns once per invocation.
394
590
 
395
591
  **Instance resolution (read/write):** `--instance-id` → `$XMEM_INSTANCE_ID` → if exactly **one** instance is visible, auto-select with a **stderr warning** → else error.
396
592