import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import * as fs from "fs/promises";
import * as path from "path";
class HandoffGenerator {
  frameManager;
  dbManager;
  handoffDir;
  constructor(frameManager, dbManager, projectRoot) {
    this.frameManager = frameManager;
    this.dbManager = dbManager;
    this.handoffDir = path.join(projectRoot, ".stackmemory", "handoffs");
  }
  /**
   * Generate a handoff document for the current session
   */
  async generateHandoff(sessionId) {
    const session = await this.dbManager.getSession(sessionId);
    if (!session) throw new Error(`Session ${sessionId} not found`);
    const activeFramePath = await this.getActiveFramePath();
    const recentTraces = await this.dbManager.getRecentTraces(sessionId, 100);
    const recentFrames = await this.dbManager.getRecentFrames(sessionId, 20);
    const tasks = await this.extractTasks(recentFrames);
    const decisions = await this.extractDecisions(recentTraces);
    const blockers = await this.extractBlockers(recentTraces, recentFrames);
    const fileEdits = await this.extractFileEdits(recentTraces);
    const commands = await this.extractCommands(recentTraces);
    const errors = await this.extractErrors(recentTraces);
    const patterns = await this.detectPatterns(recentTraces);
    const approaches = await this.extractApproaches(recentFrames);
    const sessionDuration = Math.floor(
      (Date.now() - new Date(session.startedAt).getTime()) / 6e4
    );
    const handoff = {
      session_id: sessionId,
      timestamp: (/* @__PURE__ */ new Date()).toISOString(),
      project: session.project,
      branch: session.metadata?.branch,
      active_frame_path: activeFramePath,
      active_tasks: tasks,
      pending_decisions: decisions.filter((d) => !d.resolved),
      blockers,
      recent_files: fileEdits.slice(0, 10),
      recent_commands: commands.slice(0, 10),
      recent_errors: errors.slice(0, 5),
      patterns_detected: patterns,
      approaches_tried: approaches,
      successful_strategies: this.extractSuccessfulStrategies(approaches),
      suggested_next_actions: await this.suggestNextActions(
        tasks,
        blockers,
        activeFramePath
      ),
      warnings: await this.generateWarnings(errors, blockers),
      session_duration_minutes: sessionDuration,
      frames_created: recentFrames.length,
      tool_calls_made: recentTraces.filter((t) => t.type === "tool_call").length,
      decisions_recorded: decisions.length
    };
    await this.saveHandoff(handoff);
    return handoff;
  }
  /**
   * Load the most recent handoff document
   */
  async loadHandoff() {
    try {
      await fs.mkdir(this.handoffDir, { recursive: true });
      const files = await fs.readdir(this.handoffDir);
      const handoffFiles = files.filter((f) => f.endsWith(".json")).sort().reverse();
      if (handoffFiles.length === 0) return null;
      const mostRecent = handoffFiles[0];
      const content = await fs.readFile(
        path.join(this.handoffDir, mostRecent),
        "utf-8"
      );
      return JSON.parse(content);
    } catch (error) {
      console.error("Error loading handoff:", error);
      return null;
    }
  }
  /**
   * Generate a markdown summary of the handoff
   */
  async generateMarkdownSummary(handoff) {
    const lines = [
      `# Session Handoff`,
      `**Generated**: ${new Date(handoff.timestamp).toLocaleString()}`,
      `**Project**: ${handoff.project}`,
      handoff.branch ? `**Branch**: ${handoff.branch}` : "",
      `**Duration**: ${handoff.session_duration_minutes} minutes`,
      "",
      `## Current Context`,
      `**Active Frame Path**: ${handoff.active_frame_path.join(" \u2192 ")}`,
      "",
      `## Active Tasks (${handoff.active_tasks.length})`,
      ...handoff.active_tasks.map(
        (t) => `- [${t.status}] ${t.title} (${t.progress_percentage}%)${t.blocker ? ` \u26A0\uFE0F Blocked: ${t.blocker}` : ""}`
      ),
      "",
      handoff.blockers.length > 0 ? "## Blockers" : "",
      ...handoff.blockers.map(
        (b) => `- **${b.severity}**: ${b.description}
  Tried: ${b.attempted_solutions.join(
          ", "
        )}`
      ),
      "",
      handoff.pending_decisions.length > 0 ? "## Pending Decisions" : "",
      ...handoff.pending_decisions.map(
        (d) => `- **${d.decision}**
  Rationale: ${d.rationale}`
      ),
      "",
      "## Recent Activity",
      `- Files edited: ${handoff.recent_files.length}`,
      `- Commands run: ${handoff.recent_commands.length}`,
      `- Errors encountered: ${handoff.recent_errors.length}`,
      "",
      handoff.patterns_detected.length > 0 ? "## Patterns Detected" : "",
      ...handoff.patterns_detected.map((p) => `- ${p}`),
      "",
      handoff.successful_strategies.length > 0 ? "## Successful Strategies" : "",
      ...handoff.successful_strategies.map((s) => `- ${s}`),
      "",
      "## Suggested Next Actions",
      ...handoff.suggested_next_actions.map((a) => `1. ${a}`),
      "",
      handoff.warnings.length > 0 ? "## \u26A0\uFE0F Warnings" : "",
      ...handoff.warnings.map((w) => `- ${w}`)
    ];
    return lines.filter((l) => l !== "").join("\n");
  }
  /**
   * Auto-detect session end and trigger handoff
   */
  async detectSessionEnd(sessionId) {
    const idleThreshold = 5 * 60 * 1e3;
    const lastActivity = await this.dbManager.getLastActivityTime(sessionId);
    if (!lastActivity) return false;
    const idleTime = Date.now() - lastActivity.getTime();
    if (idleTime > idleThreshold) {
      await this.generateHandoff(sessionId);
      return true;
    }
    return false;
  }
  // Private helper methods
  async getActiveFramePath() {
    const stack = await this.frameManager.getStack();
    return stack.frames.map((f) => f.description || f.type);
  }
  async extractTasks(frames) {
    return frames.filter((f) => f.type === "task").map((f) => ({
      id: f.id,
      title: f.description || "Untitled task",
      status: this.getTaskStatus(f),
      progress_percentage: f.metadata?.progress || 0,
      blocker: f.metadata?.blocker
    }));
  }
  getTaskStatus(frame) {
    if (frame.status === "closed") return "completed";
    if (frame.metadata?.blocker) return "blocked";
    if (frame.status === "open") return "in_progress";
    return "pending";
  }
  async extractDecisions(traces) {
    return traces.filter((t) => t.type === "decision").map((t) => ({
      decision: t.content.decision || "",
      rationale: t.content.rationale || "",
      alternatives_considered: t.content.alternatives,
      timestamp: t.timestamp,
      resolved: t.metadata?.resolved || false
    }));
  }
  async extractBlockers(traces, frames) {
    const blockers = [];
    const errorTraces = traces.filter(
      (t) => t.type === "error" && !t.metadata?.resolved
    );
    for (const trace of errorTraces) {
      blockers.push({
        description: trace.content.error || "Unknown error",
        attempted_solutions: trace.metadata?.attempts || [],
        suggested_approach: trace.metadata?.suggestion,
        severity: this.getErrorSeverity(trace)
      });
    }
    const blockedFrames = frames.filter((f) => f.metadata?.blocker);
    for (const frame of blockedFrames) {
      blockers.push({
        description: frame.metadata.blocker,
        attempted_solutions: frame.metadata.attempts || [],
        severity: "medium"
      });
    }
    return blockers;
  }
  getErrorSeverity(trace) {
    const error = trace.content.error?.toLowerCase() || "";
    if (error.includes("critical") || error.includes("fatal"))
      return "critical";
    if (error.includes("error") || error.includes("fail")) return "high";
    if (error.includes("warning")) return "medium";
    return "low";
  }
  async extractFileEdits(traces) {
    const fileMap = /* @__PURE__ */ new Map();
    const editTraces = traces.filter(
      (t) => ["edit", "write", "create", "delete"].includes(t.type)
    );
    for (const trace of editTraces) {
      const path2 = trace.content.file_path || trace.content.path;
      if (!path2) continue;
      if (!fileMap.has(path2)) {
        fileMap.set(path2, {
          path: path2,
          operations: [],
          line_changes: { added: 0, removed: 0 }
        });
      }
      const file = fileMap.get(path2);
      const op = this.getFileOperation(trace.type);
      if (!file.operations.includes(op)) {
        file.operations.push(op);
      }
      file.line_changes.added += trace.metadata?.lines_added || 0;
      file.line_changes.removed += trace.metadata?.lines_removed || 0;
    }
    return Array.from(fileMap.values());
  }
  getFileOperation(traceType) {
    switch (traceType) {
      case "create":
      case "write":
        return "created";
      case "edit":
        return "modified";
      case "delete":
        return "deleted";
      default:
        return "modified";
    }
  }
  async extractCommands(traces) {
    return traces.filter((t) => t.type === "bash" || t.type === "command").map((t) => ({
      command: t.content.command || "",
      success: !t.metadata?.error,
      output_summary: t.content.output?.substring(0, 100)
    }));
  }
  async extractErrors(traces) {
    return traces.filter((t) => t.type === "error").map((t) => ({
      error: t.content.error || "",
      context: t.content.context || "",
      resolved: t.metadata?.resolved || false,
      resolution: t.metadata?.resolution
    }));
  }
  async detectPatterns(traces) {
    const patterns = [];
    const testFirst = traces.some(
      (t) => t.type === "test" && traces.some(
        (t2) => t2.type === "implement" && t2.timestamp > t.timestamp
      )
    );
    if (testFirst) patterns.push("Test-Driven Development");
    const refactoring = traces.filter(
      (t) => t.content.description?.includes("refactor") || t.metadata?.operation === "refactor"
    ).length > 3;
    if (refactoring) patterns.push("Active Refactoring");
    const debugging = traces.filter((t) => t.type === "error" || t.type === "debug").length > 5;
    if (debugging) patterns.push("Deep Debugging Session");
    return patterns;
  }
  async extractApproaches(frames) {
    return frames.filter((f) => f.metadata?.approach).map((f) => ({
      approach: f.metadata.approach,
      outcome: this.getApproachOutcome(f),
      learnings: f.metadata.learnings
    }));
  }
  getApproachOutcome(frame) {
    if (frame.status === "closed" && frame.metadata?.success)
      return "successful";
    if (frame.status === "closed" && !frame.metadata?.success) return "failed";
    return "partial";
  }
  extractSuccessfulStrategies(approaches) {
    return approaches.filter((a) => a.outcome === "successful").map((a) => a.approach);
  }
  async suggestNextActions(tasks, blockers, framePath) {
    const suggestions = [];
    const inProgress = tasks.filter((t) => t.status === "in_progress");
    if (inProgress.length > 0) {
      suggestions.push(`Resume task: ${inProgress[0].title}`);
    }
    const criticalBlockers = blockers.filter((b) => b.severity === "critical");
    if (criticalBlockers.length > 0) {
      suggestions.push(
        `Resolve critical blocker: ${criticalBlockers[0].description}`
      );
    }
    const nearlyDone = tasks.filter((t) => t.progress_percentage >= 80);
    if (nearlyDone.length > 0) {
      suggestions.push(
        `Complete task: ${nearlyDone[0].title} (${nearlyDone[0].progress_percentage}% done)`
      );
    }
    return suggestions;
  }
  async generateWarnings(errors, blockers) {
    const warnings = [];
    const unresolved = errors.filter((e) => !e.resolved);
    if (unresolved.length > 0) {
      warnings.push(`${unresolved.length} unresolved errors`);
    }
    const critical = blockers.filter((b) => b.severity === "critical");
    if (critical.length > 0) {
      warnings.push(
        `${critical.length} critical blockers need immediate attention`
      );
    }
    return warnings;
  }
  async saveHandoff(handoff) {
    await fs.mkdir(this.handoffDir, { recursive: true });
    const filename = `${handoff.timestamp.replace(/[:.]/g, "-")}.json`;
    const filepath = path.join(this.handoffDir, filename);
    await fs.writeFile(filepath, JSON.stringify(handoff, null, 2), "utf-8");
    const markdown = await this.generateMarkdownSummary(handoff);
    const mdPath = filepath.replace(".json", ".md");
    await fs.writeFile(mdPath, markdown, "utf-8");
  }
}
export {
  HandoffGenerator
};
//# sourceMappingURL=handoff-generator.js.map
