@hyperspell/openclaw-hyperspell 0.7.1 → 0.8.0
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.
- package/README.md +0 -64
- package/index.ts +0 -7
- package/package.json +5 -7
- package/commands/wine.ts +0 -164
- package/lib/run-script.ts +0 -32
- package/sommeliagent/references/cross-domain-mappings.md +0 -63
- package/sommeliagent/scripts/auth.py +0 -222
- package/sommeliagent/scripts/history.py +0 -72
- package/sommeliagent/scripts/rate.py +0 -76
- package/sommeliagent/scripts/recommend.py +0 -777
- package/sommeliagent/scripts/test_recommend.py +0 -459
- package/sommeliagent/scripts/wine_db.py +0 -3224
- package/tools/sommelier.ts +0 -254
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# requires-python = ">=3.11"
|
|
3
|
-
# dependencies = []
|
|
4
|
-
# ///
|
|
5
|
-
"""
|
|
6
|
-
View SommeliAgent recommendation history and ratings.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import sys
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
|
|
13
|
-
CONFIG_DIR = Path.home() / ".sommeliagent"
|
|
14
|
-
RATINGS_FILE = CONFIG_DIR / "ratings.json"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def main():
|
|
18
|
-
if not RATINGS_FILE.exists():
|
|
19
|
-
print("No ratings yet. Try some wines and rate them!")
|
|
20
|
-
sys.exit(0)
|
|
21
|
-
|
|
22
|
-
try:
|
|
23
|
-
ratings = json.loads(RATINGS_FILE.read_text())
|
|
24
|
-
except (json.JSONDecodeError, OSError) as e:
|
|
25
|
-
print(f"Error reading ratings file: {e}", file=sys.stderr)
|
|
26
|
-
print(f"You can delete {RATINGS_FILE} to reset.", file=sys.stderr)
|
|
27
|
-
sys.exit(1)
|
|
28
|
-
if not ratings:
|
|
29
|
-
print("No ratings yet. Try some wines and rate them!")
|
|
30
|
-
sys.exit(0)
|
|
31
|
-
|
|
32
|
-
print(f"SommeliAgent — Rating History ({len(ratings)} wines rated)")
|
|
33
|
-
print("=" * 50)
|
|
34
|
-
print()
|
|
35
|
-
|
|
36
|
-
label = {1: "hated it", 2: "not great", 3: "okay", 4: "liked it", 5: "loved it"}
|
|
37
|
-
|
|
38
|
-
for i, entry in enumerate(ratings, 1):
|
|
39
|
-
wine_id = entry.get("wine_id", "unknown")
|
|
40
|
-
rating = max(1, min(5, entry.get("rating", 3)))
|
|
41
|
-
stars = "★" * rating + "☆" * (5 - rating)
|
|
42
|
-
print(f"{i}. {wine_id}")
|
|
43
|
-
print(f" {stars} ({label.get(rating, 'unknown')})")
|
|
44
|
-
if entry.get("notes"):
|
|
45
|
-
print(f" \"{entry['notes']}\"")
|
|
46
|
-
if entry.get("timestamp"):
|
|
47
|
-
print(f" Rated: {entry['timestamp'][:10]}")
|
|
48
|
-
print()
|
|
49
|
-
|
|
50
|
-
# Summary stats
|
|
51
|
-
avg = sum(r.get("rating", 3) for r in ratings) / len(ratings)
|
|
52
|
-
loved = sum(1 for r in ratings if r.get("rating", 3) >= 4)
|
|
53
|
-
print(f"Average rating: {avg:.1f}/5")
|
|
54
|
-
print(f"Wines you loved: {loved}/{len(ratings)}")
|
|
55
|
-
|
|
56
|
-
# Tag preferences
|
|
57
|
-
tag_scores: dict[str, list[int]] = {}
|
|
58
|
-
for r in ratings:
|
|
59
|
-
for tag in r.get("tags", []):
|
|
60
|
-
tag_scores.setdefault(tag, []).append(r.get("rating", 3))
|
|
61
|
-
|
|
62
|
-
if tag_scores:
|
|
63
|
-
print("\nYour taste profile (from ratings):")
|
|
64
|
-
sorted_tags = sorted(tag_scores.items(), key=lambda x: -sum(x[1]) / len(x[1]))
|
|
65
|
-
for tag, scores in sorted_tags[:8]:
|
|
66
|
-
avg_tag = sum(scores) / len(scores)
|
|
67
|
-
bar = "★" * round(avg_tag) + "☆" * (5 - round(avg_tag))
|
|
68
|
-
print(f" {tag:<20} {bar} ({avg_tag:.1f} avg, {len(scores)} wines)")
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if __name__ == "__main__":
|
|
72
|
-
main()
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# /// script
|
|
2
|
-
# requires-python = ">=3.11"
|
|
3
|
-
# dependencies = []
|
|
4
|
-
# ///
|
|
5
|
-
"""
|
|
6
|
-
Rate a wine recommendation to improve future suggestions.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import argparse
|
|
10
|
-
import json
|
|
11
|
-
import sys
|
|
12
|
-
from datetime import datetime, timezone
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
|
|
15
|
-
from wine_db import WINE_DB
|
|
16
|
-
|
|
17
|
-
CONFIG_DIR = Path.home() / ".sommeliagent"
|
|
18
|
-
RATINGS_FILE = CONFIG_DIR / "ratings.json"
|
|
19
|
-
|
|
20
|
-
# Wine ID → tags mapping (derived from wine_db)
|
|
21
|
-
WINE_TAGS = {w.id: w.tags for w in WINE_DB}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def load_ratings() -> list[dict]:
|
|
25
|
-
if RATINGS_FILE.exists():
|
|
26
|
-
try:
|
|
27
|
-
return json.loads(RATINGS_FILE.read_text())
|
|
28
|
-
except (OSError, json.JSONDecodeError) as e:
|
|
29
|
-
print(f"Warning: could not read {RATINGS_FILE}: {e}", file=sys.stderr)
|
|
30
|
-
return []
|
|
31
|
-
return []
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def save_ratings(ratings: list[dict]) -> None:
|
|
35
|
-
import stat
|
|
36
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
37
|
-
tmp = RATINGS_FILE.with_suffix(".tmp")
|
|
38
|
-
tmp.write_text(json.dumps(ratings, indent=2))
|
|
39
|
-
tmp.chmod(stat.S_IRUSR | stat.S_IWUSR)
|
|
40
|
-
tmp.replace(RATINGS_FILE)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def main():
|
|
44
|
-
parser = argparse.ArgumentParser(description="Rate a wine recommendation")
|
|
45
|
-
parser.add_argument("--wine-id", required=True, help="Wine ID from recommendation")
|
|
46
|
-
parser.add_argument("--rating", type=int, required=True, choices=[1, 2, 3, 4, 5], help="Rating 1-5")
|
|
47
|
-
parser.add_argument("--notes", default="", help="Optional tasting notes")
|
|
48
|
-
args = parser.parse_args()
|
|
49
|
-
|
|
50
|
-
tags = WINE_TAGS.get(args.wine_id)
|
|
51
|
-
if tags is None:
|
|
52
|
-
valid_ids = ", ".join(sorted(WINE_TAGS.keys()))
|
|
53
|
-
print(f"Error: Unknown wine ID '{args.wine_id}'.", file=sys.stderr)
|
|
54
|
-
print(f"Valid IDs: {valid_ids}", file=sys.stderr)
|
|
55
|
-
sys.exit(1)
|
|
56
|
-
|
|
57
|
-
ratings = load_ratings()
|
|
58
|
-
ratings.append({
|
|
59
|
-
"wine_id": args.wine_id,
|
|
60
|
-
"rating": args.rating,
|
|
61
|
-
"notes": args.notes,
|
|
62
|
-
"tags": tags,
|
|
63
|
-
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
64
|
-
})
|
|
65
|
-
save_ratings(ratings)
|
|
66
|
-
|
|
67
|
-
label = {1: "hated it", 2: "not great", 3: "okay", 4: "liked it", 5: "loved it"}
|
|
68
|
-
print(f"Rated {args.wine_id}: {args.rating}/5 ({label[args.rating]})")
|
|
69
|
-
if args.notes:
|
|
70
|
-
print(f"Notes: \"{args.notes}\"")
|
|
71
|
-
print(f"Total ratings: {len(ratings)}")
|
|
72
|
-
print("Future recommendations will factor this in.")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if __name__ == "__main__":
|
|
76
|
-
main()
|