@tekmidian/devon 3.0.0

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.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +472 -0
  3. package/dist/index.d.ts +12 -0
  4. package/dist/index.js +82 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/jxa/escape.d.ts +22 -0
  7. package/dist/jxa/escape.js +38 -0
  8. package/dist/jxa/escape.js.map +1 -0
  9. package/dist/jxa/executor.d.ts +32 -0
  10. package/dist/jxa/executor.js +65 -0
  11. package/dist/jxa/executor.js.map +1 -0
  12. package/dist/jxa/helpers.d.ts +51 -0
  13. package/dist/jxa/helpers.js +136 -0
  14. package/dist/jxa/helpers.js.map +1 -0
  15. package/dist/jxa/types.d.ts +33 -0
  16. package/dist/jxa/types.js +43 -0
  17. package/dist/jxa/types.js.map +1 -0
  18. package/dist/server.d.ts +10 -0
  19. package/dist/server.js +65 -0
  20. package/dist/server.js.map +1 -0
  21. package/dist/setup.d.ts +12 -0
  22. package/dist/setup.js +323 -0
  23. package/dist/setup.js.map +1 -0
  24. package/dist/shared/plist/parser.d.ts +29 -0
  25. package/dist/shared/plist/parser.js +112 -0
  26. package/dist/shared/plist/parser.js.map +1 -0
  27. package/dist/shared/plist/reader.d.ts +31 -0
  28. package/dist/shared/plist/reader.js +127 -0
  29. package/dist/shared/plist/reader.js.map +1 -0
  30. package/dist/shared/shell.d.ts +15 -0
  31. package/dist/shared/shell.js +22 -0
  32. package/dist/shared/shell.js.map +1 -0
  33. package/dist/tools/ai/ask-ai-about-documents.d.ts +8 -0
  34. package/dist/tools/ai/ask-ai-about-documents.js +88 -0
  35. package/dist/tools/ai/ask-ai-about-documents.js.map +1 -0
  36. package/dist/tools/ai/check-ai-health.d.ts +8 -0
  37. package/dist/tools/ai/check-ai-health.js +40 -0
  38. package/dist/tools/ai/check-ai-health.js.map +1 -0
  39. package/dist/tools/ai/create-summary-document.d.ts +8 -0
  40. package/dist/tools/ai/create-summary-document.js +102 -0
  41. package/dist/tools/ai/create-summary-document.js.map +1 -0
  42. package/dist/tools/ai/get-ai-tool-documentation.d.ts +8 -0
  43. package/dist/tools/ai/get-ai-tool-documentation.js +220 -0
  44. package/dist/tools/ai/get-ai-tool-documentation.js.map +1 -0
  45. package/dist/tools/application/is-running.d.ts +7 -0
  46. package/dist/tools/application/is-running.js +24 -0
  47. package/dist/tools/application/is-running.js.map +1 -0
  48. package/dist/tools/custom/column-layout.d.ts +9 -0
  49. package/dist/tools/custom/column-layout.js +244 -0
  50. package/dist/tools/custom/column-layout.js.map +1 -0
  51. package/dist/tools/custom/list-smart-groups.d.ts +8 -0
  52. package/dist/tools/custom/list-smart-groups.js +79 -0
  53. package/dist/tools/custom/list-smart-groups.js.map +1 -0
  54. package/dist/tools/custom/list-smart-rules.d.ts +8 -0
  55. package/dist/tools/custom/list-smart-rules.js +85 -0
  56. package/dist/tools/custom/list-smart-rules.js.map +1 -0
  57. package/dist/tools/custom/parse-eml-headers.d.ts +8 -0
  58. package/dist/tools/custom/parse-eml-headers.js +155 -0
  59. package/dist/tools/custom/parse-eml-headers.js.map +1 -0
  60. package/dist/tools/database/get-current-database.d.ts +7 -0
  61. package/dist/tools/database/get-current-database.js +29 -0
  62. package/dist/tools/database/get-current-database.js.map +1 -0
  63. package/dist/tools/database/get-open-databases.d.ts +6 -0
  64. package/dist/tools/database/get-open-databases.js +32 -0
  65. package/dist/tools/database/get-open-databases.js.map +1 -0
  66. package/dist/tools/groups/get-selected-records.d.ts +8 -0
  67. package/dist/tools/groups/get-selected-records.js +30 -0
  68. package/dist/tools/groups/get-selected-records.js.map +1 -0
  69. package/dist/tools/groups/list-group-content.d.ts +8 -0
  70. package/dist/tools/groups/list-group-content.js +65 -0
  71. package/dist/tools/groups/list-group-content.js.map +1 -0
  72. package/dist/tools/index.d.ts +9 -0
  73. package/dist/tools/index.js +87 -0
  74. package/dist/tools/index.js.map +1 -0
  75. package/dist/tools/intelligence/classify.d.ts +8 -0
  76. package/dist/tools/intelligence/classify.js +83 -0
  77. package/dist/tools/intelligence/classify.js.map +1 -0
  78. package/dist/tools/intelligence/compare.d.ts +8 -0
  79. package/dist/tools/intelligence/compare.js +121 -0
  80. package/dist/tools/intelligence/compare.js.map +1 -0
  81. package/dist/tools/records/convert-record.d.ts +10 -0
  82. package/dist/tools/records/convert-record.js +77 -0
  83. package/dist/tools/records/convert-record.js.map +1 -0
  84. package/dist/tools/records/create-record.d.ts +8 -0
  85. package/dist/tools/records/create-record.js +64 -0
  86. package/dist/tools/records/create-record.js.map +1 -0
  87. package/dist/tools/records/delete-record.d.ts +9 -0
  88. package/dist/tools/records/delete-record.js +50 -0
  89. package/dist/tools/records/delete-record.js.map +1 -0
  90. package/dist/tools/records/duplicate-record.d.ts +8 -0
  91. package/dist/tools/records/duplicate-record.js +53 -0
  92. package/dist/tools/records/duplicate-record.js.map +1 -0
  93. package/dist/tools/records/get-record-by-id.d.ts +8 -0
  94. package/dist/tools/records/get-record-by-id.js +51 -0
  95. package/dist/tools/records/get-record-by-id.js.map +1 -0
  96. package/dist/tools/records/get-record-content.d.ts +7 -0
  97. package/dist/tools/records/get-record-content.js +48 -0
  98. package/dist/tools/records/get-record-content.js.map +1 -0
  99. package/dist/tools/records/get-record-properties.d.ts +7 -0
  100. package/dist/tools/records/get-record-properties.js +42 -0
  101. package/dist/tools/records/get-record-properties.js.map +1 -0
  102. package/dist/tools/records/move-record.d.ts +8 -0
  103. package/dist/tools/records/move-record.js +60 -0
  104. package/dist/tools/records/move-record.js.map +1 -0
  105. package/dist/tools/records/rename-record.d.ts +7 -0
  106. package/dist/tools/records/rename-record.js +40 -0
  107. package/dist/tools/records/rename-record.js.map +1 -0
  108. package/dist/tools/records/replicate-record.d.ts +8 -0
  109. package/dist/tools/records/replicate-record.js +53 -0
  110. package/dist/tools/records/replicate-record.js.map +1 -0
  111. package/dist/tools/records/set-record-properties.d.ts +10 -0
  112. package/dist/tools/records/set-record-properties.js +76 -0
  113. package/dist/tools/records/set-record-properties.js.map +1 -0
  114. package/dist/tools/records/update-record-content.d.ts +7 -0
  115. package/dist/tools/records/update-record-content.js +43 -0
  116. package/dist/tools/records/update-record-content.js.map +1 -0
  117. package/dist/tools/search/lookup-record.d.ts +7 -0
  118. package/dist/tools/search/lookup-record.js +160 -0
  119. package/dist/tools/search/lookup-record.js.map +1 -0
  120. package/dist/tools/search/search.d.ts +8 -0
  121. package/dist/tools/search/search.js +146 -0
  122. package/dist/tools/search/search.js.map +1 -0
  123. package/dist/tools/tags/add-tags.d.ts +7 -0
  124. package/dist/tools/tags/add-tags.js +47 -0
  125. package/dist/tools/tags/add-tags.js.map +1 -0
  126. package/dist/tools/tags/remove-tags.d.ts +7 -0
  127. package/dist/tools/tags/remove-tags.js +53 -0
  128. package/dist/tools/tags/remove-tags.js.map +1 -0
  129. package/dist/tools/web/create-from-url.d.ts +8 -0
  130. package/dist/tools/web/create-from-url.js +140 -0
  131. package/dist/tools/web/create-from-url.js.map +1 -0
  132. package/package.json +48 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * executor.ts — JXA (JavaScript for Automation) execution via osascript.
