@lorekit/cli 1.52.0 → 1.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +76 -2
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -597,7 +597,7 @@ erases your cross-repo home lesson.
597
597
  > sharing comes from the account/token, so remote needs no second location.
598
598
  > Different mechanism, same concept.
599
599
 
600
- ### `lorekit migrate` — relocation / rename tool
600
+ ### `lorekit migrate` — relocate a store, or push one to the hosted store
601
601
 
602
602
  Moved or renamed a local store (e.g. an old `.lore/`)? `migrate` re-writes its
603
603
  entries into the current two-tier layout so lessons are never stranded:
@@ -612,6 +612,80 @@ Dry-run (preview) by default; `--yes` (or `--apply`) applies. Idempotent — a
612
612
  re-run is a no-op. It reads LoreKit's own on-disk format only (it does **not**
613
613
  import persistent-memory's `~/.agent-memory/<bucket>/` format).
614
614
 
615
+ #### `--to remote` — bring your local lessons up
616
+
617
+ Started offline and later connected a token? `--to remote` pushes the whole
618
+ local store to the hosted one in a single command, instead of the agent
619
+ re-writing lessons one at a time in remote mode:
620
+
621
+ ```bash
622
+ lorekit migrate --from .lorekit --to remote # dry-run: the plan, per scope
623
+ lorekit migrate --from .lorekit --to remote --yes # push it
624
+ ```
625
+
626
+ The connection and token come from the same place every other command reads
627
+ them (`.mcp.json` / `.lorekit.json` / `LOREKIT_MCP_URL` + `LOREKIT_TOKEN`), so
628
+ a `deny: remote` constraint still wins. Everything is checked **before** the
629
+ first request rather than mid-push:
630
+
631
+ | Condition | Result |
632
+ |---|---|
633
+ | No usable connection | Fails with the `lorekit install` command to run |
634
+ | Read-only `lk_ro_*` token | Fails — a migration writes |
635
+ | Write-only `lk_wo_*` token | Warns, then pushes without reading: its reads are denied, so the destination is not classified at all and every entry reports as `add` (the hosted upsert is idempotent either way) |
636
+ | Unrecognized token prefix | Warns and proceeds, so a self-hosted or custom token still works |
637
+ | `deny: remote` | Fails, naming the config source that denied it |
638
+
639
+ **What transfers, and what the server owns.** `scope`, `key`, `value`,
640
+ `source_agent` and `trigger` travel as the source states them (`value` is
641
+ trimmed server-side), and the original creation date travels as `created_at` —
642
+ so a migrated lesson keeps the recency its ranking depends on. The rest is not
643
+ verbatim, and every departure is reported per entry, in the dry run as well as
644
+ the apply:
645
+
646
+ | Field | What happens |
647
+ |---|---|
648
+ | `updated` | Re-stamped at the write instant — there is no parameter for it |
649
+ | `seen_count` | Starts at 1 for a key the hosted store has never seen; one it already holds lands at ITS count plus one (the RPC treats a write as a sighting, migration 00059). A local tally of 12 never transfers |
650
+ | `tags` | **Replace** the hosted row's labels, so an untagged local entry clears them |
651
+ | `origin_*` | **Sticky** — the RPC coalesces provenance, so an entry carrying none leaves whatever the hosted row already had |
652
+ | `created` | Honoured only when the lesson is new to the hosted store (the RPC's update clause omits it, so one already there keeps its hosted date); an unusable or future-dated value is dropped for the write instant, and the entry is named |
653
+ | `expires_at` | Converted to the remaining whole `ttl_days`; anything beyond the API's 365-day cap lands shortened |
654
+
655
+ **Archived and expired entries are skipped** and reported as such. Neither can
656
+ be represented: a write against an archived key inserts a second, live row
657
+ beside it rather than reviving it, and any TTL would re-date an expired row
658
+ into the future — so pushing them would resurrect lore you retired.
659
+
660
+ Idempotent, with one nuance: a re-run compares only what a hosted write can
661
+ change, and compares it the way the server stores it — trimmed `value`, only
662
+ the provenance the source carries, and an expiry that still honours the local
663
+ intent (the hosted one is fixed at push time while the local one is measured
664
+ from now, so it is judged on whether at least half the intended life remains —
665
+ matching them instant-for-instant could never converge). Comparing the server-owned fields
666
+ directly would report a change forever and re-push the whole store on every
667
+ run. A remote `unchanged` therefore means "the hosted lesson already says
668
+ this", not "byte-identical row".
669
+
670
+ Rate limits and transient failures are handled rather than surfaced as
671
+ failures. The run paces itself under the hosted 120 req/min limit (and says so
672
+ the first time it has to wait), and retries a `429` on the server's own
673
+ `Retry-After` — plus a 5xx or a dropped connection, which on a push of
674
+ thousands of requests is the likeliest failure and the least worth losing an
675
+ entry to. Any other 4xx is a decision, not a blip, and is never retried.
676
+
677
+ Retrying is bounded, so none of this should be read as "it never fails": an
678
+ entry that exhausts its attempts is reported and the run exits non-zero, and
679
+ five CONSECUTIVE failures of any kind stop the run early with a
680
+ partial-progress report — an outage is not worth grinding a whole store through
681
+ the retry budget for. Every one of those exits is safe to re-run. The memory cap ([5,000 active memories on the
682
+ free plan](../../docs/limits.md)) is returned as a `429` too but is terminal —
683
+ the run stops, reports how many entries landed, and exits non-zero, so you can
684
+ archive or upgrade and re-run to resume.
685
+
686
+ Not in v1: `--org <slug>` org-owned writes (a migration lands as your personal
687
+ lore) and the reverse remote → local direction.
688
+
615
689
  ### The control model — two layers, deny-wins
616
690
 
617
691
  Two config layers decide the mode:
@@ -870,7 +944,7 @@ also returns their headroom against the plan's memory cap.
870
944
  | `--mode <mode>` | Memory mode override for `doctor`: `off` / `local` / `remote` |
871
945
  | `--store <path>` | Local project-tier store directory (default `.lorekit`) |
872
946
  | `--from <path>` | Source store to migrate from (`migrate`) |
873
- | `--to <tier>` | Migration destination tier: `home` / `project` (`migrate`; default routes by scope) |
947
+ | `--to <dest>` | Migration destination: `home` / `project` / `remote` (`migrate`; default routes by scope across the local tiers) |
874
948
  | `--apply` | Apply the migration — alias of `--yes` (`migrate`) |
875
949
  | `-y, --yes` | Non-interactive / apply; never prompt |
876
950
  | `--hooks <mode>` | Lifecycle hooks to wire: `all` / `read-only` / `none` (`install`; `none` removes any already wired) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lorekit/cli",
3
- "version": "1.52.0",
3
+ "version": "1.52.1",
4
4
  "description": "Install the LoreKit shared-memory skill and run health checks for the LoreKit MCP server.",
5
5
  "license": "MIT",
6
6
  "repository": {