Skip to content

Importing from Evernote

When to use

When a user asks to import an Evernote export (.enex). The Import tab hands the task straight to you: you own it end to end — run the import, then make the result feel native and resolve whatever couldn't be converted, asking the user when you need a decision. Read limestone-vault-conventions and working-with-collections first.

Run the import (one tool call)

Call limestone_import_notebook(path, collection_name). It runs the deterministic importer (below) — the whole bulk conversion at once — and returns a summary: collection, notes_imported, attachments_imported, attachments_skipped, and an unconverted list. Never import notes one at a time yourself; that's what this tool is for. Then finalize (see below).

How the import works (what the tool does)

An .enex is one XML file of many <note>s, each with metadata, an ENML <content> body, and base64 <resource> attachments referenced inline by <en-media hash=…> (the hash is the MD5 of the resource's bytes). The importer streams the file (bounded memory, whatever its size) and writes:

Evernote Limestone
the notebook (the .enex) one collection (named for the file)
a <note> a record (row + Markdown note); duplicate titles get a numeric suffix so their folders don't collide
<title> the record title
<created> / <updated> (20211210T120811Z) Created / Updated date properties, ISO-8601 (2021-12-10T12:08:11+00:00)
<note-attributes><author> Author text property
<note-attributes><source-url> Source URL text property
ENML <h3> / <div> / <ul> / <b> / <a> … Markdown headings / paragraphs / lists / marks / links
ENML <table> a limestone-table fenced block (see below)
<en-media> → its <resource> the file, stored beside the note, embedded inline as ![[filename]] (images render; other files show as a clickable chip)

The limestone-table fence

A table is a fenced block whose body is JSON — exactly what the editor's serializer (apps/web/src/editor/markdown/serializer.ts) reads:

```limestone-table
{"headers": [], "rows": [["**Reagent**", "**Vol**"], ["Glucose", "20 ml"]]}
```

Evernote tables have no header row, so headers is [] and every row is data; each cell is inline Markdown (bold/links survive).

Finalizing an import (your job)

After a run you get an ImportResult with counts and an unconverted list. Make the collection feel native:

  1. Retype properties. Author imports as text; if the vault has matching members, consider a user property. Confirm Created/Updated render as dates.
  2. Split or rename the collection if one notebook clearly holds several distinct record types (e.g. protocols vs. results) — create the extra collections and move rows. Ask the user before large restructuring.
  3. Resolve unconverted items. Each names a note and a reason:
  4. attachment … Unsupported file type — a file type the vault doesn't yet accept (e.g. Mathematica .nb). Decide with the user: add the extension to VaultService.ARTIFACT_EXTENSIONS (and re-run for those notes), convert it, or leave it out with a note.
  5. attachment … unresolved en-media hash … — an inline reference whose file was skipped above; it disappears from the body. Usually resolved by fixing the attachment type, then re-importing that note.

Evolving this skill (formats change)

Evernote (and other apps) keep changing their exports. When the importer reports an unknown ENML element or an unhandled MIME type, work out the right mapping, apply it to the affected notes with the normal vault tools, and propose an update: either extend this SKILL.md's mapping table (so the next person knows) or, for a recurring structural change, flag that importers/evernote.py / enml.py needs a code change. Keep this file and the importer in sync — the importer's module docstring points back here.

Tips

  • Never try to re-do the bulk import yourself by calling tools per note — that's what the deterministic importer is for. Your value is judgment on the margins.
  • The importer is idempotent only at the collection level (it creates a fresh collection); re-running makes a second collection. Prefer fixing in place.