feat: PlanMode-Status auf Session tracken (EnterPlanMode/ExitPlanMode) #317

Closed
opened 2026-01-25 19:21:01 +00:00 by jack · 0 comments
Owner

Übersicht

Claude Code's EnterPlanMode und ExitPlanMode Tools werden zwar als Observations erfasst, aber der PlanMode-Status wird nicht auf Session-Ebene getrackt.

Aktueller Stand

Tool Observations erfasst
EnterPlanMode 4
ExitPlanMode 2

Die Tools landen in der Task-Queue und werden zu Observations verarbeitet - aber wir wissen nicht:

  • Wann eine Session in PlanMode gewechselt ist
  • Ob der User den Plan approved hat
  • Wie lange die Planungsphase gedauert hat

Vorgeschlagene Lösung

1. Session-Entity erweitern

// packages/database/src/entities/Session.ts
@Entity({ tableName: 'sessions' })
export class Session {
  // ... bestehende Felder ...

  /**
   * Whether session is currently in plan mode
   */
  @Property({ default: false })
  is_in_plan_mode!: boolean;

  /**
   * Timestamp when plan mode was entered (epoch ms)
   */
  @Property({ nullable: true, type: 'bigint' })
  plan_mode_entered_at?: number;

  /**
   * Number of times plan mode was entered in this session
   */
  @Property({ default: 0 })
  plan_mode_count!: number;
}

2. Hook-Handler für PlanMode erweitern

// packages/hooks/src/handlers/post-tool-use.ts

const PLAN_MODE_TOOLS = new Set(['EnterPlanMode', 'ExitPlanMode']);

// In handlePostToolUse():
if (PLAN_MODE_TOOLS.has(input.toolName)) {
  if (input.toolName === 'EnterPlanMode') {
    await client.post('/api/hooks/plan-mode/enter', {
      sessionId: input.sessionId,
      project: input.project,
    });
  } else if (input.toolName === 'ExitPlanMode') {
    // Parse toolInput für approval info
    const exitInput = JSON.parse(input.toolInput || '{}');
    await client.post('/api/hooks/plan-mode/exit', {
      sessionId: input.sessionId,
      project: input.project,
      approved: true, // ExitPlanMode = User hat approved
      allowedPrompts: exitInput.allowedPrompts,
    });
  }
}

3. Backend-Routen

// packages/backend/src/routes/hooks.ts

// POST /api/hooks/plan-mode/enter
private async planModeEnter(req: Request, res: Response): Promise<void> {
  const { sessionId, project } = req.body;
  
  await this.deps.sessionService.enterPlanMode(sessionId);
  
  this.deps.sseBroadcaster.broadcast({
    type: 'session:plan-mode-entered',
    data: { sessionId, project },
  });
  
  this.success(res, { success: true });
}

// POST /api/hooks/plan-mode/exit
private async planModeExit(req: Request, res: Response): Promise<void> {
  const { sessionId, project, approved, allowedPrompts } = req.body;
  
  const duration = await this.deps.sessionService.exitPlanMode(sessionId);
  
  this.deps.sseBroadcaster.broadcast({
    type: 'session:plan-mode-exited',
    data: { sessionId, project, approved, duration, allowedPrompts },
  });
  
  this.success(res, { success: true, duration });
}

Use Cases

1. WebUI Session-Ansicht

  • Badge "In Plan Mode" anzeigen wenn Session gerade plant
  • Planungszeit in Session-Details anzeigen

2. Analytics

  • Wie oft wird PlanMode genutzt?
  • Durchschnittliche Planungszeit
  • Approval-Rate

3. Real-time Updates

  • SSE-Events für Plan-Mode-Wechsel
  • UI kann Status live aktualisieren

Bezug

  • Issue #260 (User Task Tracking - TodoList & PlanMode)
  • Issue #316 (UserTask Bug - externalId nicht erfasst)

Aufwand

