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