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.
- recaller-0.1.0/.gitignore +17 -0
- recaller-0.1.0/LICENSE +21 -0
- recaller-0.1.0/PKG-INFO +344 -0
- recaller-0.1.0/README.md +327 -0
- recaller-0.1.0/examples/data.jsonl +7 -0
- recaller-0.1.0/pyproject.toml +36 -0
- recaller-0.1.0/recall.py +884 -0
- recaller-0.1.0/tests/test_clipboard.py +54 -0
- recaller-0.1.0/tests/test_data_loading.py +146 -0
- recaller-0.1.0/tests/test_init.py +113 -0
|
@@ -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.
|
recaller-0.1.0/PKG-INFO
ADDED
|
@@ -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/).
|