AgentSkillsCN

devonthink

通过JXA(JavaScript for Automation)与Python,自动化管理macOS上的DEVONthink应用。当您需要:(1) 在DEVONthink数据库中创建、搜索、整理或管理各类记录;(2) 处理文档、笔记、书签、分组、PDF文件或网页存档;(3) 添加/移除标签、移动/复制/同步记录;(4) 在不同格式间转换文档(Markdown、PDF、HTML、富文本);(5) 导入文件或URL,导出记录;(6) 使用DEVONthink的AI功能(分类、相似度分析、摘要生成);(7) 查询记录的元数据、内容或属性;(8) 根据文献引用JSON导出结果,查找引文键;(9) 通过引文键快速定位DEVONthink中的相关记录。触发关键词:“DEVONthink”、“DT3”、“DEVONthink数据库”、“整理文档”、“文档管理”、“引文键”、“文献引用”、“Zotero”。

SKILL.md
--- frontmatter
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

javascript
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:

bash
# 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:

json
{
  "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:

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

Output:

json
{
  "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.

bash
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

javascript
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

javascript
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

javascript
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

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

Tags

javascript
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

javascript
app.delete({ record: theRecord });

Rename Record

javascript
record.name = "New Name";

Get Record Content

javascript
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

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

Get Selected Records

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

List Group Content

javascript
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

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

Web Import

javascript
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

javascript
// 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

javascript
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

javascript
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

javascript
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

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

Get Metadata by Citation Key

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

Find DEVONthink Records by Citation Key

bash
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):
javascript
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:

javascript
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

code
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