Files
SuperCharged-Claude-Code-Up…/docs/plans/2025-01-19-project-session-organization-design.md
uroma 0dd2083556 Initial commit: Obsidian Web Interface for Claude Code
- Full IDE with terminal integration using xterm.js
- Session management with local and web sessions
- HTML preview functionality
- Multi-terminal support with session picker

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 16:29:44 +00:00

7.3 KiB

Project and Session Organization Design

Date: 2025-01-19 Status: Approved Author: Claude (with user collaboration)

Overview

Introduce persistent projects as first-class entities that contain multiple sessions, with intelligent assignment, reorganization, and soft-delete capabilities.

Architecture

Data Model

Projects Collection

{
  _id: ObjectId,
  name: "My API Project",
  description: "REST API development",
  icon: "🚀",
  color: "#4a9eff",
  path: "/home/uroma/api",
  sessionIds: [ObjectId, ...],
  createdAt: Date,
  lastActivity: Date,
  deletedAt: Date | null  // null = active, Date = in recycle bin
}

Sessions Collection (Updated)

{
  ...
  projectId: ObjectId | null,  // null = unassigned
  deletedAt: Date | null
}

API Endpoints

Method Endpoint Description
GET /api/projects List active projects
POST /api/projects Create project
PUT /api/projects/:id Update project
DELETE /api/projects/:id Soft delete (move to recycle bin)
POST /api/projects/:id/restore Restore from bin
DELETE /api/projects/:id/permanent Permanent delete
GET /api/recycle-bin List deleted items
POST /api/sessions/:id/move Move session to different project

UI Components

Projects Page (/projects)

  • Header: Title, "+ New Project" button, search bar
  • Project Grid: Cards with icon, name, description, path, session count, last activity
  • Context Menu: Edit, move to recycle bin
  • Empty State: "No projects yet" with CTA

Enhanced Landing Page (/claude/)

Projects as top-level, sessions nested inside:

  • Collapsible project sections
  • "Unassigned Sessions" section at bottom
  • Session cards show project badge
  • Right-click for context menu

Session Context Menu

┌─────────────────────────┐
│ Open in IDE             │
│ ─────────────────────── │
│ Move to Project  ▶      │
│   ├── 🚀 My API (🎯 95%)│
│   ├── 📱 Mobile App     │
│   ├── ──────────────────│
│   └── Show All Projects │
│ ─────────────────────── │
│ Duplicate               │
│ Delete                  │
└─────────────────────────┘

Recycle Bin

  • Accessible from projects page header
  • Shows deleted projects with faded sessions
  • "Restore" and "Delete Permanently" buttons per item

Smart Assignment Algorithm

Auto-Assignment

When creating a session from the IDE project selector:

  1. User selects project → projectId stored
  2. Session added to project's sessionIds array
  3. Project lastActivity updated

Smart Suggestions

Calculated when moving sessions:

function getSuggestions(session, allProjects) {
  const suggestions = [];

  for (const project of allProjects) {
    let score = 0;
    let reasons = [];

    // Directory matching (high weight)
    if (session.workingDir === project.path) {
      score += 90;
      reasons.push('Same directory');
    } else if (session.workingDir?.startsWith(project.path)) {
      score += 50;
      reasons.push('Subdirectory');
    }

    // Recency (medium weight)
    const daysSinceActivity = (Date.now() - project.lastActivity) / (1000 * 60 * 60 * 24);
    if (daysSinceActivity < 1) {
      score += 20;
      reasons.push('Used today');
    } else if (daysSinceActivity < 7) {
      score += 10;
      reasons.push(`Used ${Math.floor(daysSinceActivity)} days ago`);
    }

    // Name similarity (low weight)
    if (session.name?.includes(project.name) || project.name.includes(session.name)) {
      score += 15;
      reasons.push('Similar name');
    }

    if (score > 0) {
      suggestions.push({ project, score, reasons });
    }
  }

  return suggestions.sort((a, b) => b.score - a.score).slice(0, 3);
}

Visual Indicators:

  • 🎯 90%+ match → Same directory
  • 📂 50-89% match → Subdirectory or recent use
  • 💡 10-49% match → Similar name or recently used

Recycle Bin System

Soft Delete Flow

Delete:

// Mark project as deleted
await db.projects.updateOne(
  { _id: projectId },
  { $set: { deletedAt: new Date() } }
);

// Soft delete all sessions in project
await db.sessions.updateMany(
  { projectId },
  { $set: { deletedAt: new Date() } }
);

Restore:

await db.projects.updateOne({ _id }, { $set: { deletedAt: null } });
await db.sessions.updateMany({ projectId }, { $set: { deletedAt: null } });

Permanent Delete:

await db.projects.deleteOne({ _id });
await db.sessions.deleteMany({ projectId });

Edge Cases

Scenario Behavior
Reassigning from deleted project Session becomes unassigned (projectId = null)
Deleting last session in project Project persists with 0 sessions
Two projects with same path Both shown as suggestions with equal score
Local CLI sessions Can be assigned, path read from info.json
Moving to deleted project Blocked - deleted projects excluded from menu
Concurrent edits Last write wins (MongoDB atomic updates)

Implementation

File Structure

server.js (add endpoints)
public/claude-ide/
├── projects.html (new)
├── projects.js (new)
├── projects.css (new)
└── sessions-landing.js (modify)
context-menu.js (new shared component)

Migration Script

// 1. Create projects collection
db.createCollection('projects');

// 2. Find unique project names from existing sessions
const uniqueProjects = await db.sessions.distinct('metadata.project');

// 3. Create project for each unique name
for (const name of uniqueProjects) {
  const projectSessions = await db.sessions.find({ 'metadata.project': name }).toArray();
  const paths = [...new Set(projectSessions.map(s => s.workingDir).filter(Boolean))];

  await db.projects.insertOne({
    name,
    description: '',
    icon: getRandomIcon(),
    color: getRandomColor(),
    path: paths[0] || '',
    sessionIds: projectSessions.map(s => s._id),
    createdAt: projectSessions[0].createdAt,
    lastActivity: Math.max(...projectSessions.map(s => s.lastActivity)),
    deletedAt: null
  });
}

// 4. Update sessions with projectId
for (const project of await db.projects.find().toArray()) {
  await db.sessions.updateMany(
    { 'metadata.project': project.name },
    { $set: { projectId: project._id } }
  );
}

Testing Checklist

  • Create project via inline form
  • Auto-assign session on creation
  • Smart suggestions calculate correctly
  • Move session via context menu
  • Delete project → goes to recycle bin
  • Restore project from bin
  • Permanent delete works
  • Unassigned sessions display correctly
  • Migration backfills existing data
  • Icon/color randomization works
  • Path validation on project creation