Agent Skill
2/7/2026

devonthink

Automate DEVONthink on macOS via JXA (JavaScript for Automation) and Python. Use this skill when: - Creating, searching, organizing, or managing records in DEVONthink databases - Working with documents, notes, bookmarks, groups, PDFs, or web archives - Adding/removing tags, moving/duplicating/replicating records - Converting documents between formats (markdown, PDF, HTML, rich text) - Importing files/URLs or exporting records - Using DEVONthink's AI features (classification, similarity, summarization) - Querying record metadata, content, or properties - Looking up citation keys from bibliography JSON exports - Finding DEVONthink records by citation key Triggers: "DEVONthink", "DT3", "DEVONthink database", "organize documents", "document management", "citation key", "bibliography", "Zotero"

T
tombener
0GitHub Stars
2Views
npx skills add TomBener/devonthink-skill

SKILL.md

Namedevonthink
DescriptionAutomate DEVONthink on macOS via JXA (JavaScript for Automation) and Python. Use this skill when: - Creating, searching, organizing, or managing records in DEVONthink databases - Working with documents, notes, bookmarks, groups, PDFs, or web archives - Adding/removing tags, moving/duplicating/replicating records - Converting documents between formats (markdown, PDF, HTML, rich text) - Importing files/URLs or exporting records - Using DEVONthink's AI features (classification, similarity, summarization) - Querying record metadata, content, or properties - Looking up citation keys from bibliography JSON exports - Finding DEVONthink records by citation key Triggers: "DEVONthink", "DT3", "DEVONthink database", "organize documents", "document management", "citation key", "bibliography", "Zotero"

name: devonthink description: | Automate DEVONthink on macOS via JXA (JavaScript for Automation) and Python. Use this skill when:

  • Creating, searching, organizing, or managing records in DEVONthink databases
  • Working with documents, notes, bookmarks, groups, PDFs, or web archives
  • Adding/removing tags, moving/duplicating/replicating records
  • Converting documents between formats (markdown, PDF, HTML, rich text)
  • Importing files/URLs or exporting records
  • Using DEVONthink's AI features (classification, similarity, summarization)
  • Querying record metadata, content, or properties
  • Looking up citation keys from bibliography JSON exports
  • Finding DEVONthink records by citation key Triggers: "DEVONthink", "DT3", "DEVONthink database", "organize documents", "document management", "citation key", "bibliography", "Zotero"

DEVONthink Automation

Automate DEVONthink via JXA scripts executed with osascript -l JavaScript.

Quick Start

osascript -l JavaScript << 'EOF'
(() => {
  const app = Application("DEVONthink");
  app.includeStandardAdditions = true;

  // Your code here
  return JSON.stringify({ success: true });
})();
EOF

Common Workflows

1. Get Citation Key of a DEVONthink Record

Given a DEVONthink record, find its Zotero citation key:

# Using the record's file path
python3 scripts/bib_lookup.py --path "~/Library/Mobile Documents/com~apple~CloudDocs/Zotero/Pastoralism/journalArticle/Gkartzios - 2023 - Editorial. Counterurbanisation, Again.pdf"

Output:

{
  "success": true,
  "citationKey": "gkartzios2023",
  "title": "Editorial. Counterurbanisation, again..."
}

2. Find DEVONthink Record by Citation Key

Given a citation key (e.g., @gkartzios2023), find the matching DEVONthink record:

python3 scripts/bib_lookup.py --citation-key "gkartzios2023" --find-devonthink

Output:

{
  "success": true,
  "citationKey": "gkartzios2023",
  "title": "Editorial. Counterurbanisation, again...",
  "devonthinkRecords": [
    {
      "uuid": "E1DF0C33-D4C8-4ECA-9890-81B012EFDD49",
      "name": "Gkartzios - 2023 - Editorial. Counterurbanisation, Again",
      "path": "~/Library/Mobile Documents/com~apple~CloudDocs/Zotero/Pastoralism/journalArticle/Gkartzios - 2023 - Editorial....pdf",
      "recordType": "PDF document"
    }
  ]
}

3. Get Full Content of a DEVONthink Record

Read the full text of any record (PDF, markdown, HTML, etc.). DEVONthink automatically extracts/OCRs text from PDFs.

osascript -l JavaScript << 'EOF'
(() => {
  const app = Application("DEVONthink");
  try {
    const record = app.getRecordWithUuid("E1DF0C33-D4C8-4ECA-9890-81B012EFDD49");
    if (!record) {
      return JSON.stringify({ success: false, error: "Record not found" });
    }
    const result = {};
    result["name"] = record.name();
    result["recordType"] = record.recordType();
    result["plainText"] = record.plainText();
    return JSON.stringify(result);
  } catch (e) {
    return JSON.stringify({ success: false, error: e.toString() });
  }
})();
EOF

Tip: Combine workflows 2 and 3 to read a document by citation key:

  1. bib_lookup.py --citation-key "gkartzios2023" --find-devonthink → get UUID
  2. Use UUID in JXA script → get full text content

Core Operations

Search Records

const results = app.search("invoice 2024", { in: app.currentDatabase() });
// Filter: kind:pdf, kind:markdown, kind:!group, name:~keyword
// Dates: created:Yesterday, created:#3days, created>=2026-01-01

Create Record

const record = app.createRecordWith({
  name: "New Note",
  type: "markdown",  // or: group, bookmark, formatted note, txt, rtf
  content: "# Hello\n\nContent here"
}, { in: app.currentGroup() });

Get Record by UUID/ID

