@lorekit/cli 1.63.2 → 1.64.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.
package/README.md
CHANGED
|
@@ -419,11 +419,17 @@ body below a small length threshold), **untrimmed-value** (real content with
|
|
|
419
419
|
surrounding whitespace), **empty-key** (blank key), **volatile-key** (the key
|
|
420
420
|
carries a per-sighting identifier — a run of 6+ digits such as a GitHub comment
|
|
421
421
|
id, or a `pr<n>` / `issue<n>` segment — so it never collides, never dedups, and
|
|
422
|
-
freezes `seen_count` at 1),
|
|
423
|
-
a single `:` where `::` is expected)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
422
|
+
freezes `seen_count` at 1), **malformed-scope** (e.g.
|
|
423
|
+
a single `:` where `::` is expected), and **unkinded-state-record** (the value
|
|
424
|
+
parses as a JSON object/array but `kind` is unset — bare scalars are excluded,
|
|
425
|
+
caught by `short-value` only when short; a longer bare scalar is judged by no
|
|
426
|
+
rule — because the SessionStart digest excludes non-lesson kinds by *kind*,
|
|
427
|
+
not by shape, so an un-kinded state record renders as a raw JSON blob inline
|
|
428
|
+
in every session's digest; set `--kind bus`/`--kind signal`).
|
|
429
|
+
`lint` **exits non-zero (1) when any issue is found**, so it is usable as a CI
|
|
430
|
+
gate (`lorekit lint || exit 1`); a clean run — or one where only a store is
|
|
431
|
+
unavailable — exits 0. The pure rule predicates live in `lessons-view.mjs` and
|
|
432
|
+
are unit-tested one rule at a time.
|
|
427
433
|
|
|
428
434
|
### `lorekit dedupe`
|
|
429
435
|
|
|
@@ -703,7 +709,9 @@ loops: a per-user **home** tier plus an opt-in per-repo **project** tier.
|
|
|
703
709
|
|
|
704
710
|
Each tier is foldered by canonical scope, one markdown file per lesson, with
|
|
705
711
|
YAML frontmatter (`scope, key, tags, source_agent, trigger, created, updated,
|
|
706
|
-
archived_at
|
|
712
|
+
archived_at, origin_repo, origin_branch, origin_commit, origin_pr,
|
|
713
|
+
expires_at, seen_count, kind, host` — the same column set as `format.mjs`'s
|
|
714
|
+
`FIELDS`) and the lesson as the body:
|
|
707
715
|
|
|
708
716
|
```
|
|
709
717
|
~/.lorekit/ <repo>/.lorekit/ (opt-in)
|
package/package.json
CHANGED
|
@@ -56,6 +56,10 @@ Findings are structural, not semantic — each names its rule:
|
|
|
56
56
|
frozen at 1. Re-key it onto the structural pattern and move the identifier
|
|
57
57
|
into the body.
|
|
58
58
|
- **malformed-scope** — the scope string is invalid.
|
|
59
|
+
- **unkinded-state-record** — the value parses as a JSON object/array but the
|
|
60
|
+
entry has no `kind`, so it renders as a raw JSON blob in every SessionStart
|
|
61
|
+
digest instead of being excluded by `isGeneralLesson`. Set `--kind bus` or
|
|
62
|
+
`--kind signal` on the record.
|
|
59
63
|
|
|
60
64
|
These are the cheapest wins and the least controversial, so clear them first.
|
|
61
65
|
For each: either **fix it in place** (rewrite a too-short value into a real
|
|
@@ -311,6 +311,30 @@ export const LINT_RULES = {
|
|
|
311
311
|
const reason = scopeIssue(e.scope);
|
|
312
312
|
return reason ? `malformed scope: ${reason}` : null;
|
|
313
313
|
},
|
|
314
|
+
// A CI state record (ci-state-records.md) is JSON on an entry left with no
|
|
315
|
+
// `kind`. `fetchLessons`'s `isGeneralLesson(kind)` filter — `kind == null ||
|
|
316
|
+
// kind === 'lesson'` — is a hard exclusion by kind, not by shape, so a record
|
|
317
|
+
// like this is NOT excluded from the SessionStart digest: it renders inline as
|
|
318
|
+
// a raw JSON blob, unreadable next to prose lessons. Conservative by design,
|
|
319
|
+
// like `volatile-key`: only a value that PARSES as a JSON object or array
|
|
320
|
+
// fires — a bare scalar (a plain lesson body that happens to be a number or a
|
|
321
|
+
// quoted string) is excluded. A short bare scalar is caught by `short-value`;
|
|
322
|
+
// a bare scalar at or above `MIN_VALUE_LEN` (e.g. a 20-char quoted string) is
|
|
323
|
+
// judged by no rule at all — a known gap, not a claim that `short-value`
|
|
324
|
+
// covers every bare scalar — and this rule has no opinion on it either way.
|
|
325
|
+
'unkinded-state-record': (e) => {
|
|
326
|
+
if (e.kind != null) return null; // an explicit kind is exactly what this rule exists to require.
|
|
327
|
+
const v = String(e.value ?? '').trim();
|
|
328
|
+
if (!v) return null; // an empty value is `empty-value`'s to report.
|
|
329
|
+
let parsed;
|
|
330
|
+
try {
|
|
331
|
+
parsed = JSON.parse(v);
|
|
332
|
+
} catch {
|
|
333
|
+
return null; // not JSON — an ordinary prose lesson, not this rule's concern.
|
|
334
|
+
}
|
|
335
|
+
if (parsed === null || typeof parsed !== 'object') return null; // a bare scalar — short-value's to catch when short, otherwise unjudged.
|
|
336
|
+
return 'value is a JSON object/array with no kind set — it renders as a raw JSON blob in every SessionStart digest; set --kind bus or --kind signal';
|
|
337
|
+
},
|
|
314
338
|
};
|
|
315
339
|
|
|
316
340
|
// Run every lint rule against one normalized entry, returning the findings it
|
package/src/store/format.mjs
CHANGED
|
@@ -34,6 +34,13 @@ export const FIELDS = [
|
|
|
34
34
|
// columns above: a file written before this existed decodes it as absent,
|
|
35
35
|
// which the read projection reports as 0 rather than inventing a count.
|
|
36
36
|
'seen_count',
|
|
37
|
+
// Taxonomy — `kind` ('lesson'/'bus'/'signal') and `host` (which agent loop
|
|
38
|
+
// wrote it), mirroring the hosted `memories.kind`/`memories.host` columns.
|
|
39
|
+
// Appended like the columns above: a file written before these existed
|
|
40
|
+
// decodes them as absent, which `normalizeEntry` in lessons-view.mjs falls
|
|
41
|
+
// back to inferring from tags for, same as an absent remote value.
|
|
42
|
+
'kind',
|
|
43
|
+
'host',
|
|
37
44
|
];
|
|
38
45
|
|
|
39
46
|
// Serialize an entry ({ ...columns, value }) into file text.
|
package/src/store/local.mjs
CHANGED
|
@@ -100,7 +100,7 @@ class LocalStore {
|
|
|
100
100
|
// contract's error surfacing.
|
|
101
101
|
async write({
|
|
102
102
|
scope, key, value, tags, source_agent, trigger, created_at, ttl_days, clear_ttl,
|
|
103
|
-
origin_repo, origin_branch, origin_commit, origin_pr,
|
|
103
|
+
origin_repo, origin_branch, origin_commit, origin_pr, kind, host,
|
|
104
104
|
} = {}) {
|
|
105
105
|
const now = new Date().toISOString();
|
|
106
106
|
const existing = this._findByKey(scope, key);
|
|
@@ -129,6 +129,10 @@ class LocalStore {
|
|
|
129
129
|
origin_branch: origin_branch ?? existing?.entry.origin_branch ?? null,
|
|
130
130
|
origin_commit: origin_commit ?? existing?.entry.origin_commit ?? null,
|
|
131
131
|
origin_pr: origin_pr ?? existing?.entry.origin_pr ?? null,
|
|
132
|
+
// Taxonomy, same last-known-wins provenance as origin above: an
|
|
133
|
+
// omitted --kind/--host on a re-write must not erase a prior value.
|
|
134
|
+
kind: kind ?? existing?.entry.kind ?? null,
|
|
135
|
+
host: host ?? existing?.entry.host ?? null,
|
|
132
136
|
created,
|
|
133
137
|
updated: existing ? now : override || now,
|
|
134
138
|
archived_at: null,
|
|
@@ -175,6 +179,8 @@ class LocalStore {
|
|
|
175
179
|
origin_branch: entry.origin_branch ?? null,
|
|
176
180
|
origin_commit: entry.origin_commit ?? null,
|
|
177
181
|
origin_pr: entry.origin_pr ?? null,
|
|
182
|
+
kind: entry.kind ?? null,
|
|
183
|
+
host: entry.host ?? null,
|
|
178
184
|
created: entry.created ?? now,
|
|
179
185
|
updated: entry.updated ?? now,
|
|
180
186
|
archived_at: entry.archived_at ?? null,
|
|
@@ -521,7 +521,7 @@ export const MCP_TOOL_DEFS = [
|
|
|
521
521
|
"type": "integer",
|
|
522
522
|
"minimum": 1,
|
|
523
523
|
"maximum": 3650,
|
|
524
|
-
"description": "Match lessons
|
|
524
|
+
"description": "Match lessons not individually opened via MCP or the CLI for at least this many days (a bulk list/search result or a dashboard view does not count). A never-opened lesson always matches."
|
|
525
525
|
},
|
|
526
526
|
"max_seen_count": {
|
|
527
527
|
"type": "integer",
|
|
@@ -700,7 +700,7 @@ export const MCP_TOOL_DEFS = [
|
|
|
700
700
|
"type": "integer",
|
|
701
701
|
"minimum": 1,
|
|
702
702
|
"maximum": 3650,
|
|
703
|
-
"description": "Match lessons
|
|
703
|
+
"description": "Match lessons not individually opened via MCP or the CLI for at least this many days (a bulk list/search result or a dashboard view does not count). Omit to leave unchanged; pass explicit null to clear."
|
|
704
704
|
},
|
|
705
705
|
"max_seen_count": {
|
|
706
706
|
"type": "integer",
|
|
@@ -880,7 +880,7 @@ export const MCP_TOOL_DEFS = [
|
|
|
880
880
|
"type": "integer",
|
|
881
881
|
"minimum": 1,
|
|
882
882
|
"maximum": 3650,
|
|
883
|
-
"description": "Match lessons
|
|
883
|
+
"description": "Match lessons not individually opened via MCP or the CLI for at least this many days (a bulk list/search result or a dashboard view does not count). A never-opened lesson always matches."
|
|
884
884
|
},
|
|
885
885
|
"max_seen_count": {
|
|
886
886
|
"type": "integer",
|
|
@@ -1044,7 +1044,7 @@ export const MCP_TOOL_DEFS = [
|
|
|
1044
1044
|
"type": "integer",
|
|
1045
1045
|
"minimum": 1,
|
|
1046
1046
|
"maximum": 3650,
|
|
1047
|
-
"description": "Match lessons
|
|
1047
|
+
"description": "Match lessons not individually opened via MCP or the CLI for at least this many days (a bulk list/search result or a dashboard view does not count). A never-opened lesson always matches."
|
|
1048
1048
|
},
|
|
1049
1049
|
"max_seen_count": {
|
|
1050
1050
|
"type": "integer",
|