Always a pleasure to see the js version!
Let us know if you get any useful news.
In the meantime, Claude is working, just got an API (Sonnet, $5 + taxes)
Just for fun, folks - I do intend to be playing with it and looking at what the model comes up with, knowing fully that it might be %6**)) !!!
You would obviously know what I told Claude to do ![]()
yep, all ok!
Making it just a bit more complex. AI prompt was “Count the number of “.avif” records”
Well, not too bad for simple stuff that I can look at and debug if needed.
Just for the fun of it: Could you generate a JavaScript script with the same prompt? I’d like to see how that looks.
And while it’s easy to criticize the results as elementary…
- The script in this case is not far off from functional:
nameshould more safely befilename. - @cgrunenberg worked to have the AI do better and know some about typical DT scripting errors. So not only do you get useful handlers built-in, the AI script is more “smartly coded” than what you’d get from generic AI requests.
At least this can give someone new to scripting a useful starting point.
Claude 4 Sonnet Prompt: count the pdf files in the selection
(() => {
const app = Application("DEVONthink");
app.includeStandardAdditions = true;
let records = app.selectedRecords();
let pdfCount = 0;
records.forEach(record => {
if (record.recordType() === "PDF document") {
pdfCount++;
}
});
app.displayDialog(`Number of PDF files in selection: ${pdfCount}`);
})();
will do!
(() => {
const app = Application(“DEVONthink”);
app.includeStandardAdditions = true;
let records = app.selectedRecords();
let avifCount = 0;
records.forEach(r => {
if (r.name().toLowerCase().endsWith('.avif')) {
avifCount++;
}
});
app.displayAlert(`Found ${avifCount} AVIF record${avifCount !== 1 ? 's' : ''}`);
})();
And it found the 3 .avif
hm, sorry bout the ugly misformatting above
Indeed!
Fixed.
Merci!
I tried something reasonably harder, which was”
Use bash regex to replace “(dddd)” with “----”
Note: I am quite sure my regex is wrong ![]()
It built this:
Not too bad, it knew ho to write the shell script, which of course didn’t work, certainly due to me.
Because, it understood that I meant - the content -
And of course I hadn’t told it to operate on the record names.
I changed the script to this, with some cleanup:
And that didn’t work either, and I’m sure it’s just me writing bad regex patterns
And logically, YMMV based on the model…
Qwen3 Coder
(() => {
const app = Application("DEVONthink");
app.includeStandardAdditions = true;
let records = app.selectedRecords();
let pdfCount = 0;
records.forEach(r => {
if (r.recordType() === "PDF document") {
pdfCount++;
}
});
console.log(`Number of PDF files in selection: ${pdfCount}`);
return pdfCount;
})();
Grok Code Fast 1
(() => {
'use strict';
const app = Application("DEVONthink");
app.includeStandardAdditions = true;
try {
let records = app.selectedRecords();
if (!records || records.length === 0) {
console.log("No records selected.");
return 0;
}
let pdfCount = 0;
records.forEach(r => {
if (r.recordType() === "PDF document") {
pdfCount++;
}
});
console.log(`Number of PDF files in selection: ${pdfCount}`);
return pdfCount;
} catch (error) {
console.error(`Error counting PDF files: ${error.message}`);
return 0;
}
})();
Devstral Medium
// Get the current application
const app = Application('DEVONthink 3');
// Get the current selection
const selection = app.selection();
// Initialize a counter for PDF files
let pdfCount = 0;
// Iterate through the selection
selection.forEach(record => {
// Check if the record is a PDF document
if (record.recordType() === 'PDF document') {
pdfCount++;
}
});
// Log the count of PDF files
console.log(`Number of PDF files in selection: ${pdfCount}`);
Noting the improved commenting from Devstral
Gemini Flash 2.5
(() => {
const app = Application("DEVONthink");
app.includeStandardAdditions = true;
try {
const selectedRecords = app.selection();
if (!selectedRecords || selectedRecords.length === 0) {
app.displayAlert("No Selection", "Please select some records in DEVONthink.");
return 0;
}
let pdfCount = 0;
for (let i = 0; i < selectedRecords.length; i++) {
const currentRecord = selectedRecords[i];
// Check if the record type is 'PDF document'
if (currentRecord.recordType() === app.dataTypes.PDFDocument) {
pdfCount++;
}
}
app.displayAlert("PDF Count", `There are ${pdfCount} PDF documents in the current selection.`);
return pdfCount;
} catch (error) {
// Log the error to DEVONthink's log window and display an alert
const errorMessage = `Error counting PDFs: ${error.message || error}`;
app.logMessage(errorMessage);
app.displayAlert("Script Error", errorMessage);
return -1; // Indicate an error
}
})();
The award for trying too hard IMHO ![]()
And singling out this model, here is the same request made for AppleScript…
tell application id "DNtp"
try
set theRecords to selected records
set pdfCount to 0
if (count of theRecords) is 0 then
display dialog "No records are currently selected." with title "DEVONthink Automation"
return
end if
repeat with theRecord in theRecords
if the record type of theRecord is PDF document then
set pdfCount to pdfCount + 1
end if
end repeat
display dialog "Found " & pdfCount & " PDF file(s) in the selection." with title "DEVONthink PDF Count"
on error errMsg number errNum
if errNum = -1728 then -- Can't get selected records (e.g., no selection or no open database)
display dialog "DEVONthink is running, but no records are selected or no database is open." with title "DEVONthink Automation Error" buttons {"OK"} default button 1 with icon caution
else if errNum = -10000 then -- Application not running
display dialog "DEVONthink is not running." with title "DEVONthink Automation Error" buttons {"OK"} default button 1 with icon caution
else
display dialog "An error occurred: " & errMsg & " (Error number: " & errNum & ")" with title "DEVONthink Automation Error" buttons {"OK"} default button 1 with icon caution
end if
end try
end tell
Again, trying too hard.
Criss would have to comment on any more specific tweaking he may have (or have not) made for e.g., Claude, but the most succinct and immediately understandable are from Claude and GPT.
Very interesting Jim, thanks!
P.S. fascinating!
Thanks. That’s what I expected – CS 101
(() => {
const app = Application(“DEVONthink”);
app.includeStandardAdditions = true;
const avifCount = app.selectedRecords()
.filter(r => r.filename().toLowerCase().endsWith('.avif')).length;
app.displayAlert(`Found ${avifCount} AVIF record${avifCount !== 1 ? 's' : ''}`)
})
IMO, the AI-generated code is good for beginners to get an idea of JS (and perhaps AppleScript, too). But in both cases, a seasoned programmer would write different code. In AS, something like
set avifCount to count of (selected records whose filename ends with ".avif")
The repeat and forEach approach are kind of “brute force”, imo.
That is really cringe-worthy. And this line …
if (currentRecord.recordType() === app.dataTypes.PDFDocument) {
?
The right-hand side is undefined and the left-hand side is a string. So this condition should always be false. And then this useless try … catch block which will never tell you where the problem happened.




