feat: add smart project suggestions endpoint

Added GET /api/projects/suggestions endpoint that provides intelligent
project suggestions based on session context. The endpoint:

- Takes sessionId as a required query parameter
- Retrieves session from in-memory or historical sessions
- Calculates scores for each project using multiple criteria:
  * Directory match (90 points): session workingDir === project path
  * Subdirectory match (50 points): session workingDir starts with project path
  * Used today (20 points): project lastActivity < 1 day ago
  * Used this week (10 points): project lastActivity < 7 days ago
  * Name similarity (15 points): overlap between session dir name and project name

- Returns top 3 scored suggestions with reasons
- Also returns all projects sorted alphabetically
- Filters out projects with zero scores from suggestions
- Handles missing sessions with appropriate error responses

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
uroma
2026-01-19 16:58:56 +00:00
Unverified
parent 91eb0cfc38
commit 9b603e5f75

114
server.js
View File

@@ -1199,6 +1199,120 @@ app.get('/api/recycle-bin', requireAuth, (req, res) => {
} }
}); });
// GET /api/projects/suggestions - Get smart project suggestions for a session
app.get('/api/projects/suggestions', requireAuth, (req, res) => {
try {
const { sessionId } = req.query;
// Validate sessionId parameter
if (!sessionId) {
return res.status(400).json({ error: 'sessionId parameter is required' });
}
// Get the session from in-memory or historical
let session = claudeService.sessions.get(sessionId);
if (!session) {
// Try historical sessions
const historicalSessions = claudeService.loadHistoricalSessions();
session = historicalSessions.find(s => s.id === sessionId);
}
if (!session) {
return res.status(404).json({ error: 'Session not found' });
}
// Get all active projects
const projects = db.prepare(`
SELECT id, name, icon, color, path, lastActivity
FROM projects
WHERE deletedAt IS NULL
`).all();
const sessionWorkingDir = session.workingDir || '';
const now = new Date();
// Calculate scores for each project
const scoredProjects = projects.map(project => {
let score = 0;
const reasons = [];
// 1. Directory match: session workingDir === project path (90 points)
if (sessionWorkingDir === project.path) {
score += 90;
reasons.push('Same directory');
}
// 2. Subdirectory: session workingDir startsWith project path (50 points)
// Only if not already an exact match
else if (sessionWorkingDir.startsWith(project.path + '/')) {
score += 50;
reasons.push('Subdirectory');
}
// 3. Recent use: project used today (<1 day ago) (20 points)
if (project.lastActivity) {
const lastActivityDate = new Date(project.lastActivity);
const daysSinceActivity = Math.floor((now - lastActivityDate) / (1000 * 60 * 60 * 24));
if (daysSinceActivity < 1) {
score += 20;
reasons.push('Used today');
}
// 4. Week-old use: project used this week (<7 days ago) (10 points)
else if (daysSinceActivity < 7) {
score += 10;
reasons.push(`Used ${daysSinceActivity} days ago`);
}
}
// 5. Name overlap: check if session workingDir contains project name or vice versa (15 points)
const sessionDirName = path.basename(sessionWorkingDir).toLowerCase();
const projectNameLower = project.name.toLowerCase();
if (sessionDirName.includes(projectNameLower) || projectNameLower.includes(sessionDirName)) {
score += 15;
reasons.push('Similar name');
}
return {
id: project.id,
name: project.name,
icon: project.icon,
color: project.color,
score,
reasons
};
});
// Sort by score descending
scoredProjects.sort((a, b) => b.score - a.score);
// Get top 3 suggestions
const suggestions = scoredProjects
.filter(p => p.score > 0) // Only include projects with positive scores
.slice(0, 3);
// Get all projects sorted by name
const allProjects = projects
.map(p => ({
id: p.id,
name: p.name,
icon: p.icon,
color: p.color
}))
.sort((a, b) => a.name.localeCompare(b.name));
res.json({
success: true,
suggestions,
allProjects
});
} catch (error) {
console.error('Error getting project suggestions:', error);
res.status(500).json({ error: 'Failed to get project suggestions' });
}
});
// ===== Terminal API Endpoints ===== // ===== Terminal API Endpoints =====
// Create a new terminal // Create a new terminal