3
+ *
4
+ * Provides a JxaExecutor interface for testability (mock in tests)
5
+ * and an OsascriptExecutor implementation that shells out to osascript.
6
+ */
7
+ import { execSync } from "node:child_process";
8
+ import { shellQuote } from "./escape.js";
9
+ /** Default buffer limit: 50 MB (prevents OOM on large DB queries) */
10
+ const MAX_BUFFER = 50 * 1024 * 1024;
11
+ /** Default timeout: 60 seconds */
12
+ const DEFAULT_TIMEOUT = 60_000;
13
+ /**
14
+ * Production executor that runs JXA via `osascript -l JavaScript`.
15
+ */
16
+ export class OsascriptExecutor {
17
+ timeout;
18
+ maxBuffer;
19
+ constructor(opts) {
20
+ this.timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
21
+ this.maxBuffer = opts?.maxBuffer ?? MAX_BUFFER;
22
+ }
23
+ run(script) {
24
+ try {
25
+ const stdout = execSync(`osascript -l JavaScript -e ${shellQuote(script)}`, {
26
+ encoding: "utf-8",
27
+ timeout: this.timeout,
28
+ maxBuffer: this.maxBuffer,
29
+ // Capture stderr separately so DEVONthink deprecation warnings don't crash
30
+ stdio: ["pipe", "pipe", "pipe"],
31
+ });
32
+ return { stdout: stdout.trimEnd(), stderr: "" };
33
+ }
34
+ catch (err) {
35
+ // execSync throws on non-zero exit. Extract stderr if available.
36
+ if (err && typeof err === "object" && "stderr" in err) {
37
+ const execErr = err;
38
+ const stderr = (typeof execErr.stderr === "string" ? execErr.stderr : "").trim();
39
+ const stdout = (typeof execErr.stdout === "string" ? execErr.stdout : "").trim();
40
+ // If we got stdout despite non-zero exit, the script may have succeeded
41
+ // but osascript returned a warning on stderr. Treat as success if stdout is non-empty.
42
+ if (stdout && stderr && !stderr.includes("Error")) {
43
+ return { stdout, stderr };
44
+ }
45
+ throw new Error(stderr || execErr.message || "JXA execution failed");
46
+ }
47
+ throw err;
48
+ }
49
+ }
50
+ }
51
+ /** Singleton executor for production use */
52
+ let defaultExecutor = null;
53
+ export function getDefaultExecutor() {
54
+ if (!defaultExecutor) {
55
+ defaultExecutor = new OsascriptExecutor();
56
+ }
57
+ return defaultExecutor;
58
+ }
59
+ /**
60
+ * Override the default executor (for testing).
61
+ */
62
+ export function setDefaultExecutor(executor) {
63
+ defaultExecutor = executor;
64
+ }
65
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/jxa/executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAazC,qEAAqE;AACrE,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpC,kCAAkC;AAClC,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B;;GAEG;AACH,MAAM,OAAO,iBAAiB;IACpB,OAAO,CAAS;IAChB,SAAS,CAAS;IAE1B,YAAY,IAA+C;QACzD,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,eAAe,CAAC;QAChD,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,UAAU,CAAC;IACjD,CAAC;IAED,GAAG,CAAC,MAAc;QAChB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,QAAQ,CACrB,8BAA8B,UAAU,CAAC,MAAM,CAAC,EAAE,EAClD;gBACE,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,2EAA2E;gBAC3E,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aAChC,CACF,CAAC;YACF,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAClD,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,iEAAiE;YACjE,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;gBACtD,MAAM,OAAO,GAAG,GAA6D,CAAC;gBAC9E,MAAM,MAAM,GAAG,CAAC,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACjF,MAAM,MAAM,GAAG,CAAC,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAEjF,wEAAwE;gBACxE,uFAAuF;gBACvF,IAAI,MAAM,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBAClD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;gBAC5B,CAAC;gBAED,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,OAAO,IAAI,sBAAsB,CAAC,CAAC;YACvE,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,4CAA4C;AAC5C,IAAI,eAAe,GAAuB,IAAI,CAAC;AAE/C,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,eAAe,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAC5C,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAqB;IACtD,eAAe,GAAG,QAAQ,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * helpers.ts — Reusable JXA code fragments injected into tool scripts.
3
+ *
4
+ * These are JavaScript strings that get embedded into osascript calls.
5
+ * They handle common operations like finding records by various identifiers,
6
+ * looking up databases, and detecting the DEVONthink version.
7
+ */
8
+ /**
9
+ * JXA helper: Get a reference to the DEVONthink application.
10
+ * Tries "DEVONthink 3" first (v3.x), falls back to "DEVONthink" (v4.x).
11
+ */
12
+ export declare const JXA_APP: string;
13
+ /**
14
+ * JXA helper: Find a record by UUID.
15
+ * Expects `uuid` variable to be defined. Sets `record` variable.
16
+ */
17
+ export declare const JXA_FIND_BY_UUID = "\nvar record = app.getRecordWithUuid(uuid);\nif (!record || !record.uuid()) throw new Error(\"Record not found for UUID: \" + uuid);\n";
18
+ /**
19
+ * JXA helper: Find a record by ID within a database.
20
+ * Expects `recordId` (number) and `db` (database ref) variables. Sets `record` variable.
21
+ */
22
+ export declare const JXA_FIND_BY_ID = "\nvar record = db.getRecordAt(recordId);\nif (!record || !record.uuid()) throw new Error(\"Record not found for ID: \" + recordId);\n";
23
+ /**
24
+ * JXA helper: Find a record by path within a database.
25
+ * Expects `recordPath` (string) and `db` (database ref) variables. Sets `record` variable.
26
+ */
27
+ export declare const JXA_FIND_BY_PATH = "\nvar record = app.getRecordAt(recordPath, {in: db});\nif (!record || !record.uuid()) throw new Error(\"Record not found at path: \" + recordPath);\n";
28
+ /**
29
+ * JXA helper: Resolve a database by name.
30
+ * Expects `dbName` variable (string). Sets `db` variable.
31
+ * If dbName is falsy, uses the current database.
32
+ */
33
+ export declare const JXA_RESOLVE_DB = "\nvar db;\nif (dbName) {\n var dbs = app.databases();\n db = null;\n for (var i = 0; i < dbs.length; i++) {\n if (dbs[i].name() === dbName) { db = dbs[i]; break; }\n }\n if (!db) throw new Error(\"Database not found: \" + dbName);\n} else {\n db = app.currentDatabase();\n}\n";
34
+ /**
35
+ * JXA helper: Resolve a record from uuid, recordId, recordPath, or recordName.
36
+ * Expects the identifying variables to be defined (set to null if not provided).
37
+ * Uses `db` if available for ID/path lookups.
38
+ * Sets `record` variable.
39
+ */
40
+ export declare const JXA_RESOLVE_RECORD = "\nvar record;\nif (uuid) {\n record = app.getRecordWithUuid(uuid);\n if (!record || !record.uuid()) throw new Error(\"Record not found for UUID: \" + uuid);\n} else if (typeof recordId === \"number\" && recordId >= 0) {\n if (!db) throw new Error(\"databaseName is required when using recordId\");\n record = db.getRecordAt(recordId);\n if (!record || !record.uuid()) throw new Error(\"Record not found for ID: \" + recordId);\n} else if (recordPath) {\n if (!db) throw new Error(\"databaseName is required when using recordPath\");\n record = app.getRecordAt(recordPath, {in: db});\n if (!record || !record.uuid()) throw new Error(\"Record not found at path: \" + recordPath);\n} else if (recordName) {\n throw new Error(\"recordName lookup requires using the search tool instead\");\n} else {\n throw new Error(\"One of uuid, recordId, or recordPath must be provided\");\n}\n";
41
+ /**
42
+ * JXA helper: Extract standard record properties into a plain object.
43
+ * Expects `record` variable to be defined.
44
+ * Returns a string expression that evaluates to the properties object.
45
+ */
46
+ export declare const JXA_RECORD_PROPS = "(function() {\n var mimeType = null;\n try { mimeType = record.mime(); } catch(e) {}\n return {\n uuid: record.uuid(),\n name: record.name(),\n type: record.type(),\n path: record.path(),\n location: record.location(),\n database: record.database().name(),\n size: record.size(),\n creationDate: record.creationDate() ? record.creationDate().toISOString() : null,\n modificationDate: record.modificationDate() ? record.modificationDate().toISOString() : null,\n additionDate: record.additionDate() ? record.additionDate().toISOString() : null,\n score: record.score(),\n tags: record.tags(),\n comment: record.comment(),\n url: record.url(),\n referenceUrl: record.referenceURL(),\n kind: record.kind(),\n mimeType: mimeType,\n flagged: record.flag(),\n locking: record.locking(),\n wordCount: record.wordCount(),\n characterCount: record.characterCount(),\n numberOfDuplicates: record.numberOfDuplicates(),\n numberOfReplicants: record.numberOfReplicants(),\n label: record.label(),\n rating: record.rating()\n };\n})()";
47
+ /**
48
+ * JXA helper: Extract basic record properties (for lists/search results).
49
+ * Lighter than full props — used when returning arrays of records.
50
+ */
51
+ export declare const JXA_RECORD_SUMMARY = "({\n uuid: record.uuid(),\n name: record.name(),\n type: record.type(),\n location: record.location(),\n database: record.database().name(),\n tags: record.tags(),\n score: record.score(),\n flagged: record.flag(),\n label: record.label(),\n modificationDate: record.modificationDate() ? record.modificationDate().toISOString() : null\n})";
@@ -0,0 +1,136 @@
1
+ /**
2
+ * helpers.ts — Reusable JXA code fragments injected into tool scripts.
3
+ *
4
+ * These are JavaScript strings that get embedded into osascript calls.
5
+ * They handle common operations like finding records by various identifiers,
6
+ * looking up databases, and detecting the DEVONthink version.
7
+ */
8
+ /**
9
+ * JXA helper: Get a reference to the DEVONthink application.
10
+ * Tries "DEVONthink 3" first (v3.x), falls back to "DEVONthink" (v4.x).
11
+ */
12
+ export const JXA_APP = `
13
+ var app;
14
+ try { app = Application("DEVONthink 3"); app.name(); }
15
+ catch(e) { app = Application("DEVONthink"); }
16
+ `.trim();
17
+ /**
18
+ * JXA helper: Find a record by UUID.
19
+ * Expects `uuid` variable to be defined. Sets `record` variable.
20
+ */
21
+ export const JXA_FIND_BY_UUID = `
22
+ var record = app.getRecordWithUuid(uuid);
23
+ if (!record || !record.uuid()) throw new Error("Record not found for UUID: " + uuid);
24
+ `;
25
+ /**
26
+ * JXA helper: Find a record by ID within a database.
27
+ * Expects `recordId` (number) and `db` (database ref) variables. Sets `record` variable.
28
+ */
29
+ export const JXA_FIND_BY_ID = `
30
+ var record = db.getRecordAt(recordId);
31
+ if (!record || !record.uuid()) throw new Error("Record not found for ID: " + recordId);
32
+ `;
33
+ /**
34
+ * JXA helper: Find a record by path within a database.
35
+ * Expects `recordPath` (string) and `db` (database ref) variables. Sets `record` variable.
36
+ */
37
+ export const JXA_FIND_BY_PATH = `
38
+ var record = app.getRecordAt(recordPath, {in: db});
39
+ if (!record || !record.uuid()) throw new Error("Record not found at path: " + recordPath);
40
+ `;
41
+ /**
42
+ * JXA helper: Resolve a database by name.
43
+ * Expects `dbName` variable (string). Sets `db` variable.
44
+ * If dbName is falsy, uses the current database.
45
+ */
46
+ export const JXA_RESOLVE_DB = `
47
+ var db;
48
+ if (dbName) {
49
+ var dbs = app.databases();
50
+ db = null;
51
+ for (var i = 0; i < dbs.length; i++) {
52
+ if (dbs[i].name() === dbName) { db = dbs[i]; break; }
53
+ }
54
+ if (!db) throw new Error("Database not found: " + dbName);
55
+ } else {
56
+ db = app.currentDatabase();
57
+ }
58
+ `;
59
+ /**
60
+ * JXA helper: Resolve a record from uuid, recordId, recordPath, or recordName.
61
+ * Expects the identifying variables to be defined (set to null if not provided).
62
+ * Uses `db` if available for ID/path lookups.
63
+ * Sets `record` variable.
64
+ */
65
+ export const JXA_RESOLVE_RECORD = `
66
+ var record;
67
+ if (uuid) {
68
+ record = app.getRecordWithUuid(uuid);
69
+ if (!record || !record.uuid()) throw new Error("Record not found for UUID: " + uuid);
70
+ } else if (typeof recordId === "number" && recordId >= 0) {
71
+ if (!db) throw new Error("databaseName is required when using recordId");
72
+ record = db.getRecordAt(recordId);
73
+ if (!record || !record.uuid()) throw new Error("Record not found for ID: " + recordId);
74
+ } else if (recordPath) {
75
+ if (!db) throw new Error("databaseName is required when using recordPath");
76
+ record = app.getRecordAt(recordPath, {in: db});
77
+ if (!record || !record.uuid()) throw new Error("Record not found at path: " + recordPath);
78
+ } else if (recordName) {
79
+ throw new Error("recordName lookup requires using the search tool instead");
80
+ } else {
81
+ throw new Error("One of uuid, recordId, or recordPath must be provided");
82
+ }
83
+ `;
84
+ /**
85
+ * JXA helper: Extract standard record properties into a plain object.
86
+ * Expects `record` variable to be defined.
87
+ * Returns a string expression that evaluates to the properties object.
88
+ */
89
+ export const JXA_RECORD_PROPS = `(function() {
90
+ var mimeType = null;
91
+ try { mimeType = record.mime(); } catch(e) {}
92
+ return {
93
+ uuid: record.uuid(),
94
+ name: record.name(),
95
+ type: record.type(),
96
+ path: record.path(),
97
+ location: record.location(),
98
+ database: record.database().name(),
99
+ size: record.size(),
100
+ creationDate: record.creationDate() ? record.creationDate().toISOString() : null,
101
+ modificationDate: record.modificationDate() ? record.modificationDate().toISOString() : null,
102
+ additionDate: record.additionDate() ? record.additionDate().toISOString() : null,
103
+ score: record.score(),
104
+ tags: record.tags(),
105
+ comment: record.comment(),
106
+ url: record.url(),
107
+ referenceUrl: record.referenceURL(),
108
+ kind: record.kind(),
109
+ mimeType: mimeType,
110
+ flagged: record.flag(),
111
+ locking: record.locking(),
112
+ wordCount: record.wordCount(),
113
+ characterCount: record.characterCount(),
114
+ numberOfDuplicates: record.numberOfDuplicates(),
115
+ numberOfReplicants: record.numberOfReplicants(),
116
+ label: record.label(),
117
+ rating: record.rating()
118
+ };
119
+ })()`;
120
+ /**
121
+ * JXA helper: Extract basic record properties (for lists/search results).
122
+ * Lighter than full props — used when returning arrays of records.
123
+ */
124
+ export const JXA_RECORD_SUMMARY = `({
125
+ uuid: record.uuid(),
126
+ name: record.name(),
127
+ type: record.type(),
128
+ location: record.location(),
129
+ database: record.database().name(),
130
+ tags: record.tags(),
131
+ score: record.score(),
132
+ flagged: record.flag(),
133
+ label: record.label(),
134
+ modificationDate: record.modificationDate() ? record.modificationDate().toISOString() : null
135
+ })`;
136
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/jxa/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;;;;CAItB,CAAC,IAAI,EAAE,CAAC;AAET;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;CAG/B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;CAG7B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;CAG/B,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;CAY7B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;CAkBjC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8B3B,CAAC;AAEN;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;GAW/B,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * types.ts — McpTool interface and defineTool() factory.
3
+ *
4
+ * Each tool is created via defineTool(), which:
5
+ * - Takes a Zod schema for input validation
6
+ * - Converts it to JSON Schema for MCP's inputSchema
7
+ * - Wraps the run function with safeParse for readable errors
8
+ */
9
+ import { z } from "zod";
10
+ import type { JxaExecutor } from "./executor.js";
11
+ /** The shape registered with the MCP server */
12
+ export interface McpTool {
13
+ name: string;
14
+ description: string;
15
+ inputSchema: Record<string, unknown>;
16
+ run: (args: Record<string, unknown>) => Promise<unknown>;
17
+ }
18
+ /** Options for defineTool */
19
+ interface DefineToolOptions<T extends z.ZodTypeAny> {
20
+ name: string;
21
+ description: string;
22
+ schema: T;
23
+ run: (args: z.infer<T>, executor: JxaExecutor) => Promise<unknown>;
24
+ }
25
+ /**
26
+ * Factory that creates an McpTool from a Zod schema and a run function.
27
+ *
28
+ * The Zod schema is converted to JSON Schema for the MCP inputSchema.
29
+ * At runtime, args are validated with safeParse — invalid args get a
30
+ * readable error message instead of throwing.
31
+ */
32
+ export declare function defineTool<T extends z.ZodTypeAny>(opts: DefineToolOptions<T>): McpTool;
33
+ export {};
@@ -0,0 +1,43 @@
1
+ /**
2
+ * types.ts — McpTool interface and defineTool() factory.
3
+ *
4
+ * Each tool is created via defineTool(), which:
5
+ * - Takes a Zod schema for input validation
6
+ * - Converts it to JSON Schema for MCP's inputSchema
7
+ * - Wraps the run function with safeParse for readable errors
8
+ */
9
+ import { zodToJsonSchema } from "zod-to-json-schema";
10
+ import { getDefaultExecutor } from "./executor.js";
11
+ /**
12
+ * Factory that creates an McpTool from a Zod schema and a run function.
13
+ *
14
+ * The Zod schema is converted to JSON Schema for the MCP inputSchema.
15
+ * At runtime, args are validated with safeParse — invalid args get a
16
+ * readable error message instead of throwing.
17
+ */
18
+ export function defineTool(opts) {
19
+ const jsonSchema = zodToJsonSchema(opts.schema, {
20
+ $refStrategy: "none",
21
+ target: "openApi3",
22
+ });
23
+ // Remove the top-level $schema and additionalProperties keys that
24
+ // zodToJsonSchema adds — MCP servers typically don't include these.
25
+ const { $schema: _, ...schemaBody } = jsonSchema;
26
+ return {
27
+ name: opts.name,
28
+ description: opts.description,
29
+ inputSchema: schemaBody,
30
+ run: async (args) => {
31
+ const result = opts.schema.safeParse(args);
32
+ if (!result.success) {
33
+ const issues = result.error.issues
34
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
35
+ .join("; ");
36
+ return { error: `Invalid arguments: ${issues}` };
37
+ }
38
+ const executor = getDefaultExecutor();
39
+ return opts.run(result.data, executor);
40
+ },
41
+ };
42
+ }
43
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/jxa/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAkBnD;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,IAA0B;IAE1B,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE;QAC9C,YAAY,EAAE,MAAM;QACpB,MAAM,EAAE,UAAU;KACnB,CAAC,CAAC;IAEH,kEAAkE;IAClE,oEAAoE;IACpE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,UAAU,EAAE,GAAG,UAAqC,CAAC;IAE5E,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,WAAW,EAAE,UAAU;QACvB,GAAG,EAAE,KAAK,EAAE,IAA6B,EAAE,EAAE;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;qBAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;qBAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO,EAAE,KAAK,EAAE,sBAAsB,MAAM,EAAE,EAAE,CAAC;YACnD,CAAC;YACD,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * server.ts — In-process MCP server for DEVONthink.
3
+ *
4
+ * All 33 tools are our own code under MIT — zero upstream dependency.
5
+ */
6
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
+ export declare const createExtendedServer: () => Promise<{
8
+ server: Server;
9
+ cleanup: () => Promise<void>;
10
+ }>;
package/dist/server.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * server.ts — In-process MCP server for DEVONthink.
3
+ *
4
+ * All 33 tools are our own code under MIT — zero upstream dependency.
5
+ */
6
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
8
+ import { allTools } from "./tools/index.js";
9
+ export const createExtendedServer = async () => {
10
+ const server = new Server({
11
+ name: "@tekmidian/devon",
12
+ version: "3.0.0",
13
+ }, {
14
+ capabilities: {
15
+ tools: {},
16
+ resources: {},
17
+ prompts: {},
18
+ },
19
+ });
20
+ // ---- ListTools handler ----
21
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
22
+ return { tools: allTools };
23
+ });
24
+ // ---- Empty resource/prompt handlers (required by MCP spec) ----
25
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
26
+ return { resources: [] };
27
+ });
28
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
29
+ return { resources: [] };
30
+ });
31
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
32
+ return { prompts: [] };
33
+ });
34
+ // ---- CallTool handler ----
35
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
36
+ const { name, arguments: args = {} } = request.params;
37
+ const tool = allTools.find((t) => t.name === name);
38
+ if (!tool) {
39
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
40
+ }
41
+ try {
42
+ const result = await tool.run(args);
43
+ return {
44
+ content: [
45
+ {
46
+ type: "text",
47
+ text: JSON.stringify(result, null, 2),
48
+ },
49
+ ],
50
+ };
51
+ }
52
+ catch (error) {
53
+ throw error instanceof McpError
54
+ ? error
55
+ : new McpError(ErrorCode.InternalError, error instanceof Error ? error.message : String(error));
56
+ }
57
+ });
58
+ return {
59
+ server,
60
+ cleanup: async () => {
61
+ // No persistent resources to clean up
62
+ },
63
+ };
64
+ };
65
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,kCAAkC,EAClC,wBAAwB,EACxB,SAAS,EACT,QAAQ,GACT,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,IAGtC,EAAE;IACH,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;QACE,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;YACT,SAAS,EAAE,EAAE;YACb,OAAO,EAAE,EAAE;SACZ;KACF,CACF,CAAC;IAEF,8BAA8B;IAC9B,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QAC1D,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,kEAAkE;IAClE,MAAM,CAAC,iBAAiB,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QAC9D,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QACtE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QAC5D,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,6BAA6B;IAC7B,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAEtD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,iBAAiB,IAAI,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAA+B,CAAC,CAAC;YAC/D,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACtC;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,YAAY,QAAQ;gBAC7B,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,IAAI,QAAQ,CACV,SAAS,CAAC,aAAa,EACvB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;QACR,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM;QACN,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,sCAAsC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * setup.ts — Interactive first-time setup for devon
3
+ *
4
+ * Run with: npx @tekmidian/devon setup
5
+ *
6
+ * Walks the user through:
7
+ * 1. Prerequisites check (macOS, Node version, DEVONthink installed)
8
+ * 2. Update ~/.claude.json mcpServers
9
+ * 3. Update ~/.claude/settings.json enabledMcpjsonServers (if present)
10
+ * 4. Done summary with next steps
11
+ */
12
+ export declare function runSetup(): Promise<void>;