@lorekit/cli 1.0.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 ADDED
@@ -0,0 +1,320 @@
1
+ # @lorekit/cli
2
+
3
+ Install the **LoreKit shared-memory skill** into a project and run health
4
+ checks against your LoreKit MCP server — a small, zero-dependency Node CLI.
5
+
6
+ LoreKit gives coding agents a shared, persistent memory: lessons one agent
7
+ learns are stored centrally and read by every other agent, in every session,
8
+ CI included. This CLI wires an agent up to it in two commands.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npx @lorekit/cli install
14
+ npx @lorekit/cli doctor
15
+ ```
16
+
17
+ Requires Node 18+ (for the built-in `fetch`). No dependencies.
18
+
19
+ ## Commands
20
+
21
+ ### `lorekit install`
22
+
23
+ Scaffolds the `lorekit-memory` skill into `.claude/skills/lorekit-memory/` and
24
+ adds (or updates) a `lorekit` server in the project's `.mcp.json`, preserving
25
+ any other MCP servers already configured.
26
+
27
+ ```bash
28
+ lorekit install \
29
+ --endpoint https://<project-ref>.supabase.co/functions/v1/mcp \
30
+ --token lk_rw_your_token
31
+ ```
32
+
33
+ If run in a TTY without `--endpoint` / `--token`, it prompts for them.
34
+ Use `--yes` for non-interactive environments (endpoint required via flag/env).
35
+ Use `--force` to overwrite an existing skill copy.
36
+
37
+ ### `lorekit doctor`
38
+
39
+ Verifies the setup and prints a status report:
40
+
41
+ - Node runtime is 18+
42
+ - the `lorekit-memory` skill is installed
43
+ - the **resolved memory mode and which source decided it**, plus any active
44
+ deny constraints
45
+ - for `local`: the store path, entry count, and whether it is committed or
46
+ gitignored
47
+ - for `remote`: `.mcp.json` has a `lorekit` server, the endpoint is real (not
48
+ the `<project-ref>` placeholder), the token and its permission tier
49
+ (`lk_rw_*` vs `lk_ro_*`), and that the endpoint is reachable
50
+ - for `off`: a note that memory is disabled
51
+ - the git-derived read/write scopes for the current directory
52
+
53
+ ```bash
54
+ lorekit doctor # config + connectivity checks
55
+ lorekit doctor --deep # also does a write → read → delete round-trip (needs lk_rw_*)
56
+ ```
57
+
58
+ Exit code is non-zero if any check fails, so it fits CI gates.
59
+
60
+ ### `lorekit hook`
61
+
62
+ The **shared hook engine** behind the Claude Code / Cursor / Codex plugins.
63
+ It is not run by hand — the plugins wire it into their hook config. It reads
64
+ the host framework's JSON on stdin and prints that host's injection format on
65
+ stdout (lessons at session start; a nudge on failure or at end of turn),
66
+ always exiting 0 so it can never block the host agent.
67
+
68
+ ```bash
69
+ lorekit hook --adapter <claude|cursor|codex> --event <SessionStart|Stop|…>
70
+ ```
71
+
72
+ One engine serves all three hosts; each `--adapter` only reshapes input/output
73
+ to that host's contract. See [`plugins/`](../../plugins/) for the bundles.
74
+
75
+ ### `lorekit mcp`
76
+
77
+ A **local stdio MCP server**. It exposes LoreKit's `memory.*` tools backed by
78
+ the store the [control model](#memory-modes--the-control-model) resolves, so an
79
+ agent's `.mcp.json` can point at the CLI instead of `mcp-remote <url>` — giving
80
+ the model discoverable, autonomous `memory.*` tool calls **offline against the
81
+ local `.lorekit/` store** (no network, Bash-restricted contexts included).
82
+
83
+ ```bash
84
+ lorekit mcp # serve on stdin/stdout using the resolved mode
85
+ lorekit mcp --mode local --store .lorekit
86
+ ```
87
+
88
+ It speaks JSON-RPC 2.0 over newline-delimited stdin/stdout (the MCP stdio
89
+ transport, hand-rolled — zero dependencies) and is **not run by hand**: only
90
+ JSON-RPC frames reach stdout. It serves whatever mode resolves — `local` serves
91
+ the `.lorekit/` files directly, `remote` passes calls through to the hosted
92
+ endpoint, and `off` advertises no tools. Tools advertised: `memory.write`,
93
+ `memory.read`, `memory.list`, `memory.search`, `memory.delete`,
94
+ `memory.archive`.
95
+
96
+ Wire it into `.mcp.json` as an alternative to the `mcp-remote <url>` transport —
97
+ this variant needs no endpoint or token for local mode:
98
+
99
+ ```jsonc
100
+ {
101
+ "mcpServers": {
102
+ "lorekit": {
103
+ "command": "npx",
104
+ "args": ["-y", "@lorekit/cli", "mcp"]
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## Memory modes & the control model
111
+
112
+ Memory has a controllable backend. Three **modes**:
113
+
114
+ | Mode | Where lessons live | Notes |
115
+ |------|--------------------|-------|
116
+ | `off` | nowhere | Memory is disabled — every hook event and store op is a silent no-op. |
117
+ | `local` | markdown files in two tiers (see below) | **Local means _not_ on the hosted website** — local lessons never sync to the LoreKit dashboard. That is the point of local: private-by-default, greppable, git-native. |
118
+ | `remote` | the LoreKit MCP server (hosted) | The shared, cross-machine backend. Reads stay silent until an endpoint + token are configured. This is the default. |
119
+
120
+ ### Local store layout — two tiers
121
+
122
+ Local mode mirrors the two-tier model used by the `aw` / persistent-memory
123
+ loops: a per-user **home** tier plus an opt-in per-repo **project** tier.
124
+
125
+ | Tier | Path | Availability |
126
+ |------|------|--------------|
127
+ | **home** | `~/.lorekit/` (override with `LOREKIT_HOME`) | Always available — per-user, cross-repo. |
128
+ | **project** | `<repo>/.lorekit/` (override with `LOREKIT_STORE`) | **Opt-in:** active only when the directory exists. Create it once to start persisting repo/branch lessons in the project. |
129
+
130
+ Each tier is foldered by canonical scope, one markdown file per lesson, with
131
+ YAML frontmatter (`scope, key, tags, source_agent, trigger, created, updated,
132
+ archived_at`) and the lesson as the body:
133
+
134
+ ```
135
+ ~/.lorekit/ <repo>/.lorekit/ (opt-in)
136
+ ├── config.json ├── global/
137
+ ├── global/ ├── repo/<owner>/<repo>/
138
+ ├── repo/<o>/<r>/ └── branch/<owner>/<repo>/<branch>/
139
+ └── branch/<o>/<r>/… └── <slug-of-key>.md
140
+ ```
141
+
142
+ **Read = two-tier merge.** For each scope in the read order, entries from both
143
+ tiers are unioned and the **project tier wins on a key collision** (closer scope
144
+ wins), consistent with the remote narrow→broad merge.
145
+
146
+ **Write routing by scope:**
147
+
148
+ - `global` → **home** tier.
149
+ - `repo::` / `branch::` → **project** tier when it exists (opted-in), else the
150
+ **home** tier.
151
+
152
+ To start persisting repo/branch lessons in the project, create
153
+ `<repo>/.lorekit/` (or run `lorekit migrate --to project --yes`). Commit it to
154
+ share lessons with your team (git-native sharing); add it to `.gitignore` to
155
+ keep them private to your checkout.
156
+
157
+ **Delete / archive across tiers.** Deleting or archiving a key removes the
158
+ closest (project) copy first; a broader **home** copy, if any, remains and takes
159
+ a second delete. This is deliberate — one `delete` never reaches through and
160
+ erases your cross-repo home lesson.
161
+
162
+ > **Same tiering, two realizations (local ⟷ remote).** The tiered, closer-wins
163
+ > merge-read is identical in both backends. Local expresses "universal vs
164
+ > team-shared" as two physical **locations** (home dir vs committed project
165
+ > dir); remote expresses the same distinction through the canonical **scopes**
166
+ > in one shared DB (`global` ≈ your cross-repo home, `repo::` ≈ team-shared) —
167
+ > sharing comes from the account/token, so remote needs no second location.
168
+ > Different mechanism, same concept.
169
+
170
+ ### `lorekit migrate` — relocation / rename tool
171
+
172
+ Moved or renamed a local store (e.g. an old `.lore/`)? `migrate` re-writes its
173
+ entries into the current two-tier layout so lessons are never stranded:
174
+
175
+ ```bash
176
+ lorekit migrate --from .lore # dry-run: preview counts per scope
177
+ lorekit migrate --from .lore --yes # apply, routing each entry by scope
178
+ lorekit migrate --from .lore --to project --yes # force all entries into the project tier
179
+ ```
180
+
181
+ Dry-run (preview) by default; `--yes` (or `--apply`) applies. Idempotent — a
182
+ re-run is a no-op. It reads LoreKit's own on-disk format only (it does **not**
183
+ import persistent-memory's `~/.agent-memory/<bucket>/` format).
184
+
185
+ ### The control model — two layers, deny-wins
186
+
187
+ Two config layers decide the mode:
188
+
189
+ - **User / machine** — env `LOREKIT_MODE`, `LOREKIT_HOME`, `LOREKIT_STORE`,
190
+ `LOREKIT_DENY` (and `LOREKIT_MCP_URL` / `LOREKIT_TOKEN` for remote), plus a
191
+ user config file `~/.lorekit/config.json`.
192
+ - **Repo / team** — a `.lorekit.json` at the repo root (and/or the existing
193
+ `lorekit` block in `.mcp.json` for the connection).
194
+
195
+ Both files share one schema:
196
+
197
+ ```jsonc
198
+ {
199
+ "mode": "local", // select a mode (off | local | remote)
200
+ "store": ".lorekit", // project-tier store dir (relative to repo root, or absolute)
201
+ "deny": ["remote"] // forbid modes outright — deny always wins
202
+ }
203
+ ```
204
+
205
+ **Precedence (a _selection_ within what is allowed):**
206
+ `env LOREKIT_MODE` → user config `mode` → repo config `mode` → built-in default
207
+ (`remote`).
208
+
209
+ **Constraints (`deny`) always win.** Denies are a **union** across every source
210
+ and only ever accumulate — a user-level hard opt-out is a **ceiling the repo
211
+ cannot override**:
212
+
213
+ - A user who declares `"deny": ["remote"]` (privacy / compliance) can never be
214
+ flipped to remote by any repo default or env flag — they resolve to `local`
215
+ (if they selected it) or `off`, never `remote`.
216
+ - A repo or CI job that declares `"deny": ["local"]` (no `.lorekit/` in the tree)
217
+ makes local unselectable there — an env `LOREKIT_MODE=local` is capped, and
218
+ resolution falls through to `remote`, or `off` if both are denied.
219
+
220
+ `off` is never deniable, so it is always the terminal fallback. Run
221
+ `lorekit doctor` to see the resolved mode, **which source decided it**, and any
222
+ active deny constraints.
223
+
224
+ ## Options
225
+
226
+ | Flag | Meaning |
227
+ |------|---------|
228
+ | `-d, --dir <path>` | Target project root (default: cwd) |
229
+ | `-e, --endpoint <url>` | LoreKit MCP endpoint |
230
+ | `-t, --token <token>` | LoreKit token |
231
+ | `--mode <mode>` | Memory mode override for `doctor`: `off` / `local` / `remote` |
232
+ | `--store <path>` | Local project-tier store directory (default `.lorekit`) |
233
+ | `--from <path>` | Source store to migrate from (`migrate`) |
234
+ | `--to <tier>` | Migration destination tier: `home` / `project` (`migrate`; default routes by scope) |
235
+ | `--apply` | Apply the migration — alias of `--yes` (`migrate`) |
236
+ | `-y, --yes` | Non-interactive / apply; never prompt |
237
+ | `--force` | Overwrite existing skill files (`install`) |
238
+ | `--deep` | Write/read/delete round-trip (`doctor`) |
239
+ | `--adapter <name>` | Host framework for `hook`: `claude` / `cursor` / `codex` |
240
+ | `--event <name>` | Host hook event for `hook` (else read from the stdin payload) |
241
+ | `-h, --help` | Help |
242
+ | `-v, --version` | Version |
243
+
244
+ ## Environment variables
245
+
246
+ | Variable | Purpose |
247
+ |----------|---------|
248
+ | `LOREKIT_MODE` | select a mode: `off` / `local` / `remote` |
249
+ | `LOREKIT_DENY` | comma-separated modes to forbid (deny-wins); e.g. `remote` |
250
+ | `LOREKIT_HOME` | home-tier root + config directory (default `~/.lorekit`) |
251
+ | `LOREKIT_STORE` | project-tier store directory (default `.lorekit`) |
252
+ | `LOREKIT_MCP_URL` / `LOREKIT_ENDPOINT` | endpoint fallback |
253
+ | `LOREKIT_TOKEN` | token fallback |
254
+ | `NO_COLOR` | disable colored output |
255
+
256
+ ## What the skill does
257
+
258
+ The installed `lorekit-memory` skill teaches an agent to:
259
+
260
+ - **Read** scoped lessons at the start of a task, on first navigation into
261
+ unfamiliar code, and before risky operations (narrow-to-broad scope merge).
262
+ - **Write** a lesson when something goes wrong — a stuck loop, a repeated
263
+ failure, a gotcha, a near-miss, or a costly wrong assumption — phrased as an
264
+ observation and scoped to the narrowest namespace that fits.
265
+
266
+ This mirrors the read-on-start / write-on-failure loop of the `aw`
267
+ autonomous-workflow agent. See the skill's own `SKILL.md` for the full
268
+ protocol.
269
+
270
+ The **skill** is model-invoked (the agent chooses to use it). For a
271
+ **deterministic** guarantee — lessons injected on every session start, a nudge
272
+ on every tool failure — use the framework plugins in [`plugins/`](../../plugins/),
273
+ which fire the `lorekit hook` engine on host lifecycle events. The skill and
274
+ the hooks compose: hooks guarantee the *timing*, the skill supplies the
275
+ *authoring judgment*.
276
+
277
+ ## Testing & validating across frameworks
278
+
279
+ `npm test` (or `node --test test/*.test.mjs`) runs four layers, so you can
280
+ validate all three integrations without launching each agent by hand:
281
+
282
+ 1. **Unit** — scope parsing, failure heuristic, lesson formatting, adapter
283
+ mapping/emit.
284
+ 2. **Engine end-to-end** — spawns the real `lorekit hook` binary for every
285
+ adapter/event, including a mock MCP server that proves the `SessionStart`
286
+ read path injects lessons, plus throttling and bad-input handling.
287
+ 3. **Cross-framework conformance** — replays payload **fixtures** through the
288
+ binary and asserts the stdout matches each host's documented contract
289
+ (`hookSpecificOutput.additionalContext` for Claude/Codex, `followup_message`
290
+ for Cursor).
291
+ 4. **Wiring** — runs `claude plugin validate` on the Claude bundle (skipped if
292
+ the `claude` CLI is absent) and structurally validates the Cursor and Codex
293
+ configs; also asserts the vendored skill is in sync with its source.
294
+
295
+ ### Harvesting real fixtures (one run per framework)
296
+
297
+ Layer 3 ships with documented seed fixtures under `test/fixtures/`. To prove
298
+ conformance against what each framework *actually* sends, record real payloads
299
+ once by pointing its hook command at the recorder:
300
+
301
+ ```bash
302
+ # Temporarily set this env for the hook command in the framework's config,
303
+ # then drive the agent through a session start, a failing command, and a stop:
304
+ LOREKIT_HOOK_RECORD=/abs/path/to/packages/cli/test/fixtures \
305
+ npx @lorekit/cli hook --adapter claude --event SessionStart
306
+ ```
307
+
308
+ Each invocation overwrites `test/fixtures/<adapter>-<event>.json` with the real
309
+ payload. Commit the updated fixtures; the conformance tests then run offline
310
+ forever. This reduces manual validation to a single capture pass per tool.
311
+
312
+ > The one thing no offline test can cover is a real model loop (the agent
313
+ > actually consuming the injected context). `claude plugin validate` confirms
314
+ > the real Claude CLI accepts the wiring; for a true live check, install the
315
+ > plugin and start one session per tool.
316
+
317
+ ## Security note
318
+
319
+ `install` writes your token into `.mcp.json`. Keep that file out of version
320
+ control (LoreKit's root `.gitignore` already ignores `.mcp.json`).
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ // LoreKit CLI — install the shared-memory skill and run health checks.
3
+ import process from 'node:process';
4
+ import { parseArgs, log, err, c } from '../src/util.mjs';
5
+ import { install } from '../src/install.mjs';
6
+ import { doctor } from '../src/doctor.mjs';
7
+ import { hook } from '../src/hook.mjs';
8
+ import { migrate } from '../src/migrate.mjs';
9
+ import { mcpServer } from '../src/mcp-server.mjs';
10
+
11
+ const VERSION = '1.0.0';
12
+
13
+ const HELP = `${c.bold('lorekit')} — shared persistent memory for coding agents
14
+
15
+ ${c.bold('Usage')}
16
+ npx @lorekit/cli <command> [options]
17
+
18
+ ${c.bold('Commands')}
19
+ install Scaffold the lorekit-memory skill into .claude/skills and
20
+ add the LoreKit server to .mcp.json.
21
+ doctor Verify the skill install, MCP connectivity, token, and scope.
22
+ migrate Relocate a LoreKit-format local store into the current layout.
23
+ Dry-run by default; pass --yes to apply. Idempotent.
24
+ hook Hook engine for Claude Code / Cursor / Codex. Reads the host's
25
+ JSON on stdin and injects lessons or a retrospective nudge.
26
+ Not run by hand — wired into a plugin's hook config.
27
+ mcp Local stdio MCP server. Exposes the memory.* tools backed by the
28
+ resolved store (local .lorekit/ offline, or remote passthrough) so
29
+ .mcp.json can point at the CLI instead of mcp-remote. Speaks
30
+ JSON-RPC on stdin/stdout — not run by hand.
31
+
32
+ ${c.bold('Options')}
33
+ -d, --dir <path> Target project root (default: current directory)
34
+ -e, --endpoint <url> LoreKit MCP endpoint
35
+ -t, --token <token> LoreKit token (lk_rw_* to allow writes, lk_ro_* read-only)
36
+ --mode <mode> Memory mode: off | local | remote (doctor override)
37
+ --store <path> Local project-tier store directory (default: .lorekit)
38
+ --from <path> Source store to migrate from (migrate)
39
+ --to <tier> Migration destination tier: home | project (migrate;
40
+ default routes each entry by scope)
41
+ --apply Apply the migration (alias of --yes) (migrate)
42
+ -y, --yes Non-interactive / apply; never prompt
43
+ --force Overwrite existing skill files (install)
44
+ --deep Do a write→read→delete round-trip (doctor)
45
+ --adapter <name> Host framework for hook: claude | cursor | codex
46
+ --event <name> Host hook event (else read from stdin payload)
47
+ -h, --help Show this help
48
+ -v, --version Print the version
49
+
50
+ ${c.bold('Environment')}
51
+ LOREKIT_MODE off | local | remote (select a mode)
52
+ LOREKIT_DENY comma list of forbidden modes (deny-wins)
53
+ LOREKIT_HOME home-tier root + config dir (default ~/.lorekit)
54
+ LOREKIT_STORE project-tier store directory (default .lorekit)
55
+ LOREKIT_MCP_URL / LOREKIT_ENDPOINT endpoint fallback
56
+ LOREKIT_TOKEN token fallback
57
+ NO_COLOR disable colored output
58
+
59
+ ${c.bold('Examples')}
60
+ npx @lorekit/cli install --endpoint https://ref.supabase.co/functions/v1/mcp --token lk_rw_xxx
61
+ npx @lorekit/cli doctor --deep
62
+ npx @lorekit/cli migrate --from .lore # preview a rename
63
+ npx @lorekit/cli migrate --from .lore --to project --yes
64
+ `;
65
+
66
+ async function main() {
67
+ const argv = process.argv.slice(2);
68
+ const args = parseArgs(argv, {
69
+ aliases: { d: 'dir', e: 'endpoint', t: 'token', y: 'yes', h: 'help', v: 'version' },
70
+ booleans: ['yes', 'force', 'deep', 'apply', 'help', 'version'],
71
+ });
72
+
73
+ // `hook` is machine-facing: it must never print help/errors to stdout
74
+ // (that would corrupt the JSON the host parses). Handle it before the
75
+ // help/usage branch and always resolve to exit 0.
76
+ if (args._[0] === 'hook') {
77
+ return hook(args);
78
+ }
79
+
80
+ // `mcp` is machine-facing too: only JSON-RPC frames may reach stdout, so it
81
+ // must bypass the help/usage branch. It serves stdio until the client closes.
82
+ if (args._[0] === 'mcp') {
83
+ return mcpServer(args);
84
+ }
85
+
86
+ if (args.version) {
87
+ log(VERSION);
88
+ return 0;
89
+ }
90
+
91
+ const command = args._[0];
92
+
93
+ if (args.help || !command) {
94
+ log(HELP);
95
+ return command ? 0 : args.help ? 0 : 1;
96
+ }
97
+
98
+ switch (command) {
99
+ case 'install':
100
+ return install(args);
101
+ case 'doctor':
102
+ return doctor(args);
103
+ case 'migrate':
104
+ return migrate(args);
105
+ default:
106
+ err(`${c.red('Unknown command:')} ${command}\n`);
107
+ log(HELP);
108
+ return 1;
109
+ }
110
+ }
111
+
112
+ main()
113
+ .then((code) => process.exit(code ?? 0))
114
+ .catch((e) => {
115
+ err(`${c.red('Error:')} ${e && e.stack ? e.stack : e}`);
116
+ process.exit(1);
117
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@lorekit/cli",
3
+ "version": "1.0.0",
4
+ "description": "Install the LoreKit shared-memory skill and run health checks for the LoreKit MCP server.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "lorekit": "bin/lorekit.mjs"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "src",
13
+ "skill",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "keywords": [
23
+ "lorekit",
24
+ "mcp",
25
+ "claude",
26
+ "agent",
27
+ "memory",
28
+ "skill",
29
+ "cli"
30
+ ],
31
+ "scripts": {
32
+ "start": "node bin/lorekit.mjs",
33
+ "doctor": "node bin/lorekit.mjs doctor",
34
+ "test": "node --test test/*.test.mjs"
35
+ }
36
+ }
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: lorekit-memory
3
+ description: >
4
+ Shared persistent memory for coding agents, backed by LoreKit's MCP server.
5
+ Reads scoped lessons at the start of a task or when navigating into
6
+ unfamiliar code (narrow-to-broad scope resolution across branch, repo,
7
+ project, and global), and writes a lesson when something goes wrong — a
8
+ stuck loop, a repeated command failure, a surprising gotcha, a near-miss,
9
+ or a wrong assumption that cost time. Lessons are phrased as observations
10
+ (never rigid rules), scoped to the narrowest namespace that fits, and
11
+ deduplicated on write. Use at task start, before risky operations, and
12
+ after any failure or retrospective. Triggers on "read lessons", "check
13
+ memory", "what do we know about", "remember this", "save a lesson",
14
+ "record this gotcha", "capture this lesson", "/lorekit-memory".
15
+ user-invocable: true
16
+ argument-hint: '[read|write] [scope-hint or lesson]'
17
+ license: MIT
18
+ metadata:
19
+ author: mthines
20
+ version: '1.0.0'
21
+ workflow_type: shared-memory-intake-and-retrospective
22
+ tags:
23
+ - lorekit
24
+ - persistent-memory
25
+ - shared-memory
26
+ - mcp
27
+ - lessons
28
+ - self-improvement
29
+ - retrospective
30
+ ---
31
+
32
+ # LoreKit Memory
33
+
34
+ Give every agent a shared, persistent memory.
35
+ Lessons live in LoreKit (a Supabase-backed MCP server), so what one agent
36
+ learns on one machine — or in CI — is available to every agent, everywhere,
37
+ in the next session.
38
+
39
+ This skill has two jobs, mirroring the read-on-start / write-on-failure loop
40
+ of the `aw` autonomous-workflow agent:
41
+
42
+ 1. **Read** scoped lessons at the start of a task and before risky steps.
43
+ 2. **Write** a lesson when something goes wrong, so the next run avoids it.
44
+
45
+ Both jobs run through LoreKit's `memory.*` MCP tools.
46
+ If those tools are not connected, this skill is a no-op — say so once and
47
+ continue the task; never block work because memory is unavailable.
48
+
49
+ > **Modes.** Memory has a controllable backend (`lorekit doctor` shows the
50
+ > resolved one): `remote` (the hosted LoreKit server — the default), `local`
51
+ > (markdown files in two tiers — a per-user **home** tier at `~/.lorekit/` and
52
+ > an opt-in per-repo **project** tier at `<repo>/.lorekit/`), or `off`
53
+ > (disabled). **`local` means _not_ on the hosted website** — local lessons stay
54
+ > on disk and never sync to the LoreKit dashboard. `global` lessons go to home;
55
+ > `repo`/`branch` lessons go to the project tier once you create `<repo>/.lorekit/`
56
+ > (commit it to share with your team, or gitignore it to keep it private), else
57
+ > home. See the `@lorekit/cli` README for the control model, the two-tier merge,
58
+ > and precedence/deny rules.
59
+
60
+ ---
61
+
62
+ ## When to read (intake)
63
+
64
+ Read at the moments where prior lessons change what you do:
65
+
66
+ - **Task start** — before planning any non-trivial change.
67
+ - **Navigation** — the first time you open an unfamiliar package, module, or subsystem.
68
+ - **Before a risky operation** — migrations, deploys, worktree/branch surgery, force pushes, anything hard to reverse.
69
+
70
+ Follow [rules/intake.md](./rules/intake.md).
71
+ The short version: resolve the current scope, list lessons narrow-to-broad,
72
+ and treat matches as *considerations*, not commands.
73
+
74
+ ## When to write (retrospective)
75
+
76
+ Write when a run produces a durable observation worth carrying forward.
77
+ The trigger is friction, not success. Ask the 30-second question:
78
+
79
+ > Was there a stuck loop, a repeated failure, a surprise, a near-miss, or a
80
+ > guess that paid off — something a future run would benefit from knowing?
81
+
82
+ If yes, write it. If the answer is genuinely nothing, write nothing —
83
+ empty retrospectives are skipped entirely.
84
+
85
+ Follow [rules/retrospective.md](./rules/retrospective.md).
86
+ The short version: phrase the lesson as an observation, pick the narrowest
87
+ scope that fits, check for a near-duplicate first, then `memory.write`.
88
+
89
+ ---
90
+
91
+ ## Scope in one line
92
+
93
+ Lessons are partitioned by a canonical scope string (`::` is the only separator):
94
+
95
+ ```text
96
+ global universal principles
97
+ project::{name} monorepo-wide
98
+ repo::{owner}/{repo} this repository's codebase
99
+ branch::{owner}/{repo}::{branch} short-lived, this branch only
100
+ ```
101
+
102
+ Read narrow-to-broad and merge; write to the narrowest scope that correctly
103
+ describes where the lesson applies.
104
+ Full resolution rules: [references/scope-resolution.md](./references/scope-resolution.md).
105
+
106
+ ---
107
+
108
+ ## The MCP tools
109
+
110
+ | Tool | Use | Token |
111
+ |------|-----|-------|
112
+ | `memory.list` | List lessons for one scope (newest first, tag filter) | read |
113
+ | `memory.search` | Full-text search across scopes (supports `repo::owner/*`) | read |
114
+ | `memory.read` | Read one lesson by scope + key | read |
115
+ | `memory.write` | Store or update a lesson (same scope+key updates in place) | read+write |
116
+
117
+ Write tools need an `lk_rw_*` token; read tools accept `lk_rw_*` or `lk_ro_*`.
118
+ A read-only token cannot write — if a write fails with an authorization error,
119
+ report it and move on; do not retry.
120
+
121
+ Every lesson this skill writes carries the tag `skill::lorekit-memory` plus a
122
+ `source::<trigger>` tag (for example `source::stuck-loop`) so lessons are
123
+ easy to find and audit later.
124
+
125
+ ---
126
+
127
+ ## Setup
128
+
129
+ Install the skill and configure the MCP endpoint with the LoreKit CLI:
130
+
131
+ ```bash
132
+ npx @lorekit/cli install
133
+ npx @lorekit/cli doctor
134
+ ```
135
+
136
+ `install` scaffolds this skill into `.claude/skills/` and adds the LoreKit
137
+ server to `.mcp.json`.
138
+ `doctor` verifies connectivity, token permission, and scope detection.
139
+ See the CLI's own README for flags and troubleshooting.
@@ -0,0 +1,65 @@
1
+ # Scope resolution
2
+
3
+ A scope string names the namespace a lesson belongs to.
4
+ `::` is the **only** valid separator — a single `:` returns a 400 error.
5
+ All segments are lowercased by the server.
6
+
7
+ ## The four scope types
8
+
9
+ | Type | Format | Example |
10
+ |------|--------|---------|
11
+ | Global | `global` | `global` |
12
+ | Project (monorepo) | `project::{name}` | `project::agent-skills` |
13
+ | Repository | `repo::{owner}/{repo}` | `repo::mthines/lorekit` |
14
+ | Branch | `branch::{owner}/{repo}::{branch}` | `branch::mthines/lorekit::feat/x` |
15
+
16
+ ## Deriving scope from the working directory
17
+
18
+ 1. Read the `origin` remote URL and normalize it to `owner/repo`:
19
+ - `git@github.com:mthines/lorekit.git` → `mthines/lorekit`
20
+ - `https://github.com/mthines/lorekit.git` → `mthines/lorekit`
21
+ - strip a trailing `.git`, lowercase the result.
22
+ 2. Read the current branch: `git rev-parse --abbrev-ref HEAD`.
23
+ 3. Compose:
24
+ - `repo::mthines/lorekit`
25
+ - `branch::mthines/lorekit::{branch}`
26
+ 4. If there is no git remote, there is no repo/branch scope — use `global`,
27
+ and optionally `project::{basename-of-repo-root}` for a monorepo.
28
+
29
+ The LoreKit CLI's `doctor` command prints the scope it derives, which is a
30
+ quick way to confirm the agent will read and write to the right place.
31
+
32
+ ## Read order (intake)
33
+
34
+ Query most specific first, then merge; more specific scopes win on key collisions:
35
+
36
+ ```text
37
+ branch::{owner}/{repo}::{branch} → repo::{owner}/{repo} → project::{name} → global
38
+ ```
39
+
40
+ ## Write scope (retrospective)
41
+
42
+ Use the narrowest scope that correctly describes where the lesson applies:
43
+
44
+ - Branch-scoped lessons do not pollute the repo's lesson set.
45
+ - Repo-scoped lessons do not pollute global.
46
+ - Only truly universal lessons belong in `global`.
47
+
48
+ ## Wildcards (search only)
49
+
50
+ `memory.search` accepts an owner-level wildcard in `scopes`:
51
+
52
+ ```json
53
+ { "scopes": ["repo::mthines/*", "global"] }
54
+ ```
55
+
56
+ Wildcards work **only** in `memory.search` — not in `memory.list`,
57
+ `memory.read`, or `memory.write`.
58
+
59
+ ## Validation rules
60
+
61
+ 1. `::` is the only separator; a single `:` → 400.
62
+ 2. `repo::` must include a `/` (owner/repo); `repo::mthines` → 400.
63
+ 3. `branch::` must have exactly two `::` separators.
64
+ 4. Only `global`, `project`, `repo`, `branch` prefixes are valid.
65
+ 5. Segments are trimmed and lowercased on ingest.