@lorekit/cli 1.33.4 → 1.34.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 +4 -1
- package/bin/lorekit.mjs +9 -6
- package/package.json +1 -1
- package/skill/lorekit-groom/SKILL.md +2 -2
- package/skill/lorekit-groom/rules/grooming-pass.md +5 -0
- package/src/lessons-view.mjs +35 -0
- package/src/lint.mjs +3 -3
package/README.md
CHANGED
|
@@ -381,7 +381,10 @@ lorekit lint --json # { total, offline, remote } structured findings
|
|
|
381
381
|
|
|
382
382
|
Rules: **empty-value** (blank/whitespace-only body), **short-value** (a non-empty
|
|
383
383
|
body below a small length threshold), **untrimmed-value** (real content with
|
|
384
|
-
surrounding whitespace), **empty-key** (blank key),
|
|
384
|
+
surrounding whitespace), **empty-key** (blank key), **volatile-key** (the key
|
|
385
|
+
carries a per-sighting identifier — a run of 6+ digits such as a GitHub comment
|
|
386
|
+
id, or a `pr<n>` / `issue<n>` segment — so it never collides, never dedups, and
|
|
387
|
+
freezes `seen_count` at 1), and **malformed-scope** (e.g.
|
|
385
388
|
a single `:` where `::` is expected). `lint` **exits non-zero (1) when any issue
|
|
386
389
|
is found**, so it is usable as a CI gate (`lorekit lint || exit 1`); a clean run —
|
|
387
390
|
or one where only a store is unavailable — exits 0. The pure rule predicates live
|
package/bin/lorekit.mjs
CHANGED
|
@@ -78,8 +78,9 @@ ${c.bold('Commands')}
|
|
|
78
78
|
(resolve) hierarchy and mark, per key, which scope's memory WINS and which are
|
|
79
79
|
shadowed — the real hook-resolution order. --json, --scope <s>.
|
|
80
80
|
lint Flag low-quality memories (empty/short/untrimmed value, empty key,
|
|
81
|
-
malformed scope) across the applicable scopes and
|
|
82
|
-
non-zero when issues are found (CI gate).
|
|
81
|
+
volatile key, malformed scope) across the applicable scopes and
|
|
82
|
+
both stores. Exits non-zero when issues are found (CI gate).
|
|
83
|
+
--json, --scope <s>.
|
|
83
84
|
dedupe Find likely-duplicate memories via a zero-dep word-overlap HEURISTIC
|
|
84
85
|
(Jaccard >= threshold, not semantic), grouped into clusters per
|
|
85
86
|
store. --json, --scope <s>, --threshold <0..1>.
|
|
@@ -495,10 +496,12 @@ ${c.bold('Usage')}
|
|
|
495
496
|
|
|
496
497
|
Checks every memory for the current directory's scopes (project/branch/repo/
|
|
497
498
|
global), across both stores, against a small set of quality rules: empty or
|
|
498
|
-
whitespace-only value, suspiciously short value, untrimmed value, empty key,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
499
|
+
whitespace-only value, suspiciously short value, untrimmed value, empty key, a
|
|
500
|
+
volatile per-sighting identifier in the key (a run of 6+ digits, or a \`pr<n>\` /
|
|
501
|
+
\`issue<n>\` reference), and malformed scope (e.g. a single \`:\` where \`::\` is
|
|
502
|
+
expected). Each finding names the rule it violated. Exits NON-ZERO when any issue
|
|
503
|
+
is found, so it works as a CI gate; a clean run — or one where only a store is
|
|
504
|
+
unavailable — exits 0.
|
|
502
505
|
|
|
503
506
|
${c.bold('Options')}
|
|
504
507
|
-d, --dir <path> Target project root (default: current directory)
|
package/package.json
CHANGED
|
@@ -89,8 +89,8 @@ The short version is six phases; the first four are pure analysis.
|
|
|
89
89
|
`lorekit scopes` (store-wide inventory: every scope with its lesson count)
|
|
90
90
|
show where the mass is. Pick the noisiest scope to start.
|
|
91
91
|
2. **Lint** — `lorekit lint --json` flags structurally bad lessons (empty /
|
|
92
|
-
whitespace / suspiciously short / untrimmed values, empty keys,
|
|
93
|
-
scopes). These are the cheapest wins: fix or drop them first.
|
|
92
|
+
whitespace / suspiciously short / untrimmed values, empty keys, volatile
|
|
93
|
+
keys, malformed scopes). These are the cheapest wins: fix or drop them first.
|
|
94
94
|
3. **Dedupe** — `lorekit dedupe --json` clusters near-duplicate lessons. Start at
|
|
95
95
|
a high `--threshold` (e.g. `0.85`) for confident duplicates, then lower it to
|
|
96
96
|
surface looser paraphrases. Use `lorekit show <scope::key>` to read each
|
|
@@ -38,6 +38,11 @@ Findings are structural, not semantic — each names its rule:
|
|
|
38
38
|
- **empty-value / short-value / untrimmed-value** — the lesson carries little or
|
|
39
39
|
no signal, or has stray leading/trailing whitespace.
|
|
40
40
|
- **empty-key** — no key to address it by.
|
|
41
|
+
- **volatile-key** — the key carries a per-sighting identifier (a run of 6+
|
|
42
|
+
digits such as a GitHub comment id, or a `pr<n>` / `issue<n>` segment), so it
|
|
43
|
+
never collides with a later write, never dedups, and leaves `seen_count`
|
|
44
|
+
frozen at 1. Re-key it onto the structural pattern and move the identifier
|
|
45
|
+
into the body.
|
|
41
46
|
- **malformed-scope** — the scope string is invalid.
|
|
42
47
|
|
|
43
48
|
These are the cheapest wins and the least controversial, so clear them first.
|
package/src/lessons-view.mjs
CHANGED
|
@@ -271,6 +271,41 @@ export const LINT_RULES = {
|
|
|
271
271
|
return v.trim() && v !== v.trim() ? 'value has leading/trailing whitespace' : null;
|
|
272
272
|
},
|
|
273
273
|
'empty-key': (e) => (String(e.key ?? '').trim() ? null : 'key is empty or whitespace-only'),
|
|
274
|
+
// A key carrying a per-sighting identifier (a comment id, a PR/issue number) is
|
|
275
|
+
// unique forever, so it never collides, so the upsert never dedups it, so
|
|
276
|
+
// `seen_count` stays frozen at 1 and the memory can never reach a recurrence
|
|
277
|
+
// threshold — a write-only record. Detection is deliberately conservative:
|
|
278
|
+
// • a run of 6+ digits (a GitHub comment id is ~10; `sha256`, `oauth2`,
|
|
279
|
+
// `wcag22`, and semantic versions are all shorter runs);
|
|
280
|
+
// • a `pr<n>` / `issue<n>` reference — the number joined by nothing, `-`, or
|
|
281
|
+
// `_` — delimited by `:`, `-`, `_`, `/`, or a string boundary, so mid-word
|
|
282
|
+
// digits (`oauth2`) never match.
|
|
283
|
+
// `volatileKeyAllow` is an embedder/test knob mirroring `short-value`'s
|
|
284
|
+
// `minValueLen` precedent — a list of substrings that exempt a key. There is
|
|
285
|
+
// no config key and no per-entry marker.
|
|
286
|
+
'volatile-key': (e, { volatileKeyAllow = [] } = {}) => {
|
|
287
|
+
const key = String(e.key ?? '');
|
|
288
|
+
if (!key.trim()) return null; // an empty key is `empty-key`'s to report.
|
|
289
|
+
// Tolerate a bare string as well as a list, so a caller passing
|
|
290
|
+
// `{ volatileKeyAllow: 'lorekit-231' }` does not silently iterate characters.
|
|
291
|
+
const allowList = Array.isArray(volatileKeyAllow) ? volatileKeyAllow : [volatileKeyAllow];
|
|
292
|
+
for (const allow of allowList) {
|
|
293
|
+
if (allow && key.includes(String(allow))) return null;
|
|
294
|
+
}
|
|
295
|
+
const digitRun = key.match(/\d{6,}/);
|
|
296
|
+
if (digitRun) {
|
|
297
|
+
return `key contains a volatile per-sighting identifier: '${digitRun[0]}' (a run of ${digitRun[0].length} digits)`;
|
|
298
|
+
}
|
|
299
|
+
// Boundary-anchored rather than split-then-match: splitting on `-` would
|
|
300
|
+
// separate `pr` from `231` and `pr-231` would slip through. The reference
|
|
301
|
+
// must start at a boundary (`:`, `-`, `_`, `/`, or the string start) and end
|
|
302
|
+
// at one, so `oauth2`/`sha256`/`wcag22` still never match.
|
|
303
|
+
const reference = key.match(/(?:^|[:\-_/])((?:pr|issue)[-_]?\d+)(?=$|[:\-_/])/i);
|
|
304
|
+
if (reference) {
|
|
305
|
+
return `key contains a volatile per-sighting identifier: '${reference[1]}' (a pr/issue number segment)`;
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
},
|
|
274
309
|
'malformed-scope': (e) => {
|
|
275
310
|
const reason = scopeIssue(e.scope);
|
|
276
311
|
return reason ? `malformed scope: ${reason}` : null;
|
package/src/lint.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// `lorekit lint` — flag low-quality lessons across the applicable scopes and
|
|
2
2
|
// both stores. Each finding names the rule it violated (empty/whitespace value,
|
|
3
|
-
// suspiciously short value, untrimmed value, empty key,
|
|
4
|
-
// rules are pure predicates in `lessons-view.mjs` (`LINT_RULES` /
|
|
5
|
-
// each independently unit-tested.
|
|
3
|
+
// suspiciously short value, untrimmed value, empty key, volatile key, malformed
|
|
4
|
+
// scope). The rules are pure predicates in `lessons-view.mjs` (`LINT_RULES` /
|
|
5
|
+
// `lintEntry`), each independently unit-tested.
|
|
6
6
|
//
|
|
7
7
|
// Exit convention: `lint` exits NON-ZERO (1) when any finding exists, so it is
|
|
8
8
|
// usable as a CI gate (`lorekit lint || fail`); a clean run — or a run where the
|