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.
- hobkin_lore-0.2.0/LICENSE +21 -0
- hobkin_lore-0.2.0/PKG-INFO +266 -0
- hobkin_lore-0.2.0/README.md +243 -0
- hobkin_lore-0.2.0/docs/RUNBOOK.md +217 -0
- hobkin_lore-0.2.0/hobkin_lore.py +2347 -0
- hobkin_lore-0.2.0/packs/jq/cheatsheet.md +10 -0
- hobkin_lore-0.2.0/packs/jq/examples.jsonl +12 -0
- hobkin_lore-0.2.0/packs/jq/pack.json +9 -0
- hobkin_lore-0.2.0/packs/jq/sections.jsonl +9 -0
- hobkin_lore-0.2.0/packs/jq/skill.md +3 -0
- hobkin_lore-0.2.0/packs/jq/symbols.jsonl +40 -0
- hobkin_lore-0.2.0/packs/jq/tools.jsonl +1 -0
- hobkin_lore-0.2.0/pyproject.toml +37 -0
- hobkin_lore-0.2.0/tests/fake_model.py +75 -0
- hobkin_lore-0.2.0/tests/fakeps.py +38 -0
- hobkin_lore-0.2.0/tests/test_build.py +215 -0
- hobkin_lore-0.2.0/tests/test_hobkin_lore.py +248 -0
- hobkin_lore-0.2.0/tests/test_workspace.py +220 -0
|
@@ -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.
|