---
phase: 13-auto-continue-evaluation
plan: 02
type: execute
wave: 1
depends_on: [13-01]
files_modified:
  - plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
  - plugins/claude-tts-companion/Sources/claude-tts-companion/main.swift
autonomous: true
requirements: [EVAL-05]

must_haves:
  truths:
    - "Decision notification sent to Telegram includes icon, reason, progress bar, tool breakdown, and timing"
    - "main.swift notification handler uses new evaluate() API with full state tracking"
    - "Exit notifications sent for limit reached, sweep complete, and error cases"
  artifacts:
    - path: "plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift"
      provides: "formatDecisionMessage and sendDecisionNotification methods"
      contains: "formatDecisionMessage"
    - path: "plugins/claude-tts-companion/Sources/claude-tts-companion/main.swift"
      provides: "Updated notification handler using EvaluationResult"
      contains: "EvaluationResult"
  key_links:
    - from: "AutoContinue.swift formatDecisionMessage"
      to: "TelegramBot.sendNotification"
      via: "formatted HTML string"
      pattern: "sendNotification"
    - from: "main.swift notification handler"
      to: "AutoContinueEvaluator.evaluate"
      via: "EvaluationResult struct"
      pattern: "evaluationResult"
---

<objective>
Add rich decision notifications to AutoContinueEvaluator and update main.swift to use the full evaluation API with state tracking, replacing the simple one-liner notification.

Purpose: The legacy TypeScript sends detailed Telegram notifications with decision icon, reason, plan title, progress bar, iteration count, runtime, tool breakdown, git branch, and timestamp. This plan ports that notification format and wires the new evaluate() return type into main.swift.

Output: Rich decision notifications matching legacy format, main.swift using full EvaluationResult.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/13-auto-continue-evaluation/13-01-SUMMARY.md
@plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
@plugins/claude-tts-companion/Sources/claude-tts-companion/main.swift
@plugins/claude-tts-companion/Sources/claude-tts-companion/TelegramBot.swift
@plugins/claude-tts-companion/Sources/claude-tts-companion/TelegramFormatter.swift

<interfaces>
<!-- From TelegramFormatter.swift -->
```swift
enum TelegramFormatter {
    static func escapeHtml(_ text: String) -> String
    static func stripHtmlTags(_ html: String) -> String
    static func chunkTelegramHtml(_ html: String) -> [String]
    static let telegramMaxLength: Int  // 4096
}
```

<!-- From TelegramBot.swift -->

```swift
final class TelegramBot {
    func sendNotification(_ text: String) async  // HTML parse mode
    func sendSilentMessage(_ text: String) async
}
```

<!-- From Plan 13-01 (EvaluationResult) -->

```swift
struct EvaluationResult {
    let decision: ContinueDecision
    let reason: String
    let shouldBlock: Bool
    let blockReason: String?
    let state: AutoContinueState
    let planPath: String?
    let planContent: String?
    let turnCount: Int
    let toolCalls: Int
    let toolBreakdown: String
    let errors: Int
    let gitBranch: String?
    let elapsedMin: Double
}
```

</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add rich decision notification formatting to AutoContinue.swift</name>
  <files>plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift</files>
  <read_first>
    - plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift (after Plan 01)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/TelegramFormatter.swift (escapeHtml, telegramMaxLength)
    - ~/.claude/automation/claude-telegram-sync/src/hooks/auto-continue.ts (lines 408-576: formatDecisionMessage, sendExitNotification)
  </read_first>
  <action>
Add the following methods to AutoContinueEvaluator (EVAL-05). Port EXACTLY from legacy TypeScript `formatDecisionMessage()` and `sendExitNotification()`:

**1. `formatDecisionMessage(result: EvaluationResult, sessionId: String, cwd: String, maxIterations: Int, maxRuntimeMin: Double) -> String`**

Port the exact format from legacy TypeScript lines 448-533. The message structure:

- Line 1: `<b>{icon} Auto-Continue: {DECISION}</b>` where icons are: CONTINUE=🔄, SWEEP=🧹, REDIRECT=↩️, DONE=✅
- Separator: `━` repeated 24 times
- Reason: `<i>{reason truncated to 200 chars, HTML-escaped}</i>`
- If SWEEP: `⚡ <b>Action</b>: Sweep prompt injected for final review`
- Plan section: `<b>📋 Plan</b>: {planTitle}` with progress bar if checkboxes exist, then `<code>{planFile}</code>`
- Session section: `<b>📊 Session</b>` with bullet points:
  - `• Iteration: <code>{totalIterations} / {maxIterations}</code>`
  - `• Runtime: <code>{elapsedMin:.1f} / {maxRuntimeMin} min</code>`
  - `• <code>{turnCount}T {toolBreakdown || toolCalls⚙}{errors❌ if >0}</code>`
  - `• Branch: <code>{gitBranch}</code>` (if present)
  - `• Project: <code>{shortCwd}</code>` (replace homedir with ~)
  - `• Claude session uuid jsonl ~/.claude/projects: <code>{shortSession 8 chars}</code>`
- Timestamp: Vancouver timezone `en-CA` locale, `yyyy-MM-dd HH:mm` format
- If message > 4096 chars: strip progress bar first, then hard truncate at 4080 with broken HTML entity/tag cleanup