const record = app.getRecordWithUuid("UUID-HERE");
// Or by ID (requires database context):
const db = app.databases().find(d => d.name() === "MyDB");
const record = db.getRecordWithId(12345);

Move/Duplicate/Replicate

app.move({ record: theRecord, to: destinationGroup });
app.duplicate({ record: theRecord, to: destinationGroup });
app.replicate({ record: theRecord, to: destinationGroup }); // Same DB only

Tags

const tags = record.tags();           // Get tags
record.tags = ["tag1", "tag2"];       // Set tags (replaces all)

// Add a tag
const current = record.tags();
record.tags = [...current, "newTag"];

// Remove a tag
record.tags = record.tags().filter(t => t !== "oldTag");

Delete

app.delete({ record: theRecord });

Rename Record

record.name = "New Name";

Get Record Content

const text = record.plainText();      // Plain text content
const rich = record.richText();       // Rich text content
const html = record.source();         // HTML source (for HTML/webarchive)

Update Record Content

record.plainText = "New content";     // For text-based records
record.source = "<html>...</html>";   // For HTML records

Get Selected Records

const selected = app.selectedRecords();
selected.forEach(r => {
  // Process each selected record
});

List Group Content

const group = app.getRecordWithUuid("GROUP-UUID");
const children = group.children();
children.forEach(child => {
  // Process each child record
});

Record Properties

PropertyTypeR/WDescription
idNumberRDatabase-specific ID
uuidStringRGlobally unique identifier
nameStringRWRecord name
pathStringRFilesystem path
locationStringRDEVONthink location path
recordTypeStringRgroup, markdown, PDF document, etc.
plainTextStringRWPlain text content
richTextRichTextRWRich text content
sourceStringRWHTML/XML source
tagsArrayRWRecord tags
creationDateDateRWCreation date
modificationDateDateRWLast modified
sizeNumberRSize in bytes
commentStringRWRecord comment
labelNumberRWLabel index (0-7)
ratingNumberRWRating (0-5)
flagBooleanRWFlagged state
unreadBooleanRWUnread state

Record Types

group, smart group, markdown, txt, rtf, rtfd, formatted note, HTML, webarchive, PDF document, picture, multimedia, bookmark, feed, sheet, XML, unknown

Convert Formats

app.convert({ record: theRecord, to: "markdown" });
// Formats: simple, rich, note, markdown, HTML, webarchive,
//          PDF document, single page PDF document

Web Import

app.createMarkdownFrom("https://example.com", { in: app.currentGroup() });
app.createPDFDocumentFrom("https://example.com", { in: app.currentGroup() });
app.createFormattedNoteFrom("https://example.com", { in: app.currentGroup() });
app.createWebDocumentFrom("https://example.com", { in: app.currentGroup() });

AI Features

// Classification suggestions
const proposals = app.classify({ record: theRecord });

// Find similar records
const similar = app.compare({ record: theRecord });

// Chat/summarize (requires AI config in DEVONthink)
const response = app.getChatResponseForMessage("Summarize this", {
  record: theRecord, temperature: 0
});

Database Navigation

const dbs = app.databases();                    // All open databases
const db = app.currentDatabase();               // Current database
const root = db.root();                         // Root group
const incoming = db.incomingGroup();            // Inbox
const trash = db.trashGroup();                  // Trash

Lookup Records

app.lookupRecordsWithFile("report.pdf", { in: db });
app.lookupRecordsWithTags(["important", "work"], { in: db });
app.lookupRecordsWithComment("review needed", { in: db });
app.lookupRecordsWithURL("https://example.com", { in: db });
app.lookupRecordsWithPath("/path/to/file.pdf", { in: db });
app.lookupRecordsWithContentHash("hash-value", { in: db });

Custom Metadata

app.addCustomMetaData("John Doe", { for: "author", to: record });
const author = app.getCustomMetaData({ for: "author", from: record });

Bibliography Lookup (Zotero Integration)

Look up citation keys from Zotero CSL JSON exports using the bundled Python script.

Get Citation Key by File Path

python3 scripts/bib_lookup.py --path "/path/to/document.pdf"

Get Metadata by Citation Key

python3 scripts/bib_lookup.py --citation-key "smith2024"

Find DEVONthink Records by Citation Key

python3 scripts/bib_lookup.py --citation-key "smith2024" --find-devonthink

Options:

  • --path: File path to look up
  • --citation-key: Citation key to look up
  • --bib-json: Override path to Zotero JSON export (or set BIBLIOGRAPHY_JSON env var)
  • --find-devonthink: Also search DEVONthink for matching records
  • --full: Include complete bibliography item in output

JXA Best Practices

  1. Build objects with bracket notation (avoid inline object literals in returns):
const result = {};
result["success"] = true;
result["data"] = someData;
return JSON.stringify(result);
  1. Never use console.log - causes stdio errors

  2. Wrap in try-catch and return JSON:

try {
  // operations
  return JSON.stringify({ success: true, data: result });
} catch (e) {
  return JSON.stringify({ success: false, error: e.toString() });
}
  1. DEVONthink paths vs filesystem paths: Use location() for DEVONthink internal paths, path() for filesystem paths.

Search Query Syntax

kind:pdf                    # By type
kind:!group                 # Exclude groups
name:~invoice              # Name contains
created:Yesterday          # Date shortcuts
created:#3days             # Last 3 days
created>=2026-01-01        # Date range
kind:pdf created:#1week    # Combined

References

Skills Info
Original Name:devonthinkAuthor:tombener