---
title: "Importing Claude Code history: paths, parsing, and verification"
description: Check history paths, JSONL filtering, incremental import, and watcher limitations against the current ChatCrystal adapter.
image: "https://zengliangyi.com/og/blog/en/claude-code-conversation-import.png"
---

Before importing history, identify the machine and directory being read, then inspect what parsing retains. A session appearing in the list establishes that the application created a record. It does not prove that every original event was preserved.

This article covers the Claude Code adapter in [ChatCrystal](/en/projects/chatcrystal) at commit [`7642768`](https://github.com/ZengLiangYi/ChatCrystal/tree/764276814e5c5a33ecbfeae63754f74b9261a3a8). It describes the data shapes this application supports, not a guarantee that Claude Code’s file format will remain unchanged.

## Which files the adapter scans

The [adapter](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/server/src/parser/adapters/claude-code.ts) defaults to `~/.claude/projects`. Its `scan()` visits immediate subdirectories, finds `.jsonl` files directly within them, and skips files smaller than 100 bytes.



```text
~/.claude/projects/  
  └── encoded-project-directory/  
      ├── session-a.jsonl  
      └── session-b.jsonl
```



This is not an arbitrary-depth recursive scan: do not assume deeper subagent histories are included automatically. Session IDs come from filenames. Project display names are inferred from directory segments, preferring `Project` or `projects` and otherwise taking the final one or two segments. This is a heuristic, not a reliable decoder for every original path.

## Set custom paths before server startup

The server process loads `CLAUDE_PROJECTS_DIR`. For a new local service that has not started, a POSIX shell example is:



```bash
CLAUDE_PROJECTS_DIR  =  /absolute/path/to/claude/projects   crystal   serve   -d
```



In PowerShell, set `$env:CLAUDE_PROJECTS_DIR = 'C:\path\to\projects'` before starting the service. An existing server does not reload its path because you later set an environment variable before `crystal import`. Identify the target service and startup configuration, then arrange a restart if changing the path.

This guide assumes the default local connection. Remote cloud import scans at the CLI client and uploads parsed results, changing both the path location and transmission boundary. Check the connection target in status before proceeding.

## Scan manually and inspect the results



```bash
crystal   import  
  crystal   --json   conversations   --source   claude-code   --limit   20
```



The first command scans enabled sources in the current local workflow. The second uses `--json` to select the non-interactive branch and filters stored sessions by `claude-code`. Inspect projects, message counts, and status in the returned object’s `items` array. The current [import command](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/server/src/cli/commands/import.ts) registers `--source`, but its local interactive and streaming branches do not forward that option. Consequently, `crystal import --source claude-code` cannot be promised to scan only that source.

Startup with an empty database can insert demo data. A `demo-` record in the list does not prove a real file was imported. Check the original test file’s session ID rather than relying on source labels or total counts.

Repeated import does not always add records. The [import service](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/server/src/services/import.ts) first checks file size and modification time, then compares normalized content hashes when reparsing. Unchanged content is skipped or receives metadata updates; changed content follows the replacement path. After editing an original file, inspect the conversation and associated note status rather than treating an old summary as a conclusion about new content.

## Parsing retains filtered messages

JSONL is read line by line. Lines that cannot be parsed as JSON are skipped. Relevant-message processing requires a `uuid` and a top-level `user` or `assistant` type. System records, progress events, and streaming deltas are not retained as ordinary conversation messages.

Within content arrays, the adapter collects `text` blocks. It saves `thinking` blocks separately, marks `tool_use`, and excludes `tool_result` from body extraction. A message with no body can still survive if it contains thinking or tool use. Filtering top-level thinking noise therefore does not mean every nested thinking block is discarded.

Some command echoes and `system-reminder` tags are cleaned according to existing rules; messages are then sorted by timestamps. This is neither general redaction nor a complete event archive. Back up original files separately and inspect sensitive material independently.

## A skip is not necessarily a failure

| Result | Causes to investigate |

| --- | --- |

| No files scanned | Missing path, unsupported directory depth, small files, read permissions |

| Scanned but skipped | Unchanged size and modification time, unchanged parsed content, fewer than two meaningful messages |

| Content appears incomplete | Noise filters, block extraction rules, invalid JSON lines, unfinished history writes |

| Error count increases | Inspect the affected file and import logs, beyond the overall conversation count |

Two messages meet the current import threshold; they do not establish completeness, importance, or suitability for notes. Import and summarization are separate stages. After summarizing one chosen record, inspect note content and vector processing status as well.

## Verify automatic watching separately

The [watcher](https://github.com/ZengLiangYi/ChatCrystal/blob/764276814e5c5a33ecbfeae63754f74b9261a3a8/server/src/watcher/index.ts) starts by default in local mode, debouncing add and change events; cloud mode disables local watching. However, it currently passes a Claude Code `**/*.jsonl` glob to Chokidar 5, which no longer supports globs; see the [official upgrade guidance](https://github.com/paulmillr/chokidar#upgrading). This version therefore cannot guarantee automatic import of new conversations; verify watching separately.

First import a known file manually. Then create or update another history containing non-sensitive test content and inspect watcher logs and the session list. If nothing changes, manual `crystal import` provides an explicit check. Debouncing does not prove that the watched target works; preserve the actual path, dependency version, and logs when investigating.

See the [getting-started workflow](/en/blog/chatcrystal-getting-started) for installation and both model checks, and [AI conversation formats](/en/blog/ai-conversation-data-formats) for differences across sources.

Share:

##  Related Posts

[##  Filtering Claude Code JSONL: Events, Blocks, and Omissions

Follow ChatCrystal relevance checks, content extraction, and tag cleanup to distinguish top-level filtering from nested thinking and tool flags.          曾亮懿       October 3, 2026      4 min read

#ChatCrystal

#Claude Code

#JSONL

#Data parsing](/en/blog/claude-code-jsonl-noise-filter)    [##  Set up ChatCrystal MCP: make Claude Code calls reviewable

Connect ChatCrystal to Claude Code with a project-scoped stdio server, then verify its seven tools, HTTP backend, approvals, and request path.          曾亮懿       October 3, 2026      3 min read

#ChatCrystal

#MCP

#Claude Code](/en/blog/chatcrystal-mcp-setup)    [##  Claude Code memory workflow: recall, verify, then write back

Use recall_for_task, validate_task_memory, and write_task_memory as an auditable workflow without treating MCP configuration as an automatic-memory guarantee.          曾亮懿       October 3, 2026      4 min read

#ChatCrystal

#MCP

#Claude Code](/en/blog/claude-code-memory-workflow)

##  From writing to code

Explore ChatCrystal, developer tools and daily learning notes on my GitHub. Questions about the implementation are welcome.

[Visit my GitHub](https://github.com/ZengLiangYi)

```json
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "ZengLiangYi Blog",
  "alternateName": [
    "Zeng Liangyi",
    "ZengLiangYi",
    "zengliangyi.com"
  ],
  "url": "https://zengliangyi.com",
  "description": "曾亮懿的个人网站。记录 AI Native 探索、开发者工具与全栈产品实践。"
}
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Importing Claude Code history: paths, parsing, and verification",
  "description": "Check history paths, JSONL filtering, incremental import, and watcher limitations against the current ChatCrystal adapter.",
  "url": "https://zengliangyi.com/en/blog/claude-code-conversation-import/",
  "image": "https://zengliangyi.com/images/blog/claude-code-conversation-import.svg",
  "datePublished": "2026-10-03T00:00:00.000Z",
  "dateModified": "2026-10-03T00:00:00.000Z",
  "author": {
    "@type": "Person",
    "name": "曾亮懿",
    "url": "https://zengliangyi.com/en/about"
  },
  "publisher": {
    "@type": "Organization",
    "name": "ZengLiangYi Blog",
    "logo": {
      "@type": "ImageObject",
      "url": "https://zengliangyi.com/favicon-96x96.png"
    }
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://zengliangyi.com/en/blog/claude-code-conversation-import/"
  }
}
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://zengliangyi.com/en"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Blog",
      "item": "https://zengliangyi.com/en/blog"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "Importing Claude Code history: paths, parsing, and verification",
      "item": "https://zengliangyi.com/en/blog/claude-code-conversation-import/"
    }
  ]
}
```
