recaller 0.1.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,17 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .venv/
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+
10
+ # Never commit a real data jar or audit log to this public repo
11
+ data.jsonl
12
+ config.json
13
+ data.yaml
14
+ facts.yaml
15
+ config.yaml
16
+ audit.log
17
+ !examples/data.jsonl
recaller-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 YY Ahn
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,344 @@
1
+ Metadata-Version: 2.4
2
+ Name: recaller
3
+ Version: 0.1.0
4
+ Summary: A grep-friendly CLI data jar for URLs, IDs, account numbers, snippets, and vault-backed secret references.
5
+ Project-URL: Homepage, https://github.com/yy/recall
6
+ Project-URL: Repository, https://github.com/yy/recall
7
+ Author: YY Ahn
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: 1password,cli,clipboard,grep,jsonl,knowledge,secrets
11
+ Classifier: Environment :: Console
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Programming Language :: Python :: 3
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+
18
+ # recall
19
+
20
+ A small CLI **data jar**[^datajar] for lookup data you fetch over and over: URLs, IDs, account numbers, an ORCID, reusable snippets, plus *references* to secrets kept in a real vault.
21
+
22
+ You stop digging through email, password managers, and old notes for the same account number or portal URL. You, or an agent working for you, just ask:
23
+
24
+ ```console
25
+ $ recall orcid.self
26
+ copied orcid.self (id) to clipboard
27
+
28
+ $ recall insurance.portal
29
+ copied insurance.portal (url) to clipboard
30
+
31
+ $ recall search card
32
+ library-card [account] — Public library card number
33
+
34
+ $ recall github.token
35
+ github.token is a secret → op://Private/GitHub/token
36
+ run: recall secret github.token (opens it in 1Password to copy by hand)
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Contents
42
+
43
+ - [Philosophy](#philosophy)
44
+ - [Install](#install)
45
+ - [Quickstart](#quickstart)
46
+ - [The data file](#the-data-file)
47
+ - [Multiline data](#multiline-data)
48
+ - [Commands](#commands)
49
+ - [Secrets](#secrets)
50
+ - [Configuration](#configuration)
51
+ - [Agents](#agents)
52
+ - [Security model](#security-model)
53
+ - [Why JSONL](#why-jsonl)
54
+ - [Troubleshooting](#troubleshooting)
55
+ - [Development](#development)
56
+
57
+ ---
58
+
59
+ ## Philosophy
60
+
61
+ Most reusable lookup data is not secret: an ORCID, a health-insurance member ID, a portal URL, a library card number. A few items genuinely are secret: API keys, tokens, passwords.
62
+
63
+ `recall` splits data into two tiers:
64
+
65
+ | Tier | Examples | Where the value lives |
66
+ |---|---|---|
67
+ | **non-secret** | URLs, IDs, account numbers, ORCID, snippets | in a plain JSONL file, returned directly |
68
+ | **secret** | API keys, tokens, passwords | only a reference is stored; the value lives in 1Password and `recall` never reads it by default |
69
+
70
+ That split is the whole design. It lets you keep the data file in a private git repo, and lets an agent use `recall search`, `recall list`, and `recall <key>` without being able to exfiltrate credentials.
71
+
72
+ ---
73
+
74
+ ## Install
75
+
76
+ ```console
77
+ uv tool install recaller # PyPI distribution is `recaller`; the command is `recall`
78
+ ```
79
+
80
+ For the secret tier you also need the [1Password CLI](https://developer.1password.com/docs/cli/) with desktop-app integration turned on:
81
+
82
+ 1. `brew install 1password-cli`
83
+ 2. In 1Password: **Settings -> Developer -> Integrate with 1Password CLI**
84
+
85
+ `recall` is macOS-first today: it uses `pbcopy` for the clipboard and `open` for URLs, files, and 1Password deep links.
86
+
87
+ ---
88
+
89
+ ## Quickstart
90
+
91
+ 1. **Run init** and choose where the private data file should live:
92
+
93
+ ```console
94
+ $ recall init
95
+ Where should recall store data.jsonl? [/Users/you/.config/recall/data.jsonl]: ~/git/dotfiles/recall/data.jsonl
96
+ Seconds before copied secrets are cleared [45]:
97
+ Default secret backend [1password]:
98
+ Create a starter data file? [Y/n]:
99
+ ```
100
+
101
+ For a scriptable setup, pass the choices directly:
102
+
103
+ ```console
104
+ $ recall init --data-file ~/git/dotfiles/recall/data.jsonl --yes
105
+ ```
106
+
107
+ 2. **Add entries** to the data file:
108
+
109
+ ```jsonl
110
+ {"key": "orcid.self", "kind": "id", "value": "0000-0000-0000-0000", "note": "My ORCID iD", "tags": ["identity"]}
111
+ ```
112
+
113
+ 3. **Use it:**
114
+
115
+ ```console
116
+ $ recall orcid.self
117
+ copied orcid.self (id) to clipboard
118
+ $ recall doctor
119
+ ```
120
+
121
+ ---
122
+
123
+ ## The data file
124
+
125
+ The data file is `data.jsonl`: one JSON object per physical line. Each object is a complete entry and must include a `key` field.
126
+
127
+ ```jsonl
128
+ {"key": "orcid.self", "kind": "id", "value": "0000-0000-0000-0000", "note": "My ORCID iD", "tags": ["identity"]}
129
+ {"key": "orcid.coauthor", "kind": "id", "value": "0000-0000-0000-0001", "note": "A frequent collaborator", "tags": ["identity"]}
130
+ {"key": "insurance.member-id", "kind": "account", "value": "MEMBER-ID-EXAMPLE", "note": "Health insurance member ID", "tags": ["health"]}
131
+ {"key": "insurance.portal", "kind": "url", "value": "https://example.com/login", "note": "Claims portal; sign in with the member ID", "tags": ["health"]}
132
+ {"key": "library-card", "kind": "account", "value": "LIBRARY-CARD-EXAMPLE", "note": "Public library card number", "tags": ["library"]}
133
+ {"key": "github.token", "kind": "secret", "backend": "1password", "ref": "op://Private/GitHub/token", "note": "GitHub personal access token", "tags": ["dev"]}
134
+ ```
135
+
136
+ This format is intentionally boring. The lookup key, kind, value/ref, note, and tags are on the same line, so `rg` output has enough context for a human or agent:
137
+
138
+ ```console
139
+ $ rg -n 'insurance|health|member-id' ~/git/dotfiles/recall/data.jsonl
140
+ ```
141
+
142
+ Only the one-object-per-line rule matters; whitespace *within* a line is free. The canonical form (what `recall format` emits) is one space after each `:` and `,`, exactly what `json.dumps` produces by default. Minified lines parse identically, but the spaced form is easier to read and hand-edit.
143
+
144
+ JSONL has no comments. Keep commentary in `note`, in a linked file, or in project docs such as `README.md` or `ROADMAP.md`. That constraint is useful here: the data file stays machine-parseable, line-oriented, and unambiguous.
145
+
146
+ **Keys and namespaces.** Dots create virtual namespaces. `insurance.member-id` and `insurance.portal` are entries; `insurance` is inferred from those prefixes. Use short lowercase keys with dots between namespaces and hyphens inside a segment: `service.account-id`, `person.orcid`, `grant.nsf.institution-id`.
147
+
148
+ **Fields:**
149
+
150
+ | Field | Applies to | Meaning |
151
+ |---|---|---|
152
+ | `key` | all entries | required dotted lookup key |
153
+ | `kind` | all entries | `url`, `file`, `id`, `account`, `note`, `snippet`, `secret`, or another free-form label |
154
+ | `value` | non-secret entries | the value returned/copied; for `file`, this is a path |
155
+ | `backend` | secret entries | `1password` (default) or `keychain` |
156
+ | `ref` | secret entries | a vault reference, e.g. `op://Private/GitHub/token` |
157
+ | `note` | optional | shown in `search` / `list`; never affects the value |
158
+ | `tags` | optional | list of strings for `recall tags` |
159
+
160
+ ---
161
+
162
+ ## Multiline Data
163
+
164
+ The data file is an index, not a content store. Keep each entry and each field value to one physical line.
165
+
166
+ For multiline or large reusable content, store the content in a separate file and point to it:
167
+
168
+ ```jsonl
169
+ {"key": "email.reply-template", "kind": "file", "value": "~/git/dotfiles/recall/snippets/reply.md", "note": "Standard email reply template", "tags": ["email"]}
170
+ ```
171
+
172
+ `recall` rejects multiline `value`, `note`, `ref`, `backend`, `kind`, and tag strings. Use a `file` entry when the payload does not fit on one line.
173
+
174
+ For file-backed entries:
175
+
176
+ ```console
177
+ $ recall email.reply-template --show
178
+ ~/git/dotfiles/recall/snippets/reply.md
179
+
180
+ $ recall open email.reply-template
181
+ ```
182
+
183
+ `recall <key>` copies the file path. `recall open <key>` opens the file with the system default app.
184
+
185
+ ---
186
+
187
+ ## Commands
188
+
189
+ | Command | What it does |
190
+ |---|---|
191
+ | `recall init` | create `config.json` and the first data file |
192
+ | `recall <key>` | copy a non-secret value to the clipboard |
193
+ | `recall get <key> [--show]` | same; `--show` prints the value instead of copying |
194
+ | `recall secret <key>` | open the item in 1Password so you copy it by hand |
195
+ | `recall secret <key> --copy` | resolve via `op` and copy to the clipboard (auto-clears) |
196
+ | `recall secret <key> --show` | resolve and print the value (discouraged) |
197
+ | `recall open <key>` | open a `url` or `file` entry |
198
+ | `recall search <query>` | search keys, notes, tags, and non-secret values |
199
+ | `recall list [prefix]` | list entries, optionally under a namespace |
200
+ | `recall tags <tag>` | list entries carrying a tag |
201
+ | `recall json [key]` | dump a subtree as JSON |
202
+ | `recall export` | alias for `recall json` |
203
+ | `recall doctor` | validate the data file and environment |
204
+
205
+ Asking for a namespace instead of an entry lists its children:
206
+
207
+ ```console
208
+ $ recall insurance
209
+ insurance/ (namespace)
210
+ insurance.member-id [account] — Health insurance member ID
211
+ insurance.portal [url] — Claims portal; sign in with the member ID
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Secrets
217
+
218
+ Secrets store only a reference:
219
+
220
+ ```jsonl
221
+ {"key": "github.token", "kind": "secret", "backend": "1password", "ref": "op://Private/GitHub/token"}
222
+ ```
223
+
224
+ The `op://` reference has this shape:
225
+
226
+ ```text
227
+ op://<vault>/<item>/<field>
228
+ ```
229
+
230
+ There are three ways to interact with a secret, in increasing order of exposure:
231
+
232
+ 1. **`recall github.token`** refuses to resolve. It prints the reference and a hint.
233
+ 2. **`recall secret github.token`** opens the item in the 1Password app. `recall` does not read the secret value.
234
+ 3. **`recall secret github.token --copy`** runs `op read`, copies the value, and clears the clipboard after the configured timeout.
235
+
236
+ `--show` prints the value to stdout and exists only for debugging. Do not use it where anything is capturing terminal output.
237
+
238
+ Because secret entries already store `op://` references, they also work with 1Password's `op run` and `op inject`. An env file like this:
239
+
240
+ ```dotenv
241
+ GITHUB_TOKEN=op://Private/GitHub/token
242
+ ```
243
+
244
+ resolves at launch with `op run --env-file=.env -- your-command`. Keep `op run` to non-agent contexts, because resolved secrets live in the child process environment.
245
+
246
+ ---
247
+
248
+ ## Configuration
249
+
250
+ `recall` reads `~/.config/recall/config.json`. Override the directory with `$XDG_CONFIG_HOME` or `$RECALL_DIR`.
251
+
252
+ `recall init` writes this file for you:
253
+
254
+ ```json
255
+ {
256
+ "data_file": "~/git/dotfiles/recall/data.jsonl",
257
+ "clipboard_clear_seconds": 45,
258
+ "default_backend": "1password"
259
+ }
260
+ ```
261
+
262
+ **Data-file resolution order:** `$RECALL_DATA_FILE` -> `config.json`'s `data_file` -> `~/.config/recall/data.jsonl`.
263
+
264
+ A natural setup: the **tool** is public while your **data** is a `data.jsonl` in a private repo, e.g. your dotfiles, pointed at by `config.json`. The public code hardcodes nothing personal.
265
+
266
+ ---
267
+
268
+ ## Agents
269
+
270
+ Give your coding agent one instruction:
271
+
272
+ > If you need a URL, account number, identifier, reusable snippet, or credential reference, use `recall`. Find keys with `recall search <term>` or `recall list`. Never ask me to paste a secret; non-secret values go to the clipboard and secrets open in 1Password.
273
+
274
+ Then enforce the boundary in your harness. For **Claude Code** (`~/.claude/settings.json`):
275
+
276
+ ```jsonc
277
+ {
278
+ "permissions": {
279
+ "allow": ["Bash(recall:*)"],
280
+ "deny": ["Bash(recall secret:*)"]
281
+ }
282
+ }
283
+ ```
284
+
285
+ With this, an agent can look up non-secret data unattended, but secret resolution stays gated.
286
+
287
+ ---
288
+
289
+ ## Security Model
290
+
291
+ | Actor | Sees a non-secret | Sees a secret value |
292
+ |---|---|---|
293
+ | `recall <key>` | yes | no, it prints the reference only |
294
+ | `recall secret <key>` | - | no, it opens the vault app |
295
+ | `recall secret <key> --copy` | - | transiently, via `op`, to clipboard |
296
+ | an agent allowed `recall` but denied `recall secret` | yes | never |
297
+ | 1Password | - | yes |
298
+
299
+ Other properties:
300
+
301
+ - The data file is plain text by design, so keep it in a private repo. Filenames and keys are visible to anyone with repo access; do not encode secrets in key names.
302
+ - `recall` logs access to `~/.config/recall/audit.log`.
303
+ - The repo's `.gitignore` refuses to commit a real `data.jsonl`, `config.json`, older local data/config filenames, or `audit.log`.
304
+
305
+ ---
306
+
307
+ ## Why JSONL
308
+
309
+ - **One entry per line.** Grep hits are self-contained.
310
+ - **Strict parser.** No aliases, implicit typing, or layout-sensitive nesting.
311
+ - **Append and review friendly.** A new entry is a one-line diff.
312
+ - **Portable.** Every language can parse JSON without an extra dependency.
313
+ - **Clear escape hatch.** Multiline content belongs in a linked file, not inside the index.
314
+
315
+ Compared with a plain text file plus `grep`, the CLI adds validation, clipboard ergonomics, namespace listing, JSON export, `op://` indirection, and the agent-safe split between `recall` and `recall secret`.
316
+
317
+ ---
318
+
319
+ ## Troubleshooting
320
+
321
+ - **`no data file at ...`**: create the file and point `config.json`'s `data_file` at it, or set `$RECALL_DATA_FILE`, then run `recall doctor`.
322
+ - **`invalid JSONL ...`**: each nonblank line must be one complete JSON object. Run `recall doctor` after edits.
323
+ - **`op read failed` / `op not found`**: install the 1Password CLI and enable the desktop-app integration.
324
+ - **`recall secret` opens 1Password but not the right item**: check the `ref` against `op item get "<item>" --vault "<vault>"`.
325
+ - **Clipboard did not clear**: clipboard-history managers can keep copies after `recall` clears the system clipboard. Exclude `recall` or pause history when using `--copy`.
326
+
327
+ ---
328
+
329
+ ## Development
330
+
331
+ ```console
332
+ git clone https://github.com/yy/recall && cd recall
333
+ uv tool install --editable . --force
334
+ uv run python recall.py doctor
335
+ uv run pytest
336
+ ```
337
+
338
+ The core is one Python file (`recall.py`) and has no runtime package dependencies. The lookup layer is plain functions, so a future MCP server or other front-end can wrap it without going through the CLI.
339
+
340
+ ## License
341
+
342
+ MIT
343
+
344
+ [^datajar]: The idea is inspired by [Data Jar](https://datajar.app/).