@lorekit/cli 1.30.2 → 1.31.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/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Linux, and Windows (npm creates the `lorekit` shim on every platform).
|
|
|
47
47
|
Sets up the full memory loop — the same three parts as the Claude plugin,
|
|
48
48
|
without needing a marketplace:
|
|
49
49
|
|
|
50
|
-
1. **Skills** (`lorekit-memory` + `lorekit-setup`) — the model-invoked
|
|
50
|
+
1. **Skills** (`lorekit-memory` + `lorekit-setup` + `lorekit-groom`) — the model-invoked runtime read/write loop, the self-improvement loop authoring counterpart, and the store-grooming maintenance counterpart.
|
|
51
51
|
2. **MCP server** (`lorekit`) — the connection to your lessons, merged into the
|
|
52
52
|
MCP config (preserving any other servers).
|
|
53
53
|
3. **Hooks** — the *deterministic* layer: lessons injected on every
|
|
@@ -751,7 +751,7 @@ also returns their headroom against the plan's memory cap.
|
|
|
751
751
|
|
|
752
752
|
## What the skills do
|
|
753
753
|
|
|
754
|
-
`install` scaffolds
|
|
754
|
+
`install` scaffolds three skills:
|
|
755
755
|
|
|
756
756
|
The **`lorekit-memory`** skill teaches an agent to:
|
|
757
757
|
|
|
@@ -770,6 +770,14 @@ to wire a self-improvement loop into one of *your own* skills or workflows — t
|
|
|
770
770
|
two-tier model, the lesson bucket convention, and the entrenchment guards. See
|
|
771
771
|
its `SKILL.md` and `rules/self-improvement-loops.md`.
|
|
772
772
|
|
|
773
|
+
The **`lorekit-groom`** skill is the maintenance counterpart: it teaches an agent
|
|
774
|
+
to run a grooming pass over an accumulated store — survey (`stats` / `scopes`),
|
|
775
|
+
lint, dedupe & merge near-duplicates, set expiry (TTL) on time-bound lessons, and
|
|
776
|
+
prune or archive obsolete ones. It always analyses read-only and proposes a plan
|
|
777
|
+
before mutating (archive is preferred over hard-delete), because the store is
|
|
778
|
+
shared and a merge or delete is permanent for every agent. See its `SKILL.md`,
|
|
779
|
+
`rules/grooming-pass.md`, and `references/merge-and-expiry.md`.
|
|
780
|
+
|
|
773
781
|
The **skills** are model-invoked (the agent chooses to use them). For a
|
|
774
782
|
**deterministic** guarantee — lessons injected on every session start, a nudge
|
|
775
783
|
on every tool failure — use the framework plugins in [`plugins/`](../../plugins/),
|
package/package.json
CHANGED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lorekit-groom
|
|
3
|
+
description: >
|
|
4
|
+
Grooms and maintains an accumulated LoreKit memory store — the cleanup
|
|
5
|
+
counterpart to lorekit-memory (which reads and records single lessons). It
|
|
6
|
+
finds and merges near-duplicate lessons, lints out low-quality ones, sets
|
|
7
|
+
expiry (TTL) on time-bound lessons, and prunes or archives obsolete ones,
|
|
8
|
+
ALWAYS analysing read-only and proposing a plan before any change (nothing
|
|
9
|
+
is deleted or merged without confirmation, and archive is preferred over
|
|
10
|
+
hard-delete). Reach for this whenever a lesson/memory store has grown noisy,
|
|
11
|
+
large, or stale and needs tidying — not for reading or writing one lesson.
|
|
12
|
+
Triggers on "groom my memories", "clean up my lessons", "dedupe the memory
|
|
13
|
+
store", "merge duplicate lessons", "too many memories", "my lorekit is a
|
|
14
|
+
mess", "consolidate lessons", "add expiry dates to old lessons", "prune stale
|
|
15
|
+
branch lessons", "lint my memories", "/lorekit-groom".
|
|
16
|
+
user-invocable: true
|
|
17
|
+
argument-hint: '[scope-hint]'
|
|
18
|
+
license: MIT
|
|
19
|
+
metadata:
|
|
20
|
+
author: mthines
|
|
21
|
+
version: '1.0.0'
|
|
22
|
+
workflow_type: shared-memory-grooming-and-consolidation
|
|
23
|
+
tags:
|
|
24
|
+
- lorekit
|
|
25
|
+
- persistent-memory
|
|
26
|
+
- shared-memory
|
|
27
|
+
- mcp
|
|
28
|
+
- lessons
|
|
29
|
+
- grooming
|
|
30
|
+
- dedupe
|
|
31
|
+
- lint
|
|
32
|
+
- maintenance
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# LoreKit Groom
|
|
36
|
+
|
|
37
|
+
Keep a shared memory store healthy as it grows.
|
|
38
|
+
|
|
39
|
+
A memory store that is never tended slowly rots: the same lesson gets written
|
|
40
|
+
three times in slightly different words, a stub with an empty value sits next to
|
|
41
|
+
a real one, a branch-specific gotcha outlives the branch by a year. Noise like
|
|
42
|
+
that is not harmless — it drowns the signal every future read depends on, so the
|
|
43
|
+
next agent scrolls past ten near-identical lines instead of acting on one clear
|
|
44
|
+
one.
|
|
45
|
+
|
|
46
|
+
This skill runs a **grooming pass** over the lessons for a scope: survey → lint →
|
|
47
|
+
dedupe & merge → set expiry → prune → verify. It is the maintenance counterpart
|
|
48
|
+
to the other two LoreKit skills:
|
|
49
|
+
|
|
50
|
+
| Skill | Job |
|
|
51
|
+
|-------|-----|
|
|
52
|
+
| `lorekit-memory` | Runtime: read scoped lessons on a task, write one on failure |
|
|
53
|
+
| `lorekit-setup` | Authoring: wire a self-improvement loop into a host |
|
|
54
|
+
| **`lorekit-groom`** | **Maintenance: consolidate and clean an accumulated store** |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## The one rule that governs everything: propose, then confirm
|
|
59
|
+
|
|
60
|
+
Grooming edits a **shared, persistent** store — a lesson you merge or delete is
|
|
61
|
+
gone for every agent on every machine, not just this session. So the entire pass
|
|
62
|
+
is read-only until a human says otherwise:
|
|
63
|
+
|
|
64
|
+
1. **Analyse read-only.** Every survey step below is a read command. Run them
|
|
65
|
+
first and build the full picture before touching anything.
|
|
66
|
+
2. **Propose a concrete plan.** Show exactly what you intend to do — which
|
|
67
|
+
lessons merge into which, what gets an expiry, what gets archived — with the
|
|
68
|
+
actual keys and values, grouped so a human can scan it in one screen.
|
|
69
|
+
3. **Only mutate after confirmation.** Wait for the go-ahead, then apply. Prefer
|
|
70
|
+
**archive** (reversible) over hard-delete; reserve permanent deletion for
|
|
71
|
+
genuine junk and only with explicit sign-off.
|
|
72
|
+
|
|
73
|
+
Never batch-delete on a similarity score alone — the dedupe heuristic is a
|
|
74
|
+
word-overlap guess, not a semantic judge (see the toolbox note below). It finds
|
|
75
|
+
*candidates for a human's eye*, and merging is a judgement call about meaning.
|
|
76
|
+
|
|
77
|
+
If you are asked to "just clean it up" without oversight, still surface the plan
|
|
78
|
+
once and get a single confirmation for the batch — that is the floor, not a
|
|
79
|
+
step to skip.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## The grooming pass
|
|
84
|
+
|
|
85
|
+
Follow [rules/grooming-pass.md](./rules/grooming-pass.md) for the full playbook.
|
|
86
|
+
The short version is six phases; the first four are pure analysis.
|
|
87
|
+
|
|
88
|
+
1. **Survey** — size the problem. `lorekit stats` (counts per scope + store) and
|
|
89
|
+
`lorekit scopes` (store-wide inventory: every scope with its lesson count)
|
|
90
|
+
show where the mass is. Pick the noisiest scope to start.
|
|
91
|
+
2. **Lint** — `lorekit lint --json` flags structurally bad lessons (empty /
|
|
92
|
+
whitespace / suspiciously short / untrimmed values, empty keys, malformed
|
|
93
|
+
scopes). These are the cheapest wins: fix or drop them first.
|
|
94
|
+
3. **Dedupe** — `lorekit dedupe --json` clusters near-duplicate lessons. Start at
|
|
95
|
+
a high `--threshold` (e.g. `0.85`) for confident duplicates, then lower it to
|
|
96
|
+
surface looser paraphrases. Use `lorekit show <scope::key>` to read each
|
|
97
|
+
cluster member's full value before deciding.
|
|
98
|
+
4. **Plan** — turn the findings into a proposed set of merges, expiries, and
|
|
99
|
+
removals. This is where propose-then-confirm happens.
|
|
100
|
+
5. **Apply** (after confirmation) — merge, expire, and prune. See the toolbox.
|
|
101
|
+
6. **Verify** — re-run `lorekit lint` (should exit 0) and `lorekit dedupe` (no
|
|
102
|
+
clusters at your threshold) and `lorekit stats` (a lower count). Green means
|
|
103
|
+
the pass landed.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## The toolbox
|
|
108
|
+
|
|
109
|
+
Grooming spans two surfaces. **Analysis is the CLI** — its read commands
|
|
110
|
+
(`lint`, `dedupe`, `stats`, `scopes`, `show`, `list`, `search`, `tree`, `diff`)
|
|
111
|
+
survey the store far more richly than the MCP tools do, across both the offline
|
|
112
|
+
and remote stores at once. **Mutation is the `memory.*` MCP tools** — and
|
|
113
|
+
crucially, *removal* is MCP-only: there is no `lorekit delete` command, so
|
|
114
|
+
archiving and deleting always go through `memory.delete` / `memory.archive`.
|
|
115
|
+
|
|
116
|
+
| Job | How | Surface |
|
|
117
|
+
|-----|-----|---------|
|
|
118
|
+
| Count lessons per scope/store | `lorekit stats [--scope <s>]` | CLI (read) |
|
|
119
|
+
| Inventory every scope + lesson count | `lorekit scopes` | CLI (read) |
|
|
120
|
+
| Find low-quality lessons | `lorekit lint --json` | CLI (read) |
|
|
121
|
+
| Find near-duplicate clusters | `lorekit dedupe --json [--threshold <n>]` | CLI (read) |
|
|
122
|
+
| Read one lesson in full | `lorekit show <scope::key> [--json]` | CLI (read) |
|
|
123
|
+
| Compare offline vs remote | `lorekit diff` | CLI (read) |
|
|
124
|
+
| Write the merged/consolidated lesson | `memory.write` (or `lorekit write`) | MCP / CLI |
|
|
125
|
+
| Set / clear an expiry | `memory.write { ttl_days }` / `{ clear_ttl: true }` | MCP / CLI (`--ttl-days` / `--clear-ttl`) |
|
|
126
|
+
| Archive a lesson (reversible) | `memory.archive` (or `memory.delete`) | **MCP only** |
|
|
127
|
+
| Hard-delete a lesson (permanent) | `memory.delete { force: true }` | **MCP only** |
|
|
128
|
+
|
|
129
|
+
> **`dedupe` is a heuristic, not a semantic judge.** It clusters on Jaccard
|
|
130
|
+
> word-token overlap, so it can both miss reworded duplicates *and* group
|
|
131
|
+
> coincidental ones. Treat every cluster as a candidate to read and decide on,
|
|
132
|
+
> never as an instruction to merge.
|
|
133
|
+
|
|
134
|
+
> **If the `memory.*` MCP tools are not connected**, analysis still works
|
|
135
|
+
> (the CLI reads and `lorekit write --ttl-days` sets expiries), but you cannot
|
|
136
|
+
> archive or delete. Say so once, complete everything you can, and hand the
|
|
137
|
+
> removal list to the human rather than silently skipping it.
|
|
138
|
+
|
|
139
|
+
Merging, expiry tiers, and the archive-vs-delete call each have real judgement
|
|
140
|
+
in them — the details, with examples, live in
|
|
141
|
+
[references/merge-and-expiry.md](./references/merge-and-expiry.md). Read it
|
|
142
|
+
before your first merge or TTL decision in a pass.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Scope in one line
|
|
147
|
+
|
|
148
|
+
Lessons are partitioned by a canonical scope string (`::` is the only separator):
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
global universal principles
|
|
152
|
+
project::{name} monorepo-wide
|
|
153
|
+
repo::{owner}/{repo} this repository's codebase
|
|
154
|
+
branch::{owner}/{repo}::{branch} short-lived, this branch only
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Scope drives two grooming decisions: **branch-scoped lessons are the prime
|
|
158
|
+
candidates for expiry** (a branch is short-lived, its lessons usually are too),
|
|
159
|
+
and **a merge lands in the narrowest scope that still covers all its members**.
|
|
160
|
+
Both are spelled out in the reference file.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Setup
|
|
165
|
+
|
|
166
|
+
Grooming uses the same install as the rest of LoreKit:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npx @lorekit/cli install
|
|
170
|
+
npx @lorekit/cli doctor
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`doctor` confirms the store backend (`remote`, `local`, or `off`) and token
|
|
174
|
+
permission. Removal needs a token with **write** permission (`lk_rw_*` or
|
|
175
|
+
`lk_wo_*`); a read-only token can survey and propose but not apply. If a mutate
|
|
176
|
+
call fails with an authorization error, report it and stop — do not retry.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Merge, expiry, and removal — the judgement calls
|
|
2
|
+
|
|
3
|
+
The mechanical parts of grooming (survey, lint, dedupe) are commands. The parts
|
|
4
|
+
that need thought are: how to synthesise a merged lesson, where it should live,
|
|
5
|
+
how long a lesson should last, and whether a stale lesson should be archived or
|
|
6
|
+
deleted. This file is the reference for those four decisions. Read it before the
|
|
7
|
+
first merge or TTL choice in a pass.
|
|
8
|
+
|
|
9
|
+
## Table of contents
|
|
10
|
+
|
|
11
|
+
1. [Synthesising a merged lesson](#1-synthesising-a-merged-lesson)
|
|
12
|
+
2. [Choosing the merged lesson's scope](#2-choosing-the-merged-lessons-scope)
|
|
13
|
+
3. [Choosing the key](#3-choosing-the-key)
|
|
14
|
+
4. [Expiry tiers (TTL)](#4-expiry-tiers-ttl)
|
|
15
|
+
5. [Archive vs. hard-delete](#5-archive-vs-hard-delete)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 1. Synthesising a merged lesson
|
|
20
|
+
|
|
21
|
+
A merge is not a concatenation. Three lessons that say the same thing become
|
|
22
|
+
**one lesson that says it best** — otherwise you have replaced three near-copies
|
|
23
|
+
with one long lumpy one and gained nothing.
|
|
24
|
+
|
|
25
|
+
- **Keep the observation, drop the repetition.** Write the single clearest
|
|
26
|
+
statement of the shared point. If two members add genuinely different detail
|
|
27
|
+
(one names the failing command, another names the fix), fold both facts into
|
|
28
|
+
one lesson; if they only reword each other, keep the sharpest phrasing.
|
|
29
|
+
- **Stay an observation, not a rule.** LoreKit lessons are advisory ("migrations
|
|
30
|
+
that skip an explicit transaction have left the schema half-applied here"), not
|
|
31
|
+
commandments ("ALWAYS wrap migrations"). Merging is a chance to soften a member
|
|
32
|
+
that drifted into a rigid MUST.
|
|
33
|
+
- **Preserve the union of tags and the richest provenance.** Carry every source's
|
|
34
|
+
tags onto the merged lesson so it stays as findable as the originals. Keep the
|
|
35
|
+
most complete origin (repo / branch / commit / PR) among the members.
|
|
36
|
+
- **Prefer updating a survivor over minting a new key.** If one member is already
|
|
37
|
+
well-named and well-scoped, write the merged value onto *its* `scope::key`
|
|
38
|
+
(an in-place update) and delete the others. That keeps any external links to
|
|
39
|
+
that key alive. Mint a fresh key only when no member's key fits the merged
|
|
40
|
+
meaning.
|
|
41
|
+
|
|
42
|
+
**Example**
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
Sources:
|
|
46
|
+
- "wrap every migration in a transaction"
|
|
47
|
+
- "schema changes need a transaction or they half-apply on error"
|
|
48
|
+
- "migrations must be atomic"
|
|
49
|
+
Merged value:
|
|
50
|
+
"Migrations here half-apply on error unless wrapped in an explicit
|
|
51
|
+
transaction — a failed step otherwise leaves the schema partly changed."
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The merged version keeps the *why* (half-apply on error) that only one source
|
|
55
|
+
had, and states it as an observation.
|
|
56
|
+
|
|
57
|
+
## 2. Choosing the merged lesson's scope
|
|
58
|
+
|
|
59
|
+
Merge into the **narrowest scope that still correctly covers every member**.
|
|
60
|
+
|
|
61
|
+
- All members share a scope → merge stays in that scope.
|
|
62
|
+
- Members span scopes (e.g. two `branch::…` and one `repo::…` saying the same
|
|
63
|
+
thing) → the lesson is really about the broader scope. Merge up to the
|
|
64
|
+
narrowest scope that is still true for all of them — usually `repo::` when a
|
|
65
|
+
branch lesson turned out to be a repo-wide truth. Broadening scope is itself a
|
|
66
|
+
useful grooming outcome: a durable lesson trapped on a dead branch is nearly
|
|
67
|
+
invisible.
|
|
68
|
+
- Never merge *down* into a scope narrower than some members — that hides the
|
|
69
|
+
lesson from contexts where it applies.
|
|
70
|
+
|
|
71
|
+
Precedence check: bare `lorekit tree` shows which scope's lesson wins when keys
|
|
72
|
+
collide across scopes. Run it without `--scope` — that flag narrows the
|
|
73
|
+
resolution to a single scope, which by construction cannot show a collision.
|
|
74
|
+
Use it to confirm a merged lesson will actually surface where you intend.
|
|
75
|
+
|
|
76
|
+
## 3. Choosing the key
|
|
77
|
+
|
|
78
|
+
- Reuse a good survivor's key when one exists (see §1 — keeps links alive).
|
|
79
|
+
- Otherwise write a short, specific, hyphenated key that names the *observation*,
|
|
80
|
+
not the symptom: `db-migrations-need-explicit-tx`, not `migration-bug` or
|
|
81
|
+
`note-3`. A key is how a future agent recognises the lesson at a glance in a
|
|
82
|
+
list, so vague keys are their own kind of noise.
|
|
83
|
+
|
|
84
|
+
## 4. Expiry tiers (TTL)
|
|
85
|
+
|
|
86
|
+
`ttl_days` (1–365) auto-expires a lesson that many days after the write, after
|
|
87
|
+
which it is hidden from reads. Set it via `memory.write { ttl_days }` or
|
|
88
|
+
`lorekit write --ttl-days <n>`; clear it with `{ clear_ttl: true }` /
|
|
89
|
+
`--clear-ttl` to make a lesson permanent again.
|
|
90
|
+
|
|
91
|
+
Match the TTL to how long the lesson's *truth* lasts, not to how old it is:
|
|
92
|
+
|
|
93
|
+
| Lesson kind | Guidance |
|
|
94
|
+
|-------------|----------|
|
|
95
|
+
| `branch::` — branch-specific gotcha | Short. The branch is short-lived; 14–30 days usually outlasts it. Prime expiry candidates. |
|
|
96
|
+
| Version- or dependency-pinned ("works around the bug in lib X 2.3") | Medium (30–90d), or archive once the pin is gone. |
|
|
97
|
+
| Repo- or project-wide architectural truth | Usually **permanent** — no TTL. Expiring durable knowledge is how a store forgets the things worth keeping. |
|
|
98
|
+
| `global` principle | Permanent. |
|
|
99
|
+
|
|
100
|
+
The instinct to worry about is expiring something durable. When unsure whether a
|
|
101
|
+
lesson's truth is time-bound, leave it permanent and flag it in the plan rather
|
|
102
|
+
than quietly attaching a TTL — a lesson that silently vanishes is worse than one
|
|
103
|
+
that lingers.
|
|
104
|
+
|
|
105
|
+
## 5. Archive vs. hard-delete
|
|
106
|
+
|
|
107
|
+
Two removal paths, and the default is the reversible one.
|
|
108
|
+
|
|
109
|
+
- **Archive** (`memory.archive`, or `memory.delete` without `force`) —
|
|
110
|
+
soft-removes: hidden from reads but restorable. This is the default for
|
|
111
|
+
anything that is *stale* rather than *junk*: superseded workarounds, lessons
|
|
112
|
+
about a since-deleted subsystem, a merge's source lessons. If a future run
|
|
113
|
+
might ever want it back, archive.
|
|
114
|
+
- **Hard-delete** (`memory.delete { force: true }`) — permanent, no recovery.
|
|
115
|
+
Reserve it for genuine junk: lint casualties with no meaning (empty values,
|
|
116
|
+
placeholder keys), test detritus, accidental writes. Only with explicit
|
|
117
|
+
human sign-off in the plan.
|
|
118
|
+
|
|
119
|
+
When a lesson came from another agent or teammate (check its tags / source),
|
|
120
|
+
lean further toward archive — deleting someone else's recorded knowledge outright
|
|
121
|
+
is a heavier call than tidying your own.
|
|
122
|
+
|
|
123
|
+
Rule of thumb: **if you would hesitate to lose it forever, archive it.**
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# The grooming pass — playbook
|
|
2
|
+
|
|
3
|
+
A single, repeatable pass over the lessons for a scope. The first four phases are
|
|
4
|
+
read-only; nothing is mutated until a human has seen and approved the plan.
|
|
5
|
+
|
|
6
|
+
Work **one scope at a time**. A whole-store pass in a single breath produces a
|
|
7
|
+
wall of proposed changes no one can review; a scope at a time keeps each
|
|
8
|
+
confirmation small enough to actually read.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Phase 1 — Survey (read-only)
|
|
13
|
+
|
|
14
|
+
Size the problem before touching it, so you groom where the mass actually is
|
|
15
|
+
rather than wherever you happened to look first.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
lorekit stats # counts per scope and per store — where the mass is
|
|
19
|
+
lorekit scopes # every scope in the store + its lesson count
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`scopes` is the one command that is store-wide rather than cwd-scoped, so it is
|
|
23
|
+
how you notice a `branch::…` scope you had forgotten about, or a scope with
|
|
24
|
+
hundreds of lessons that dwarfs the rest. Neither command reports a
|
|
25
|
+
last-activity date — the inventory is counts only — so judge staleness from the
|
|
26
|
+
lessons themselves once you narrow in (`lorekit list --scope <scope>` tags each
|
|
27
|
+
one with its `updated` date). Read both, then pick the noisiest scope as the
|
|
28
|
+
target for this pass and narrow to it with `--scope <scope>` from here on.
|
|
29
|
+
|
|
30
|
+
## Phase 2 — Lint (read-only)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
lorekit lint --json --scope <scope>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Findings are structural, not semantic — each names its rule:
|
|
37
|
+
|
|
38
|
+
- **empty-value / short-value / untrimmed-value** — the lesson carries little or
|
|
39
|
+
no signal, or has stray leading/trailing whitespace.
|
|
40
|
+
- **empty-key** — no key to address it by.
|
|
41
|
+
- **malformed-scope** — the scope string is invalid.
|
|
42
|
+
|
|
43
|
+
These are the cheapest wins and the least controversial, so clear them first.
|
|
44
|
+
For each: either **fix it in place** (rewrite a too-short value into a real
|
|
45
|
+
observation, trim whitespace — a `memory.write` to the same `scope`+`key`
|
|
46
|
+
updates in place) or, if the lesson is genuinely empty of meaning, **queue it for
|
|
47
|
+
removal** in the plan. `lint` exits non-zero while findings remain, which also
|
|
48
|
+
makes it a clean CI gate — a passing `lint` is your Phase 6 proof.
|
|
49
|
+
|
|
50
|
+
## Phase 3 — Dedupe (read-only)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
lorekit dedupe --json --scope <scope> --threshold 0.85
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Each cluster is a set of lessons whose values overlap heavily by word tokens.
|
|
57
|
+
Start high (`0.85`) to see the confident duplicates, then re-run lower (`0.75`,
|
|
58
|
+
`0.7`) to surface looser paraphrases — but the lower you go, the more the
|
|
59
|
+
clusters are coincidental overlaps rather than true duplicates, so read more
|
|
60
|
+
carefully.
|
|
61
|
+
|
|
62
|
+
For every cluster you intend to act on, read the members in full first:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
lorekit show <scope::key> --json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
You are deciding whether these lessons *mean* the same thing, which the score
|
|
69
|
+
cannot tell you. Only clusters that survive that read become merges in the plan.
|
|
70
|
+
|
|
71
|
+
## Phase 4 — Build and propose the plan
|
|
72
|
+
|
|
73
|
+
Turn Phases 2–3 into one concrete, scannable proposal. Group it by action and
|
|
74
|
+
show the real content, because a human approving a shared-store change needs to
|
|
75
|
+
see what actually changes, not a count:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
MERGE (3 → 1) in repo::acme/api
|
|
79
|
+
keep/new key: db-migrations-need-explicit-tx
|
|
80
|
+
merged from:
|
|
81
|
+
- repo::acme/api::migrations-wrap-in-transaction "wrap every migration in a tx…"
|
|
82
|
+
- repo::acme/api::tx-around-schema-changes "schema changes need a transaction…"
|
|
83
|
+
- repo::acme/api::migration-atomicity "migrations must be atomic…"
|
|
84
|
+
proposed value: "<the synthesised lesson>"
|
|
85
|
+
|
|
86
|
+
EXPIRE (set TTL)
|
|
87
|
+
- branch::acme/api::feat-x::stub-endpoint-shape → ttl_days: 14 (branch-scoped, short-lived)
|
|
88
|
+
|
|
89
|
+
ARCHIVE (reversible)
|
|
90
|
+
- repo::acme/api::old-node-14-workaround (node 14 dropped; superseded)
|
|
91
|
+
|
|
92
|
+
DELETE (permanent — junk only)
|
|
93
|
+
- repo::acme/api::asdf (empty value, no key meaning)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Then stop and get confirmation. See
|
|
97
|
+
[references/merge-and-expiry.md](../references/merge-and-expiry.md) for how to
|
|
98
|
+
synthesise a merged value, choose its scope and key, pick a TTL tier, and decide
|
|
99
|
+
archive vs delete — those are the judgement calls, and getting them right is the
|
|
100
|
+
whole point of doing this by hand rather than by script.
|
|
101
|
+
|
|
102
|
+
## Phase 5 — Apply (only after confirmation)
|
|
103
|
+
|
|
104
|
+
Apply in an order that never loses information, because the store is the only
|
|
105
|
+
record — there is no undo for a hard delete:
|
|
106
|
+
|
|
107
|
+
1. **Fixes and merges first — write before you remove.** Write the corrected or
|
|
108
|
+
merged lesson (`memory.write`, or `lorekit write`). Confirm it landed
|
|
109
|
+
(`lorekit show <scope::key>`) *before* removing any source lesson, so a
|
|
110
|
+
failure mid-way leaves the originals intact rather than a gap.
|
|
111
|
+
2. **Expiries.** `memory.write { scope, key, ttl_days }` on the lessons that get
|
|
112
|
+
a TTL, or `{ clear_ttl: true }` to make one permanent again. Same key updates
|
|
113
|
+
in place — no new lesson is created.
|
|
114
|
+
3. **Removals last.** Archive (`memory.archive`, reversible) for anything a
|
|
115
|
+
future run might still want; `memory.delete { force: true }` (permanent) only
|
|
116
|
+
for the junk explicitly approved for deletion. Remove the merge sources here,
|
|
117
|
+
once their replacement is confirmed written.
|
|
118
|
+
|
|
119
|
+
Preserve `tags` and provenance when you rewrite a lesson — carry the union of the
|
|
120
|
+
sources' tags onto a merge so the consolidated lesson stays as findable as the
|
|
121
|
+
originals were.
|
|
122
|
+
|
|
123
|
+
## Phase 6 — Verify (read-only)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
lorekit lint --scope <scope> # expect exit 0 — clean
|
|
127
|
+
lorekit dedupe --scope <scope> --threshold 0.85 # expect no clusters
|
|
128
|
+
lorekit stats --scope <scope> # expect a lower count than Phase 1
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Report the before/after: lessons removed, clusters merged, expiries set, and the
|
|
132
|
+
new count. Then either move to the next scope or close out the pass.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Guardrails
|
|
137
|
+
|
|
138
|
+
- **Read before you remove.** Never delete a merge source until its replacement
|
|
139
|
+
is written and confirmed.
|
|
140
|
+
- **Archive beats delete.** Prefer the reversible path unless the lesson is
|
|
141
|
+
provably junk (a lint casualty) and the human approved permanent removal.
|
|
142
|
+
- **The score is a hint, not a verdict.** A dedupe cluster is a candidate to
|
|
143
|
+
read, not a merge to execute.
|
|
144
|
+
- **One scope, one confirmation.** Keep each proposed batch small enough to
|
|
145
|
+
actually review.
|
|
146
|
+
- **Someone else's lesson deserves more caution.** A lesson tagged from another
|
|
147
|
+
agent or a teammate is not automatically yours to delete — when in doubt,
|
|
148
|
+
archive rather than hard-delete, and flag it in the plan.
|
package/src/config.mjs
CHANGED
|
@@ -12,8 +12,10 @@ export const SKILL_SOURCE = path.join(PKG_ROOT, 'skill', SKILL_NAME);
|
|
|
12
12
|
// Every skill the CLI ships. `lorekit-memory` is the operational read/write
|
|
13
13
|
// loop (kept first as the primary — `SKILL_NAME`/`SKILL_SOURCE` above alias it
|
|
14
14
|
// for back-compat); `lorekit-setup` is the authoring skill that wires a
|
|
15
|
-
// self-improvement loop into a host
|
|
16
|
-
|
|
15
|
+
// self-improvement loop into a host; `lorekit-groom` is the maintenance skill
|
|
16
|
+
// that dedupes/lints/merges/expires an accumulated store. install/uninstall/
|
|
17
|
+
// doctor iterate this list.
|
|
18
|
+
export const SKILLS = ['lorekit-memory', 'lorekit-setup', 'lorekit-groom'].map((name) => ({
|
|
17
19
|
name,
|
|
18
20
|
source: path.join(PKG_ROOT, 'skill', name),
|
|
19
21
|
}));
|
package/src/mcp.mjs
CHANGED
|
@@ -154,6 +154,12 @@ export async function restFetch(baseUrl, token, path, { method = 'GET', body, ti
|
|
|
154
154
|
...(token ? { authorization: `Bearer ${token}` } : {}),
|
|
155
155
|
...(traceparent ? { traceparent } : {}),
|
|
156
156
|
...(correlationId ? { 'x-lorekit-correlation-id': correlationId } : {}),
|
|
157
|
+
// Name the calling surface so usage analytics can tell a CLI read from a
|
|
158
|
+
// dashboard one. Not cosmetic: `GET /memories/read-activity` EXCLUDES the
|
|
159
|
+
// `dashboard` client (a human browsing lore is not consuming it), so a
|
|
160
|
+
// caller that wants its reads counted has to be attributable. Constant,
|
|
161
|
+
// never env-driven — this binary is always the CLI.
|
|
162
|
+
'x-lorekit-client': 'cli',
|
|
157
163
|
};
|
|
158
164
|
const res = await fetch(url, {
|
|
159
165
|
method,
|