Komponente Aufwand
Session-Entity + Migration 1h
Hook-Handler erweitern 1h
Backend-Routen 1.5h
SSE-Events 0.5h
Gesamt 4h
## Übersicht Claude Code's `EnterPlanMode` und `ExitPlanMode` Tools werden zwar als Observations erfasst, aber der PlanMode-Status wird nicht auf Session-Ebene getrackt. ### Aktueller Stand | Tool | Observations erfasst | |------|---------------------| | `EnterPlanMode` | 4 | | `ExitPlanMode` | 2 | Die Tools landen in der Task-Queue und werden zu Observations verarbeitet - aber wir wissen nicht: - Wann eine Session in PlanMode gewechselt ist - Ob der User den Plan approved hat - Wie lange die Planungsphase gedauert hat ## Vorgeschlagene Lösung ### 1. Session-Entity erweitern ```typescript // packages/database/src/entities/Session.ts @Entity({ tableName: 'sessions' }) export class Session { // ... bestehende Felder ... /** * Whether session is currently in plan mode */ @Property({ default: false }) is_in_plan_mode!: boolean; /** * Timestamp when plan mode was entered (epoch ms) */ @Property({ nullable: true, type: 'bigint' }) plan_mode_entered_at?: number; /** * Number of times plan mode was entered in this session */ @Property({ default: 0 }) plan_mode_count!: number; } ``` ### 2. Hook-Handler für PlanMode erweitern ```typescript // packages/hooks/src/handlers/post-tool-use.ts const PLAN_MODE_TOOLS = new Set(['EnterPlanMode', 'ExitPlanMode']); // In handlePostToolUse(): if (PLAN_MODE_TOOLS.has(input.toolName)) { if (input.toolName === 'EnterPlanMode') { await client.post('/api/hooks/plan-mode/enter', { sessionId: input.sessionId, project: input.project, }); } else if (input.toolName === 'ExitPlanMode') { // Parse toolInput für approval info const exitInput = JSON.parse(input.toolInput || '{}'); await client.post('/api/hooks/plan-mode/exit', { sessionId: input.sessionId, project: input.project, approved: true, // ExitPlanMode = User hat approved allowedPrompts: exitInput.allowedPrompts, }); } } ``` ### 3. Backend-Routen ```typescript // packages/backend/src/routes/hooks.ts // POST /api/hooks/plan-mode/enter private async planModeEnter(req: Request, res: Response): Promise<void> { const { sessionId, project } = req.body; await this.deps.sessionService.enterPlanMode(sessionId); this.deps.sseBroadcaster.broadcast({ type: 'session:plan-mode-entered', data: { sessionId, project }, }); this.success(res, { success: true }); } // POST /api/hooks/plan-mode/exit private async planModeExit(req: Request, res: Response): Promise<void> { const { sessionId, project, approved, allowedPrompts } = req.body; const duration = await this.deps.sessionService.exitPlanMode(sessionId); this.deps.sseBroadcaster.broadcast({ type: 'session:plan-mode-exited', data: { sessionId, project, approved, duration, allowedPrompts }, }); this.success(res, { success: true, duration }); } ``` ## Use Cases ### 1. WebUI Session-Ansicht - Badge "In Plan Mode" anzeigen wenn Session gerade plant - Planungszeit in Session-Details anzeigen ### 2. Analytics - Wie oft wird PlanMode genutzt? - Durchschnittliche Planungszeit - Approval-Rate ### 3. Real-time Updates - SSE-Events für Plan-Mode-Wechsel - UI kann Status live aktualisieren ## Bezug - Issue #260 (User Task Tracking - TodoList & PlanMode) - Issue #316 (UserTask Bug - externalId nicht erfasst) ## Aufwand | Komponente | Aufwand | |------------|---------| | Session-Entity + Migration | 1h | | Hook-Handler erweitern | 1h | | Backend-Routen | 1.5h | | SSE-Events | 0.5h | | **Gesamt** | **4h** |
jack closed this issue 2026-01-25 20:08:50 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
customable/claude-mem#317
No description provided.