lorewell 0.3.0__tar.gz

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.
lorewell-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Frank Lynam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,285 @@
1
+ Metadata-Version: 2.4
2
+ Name: lorewell
3
+ Version: 0.3.0
4
+ Summary: Reference packs for languages and their tools, that an agent looks things up in instead of holding them in context: one SQLite file each, installed as a skill any skill-reading agent can use (designed for hobkin; Claude Code reads it as it is).
5
+ Keywords: agent,llm,ai,skills,reference,documentation,rag,sqlite,mcp,hobkin,claude-code
6
+ Author: Frank Lynam
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: POSIX
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Topic :: Software Development :: Documentation
17
+ Classifier: Topic :: Utilities
18
+ License-File: LICENSE
19
+ Project-URL: Homepage, https://github.com/frank-lynam/lorewell
20
+ Project-URL: Issues, https://github.com/frank-lynam/lorewell/issues
21
+ Project-URL: hobkin, https://github.com/frank-lynam/hobkin
22
+
23
+ # lorewell
24
+
25
+ Reference packs ("lores") for programming languages and their tools, that an AI agent looks things up in
26
+ instead of holding them in context. A whole language reference, every function, class and keyword, worked
27
+ examples, and the documentation of its command-line tools: far more than fits in a context window, searchable
28
+ in a few hundred tokens a lookup.
29
+
30
+ A lore is one SQLite file. Installed, it's a standard skill folder that any agent which reads skills can use.
31
+ It's designed for [hobkin](https://github.com/frank-lynam/hobkin), and
32
+ [Claude Code](https://code.claude.com) reads it as it is. Other tools reach it as an MCP server (below):
33
+
34
+ ```text
35
+ jq-lore/
36
+ SKILL.md when to use it, and how (name and description in front matter)
37
+ lore.py the lookup tool: one stdlib-only Python file
38
+ jq.lore.db the lore
39
+ jq.notes.db what agents learned using it (made on the first note; kept across reinstalls)
40
+ ```
41
+
42
+ One file, standard library only, Python 3.9+. No services, no embeddings, no network at lookup time.
43
+
44
+ **Setting it up at a site?** Follow [docs/RUNBOOK.md](docs/RUNBOOK.md): a step-by-step procedure (for a person
45
+ or an agent) from documentation and command-line tools to installed skills and a working workspace.
46
+
47
+ ## Use a lore
48
+
49
+ ```sh
50
+ pip install lorewell
51
+ lorewell compile packs/jq # a pack source (below) into jq.lore.db
52
+ lorewell install jq.lore.db # for the agent that's here: hobkin's shared skills, else Claude Code's
53
+ lorewell install jq.lore.db --hobkin # for every hobkin (~/.hobkins/_shared/skills/)
54
+ lorewell install jq.lore.db --claude # for Claude Code (~/.claude/skills/)
55
+ lorewell install jq.lore.db --project . # for one project's Claude Code (.claude/skills/)
56
+ lorewell install jq.lore.db --to DIR # any skills folder
57
+ ```
58
+
59
+ For VS Code's Copilot, Cursor or Claude Desktop, see [MCP](#in-vs-code-cursor-claude-desktop-and-other-mcp-clients) below.
60
+
61
+ The agent sees the skill's one-line description in its list of skills and opens it when a task touches the
62
+ language. From then on it looks things up:
63
+
64
+ ```text
65
+ $ python3 jq-lore/lore.py symbol map
66
+ map (function)
67
+ map(f)
68
+ Runs f on every element of an array and collects the results: same as `[.[] | f]`.
69
+ On an object it maps the values and returns an array; use `map_values` to keep an object.
70
+ see: map_values, select
71
+ examples: map-select (Keep some elements), group-count (Count per group) -> examples --symbol map
72
+ ```
73
+
74
+ | Command | What it gives |
75
+ | --- | --- |
76
+ | `info` | What the lore covers, its counts, where it came from |
77
+ | `cheatsheet` | The short page an agent reads once per task |
78
+ | `toc [ID]` | The outline, or one branch of it |
79
+ | `search WORDS [--kind K]` | Ranked hits with snippets (sections, symbols, tools, examples, notes); a word that's exactly a symbol's name puts that symbol first |
80
+ | `symbol NAME` | One function, class, keyword, operator...: signature, behaviour, see-also, examples, notes; a near miss suggests the right name |
81
+ | `show ID [--from N]` | A section, 80 lines at a time |
82
+ | `examples [WORDS] [--symbol NAME] [--id ID]` | Worked examples, each marked whether the language's own tool ran it as shown |
83
+ | `tools`, `tool NAME` | The language's command-line tools: purpose, usage, flags, real runs |
84
+ | `note TEXT [--on NAME]` | Keep something learned the hard way; it shows on that entry and in search from then on |
85
+ | `notes [WORDS]` | What's been noted |
86
+
87
+ Every output is short and paged: a lookup can't flood an agent's context. Outside a skill folder, point it at a
88
+ lore with `--lore FILE` or `LORE=FILE`.
89
+
90
+ ## In VS Code, Cursor, Claude Desktop and other MCP clients
91
+
92
+ Claude Code (in the terminal or in VS Code) and hobkin use the skill folder directly. For any other AI tool
93
+ that speaks [MCP](https://modelcontextprotocol.io), a lore is also an MCP server over stdio: the same lookups
94
+ as tools (`cheatsheet`, `search`, `symbol`, `toc`, `show`, `examples`, `tools`, `tool`, `note`, `info`). An
95
+ installed skill's own `lore.py` serves it, so the editor needs nothing installed:
96
+
97
+ ```sh
98
+ python3 ~/.claude/skills/jq-lore/lore.py mcp # its own lore
99
+ lorewell mcp jq.lore.db peoplecode.lore.db # several: each tool then takes `lore`
100
+ ```
101
+
102
+ VS Code (`.vscode/mcp.json` in a workspace, or **MCP: Open User Configuration**):
103
+
104
+ ```json
105
+ {
106
+ "servers": {
107
+ "jq-lore": {"type": "stdio", "command": "python3", "args": ["/home/you/.claude/skills/jq-lore/lore.py", "mcp"]}
108
+ }
109
+ }
110
+ ```
111
+
112
+ Cursor (`.cursor/mcp.json`) and Claude Desktop (`claude_desktop_config.json`) take the same command under
113
+ `mcpServers`:
114
+
115
+ ```json
116
+ {
117
+ "mcpServers": {
118
+ "jq-lore": {"command": "python3", "args": ["/home/you/.claude/skills/jq-lore/lore.py", "mcp"]}
119
+ }
120
+ }
121
+ ```
122
+
123
+ The server tells the model how to use it (read the cheatsheet once, look up what it isn't sure of), and stdout
124
+ carries nothing but the protocol. Notes an agent makes through MCP land in the same notes file as everyone
125
+ else's.
126
+
127
+ ## Make a lore: the pack source
128
+
129
+ A lore is compiled from a pack source: a folder of plain files you can read, review and keep in git.
130
+ [`packs/jq`](packs/jq) is a small hand-written one.
131
+
132
+ | File | What it holds |
133
+ | --- | --- |
134
+ | `pack.json` | `name` (it names the skill: `NAME-lore`), `title`, `description`, `language`, `version`, `sources` (where it came from), `check` (below) |
135
+ | `sections.jsonl` | The reference's text, one section per line: `id`, `parent`, `title`, `body` (Markdown), `source` |
136
+ | `symbols.jsonl` | One per function, class, keyword...: `name`, `kind` (function, method, class, property, event, keyword, operator, statement, type, variable, constant, directive, builtin, other), `signature`, `summary`, `body`, `section`, `source`, `see` |
137
+ | `examples.jsonl` | `id`, `title`, `code`, `input` (stdin), `expect` (its output), `notes`, `symbols` (which it shows) |
138
+ | `tools.jsonl` | One per command-line tool: `name`, `purpose`, `usage`, `body`, `flags` (`flag`, `meaning`), `runs` (`command`, `output`) |
139
+ | `cheatsheet.md` | The essentials: a screenful at most |
140
+ | `skill.md` | How to work in this language (testing, conventions), added to the skill's instructions |
141
+ | `files/` | Helper files that ship with the lore (a check harness, test fixtures, small scripts for its tools): stored inside the `.lore.db`, written to the skill folder's `files/` on install, listed in its `SKILL.md` |
142
+
143
+ `source` on every section and symbol says where in the original documentation it came from, so a reviewer can
144
+ check it.
145
+
146
+ **Packs that come with it:** [`packs/jq`](packs/jq), a small sample (a language and its tool), and
147
+ [`packs/peoplesoft-pia`](packs/peoplesoft-pia): testing PeopleSoft pages (PIA) with Playwright for Python. That
148
+ one is a hand-written starter from general PeopleSoft knowledge (the TargetContent frame, `/psp/` and `/psc/`
149
+ URLs, `RECORD_FIELD$row` ids, waiting out server round trips, toolbar and message-box ids that start with `#`),
150
+ whose examples run against a small imitation of PIA's structure in its `files/` (they pass where Playwright and
151
+ Chromium are installed, and are unchecked elsewhere). Ids and behaviour vary by PeopleTools version: verify on
152
+ your pages, note what differs, and extend it with your team's own test scripts (`lorewell build` into the same
153
+ folder keeps everything written by hand).
154
+
155
+ **Checked examples.** With `check` in `pack.json`, `compile` runs every example through the language's own
156
+ tool and records whether it ran as shown: `{"command": "jq -c -f {file}", "ext": ".jq"}` writes the example's
157
+ code to `{file}`, feeds its `input` on stdin, and compares the output with `expect`. A failing example fails the
158
+ compile (exit 1) and is marked as failed in the lore, so an agent knows not to trust it. `--no-check` skips
159
+ this, for instance where the tool isn't installed.
160
+
161
+ **A harness for checks.** When examples need more than a command to run (a browser, a server, a fixture), put
162
+ the harness in `files/` and point `check` at it with `{pack}`, the pack source folder: `{"command": "python3
163
+ {pack}/files/run_snippet.py {file}", "ext": ".py", "compare": "words", "timeout": 60}`. A harness that can't
164
+ run where it is (no browser installed, say) exits with code 77: the example is recorded as unchecked, not
165
+ failed, so a lore still compiles on a machine without it.
166
+
167
+ **One schema at a time.** A lore records its schema version, and `lore.py` refuses a lore of another version
168
+ with "rebuild it": lores are built artifacts, rebuilt from their source, never converted.
169
+
170
+ ## Build a lore from any documentation
171
+
172
+ Hand-writing a pack is fine for a small language; a real one (PeopleCode, a vendor's DSL) has thousands of
173
+ pages. `lorewell build` has a model write the pack source from the documentation itself:
174
+
175
+ ```sh
176
+ export LORE_API_KEY=... # any OpenAI-format endpoint: OpenRouter, a gateway, Ollama, vLLM...
177
+ lorewell build jq https://jqlang.org/manual/v1.7/ --tool jq --version 1.7 \
178
+ --check 'jq -c -f {file}' --check-ext .jq --check-compare json \
179
+ --base-url https://openrouter.ai/api/v1 --model nvidia/nemotron-3-ultra-550b-a55b
180
+ lorewell review packs/jq # check what it made, in the browser
181
+ lorewell compile packs/jq # then compile and install as usual
182
+ ```
183
+
184
+ 1. **Gather.** PDFs (with `pdftotext`), HTML (files, folders or URLs; navigation and scripts dropped,
185
+ headings and code kept), Markdown and text, scripts that use the tools (`.sh`, `.dms`, `.sql`, `.ps1`,
186
+ `.bat`, logs and captured terminal sessions: how a team really uses them), and for each `--tool`, its help
187
+ and man page. Each becomes a numbered text source in `packs/NAME/.build/sources/`.
188
+
189
+ **Tools nobody outside your network has seen.** `--tool` tries the help flags of every era (`--help`, `-h`,
190
+ `-help`, `-H`, `-?`, `help`, `/?`), then has the model read that help and propose the help pages of its
191
+ subcommands, two levels down (`--help-limit`, default 60 pages). Plain code runs a proposal only if it asks
192
+ for help (subcommand words, then a help flag, or `help WORD`), so a build never lists, exports, connects or
193
+ changes anything, whatever the model suggests. Tools always run with no stdin, so one waiting for a login
194
+ just gets end-of-file.
195
+ 2. **Chunk** at headings into pieces a model reads whole.
196
+ 3. **Distill.** Each chunk goes to the model, which writes sections, symbols, examples and tools in its own
197
+ words, each with a short quote from the passage it rests on (`--jobs` at a time).
198
+ 4. **Verify,** in plain code: each quote is looked up in its chunk (whitespace, case, Markdown marks and a
199
+ shortening "..." aside). Found, it becomes a citation (`manual, lines 1843-1844`); not found, the entry is
200
+ flagged for review. Duplicates (a manual and its man page) are merged, and an example is tied only to the
201
+ symbols its code really uses.
202
+ 5. **Finish.** The model writes the description, the cheatsheet and the skill text from the merged entries.
203
+
204
+ Every model answer is recorded in `.build/answers/`, keyed by the prompt and model: a rebuild asks again only
205
+ about what changed. What people did outlives a rebuild: entries written by hand, and entries accepted, edited or
206
+ rejected in review, stay as they are, and a hand-edited cheatsheet or skill text is kept. So a build can also
207
+ extend a hand-written pack. `--limit N` tries a build on the first
208
+ N chunks. With `--check`, `compile` runs the examples; `--check-compare json` compares their output as JSON
209
+ values (spacing aside), `words` ignoring spacing, `exact` (the default) as text.
210
+
211
+ For a sense of scale: built from jq's online manual and its man page, with Nemotron 3 Ultra, it made 236
212
+ sections, 178 symbols and 280 examples (252 of them run as documented) in 23 chunks, for under a dollar, with
213
+ 48 entries flagged for review. `packs/jq` is the hand-written comparison: 40 symbols, 12 examples.
214
+
215
+ ## Review
216
+
217
+ `lorewell review packs/NAME` serves a page on `127.0.0.1:8765` (local only, and changes need a token
218
+ embedded in the page). Each entry is shown beside the source lines it cites, its quote highlighted; flagged
219
+ and unreviewed entries come first. Accept, reject, or edit and save: the pack source's JSONL is updated in
220
+ place, and `compile` leaves rejected entries out. Examples can be run through the pack's check command, and
221
+ the cheatsheet and skill text edited, from the same page.
222
+
223
+ ## Code that lives in a database: the workspace
224
+
225
+ Some languages keep their code in a database, not in files: PeopleCode, Oracle Forms and many vendor platforms
226
+ are edited through their own tools and saved straight into tables. When a lore's tools can list, pull and deploy
227
+ that code, the lore can carry **operations** too, and the same `lore.py` works the code from a git repo, so
228
+ developers (and Claude Code, in VS Code) work in files with history, review and branches, while the database
229
+ stays where the code runs.
230
+
231
+ **The operations** are in the pack source's `operations.json`: a command template for each thing the tools can
232
+ do, with `{placeholders}`.
233
+
234
+ ```json
235
+ {"variables": {"db": "the database to work in"},
236
+ "secrets": {"password": "the database password"},
237
+ "layout": {"ext": {"PeopleCode": ".pcode", "SQL": ".sql", "*": ".txt"}},
238
+ "operations": {
239
+ "list": {"command": "pscli list -db {db} -type {type}", "parse": "^(?P<type>\\S+) (?P<name>\\S+)$"},
240
+ "pull": {"command": "pscli export -db {db} -type {type} -name {name} -out {file}"},
241
+ "deploy": {"command": "pscli import -db {db} -type {type} -name {name} -in {file} -pw {password}"},
242
+ "validate": {"command": "pscli validate -in {file}"}}}
243
+ ```
244
+
245
+ Operations: `list` and `project_list` (output parsed line by line with `parse`), `pull`, `deploy`, `validate`,
246
+ `compile`, `build_project`, `export_project`. `build` drafts them from the tool documentation and scripts it
247
+ learned, flagged for review; the review page edits them (and refuses one that doesn't hold together); `compile`
248
+ checks them again. Every command is one program and its words, no shell: it's split into words before values
249
+ are filled in, so a value is always exactly one word.
250
+
251
+ **A workspace** is a repo set up for a lore with operations:
252
+
253
+ ```sh
254
+ cd my-peoplesoft-repo
255
+ python3 ~/.claude/skills/pscli-lore/lore.py ws init # config, .gitignore lines, VS Code tasks
256
+ # fill in .lore-workspace/config.json's variables (db...); export LORE_PASSWORD=... (secrets: env only)
257
+ lore.py ws test # list, pull one object, validate it: nothing changes
258
+ lore.py ws pull --project HR_FIX # into src/TYPE/NAME.ext
259
+ lore.py ws status --remote # changed here, in the database, or both
260
+ lore.py ws validate src/PeopleCode/JOB.EMPLID.FieldChange.pcode
261
+ lore.py ws deploy src/PeopleCode/JOB.EMPLID.FieldChange.pcode --compile
262
+ ```
263
+
264
+ - **A pull never loses work:** a file changed here since its last pull is left alone (`--force` to overwrite).
265
+ - **A deploy never overwrites someone else's:** it compares the database's copy with what you pulled first.
266
+ If someone deployed over it since, it stops and saves the base, theirs and yours side by side, with the
267
+ command to open a three-way merge in VS Code (`code --merge ...`). Merge by hand, then `deploy --force`.
268
+ - **Secrets** come only from environment variables (the config names them), and are masked in every message.
269
+ - **Site differences** go in the config's `operations`, which override the lore's without a rebuild.
270
+ - **The skill knows:** a lore with operations adds a section to its `SKILL.md` saying how to pull, validate and
271
+ deploy, and to bring a conflict to a person rather than force it.
272
+
273
+ ## Tests
274
+
275
+ ```sh
276
+ python3 -m unittest discover -v tests
277
+ ```
278
+
279
+ ## Releasing
280
+
281
+ Releases go to PyPI from GitHub Actions with Trusted Publishing (no token stored), the same way as hobkin:
282
+ bump `__version__` in `lorewell.py`, commit and push, then publish a GitHub release tagged `vX.Y.Z` to match.
283
+ The workflow tests on Python 3.9, 3.12 and 3.14, checks the tag, builds and uploads. Running it by hand
284
+ (Actions → Publish → Run workflow) goes to TestPyPI instead.
285
+
@@ -0,0 +1,262 @@
1
+ # lorewell
2
+
3
+ Reference packs ("lores") for programming languages and their tools, that an AI agent looks things up in
4
+ instead of holding them in context. A whole language reference, every function, class and keyword, worked
5
+ examples, and the documentation of its command-line tools: far more than fits in a context window, searchable
6
+ in a few hundred tokens a lookup.
7
+
8
+ A lore is one SQLite file. Installed, it's a standard skill folder that any agent which reads skills can use.
9
+ It's designed for [hobkin](https://github.com/frank-lynam/hobkin), and
10
+ [Claude Code](https://code.claude.com) reads it as it is. Other tools reach it as an MCP server (below):
11
+
12
+ ```text
13
+ jq-lore/
14
+ SKILL.md when to use it, and how (name and description in front matter)
15
+ lore.py the lookup tool: one stdlib-only Python file
16
+ jq.lore.db the lore
17
+ jq.notes.db what agents learned using it (made on the first note; kept across reinstalls)
18
+ ```
19
+
20
+ One file, standard library only, Python 3.9+. No services, no embeddings, no network at lookup time.
21
+
22
+ **Setting it up at a site?** Follow [docs/RUNBOOK.md](docs/RUNBOOK.md): a step-by-step procedure (for a person
23
+ or an agent) from documentation and command-line tools to installed skills and a working workspace.
24
+
25
+ ## Use a lore
26
+
27
+ ```sh
28
+ pip install lorewell
29
+ lorewell compile packs/jq # a pack source (below) into jq.lore.db
30
+ lorewell install jq.lore.db # for the agent that's here: hobkin's shared skills, else Claude Code's
31
+ lorewell install jq.lore.db --hobkin # for every hobkin (~/.hobkins/_shared/skills/)
32
+ lorewell install jq.lore.db --claude # for Claude Code (~/.claude/skills/)
33
+ lorewell install jq.lore.db --project . # for one project's Claude Code (.claude/skills/)
34
+ lorewell install jq.lore.db --to DIR # any skills folder
35
+ ```
36
+
37
+ For VS Code's Copilot, Cursor or Claude Desktop, see [MCP](#in-vs-code-cursor-claude-desktop-and-other-mcp-clients) below.
38
+
39
+ The agent sees the skill's one-line description in its list of skills and opens it when a task touches the
40
+ language. From then on it looks things up:
41
+
42
+ ```text
43
+ $ python3 jq-lore/lore.py symbol map
44
+ map (function)
45
+ map(f)
46
+ Runs f on every element of an array and collects the results: same as `[.[] | f]`.
47
+ On an object it maps the values and returns an array; use `map_values` to keep an object.
48
+ see: map_values, select
49
+ examples: map-select (Keep some elements), group-count (Count per group) -> examples --symbol map
50
+ ```
51
+
52
+ | Command | What it gives |
53
+ | --- | --- |
54
+ | `info` | What the lore covers, its counts, where it came from |
55
+ | `cheatsheet` | The short page an agent reads once per task |
56
+ | `toc [ID]` | The outline, or one branch of it |
57
+ | `search WORDS [--kind K]` | Ranked hits with snippets (sections, symbols, tools, examples, notes); a word that's exactly a symbol's name puts that symbol first |
58
+ | `symbol NAME` | One function, class, keyword, operator...: signature, behaviour, see-also, examples, notes; a near miss suggests the right name |
59
+ | `show ID [--from N]` | A section, 80 lines at a time |
60
+ | `examples [WORDS] [--symbol NAME] [--id ID]` | Worked examples, each marked whether the language's own tool ran it as shown |
61
+ | `tools`, `tool NAME` | The language's command-line tools: purpose, usage, flags, real runs |
62
+ | `note TEXT [--on NAME]` | Keep something learned the hard way; it shows on that entry and in search from then on |
63
+ | `notes [WORDS]` | What's been noted |
64
+
65
+ Every output is short and paged: a lookup can't flood an agent's context. Outside a skill folder, point it at a
66
+ lore with `--lore FILE` or `LORE=FILE`.
67
+
68
+ ## In VS Code, Cursor, Claude Desktop and other MCP clients
69
+
70
+ Claude Code (in the terminal or in VS Code) and hobkin use the skill folder directly. For any other AI tool
71
+ that speaks [MCP](https://modelcontextprotocol.io), a lore is also an MCP server over stdio: the same lookups
72
+ as tools (`cheatsheet`, `search`, `symbol`, `toc`, `show`, `examples`, `tools`, `tool`, `note`, `info`). An
73
+ installed skill's own `lore.py` serves it, so the editor needs nothing installed:
74
+
75
+ ```sh
76
+ python3 ~/.claude/skills/jq-lore/lore.py mcp # its own lore
77
+ lorewell mcp jq.lore.db peoplecode.lore.db # several: each tool then takes `lore`
78
+ ```
79
+
80
+ VS Code (`.vscode/mcp.json` in a workspace, or **MCP: Open User Configuration**):
81
+
82
+ ```json
83
+ {
84
+ "servers": {
85
+ "jq-lore": {"type": "stdio", "command": "python3", "args": ["/home/you/.claude/skills/jq-lore/lore.py", "mcp"]}
86
+ }
87
+ }
88
+ ```
89
+
90
+ Cursor (`.cursor/mcp.json`) and Claude Desktop (`claude_desktop_config.json`) take the same command under
91
+ `mcpServers`:
92
+
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "jq-lore": {"command": "python3", "args": ["/home/you/.claude/skills/jq-lore/lore.py", "mcp"]}
97
+ }
98
+ }
99
+ ```
100
+
101
+ The server tells the model how to use it (read the cheatsheet once, look up what it isn't sure of), and stdout
102
+ carries nothing but the protocol. Notes an agent makes through MCP land in the same notes file as everyone
103
+ else's.
104
+
105
+ ## Make a lore: the pack source
106
+
107
+ A lore is compiled from a pack source: a folder of plain files you can read, review and keep in git.
108
+ [`packs/jq`](packs/jq) is a small hand-written one.
109
+
110
+ | File | What it holds |
111
+ | --- | --- |
112
+ | `pack.json` | `name` (it names the skill: `NAME-lore`), `title`, `description`, `language`, `version`, `sources` (where it came from), `check` (below) |
113
+ | `sections.jsonl` | The reference's text, one section per line: `id`, `parent`, `title`, `body` (Markdown), `source` |
114
+ | `symbols.jsonl` | One per function, class, keyword...: `name`, `kind` (function, method, class, property, event, keyword, operator, statement, type, variable, constant, directive, builtin, other), `signature`, `summary`, `body`, `section`, `source`, `see` |
115
+ | `examples.jsonl` | `id`, `title`, `code`, `input` (stdin), `expect` (its output), `notes`, `symbols` (which it shows) |
116
+ | `tools.jsonl` | One per command-line tool: `name`, `purpose`, `usage`, `body`, `flags` (`flag`, `meaning`), `runs` (`command`, `output`) |
117
+ | `cheatsheet.md` | The essentials: a screenful at most |
118
+ | `skill.md` | How to work in this language (testing, conventions), added to the skill's instructions |
119
+ | `files/` | Helper files that ship with the lore (a check harness, test fixtures, small scripts for its tools): stored inside the `.lore.db`, written to the skill folder's `files/` on install, listed in its `SKILL.md` |
120
+
121
+ `source` on every section and symbol says where in the original documentation it came from, so a reviewer can
122
+ check it.
123
+
124
+ **Packs that come with it:** [`packs/jq`](packs/jq), a small sample (a language and its tool), and
125
+ [`packs/peoplesoft-pia`](packs/peoplesoft-pia): testing PeopleSoft pages (PIA) with Playwright for Python. That
126
+ one is a hand-written starter from general PeopleSoft knowledge (the TargetContent frame, `/psp/` and `/psc/`
127
+ URLs, `RECORD_FIELD$row` ids, waiting out server round trips, toolbar and message-box ids that start with `#`),
128
+ whose examples run against a small imitation of PIA's structure in its `files/` (they pass where Playwright and
129
+ Chromium are installed, and are unchecked elsewhere). Ids and behaviour vary by PeopleTools version: verify on
130
+ your pages, note what differs, and extend it with your team's own test scripts (`lorewell build` into the same
131
+ folder keeps everything written by hand).
132
+
133
+ **Checked examples.** With `check` in `pack.json`, `compile` runs every example through the language's own
134
+ tool and records whether it ran as shown: `{"command": "jq -c -f {file}", "ext": ".jq"}` writes the example's
135
+ code to `{file}`, feeds its `input` on stdin, and compares the output with `expect`. A failing example fails the
136
+ compile (exit 1) and is marked as failed in the lore, so an agent knows not to trust it. `--no-check` skips
137
+ this, for instance where the tool isn't installed.
138
+
139
+ **A harness for checks.** When examples need more than a command to run (a browser, a server, a fixture), put
140
+ the harness in `files/` and point `check` at it with `{pack}`, the pack source folder: `{"command": "python3
141
+ {pack}/files/run_snippet.py {file}", "ext": ".py", "compare": "words", "timeout": 60}`. A harness that can't
142
+ run where it is (no browser installed, say) exits with code 77: the example is recorded as unchecked, not
143
+ failed, so a lore still compiles on a machine without it.
144
+
145
+ **One schema at a time.** A lore records its schema version, and `lore.py` refuses a lore of another version
146
+ with "rebuild it": lores are built artifacts, rebuilt from their source, never converted.
147
+
148
+ ## Build a lore from any documentation
149
+
150
+ Hand-writing a pack is fine for a small language; a real one (PeopleCode, a vendor's DSL) has thousands of
151
+ pages. `lorewell build` has a model write the pack source from the documentation itself:
152
+
153
+ ```sh
154
+ export LORE_API_KEY=... # any OpenAI-format endpoint: OpenRouter, a gateway, Ollama, vLLM...
155
+ lorewell build jq https://jqlang.org/manual/v1.7/ --tool jq --version 1.7 \
156
+ --check 'jq -c -f {file}' --check-ext .jq --check-compare json \
157
+ --base-url https://openrouter.ai/api/v1 --model nvidia/nemotron-3-ultra-550b-a55b
158
+ lorewell review packs/jq # check what it made, in the browser
159
+ lorewell compile packs/jq # then compile and install as usual
160
+ ```
161
+
162
+ 1. **Gather.** PDFs (with `pdftotext`), HTML (files, folders or URLs; navigation and scripts dropped,
163
+ headings and code kept), Markdown and text, scripts that use the tools (`.sh`, `.dms`, `.sql`, `.ps1`,
164
+ `.bat`, logs and captured terminal sessions: how a team really uses them), and for each `--tool`, its help
165
+ and man page. Each becomes a numbered text source in `packs/NAME/.build/sources/`.
166
+
167
+ **Tools nobody outside your network has seen.** `--tool` tries the help flags of every era (`--help`, `-h`,
168
+ `-help`, `-H`, `-?`, `help`, `/?`), then has the model read that help and propose the help pages of its
169
+ subcommands, two levels down (`--help-limit`, default 60 pages). Plain code runs a proposal only if it asks
170
+ for help (subcommand words, then a help flag, or `help WORD`), so a build never lists, exports, connects or
171
+ changes anything, whatever the model suggests. Tools always run with no stdin, so one waiting for a login
172
+ just gets end-of-file.
173
+ 2. **Chunk** at headings into pieces a model reads whole.
174
+ 3. **Distill.** Each chunk goes to the model, which writes sections, symbols, examples and tools in its own
175
+ words, each with a short quote from the passage it rests on (`--jobs` at a time).
176
+ 4. **Verify,** in plain code: each quote is looked up in its chunk (whitespace, case, Markdown marks and a
177
+ shortening "..." aside). Found, it becomes a citation (`manual, lines 1843-1844`); not found, the entry is
178
+ flagged for review. Duplicates (a manual and its man page) are merged, and an example is tied only to the
179
+ symbols its code really uses.
180
+ 5. **Finish.** The model writes the description, the cheatsheet and the skill text from the merged entries.
181
+
182
+ Every model answer is recorded in `.build/answers/`, keyed by the prompt and model: a rebuild asks again only
183
+ about what changed. What people did outlives a rebuild: entries written by hand, and entries accepted, edited or
184
+ rejected in review, stay as they are, and a hand-edited cheatsheet or skill text is kept. So a build can also
185
+ extend a hand-written pack. `--limit N` tries a build on the first
186
+ N chunks. With `--check`, `compile` runs the examples; `--check-compare json` compares their output as JSON
187
+ values (spacing aside), `words` ignoring spacing, `exact` (the default) as text.
188
+
189
+ For a sense of scale: built from jq's online manual and its man page, with Nemotron 3 Ultra, it made 236
190
+ sections, 178 symbols and 280 examples (252 of them run as documented) in 23 chunks, for under a dollar, with
191
+ 48 entries flagged for review. `packs/jq` is the hand-written comparison: 40 symbols, 12 examples.
192
+
193
+ ## Review
194
+
195
+ `lorewell review packs/NAME` serves a page on `127.0.0.1:8765` (local only, and changes need a token
196
+ embedded in the page). Each entry is shown beside the source lines it cites, its quote highlighted; flagged
197
+ and unreviewed entries come first. Accept, reject, or edit and save: the pack source's JSONL is updated in
198
+ place, and `compile` leaves rejected entries out. Examples can be run through the pack's check command, and
199
+ the cheatsheet and skill text edited, from the same page.
200
+
201
+ ## Code that lives in a database: the workspace
202
+
203
+ Some languages keep their code in a database, not in files: PeopleCode, Oracle Forms and many vendor platforms
204
+ are edited through their own tools and saved straight into tables. When a lore's tools can list, pull and deploy
205
+ that code, the lore can carry **operations** too, and the same `lore.py` works the code from a git repo, so
206
+ developers (and Claude Code, in VS Code) work in files with history, review and branches, while the database
207
+ stays where the code runs.
208
+
209
+ **The operations** are in the pack source's `operations.json`: a command template for each thing the tools can
210
+ do, with `{placeholders}`.
211
+
212
+ ```json
213
+ {"variables": {"db": "the database to work in"},
214
+ "secrets": {"password": "the database password"},
215
+ "layout": {"ext": {"PeopleCode": ".pcode", "SQL": ".sql", "*": ".txt"}},
216
+ "operations": {
217
+ "list": {"command": "pscli list -db {db} -type {type}", "parse": "^(?P<type>\\S+) (?P<name>\\S+)$"},
218
+ "pull": {"command": "pscli export -db {db} -type {type} -name {name} -out {file}"},
219
+ "deploy": {"command": "pscli import -db {db} -type {type} -name {name} -in {file} -pw {password}"},
220
+ "validate": {"command": "pscli validate -in {file}"}}}
221
+ ```
222
+
223
+ Operations: `list` and `project_list` (output parsed line by line with `parse`), `pull`, `deploy`, `validate`,
224
+ `compile`, `build_project`, `export_project`. `build` drafts them from the tool documentation and scripts it
225
+ learned, flagged for review; the review page edits them (and refuses one that doesn't hold together); `compile`
226
+ checks them again. Every command is one program and its words, no shell: it's split into words before values
227
+ are filled in, so a value is always exactly one word.
228
+
229
+ **A workspace** is a repo set up for a lore with operations:
230
+
231
+ ```sh
232
+ cd my-peoplesoft-repo
233
+ python3 ~/.claude/skills/pscli-lore/lore.py ws init # config, .gitignore lines, VS Code tasks
234
+ # fill in .lore-workspace/config.json's variables (db...); export LORE_PASSWORD=... (secrets: env only)
235
+ lore.py ws test # list, pull one object, validate it: nothing changes
236
+ lore.py ws pull --project HR_FIX # into src/TYPE/NAME.ext
237
+ lore.py ws status --remote # changed here, in the database, or both
238
+ lore.py ws validate src/PeopleCode/JOB.EMPLID.FieldChange.pcode
239
+ lore.py ws deploy src/PeopleCode/JOB.EMPLID.FieldChange.pcode --compile
240
+ ```
241
+
242
+ - **A pull never loses work:** a file changed here since its last pull is left alone (`--force` to overwrite).
243
+ - **A deploy never overwrites someone else's:** it compares the database's copy with what you pulled first.
244
+ If someone deployed over it since, it stops and saves the base, theirs and yours side by side, with the
245
+ command to open a three-way merge in VS Code (`code --merge ...`). Merge by hand, then `deploy --force`.
246
+ - **Secrets** come only from environment variables (the config names them), and are masked in every message.
247
+ - **Site differences** go in the config's `operations`, which override the lore's without a rebuild.
248
+ - **The skill knows:** a lore with operations adds a section to its `SKILL.md` saying how to pull, validate and
249
+ deploy, and to bring a conflict to a person rather than force it.
250
+
251
+ ## Tests
252
+
253
+ ```sh
254
+ python3 -m unittest discover -v tests
255
+ ```
256
+
257
+ ## Releasing
258
+
259
+ Releases go to PyPI from GitHub Actions with Trusted Publishing (no token stored), the same way as hobkin:
260
+ bump `__version__` in `lorewell.py`, commit and push, then publish a GitHub release tagged `vX.Y.Z` to match.
261
+ The workflow tests on Python 3.9, 3.12 and 3.14, checks the tag, builds and uploads. Running it by hand
262
+ (Actions → Publish → Run workflow) goes to TestPyPI instead.