Skip to content

claude-code-chat-browser: Consolidate export pipeline between HTTP API and CLI #50

@clean6378-max-it

Description

@clean6378-max-it

Problem

The session export logic exists in two independent, near-identical implementations:

  • bulk_export() in api/export_api.py:89-272 (HTTP API — writes zip to BytesIO)
  • cmd_export() in scripts/export.py:452-627 (CLI — writes to filesystem or zip)

Both independently perform the same sequence: list projects, iterate sessions, check exclusion rules, parse JSONL, skip untitled sessions, compute stats, format to Markdown, build manifest entries, and persist export state. Divergence is already present (manifest fields, zip inner paths, error handling). Every behavioral fix must be applied in two places.

Goal

Extract a shared export engine that owns the common session loop. HTTP and CLI callers delegate to it and provide sink/path-builder strategies for their respective output targets.

Prerequisites

  • Monday PR2 merged (runtime JSONL validation at parse_session() boundary)
  • Tuesday fixtures merged (optional but useful for parity tests)
git checkout master
git pull
git checkout -b feat/export-engine

Scope

New module: utils/export_engine.py (or export_pipeline.py)

  • Encapsulate: project listing, session iteration, exclusion checking, JSONL parsing, untitled skip, stat computation, Markdown/JSON formatting, manifest building, export-state mtime tracking.
  • Accept an ExportSink protocol (or equivalent callback/strategy):
    • HTTP: ZipSink writing to zipfile.ZipFile / BytesIO
    • CLI: collector that feeds existing filesystem/zip write block
  • Accept injectable path builder so HTTP keeps {proj_slug}/{file}.md layout and CLI keeps {date}/{proj_slug}/{file}.md without duplicating the loop.
  • Return structured result: exported_count, failure_count, skipped_count, manifest, new_sessions_map, etc.
  • Per-session failures: catch export errors, increment failure counter, continue (batch semantics unchanged).

Caller changes

File Change
api/export_api.py bulk_export() delegates to engine; retains request validation, 422 on empty export, send_file, _write_state
scripts/export.py cmd_export() delegates bulk loop to engine; retains argparse, disk/zip output, _save_state, human-readable prints

Tests

  • At least one integration/parity test (tests/test_export_engine_parity.py):
    • Same fixture project dir
    • HTTP path (test client POST /api/export) vs CLI path (export --no-zip)
    • Assert identical Markdown content and manifest rows for shared keys (session_id, title, project, tokens, tool_calls)
  • All existing export tests remain green:
    • tests/test_export_api_bulk.py
    • tests/test_cli_e2e.py
    • tests/test_export_exclusion_filtering.py

Out of Scope

  • CLI exit codes on partial failure — separate issue/PR (feat/cli-export-exit-codes)
  • JSONL parser module split (Thursday Add CLI + Web GUI for browsing Claude Code sessions #6)
  • Unifying zip inner path layout between HTTP and CLI (unless trivial; document if deferred)
  • Full manifest field unification (test compares shared subset; extended fields may remain CLI-only until follow-up)

Acceptance Criteria

  • Shared export engine module encapsulates the common bulk-export sequence
  • Engine accepts a sink abstraction for HTTP (BytesIO zip) vs CLI (filesystem/zip)
  • api/export_api.py and scripts/export.py both delegate to the shared engine
  • No behavioral regression on valid inputs; existing export tests pass
  • ≥1 integration test exercises both code paths with the same fixture and asserts identical Markdown + manifest core fields
  • mypy --strict passes on new module
  • pytest -q green in CI
  • PR approved by at least 1 reviewer

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions