Function bodies 299 total
getFriendActivity function · typescript · L33-L75 (43 LOC)src/services/activityFeed.ts
export async function getFriendActivity(limit = 50): Promise<ActivityFeedItem[]> {
const userId = await getUserId();
if (!userId) return [];
try {
// RLS handles privacy filtering. We select activities from friends + self.
const { data: rows, error } = await supabase
.from('activity_feed')
.select('id, user_id, activity_type, activity_data, created_at')
.order('created_at', { ascending: false })
.limit(limit);
if (error) throw error;
if (!rows || rows.length === 0) return [];
const userIds = [...new Set(rows.map(r => r.user_id))];
const { data: profiles, error: profErr } = await supabase
.from('public_profiles')
.select('id, username, display_name, profile_picture_url')
.in('id', userIds);
if (profErr) throw profErr;
const profileMap = new Map((profiles ?? []).map(p => [p.id, p]));
return rows.map(row => {
const p = profileMap.get(row.user_id);
return {
id: row.id,
userinsertStreakMilestone function · typescript · L79-L90 (12 LOC)src/services/activityFeed.ts
export async function insertStreakMilestone(streakDays: number): Promise<void> {
const userId = await getUserId();
if (!userId) return;
const { error } = await supabase.from('activity_feed').insert({
user_id: userId,
activity_type: 'streak_milestone',
activity_data: { streak_days: streakDays },
});
if (error) console.error('Error inserting streak milestone:', error);
}checkAndRecordMilestone function · typescript · L92-L113 (22 LOC)src/services/activityFeed.ts
export async function checkAndRecordMilestone(streakDays: number): Promise<void> {
if (!isMilestone(streakDays)) return;
const userId = await getUserId();
if (!userId) return;
const todayStart = new Date();
todayStart.setHours(0, 0, 0, 0);
// Avoid duplicate milestone for same day
const { data: existing } = await supabase
.from('activity_feed')
.select('id')
.eq('user_id', userId)
.eq('activity_type', 'streak_milestone')
.gte('created_at', todayStart.toISOString())
.limit(1);
if (existing && existing.length > 0) return;
await insertStreakMilestone(streakDays);
}getStreakEmoji function · typescript · L115-L121 (7 LOC)src/services/activityFeed.ts
export function getStreakEmoji(days: number): string {
if (days >= 60) return '\uD83D\uDCAA';
if (days >= 30) return '\uD83D\uDE80';
if (days >= 14) return '\uD83D\uDD25\uD83D\uDD25\uD83D\uDD25';
if (days >= 7) return '\uD83D\uDD25\uD83D\uDD25';
return '\uD83D\uDD25';
}getStreakText function · typescript · L123-L126 (4 LOC)src/services/activityFeed.ts
export function getStreakText(days: number): string {
if (days >= 14 && days % 7 === 0) return `hit a ${Math.floor(days / 7)}-week streak!`;
return `hit a ${days}-day streak!`;
}daysAgoISO function · typescript · L87-L91 (5 LOC)src/services/adminAnalytics.ts
function daysAgoISO(days: number): string {
const d = new Date();
d.setDate(d.getDate() - days);
return d.toISOString();
}todayStartISO function · typescript · L93-L97 (5 LOC)src/services/adminAnalytics.ts
function todayStartISO(): string {
const d = new Date();
d.setHours(0, 0, 0, 0);
return d.toISOString();
}About: code-quality intelligence by Repobility · https://repobility.com
getUsageOverview function · typescript · L102-L156 (55 LOC)src/services/adminAnalytics.ts
export async function getUsageOverview(): Promise<UsageOverview> {
const { data: all, error } = await supabase
.from('ai_usage_logs')
.select('user_id, estimated_cost_usd, total_tokens, latency_ms, success, created_at');
if (error) throw error;
const rows = all ?? [];
const now = new Date();
const todayStart = todayStartISO();
const weekStart = daysAgoISO(7);
const monthStart = daysAgoISO(30);
let totalCost = 0;
let totalTokens = 0;
let totalLatency = 0;
let errorCount = 0;
let todayCalls = 0;
let weekCalls = 0;
let monthCalls = 0;
let todayCost = 0;
let weekCost = 0;
let monthCost = 0;
const userIds = new Set<string>();
for (const r of rows) {
const cost = Number(r.estimated_cost_usd ?? 0);
totalCost += cost;
totalTokens += Number(r.total_tokens ?? 0);
totalLatency += Number(r.latency_ms ?? 0);
if (!r.success) errorCount++;
if (r.created_at >= todayStart) { todayCalls++; todayCost += cost; }
if (r.created_atgetUsageByDay function · typescript · L159-L183 (25 LOC)src/services/adminAnalytics.ts
export async function getUsageByDay(days: number = 30): Promise<DailyUsage[]> {
const since = daysAgoISO(days);
const { data, error } = await supabase
.from('ai_usage_logs')
.select('created_at, total_tokens, estimated_cost_usd, success')
.gte('created_at', since)
.order('created_at', { ascending: true });
if (error) throw error;
const byDay = new Map<string, DailyUsage>();
for (const r of data ?? []) {
const date = r.created_at.slice(0, 10);
const existing = byDay.get(date) ?? { date, calls: 0, tokens: 0, cost: 0, errors: 0 };
existing.calls++;
existing.tokens += Number(r.total_tokens ?? 0);
existing.cost += Number(r.estimated_cost_usd ?? 0);
if (!r.success) existing.errors++;
byDay.set(date, existing);
}
return Array.from(byDay.values());
}getUserUsageStats function · typescript · L186-L229 (44 LOC)src/services/adminAnalytics.ts
export async function getUserUsageStats(): Promise<UserUsage[]> {
const { data: logs, error: logsErr } = await supabase
.from('ai_usage_logs')
.select('user_id, total_tokens, estimated_cost_usd');
if (logsErr) throw logsErr;
const byUser = new Map<string, { calls: number; tokens: number; cost: number }>();
for (const r of logs ?? []) {
const uid = r.user_id;
const existing = byUser.get(uid) ?? { calls: 0, tokens: 0, cost: 0 };
existing.calls++;
existing.tokens += Number(r.total_tokens ?? 0);
existing.cost += Number(r.estimated_cost_usd ?? 0);
byUser.set(uid, existing);
}
const { data: profiles, error: profErr } = await supabase
.from('profiles')
.select('id, email, role');
if (profErr) throw profErr;
const profileMap = new Map<string, { email: string; role: string }>();
for (const p of profiles ?? []) {
profileMap.set(p.id, { email: p.email ?? 'unknown', role: p.role ?? 'byok' });
}
const result: UserUsage[] = [];
getRecentActivity function · typescript · L232-L266 (35 LOC)src/services/adminAnalytics.ts
export async function getRecentActivity(limit: number = 20): Promise<RecentLogEntry[]> {
const { data, error } = await supabase
.from('ai_usage_logs')
.select('*')
.order('created_at', { ascending: false })
.limit(limit);
if (error) throw error;
const userIds = [...new Set((data ?? []).map(r => r.user_id))];
const { data: profiles } = await supabase
.from('profiles')
.select('id, email')
.in('id', userIds);
const emailMap = new Map<string, string>();
for (const p of profiles ?? []) {
emailMap.set(p.id, p.email ?? 'unknown');
}
return (data ?? []).map(r => ({
id: r.id,
createdAt: r.created_at,
userEmail: emailMap.get(r.user_id) ?? 'unknown',
model: r.model,
tokensInput: r.tokens_input,
tokensOutput: r.tokens_output,
totalTokens: r.total_tokens,
estimatedCostUsd: Number(r.estimated_cost_usd ?? 0),
latencyMs: r.latency_ms,
success: r.success,
errorMessage: r.error_message,
toolsUsed: r.toolgetToolUsageStats function · typescript · L269-L288 (20 LOC)src/services/adminAnalytics.ts
export async function getToolUsageStats(): Promise<ToolUsageStat[]> {
const { data, error } = await supabase
.from('ai_usage_logs')
.select('tools_used')
.not('tools_used', 'is', null);
if (error) throw error;
const counts = new Map<string, number>();
for (const r of data ?? []) {
const tools: string[] = Array.isArray(r.tools_used) ? r.tools_used : [];
for (const t of tools) {
counts.set(t, (counts.get(t) ?? 0) + 1);
}
}
return Array.from(counts.entries())
.map(([tool, count]) => ({ tool, count }))
.sort((a, b) => b.count - a.count);
}getRecentErrors function · typescript · L291-L326 (36 LOC)src/services/adminAnalytics.ts
export async function getRecentErrors(limit: number = 20): Promise<RecentLogEntry[]> {
const { data, error } = await supabase
.from('ai_usage_logs')
.select('*')
.eq('success', false)
.order('created_at', { ascending: false })
.limit(limit);
if (error) throw error;
const userIds = [...new Set((data ?? []).map(r => r.user_id))];
const { data: profiles } = await supabase
.from('profiles')
.select('id, email')
.in('id', userIds);
const emailMap = new Map<string, string>();
for (const p of profiles ?? []) {
emailMap.set(p.id, p.email ?? 'unknown');
}
return (data ?? []).map(r => ({
id: r.id,
createdAt: r.created_at,
userEmail: emailMap.get(r.user_id) ?? 'unknown',
model: r.model,
tokensInput: r.tokens_input,
tokensOutput: r.tokens_output,
totalTokens: r.total_tokens,
estimatedCostUsd: Number(r.estimated_cost_usd ?? 0),
latencyMs: r.latency_ms,
success: false,
errorMessage: r.error_message,
getToolCostStats function · typescript · L329-L359 (31 LOC)src/services/adminAnalytics.ts
export async function getToolCostStats(): Promise<ToolCostStat[]> {
const { data, error } = await supabase
.from('ai_usage_logs')
.select('tools_used, estimated_cost_usd')
.not('tools_used', 'is', null);
if (error) throw error;
const map = new Map<string, { totalCost: number; count: number }>();
for (const r of data ?? []) {
const tools: string[] = Array.isArray(r.tools_used) ? r.tools_used : [];
if (tools.length === 0) continue;
const costPerTool = Number(r.estimated_cost_usd ?? 0) / tools.length;
for (const t of tools) {
const existing = map.get(t) ?? { totalCost: 0, count: 0 };
existing.totalCost += costPerTool;
existing.count++;
map.set(t, existing);
}
}
return Array.from(map.entries())
.map(([tool, { totalCost, count }]) => ({
tool,
totalCost,
avgCost: count > 0 ? totalCost / count : 0,
count,
}))
.sort((a, b) => b.avgCost - a.avgCost);
}getMonthlyAvgPerUser function · typescript · L362-L394 (33 LOC)src/services/adminAnalytics.ts
export async function getMonthlyAvgPerUser(): Promise<MonthlyUserCost[]> {
const { data, error } = await supabase
.from('ai_usage_logs')
.select('user_id, estimated_cost_usd, created_at')
.order('created_at', { ascending: true });
if (error) throw error;
const MONTH_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const byMonth = new Map<string, { cost: number; users: Set<string> }>();
for (const r of data ?? []) {
const d = new Date(r.created_at);
const key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
const existing = byMonth.get(key) ?? { cost: 0, users: new Set<string>() };
existing.cost += Number(r.estimated_cost_usd ?? 0);
if (r.user_id) existing.users.add(r.user_id);
byMonth.set(key, existing);
}
return Array.from(byMonth.entries()).map(([month, { cost, users }]) => {
const [y, m] = month.split('-');
const monthIdx = parseInt(m, 10) - 1;
return Repobility · severity-and-effort ranking · https://repobility.com
getActiveUserCounts function · typescript · L397-L427 (31 LOC)src/services/adminAnalytics.ts
export async function getActiveUserCounts(): Promise<ActiveUserCounts> {
const { data, error } = await supabase
.from('ai_usage_logs')
.select('user_id, created_at');
if (error) throw error;
const todayStart = todayStartISO();
const weekStart = daysAgoISO(7);
const monthStart = daysAgoISO(30);
const dailyUsers = new Set<string>();
const weeklyUsers = new Set<string>();
const monthlyUsers = new Set<string>();
const allUsers = new Set<string>();
for (const r of data ?? []) {
if (!r.user_id) continue;
allUsers.add(r.user_id);
if (r.created_at >= todayStart) dailyUsers.add(r.user_id);
if (r.created_at >= weekStart) weeklyUsers.add(r.user_id);
if (r.created_at >= monthStart) monthlyUsers.add(r.user_id);
}
return {
daily: dailyUsers.size,
weekly: weeklyUsers.size,
monthly: monthlyUsers.size,
total: allUsers.size,
};
}getRoleDistribution function · typescript · L430-L446 (17 LOC)src/services/adminAnalytics.ts
export async function getRoleDistribution(): Promise<RoleCount[]> {
const { data, error } = await supabase
.from('profiles')
.select('role');
if (error) throw error;
const counts = new Map<string, number>();
for (const r of data ?? []) {
const role = r.role ?? 'byok';
counts.set(role, (counts.get(role) ?? 0) + 1);
}
return Array.from(counts.entries())
.map(([role, count]) => ({ role, count }))
.sort((a, b) => b.count - a.count);
}updateUserRole function · typescript · L449-L459 (11 LOC)src/services/adminAnalytics.ts
export async function updateUserRole(
userId: string,
newRole: string,
): Promise<void> {
const { error } = await supabase
.from('profiles')
.update({ role: newRole, updated_at: new Date().toISOString() })
.eq('id', userId);
if (error) throw error;
}predictGoalDate function · typescript · L73-L173 (101 LOC)src/services/analytics.ts
export async function predictGoalDate(): Promise<GoalProjection | null> {
const [profile, weightLogs] = await Promise.all([
loadUserProfile(),
getWeightLogs(0),
]);
if (!profile) return null;
const targetKg = profile.targetWeightKg;
const currentKg = profile.weightKg;
const currentLbs = Math.round(kgToLbs(currentKg) * 10) / 10;
const goalLbs = targetKg ? Math.round(kgToLbs(targetKg) * 10) / 10 : null;
if (weightLogs.length < 5) {
return {
currentLbs,
goalLbs: goalLbs ?? currentLbs,
weeklyRateLbs: 0,
estimatedDate: null,
estimatedWeeks: null,
status: 'insufficient_data',
confidence: 'low',
};
}
// Convert weight logs to (daysSinceFirst, lbs) for linear regression
const firstTime = new Date(weightLogs[0].loggedAt).getTime();
const xs = weightLogs.map(w => (new Date(w.loggedAt).getTime() - firstTime) / (1000 * 60 * 60 * 24));
const ys = weightLogs.map(w => kgToLbs(w.weightKg));
const { slope: dailyRcalculateAdherencePatterns function · typescript · L177-L242 (66 LOC)src/services/analytics.ts
export async function calculateAdherencePatterns(): Promise<DayOfWeekPattern[]> {
const [profile, summaries] = await Promise.all([
loadUserProfile(),
getDailySummaries(0),
]);
if (summaries.length < 7) return [];
const goals = profile ? estimateDailyGoals(profile) : null;
// Group summaries by day of week
const buckets: Map<number, DailySummary[]> = new Map();
for (let i = 0; i < 7; i++) buckets.set(i, []);
for (const s of summaries) {
const dow = new Date(s.date + 'T12:00:00').getDay();
buckets.get(dow)!.push(s);
}
return Array.from(buckets.entries()).map(([dow, days]) => {
const n = days.length;
if (n === 0) {
return {
day: DAY_NAMES[dow],
dayIndex: dow,
avgCalories: 0,
avgProtein: 0,
avgCarbs: 0,
avgFat: 0,
calorieAdherenceRate: 0,
proteinAdherenceRate: 0,
sampleSize: 0,
};
}
const avgCal = days.reduce((a, d) => a + d.calories, 0) / n;
confindCorrelations function · typescript · L246-L372 (127 LOC)src/services/analytics.ts
export async function findCorrelations(): Promise<Correlation[]> {
const [profile, summaries] = await Promise.all([
loadUserProfile(),
getDailySummaries(0),
]);
if (summaries.length < 7) return [];
const goals = profile ? estimateDailyGoals(profile) : null;
const correlations: Correlation[] = [];
// --- Weekend vs Weekday adherence ---
if (goals) {
const calLo = goals.calories * 0.9;
const calHi = goals.calories * 1.1;
const weekdayDays: DailySummary[] = [];
const weekendDays: DailySummary[] = [];
for (const s of summaries) {
const dow = new Date(s.date + 'T12:00:00').getDay();
if (dow === 0 || dow === 6) weekendDays.push(s);
else weekdayDays.push(s);
}
if (weekdayDays.length >= 3 && weekendDays.length >= 2) {
const wdHit = weekdayDays.filter(d => d.calories >= calLo && d.calories <= calHi).length;
const weHit = weekendDays.filter(d => d.calories >= calLo && d.calories <= calHi).length;
const wdgetAnalytics function · typescript · L376-L384 (9 LOC)src/services/analytics.ts
export async function getAnalytics(): Promise<AnalyticsResult> {
const [goalProjection, dayOfWeekPatterns, correlations] = await Promise.all([
predictGoalDate(),
calculateAdherencePatterns(),
findCorrelations(),
]);
return { goalProjection, dayOfWeekPatterns, correlations };
}getUserId function · typescript · L8-L11 (4 LOC)src/services/claudeTools.ts
async function getUserId(): Promise<string | null> {
const { data: { user } } = await supabase.auth.getUser();
return user?.id ?? null;
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
toLocalDateString function · typescript · L13-L18 (6 LOC)src/services/claudeTools.ts
function toLocalDateString(date: Date): string {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}todayStr function · typescript · L20-L22 (3 LOC)src/services/claudeTools.ts
function todayStr(): string {
return toLocalDateString(new Date());
}localMidnightDaysAgo function · typescript · L25-L30 (6 LOC)src/services/claudeTools.ts
function localMidnightDaysAgo(n: number): Date {
const d = new Date();
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() - n);
return d;
}applyDateFilter function · typescript · L33-L43 (11 LOC)src/services/claudeTools.ts
function applyDateFilter(
query: any,
column: string,
days: number,
): any {
if (days > 0) {
const start = localMidnightDaysAgo(days - 1);
return query.gte(column, start.toISOString());
}
return query; // 0 = all time, no filter
}getToolsForIntent function · typescript · L286-L302 (17 LOC)src/services/claudeTools.ts
export function getToolsForIntent(intent: ToolIntent): ToolDefinition[] {
switch (intent) {
case 'meal_log':
// Claude already has daily context in the system prompt — no tools needed for meal logging
return [];
case 'data_query':
// Full data toolset
return TOOL_DEFINITIONS.filter(t => DATA_QUERY_TOOL_NAMES.has(t.name) || CORE_TOOL_NAMES.has(t.name));
case 'meal_suggestion':
// Suggestion-relevant tools
return TOOL_DEFINITIONS.filter(t => SUGGESTION_TOOL_NAMES.has(t.name));
case 'general':
default:
// Unknown intent — include everything
return [...TOOL_DEFINITIONS];
}
}getDailyTotals function · typescript · L306-L355 (50 LOC)src/services/claudeTools.ts
async function getDailyTotals(input: { date: string }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
const start = new Date(input.date + 'T00:00:00');
const end = new Date(input.date + 'T23:59:59.999');
const { data: rows, error } = await supabase
.from('daily_logs')
.select('calories, protein_g, carbs_g, fat_g')
.eq('user_id', userId)
.gte('logged_at', start.toISOString())
.lte('logged_at', end.toISOString());
if (error) return JSON.stringify({ error: error.message });
const today = todayStr();
if (!rows || rows.length === 0) {
return JSON.stringify({
today_is: today,
queried_date: input.date,
is_today: input.date === today,
calories: 0,
protein_g: 0,
carbs_g: 0,
fat_g: 0,
meal_count: 0,
note: 'No meals logged on this date.',
});
}
const totals = rows.reduce(
(acc, r) => ({
calories: acc.caloriegetMealsByDateRange function · typescript · L357-L396 (40 LOC)src/services/claudeTools.ts
async function getMealsByDateRange(input: { start_date: string; end_date: string }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
const start = new Date(input.start_date + 'T00:00:00');
const end = new Date(input.end_date + 'T23:59:59.999');
const { data: rows, error } = await supabase
.from('daily_logs')
.select('meal_name, calories, protein_g, carbs_g, fat_g, logged_at')
.eq('user_id', userId)
.gte('logged_at', start.toISOString())
.lte('logged_at', end.toISOString())
.order('logged_at', { ascending: true });
if (error) return JSON.stringify({ error: error.message });
const today = todayStr();
const meals = (rows ?? []).map(r => {
const date = toLocalDateString(new Date(r.logged_at));
return {
date,
is_today: date === today,
name: r.meal_name,
calories: r.calories || 0,
protein_g: Number(r.protein_g) || 0,
carbs_g: Number(getPeriodSummary function · typescript · L398-L468 (71 LOC)src/services/claudeTools.ts
async function getPeriodSummary(input: { days: number }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
let query = supabase
.from('daily_logs')
.select('calories, protein_g, carbs_g, fat_g, logged_at')
.eq('user_id', userId)
.order('logged_at', { ascending: true });
query = applyDateFilter(query, 'logged_at', input.days);
const { data: rows, error } = await query;
if (error) return JSON.stringify({ error: error.message });
const today = todayStr();
if (!rows || rows.length === 0) {
return JSON.stringify({
today_is: today,
period_days: input.days === 0 ? 'all_time' : input.days,
days_tracked: 0,
total_meals: 0,
note: 'No data for this period.',
});
}
const byDay = new Map<string, { calories: number; protein: number; carbs: number; fat: number; meals: number }>();
for (const r of rows) {
const key = toLocalDateString(new Date(r.lHi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
calculateAdherenceRate function · typescript · L470-L509 (40 LOC)src/services/claudeTools.ts
async function calculateAdherenceRate(input: { days: number; calorie_goal: number }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
let query = supabase
.from('daily_logs')
.select('calories, logged_at')
.eq('user_id', userId);
query = applyDateFilter(query, 'logged_at', input.days);
const { data: rows, error } = await query;
if (error) return JSON.stringify({ error: error.message });
const byDay = new Map<string, number>();
for (const r of (rows ?? [])) {
const key = toLocalDateString(new Date(r.logged_at));
byDay.set(key, (byDay.get(key) ?? 0) + (r.calories || 0));
}
const lo = input.calorie_goal * 0.9;
const hi = input.calorie_goal * 1.1;
let hitDays = 0;
for (const cal of byDay.values()) {
if (cal >= lo && cal <= hi) hitDays++;
}
const daysTracked = byDay.size;
return JSON.stringify({
today_is: todayStr(),
period_days: input.days === 0 ? getWeightTrend function · typescript · L511-L549 (39 LOC)src/services/claudeTools.ts
async function getWeightTrend(input: { days: number }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
let query = supabase
.from('weight_logs')
.select('weight_kg, logged_at')
.eq('user_id', userId)
.order('logged_at', { ascending: true });
query = applyDateFilter(query, 'logged_at', input.days);
const { data: rows, error } = await query;
if (error) return JSON.stringify({ error: error.message });
const today = todayStr();
if (!rows || rows.length === 0) {
return JSON.stringify({ today_is: today, entries: [], note: 'No weight data found.' });
}
const entries = rows.map(r => ({
date: toLocalDateString(new Date(r.logged_at)),
weight_lbs: Math.round(kgToLbs(Number(r.weight_kg)) * 10) / 10,
}));
const first = entries[0];
const last = entries[entries.length - 1];
const changeLbs = Math.round((last.weight_lbs - first.weight_lbs) * 10) / 10;
return JSON.ssearchSavedMeals function · typescript · L551-L578 (28 LOC)src/services/claudeTools.ts
async function searchSavedMeals(input: { query: string }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
let query = supabase
.from('saved_meals')
.select('meal_name, calories, protein_g, carbs_g, fat_g, notes')
.eq('user_id', userId)
.order('created_at', { ascending: false });
if (input.query.trim()) {
query = query.ilike('meal_name', `%${input.query.trim()}%`);
}
const { data: rows, error } = await query;
if (error) return JSON.stringify({ error: error.message });
const meals = (rows ?? []).map(r => ({
name: r.meal_name,
calories: r.calories || 0,
protein_g: Number(r.protein_g) || 0,
carbs_g: Number(r.carbs_g) || 0,
fat_g: Number(r.fat_g) || 0,
notes: r.notes,
}));
return JSON.stringify({ query: input.query, result_count: meals.length, meals });
}getAnalyticsTool function · typescript · L580-L583 (4 LOC)src/services/claudeTools.ts
async function getAnalyticsTool(): Promise<string> {
const result = await getAnalytics();
return JSON.stringify({ today_is: todayStr(), ...result });
}getFrequentMeals function · typescript · L585-L621 (37 LOC)src/services/claudeTools.ts
async function getFrequentMeals(input: { limit?: number }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
const { data: rows, error } = await supabase
.from('daily_logs')
.select('meal_name, calories, protein_g, carbs_g, fat_g')
.eq('user_id', userId);
if (error) return JSON.stringify({ error: error.message });
const freq = new Map<string, { count: number; calories: number; protein: number; carbs: number; fat: number }>();
for (const r of (rows ?? [])) {
const key = (r.meal_name ?? '').toLowerCase().trim();
const existing = freq.get(key) ?? { count: 0, calories: 0, protein: 0, carbs: 0, fat: 0 };
existing.count += 1;
existing.calories += r.calories || 0;
existing.protein += Number(r.protein_g) || 0;
existing.carbs += Number(r.carbs_g) || 0;
existing.fat += Number(r.fat_g) || 0;
freq.set(key, existing);
}
const sorted = Array.from(freq.entries())
searchMealsByMacro function · typescript · L623-L658 (36 LOC)src/services/claudeTools.ts
async function searchMealsByMacro(input: { macro: string; min_amount?: number; max_amount?: number; limit?: number }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
const colMap: Record<string, string> = { calories: 'calories', protein: 'protein_g', carbs: 'carbs_g', fat: 'fat_g' };
const col = colMap[input.macro];
if (!col) return JSON.stringify({ error: `Unknown macro: ${input.macro}` });
let query = supabase
.from('daily_logs')
.select('meal_name, calories, protein_g, carbs_g, fat_g, logged_at')
.eq('user_id', userId)
.order(col, { ascending: false });
if (input.min_amount != null && input.min_amount > 0) {
query = query.gte(col, input.min_amount);
}
if (input.max_amount != null) {
query = query.lte(col, input.max_amount);
}
const { data: rows, error } = await query.limit(input.limit ?? 10);
if (error) return JSON.stringify({ error: error.message });
constgetBestAdherenceDays function · typescript · L660-L702 (43 LOC)src/services/claudeTools.ts
async function getBestAdherenceDays(input: { calorie_goal: number; limit?: number }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
const { data: rows, error } = await supabase
.from('daily_logs')
.select('meal_name, calories, protein_g, carbs_g, fat_g, logged_at')
.eq('user_id', userId)
.order('logged_at', { ascending: true });
if (error) return JSON.stringify({ error: error.message });
const byDay = new Map<string, { calories: number; protein: number; carbs: number; fat: number; meals: string[] }>();
for (const r of (rows ?? [])) {
const key = toLocalDateString(new Date(r.logged_at));
const existing = byDay.get(key) ?? { calories: 0, protein: 0, carbs: 0, fat: 0, meals: [] };
existing.calories += r.calories || 0;
existing.protein += Number(r.protein_g) || 0;
existing.carbs += Number(r.carbs_g) || 0;
existing.fat += Number(r.fat_g) || 0;
existing.meals.pgetMealHistory function · typescript · L704-L733 (30 LOC)src/services/claudeTools.ts
async function getMealHistory(input: { meal_name: string }): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
const { data: rows, error } = await supabase
.from('daily_logs')
.select('meal_name, calories, protein_g, carbs_g, fat_g, logged_at')
.eq('user_id', userId)
.ilike('meal_name', `%${input.meal_name.trim()}%`)
.order('logged_at', { ascending: false });
if (error) return JSON.stringify({ error: error.message });
const today = todayStr();
const entries = (rows ?? []).map(r => ({
name: r.meal_name,
calories: r.calories || 0,
protein_g: Number(r.protein_g) || 0,
carbs_g: Number(r.carbs_g) || 0,
fat_g: Number(r.fat_g) || 0,
date: toLocalDateString(new Date(r.logged_at)),
}));
return JSON.stringify({
today_is: today,
search_term: input.meal_name,
times_found: entries.length,
entries,
});
}About: code-quality intelligence by Repobility · https://repobility.com
getRemainingMacros function · typescript · L735-L777 (43 LOC)src/services/claudeTools.ts
async function getRemainingMacros(): Promise<string> {
const userId = await getUserId();
if (!userId) return JSON.stringify({ error: 'Not authenticated' });
const profile = await loadUserProfile();
if (!profile) return JSON.stringify({ error: 'No profile found' });
const goals = estimateDailyGoals(profile);
const today = todayStr();
const startOfDay = new Date();
startOfDay.setHours(0, 0, 0, 0);
const { data: rows, error } = await supabase
.from('daily_logs')
.select('calories, protein_g, carbs_g, fat_g')
.eq('user_id', userId)
.gte('logged_at', startOfDay.toISOString());
if (error) return JSON.stringify({ error: error.message });
const consumed = (rows ?? []).reduce(
(acc, r) => ({
calories: acc.calories + (r.calories || 0),
protein_g: acc.protein_g + (Number(r.protein_g) || 0),
carbs_g: acc.carbs_g + (Number(r.carbs_g) || 0),
fat_g: acc.fat_g + (Number(r.fat_g) || 0),
}),
{ calories: 0, protein_g: 0, carbsexecuteTool function · typescript · L781-L810 (30 LOC)src/services/claudeTools.ts
export async function executeTool(name: string, input: Record<string, unknown>): Promise<string> {
switch (name) {
case 'get_daily_totals':
return getDailyTotals(input as { date: string });
case 'get_meals_by_date_range':
return getMealsByDateRange(input as { start_date: string; end_date: string });
case 'get_period_summary':
return getPeriodSummary(input as { days: number });
case 'calculate_adherence_rate':
return calculateAdherenceRate(input as { days: number; calorie_goal: number });
case 'get_weight_trend':
return getWeightTrend(input as { days: number });
case 'search_saved_meals':
return searchSavedMeals(input as { query: string });
case 'get_analytics':
return getAnalyticsTool();
case 'get_frequent_meals':
return getFrequentMeals(input as { limit?: number });
case 'search_meals_by_macro':
return searchMealsByMacro(input as { macro: string; min_amount?: number; max_amount?: number; limit?toLocalDateString function · typescript · L35-L40 (6 LOC)src/services/claude.ts
function toLocalDateString(date: Date): string {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}normalizeMediaType function · typescript · L52-L57 (6 LOC)src/services/claude.ts
function normalizeMediaType(raw: string): string {
const lower = raw.toLowerCase().trim();
if (VALID_MEDIA_TYPES.has(lower)) return lower;
if (lower === 'image/jpg') return 'image/jpeg';
return 'image/jpeg';
}stripDataUriPrefix function · typescript · L60-L63 (4 LOC)src/services/claude.ts
function stripDataUriPrefix(data: string): string {
const commaIdx = data.indexOf(',');
return commaIdx !== -1 ? data.slice(commaIdx + 1) : data;
}estimateCost function · typescript · L81-L95 (15 LOC)src/services/claude.ts
function estimateCost(
inputTokens: number,
outputTokens: number,
cacheReadTokens: number = 0,
cacheCreationTokens: number = 0,
): number {
// Cache-read tokens are NOT counted in input_tokens by the API,
// so we cost them separately at the discounted rate.
return (
(inputTokens / 1_000_000) * INPUT_COST_PER_MILLION +
(outputTokens / 1_000_000) * OUTPUT_COST_PER_MILLION +
(cacheReadTokens / 1_000_000) * CACHE_READ_COST_PER_MILLION +
(cacheCreationTokens / 1_000_000) * CACHE_WRITE_COST_PER_MILLION
);
}logAiUsage function · typescript · L97-L136 (40 LOC)src/services/claude.ts
async function logAiUsage(params: {
model: string;
tokensInput: number;
tokensOutput: number;
cacheReadTokens?: number;
cacheCreationTokens?: number;
latencyMs: number;
success: boolean;
errorMessage?: string | null;
toolsUsed?: string[];
}): Promise<void> {
try {
const { data: { user } } = await supabase.auth.getUser();
if (!user) return;
const cacheRead = params.cacheReadTokens ?? 0;
const cacheCreation = params.cacheCreationTokens ?? 0;
const totalTokens = params.tokensInput + params.tokensOutput + cacheRead;
const cost = estimateCost(params.tokensInput, params.tokensOutput, cacheRead, cacheCreation);
if (cacheRead > 0 || cacheCreation > 0) {
console.log(`[AI Cache] read=${cacheRead} creation=${cacheCreation} input=${params.tokensInput} output=${params.tokensOutput}`);
}
await supabase.from('ai_usage_logs').insert({
user_id: user.id,
model: params.model,
tokens_input: params.tokensInput,
tokens_estimateTokens function · typescript · L141-L143 (3 LOC)src/services/claude.ts
function estimateTokens(text: string): number {
return Math.ceil(text.length / 4);
}Repobility · severity-and-effort ranking · https://repobility.com
estimateMessageTokens function · typescript · L145-L151 (7 LOC)src/services/claude.ts
function estimateMessageTokens(messages: ChatMessage[]): number {
return messages.reduce((sum, m) => {
let tokens = estimateTokens(m.content);
if (m.imageBase64) tokens += 1600; // ~1600 tokens for a typical image
return sum + tokens;
}, 0);
}windowMessages function · typescript · L159-L162 (4 LOC)src/services/claude.ts
export function windowMessages(
messages: ChatMessage[],
systemPromptTokens: number,
): { kept: ChatMessage[]; dropped: ChatMessage[] } {summarizeDroppedMessages function · typescript · L187-L222 (36 LOC)src/services/claude.ts
export async function summarizeDroppedMessages(
droppedMessages: ChatMessage[],
existingSummary: string | null,
apiKey: string,
): Promise<string> {
const transcript = droppedMessages
.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content.slice(0, 500)}`)
.join('\n');
const prompt = existingSummary
? `Here is an existing summary of an earlier part of the conversation:\n\n${existingSummary}\n\nHere are additional messages that need to be incorporated into the summary:\n\n${transcript}\n\nProduce an updated, concise summary (max 300 words) that captures all key facts, preferences, decisions, and meal/nutrition details mentioned. Focus on information the user would expect you to remember.`
: `Here is the beginning of a conversation between a user and their AI nutrition coach:\n\n${transcript}\n\nProduce a concise summary (max 300 words) that captures all key facts, preferences, decisions, and meal/nutrition details mentioned. Focus on information th