File size: 3,734 Bytes
5b7f9b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3c7877
5b7f9b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
const User = require('../models/User');
const SkillEvent = require('../models/SkillEvent');

/**
 * Returns a human-readable relative time string for a given ISO date string.
 * e.g. "2 weeks ago", "1 month ago"
 */
function relativeTime(isoString) {
  const now = Date.now();
  const then = new Date(isoString).getTime();
  const diffMs = now - then;
  const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));

  if (diffDays < 1) return 'today';
  if (diffDays === 1) return '1 day ago';
  if (diffDays < 7) return `${diffDays} days ago`;
  if (diffDays < 14) return '1 week ago';
  if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
  if (diffDays < 60) return '1 month ago';
  return `${Math.floor(diffDays / 30)} months ago`;
}

const userProfileManager = {
  /**
   * Looks up a user by ID. If not found, creates a new user with that ID and returns it.
   * @param {string} id
   * @returns {object} user record
   */
  getOrCreateUser(id) {
    if (!id) throw new Error('id is required');
    const existing = User.findById(id);
    if (existing) return existing;
    return User.create({ id });
  },

  /**
   * Updates allowed profile fields for a user.
   * Allowed fields: name, os_type, vocabulary_level, comfort_level, accessibility_needs, onboarded
   * @param {string} id
   * @param {object} fields
   * @returns {object} updated user record
   */
  updateProfile(id, fields) {
    const allowed = ['name', 'os_type', 'vocabulary_level', 'comfort_level', 'accessibility_needs', 'onboarded'];
    const filtered = {};
    for (const key of allowed) {
      if (fields[key] !== undefined) {
        filtered[key] = fields[key];
      }
    }
    return User.update(id, filtered);
  },

  /**
   * Returns a formatted string for injecting into Claude's system prompt,
   * including user profile data and skill history.
   * @param {string} id
   * @returns {string} formatted profile string
   */
  getProfileForPrompt(id) {
    const user = User.findById(id);
    if (!user) return 'User: unknown (no profile found)';

    const skillEvents = SkillEvent.findByUserId(id);

    // Build name/age line
    const namePart = user.name || 'Unknown';
    const nameLine = `User: ${namePart}`;

    // Device line
    const deviceLine = `Device: ${user.os_type || 'unknown'}`;

    // Vocabulary level
    const vocabLine = `Vocabulary level: ${user.vocabulary_level || 'basic'}`;

    // Comfort level
    const comfortLine = `Comfort level: ${user.comfort_level !== null && user.comfort_level !== undefined ? user.comfort_level : 1}/5`;

    // Skills completed — deduplicate by skill_name, keep most recent
    const skillMap = {};
    for (const event of skillEvents) {
      if (event.status === 'completed') {
        if (!skillMap[event.skill_name] || event.practiced_at > skillMap[event.skill_name].practiced_at) {
          skillMap[event.skill_name] = event;
        }
      }
    }
    const skillList = Object.values(skillMap);
    let skillsLine;
    if (skillList.length === 0) {
      skillsLine = 'Skills completed: none yet';
    } else {
      const formatted = skillList.map(e => `${e.skill_name} (${relativeTime(e.practiced_at)})`).join(', ');
      skillsLine = `Skills completed: [${formatted}]`;
    }

    // Accessibility needs
    let accessibilityNeeds = [];
    try {
      accessibilityNeeds = JSON.parse(user.accessibility_needs || '[]');
    } catch (_) {
      accessibilityNeeds = [];
    }
    const accessibilityLine = `Accessibility: ${accessibilityNeeds.length > 0 ? accessibilityNeeds.join(', ') : 'none specified'}`;

    return [nameLine, deviceLine, vocabLine, comfortLine, skillsLine, accessibilityLine].join('\n');
  },
};

module.exports = userProfileManager;