**2. `formatExitMessage(reason: String, sessionId: String, cwd: String, state: AutoContinueState?, maxIterations: Int, maxRuntimeMin: Double) -> String`**

Port from legacy `sendExitNotification()` lines 578-636. Lighter format:

- `<b>⏹ Auto-Continue: STOP</b>`
- Separator
- `<i>{reason}</i>`
- Session info with iteration/runtime if state is available
- `• Project: <code>{shortCwd}</code>`
- `• Claude session uuid jsonl ~/.claude/projects: <code>{shortSession}</code>`
- Timestamp

**3. Helper: `extractPlanTitle(_ planContent: String) -> String`** — port from TypeScript:

- Match first `# Title` line in plan content
- Strip leading "Plan: " prefix
- Default "Untitled Plan"

Note: `progressBar()` and `checkboxCounts()` should already exist from Plan 01. If not, add them here.
</action>
<verify>
<automated>cd /Users/terryli/eon/cc-skills && swift build 2>&1 | tail -5</automated>
</verify>
<acceptance_criteria> - formatDecisionMessage produces HTML with icon, separator, reason, plan info, progress bar, session stats, timestamp - formatExitMessage produces lighter HTML for early exits (limits, errors) - Message truncation at 4096 chars with HTML entity/tag safety - Timestamp uses America/Vancouver timezone - `swift build` succeeds
</acceptance_criteria>
<done>Rich notification formatting methods added, swift build passes</done>
</task>

<task type="auto">
  <name>Task 2: Wire new evaluate() API and rich notifications into main.swift</name>
  <files>plugins/claude-tts-companion/Sources/claude-tts-companion/main.swift</files>
  <read_first>
    - plugins/claude-tts-companion/Sources/claude-tts-companion/main.swift (current wiring)
    - plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift (after Task 1)
    - ~/.claude/automation/claude-telegram-sync/src/hooks/auto-continue.ts (lines 650-915: main() function)
  </read_first>
  <action>
Update the NotificationWatcher callback in main.swift to use the new full evaluation API from AutoContinueEvaluator. Replace the current simple evaluate() call + one-liner notification.

**Replace the current notification handler block** (approximately lines 96-160 of main.swift) with:

1. Extract sessionId, transcriptPath, cwd from notification JSON (keep existing parsing)
2. Call the new `autoContinue.evaluate(sessionId:transcriptPath:cwd:)` which returns `EvaluationResult`
3. Based on result:
   - Format rich decision notification using `autoContinue.formatDecisionMessage(result:sessionId:cwd:...)`
   - Send via `bot.sendNotification(message)` (silent for all decisions)
   - For early exits (limits, sweep_done, errors), use `autoContinue.formatExitMessage(...)`
4. Continue sending session notification (Arc Summary + Tail Brief) as before — this is separate from the auto-continue decision notification

The key change: instead of the current pattern:

```swift
let (decision, reason) = await autoContinue.evaluate(transcriptPath: tp)
await bot.sendNotification("<b>[\(decisionLabel)]</b> \(reason)")
```

Use the new pattern:

```swift
let result = await autoContinue.evaluate(sessionId: sessionId, transcriptPath: tp, cwd: cwd ?? "")
let message = autoContinue.formatDecisionMessage(result: result, sessionId: sessionId, cwd: cwd ?? "", maxIterations: 10, maxRuntimeMin: 180)
await bot.sendSilentMessage(message)
```

The decision notification should be sent as a **silent message** (no push notification) since auto-continue fires frequently during autonomous sessions.
</action>
<verify>
<automated>cd /Users/terryli/eon/cc-skills && swift build 2>&1 | tail -5</automated>
</verify>
<acceptance_criteria> - main.swift uses new evaluate() returning EvaluationResult - Rich decision notification sent via sendSilentMessage - Exit notifications sent for limit/error/sweep-complete cases - Session notification (Arc Summary + Tail Brief) still works independently - `swift build` succeeds with zero errors
</acceptance_criteria>
<done>main.swift updated to use full evaluation API with rich notifications, swift build passes</done>
</task>

</tasks>

<verification>
```bash
cd /Users/terryli/eon/cc-skills && swift build 2>&1 | tail -5
# Verify rich notification format
grep -c "formatDecisionMessage" plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
grep -c "formatExitMessage" plugins/claude-tts-companion/Sources/claude-tts-companion/AutoContinue.swift
# Verify main.swift uses new API
grep -c "EvaluationResult" plugins/claude-tts-companion/Sources/claude-tts-companion/main.swift
grep -c "formatDecisionMessage\|formatExitMessage" plugins/claude-tts-companion/Sources/claude-tts-companion/main.swift
```
</verification>

<success_criteria>

1. Decision notifications include icon, reason, progress bar, tool breakdown, timing (EVAL-05)
2. main.swift uses new evaluate() API returning EvaluationResult with state tracking
3. Exit notifications sent for limits, errors, and sweep completion
4. `swift build` succeeds with zero errors
   </success_criteria>

<output>
After completion, create `.planning/phases/13-auto-continue-evaluation/13-02-SUMMARY.md`
</output>
