keep-skill 0.7.0__tar.gz → 0.8.1__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.
- {keep_skill-0.7.0 → keep_skill-0.8.1}/PKG-INFO +3 -3
- {keep_skill-0.7.0 → keep_skill-0.8.1}/README.md +2 -2
- {keep_skill-0.7.0 → keep_skill-0.8.1}/SKILL.md +15 -5
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/api.py +12 -3
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/cli.py +81 -51
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/data/system/domains.md +4 -4
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/data/system/now.md +1 -2
- {keep_skill-0.7.0 → keep_skill-0.8.1}/pyproject.toml +1 -1
- {keep_skill-0.7.0 → keep_skill-0.8.1}/.gitignore +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/LICENSE +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/__init__.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/__main__.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/chunking.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/config.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/context.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/data/__init__.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/data/system/__init__.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/data/system/conversations.md +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/document_store.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/errors.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/indexing.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/logging_config.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/paths.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/pending_summaries.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/__init__.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/base.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/documents.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/embedding_cache.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/embeddings.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/llm.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/mlx.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/providers/summarization.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/store.py +0 -0
- {keep_skill-0.7.0 → keep_skill-0.8.1}/keep/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: keep-skill
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: Reflective memory - remember and search documents by meaning
|
|
5
5
|
Project-URL: Homepage, https://github.com/hughpyle/keep
|
|
6
6
|
Project-URL: Repository, https://github.com/hughpyle/keep
|
|
@@ -128,8 +128,8 @@ keep get "ID@V{1}" # Same as -V 1 (version identifier)
|
|
|
128
128
|
keep get ID --history # All versions
|
|
129
129
|
|
|
130
130
|
# Tags
|
|
131
|
-
keep tag project=myapp
|
|
132
|
-
keep
|
|
131
|
+
keep list --tag project=myapp # Find by tag
|
|
132
|
+
keep list --tags= # List all tag keys
|
|
133
133
|
|
|
134
134
|
# Current context
|
|
135
135
|
keep now # Show what you're working on
|
|
@@ -77,8 +77,8 @@ keep get "ID@V{1}" # Same as -V 1 (version identifier)
|
|
|
77
77
|
keep get ID --history # All versions
|
|
78
78
|
|
|
79
79
|
# Tags
|
|
80
|
-
keep tag project=myapp
|
|
81
|
-
keep
|
|
80
|
+
keep list --tag project=myapp # Find by tag
|
|
81
|
+
keep list --tags= # List all tag keys
|
|
82
82
|
|
|
83
83
|
# Current context
|
|
84
84
|
keep now # Show what you're working on
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keep
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.8.1
|
|
4
4
|
description: Reflective Memory
|
|
5
5
|
homepage: https://github.com/hughpyle/keep
|
|
6
6
|
runtime: python:3.12-slim
|
|
@@ -29,6 +29,15 @@ Before you can practice, ensure the practice is available in future sessions.
|
|
|
29
29
|
1. Check if your system rules file contains the keep Protocol Block (look for `## keep: Reflective Memory`)
|
|
30
30
|
2. If missing, append the Protocol Block
|
|
31
31
|
|
|
32
|
+
**Global System Prompt Locations:**
|
|
33
|
+
|
|
34
|
+
| Assistant | Location |
|
|
35
|
+
|-----------|----------|
|
|
36
|
+
| Claude Code | `~/.claude/CLAUDE.md` |
|
|
37
|
+
| OpenAI Codex | `~/.codex/AGENTS.md` |
|
|
38
|
+
|
|
39
|
+
Installing the Protocol Block at the global level makes it available across all projects. Prefer global installation when possible.
|
|
40
|
+
|
|
32
41
|
**Protocol Block:**
|
|
33
42
|
|
|
34
43
|
```markdown
|
|
@@ -252,8 +261,8 @@ keep update "Token refresh fails if clock skew > 30s" -t domain=auth -t type=fin
|
|
|
252
261
|
|
|
253
262
|
Later:
|
|
254
263
|
```bash
|
|
255
|
-
keep tag domain=auth # Everything about auth
|
|
256
|
-
keep tag type=decision # All decisions made
|
|
264
|
+
keep list --tag domain=auth # Everything about auth
|
|
265
|
+
keep list --tag type=decision # All decisions made
|
|
257
266
|
```
|
|
258
267
|
|
|
259
268
|
**Suggested tag dimensions:**
|
|
@@ -300,7 +309,8 @@ Don't dump everything into context. Navigate the tree:
|
|
|
300
309
|
| `get` | Retrieve item (shows similar items) | `keep get "file:///path/to/doc.md"` |
|
|
301
310
|
| `get --similar` | List similar items | `keep get ID --similar` or `-n 20` for more |
|
|
302
311
|
| `get -V N` | Previous versions | `keep get ID -V 1` or `keep get ID --history` |
|
|
303
|
-
| `tag` |
|
|
312
|
+
| `list --tag` | Filter items by tag | `keep list --tag domain=auth` |
|
|
313
|
+
| `list --tags` | List tag keys or values | `keep list --tags=` or `keep list --tags=domain` |
|
|
304
314
|
| `tag-update` | Modify tags on existing item | `keep tag-update "id" --tag key=value` |
|
|
305
315
|
| `collections` | List all collections | `keep collections` |
|
|
306
316
|
| `init` | Initialize or verify store | `keep init` |
|
|
@@ -360,7 +370,7 @@ keep -v find "auth" # Debug logging
|
|
|
360
370
|
Use `--ids` for Unix-style composition:
|
|
361
371
|
```bash
|
|
362
372
|
keep --ids find "auth" | xargs keep get # Get full details of matches
|
|
363
|
-
keep --ids tag project=foo | xargs keep tag-update --tag status=done
|
|
373
|
+
keep --ids list --tag project=foo | xargs keep tag-update --tag status=done
|
|
364
374
|
keep --ids list | xargs -I{} keep get "{}" # Get details for recent items
|
|
365
375
|
```
|
|
366
376
|
|
|
@@ -1554,6 +1554,7 @@ class Keeper:
|
|
|
1554
1554
|
self,
|
|
1555
1555
|
limit: int = 10,
|
|
1556
1556
|
*,
|
|
1557
|
+
since: Optional[str] = None,
|
|
1557
1558
|
collection: Optional[str] = None,
|
|
1558
1559
|
) -> list[Item]:
|
|
1559
1560
|
"""
|
|
@@ -1561,16 +1562,24 @@ class Keeper:
|
|
|
1561
1562
|
|
|
1562
1563
|
Args:
|
|
1563
1564
|
limit: Maximum number to return (default 10)
|
|
1565
|
+
since: Only include items updated since (ISO duration like P3D, or date)
|
|
1564
1566
|
collection: Collection to query (uses default if not specified)
|
|
1565
1567
|
|
|
1566
1568
|
Returns:
|
|
1567
1569
|
List of Items, most recently updated first
|
|
1568
1570
|
"""
|
|
1569
1571
|
coll = self._resolve_collection(collection)
|
|
1570
|
-
records = self._document_store.list_recent(coll, limit)
|
|
1571
1572
|
|
|
1572
|
-
|
|
1573
|
-
|
|
1573
|
+
# Fetch extra when filtering by date
|
|
1574
|
+
fetch_limit = limit * 3 if since is not None else limit
|
|
1575
|
+
records = self._document_store.list_recent(coll, fetch_limit)
|
|
1576
|
+
items = [_record_to_item(rec) for rec in records]
|
|
1577
|
+
|
|
1578
|
+
# Apply date filter if specified
|
|
1579
|
+
if since is not None:
|
|
1580
|
+
items = _filter_by_date(items, since)
|
|
1581
|
+
|
|
1582
|
+
return items[:limit]
|
|
1574
1583
|
|
|
1575
1584
|
def embedding_cache_stats(self) -> dict:
|
|
1576
1585
|
"""
|
|
@@ -47,6 +47,7 @@ def _verbose_callback(value: bool):
|
|
|
47
47
|
_json_output = False
|
|
48
48
|
_ids_output = False
|
|
49
49
|
_full_output = False
|
|
50
|
+
_store_override: Optional[Path] = None
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
def _json_callback(value: bool):
|
|
@@ -76,6 +77,16 @@ def _get_full_output() -> bool:
|
|
|
76
77
|
return _full_output
|
|
77
78
|
|
|
78
79
|
|
|
80
|
+
def _store_callback(value: Optional[Path]):
|
|
81
|
+
global _store_override
|
|
82
|
+
if value is not None:
|
|
83
|
+
_store_override = value
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _get_store_override() -> Optional[Path]:
|
|
87
|
+
return _store_override
|
|
88
|
+
|
|
89
|
+
|
|
79
90
|
app = typer.Typer(
|
|
80
91
|
name="keep",
|
|
81
92
|
help="Reflective memory with semantic search.",
|
|
@@ -231,6 +242,13 @@ def main_callback(
|
|
|
231
242
|
callback=_full_callback,
|
|
232
243
|
is_eager=True,
|
|
233
244
|
)] = False,
|
|
245
|
+
store: Annotated[Optional[Path], typer.Option(
|
|
246
|
+
"--store", "-s",
|
|
247
|
+
envvar="KEEP_STORE_PATH",
|
|
248
|
+
help="Path to the store directory",
|
|
249
|
+
callback=_store_callback,
|
|
250
|
+
is_eager=True,
|
|
251
|
+
)] = None,
|
|
234
252
|
):
|
|
235
253
|
"""Reflective memory with semantic search."""
|
|
236
254
|
# If no subcommand provided, show the current context (now)
|
|
@@ -401,9 +419,10 @@ def _format_items(items: list[Item], as_json: bool = False) -> str:
|
|
|
401
419
|
|
|
402
420
|
def _get_keeper(store: Optional[Path], collection: str) -> Keeper:
|
|
403
421
|
"""Initialize memory, handling errors gracefully."""
|
|
404
|
-
#
|
|
422
|
+
# Check global override from --store on main command
|
|
423
|
+
actual_store = store if store is not None else _get_store_override()
|
|
405
424
|
try:
|
|
406
|
-
return Keeper(
|
|
425
|
+
return Keeper(actual_store, collection=collection)
|
|
407
426
|
except Exception as e:
|
|
408
427
|
typer.echo(f"Error: {e}", err=True)
|
|
409
428
|
raise typer.Exit(1)
|
|
@@ -464,6 +483,7 @@ def find(
|
|
|
464
483
|
"""
|
|
465
484
|
Find items using semantic similarity search.
|
|
466
485
|
|
|
486
|
+
\b
|
|
467
487
|
Examples:
|
|
468
488
|
keep find "authentication" # Search by text
|
|
469
489
|
keep find --id file:///path/to/doc.md # Find similar to item
|
|
@@ -509,71 +529,76 @@ def list_recent(
|
|
|
509
529
|
"--limit", "-n",
|
|
510
530
|
help="Number of items to show"
|
|
511
531
|
)] = 10,
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
results = kp.list_recent(limit=limit)
|
|
520
|
-
typer.echo(_format_items(results, as_json=_get_json_output()))
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
@app.command()
|
|
524
|
-
def tag(
|
|
525
|
-
query: Annotated[Optional[str], typer.Argument(
|
|
526
|
-
help="Tag key to list values, or key=value to find docs"
|
|
532
|
+
tag: Annotated[Optional[list[str]], typer.Option(
|
|
533
|
+
"--tag", "-t",
|
|
534
|
+
help="Filter by tag (key or key=value, repeatable)"
|
|
535
|
+
)] = None,
|
|
536
|
+
tags: Annotated[Optional[str], typer.Option(
|
|
537
|
+
"--tags", "-T",
|
|
538
|
+
help="List tag keys (--tags=), or values for KEY (--tags=KEY)"
|
|
527
539
|
)] = None,
|
|
528
|
-
list_keys: Annotated[bool, typer.Option(
|
|
529
|
-
"--list", "-l",
|
|
530
|
-
help="List all distinct tag keys"
|
|
531
|
-
)] = False,
|
|
532
|
-
store: StoreOption = None,
|
|
533
|
-
collection: CollectionOption = "default",
|
|
534
|
-
limit: LimitOption = 100,
|
|
535
540
|
since: SinceOption = None,
|
|
536
541
|
):
|
|
537
542
|
"""
|
|
538
|
-
List
|
|
543
|
+
List recent items, filter by tags, or list tag keys/values.
|
|
539
544
|
|
|
545
|
+
\b
|
|
540
546
|
Examples:
|
|
541
|
-
keep
|
|
542
|
-
keep tag
|
|
543
|
-
keep tag
|
|
547
|
+
keep list # Recent items
|
|
548
|
+
keep list --tag foo # Items with tag 'foo' (any value)
|
|
549
|
+
keep list --tag foo=bar # Items with tag foo=bar
|
|
550
|
+
keep list --tag foo --tag bar # Items with both tags
|
|
551
|
+
keep list --tags= # List all tag keys
|
|
552
|
+
keep list --tags=foo # List values for tag 'foo'
|
|
553
|
+
keep list --since P3D # Items updated in last 3 days
|
|
544
554
|
"""
|
|
545
555
|
kp = _get_keeper(store, collection)
|
|
546
556
|
|
|
547
|
-
#
|
|
548
|
-
if
|
|
549
|
-
|
|
550
|
-
if
|
|
551
|
-
|
|
552
|
-
else:
|
|
553
|
-
if not tags:
|
|
554
|
-
typer.echo("No tags found.")
|
|
555
|
-
else:
|
|
556
|
-
for t in tags:
|
|
557
|
-
typer.echo(t)
|
|
558
|
-
return
|
|
559
|
-
|
|
560
|
-
# Check if query is key=value or just key
|
|
561
|
-
if "=" in query:
|
|
562
|
-
# key=value → find documents
|
|
563
|
-
key, value = query.split("=", 1)
|
|
564
|
-
results = kp.query_tag(key, value, limit=limit, since=since)
|
|
565
|
-
typer.echo(_format_items(results, as_json=_get_json_output()))
|
|
566
|
-
else:
|
|
567
|
-
# key only → list values
|
|
568
|
-
values = kp.list_tags(query, collection=collection)
|
|
557
|
+
# --tags mode: list keys or values
|
|
558
|
+
if tags is not None:
|
|
559
|
+
# Empty string means list all keys, otherwise list values for key
|
|
560
|
+
key = tags if tags else None
|
|
561
|
+
values = kp.list_tags(key, collection=collection)
|
|
569
562
|
if _get_json_output():
|
|
570
563
|
typer.echo(json.dumps(values))
|
|
571
564
|
else:
|
|
572
565
|
if not values:
|
|
573
|
-
|
|
566
|
+
if key:
|
|
567
|
+
typer.echo(f"No values for tag '{key}'.")
|
|
568
|
+
else:
|
|
569
|
+
typer.echo("No tags found.")
|
|
574
570
|
else:
|
|
575
571
|
for v in values:
|
|
576
572
|
typer.echo(v)
|
|
573
|
+
return
|
|
574
|
+
|
|
575
|
+
# --tag mode: filter items by tag(s)
|
|
576
|
+
if tag:
|
|
577
|
+
# Parse each tag as key or key=value
|
|
578
|
+
# Multiple tags require all to match (AND)
|
|
579
|
+
results = None
|
|
580
|
+
for t in tag:
|
|
581
|
+
if "=" in t:
|
|
582
|
+
key, value = t.split("=", 1)
|
|
583
|
+
matches = kp.query_tag(key, value, limit=limit, since=since, collection=collection)
|
|
584
|
+
else:
|
|
585
|
+
# Key only - find items with this tag key (any value)
|
|
586
|
+
matches = kp.query_tag(t, limit=limit, since=since, collection=collection)
|
|
587
|
+
|
|
588
|
+
if results is None:
|
|
589
|
+
results = {item.id: item for item in matches}
|
|
590
|
+
else:
|
|
591
|
+
# Intersect with previous results
|
|
592
|
+
match_ids = {item.id for item in matches}
|
|
593
|
+
results = {id: item for id, item in results.items() if id in match_ids}
|
|
594
|
+
|
|
595
|
+
items = list(results.values()) if results else []
|
|
596
|
+
typer.echo(_format_items(items[:limit], as_json=_get_json_output()))
|
|
597
|
+
return
|
|
598
|
+
|
|
599
|
+
# Default: recent items
|
|
600
|
+
results = kp.list_recent(limit=limit, since=since, collection=collection)
|
|
601
|
+
typer.echo(_format_items(results, as_json=_get_json_output()))
|
|
577
602
|
|
|
578
603
|
|
|
579
604
|
@app.command("tag-update")
|
|
@@ -595,6 +620,7 @@ def tag_update(
|
|
|
595
620
|
|
|
596
621
|
Does not re-process the document - only updates tags.
|
|
597
622
|
|
|
623
|
+
\b
|
|
598
624
|
Examples:
|
|
599
625
|
keep tag-update doc:1 --tag project=myapp
|
|
600
626
|
keep tag-update doc:1 doc:2 --tag status=reviewed
|
|
@@ -665,12 +691,14 @@ def update(
|
|
|
665
691
|
"""
|
|
666
692
|
Add or update a document in the store.
|
|
667
693
|
|
|
694
|
+
\b
|
|
668
695
|
Three input modes (auto-detected):
|
|
669
696
|
keep update file:///path # URI mode: has ://
|
|
670
697
|
keep update "my note" # Text mode: content-addressed ID
|
|
671
698
|
keep update - # Stdin mode: explicit -
|
|
672
699
|
echo "pipe" | keep update # Stdin mode: piped input
|
|
673
700
|
|
|
701
|
+
\b
|
|
674
702
|
Text mode uses content-addressed IDs for versioning:
|
|
675
703
|
keep update "my note" # Creates _text:{hash}
|
|
676
704
|
keep update "my note" -t done # Same ID, new version (tag change)
|
|
@@ -737,6 +765,7 @@ def now(
|
|
|
737
765
|
With no arguments, displays the current context.
|
|
738
766
|
With content, replaces it.
|
|
739
767
|
|
|
768
|
+
\b
|
|
740
769
|
Examples:
|
|
741
770
|
keep now # Show current context
|
|
742
771
|
keep now "What's important now" # Update context
|
|
@@ -907,6 +936,7 @@ def get(
|
|
|
907
936
|
|
|
908
937
|
Version identifiers: Append @V{N} to get a specific version.
|
|
909
938
|
|
|
939
|
+
\b
|
|
910
940
|
Examples:
|
|
911
941
|
keep get doc:1 # Current version with similar items
|
|
912
942
|
keep get doc:1 -V 1 # Previous version with prev/next nav
|
|
@@ -145,17 +145,17 @@ Ask about scope before large changes." \
|
|
|
145
145
|
**Temporal queries using system tags:**
|
|
146
146
|
```bash
|
|
147
147
|
# Find items updated today
|
|
148
|
-
keep tag _updated_date=2026-01-30
|
|
148
|
+
keep list --tag _updated_date=2026-01-30
|
|
149
149
|
|
|
150
150
|
# Find all inline content (from remember)
|
|
151
|
-
keep tag _source=inline
|
|
151
|
+
keep list --tag _source=inline
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
**Progressive refinement:**
|
|
155
155
|
```bash
|
|
156
156
|
# Start broad, then narrow
|
|
157
157
|
keep find "authentication"
|
|
158
|
-
keep tag module=auth
|
|
158
|
+
keep list --tag module=auth
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
---
|
|
@@ -173,7 +173,7 @@ Only then commit to action. Breakdown risk: Promising too early leads to rework.
|
|
|
173
173
|
--tag type=conversation_pattern --tag domain=general
|
|
174
174
|
|
|
175
175
|
# Later, retrieve it
|
|
176
|
-
keep tag type=conversation_pattern
|
|
176
|
+
keep list --tag type=conversation_pattern
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
See [conversations.md](conversations.md) for the full framework.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|