Table of contents Show Hide
- Define the direction of data flow
- A list response is one page, not the entire knowledge base
- Organize a complete export
- Use IDs for filenames and titles for presentation
- Relationships can point in either direction
- Code fences cannot always be three backticks
- Snapshots and incremental synchronization make different promises
- Using the result in Obsidian
- Checking the export
ChatCrystal organizes knowledge from AI coding conversations. Obsidian can bring that knowledge together with project documentation, reading notes, and your own thinking. Connecting them can be straightforward: read structured notes, produce Markdown files, and place them inside a vault.
This article explains how to read notes through the CLI and design an independent snapshot for Obsidian. The commands and excerpts illustrate conversion; generating Markdown files requires your own implementation. Commands and response shapes were revised on September 23, 2026, against ChatCrystal commit 7642768.
Define the direction of data flow
ChatCrystal notes and relationships
→ crystal --json notes list / get / relations
→ Markdown snapshot directory
→ Reading, searching, and linking in Obsidian
This workflow neither writes Obsidian edits back to ChatCrystal nor copies the embedding index. Each application retains its own search capabilities. Markdown is a readable export, not a complete database backup.
The core content includes titles, summaries, conclusions, code snippets, tags, projects, and creation times. Relationships are fetched separately to produce wikilinks to other exported notes.
A list response is one page, not the entire knowledge base
Start in a terminal where the connection is already configured:
crystal --version
crystal --json notes list --page 1 --limit 100
The current list shape is:
{
"items": [{"id": 1, "title": "Example note"}],
"total": 1,
"offset": 0,
"limit": 100
}
items contains the current page; destructuring { notes } is incorrect. The default listing is not a full export either. An export needs to request subsequent pages until it has collected the number of notes indicated by total.
It then fetches details and relationships individually:
crystal --json notes get 1
crystal --json notes relations 1
The CLI already unwraps the server response. Note details can therefore be read as a note object, while relationships are an array. Connection settings and credentials come from the existing CLI configuration; see the project’s CLI documentation for remote connections.
Organize a complete export
The ChatCrystal version referenced here requires Node.js 24. First confirm that the list, detail, and relationship commands return successfully, then implement conversion in this order:
- Collect note IDs across all pages and check the total and pagination progress.
- Fetch full details and relationships for every note.
- Convert fields to Markdown and check code blocks and related-note links.
- Write to a new directory inside the vault, such as
ChatCrystal-2026-09-24, and verify it before using it for reading.
Pause imports, summarization, and editing during export. Multiple CLI requests do not form a database transaction snapshot, and a stable total does not prove the content stayed unchanged. Treat request or disk-write failures as an incomplete export; keep the previous snapshot and retry in a new directory.
Use IDs for filenames and titles for presentation
Titles are unreliable file identifiers: notes can share a title, titles can change, and they can contain path separators. Use note-<id>.md, storing the title in frontmatter and the body:
---
title: "SQLite transactions and recovery"
chatcrystal_id: 42
tags: ["sqlite", "recovery"]
project: "ChatCrystal"
created: "2026-06-11T01:00:00Z"
---
This is illustrative output. JSON string serialization can produce the YAML values, handling quotes and line breaks instead of merely replacing double quotes. created copies the source value; missing times remain empty rather than acquiring an inferred timezone.
Within one ChatCrystal instance, renaming a note does not rename its exported file. Equal numeric IDs from different instances are not interchangeable; use separate output directories for separate instances.
Relationships can point in either direction
A relationship can lead from the current note to another note or from another note to the current one. The destination is not always target_note_id:
const other = relation.source_note_id === note.id
? relation.target_note_id
: relation.target_note_id === note.id
? relation.source_note_id
: null;
Create links only to targets included in this export. Use [[note-42]] to match the filename and retain the source ID, target ID, relationship type, and confidence. For example, [[note-2]] → [[note-1]] — DEPENDS_ON (80%) means note 2 depends on note 1; the arrow keeps that direction in both notes. A confidence of zero should be displayed instead of being lost to a truthiness check.
Obsidian supports wikilinks and backlinks. Their practical meaning still depends on the source data; a graph edge does not establish a verified causal relationship. Internal-link documentation
Each snapshot directory may contain its own note-42.md. Keeping many complete snapshots inside one vault can make short filename links ambiguous. Move historical snapshots outside the vault, or use explicit vault-relative paths in a longer-term integration.
Code fences cannot always be three backticks
An exported snippet may itself contain Markdown with triple backticks. Wrapping it in another triple-backtick fence can turn the remainder into ordinary prose.
When generating code blocks, find the longest run of backticks and make the outer fence longer. Restrict the language marker to identifier characters. This preserves the source code instead of deleting backticks to make the formatting appear correct.
Preserve complete summaries and original tag values. If you need normalized case or special-character handling, define that policy explicitly rather than changing meaning during export.
Snapshots and incremental synchronization make different promises
“Export only notes whose updated_at is newer than the previous run” is insufficient for reliable synchronization. What if relationships change while the body stays the same? How do deletions propagate? Does a title change leave an old file? Which version wins when the user has edited the Obsidian copy?
The current CLI list contract used here does not provide sufficient version information for that synchronization model. This workflow therefore uses a full snapshot written to a new directory each time. Establish accurate exports before adding a more complex synchronization system.
A future incremental implementation needs at least a stable ID-to-file mapping, content and relationship hashes, a deletion policy, and rules for conflicts with manual edits. A failed run must not advance its checkpoint to the current time, or unfinished notes may be skipped permanently.
Scheduled exports should follow the same boundary: use a unique directory, record the exit status, and switch to the new snapshot only after success. Do not rewrite actively edited vault files every hour.
Using the result in Obsidian
Open exported notes and check their body text, code blocks, and related-note links. Search by title and content, browse tags, and follow backlinks to explore connections. Keep personal additions in separate notes that link to the exports, so future exports and manual edits do not compete for the same file.
For ChatCrystal’s semantic search, run:
crystal search "SQLite transaction recovery" --limit 5
You can integrate this into a broader workflow, but avoid placing unescaped selected text directly into a shell command. Search and export are separate access paths; vector files do not need to move into Obsidian.
Checking the export
When implementing automated export, use synthetic CLI responses to test pagination, duplicate titles, relationship direction, zero confidence, nested fences, request failures, and refusal to overwrite. Then check samples from your actual knowledge base.
After the first run, verify the note count, a long code snippet, a pair of related notes, and a title containing quotation marks. Once those checks pass, use the output for everyday reading.
The purpose is to let knowledge remain useful beyond the tool that collected it. See five AI conversation formats for import boundaries and local vector database selection for retrieval tradeoffs.