keep-skill 0.7.0__py3-none-any.whl → 0.9.0__py3-none-any.whl
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/api.py +34 -33
- keep/cli.py +90 -64
- keep/data/system/domains.md +4 -4
- keep/data/system/now.md +1 -2
- {keep_skill-0.7.0.dist-info → keep_skill-0.9.0.dist-info}/METADATA +6 -6
- {keep_skill-0.7.0.dist-info → keep_skill-0.9.0.dist-info}/RECORD +9 -9
- {keep_skill-0.7.0.dist-info → keep_skill-0.9.0.dist-info}/WHEEL +0 -0
- {keep_skill-0.7.0.dist-info → keep_skill-0.9.0.dist-info}/entry_points.txt +0 -0
- {keep_skill-0.7.0.dist-info → keep_skill-0.9.0.dist-info}/licenses/LICENSE +0 -0
keep/api.py
CHANGED
|
@@ -534,13 +534,17 @@ class Keeper:
|
|
|
534
534
|
summary: Optional[str] = None,
|
|
535
535
|
source_tags: Optional[dict[str, str]] = None, # Deprecated alias
|
|
536
536
|
collection: Optional[str] = None,
|
|
537
|
-
lazy: bool = False
|
|
538
537
|
) -> Item:
|
|
539
538
|
"""
|
|
540
539
|
Insert or update a document in the store.
|
|
541
540
|
|
|
542
541
|
Fetches the document, generates embeddings and summary, then stores it.
|
|
543
542
|
|
|
543
|
+
**Summary behavior:**
|
|
544
|
+
- If summary is provided, use it (skips auto-summarization)
|
|
545
|
+
- For large content, summarization is async (truncated placeholder
|
|
546
|
+
stored immediately, real summary generated in background)
|
|
547
|
+
|
|
544
548
|
**Update behavior:**
|
|
545
549
|
- Summary: Replaced with user-provided or newly generated summary
|
|
546
550
|
- Tags: Merged - existing tags are preserved, new tags override
|
|
@@ -553,9 +557,6 @@ class Keeper:
|
|
|
553
557
|
summary: User-provided summary (skips auto-summarization if given)
|
|
554
558
|
source_tags: Deprecated alias for 'tags'
|
|
555
559
|
collection: Target collection (uses default if None)
|
|
556
|
-
lazy: If True, use truncated placeholder summary and queue for
|
|
557
|
-
background processing. Use `process_pending()` to generate
|
|
558
|
-
real summaries later. Ignored if summary is provided.
|
|
559
560
|
|
|
560
561
|
Returns:
|
|
561
562
|
The stored Item with merged tags and new summary
|
|
@@ -615,17 +616,14 @@ class Keeper:
|
|
|
615
616
|
)
|
|
616
617
|
summary = summary[:max_len]
|
|
617
618
|
final_summary = summary
|
|
618
|
-
|
|
619
|
-
#
|
|
619
|
+
else:
|
|
620
|
+
# Large content: async summarization (truncated placeholder now, real summary later)
|
|
620
621
|
if len(doc.content) > max_len:
|
|
621
622
|
final_summary = doc.content[:max_len] + "..."
|
|
623
|
+
# Queue for background processing
|
|
624
|
+
self._pending_queue.enqueue(id, coll, doc.content)
|
|
622
625
|
else:
|
|
623
626
|
final_summary = doc.content
|
|
624
|
-
# Queue for background processing
|
|
625
|
-
self._pending_queue.enqueue(id, coll, doc.content)
|
|
626
|
-
else:
|
|
627
|
-
# Auto-generate summary
|
|
628
|
-
final_summary = self._get_summarization_provider().summarize(doc.content)
|
|
629
627
|
|
|
630
628
|
# Build tags: existing → config → env → user (later wins on collision)
|
|
631
629
|
merged_tags = {**existing_tags}
|
|
@@ -686,8 +684,8 @@ class Keeper:
|
|
|
686
684
|
tags=old_doc.tags,
|
|
687
685
|
)
|
|
688
686
|
|
|
689
|
-
# Spawn background processor if
|
|
690
|
-
if
|
|
687
|
+
# Spawn background processor if content was queued (large content, no user summary, content changed)
|
|
688
|
+
if summary is None and len(doc.content) > max_len and not content_unchanged:
|
|
691
689
|
self._spawn_processor()
|
|
692
690
|
|
|
693
691
|
# Return the stored item
|
|
@@ -703,7 +701,6 @@ class Keeper:
|
|
|
703
701
|
tags: Optional[dict[str, str]] = None,
|
|
704
702
|
source_tags: Optional[dict[str, str]] = None, # Deprecated alias
|
|
705
703
|
collection: Optional[str] = None,
|
|
706
|
-
lazy: bool = False
|
|
707
704
|
) -> Item:
|
|
708
705
|
"""
|
|
709
706
|
Store inline content directly (without fetching from a URI).
|
|
@@ -713,7 +710,8 @@ class Keeper:
|
|
|
713
710
|
**Smart summary behavior:**
|
|
714
711
|
- If summary is provided, use it (skips auto-summarization)
|
|
715
712
|
- If content is short (≤ max_summary_length), use content verbatim
|
|
716
|
-
-
|
|
713
|
+
- For large content, summarization is async (truncated placeholder
|
|
714
|
+
stored immediately, real summary generated in background)
|
|
717
715
|
|
|
718
716
|
**Update behavior (when id already exists):**
|
|
719
717
|
- Summary: Replaced with user-provided, content, or generated summary
|
|
@@ -728,9 +726,6 @@ class Keeper:
|
|
|
728
726
|
tags: User-provided tags to merge with existing tags
|
|
729
727
|
source_tags: Deprecated alias for 'tags'
|
|
730
728
|
collection: Target collection (uses default if None)
|
|
731
|
-
lazy: If True and content is long, use truncated placeholder summary
|
|
732
|
-
and queue for background processing. Ignored if content is
|
|
733
|
-
short or summary is provided.
|
|
734
729
|
|
|
735
730
|
Returns:
|
|
736
731
|
The stored Item with merged tags and new summary
|
|
@@ -794,14 +789,11 @@ class Keeper:
|
|
|
794
789
|
elif len(content) <= max_len:
|
|
795
790
|
# Content is short enough - use verbatim (smart summary)
|
|
796
791
|
final_summary = content
|
|
797
|
-
|
|
798
|
-
# Content is long
|
|
792
|
+
else:
|
|
793
|
+
# Content is long - async summarization (truncated placeholder now, real summary later)
|
|
799
794
|
final_summary = content[:max_len] + "..."
|
|
800
795
|
# Queue for background processing
|
|
801
796
|
self._pending_queue.enqueue(id, coll, content)
|
|
802
|
-
else:
|
|
803
|
-
# Content is long - generate summary
|
|
804
|
-
final_summary = self._get_summarization_provider().summarize(content)
|
|
805
797
|
|
|
806
798
|
# Build tags: existing → config → env → user (later wins on collision)
|
|
807
799
|
merged_tags = {**existing_tags}
|
|
@@ -860,8 +852,8 @@ class Keeper:
|
|
|
860
852
|
tags=old_doc.tags,
|
|
861
853
|
)
|
|
862
854
|
|
|
863
|
-
# Spawn background processor if
|
|
864
|
-
if
|
|
855
|
+
# Spawn background processor if content was queued (large content, no user summary, content changed)
|
|
856
|
+
if summary is None and len(content) > max_len and not content_unchanged:
|
|
865
857
|
self._spawn_processor()
|
|
866
858
|
|
|
867
859
|
# Return the stored item
|
|
@@ -1366,14 +1358,14 @@ class Keeper:
|
|
|
1366
1358
|
|
|
1367
1359
|
def get_now(self) -> Item:
|
|
1368
1360
|
"""
|
|
1369
|
-
Get the current working
|
|
1361
|
+
Get the current working intentions.
|
|
1370
1362
|
|
|
1371
1363
|
A singleton document representing what you're currently working on.
|
|
1372
1364
|
If it doesn't exist, creates one with default content and tags from
|
|
1373
1365
|
the bundled system now.md file.
|
|
1374
1366
|
|
|
1375
1367
|
Returns:
|
|
1376
|
-
The current
|
|
1368
|
+
The current intentions Item (never None - auto-creates if missing)
|
|
1377
1369
|
"""
|
|
1378
1370
|
item = self.get(NOWDOC_ID)
|
|
1379
1371
|
if item is None:
|
|
@@ -1394,13 +1386,13 @@ class Keeper:
|
|
|
1394
1386
|
tags: Optional[dict[str, str]] = None,
|
|
1395
1387
|
) -> Item:
|
|
1396
1388
|
"""
|
|
1397
|
-
Set the current working
|
|
1389
|
+
Set the current working intentions.
|
|
1398
1390
|
|
|
1399
|
-
Updates the singleton
|
|
1391
|
+
Updates the singleton intentions with new content. Uses remember()
|
|
1400
1392
|
internally with the fixed NOWDOC_ID.
|
|
1401
1393
|
|
|
1402
1394
|
Args:
|
|
1403
|
-
content: New content for the current
|
|
1395
|
+
content: New content for the current intentions
|
|
1404
1396
|
tags: Optional additional tags to apply
|
|
1405
1397
|
|
|
1406
1398
|
Returns:
|
|
@@ -1554,6 +1546,7 @@ class Keeper:
|
|
|
1554
1546
|
self,
|
|
1555
1547
|
limit: int = 10,
|
|
1556
1548
|
*,
|
|
1549
|
+
since: Optional[str] = None,
|
|
1557
1550
|
collection: Optional[str] = None,
|
|
1558
1551
|
) -> list[Item]:
|
|
1559
1552
|
"""
|
|
@@ -1561,16 +1554,24 @@ class Keeper:
|
|
|
1561
1554
|
|
|
1562
1555
|
Args:
|
|
1563
1556
|
limit: Maximum number to return (default 10)
|
|
1557
|
+
since: Only include items updated since (ISO duration like P3D, or date)
|
|
1564
1558
|
collection: Collection to query (uses default if not specified)
|
|
1565
1559
|
|
|
1566
1560
|
Returns:
|
|
1567
1561
|
List of Items, most recently updated first
|
|
1568
1562
|
"""
|
|
1569
1563
|
coll = self._resolve_collection(collection)
|
|
1570
|
-
records = self._document_store.list_recent(coll, limit)
|
|
1571
1564
|
|
|
1572
|
-
|
|
1573
|
-
|
|
1565
|
+
# Fetch extra when filtering by date
|
|
1566
|
+
fetch_limit = limit * 3 if since is not None else limit
|
|
1567
|
+
records = self._document_store.list_recent(coll, fetch_limit)
|
|
1568
|
+
items = [_record_to_item(rec) for rec in records]
|
|
1569
|
+
|
|
1570
|
+
# Apply date filter if specified
|
|
1571
|
+
if since is not None:
|
|
1572
|
+
items = _filter_by_date(items, since)
|
|
1573
|
+
|
|
1574
|
+
return items[:limit]
|
|
1574
1575
|
|
|
1575
1576
|
def embedding_cache_stats(self) -> dict:
|
|
1576
1577
|
"""
|
keep/cli.py
CHANGED
|
@@ -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,9 +242,16 @@ 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
|
-
# If no subcommand provided, show the current
|
|
254
|
+
# If no subcommand provided, show the current intentions (now)
|
|
237
255
|
if ctx.invoked_subcommand is None:
|
|
238
256
|
from .api import NOWDOC_ID
|
|
239
257
|
kp = _get_keeper(None, "default")
|
|
@@ -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
|
|
@@ -657,20 +683,18 @@ def update(
|
|
|
657
683
|
"--summary",
|
|
658
684
|
help="User-provided summary (skips auto-summarization)"
|
|
659
685
|
)] = None,
|
|
660
|
-
lazy: Annotated[bool, typer.Option(
|
|
661
|
-
"--lazy",
|
|
662
|
-
help="Fast mode: use truncated summary, queue for later processing"
|
|
663
|
-
)] = False,
|
|
664
686
|
):
|
|
665
687
|
"""
|
|
666
688
|
Add or update a document in the store.
|
|
667
689
|
|
|
690
|
+
\b
|
|
668
691
|
Three input modes (auto-detected):
|
|
669
692
|
keep update file:///path # URI mode: has ://
|
|
670
693
|
keep update "my note" # Text mode: content-addressed ID
|
|
671
694
|
keep update - # Stdin mode: explicit -
|
|
672
695
|
echo "pipe" | keep update # Stdin mode: piped input
|
|
673
696
|
|
|
697
|
+
\b
|
|
674
698
|
Text mode uses content-addressed IDs for versioning:
|
|
675
699
|
keep update "my note" # Creates _text:{hash}
|
|
676
700
|
keep update "my note" -t done # Same ID, new version (tag change)
|
|
@@ -687,15 +711,15 @@ def update(
|
|
|
687
711
|
parsed_tags = {**frontmatter_tags, **parsed_tags} # CLI tags override
|
|
688
712
|
# Use content-addressed ID for stdin text (enables versioning)
|
|
689
713
|
doc_id = id or _text_content_id(content)
|
|
690
|
-
item = kp.remember(content, id=doc_id, summary=summary, tags=parsed_tags or None
|
|
714
|
+
item = kp.remember(content, id=doc_id, summary=summary, tags=parsed_tags or None)
|
|
691
715
|
elif source and _URI_SCHEME_PATTERN.match(source):
|
|
692
716
|
# URI mode: fetch from URI (ID is the URI itself)
|
|
693
|
-
item = kp.update(source, tags=parsed_tags or None, summary=summary
|
|
717
|
+
item = kp.update(source, tags=parsed_tags or None, summary=summary)
|
|
694
718
|
elif source:
|
|
695
719
|
# Text mode: inline content (no :// in source)
|
|
696
720
|
# Use content-addressed ID for text (enables versioning)
|
|
697
721
|
doc_id = id or _text_content_id(source)
|
|
698
|
-
item = kp.remember(source, id=doc_id, summary=summary, tags=parsed_tags or None
|
|
722
|
+
item = kp.remember(source, id=doc_id, summary=summary, tags=parsed_tags or None)
|
|
699
723
|
else:
|
|
700
724
|
typer.echo("Error: Provide content, URI, or '-' for stdin", err=True)
|
|
701
725
|
raise typer.Exit(1)
|
|
@@ -732,14 +756,15 @@ def now(
|
|
|
732
756
|
)] = None,
|
|
733
757
|
):
|
|
734
758
|
"""
|
|
735
|
-
Get or set the current working
|
|
759
|
+
Get or set the current working intentions.
|
|
736
760
|
|
|
737
|
-
With no arguments, displays the current
|
|
761
|
+
With no arguments, displays the current intentions.
|
|
738
762
|
With content, replaces it.
|
|
739
763
|
|
|
764
|
+
\b
|
|
740
765
|
Examples:
|
|
741
|
-
keep now # Show current
|
|
742
|
-
keep now "What's important now" # Update
|
|
766
|
+
keep now # Show current intentions
|
|
767
|
+
keep now "What's important now" # Update intentions
|
|
743
768
|
keep now -f context.md # Read content from file
|
|
744
769
|
keep now --reset # Reset to default from system
|
|
745
770
|
keep now -V 1 # Previous version
|
|
@@ -862,7 +887,7 @@ def now(
|
|
|
862
887
|
item = kp.set_now(new_content, tags=parsed_tags or None)
|
|
863
888
|
typer.echo(_format_item(item, as_json=_get_json_output()))
|
|
864
889
|
else:
|
|
865
|
-
# Get current
|
|
890
|
+
# Get current intentions with version navigation and similar items
|
|
866
891
|
item = kp.get_now()
|
|
867
892
|
version_nav = kp.get_version_nav(NOWDOC_ID, None, collection=collection)
|
|
868
893
|
similar_items = kp.get_similar_for_display(NOWDOC_ID, limit=3, collection=collection)
|
|
@@ -907,6 +932,7 @@ def get(
|
|
|
907
932
|
|
|
908
933
|
Version identifiers: Append @V{N} to get a specific version.
|
|
909
934
|
|
|
935
|
+
\b
|
|
910
936
|
Examples:
|
|
911
937
|
keep get doc:1 # Current version with similar items
|
|
912
938
|
keep get doc:1 -V 1 # Previous version with prev/next nav
|
keep/data/system/domains.md
CHANGED
|
@@ -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.
|
keep/data/system/now.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: keep-skill
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
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
|
|
@@ -68,7 +68,7 @@ keep find "what's the rate limit?"
|
|
|
68
68
|
|
|
69
69
|
# Track what you're working on
|
|
70
70
|
keep now "Debugging auth flow"
|
|
71
|
-
keep now -V 1 # Previous
|
|
71
|
+
keep now -V 1 # Previous intentions
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
---
|
|
@@ -128,12 +128,12 @@ 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
|
-
# Current
|
|
134
|
+
# Current intentions
|
|
135
135
|
keep now # Show what you're working on
|
|
136
|
-
keep now "Fixing login bug" # Update
|
|
136
|
+
keep now "Fixing login bug" # Update intentions
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
### Python API
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
keep/__init__.py,sha256=yBK7jvbQmx9fRBGanICNgrQdyCQHzt5bNPp098Qvh9E,1621
|
|
2
2
|
keep/__main__.py,sha256=3Uu70IhIDIjh8OW6jp9jQQ3dF2lKdJWi_3FtRIQMiMY,104
|
|
3
|
-
keep/api.py,sha256=
|
|
3
|
+
keep/api.py,sha256=3DHqgIsX7Fu6SwpxHwOnCA5ao0S9DlPschfK4WlN_Hs,64538
|
|
4
4
|
keep/chunking.py,sha256=neAXOLSvVwbUxapbqq7nZrbSNSzMXuhxj-ODoOSodsU,11830
|
|
5
|
-
keep/cli.py,sha256=
|
|
5
|
+
keep/cli.py,sha256=m3yokiLUbz1SWUD2IQEhI1WieY3IKVdpLV-f8Nd-BP8,45859
|
|
6
6
|
keep/config.py,sha256=hWjiJDg2u6p8IJpksXe0ngVxQNcKHKRFKUDJQBFlG7I,16226
|
|
7
7
|
keep/context.py,sha256=CNpjmrv6eW2kV1E0MO6qAQfhYKRlfzAL--6v4Mj1nFY,71
|
|
8
8
|
keep/document_store.py,sha256=UswqKIGSc5E-r7Tg9k0g5-byYnuar3e9FieQ7WNod9k,29109
|
|
@@ -16,8 +16,8 @@ keep/types.py,sha256=irvUJYUHQgQdVqC4_lgrG0FbTN1BdZqFxZr0ubVPSG4,2314
|
|
|
16
16
|
keep/data/__init__.py,sha256=C1YARrudHwK2Bmlxkh7dZlIaNJ5m5WrSTglCdG8e3T0,24
|
|
17
17
|
keep/data/system/__init__.py,sha256=Rp92_sBO3kscuWXJomo0HKeHfU-N4BgBeT3-5El0Mcg,28
|
|
18
18
|
keep/data/system/conversations.md,sha256=jE53wYSUyu5uPFNtO1Tu6w4f5QxqLei7muxLF_kZE2s,9837
|
|
19
|
-
keep/data/system/domains.md,sha256=
|
|
20
|
-
keep/data/system/now.md,sha256=
|
|
19
|
+
keep/data/system/domains.md,sha256=EHE6zU2-lx7UeLqyOTmoWl1WVlvgRq3_QnFb_EZceEY,5584
|
|
20
|
+
keep/data/system/now.md,sha256=GyQo_LizSIVKbj5q52q4ErV-nxz8rzUOlkILjgNu25s,388
|
|
21
21
|
keep/providers/__init__.py,sha256=6AwJYc6cF1ZT6BcU_6ATyeWk7MHohdVU2-ccqDSvCHU,1094
|
|
22
22
|
keep/providers/base.py,sha256=7Ug4Kj9fK2Dq4zDcZjn-GKsoZBOAlB9b-FMk969ER-g,14590
|
|
23
23
|
keep/providers/documents.py,sha256=EXeSy5i3RUL0kciIC6w3ldAEfbTIyC5fgfzC_WAI0iY,8211
|
|
@@ -26,8 +26,8 @@ keep/providers/embeddings.py,sha256=zi8GyitKexdbCJyU1nLrUhGt_zzPn3udYrrPZ5Ak8Wo,
|
|
|
26
26
|
keep/providers/llm.py,sha256=BxROKOklKbkGsHcSADPNNgWQExgSN6Bg4KPQIxVuB3U,12441
|
|
27
27
|
keep/providers/mlx.py,sha256=aNl00r9tGi5tCGj2ArYH7CmDHtL1jLjVzb1rofU1DAo,9050
|
|
28
28
|
keep/providers/summarization.py,sha256=MlVTcYipaqp2lT-QYnznp0AMuPVG36QfcTQnvY7Gb-Q,3409
|
|
29
|
-
keep_skill-0.
|
|
30
|
-
keep_skill-0.
|
|
31
|
-
keep_skill-0.
|
|
32
|
-
keep_skill-0.
|
|
33
|
-
keep_skill-0.
|
|
29
|
+
keep_skill-0.9.0.dist-info/METADATA,sha256=nzvsFdQRS659llg52q-GkZv9CPKF9jX9hx-cP5i-c6k,6051
|
|
30
|
+
keep_skill-0.9.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
31
|
+
keep_skill-0.9.0.dist-info/entry_points.txt,sha256=W8yiI4kNeW0IC8ji4EHRWrvdhFxzaqTIePUhJAJAMOo,39
|
|
32
|
+
keep_skill-0.9.0.dist-info/licenses/LICENSE,sha256=zsm0tpvtyUkevcjn5BIvs9jAho8iwxq3Ax9647AaOSg,1086
|
|
33
|
+
keep_skill-0.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|