JARVIS / tools /user_tools.py
Khanna, Videh Rakesh Rakesh
feat: add cloud DB, user profiles, daily routines & work session tracking
ea9ee18
"""User profile tools — preferences, routines, and work session management."""
from tools import tool
from user_profile import (
get_preferences,
set_preference,
get_preference,
set_user_name,
add_routine_entry,
update_routine_entry,
get_today_routine,
get_routine_history,
start_work_session,
add_work_note,
end_work_session,
get_last_work_session,
get_active_work_session,
get_work_history,
)
# ── Preferences ────────────────────────────────────────────────────
@tool(
name="set_user_preference",
description="Save a user preference (e.g. theme, language, schedule, habits). Persists to cloud.",
parameters={
"type": "object",
"properties": {
"key": {"type": "string", "description": "Preference key (e.g. 'wake_time', 'theme', 'language')"},
"value": {"type": "string", "description": "Preference value"},
},
"required": ["key", "value"],
},
)
async def set_user_preference_tool(key: str, value: str) -> str:
await set_preference(key, value)
return f"Preference saved: {key} = {value}"
@tool(
name="get_user_preference",
description="Retrieve a specific user preference",
parameters={
"type": "object",
"properties": {
"key": {"type": "string", "description": "Preference key to look up"},
},
"required": ["key"],
},
)
async def get_user_preference_tool(key: str) -> str:
val = await get_preference(key)
if val is None:
return f"No preference found for '{key}'"
return f"{key} = {val}"
@tool(
name="get_all_preferences",
description="Get all saved user preferences",
parameters={"type": "object", "properties": {}},
)
async def get_all_preferences_tool() -> str:
profile = await get_preferences()
prefs = profile.get("preferences", {})
if not prefs:
return "No preferences saved yet."
lines = [f" {k}: {v}" for k, v in prefs.items()]
name = profile.get("name", "")
header = f"User: {name}\n" if name else ""
return header + "Preferences:\n" + "\n".join(lines)
@tool(
name="set_my_name",
description="Set the user's name so JARVIS can address them personally",
parameters={
"type": "object",
"properties": {
"name": {"type": "string", "description": "User's name"},
},
"required": ["name"],
},
)
async def set_my_name_tool(name: str) -> str:
await set_user_name(name)
return f"Name set to: {name}"
# ── Daily Routines ─────────────────────────────────────────────────
@tool(
name="add_routine",
description="Add an activity to today's daily routine/schedule",
parameters={
"type": "object",
"properties": {
"activity": {"type": "string", "description": "Activity description (e.g. 'morning standup')"},
"time": {"type": "string", "description": "Time in HH:MM format (optional, defaults to now)"},
},
"required": ["activity"],
},
)
async def add_routine_tool(activity: str, time: str = "") -> str:
routine = await add_routine_entry(activity, time)
return f"Added to routine: {time or 'now'}{activity}\nTotal entries today: {len(routine.get('entries', []))}"
@tool(
name="complete_routine",
description="Mark a routine entry as completed by its index (0-based)",
parameters={
"type": "object",
"properties": {
"index": {"type": "integer", "description": "Entry index (0-based)"},
},
"required": ["index"],
},
)
async def complete_routine_tool(index: int) -> str:
routine = await update_routine_entry(index, "completed")
entries = routine.get("entries", [])
if 0 <= index < len(entries):
return f"Marked as completed: {entries[index]['activity']}"
return "Invalid entry index."
@tool(
name="show_routine",
description="Show today's daily routine / schedule",
parameters={"type": "object", "properties": {}},
)
async def show_routine_tool() -> str:
routine = await get_today_routine()
entries = routine.get("entries", [])
if not entries:
return "No routine entries for today yet."
lines = [f"Today's Routine ({routine['date']}):"]
for i, e in enumerate(entries):
icon = {"completed": "✓", "in_progress": "→", "pending": "○"}.get(
e.get("status", "pending"), "○"
)
lines.append(f" {i}. {icon} {e.get('time', '??:??')}{e['activity']}")
return "\n".join(lines)
@tool(
name="routine_history",
description="Show routine history for the past N days",
parameters={
"type": "object",
"properties": {
"days": {"type": "integer", "description": "Number of days to look back (default 7)"},
},
},
)
async def routine_history_tool(days: int = 7) -> str:
history = await get_routine_history(days)
if not history:
return "No routine history found."
lines = []
for r in history:
entries = r.get("entries", [])
completed = sum(1 for e in entries if e.get("status") == "completed")
lines.append(f"{r['date']}: {completed}/{len(entries)} completed")
for e in entries:
icon = "✓" if e.get("status") == "completed" else "○"
lines.append(f" {icon} {e.get('time', '')} {e['activity']}")
return "\n".join(lines)
# ── Work Sessions ──────────────────────────────────────────────────
@tool(
name="start_work",
description="Start tracking a new work session (auto-ends previous if active)",
parameters={
"type": "object",
"properties": {
"title": {"type": "string", "description": "What you're working on"},
"description": {"type": "string", "description": "Details about the work (optional)"},
"tags": {"type": "string", "description": "Comma-separated tags (optional)"},
},
"required": ["title"],
},
)
async def start_work_tool(title: str, description: str = "", tags: str = "") -> str:
tag_list = [t.strip() for t in tags.split(",") if t.strip()] if tags else []
session = await start_work_session(title, description, tag_list)
return f"Work session started: {title}\nSession ID: {session['id']}"
@tool(
name="work_note",
description="Add a note to the current active work session (progress, blockers, etc.)",
parameters={
"type": "object",
"properties": {
"note": {"type": "string", "description": "Note to add"},
},
"required": ["note"],
},
)
async def work_note_tool(note: str) -> str:
session = await add_work_note(note)
if session is None:
return "No active work session. Use start_work first."
return f"Note added to '{session['title']}' ({len(session['notes'])} total notes)"
@tool(
name="end_work",
description="End the current work session with an optional summary",
parameters={
"type": "object",
"properties": {
"summary": {"type": "string", "description": "Brief summary of what was accomplished"},
},
},
)
async def end_work_tool(summary: str = "") -> str:
session = await end_work_session(summary)
if session is None:
return "No active work session to end."
return f"Work session ended: {session['title']}\nDuration: {session['started_at']}{session['ended_at']}"
@tool(
name="current_work",
description="Show the currently active work session",
parameters={"type": "object", "properties": {}},
)
async def current_work_tool() -> str:
session = await get_active_work_session()
if session is None:
return "No active work session."
lines = [
f"Active Session: {session['title']}",
f" Started: {session['started_at']}",
]
if session.get("description"):
lines.append(f" Description: {session['description']}")
if session.get("tags"):
lines.append(f" Tags: {', '.join(session['tags'])}")
if session.get("notes"):
lines.append(f" Notes ({len(session['notes'])}):")
for n in session["notes"][-5:]:
lines.append(f" - {n['text']}")
return "\n".join(lines)
@tool(
name="last_work",
description="Show the most recent completed work session",
parameters={"type": "object", "properties": {}},
)
async def last_work_tool() -> str:
session = await get_last_work_session()
if session is None:
return "No work sessions recorded yet."
lines = [
f"Last Session: {session['title']} [{session.get('status', 'unknown')}]",
f" Started: {session.get('started_at', '?')}",
f" Ended: {session.get('ended_at', 'still active')}",
]
if session.get("tags"):
lines.append(f" Tags: {', '.join(session['tags'])}")
if session.get("notes"):
lines.append(f" Notes:")
for n in session["notes"][-5:]:
lines.append(f" - {n['text']}")
return "\n".join(lines)
@tool(
name="work_history",
description="Show recent work session history",
parameters={
"type": "object",
"properties": {
"limit": {"type": "integer", "description": "Number of sessions to show (default 10)"},
},
},
)
async def work_history_tool(limit: int = 10) -> str:
sessions = await get_work_history(limit)
if not sessions:
return "No work sessions recorded yet."
lines = ["Work History:"]
for s in sessions:
status = {"in_progress": "→", "completed": "✓", "paused": "⏸"}.get(
s.get("status", ""), "?"
)
tags = f" [{', '.join(s['tags'])}]" if s.get("tags") else ""
lines.append(f" {status} {s['title']}{tags}{s.get('started_at', '?')}")
return "\n".join(lines)