@tekmidian/devonthink-mcp 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +383 -20
- package/dist/tools/columnLayout.d.ts +20 -24
- package/dist/tools/columnLayout.js +284 -191
- package/dist/tools/columnLayout.js.map +1 -1
- package/dist/tools/listSmartGroups.d.ts +28 -4
- package/dist/tools/listSmartGroups.js +150 -56
- package/dist/tools/listSmartGroups.js.map +1 -1
- package/dist/tools/listSmartRules.d.ts +9 -6
- package/dist/tools/listSmartRules.js +64 -62
- package/dist/tools/listSmartRules.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columnLayout.js","sourceRoot":"","sources":["../../src/tools/columnLayout.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"columnLayout.js","sourceRoot":"","sources":["../../src/tools/columnLayout.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
* listSmartGroups.ts — Parse DEVONthink SmartGroups.plist to enumerate all smart groups.
|
|
3
3
|
*
|
|
4
4
|
* Smart groups are NOT accessible via the standard MCP or AppleScript scripting dictionary.
|
|
5
|
-
* This tool reads the plist file directly using `plutil -convert
|
|
5
|
+
* This tool reads the plist file directly using `plutil -convert xml1` to return
|
|
6
6
|
* all smart groups with their names, UUIDs, and target database info.
|
|
7
|
+
*
|
|
8
|
+
* Note: `plutil -convert json` fails because the `data` field contains binary NSKeyedArchiver
|
|
9
|
+
* content that cannot be represented in JSON. XML format represents it as base64, which we skip.
|
|
7
10
|
*/
|
|
8
11
|
interface SmartGroupEntry {
|
|
9
12
|
name: string;
|
|
10
13
|
uuid: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
searchString: string | null;
|
|
14
|
+
syncDate: string | null;
|
|
15
|
+
useUuidKey: boolean | null;
|
|
14
16
|
}
|
|
15
17
|
interface SmartGroupsResult {
|
|
16
18
|
success: boolean;
|
|
@@ -18,6 +20,28 @@ interface SmartGroupsResult {
|
|
|
18
20
|
totalCount?: number;
|
|
19
21
|
error?: string;
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Extract top-level <dict>...</dict> blocks from an XML string, respecting nesting.
|
|
25
|
+
* This correctly handles dicts nested inside other dicts or arrays.
|
|
26
|
+
* Returns the INNER content of each top-level dict (not including the <dict> tags themselves).
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractTopLevelDicts(xml: string): string[];
|
|
29
|
+
/** Extract the string value that follows <key>keyName</key><string>...</string> */
|
|
30
|
+
export declare function extractStringAfterKey(content: string, keyName: string): string | null;
|
|
31
|
+
/** Extract the date value that follows <key>keyName</key><date>...</date> */
|
|
32
|
+
export declare function extractDateAfterKey(content: string, keyName: string): string | null;
|
|
33
|
+
/** Extract boolean (<true/> or <false/>) that follows <key>keyName</key> */
|
|
34
|
+
export declare function extractBoolAfterKey(content: string, keyName: string): boolean | null;
|
|
35
|
+
/** Extract the integer value that follows <key>keyName</key><integer>...</integer> */
|
|
36
|
+
export declare function extractIntegerAfterKey(content: string, keyName: string): number | null;
|
|
37
|
+
/** Extract the real value that follows <key>keyName</key><real>...</real> */
|
|
38
|
+
export declare function extractRealAfterKey(content: string, keyName: string): number | null;
|
|
39
|
+
/**
|
|
40
|
+
* Extract the inner content of the first <dict>...</dict> block that appears
|
|
41
|
+
* immediately after <key>keyName</key>, respecting nesting.
|
|
42
|
+
*/
|
|
43
|
+
export declare function extractSubDictAfterKey(content: string, keyName: string): string | null;
|
|
44
|
+
export declare function escapeRegex(str: string): string;
|
|
21
45
|
export declare const listSmartGroupsTool: {
|
|
22
46
|
name: string;
|
|
23
47
|
description: string;
|
|
@@ -2,14 +2,156 @@
|
|
|
2
2
|
* listSmartGroups.ts — Parse DEVONthink SmartGroups.plist to enumerate all smart groups.
|
|
3
3
|
*
|
|
4
4
|
* Smart groups are NOT accessible via the standard MCP or AppleScript scripting dictionary.
|
|
5
|
-
* This tool reads the plist file directly using `plutil -convert
|
|
5
|
+
* This tool reads the plist file directly using `plutil -convert xml1` to return
|
|
6
6
|
* all smart groups with their names, UUIDs, and target database info.
|
|
7
|
+
*
|
|
8
|
+
* Note: `plutil -convert json` fails because the `data` field contains binary NSKeyedArchiver
|
|
9
|
+
* content that cannot be represented in JSON. XML format represents it as base64, which we skip.
|
|
7
10
|
*/
|
|
8
11
|
import { execSync } from "node:child_process";
|
|
9
12
|
import { homedir } from "node:os";
|
|
10
13
|
import { join } from "node:path";
|
|
11
14
|
import { existsSync } from "node:fs";
|
|
12
15
|
const PLIST_PATH = join(homedir(), "Library", "Application Support", "DEVONthink", "SmartGroups.plist");
|
|
16
|
+
/**
|
|
17
|
+
* Extract top-level <dict>...</dict> blocks from an XML string, respecting nesting.
|
|
18
|
+
* This correctly handles dicts nested inside other dicts or arrays.
|
|
19
|
+
* Returns the INNER content of each top-level dict (not including the <dict> tags themselves).
|
|
20
|
+
*/
|
|
21
|
+
export function extractTopLevelDicts(xml) {
|
|
22
|
+
const results = [];
|
|
23
|
+
let pos = 0;
|
|
24
|
+
while (pos < xml.length) {
|
|
25
|
+
const openIdx = xml.indexOf("<dict>", pos);
|
|
26
|
+
if (openIdx === -1)
|
|
27
|
+
break;
|
|
28
|
+
// Walk forward tracking depth to find the matching </dict>
|
|
29
|
+
let depth = 1;
|
|
30
|
+
let cur = openIdx + 6; // skip past "<dict>"
|
|
31
|
+
while (cur < xml.length && depth > 0) {
|
|
32
|
+
const nextOpen = xml.indexOf("<dict>", cur);
|
|
33
|
+
const nextClose = xml.indexOf("</dict>", cur);
|
|
34
|
+
if (nextClose === -1)
|
|
35
|
+
break; // malformed XML
|
|
36
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
37
|
+
depth++;
|
|
38
|
+
cur = nextOpen + 6;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
depth--;
|
|
42
|
+
if (depth === 0) {
|
|
43
|
+
// Extract inner content (between <dict> and </dict>)
|
|
44
|
+
results.push(xml.slice(openIdx + 6, nextClose));
|
|
45
|
+
pos = nextClose + 7; // skip past "</dict>"
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
cur = nextClose + 7;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (depth !== 0)
|
|
52
|
+
break; // safety: malformed XML, stop
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
}
|
|
56
|
+
/** Extract the string value that follows <key>keyName</key><string>...</string> */
|
|
57
|
+
export function extractStringAfterKey(content, keyName) {
|
|
58
|
+
const pattern = new RegExp(`<key>${escapeRegex(keyName)}</key>\\s*<string>([^<]*)<\\/string>`);
|
|
59
|
+
const m = pattern.exec(content);
|
|
60
|
+
return m ? m[1].trim() : null;
|
|
61
|
+
}
|
|
62
|
+
/** Extract the date value that follows <key>keyName</key><date>...</date> */
|
|
63
|
+
export function extractDateAfterKey(content, keyName) {
|
|
64
|
+
const pattern = new RegExp(`<key>${escapeRegex(keyName)}</key>\\s*<date>([^<]*)<\\/date>`);
|
|
65
|
+
const m = pattern.exec(content);
|
|
66
|
+
return m ? m[1].trim() : null;
|
|
67
|
+
}
|
|
68
|
+
/** Extract boolean (<true/> or <false/>) that follows <key>keyName</key> */
|
|
69
|
+
export function extractBoolAfterKey(content, keyName) {
|
|
70
|
+
const pattern = new RegExp(`<key>${escapeRegex(keyName)}</key>\\s*<(true|false)\\/>`);
|
|
71
|
+
const m = pattern.exec(content);
|
|
72
|
+
if (!m)
|
|
73
|
+
return null;
|
|
74
|
+
return m[1] === "true";
|
|
75
|
+
}
|
|
76
|
+
/** Extract the integer value that follows <key>keyName</key><integer>...</integer> */
|
|
77
|
+
export function extractIntegerAfterKey(content, keyName) {
|
|
78
|
+
const pattern = new RegExp(`<key>${escapeRegex(keyName)}</key>\\s*<integer>([^<]*)<\\/integer>`);
|
|
79
|
+
const m = pattern.exec(content);
|
|
80
|
+
return m ? parseInt(m[1].trim(), 10) : null;
|
|
81
|
+
}
|
|
82
|
+
/** Extract the real value that follows <key>keyName</key><real>...</real> */
|
|
83
|
+
export function extractRealAfterKey(content, keyName) {
|
|
84
|
+
const pattern = new RegExp(`<key>${escapeRegex(keyName)}</key>\\s*<real>([^<]*)<\\/real>`);
|
|
85
|
+
const m = pattern.exec(content);
|
|
86
|
+
return m ? parseFloat(m[1].trim()) : null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extract the inner content of the first <dict>...</dict> block that appears
|
|
90
|
+
* immediately after <key>keyName</key>, respecting nesting.
|
|
91
|
+
*/
|
|
92
|
+
export function extractSubDictAfterKey(content, keyName) {
|
|
93
|
+
const keyPattern = new RegExp(`<key>${escapeRegex(keyName)}</key>\\s*<dict>`);
|
|
94
|
+
const keyMatch = keyPattern.exec(content);
|
|
95
|
+
if (!keyMatch)
|
|
96
|
+
return null;
|
|
97
|
+
// Walk forward from the end of "<dict>" to find the matching "</dict>"
|
|
98
|
+
let depth = 1;
|
|
99
|
+
let pos = keyMatch.index + keyMatch[0].length;
|
|
100
|
+
const startPos = pos;
|
|
101
|
+
while (pos < content.length && depth > 0) {
|
|
102
|
+
const openIdx = content.indexOf("<dict>", pos);
|
|
103
|
+
const closeIdx = content.indexOf("</dict>", pos);
|
|
104
|
+
if (closeIdx === -1)
|
|
105
|
+
break;
|
|
106
|
+
if (openIdx !== -1 && openIdx < closeIdx) {
|
|
107
|
+
depth++;
|
|
108
|
+
pos = openIdx + 6;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
depth--;
|
|
112
|
+
if (depth === 0) {
|
|
113
|
+
return content.slice(startPos, closeIdx);
|
|
114
|
+
}
|
|
115
|
+
pos = closeIdx + 7;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
export function escapeRegex(str) {
|
|
121
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Parse a plist XML string (output of `plutil -convert xml1`) and return
|
|
125
|
+
* an array of smart group entries.
|
|
126
|
+
*
|
|
127
|
+
* The top-level structure is an array of dicts. Each dict has:
|
|
128
|
+
* - name (string)
|
|
129
|
+
* - UseUUIDKey (bool)
|
|
130
|
+
* - data (binary blob — skipped)
|
|
131
|
+
* - settings (dict — skipped)
|
|
132
|
+
* - sync (dict containing UUID string and date)
|
|
133
|
+
*/
|
|
134
|
+
function parsePlistXmlToSmartGroups(xml) {
|
|
135
|
+
const topLevelDicts = extractTopLevelDicts(xml);
|
|
136
|
+
const results = [];
|
|
137
|
+
for (const dictContent of topLevelDicts) {
|
|
138
|
+
const name = extractStringAfterKey(dictContent, "name");
|
|
139
|
+
const useUuidKey = extractBoolAfterKey(dictContent, "UseUUIDKey");
|
|
140
|
+
const syncContent = extractSubDictAfterKey(dictContent, "sync");
|
|
141
|
+
const uuid = syncContent ? extractStringAfterKey(syncContent, "UUID") : null;
|
|
142
|
+
const syncDate = syncContent ? extractDateAfterKey(syncContent, "date") : null;
|
|
143
|
+
// Skip entries with no name or UUID
|
|
144
|
+
if (!name && !uuid)
|
|
145
|
+
continue;
|
|
146
|
+
results.push({
|
|
147
|
+
name: name ?? "(unnamed)",
|
|
148
|
+
uuid: uuid ?? "",
|
|
149
|
+
syncDate: syncDate ?? null,
|
|
150
|
+
useUuidKey: useUuidKey,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
13
155
|
const listSmartGroups = async () => {
|
|
14
156
|
if (!existsSync(PLIST_PATH)) {
|
|
15
157
|
return {
|
|
@@ -17,9 +159,9 @@ const listSmartGroups = async () => {
|
|
|
17
159
|
error: `SmartGroups.plist not found at: ${PLIST_PATH}. Ensure DEVONthink has been run at least once.`,
|
|
18
160
|
};
|
|
19
161
|
}
|
|
20
|
-
let
|
|
162
|
+
let xml;
|
|
21
163
|
try {
|
|
22
|
-
|
|
164
|
+
xml = execSync(`plutil -convert xml1 -o - "${PLIST_PATH}"`, {
|
|
23
165
|
encoding: "utf-8",
|
|
24
166
|
timeout: 10000,
|
|
25
167
|
});
|
|
@@ -30,64 +172,16 @@ const listSmartGroups = async () => {
|
|
|
30
172
|
error: `Failed to parse SmartGroups.plist: ${err instanceof Error ? err.message : String(err)}`,
|
|
31
173
|
};
|
|
32
174
|
}
|
|
33
|
-
let
|
|
175
|
+
let smartGroups;
|
|
34
176
|
try {
|
|
35
|
-
|
|
177
|
+
smartGroups = parsePlistXmlToSmartGroups(xml);
|
|
36
178
|
}
|
|
37
179
|
catch (err) {
|
|
38
180
|
return {
|
|
39
181
|
success: false,
|
|
40
|
-
error: `Failed to parse
|
|
182
|
+
error: `Failed to parse XML output from plutil: ${err instanceof Error ? err.message : String(err)}`,
|
|
41
183
|
};
|
|
42
184
|
}
|
|
43
|
-
// The plist is an array of smart group objects at the top level.
|
|
44
|
-
// Each entry has at minimum: "Name" (string), "UUID" (string),
|
|
45
|
-
// and a "Data" blob containing the predicate/target info.
|
|
46
|
-
// We extract what we can without decoding the binary predicate.
|
|
47
|
-
if (!Array.isArray(parsed)) {
|
|
48
|
-
// Some DEVONthink versions wrap in a dict with a root key
|
|
49
|
-
const asDict = parsed;
|
|
50
|
-
const keys = Object.keys(asDict);
|
|
51
|
-
const arrayKey = keys.find((k) => Array.isArray(asDict[k]));
|
|
52
|
-
if (arrayKey) {
|
|
53
|
-
parsed = asDict[arrayKey];
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
// It might be a flat dict where each key is a UUID
|
|
57
|
-
const smartGroups = [];
|
|
58
|
-
for (const [key, value] of Object.entries(asDict)) {
|
|
59
|
-
if (value && typeof value === "object") {
|
|
60
|
-
const entry = value;
|
|
61
|
-
smartGroups.push({
|
|
62
|
-
uuid: key,
|
|
63
|
-
name: entry["Name"] ?? entry["name"] ?? key,
|
|
64
|
-
databaseUuid: entry["DatabaseUUID"] ?? null,
|
|
65
|
-
groupUuid: entry["GroupUUID"] ?? null,
|
|
66
|
-
searchString: entry["SearchString"] ?? entry["predicateString"] ?? null,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return { success: true, smartGroups, totalCount: smartGroups.length };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const entries = parsed;
|
|
74
|
-
const smartGroups = entries.map((entry) => ({
|
|
75
|
-
name: entry["Name"] ??
|
|
76
|
-
entry["name"] ??
|
|
77
|
-
"(unnamed)",
|
|
78
|
-
uuid: entry["UUID"] ??
|
|
79
|
-
entry["uuid"] ??
|
|
80
|
-
"",
|
|
81
|
-
databaseUuid: entry["DatabaseUUID"] ??
|
|
82
|
-
entry["databaseUUID"] ??
|
|
83
|
-
null,
|
|
84
|
-
groupUuid: entry["GroupUUID"] ??
|
|
85
|
-
entry["groupUUID"] ??
|
|
86
|
-
null,
|
|
87
|
-
searchString: entry["SearchString"] ??
|
|
88
|
-
entry["predicateString"] ??
|
|
89
|
-
null,
|
|
90
|
-
}));
|
|
91
185
|
// Sort alphabetically by name for convenience
|
|
92
186
|
smartGroups.sort((a, b) => a.name.localeCompare(b.name));
|
|
93
187
|
return { success: true, smartGroups, totalCount: smartGroups.length };
|
|
@@ -95,9 +189,9 @@ const listSmartGroups = async () => {
|
|
|
95
189
|
export const listSmartGroupsTool = {
|
|
96
190
|
name: "list_smart_groups",
|
|
97
191
|
description: "List all DEVONthink smart groups by parsing SmartGroups.plist. " +
|
|
98
|
-
"Returns name, UUID
|
|
192
|
+
"Returns name, UUID (from sync.UUID), sync date, and UseUUIDKey flag for each smart group. " +
|
|
99
193
|
"Smart groups are NOT accessible via the standard AppleScript API — this is the only way to enumerate them. " +
|
|
100
|
-
"Use the returned
|
|
194
|
+
"Use the returned uuid with the search tool (groupUuid parameter) to query the contents of a smart group.",
|
|
101
195
|
inputSchema: {
|
|
102
196
|
type: "object",
|
|
103
197
|
properties: {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listSmartGroups.js","sourceRoot":"","sources":["../../src/tools/listSmartGroups.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"listSmartGroups.js","sourceRoot":"","sources":["../../src/tools/listSmartGroups.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,17 +2,20 @@
|
|
|
2
2
|
* listSmartRules.ts — Parse DEVONthink SmartRules.plist to enumerate all smart rules.
|
|
3
3
|
*
|
|
4
4
|
* Smart rules are NOT accessible via the standard MCP or AppleScript scripting dictionary.
|
|
5
|
-
* This tool reads the plist file directly using `plutil -convert
|
|
6
|
-
* all smart rules with their names, UUIDs,
|
|
5
|
+
* This tool reads the plist file directly using `plutil -convert xml1` to return
|
|
6
|
+
* all smart rules with their names, UUIDs, enabled state, and other metadata.
|
|
7
|
+
*
|
|
8
|
+
* Note: `plutil -convert json` fails because the `data` field contains binary NSKeyedArchiver
|
|
9
|
+
* content that cannot be represented in JSON. XML format represents it as base64, which we skip.
|
|
7
10
|
*/
|
|
8
11
|
interface SmartRuleEntry {
|
|
9
12
|
name: string;
|
|
10
13
|
uuid: string;
|
|
11
14
|
enabled: boolean | null;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
indexOffset: number | null;
|
|
16
|
+
lastExecution: number | null;
|
|
17
|
+
syncDate: string | null;
|
|
18
|
+
useUuidKey: boolean | null;
|
|
16
19
|
}
|
|
17
20
|
interface SmartRulesResult {
|
|
18
21
|
success: boolean;
|
|
@@ -2,14 +2,69 @@
|
|
|
2
2
|
* listSmartRules.ts — Parse DEVONthink SmartRules.plist to enumerate all smart rules.
|
|
3
3
|
*
|
|
4
4
|
* Smart rules are NOT accessible via the standard MCP or AppleScript scripting dictionary.
|
|
5
|
-
* This tool reads the plist file directly using `plutil -convert
|
|
6
|
-
* all smart rules with their names, UUIDs,
|
|
5
|
+
* This tool reads the plist file directly using `plutil -convert xml1` to return
|
|
6
|
+
* all smart rules with their names, UUIDs, enabled state, and other metadata.
|
|
7
|
+
*
|
|
8
|
+
* Note: `plutil -convert json` fails because the `data` field contains binary NSKeyedArchiver
|
|
9
|
+
* content that cannot be represented in JSON. XML format represents it as base64, which we skip.
|
|
7
10
|
*/
|
|
8
11
|
import { execSync } from "node:child_process";
|
|
9
12
|
import { homedir } from "node:os";
|
|
10
13
|
import { join } from "node:path";
|
|
11
14
|
import { existsSync } from "node:fs";
|
|
15
|
+
import { extractStringAfterKey, extractBoolAfterKey, extractIntegerAfterKey, extractRealAfterKey, extractDateAfterKey, extractSubDictAfterKey, extractTopLevelDicts, } from "./listSmartGroups.js";
|
|
12
16
|
const PLIST_PATH = join(homedir(), "Library", "Application Support", "DEVONthink", "SmartRules.plist");
|
|
17
|
+
/**
|
|
18
|
+
* Parse a plist XML string (output of `plutil -convert xml1`) and return
|
|
19
|
+
* an array of smart rule entries.
|
|
20
|
+
*
|
|
21
|
+
* The top-level structure is:
|
|
22
|
+
* <plist><array>
|
|
23
|
+
* <dict>
|
|
24
|
+
* <key>Enabled</key><true/> or <false/>
|
|
25
|
+
* <key>IndexOffset</key><integer>...</integer>
|
|
26
|
+
* <key>LastExecution</key><real>...</real>
|
|
27
|
+
* <key>UseUUIDKey</key><true/> or <false/>
|
|
28
|
+
* <key>data</key><data>...</data> <!-- binary, skip -->
|
|
29
|
+
* <key>name</key><string>...</string>
|
|
30
|
+
* <key>settings</key><dict>...</dict> <!-- skip -->
|
|
31
|
+
* <key>sync</key>
|
|
32
|
+
* <dict>
|
|
33
|
+
* <key>UUID</key><string>...</string>
|
|
34
|
+
* <key>date</key><date>...</date>
|
|
35
|
+
* </dict>
|
|
36
|
+
* </dict>
|
|
37
|
+
* ...
|
|
38
|
+
* </array></plist>
|
|
39
|
+
*/
|
|
40
|
+
function parsePlistXmlToSmartRules(xml) {
|
|
41
|
+
const topLevelDicts = extractTopLevelDicts(xml);
|
|
42
|
+
const results = [];
|
|
43
|
+
for (const dictContent of topLevelDicts) {
|
|
44
|
+
const name = extractStringAfterKey(dictContent, "name");
|
|
45
|
+
const enabled = extractBoolAfterKey(dictContent, "Enabled");
|
|
46
|
+
const indexOffset = extractIntegerAfterKey(dictContent, "IndexOffset");
|
|
47
|
+
const lastExecution = extractRealAfterKey(dictContent, "LastExecution");
|
|
48
|
+
const useUuidKey = extractBoolAfterKey(dictContent, "UseUUIDKey");
|
|
49
|
+
// The sync sub-dict: find the <dict> that follows the <key>sync</key>
|
|
50
|
+
const syncContent = extractSubDictAfterKey(dictContent, "sync");
|
|
51
|
+
const uuid = syncContent ? extractStringAfterKey(syncContent, "UUID") : null;
|
|
52
|
+
const syncDate = syncContent ? extractDateAfterKey(syncContent, "date") : null;
|
|
53
|
+
// Skip entries with no name or UUID (shouldn't happen, but be defensive)
|
|
54
|
+
if (!name && !uuid)
|
|
55
|
+
continue;
|
|
56
|
+
results.push({
|
|
57
|
+
name: name ?? "(unnamed)",
|
|
58
|
+
uuid: uuid ?? "",
|
|
59
|
+
enabled: enabled,
|
|
60
|
+
indexOffset: indexOffset,
|
|
61
|
+
lastExecution: lastExecution,
|
|
62
|
+
syncDate: syncDate ?? null,
|
|
63
|
+
useUuidKey: useUuidKey,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return results;
|
|
67
|
+
}
|
|
13
68
|
const listSmartRules = async () => {
|
|
14
69
|
if (!existsSync(PLIST_PATH)) {
|
|
15
70
|
return {
|
|
@@ -17,9 +72,9 @@ const listSmartRules = async () => {
|
|
|
17
72
|
error: `SmartRules.plist not found at: ${PLIST_PATH}. Ensure DEVONthink has been run at least once and smart rules have been created.`,
|
|
18
73
|
};
|
|
19
74
|
}
|
|
20
|
-
let
|
|
75
|
+
let xml;
|
|
21
76
|
try {
|
|
22
|
-
|
|
77
|
+
xml = execSync(`plutil -convert xml1 -o - "${PLIST_PATH}"`, {
|
|
23
78
|
encoding: "utf-8",
|
|
24
79
|
timeout: 10000,
|
|
25
80
|
});
|
|
@@ -30,70 +85,16 @@ const listSmartRules = async () => {
|
|
|
30
85
|
error: `Failed to parse SmartRules.plist: ${err instanceof Error ? err.message : String(err)}`,
|
|
31
86
|
};
|
|
32
87
|
}
|
|
33
|
-
let
|
|
88
|
+
let smartRules;
|
|
34
89
|
try {
|
|
35
|
-
|
|
90
|
+
smartRules = parsePlistXmlToSmartRules(xml);
|
|
36
91
|
}
|
|
37
92
|
catch (err) {
|
|
38
93
|
return {
|
|
39
94
|
success: false,
|
|
40
|
-
error: `Failed to parse
|
|
95
|
+
error: `Failed to parse XML output from plutil: ${err instanceof Error ? err.message : String(err)}`,
|
|
41
96
|
};
|
|
42
97
|
}
|
|
43
|
-
// Normalise to an array of entries
|
|
44
|
-
if (!Array.isArray(parsed)) {
|
|
45
|
-
const asDict = parsed;
|
|
46
|
-
const keys = Object.keys(asDict);
|
|
47
|
-
const arrayKey = keys.find((k) => Array.isArray(asDict[k]));
|
|
48
|
-
if (arrayKey) {
|
|
49
|
-
parsed = asDict[arrayKey];
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
// Flat dict: each key is a UUID
|
|
53
|
-
const smartRules = [];
|
|
54
|
-
for (const [key, value] of Object.entries(asDict)) {
|
|
55
|
-
if (value && typeof value === "object") {
|
|
56
|
-
const entry = value;
|
|
57
|
-
smartRules.push({
|
|
58
|
-
uuid: key,
|
|
59
|
-
name: entry["Name"] ?? entry["name"] ?? key,
|
|
60
|
-
enabled: typeof entry["Enabled"] === "boolean" ? entry["Enabled"] : null,
|
|
61
|
-
trigger: entry["Trigger"] ?? entry["trigger"] ?? null,
|
|
62
|
-
searchString: entry["SearchString"] ?? null,
|
|
63
|
-
databaseUuid: entry["DatabaseUUID"] ?? null,
|
|
64
|
-
groupUuid: entry["GroupUUID"] ?? null,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return { success: true, smartRules, totalCount: smartRules.length };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
const entries = parsed;
|
|
72
|
-
const smartRules = entries.map((entry) => ({
|
|
73
|
-
name: entry["Name"] ??
|
|
74
|
-
entry["name"] ??
|
|
75
|
-
"(unnamed)",
|
|
76
|
-
uuid: entry["UUID"] ??
|
|
77
|
-
entry["uuid"] ??
|
|
78
|
-
"",
|
|
79
|
-
enabled: typeof entry["Enabled"] === "boolean"
|
|
80
|
-
? entry["Enabled"]
|
|
81
|
-
: typeof entry["enabled"] === "boolean"
|
|
82
|
-
? entry["enabled"]
|
|
83
|
-
: null,
|
|
84
|
-
trigger: entry["Trigger"] ??
|
|
85
|
-
entry["trigger"] ??
|
|
86
|
-
null,
|
|
87
|
-
searchString: entry["SearchString"] ??
|
|
88
|
-
entry["predicateString"] ??
|
|
89
|
-
null,
|
|
90
|
-
databaseUuid: entry["DatabaseUUID"] ??
|
|
91
|
-
entry["databaseUUID"] ??
|
|
92
|
-
null,
|
|
93
|
-
groupUuid: entry["GroupUUID"] ??
|
|
94
|
-
entry["groupUUID"] ??
|
|
95
|
-
null,
|
|
96
|
-
}));
|
|
97
98
|
// Sort alphabetically by name
|
|
98
99
|
smartRules.sort((a, b) => a.name.localeCompare(b.name));
|
|
99
100
|
return { success: true, smartRules, totalCount: smartRules.length };
|
|
@@ -101,7 +102,8 @@ const listSmartRules = async () => {
|
|
|
101
102
|
export const listSmartRulesTool = {
|
|
102
103
|
name: "list_smart_rules",
|
|
103
104
|
description: "List all DEVONthink smart rules by parsing SmartRules.plist. " +
|
|
104
|
-
"Returns name, UUID, enabled state,
|
|
105
|
+
"Returns name, UUID (from sync.UUID), enabled state, indexOffset, lastExecution timestamp, " +
|
|
106
|
+
"and sync date for each rule. " +
|
|
105
107
|
"Smart rules are NOT accessible via the standard AppleScript API — this is the only way to enumerate them.",
|
|
106
108
|
inputSchema: {
|
|
107
109
|
type: "object",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listSmartRules.js","sourceRoot":"","sources":["../../src/tools/listSmartRules.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"listSmartRules.js","sourceRoot":"","sources":["../../src/tools/listSmartRules.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED