@torsday/omnifocus-mcp 1.0.2 → 1.2.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.
- package/CHANGELOG.md +147 -2
- package/README.md +68 -10
- package/dist/index.js +2258 -193
- package/package.json +11 -4
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {createHash}from'crypto';import fu,{homedir}from'os';import {z}from'zod';import go from'pino';import {spawn,execFile}from'child_process';import rt from'fs';import to,{join,sep}from'path';import {createInterface}from'readline';import {fileURLToPath}from'url';import {AsyncLocalStorage}from'async_hooks';import {ulid}from'ulid';import {EventEmitter}from'events';import {LRUCache}from'lru-cache';import {realpath,stat}from'fs/promises';var re={name:"@torsday/omnifocus-mcp",version:"1.0.2",description:"MCP server exposing the full OmniFocus surface to LLM agents \u2014 80 typed tools spanning tasks, projects, tags, folders, perspectives, forecast, review, notes, attachments, and sync, with a strict typed-error taxonomy and per-tool circuit breakers, rate limiter, and idempotency-key support. macOS-only; talks to OmniFocus 4 via JXA + OmniJS.",homepage:"https://github.com/torsday/omnifocus-mcp#readme"};var Ue=Object.freeze({listTasks:"jxa",getTask:"jxa",getTasksMany:"jxa",createTask:"jxa",updateTask:"jxa",completeTask:"jxa",uncompleteTask:"jxa",dropTask:"jxa",undropTask:"jxa",deleteTask:"jxa",moveTask:"omnijs",batchMoveTasks:"omnijs",reorderTask:"omnijs",duplicateTask:"jxa",batchCreateTasks:"jxa",batchUpdateTasks:"jxa",batchCompleteTasks:"jxa",batchUncompleteTasks:"jxa",batchDeleteTasks:"jxa",batchDropTasks:"jxa",batchUndropTasks:"jxa",listProjects:"jxa",getProject:"jxa",getProjectsMany:"jxa",createProject:"jxa",updateProject:"jxa",completeProject:"jxa",batchCompleteProjects:"jxa",dropProject:"jxa",batchDropProjects:"jxa",moveProject:"jxa",deleteProject:"jxa",markProjectReviewed:"jxa",listProjectsDueForReview:"jxa",setProjectReviewInterval:"jxa",listTags:"jxa",getTag:"jxa",getTagsMany:"jxa",createTag:"jxa",updateTag:"jxa",deleteTag:"jxa",listFolders:"jxa",getFolder:"jxa",createFolder:"jxa",updateFolder:"jxa",deleteFolder:"jxa",searchTasks:"jxa",getForecast:"jxa",listPerspectives:"jxa",evaluatePerspective:"jxa",evaluateCustomPerspective:"omnijs",syncTrigger:"jxa",getLastSync:"jxa",listAttachments:"jxa",addAttachment:"jxa",removeAttachment:"jxa",saveAttachmentToPath:"jxa",appLaunch:"jxa",pluginInvoke:"omnijs",getChangesSince:"jxa",runJxaScript:"jxa",runOmniJsScript:"omnijs"});var Le=class n{jxa;omnijs;constructor(e){this.jxa=e.jxa,this.omnijs=e.omnijs;}get routingTable(){return Ue}static fromTransports(e,t){return new n({jxa:e,omnijs:t})}pick(e){return Ue[e]==="jxa"?this.jxa:this.omnijs}listTasks(e){return this.pick("listTasks").listTasks(e)}getTask(e){return this.pick("getTask").getTask(e)}getTasksMany(e){return this.pick("getTasksMany").getTasksMany(e)}createTask(e){return this.pick("createTask").createTask(e)}updateTask(e,t){return this.pick("updateTask").updateTask(e,t)}completeTask(e,t){return this.pick("completeTask").completeTask(e,t)}uncompleteTask(e){return this.pick("uncompleteTask").uncompleteTask(e)}dropTask(e,t){return this.pick("dropTask").dropTask(e,t)}undropTask(e){return this.pick("undropTask").undropTask(e)}deleteTask(e){return this.pick("deleteTask").deleteTask(e)}moveTask(e,t){return this.pick("moveTask").moveTask(e,t)}batchMoveTasks(e){return this.pick("batchMoveTasks").batchMoveTasks(e)}reorderTask(e,t){return this.pick("reorderTask").reorderTask(e,t)}duplicateTask(e,t){return this.pick("duplicateTask").duplicateTask(e,t)}batchCreateTasks(e){return this.pick("batchCreateTasks").batchCreateTasks(e)}batchUpdateTasks(e){return this.pick("batchUpdateTasks").batchUpdateTasks(e)}batchCompleteTasks(e){return this.pick("batchCompleteTasks").batchCompleteTasks(e)}batchUncompleteTasks(e){return this.pick("batchUncompleteTasks").batchUncompleteTasks(e)}batchDeleteTasks(e){return this.pick("batchDeleteTasks").batchDeleteTasks(e)}batchDropTasks(e){return this.pick("batchDropTasks").batchDropTasks(e)}batchUndropTasks(e){return this.pick("batchUndropTasks").batchUndropTasks(e)}listProjects(e){return this.pick("listProjects").listProjects(e)}getProject(e){return this.pick("getProject").getProject(e)}getProjectsMany(e){return this.pick("getProjectsMany").getProjectsMany(e)}createProject(e){return this.pick("createProject").createProject(e)}updateProject(e,t){return this.pick("updateProject").updateProject(e,t)}completeProject(e,t){return this.pick("completeProject").completeProject(e,t)}dropProject(e,t){return this.pick("dropProject").dropProject(e,t)}batchCompleteProjects(e){return this.pick("batchCompleteProjects").batchCompleteProjects(e)}batchDropProjects(e){return this.pick("batchDropProjects").batchDropProjects(e)}moveProject(e,t){return this.pick("moveProject").moveProject(e,t)}deleteProject(e){return this.pick("deleteProject").deleteProject(e)}markProjectReviewed(e){return this.pick("markProjectReviewed").markProjectReviewed(e)}listProjectsDueForReview(){return this.pick("listProjectsDueForReview").listProjectsDueForReview()}setProjectReviewInterval(e,t){return this.pick("setProjectReviewInterval").setProjectReviewInterval(e,t)}listTags(e){return this.pick("listTags").listTags(e)}getTag(e){return this.pick("getTag").getTag(e)}getTagsMany(e){return this.pick("getTagsMany").getTagsMany(e)}createTag(e){return this.pick("createTag").createTag(e)}updateTag(e,t){return this.pick("updateTag").updateTag(e,t)}deleteTag(e){return this.pick("deleteTag").deleteTag(e)}listFolders(e){return this.pick("listFolders").listFolders(e)}getFolder(e){return this.pick("getFolder").getFolder(e)}createFolder(e){return this.pick("createFolder").createFolder(e)}updateFolder(e,t){return this.pick("updateFolder").updateFolder(e,t)}deleteFolder(e){return this.pick("deleteFolder").deleteFolder(e)}searchTasks(e){return this.pick("searchTasks").searchTasks(e)}getForecast(e){return this.pick("getForecast").getForecast(e)}listPerspectives(){return this.pick("listPerspectives").listPerspectives()}evaluatePerspective(e){return this.pick("evaluatePerspective").evaluatePerspective(e)}evaluateCustomPerspective(e){return this.pick("evaluateCustomPerspective").evaluateCustomPerspective(e)}syncTrigger(){return this.pick("syncTrigger").syncTrigger()}getLastSync(){return this.pick("getLastSync").getLastSync()}listAttachments(e){return this.pick("listAttachments").listAttachments(e)}addAttachment(e){return this.pick("addAttachment").addAttachment(e)}removeAttachment(e){return this.pick("removeAttachment").removeAttachment(e)}saveAttachmentToPath(e){return this.pick("saveAttachmentToPath").saveAttachmentToPath(e)}appLaunch(){return this.pick("appLaunch").appLaunch()}pluginInvoke(e){return this.pick("pluginInvoke").pluginInvoke(e)}runJxaScript(e,t){let r=this.pick("runJxaScript");return typeof r.runJxaScript!="function"?Promise.reject(new TypeError("Router dispatched runJxaScript to a transport that does not implement it")):r.runJxaScript(e,t)}runOmniJsScript(e,t){let r=this.pick("runOmniJsScript");return typeof r.runOmniJsScript!="function"?Promise.reject(new TypeError("Router dispatched runOmniJsScript to a transport that does not implement it")):r.runOmniJsScript(e,t)}getChangesSince(e){return this.pick("getChangesSince").getChangesSince(e)}};var ac=new Set(["createTask","updateTask","completeTask","uncompleteTask","dropTask","undropTask","deleteTask","moveTask","reorderTask","duplicateTask","batchCreateTasks","batchUpdateTasks","batchCompleteTasks","createProject","updateProject","completeProject","dropProject","moveProject","deleteProject","markProjectReviewed","setProjectReviewInterval","createTag","updateTag","deleteTag","createFolder","updateFolder","deleteFolder","syncTrigger","addAttachment","removeAttachment","appLaunch","pluginInvoke","runJxaScript","runOmniJsScript"]);function sc(n,e){return Ue[n]==="omnijs"?e.omniJsQueue:ac.has(n)?e.jxaWriteQueue:e.readPool}function po(n,e){return new Proxy(n,{get(t,r,o){if(typeof r!="string"||!(r in Ue))return Reflect.get(t,r,o);let a=r,s=sc(a,e),l=Reflect.get(t,a,o).bind(t);return (...u)=>s.run(()=>l(...u))}})}var st=class{name;size;inFlight=0;waiters=[];constructor(e){if(!Number.isInteger(e.size)||e.size<1)throw new RangeError(`ReadPool.size must be a positive integer (got ${String(e.size)})`);this.size=e.size,this.name=e.name??"read-pool";}async run(e){await this.acquire();try{return await e()}finally{this.release();}}inFlightCount(){return this.inFlight}waitingCount(){return this.waiters.length}pendingCount(){return this.inFlight+this.waiters.length}acquire(){return this.inFlight<this.size?(this.inFlight++,Promise.resolve()):new Promise(e=>{this.waiters.push(()=>{this.inFlight++,e();});})}release(){this.inFlight--;let e=this.waiters.shift();e!==void 0&&e();}};var E=class extends Error{code;remediationClass;suggestion;details;constructor(e,t,r={}){super(t,r.cause!==void 0?{cause:r.cause}:void 0),this.name=new.target.name,this.code=e,r.remediationClass!==void 0&&(this.remediationClass=r.remediationClass),r.suggestion!==void 0&&(this.suggestion=r.suggestion),r.details!==void 0&&(this.details=r.details);}toJSON(){let e={name:this.name,code:this.code,message:this.message};return this.remediationClass!==void 0&&(e.remediationClass=this.remediationClass),this.suggestion!==void 0&&(e.suggestion=this.suggestion),this.details!==void 0&&(e.details=this.details),e}};function uo(n){return n instanceof E}var we=class extends E{constructor(e={}){super("OF_NOT_RUNNING","OmniFocus is not running.",{remediationClass:"environment",suggestion:"Launch OmniFocus and retry.",...e});}},je=class extends E{constructor(e={}){super("OF_PERMISSION_DENIED","Automation permission for OmniFocus is denied.",{remediationClass:"environment",suggestion:"Open System Settings \u2192 Privacy & Security \u2192 Automation; grant this terminal or client access to OmniFocus. See docs/troubleshooting.md for step-by-step recovery.",...e});}},it=class extends E{constructor(e,t={}){super("OF_FEATURE_REQUIRES_PRO",e,{remediationClass:"environment",suggestion:"This feature requires OmniFocus Pro. Upgrade or use a different tool.",...t});}};var I=class extends E{constructor(e,t={}){super("OF_VALIDATION",e,{remediationClass:"input",suggestion:"Fix the input and retry. See `details` for field-level reasons.",...t});}},P=class extends E{constructor(e,t={}){super("OF_NOT_FOUND",e,{remediationClass:"input",suggestion:"Confirm the ID with the corresponding `*_list` tool. Use OmniFocus persistent IDs, not names.",...t});}},be=class extends E{constructor(e,t={}){super("OF_CONFLICT",e,{remediationClass:"input",suggestion:"The resource was modified since you read it. Re-read with the corresponding `*_get` tool, merge your changes, and retry with the fresh `modifiedAt` value.",...t});}},Pe=class extends E{constructor(e,t={}){super("OF_TIMEOUT",e,{remediationClass:"transient",suggestion:"Retry once. If repeated, OmniFocus may be wedged \u2014 relaunch it.",...t});}},ct=class extends E{constructor(e,t={}){super("OF_RATE_LIMITED",e,{remediationClass:"transient",suggestion:"Wait details.retryAfterMs milliseconds then retry.",...t,details:{retryAfterMs:6e4,...t.details}});}},dt=class extends E{constructor(e,t={}){super("OF_QUEUE_FULL",e,{remediationClass:"transient",suggestion:"The write queue is saturated. Wait for in-flight writes to drain, then retry.",...t});}},Je=class extends E{constructor(e,t={}){super("OF_CIRCUIT_OPEN",e,{remediationClass:"transient",suggestion:"This tool failed repeatedly and is rejecting calls fast. Wait details.retryAfterMs milliseconds for the circuit to half-open.",...t,details:{retryAfterMs:6e4,...t.details}});}},Oe=class extends E{constructor(e,t={}){super("OF_TRANSPORT_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"The required transport is unreachable. Verify OmniFocus is running and responsive.",...t});}},L=class extends E{constructor(e,t={}){super("OF_SCRIPT_ERROR",e,{remediationClass:"infrastructure",suggestion:"The OmniFocus script failed. Inspect `details.transport` and `details.reason` for context.",...t});}},lt=class extends E{constructor(e={}){super("OF_SHUTTING_DOWN","Server is shutting down; not accepting new requests.",{remediationClass:"lifecycle",suggestion:"Reconnect to a fresh server instance.",...e});}},pt=class extends E{constructor(e,t,r,o={}){super("OF_LOOP_DETECTED",`Tool "${e}" has been called ${t} time(s) with identical arguments within ${r}s. The agent appears to be stuck.`,{remediationClass:"input",suggestion:"Act on the result of the previous call before repeating this tool. If you need the same data again, verify the previous response was consumed.",details:{tool:e,count:t,windowSeconds:r},...o});}};var Be=class{name;cap;inFlight=0;queue=[];constructor(e){if(!Number.isInteger(e.cap)||e.cap<1)throw new RangeError(`WriteQueue.cap must be a positive integer (got ${String(e.cap)})`);this.cap=e.cap,this.name=e.name??"write-queue";}run(e){if(this.pendingCount()>=this.cap)throw new dt(`${this.name} is full (cap ${this.cap}); ${this.pendingCount()} pending`,{details:{queue:this.name,cap:this.cap,pending:this.pendingCount()}});let{promise:t,resolve:r,reject:o}=Promise.withResolvers();return this.queue.push({fn:e,resolve:r,reject:o}),this.pump(),t}inFlightCount(){return this.inFlight}waitingCount(){return this.queue.length}pendingCount(){return this.inFlight+this.queue.length}pump(){if(this.inFlight>0)return;let e=this.queue.shift();e!==void 0&&(this.inFlight++,(async()=>{try{let t=await e.fn();e.resolve(t);}catch(t){e.reject(t);}finally{this.inFlight--,this.pump();}})());}};var dc=z.string().regex(/^\d+\/\d+$/,'must be "N/SECONDS" format, e.g. "120/60"').transform(n=>{let[e,t]=n.split("/").map(Number);return {limit:e,windowSeconds:t}}),lc=z.object({OMNIFOCUS_LOG_LEVEL:z.enum(["trace","debug","info","warn","error"]).default("info"),OMNIFOCUS_INTEGRATION:z.string().prefault("").transform(n=>n==="1"),OMNIFOCUS_E2E:z.string().prefault("").transform(n=>n==="1"),OMNIFOCUS_E2E_USE_MEMORY:z.string().prefault("").transform(n=>n==="1"),OMNIFOCUS_ALLOW_RAW_SCRIPT:z.string().prefault("").transform(n=>n==="1"),OMNIFOCUS_CACHE_TTL_MS:z.coerce.number().int().positive().default(3e4),OMNIFOCUS_CACHE_CAPACITY:z.coerce.number().int().positive().default(256),OMNIFOCUS_READ_POOL_SIZE:z.coerce.number().int().min(1).max(8).default(2),OMNIFOCUS_WRITE_QUEUE_CAP:z.coerce.number().int().positive().default(50),OMNIFOCUS_JXA_TIMEOUT_MS:z.coerce.number().int().positive().default(3e4),OMNIFOCUS_OMNIJS_TIMEOUT_MS:z.coerce.number().int().positive().default(45e3),OMNIFOCUS_ATTACHMENT_PATHS:z.string().prefault(homedir()).transform(n=>n.split(":").filter(Boolean)),OMNIFOCUS_MAX_ATTACHMENT_MB:z.coerce.number().int().positive().default(100),OMNIFOCUS_TOOL_RATE_LIMIT:dc.prefault("120/60")});function mo(n=process.env,e=t=>{process.stderr.write(`[omnifocus-mcp] Config error: ${t}
|
|
3
|
-
`),process.exit(1);}){let
|
|
2
|
+
import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {createHmac,createHash,randomBytes}from'crypto';import xb,{homedir}from'os';import {z}from'zod';import uc from'pino';import {spawn,execFile}from'child_process';import dt from'fs';import Zi,{extname,sep}from'path';import {fileURLToPath}from'url';import {createInterface}from'readline';import Mb from'https';import {AsyncLocalStorage}from'async_hooks';import {ulid}from'ulid';import {EventEmitter}from'events';import {LRUCache}from'lru-cache';import {realpath,stat}from'fs/promises';var Fe={name:"@torsday/omnifocus-mcp",version:"1.2.0",description:"MCP server exposing the full OmniFocus surface to LLM agents \u2014 80 typed tools spanning tasks, projects, tags, folders, perspectives, forecast, review, notes, attachments, and sync, with a strict typed-error taxonomy and per-tool circuit breakers, rate limiter, and idempotency-key support. macOS-only; talks to OmniFocus 4 via JXA + OmniJS.",homepage:"https://github.com/torsday/omnifocus-mcp#readme"};var $t=Object.freeze({listTasks:"jxa",getTask:"jxa",getTasksMany:"jxa",createTask:"jxa",updateTask:"jxa",completeTask:"jxa",uncompleteTask:"jxa",dropTask:"jxa",undropTask:"jxa",deleteTask:"jxa",moveTask:"omnijs",convertTaskToProject:"omnijs",batchMoveTasks:"omnijs",reorderTask:"omnijs",duplicateTask:"jxa",batchCreateTasks:"jxa",batchUpdateTasks:"jxa",batchCompleteTasks:"jxa",batchUncompleteTasks:"jxa",batchDeleteTasks:"jxa",batchDropTasks:"jxa",batchUndropTasks:"jxa",listProjects:"jxa",getProject:"jxa",getProjectsMany:"jxa",createProject:"jxa",updateProject:"jxa",completeProject:"jxa",batchCompleteProjects:"jxa",dropProject:"jxa",batchDropProjects:"jxa",moveProject:"jxa",deleteProject:"jxa",markProjectReviewed:"jxa",listProjectsDueForReview:"jxa",setProjectReviewInterval:"jxa",setProjectNextReviewDate:"jxa",listTags:"jxa",getTag:"jxa",getTagsMany:"jxa",createTag:"jxa",updateTag:"jxa",deleteTag:"jxa",listFolders:"jxa",getFolder:"jxa",createFolder:"jxa",updateFolder:"jxa",deleteFolder:"jxa",searchTasks:"jxa",getForecast:"jxa",getForecastTag:"omnijs",setForecastTag:"omnijs",listPerspectives:"jxa",evaluatePerspective:"jxa",evaluateCustomPerspective:"omnijs",evaluatePerspectiveRules:"omnijs",getCustomPerspective:"omnijs",deleteCustomPerspective:"omnijs",createCustomPerspective:"omnijs",updateCustomPerspective:"omnijs",syncTrigger:"jxa",getLastSync:"jxa",undoLastMutation:"omnijs",redoLastMutation:"omnijs",setTaskAlarms:"omnijs",clearTaskAlarms:"omnijs",listAttachments:"jxa",addAttachment:"jxa",removeAttachment:"jxa",saveAttachmentToPath:"jxa",appLaunch:"jxa",getWindowState:"jxa",setWindowPerspective:"jxa",setWindowFocus:"jxa",appWindowNew:"omnijs",appWindowNewTab:"omnijs",pluginInvoke:"omnijs",getChangesSince:"jxa",runJxaScript:"jxa",runOmniJsScript:"omnijs"});var Wt=class t{jxa;omnijs;constructor(e){this.jxa=e.jxa,this.omnijs=e.omnijs;}get routingTable(){return $t}static fromTransports(e,n){return new t({jxa:e,omnijs:n})}pick(e){return $t[e]==="jxa"?this.jxa:this.omnijs}listTasks(e){return this.pick("listTasks").listTasks(e)}getTask(e){return this.pick("getTask").getTask(e)}getTasksMany(e){return this.pick("getTasksMany").getTasksMany(e)}createTask(e){return this.pick("createTask").createTask(e)}updateTask(e,n){return this.pick("updateTask").updateTask(e,n)}completeTask(e,n){return this.pick("completeTask").completeTask(e,n)}uncompleteTask(e){return this.pick("uncompleteTask").uncompleteTask(e)}dropTask(e,n){return this.pick("dropTask").dropTask(e,n)}undropTask(e){return this.pick("undropTask").undropTask(e)}deleteTask(e){return this.pick("deleteTask").deleteTask(e)}moveTask(e,n){return this.pick("moveTask").moveTask(e,n)}convertTaskToProject(e,n){return this.pick("convertTaskToProject").convertTaskToProject(e,n)}batchMoveTasks(e){return this.pick("batchMoveTasks").batchMoveTasks(e)}reorderTask(e,n){return this.pick("reorderTask").reorderTask(e,n)}duplicateTask(e,n){return this.pick("duplicateTask").duplicateTask(e,n)}batchCreateTasks(e){return this.pick("batchCreateTasks").batchCreateTasks(e)}batchUpdateTasks(e){return this.pick("batchUpdateTasks").batchUpdateTasks(e)}batchCompleteTasks(e){return this.pick("batchCompleteTasks").batchCompleteTasks(e)}batchUncompleteTasks(e){return this.pick("batchUncompleteTasks").batchUncompleteTasks(e)}batchDeleteTasks(e){return this.pick("batchDeleteTasks").batchDeleteTasks(e)}batchDropTasks(e){return this.pick("batchDropTasks").batchDropTasks(e)}batchUndropTasks(e){return this.pick("batchUndropTasks").batchUndropTasks(e)}listProjects(e){return this.pick("listProjects").listProjects(e)}getProject(e){return this.pick("getProject").getProject(e)}getProjectsMany(e){return this.pick("getProjectsMany").getProjectsMany(e)}createProject(e){return this.pick("createProject").createProject(e)}updateProject(e,n){return this.pick("updateProject").updateProject(e,n)}completeProject(e,n){return this.pick("completeProject").completeProject(e,n)}dropProject(e,n){return this.pick("dropProject").dropProject(e,n)}batchCompleteProjects(e){return this.pick("batchCompleteProjects").batchCompleteProjects(e)}batchDropProjects(e){return this.pick("batchDropProjects").batchDropProjects(e)}moveProject(e,n){return this.pick("moveProject").moveProject(e,n)}deleteProject(e){return this.pick("deleteProject").deleteProject(e)}markProjectReviewed(e){return this.pick("markProjectReviewed").markProjectReviewed(e)}listProjectsDueForReview(){return this.pick("listProjectsDueForReview").listProjectsDueForReview()}setProjectReviewInterval(e,n){return this.pick("setProjectReviewInterval").setProjectReviewInterval(e,n)}setProjectNextReviewDate(e,n){return this.pick("setProjectNextReviewDate").setProjectNextReviewDate(e,n)}listTags(e){return this.pick("listTags").listTags(e)}getTag(e){return this.pick("getTag").getTag(e)}getTagsMany(e){return this.pick("getTagsMany").getTagsMany(e)}createTag(e){return this.pick("createTag").createTag(e)}updateTag(e,n){return this.pick("updateTag").updateTag(e,n)}deleteTag(e){return this.pick("deleteTag").deleteTag(e)}listFolders(e){return this.pick("listFolders").listFolders(e)}getFolder(e){return this.pick("getFolder").getFolder(e)}createFolder(e){return this.pick("createFolder").createFolder(e)}updateFolder(e,n){return this.pick("updateFolder").updateFolder(e,n)}deleteFolder(e){return this.pick("deleteFolder").deleteFolder(e)}searchTasks(e){return this.pick("searchTasks").searchTasks(e)}getForecast(e){return this.pick("getForecast").getForecast(e)}getForecastTag(){return this.pick("getForecastTag").getForecastTag()}setForecastTag(e){return this.pick("setForecastTag").setForecastTag(e)}listPerspectives(){return this.pick("listPerspectives").listPerspectives()}evaluatePerspective(e){return this.pick("evaluatePerspective").evaluatePerspective(e)}evaluateCustomPerspective(e){return this.pick("evaluateCustomPerspective").evaluateCustomPerspective(e)}evaluatePerspectiveRules(e,n){return this.pick("evaluatePerspectiveRules").evaluatePerspectiveRules(e,n)}getCustomPerspective(e){return this.pick("getCustomPerspective").getCustomPerspective(e)}deleteCustomPerspective(e){return this.pick("deleteCustomPerspective").deleteCustomPerspective(e)}createCustomPerspective(e){return this.pick("createCustomPerspective").createCustomPerspective(e)}updateCustomPerspective(e,n){return this.pick("updateCustomPerspective").updateCustomPerspective(e,n)}syncTrigger(){return this.pick("syncTrigger").syncTrigger()}getLastSync(){return this.pick("getLastSync").getLastSync()}undoLastMutation(){return this.pick("undoLastMutation").undoLastMutation()}redoLastMutation(){return this.pick("redoLastMutation").redoLastMutation()}setTaskAlarms(e,n){return this.pick("setTaskAlarms").setTaskAlarms(e,n)}clearTaskAlarms(e){return this.pick("clearTaskAlarms").clearTaskAlarms(e)}listAttachments(e){return this.pick("listAttachments").listAttachments(e)}addAttachment(e){return this.pick("addAttachment").addAttachment(e)}removeAttachment(e){return this.pick("removeAttachment").removeAttachment(e)}saveAttachmentToPath(e){return this.pick("saveAttachmentToPath").saveAttachmentToPath(e)}appLaunch(){return this.pick("appLaunch").appLaunch()}getWindowState(){return this.pick("getWindowState").getWindowState()}setWindowPerspective(e){return this.pick("setWindowPerspective").setWindowPerspective(e)}setWindowFocus(e){return this.pick("setWindowFocus").setWindowFocus(e)}appWindowNew(){return this.pick("appWindowNew").appWindowNew()}appWindowNewTab(){return this.pick("appWindowNewTab").appWindowNewTab()}pluginInvoke(e){return this.pick("pluginInvoke").pluginInvoke(e)}runJxaScript(e,n){let r=this.pick("runJxaScript");return typeof r.runJxaScript!="function"?Promise.reject(new TypeError("Router dispatched runJxaScript to a transport that does not implement it")):r.runJxaScript(e,n)}runOmniJsScript(e,n){let r=this.pick("runOmniJsScript");return typeof r.runOmniJsScript!="function"?Promise.reject(new TypeError("Router dispatched runOmniJsScript to a transport that does not implement it")):r.runOmniJsScript(e,n)}getChangesSince(e){return this.pick("getChangesSince").getChangesSince(e)}};var eh=new Set(["createTask","updateTask","completeTask","uncompleteTask","dropTask","undropTask","deleteTask","moveTask","reorderTask","duplicateTask","batchCreateTasks","batchUpdateTasks","batchCompleteTasks","createProject","updateProject","completeProject","dropProject","moveProject","deleteProject","markProjectReviewed","setProjectReviewInterval","createTag","updateTag","deleteTag","createFolder","updateFolder","deleteFolder","syncTrigger","addAttachment","removeAttachment","appLaunch","pluginInvoke","runJxaScript","runOmniJsScript"]);function th(t,e){return $t[t]==="omnijs"?e.omniJsQueue:eh.has(t)?e.jxaWriteQueue:e.readPool}function cc(t,e){return new Proxy(t,{get(n,r,o){if(typeof r!="string"||!(r in $t))return Reflect.get(n,r,o);let a=r,i=th(a,e),c=Reflect.get(n,a,o).bind(n);return (...d)=>i.run(()=>c(...d))}})}var In=class{name;size;inFlight=0;waiters=[];constructor(e){if(!Number.isInteger(e.size)||e.size<1)throw new RangeError(`ReadPool.size must be a positive integer (got ${String(e.size)})`);this.size=e.size,this.name=e.name??"read-pool";}async run(e){await this.acquire();try{return await e()}finally{this.release();}}inFlightCount(){return this.inFlight}waitingCount(){return this.waiters.length}pendingCount(){return this.inFlight+this.waiters.length}acquire(){return this.inFlight<this.size?(this.inFlight++,Promise.resolve()):new Promise(e=>{this.waiters.push(()=>{this.inFlight++,e();});})}release(){this.inFlight--;let e=this.waiters.shift();e!==void 0&&e();}};var Y=class extends Error{code;remediationClass;suggestion;details;constructor(e,n,r={}){super(n,r.cause!==void 0?{cause:r.cause}:void 0),this.name=new.target.name,this.code=e,r.remediationClass!==void 0&&(this.remediationClass=r.remediationClass),r.suggestion!==void 0&&(this.suggestion=r.suggestion),r.details!==void 0&&(this.details=r.details);}toJSON(){let e={name:this.name,code:this.code,message:this.message};return this.remediationClass!==void 0&&(e.remediationClass=this.remediationClass),this.suggestion!==void 0&&(e.suggestion=this.suggestion),this.details!==void 0&&(e.details=this.details),e}};function dc(t){return t instanceof Y}var ut=class extends Y{constructor(e={}){super("OF_NOT_RUNNING","OmniFocus is not running.",{remediationClass:"environment",suggestion:"Launch OmniFocus and retry.",...e});}},mt=class extends Y{constructor(e={}){super("OF_PERMISSION_DENIED","Automation permission for OmniFocus is denied.",{remediationClass:"environment",suggestion:"Open System Settings \u2192 Privacy & Security \u2192 Automation; grant this terminal or client access to OmniFocus. See docs/troubleshooting.md for step-by-step recovery.",...e});}},vn=class extends Y{constructor(e={}){super("OF_CALENDAR_PERMISSION_DENIED","Calendar access has not been granted.",{remediationClass:"environment",suggestion:"Open System Settings \u2192 Privacy & Security \u2192 Calendars; grant this terminal or client access. Or invoke the calendar-bridge `request-access` flow to trigger the macOS prompt.",...e});}},ge=class extends Y{constructor(e,n={}){super("OF_CALENDAR_BRIDGE_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"Run `pnpm build:calendar-bridge` to compile the Swift binary. The published npm tarball includes a prebuilt binary; if you're running from source, the binary is built on demand.",...n});}},Ue=class extends Y{constructor(e,n={}){super("OF_FEATURE_REQUIRES_PRO",e,{remediationClass:"environment",suggestion:"This feature requires OmniFocus Pro. Upgrade or use a different tool.",...n});}};var y=class extends Y{constructor(e,n={}){super("OF_VALIDATION",e,{remediationClass:"input",suggestion:"Fix the input and retry. See `details` for field-level reasons.",...n});}},P=class extends Y{constructor(e,n={}){super("OF_NOT_FOUND",e,{remediationClass:"input",suggestion:"Confirm the ID with the corresponding `*_list` tool. Use OmniFocus persistent IDs, not names.",...n});}},ft=class extends Y{constructor(e,n={}){super("OF_CONFLICT",e,{remediationClass:"input",suggestion:"The resource was modified since you read it. Re-read with the corresponding `*_get` tool, merge your changes, and retry with the fresh `modifiedAt` value.",...n});}},gt=class extends Y{constructor(e,n={}){super("OF_TIMEOUT",e,{remediationClass:"transient",suggestion:"Retry once. If repeated, OmniFocus may be wedged \u2014 relaunch it.",...n});}},Tn=class extends Y{constructor(e,n={}){super("OF_RATE_LIMITED",e,{remediationClass:"transient",suggestion:"Wait details.retryAfterMs milliseconds then retry.",...n,details:{retryAfterMs:6e4,...n.details}});}},wn=class extends Y{constructor(e,n={}){super("OF_QUEUE_FULL",e,{remediationClass:"transient",suggestion:"The write queue is saturated. Wait for in-flight writes to drain, then retry.",...n});}},Ht=class extends Y{constructor(e,n={}){super("OF_CIRCUIT_OPEN",e,{remediationClass:"transient",suggestion:"This tool failed repeatedly and is rejecting calls fast. Wait details.retryAfterMs milliseconds for the circuit to half-open.",...n,details:{retryAfterMs:6e4,...n.details}});}},ht=class extends Y{constructor(e,n={}){super("OF_TRANSPORT_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"The required transport is unreachable. Verify OmniFocus is running and responsive.",...n});}},yt=class extends Y{constructor(e,n={}){super("OF_WINDOW_UNAVAILABLE",e,{remediationClass:"environment",suggestion:"OmniFocus has no front window. Ask the user to open an OmniFocus window (Cmd-N or click the Dock icon) and retry.",...n});}},N=class extends Y{constructor(e,n={}){super("OF_SCRIPT_ERROR",e,{remediationClass:"infrastructure",suggestion:"The OmniFocus script failed. Inspect `details.transport` and `details.reason` for context.",...n});}},Sn=class extends Y{constructor(e={}){super("OF_SHUTTING_DOWN","Server is shutting down; not accepting new requests.",{remediationClass:"lifecycle",suggestion:"Reconnect to a fresh server instance.",...e});}},bn=class extends Y{constructor(e,n,r,o={}){super("OF_LOOP_DETECTED",`Tool "${e}" has been called ${n} time(s) with identical arguments within ${r}s. The agent appears to be stuck.`,{remediationClass:"input",suggestion:"Act on the result of the previous call before repeating this tool. If you need the same data again, verify the previous response was consumed.",details:{tool:e,count:n,windowSeconds:r},...o});}};var zt=class{name;cap;inFlight=0;queue=[];constructor(e){if(!Number.isInteger(e.cap)||e.cap<1)throw new RangeError(`WriteQueue.cap must be a positive integer (got ${String(e.cap)})`);this.cap=e.cap,this.name=e.name??"write-queue";}run(e){if(this.pendingCount()>=this.cap)throw new wn(`${this.name} is full (cap ${this.cap}); ${this.pendingCount()} pending`,{details:{queue:this.name,cap:this.cap,pending:this.pendingCount()}});let{promise:n,resolve:r,reject:o}=Promise.withResolvers();return this.queue.push({fn:e,resolve:r,reject:o}),this.pump(),n}inFlightCount(){return this.inFlight}waitingCount(){return this.queue.length}pendingCount(){return this.inFlight+this.queue.length}pump(){if(this.inFlight>0)return;let e=this.queue.shift();e!==void 0&&(this.inFlight++,(async()=>{try{let n=await e.fn();e.resolve(n);}catch(n){e.reject(n);}finally{this.inFlight--,this.pump();}})());}};var oh=z.string().regex(/^\d+\/\d+$/,'must be "N/SECONDS" format, e.g. "120/60"').transform(t=>{let[e,n]=t.split("/").map(Number);return {limit:e,windowSeconds:n}}),ah=z.object({OMNIFOCUS_LOG_LEVEL:z.enum(["trace","debug","info","warn","error"]).default("info"),OMNIFOCUS_INTEGRATION:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_E2E:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_E2E_USE_MEMORY:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_ALLOW_RAW_SCRIPT:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_WEBHOOKS_ENABLED:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_CACHE_TTL_MS:z.coerce.number().int().positive().default(3e4),OMNIFOCUS_CACHE_CAPACITY:z.coerce.number().int().positive().default(256),OMNIFOCUS_READ_POOL_SIZE:z.coerce.number().int().min(1).max(8).default(2),OMNIFOCUS_WRITE_QUEUE_CAP:z.coerce.number().int().positive().default(50),OMNIFOCUS_JXA_TIMEOUT_MS:z.coerce.number().int().positive().default(3e4),OMNIFOCUS_OMNIJS_TIMEOUT_MS:z.coerce.number().int().positive().default(45e3),OMNIFOCUS_ATTACHMENT_PATHS:z.string().prefault(homedir()).transform(t=>t.split(":").filter(Boolean)),OMNIFOCUS_MAX_ATTACHMENT_MB:z.coerce.number().int().positive().default(100),OMNIFOCUS_TOOL_RATE_LIMIT:oh.prefault("120/60"),OMNIFOCUS_WAITING_TAG_NAME:z.string().min(1).default("waiting"),OMNIFOCUS_TEMPLATES_FOLDER_NAME:z.string().min(1).default("Templates")});function lc(t=process.env,e=n=>{process.stderr.write(`[omnifocus-mcp] Config error: ${n}
|
|
3
|
+
`),process.exit(1);}){let n=ah.safeParse({OMNIFOCUS_LOG_LEVEL:t.OMNIFOCUS_LOG_LEVEL,OMNIFOCUS_INTEGRATION:t.OMNIFOCUS_INTEGRATION,OMNIFOCUS_E2E:t.OMNIFOCUS_E2E,OMNIFOCUS_E2E_USE_MEMORY:t.OMNIFOCUS_E2E_USE_MEMORY,OMNIFOCUS_ALLOW_RAW_SCRIPT:t.OMNIFOCUS_ALLOW_RAW_SCRIPT,OMNIFOCUS_WEBHOOKS_ENABLED:t.OMNIFOCUS_WEBHOOKS_ENABLED,OMNIFOCUS_CACHE_TTL_MS:t.OMNIFOCUS_CACHE_TTL_MS,OMNIFOCUS_CACHE_CAPACITY:t.OMNIFOCUS_CACHE_CAPACITY,OMNIFOCUS_READ_POOL_SIZE:t.OMNIFOCUS_READ_POOL_SIZE,OMNIFOCUS_WRITE_QUEUE_CAP:t.OMNIFOCUS_WRITE_QUEUE_CAP,OMNIFOCUS_JXA_TIMEOUT_MS:t.OMNIFOCUS_JXA_TIMEOUT_MS,OMNIFOCUS_OMNIJS_TIMEOUT_MS:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS,OMNIFOCUS_ATTACHMENT_PATHS:t.OMNIFOCUS_ATTACHMENT_PATHS,OMNIFOCUS_MAX_ATTACHMENT_MB:t.OMNIFOCUS_MAX_ATTACHMENT_MB,OMNIFOCUS_TOOL_RATE_LIMIT:t.OMNIFOCUS_TOOL_RATE_LIMIT,OMNIFOCUS_WAITING_TAG_NAME:t.OMNIFOCUS_WAITING_TAG_NAME,OMNIFOCUS_TEMPLATES_FOLDER_NAME:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME});if(!n.success){let r=n.error.issues.map(o=>` ${o.path.join(".")}: ${o.message}`);return e(`Invalid environment configuration:
|
|
4
4
|
${r.join(`
|
|
5
5
|
`)}
|
|
6
|
-
See DESIGN \xA722 for allowed values.`)}return
|
|
6
|
+
See DESIGN \xA722 for allowed values.`)}return n.data}function sh(t){return createHash("sha256").update(t).digest("hex").slice(0,12)}function pc(t){return {OMNIFOCUS_LOG_LEVEL:t.OMNIFOCUS_LOG_LEVEL,OMNIFOCUS_INTEGRATION:t.OMNIFOCUS_INTEGRATION,OMNIFOCUS_E2E:t.OMNIFOCUS_E2E,OMNIFOCUS_E2E_USE_MEMORY:t.OMNIFOCUS_E2E_USE_MEMORY,OMNIFOCUS_ALLOW_RAW_SCRIPT:t.OMNIFOCUS_ALLOW_RAW_SCRIPT,OMNIFOCUS_WEBHOOKS_ENABLED:t.OMNIFOCUS_WEBHOOKS_ENABLED,OMNIFOCUS_CACHE_TTL_MS:t.OMNIFOCUS_CACHE_TTL_MS,OMNIFOCUS_CACHE_CAPACITY:t.OMNIFOCUS_CACHE_CAPACITY,OMNIFOCUS_READ_POOL_SIZE:t.OMNIFOCUS_READ_POOL_SIZE,OMNIFOCUS_WRITE_QUEUE_CAP:t.OMNIFOCUS_WRITE_QUEUE_CAP,OMNIFOCUS_JXA_TIMEOUT_MS:t.OMNIFOCUS_JXA_TIMEOUT_MS,OMNIFOCUS_OMNIJS_TIMEOUT_MS:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS,OMNIFOCUS_ATTACHMENT_PATHS:t.OMNIFOCUS_ATTACHMENT_PATHS.map(sh),OMNIFOCUS_MAX_ATTACHMENT_MB:t.OMNIFOCUS_MAX_ATTACHMENT_MB,OMNIFOCUS_TOOL_RATE_LIMIT:t.OMNIFOCUS_TOOL_RATE_LIMIT,OMNIFOCUS_WAITING_TAG_NAME:t.OMNIFOCUS_WAITING_TAG_NAME,OMNIFOCUS_TEMPLATES_FOLDER_NAME:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME}}var ih=["name","note","noteHtml","tagNames","tagNames[*]","data.name","data.note","data.noteHtml","data.tagNames","data.tagNames[*]","*.name","*.note","*.noteHtml","*.tagNames"];function ch(t="info"){return uc({level:t,redact:{paths:ih,censor:"[redacted]"},formatters:{level(e){return {level:e}}},timestamp:uc.stdTimeFunctions.epochTime},process.stderr)}var R=ch(process.env.OMNIFOCUS_LOG_LEVEL??"info");var lh={threshold:5,errorThreshold:10,windowSeconds:60};function ph(t,e){let n=JSON.stringify(e,Object.keys(e).sort()),r=createHash("sha1").update(n).digest("hex").slice(0,16);return `${t}:${r}`}var jn=class{config;windows=new Map;constructor(e={}){this.config={...lh,...e};}record(e,n){let r=ph(e,n),o=Date.now(),a=o-this.config.windowSeconds*1e3,i=this.getAndPrune(r,a);i.push(o);let s=i.length;if(s<this.config.threshold)return;let c={code:"WARN_LOOP_DETECTED",message:`Tool "${e}" has been called ${s} time(s) with identical arguments within ${this.config.windowSeconds}s.`,suggestion:"The agent may be stuck in a loop. Verify that the previous response was acted on before repeating this call.",details:{tool:e,count:s,windowSeconds:this.config.windowSeconds}};return s>=this.config.errorThreshold?{level:"error",warning:c}:{level:"warn",warning:c}}reset(e){e===void 0?this.windows.clear():this.windows.delete(e);}getAndPrune(e,n){this.windows.has(e)||this.windows.set(e,[]);let r=this.windows.get(e),o=0;for(;o<r.length&&r[o]<=n;)o++;return o>0&&r.splice(0,o),r}};var Ar="daily-review",Cr="weekly-review",Er="capture-meeting",Mr="project-planning",uh="inbox-triage",mh="perspective-author";function fh(){return "You are running a daily OmniFocus review. Follow these steps in order:\n\n1. **Load context** \u2014 read these three resources:\n - `omnifocus://snapshot` (5-count orientation: inbox, overdue, due-today, flagged, review-due)\n - `omnifocus://overdue` (tasks whose due date has passed, sorted oldest-first)\n - `omnifocus://forecast/today` (tasks due or deferred to today, plus flagged)\n\n2. **Clear overdue** \u2014 for every task in `overdue`:\n - If it should be done today, leave it (it will appear in forecast/today too).\n - If it can be rescheduled, call `task_update` with a new `dueDate`.\n - If it should be dropped, call `task_drop` with a reason note.\n\n3. **Plan due-today** \u2014 for every task in `forecast/today.dueToday`:\n - Confirm it is still relevant. If the due date should change, call `task_update`.\n - If it is already done, call `task_complete`.\n\n4. **Decide on flagged** \u2014 for every task in `forecast/today.flagged`:\n - If it is genuinely today's priority, leave the flag.\n - If it should not be today, call `task_update` to remove the flag (`flagged: false`).\n\n5. **Inbox zero** \u2014 if `snapshot.inboxCount > 0`, load `omnifocus://inbox` and process each\n task: assign to a project (`task_update` with `projectId`), add tags, or complete it.\n\n6. **Report** \u2014 summarise what you did: counts of rescheduled, dropped, completed, and\n inbox-cleared tasks. Mention any items you left for the user to decide."}function gh(){return `You are running a weekly OmniFocus review. Follow these steps in order:
|
|
7
7
|
|
|
8
8
|
1. **Load review queue** \u2014 read \`omnifocus://review-due\` (projects whose review date has
|
|
9
9
|
arrived, sorted by nextReviewDate ascending). If the list is empty, report "No projects
|
|
@@ -22,7 +22,7 @@ See DESIGN \xA722 for allowed values.`)}return t.data}function pc(n){return crea
|
|
|
22
22
|
a project, call \`task_update\` to assign them.
|
|
23
23
|
|
|
24
24
|
4. **Report** \u2014 summarise: how many projects reviewed, how many marked complete/dropped,
|
|
25
|
-
how many tasks rescheduled or cleaned up.`}function
|
|
25
|
+
how many tasks rescheduled or cleaned up.`}function hh(t,e){return `You are capturing action items from meeting notes. Follow these steps:
|
|
26
26
|
|
|
27
27
|
1. **Read the notes** below carefully.
|
|
28
28
|
|
|
@@ -42,10 +42,10 @@ See DESIGN \xA722 for allowed values.`)}return t.data}function pc(n){return crea
|
|
|
42
42
|
---
|
|
43
43
|
### Meeting notes
|
|
44
44
|
|
|
45
|
-
${
|
|
45
|
+
${t}`}function yh(t,e,n){let r=n?` - \`folderId\`: \`${n}\``:" - No folder (top-level project)";return `You are setting up a new OmniFocus project. Follow these steps:
|
|
46
46
|
|
|
47
47
|
1. **Create the project** \u2014 call \`project_create\` with:
|
|
48
|
-
- \`name\`: \`${
|
|
48
|
+
- \`name\`: \`${t}\`
|
|
49
49
|
${r}
|
|
50
50
|
- \`note\`: the brief below, verbatim, as the project note.
|
|
51
51
|
|
|
@@ -65,9 +65,173 @@ ${r}
|
|
|
65
65
|
---
|
|
66
66
|
### Project brief
|
|
67
67
|
|
|
68
|
-
${e}`}function ho(n){n.registerPrompt(Qt,{description:"Run a daily OmniFocus triage: load snapshot + overdue + forecast/today, reschedule or drop overdue tasks, confirm due-today tasks, unflag low-priority flagged tasks, and process the inbox. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:Ic()}}]})),n.registerPrompt(Zt,{description:"Run a weekly OmniFocus review: walk every project due for review, check its tasks, mark it reviewed or complete/drop it, and clean up stale tasks. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:kc()}}]})),n.registerPrompt(en,{description:"Extract action items from meeting notes and create OmniFocus tasks. Pass raw notes as `notes`; optionally target a project with `projectId`. Tasks land in the inbox when projectId is omitted.",argsSchema:{notes:z.string().min(1).describe("Raw meeting notes to extract action items from."),projectId:z.string().optional().describe("Persistent OmniFocus project ID to assign tasks to. Omit to use the inbox.")}},async({notes:e,projectId:t})=>({messages:[{role:"user",content:{type:"text",text:Tc(e,t)}}]})),n.registerPrompt(tn,{description:"Create a new OmniFocus project and populate it with tasks derived from a brief. Pass `name` and `brief`; optionally place it in a folder with `folderId`.",argsSchema:{name:z.string().min(1).describe("Name of the new project."),brief:z.string().min(1).describe("One-paragraph description of the project goal. Used to derive subtasks."),folderId:z.string().optional().describe("Persistent OmniFocus folder ID to place the project in. Omit for top-level.")}},async({name:e,brief:t,folderId:r})=>({messages:[{role:"user",content:{type:"text",text:yc(e,t,r)}}]}));}var mt=class{config;windows=new Map;constructor(e){this.config=e;}check(e){let t=Date.now(),r=t-this.config.windowSeconds*1e3,o=this.getAndPrune(e,r);if(o.length>=this.config.limit){let s=o[0]+this.config.windowSeconds*1e3-t+1;throw new ct(`Rate limit exceeded for tool "${e}". Limit: ${this.config.limit} calls per ${this.config.windowSeconds}s.`,{details:{retryAfterMs:s}})}o.push(t);}remaining(e){let t=Date.now(),r=t-this.config.windowSeconds*1e3,o=this.getAndPrune(e,r),a=this.config.limit-o.length,s=o.length>0?new Date(o[0]+this.config.windowSeconds*1e3).toISOString():new Date(t+this.config.windowSeconds*1e3).toISOString();return {remaining:a,resetAt:s}}reset(e){this.windows.delete(e);}getAndPrune(e,t){this.windows.has(e)||this.windows.set(e,[]);let r=this.windows.get(e),o=0;for(;o<r.length&&r[o]<=t;)o++;return o>0&&r.splice(0,o),r}};function Io(n,e={}){let{limit:t,windowSeconds:r}=n.OMNIFOCUS_TOOL_RATE_LIMIT,o=Math.round(t*60/r),a=e.ofEdition??"standard",s=a==="pro";return {ofVersion:e.ofVersion??"unknown",ofEdition:a,transports:{jxa:{available:true,timeoutMs:n.OMNIFOCUS_JXA_TIMEOUT_MS},omnijs:{available:true,timeoutMs:n.OMNIFOCUS_OMNIJS_TIMEOUT_MS}},features:{customPerspectives:s,forecastTag:s,repetitionRules:s,pluginInvocation:s,rawScriptTools:n.OMNIFOCUS_ALLOW_RAW_SCRIPT},rateLimits:{defaultPerToolPerMinute:o},idempotencyTtlMs:864e5}}var ft="omnifocus://capabilities";function ko(n,e){n.registerResource("omnifocus-capabilities",ft,{description:"Structured capabilities object for this omnifocus-mcp server instance. Read once at session start to discover available features (Pro vs Standard), transport timeouts, rate limits, and whether raw-script tools are enabled. ofVersion and ofEdition are 'unknown'/'standard' until the lazy OF probe runs.",mimeType:"application/json"},async t=>({contents:[{uri:ft,mimeType:"application/json",text:JSON.stringify(e(),null,2)}]}));}var To=/^[A-Za-z0-9_-]{3,64}$/;function Sc(n){return typeof n=="string"&&To.test(n)}function He(n){let e=z.string().regex(To,`Invalid ${n}: expected 3-64 alphanumeric / _ / - characters`).transform(t=>t);return {kind:n,of(t){return e.parse(t)},is(t){return Sc(t)},schema:e}}var f=He("TaskId"),k=He("ProjectId"),y=He("TagId"),A=He("FolderId"),oe=He("AttachmentId");var ze="omnifocus://snapshot",qe="omnifocus://inbox",Ve="omnifocus://forecast/today",We="omnifocus://overdue",Xe="omnifocus://flagged",Ke="omnifocus://review-due",rn="omnifocus://project/{id}",on="omnifocus://tag/{id}",an="omnifocus://perspective/{id}",yo="omnifocus://tasks/inbox",wc="omnifocus://tasks/project/{projectId}",jc="omnifocus://tasks/tag/{tagId}";function nn(){let n=new Date,e=new Date(n.getFullYear(),n.getMonth(),n.getDate(),0,0,0,0),t=new Date(n.getFullYear(),n.getMonth(),n.getDate(),23,59,59,999);return {from:e.toISOString(),to:t.toISOString()}}function q(n,e){return {contents:[{uri:n,mimeType:"application/json",text:JSON.stringify(e,null,2)}]}}function vo(n,e){let{adapter:t,projectService:r,reviewService:o,forecastService:a,perspectiveService:s}=e;n.registerResource("omnifocus-snapshot",ze,{description:"Orientation snapshot of the current OmniFocus state: inboxCount, overdueCount, dueTodayCount, flaggedCount, reviewDueCount, and syncStatus { lastSyncAt, inFlight }. Read at session start to orient before calling task_list or forecast_get. Use syncStatus.lastSyncAt to detect stale data before making decisions.",mimeType:"application/json"},async i=>{let{from:l,to:u}=nn(),[c,h,m,g]=await Promise.all([t.listTasks({completed:false}),t.getForecast({from:l,to:u,includeOverdue:true,includeFlagged:true}),t.listProjectsDueForReview(),t.getLastSync()]),b=c.filter(w=>w.projectId===null&&w.parentId===null).length;return q(ze,{inboxCount:b,overdueCount:h.overdue.length,dueTodayCount:h.dueToday.length,flaggedCount:h.flagged.length,reviewDueCount:m.length,syncStatus:{lastSyncAt:g.lastSyncAt,inFlight:g.inFlight}})}),n.registerResource("omnifocus-inbox",qe,{description:"Inbox tasks as Task[]. Incomplete tasks not assigned to any project or parent task. Use to triage the inbox without calling task_list.",mimeType:"application/json"},async i=>{let u=(await t.listTasks({completed:false})).filter(c=>c.projectId===null&&c.parentId===null);return q(qe,u)}),n.registerResource("omnifocus-forecast-today",Ve,{description:"Today's forecast tasks grouped by category: overdue[], dueToday[], deferredToday[], flagged[]. Equivalent to forecast_get with from/to=today. Use for 'what's on my plate today' without a tool call.",mimeType:"application/json"},async i=>{let{from:l,to:u}=nn(),c=await a.get({from:l,to:u});return q(Ve,{overdue:c.overdue,dueToday:c.dueToday,deferredToday:c.deferredToday,flagged:c.flagged})}),n.registerResource("omnifocus-overdue",We,{description:"All overdue tasks as Task[], sorted by dueDate ascending. Tasks whose dueDate is in the past and are not completed/dropped.",mimeType:"application/json"},async i=>{let{from:l}=nn(),c=[...(await t.getForecast({from:l,to:l,includeOverdue:true,includeFlagged:false,includeDeferred:false})).overdue].sort((h,m)=>h.dueDate?m.dueDate?h.dueDate<m.dueDate?-1:h.dueDate>m.dueDate?1:0:-1:1);return q(We,c)}),n.registerResource("omnifocus-flagged",Xe,{description:"All flagged available tasks as Task[]. Equivalent to task_list with flagged=true. Use to review the flagged list without a tool call.",mimeType:"application/json"},async i=>{let l=await t.listTasks({flagged:true,completed:false});return q(Xe,l)}),n.registerResource("omnifocus-review-due",Ke,{description:"Projects due for review as Project[], sorted by nextReviewDate ascending. Equivalent to review_list_due. Use to start a review session without a tool call.",mimeType:"application/json"},async i=>{let u=[...(await o.listDue()).projects].sort((c,h)=>c.nextReviewDate?h.nextReviewDate?c.nextReviewDate<h.nextReviewDate?-1:c.nextReviewDate>h.nextReviewDate?1:0:-1:1);return q(Ke,u)}),n.registerResource("omnifocus-project",new ResourceTemplate(rn,{list:void 0}),{description:"Single project with its full task tree as { project: Project, tasks: Task[] }. Get the project ID from project_list. Tasks are a flat array; rebuild the tree via task.parentId.",mimeType:"application/json"},async(i,l)=>{let u=k.of(l.id),c=await r.get({id:u,includeTaskTree:true});return q(`omnifocus://project/${u}`,{project:c.project,tasks:c.tasks??[]})}),n.registerResource("omnifocus-tag",new ResourceTemplate(on,{list:void 0}),{description:"Single tag with its tasks as { tag: Tag, tasks: Task[] }. Get the tag ID from tag_list.",mimeType:"application/json"},async(i,l)=>{let u=y.of(l.id),[c,h]=await Promise.all([t.getTag(u),t.listTasks({tagId:u,completed:false})]);return q(`omnifocus://tag/${u}`,{tag:c,tasks:h})}),n.registerResource("omnifocus-perspective",new ResourceTemplate(an,{list:void 0}),{description:"Perspective evaluation result as { perspectiveId: string, tasks: Task[] }. Get perspective IDs from perspective_list. Built-in IDs: inbox, projects, tags, forecast, flagged, nearby, review.",mimeType:"application/json"},async(i,l)=>{let u=l.id,c=await s.evaluate(u);return q(`omnifocus://perspective/${u}`,{perspectiveId:u,tasks:c.tasks})}),n.registerResource("omnifocus-tasks-inbox",yo,{description:"Inbox tasks as Task[]. Alias for omnifocus://inbox using the unified tasks namespace. Returns incomplete tasks not assigned to any project or parent task.",mimeType:"application/json"},async()=>{let l=(await t.listTasks({completed:false})).filter(u=>u.projectId===null&&u.parentId===null);return q(yo,l)}),n.registerResource("omnifocus-tasks-by-project",new ResourceTemplate(wc,{list:void 0}),{description:"Active tasks in a project as Task[]. Get the project ID from project_list or project_get. Returns incomplete tasks whose projectId matches the given ID.",mimeType:"application/json"},async(i,l)=>{let u=k.of(l.projectId),c=await t.listTasks({projectId:u,completed:false});return q(`omnifocus://tasks/project/${u}`,c)}),n.registerResource("omnifocus-tasks-by-tag",new ResourceTemplate(jc,{list:void 0}),{description:"Active tasks with a specific tag as Task[]. Get the tag ID from tag_list or tag_get. Returns incomplete tasks that carry the given tag.",mimeType:"application/json"},async(i,l)=>{let u=y.of(l.tagId),c=await t.listTasks({tagId:u,completed:false});return q(`omnifocus://tasks/tag/${u}`,c)});}function _e(n){return {code:"WARN_IDS_NOT_FOUND",message:`${n.length} requested ID(s) were not found and have been omitted.`,suggestion:"Verify the IDs are correct and that the items have not been deleted.",details:{missing:n}}}function d(n,e,t){let r={data:n,meta:e};return t!==void 0&&(r.pagination=t),r}function p(n){return {content:[{type:"text",text:JSON.stringify(n)}],structuredContent:n}}var sn="Explicitly launch OmniFocus. Do NOT call this automatically \u2014 only invoke when the user explicitly asks to open OmniFocus; prefer other tools when OF is already running. Safe to call when OmniFocus is already running (idempotent). Returns { launched, alreadyRunning } \u2014 launched=true means OmniFocus was not running and was started; alreadyRunning=true means it was already open. Side effects: may open OmniFocus and bring it to the foreground.",Pc=z.object({});async function Oc(n,e){let t=await e.adapter.appLaunch(),r=e.makeMeta();return d({launched:t.launched,alreadyRunning:t.alreadyRunning},r)}function So(n,e){return n.registerTool("app_launch",{description:sn,inputSchema:Pc.shape},async t=>{let r=await Oc(t,e);return p(r)})}var gt=z.object({taskId:f.schema.optional().describe("Persistent ID of the task that owns the attachment. Provide exactly one of taskId or projectId."),projectId:k.schema.optional().describe("Persistent ID of the project that owns the attachment. Provide exactly one of taskId or projectId.")});function ht(n){if(n.taskId)return {taskId:f.of(n.taskId)};if(n.projectId)return {projectId:k.of(n.projectId)};throw new I("Provide exactly one of taskId or projectId.",{})}var dn="List all file attachments on a task or project. Do not use to retrieve attachment content \u2014 use attachment_save_to_path instead. Returns { attachments } \u2014 array of objects with id, name, mimeType, sizeBytes, addedAt, and kind (embedded|alias). Provide exactly one of taskId or projectId. Read-only; safe to retry.",_c=gt;async function xc(n,e){let t=ht(n),r=await e.attachmentService.list(t);return d({attachments:r},e.makeMeta())}var ln="Add a file attachment to a task or project from a local file path. The file is embedded into the OmniFocus database. Path must be within the allowed scope (default: $HOME; override via OMNIFOCUS_ATTACHMENT_PATHS). File must not exceed the size cap (default 100 MB; override via OMNIFOCUS_MAX_ATTACHMENT_MB). Returns the new attachment ID. Mutations do not propagate until sync_trigger is called.",Ac=gt.extend({filePath:z.string().min(1).describe("Absolute path to the source file to attach. Must be within the allowed attachment path scope.")});async function Dc(n,e){let t=ht(n),r=await e.attachmentService.add({...t,filePath:n.filePath});return d({id:r},e.makeMeta())}var pn="Remove an attachment from a task or project by attachment ID. Do not use to retrieve or export attachment content \u2014 use attachment_save_to_path instead. Returns { removed: true } on success. Throws NotFound if the attachment or owner does not exist. Permanent \u2014 cannot be undone. Mutations do not propagate until sync_trigger is called.",Cc=gt.extend({attachmentId:oe.schema.describe("Persistent ID of the attachment to remove. Get from attachment_list.")});async function Rc(n,e){let t=ht(n);return await e.attachmentService.remove({...t,attachmentId:oe.of(n.attachmentId)}),d({removed:true},e.makeMeta())}var un="Copy an attachment's content to a local file path. Do not use to list or remove attachments \u2014 use attachment_list or attachment_remove instead. Returns { saved: true, path, sizeBytes } on success. Destination path must be within the allowed scope (default: $HOME). Writes the file to destPath (creates or overwrites); no side effects on OmniFocus data.",Mc=gt.extend({attachmentId:oe.schema.describe("Persistent ID of the attachment to save. Get from attachment_list."),destPath:z.string().min(1).describe("Absolute destination path where the attachment will be written. Must be within the allowed attachment path scope. Existing files are overwritten.")});async function Fc(n,e){let t=ht(n),r=await e.attachmentService.saveTo({...t,attachmentId:oe.of(n.attachmentId),destPath:n.destPath});return d(r,e.makeMeta())}function wo(n,e){n.registerTool("attachment_list",{description:dn,inputSchema:_c.shape},async t=>{let r=await xc(t,e);return p(r)}),n.registerTool("attachment_add",{description:ln,inputSchema:Ac.shape},async t=>{let r=await Dc(t,e);return p(r)}),n.registerTool("attachment_remove",{description:pn,inputSchema:Cc.shape},async t=>{let r=await Rc(t,e);return p(r)}),n.registerTool("attachment_save_to_path",{description:un,inputSchema:Mc.shape},async t=>{let r=await Fc(t,e);return p(r)});}var fn="Export OmniFocus data as OPML XML \u2014 a structured outline format OmniFocus can import. Do NOT use to export a single task; OPML scope is project-level or broader. Three scopes: 'project' (one project + its tasks), 'folder' (all projects in a folder), or 'all' (all active projects). Returns { opml, projectCount, taskCount } where opml is a complete XML string. Safe to call repeatedly; no side effects.",Ec=z.object({scope:z.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z.string().optional().describe("Required when scope='project' (project ID from project_list) or scope='folder' (folder ID from folder_list). Omit for scope='all'.")});function Nc(n){if(n.scope==="all")return {kind:"all"};if(!n.id)throw new I(`scope='${n.scope}' requires an id`,{details:{field:"id",scope:n.scope}});return n.scope==="project"?{kind:"project",id:k.of(n.id)}:{kind:"folder",id:A.of(n.id)}}async function Uc(n,e){let t=Nc(n),r=await e.exportService.exportOpml(t),o=e.makeMeta();return d({opml:r.opml,projectCount:r.projectCount,taskCount:r.taskCount},o)}function jo(n,e){return n.registerTool("export_opml",{description:fn,inputSchema:Ec.shape},async t=>{let r=await Uc(t,e);return p(r)})}var hn='Import tasks from an OPML XML string into OmniFocus. Parses the OPML produced by export_opml and recreates the task hierarchy. Top-level <outline type="omnifocus:project"> elements are matched to existing projects by OmniFocus ID (for round-trip) then by name; unmatched project outlines land in the Inbox. LOSSY: due dates, defer dates, and flagged state are preserved; tags, notes, attachments, and repetition rules are silently dropped (not encoded in OPML). Do NOT use to export data; prefer export_opml for that. Returns { imported, taskIds } where imported is the count of tasks created. Writes to OmniFocus; call sync_trigger after import to propagate changes to other devices.',Lc=z.object({opml:z.string().min(1).describe("Well-formed OPML XML string to import. Use the output of export_opml for a round-trip."),destinationProjectId:z.string().optional().describe("When set, all tasks are created in this project regardless of project headings in the OPML. Get the ID from project_list. Omit to match projects by ID/name from the OPML structure.")});async function Jc(n,e){let t=await e.exportService.importOpml(n.opml,{...n.destinationProjectId!==void 0?{destinationProjectId:k.of(n.destinationProjectId)}:{}}),r=e.makeMeta({syncPending:true});return d({imported:t.imported,taskIds:t.taskIds.map(String)},r)}function bo(n,e){return n.registerTool("import_opml",{description:hn,inputSchema:Lc.shape},async t=>{let r=await Jc(t,e);return p(r)})}var In="Export OmniFocus data as TaskPaper plain text. Three scopes: 'project' (one project + its tasks), 'folder' (all projects in a folder), or 'all' (all active projects). Export is lossy \u2014 HTML notes are downgraded to plain text; tag locations, attachments, and complex repetition rules are omitted. Lossiness warnings are returned in meta.warnings. Do NOT use to import data; prefer import_taskpaper for that. Returns { taskpaper, projectCount, taskCount }. Safe to call repeatedly; no side effects.",kn="Import tasks from TaskPaper text into OmniFocus. Parses '- Task name @tag @due(2026-01-15) @defer(2026-01-10) @flagged' lines. Indented subtasks become children of the nearest parent task. Project headings ('Project name:') map to existing OF projects by name \u2014 unrecognised headings fall back to inbox (warning emitted). Unknown @tags are created automatically. Do NOT use to export data; prefer export_taskpaper for that. Returns { created: TaskId[], warnings: string[] }. Writes to OmniFocus; call sync_trigger to propagate changes to other devices.",Bc=z.object({scope:z.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z.string().optional().describe("Required when scope='project' (project ID from project_list) or scope='folder' (folder ID from folder_list). Omit for scope='all'.")}),Gc=z.object({text:z.string().min(1).describe("TaskPaper-formatted text to import. Each '- Task name' line becomes a task."),targetProjectId:z.string().optional().describe("When set, all top-level tasks are created in this project regardless of project headings in the text. Get the ID from project_list.")});function Hc(n){if(n.scope==="all")return {kind:"all"};if(!n.id)throw new I(`scope="${n.scope}" requires an id \u2014 provide the ${n.scope==="project"?"project":"folder"} ID`,{details:{field:"id",scope:n.scope}});return n.scope==="project"?{kind:"project",id:k.of(n.id)}:{kind:"folder",id:A.of(n.id)}}function Po(n,e){n.registerTool("export_taskpaper",{description:In,inputSchema:Bc.shape},async t=>{let r=Hc(t),o=await e.exportService.exportTaskPaper(r);return p(d({taskpaper:o.taskpaper,projectCount:o.projectCount,taskCount:o.taskCount,warnings:o.warnings},e.makeMeta()))}),n.registerTool("import_taskpaper",{description:kn,inputSchema:Gc.shape},async t=>{let r=await e.exportService.importTaskPaper(t.text,t.targetProjectId===void 0?void 0:k.of(t.targetProjectId));return p(d({created:r.created,warnings:r.warnings},e.makeMeta()))});}var Tn="Create a new folder in OmniFocus. Optionally nest it inside an existing parent folder (get IDs from folder_list). Do not use to move an existing folder; prefer folder_move instead. Returns the new folder's persistent ID. Triggers a sync; call sync_trigger after to propagate to other devices.",$c=z.object({name:z.string().min(1).describe("Folder name. Must be non-empty."),parentId:A.schema.optional().describe("Parent folder ID. Omit for a root-level folder. Get from folder_list.")});async function zc(n,e){let t=await e.folderService.create({name:n.name,...n.parentId!==void 0?{parentId:n.parentId}:{}}),{folder:r}=await e.folderService.get(t.id);return d({folder:r},e.makeMeta({syncPending:true}))}function _o(n,e){return n.registerTool("folder_create",{description:Tn,inputSchema:$c.shape},async t=>{let r=await zc(t,e);return p(r)})}var yn="Delete a folder from OmniFocus. By default returns ValidationError when the folder contains projects or subfolders. Pass cascade=true to orphan all direct projects (move to no folder) and recursively delete subfolders before deleting. IRREVERSIBLE \u2014 do not use to archive; prefer folder_update to rename instead. Get the folder ID from folder_list. Triggers a sync; call sync_trigger after to propagate to other devices.",qc=z.object({id:A.schema.describe("Persistent folder ID to delete. Get from folder_list."),cascade:z.boolean().optional().describe("When true, orphan all direct projects and recursively delete subfolders before deleting. Default false \u2014 returns an error if the folder is non-empty.")});async function Vc(n,e){return await e.folderService.delete(n.id,n.cascade??false),d({deleted:true,id:n.id},e.makeMeta({syncPending:true}))}function Ao(n,e){return n.registerTool("folder_delete",{description:yn,inputSchema:qc.shape},async t=>{let r=await Vc(t,e);return p(r)})}var vn="Fetch a single folder by its persistent ID, including project and subfolder counts. Do not use to list multiple folders; prefer folder_list instead. Returns folder details including name, parentId, projectCount, and subfolderCount. Safe to call repeatedly; no side effects.",Xc=z.object({id:A.schema.describe("Persistent folder ID. Get from folder_list. IDs are stable across renames.")});async function Kc(n,e){let t=await e.folderService.get(n.id);return d({folder:t.folder},e.makeMeta({cacheHit:t.cacheHit}))}function Do(n,e){return n.registerTool("folder_get",{description:vn,inputSchema:Xc.shape},async t=>{let r=await Kc(t,e);return p(r)})}var Sn="List folders in OmniFocus, optionally filtered by parent folder. Do not use to fetch a single folder by ID; prefer folder_get instead. Returns a flat array with projectCount and subfolderCount per folder. Use parentId to walk the hierarchy one level at a time. Safe to call repeatedly; no side effects.",Qc=z.object({parentId:A.schema.optional().describe("Return only direct children of this folder. Get the ID from a previous folder_list call. Omit for root folders.")});async function Zc(n,e){let t={...n.parentId!==void 0?{parentId:n.parentId}:{}},r=await e.folderService.list(t);return d({folders:r.folders},e.makeMeta({cacheHit:r.cacheHit}))}function Co(n,e){return n.registerTool("folder_list",{description:Sn,inputSchema:Qc.shape},async t=>{let r=await Zc(t,e);return p(r)})}var wn="Move a folder to a new parent, or promote it to a root folder by passing parentId=null. Do not use to rename a folder; prefer folder_update instead. Get folder IDs from folder_list. Returns the updated folder's ID and new parentId on success. Triggers a sync; call sync_trigger after to propagate to other devices.",td=z.object({id:A.schema.describe("Persistent ID of the folder to move. Get from folder_list."),parentId:A.schema.nullable().describe("New parent folder ID, or null to promote the folder to root level.")});async function nd(n,e){await e.folderService.move(n.id,n.parentId);let{folder:t}=await e.folderService.get(n.id);return d({folder:t},e.makeMeta({syncPending:true}))}function Ro(n,e){return n.registerTool("folder_move",{description:wn,inputSchema:td.shape},async t=>{let r=await nd(t,e);return p(r)})}var jn="Rename a folder (partial patch \u2014 only supplied fields are changed). To move a folder use folder_move instead. Get the folder ID from folder_list. Returns the updated folder on success. Triggers a sync; call sync_trigger after to propagate to other devices.",rd=z.object({id:A.schema.describe("Persistent folder ID. Get from folder_list."),name:z.string().min(1).optional().describe("New folder name. Must be non-empty if supplied.")});async function od(n,e){let{id:t,...r}=n;await e.folderService.update(t,{...r.name!==void 0?{name:r.name}:{}});let{folder:o}=await e.folderService.get(t);return d({folder:o},e.makeMeta({syncPending:true}))}function Fo(n,e){return n.registerTool("folder_update",{description:jn,inputSchema:rd.shape},async t=>{let r=await od(t,e);return p(r)})}var ad=new Set(["today","tomorrow","yesterday","this-week","next-week","end-of-week","end-of-month"]);function Ye(n){return typeof n=="string"&&ad.has(n)}function he(n){let e=-n.getTimezoneOffset(),t=e>=0?"+":"-",r=Math.abs(e),o=String(Math.floor(r/60)).padStart(2,"0"),a=String(r%60).padStart(2,"0"),s=n.getFullYear(),i=String(n.getMonth()+1).padStart(2,"0"),l=String(n.getDate()).padStart(2,"0");return `${s}-${i}-${l}T00:00:00${t}${o}:${a}`}function Qe(n,e=new Date){let t=new Date(e);switch(t.setHours(0,0,0,0),n){case "today":return he(t);case "tomorrow":{let r=new Date(t);return r.setDate(r.getDate()+1),he(r)}case "yesterday":{let r=new Date(t);return r.setDate(r.getDate()-1),he(r)}case "this-week":{let r=new Date(t),o=r.getDay();return r.setDate(r.getDate()+(o===0?-6:1-o)),he(r)}case "next-week":{let r=new Date(t),o=r.getDay();return r.setDate(r.getDate()+(o===0?1:8-o)),he(r)}case "end-of-week":{let r=new Date(t),o=r.getDay();return r.setDate(r.getDate()+(o===0?0:7-o)),he(r)}case "end-of-month":{let r=new Date(t);return r.setMonth(r.getMonth()+1,0),he(r)}}}var Eo=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;function Ze(n){return typeof n=="string"&&Eo.test(n)&&!Number.isNaN(Date.parse(n))}function Ie(){return z.string().regex(Eo,"Expected ISO-8601 with offset (e.g. 2026-04-19T12:00:00-05:00 or 2026-04-19T17:00:00Z). Bare local time is rejected.").refine(n=>!Number.isNaN(Date.parse(n)),{message:"Well-formed ISO-8601 but not a valid date (out-of-range month, hour, or minute)."})}function ae(){return z.string().transform((n,e)=>Ze(n)?n:Ye(n)?Qe(n):(e.addIssue({code:z.ZodIssueCode.custom,message:`Expected ISO-8601 with offset or a relative shortcut (today, tomorrow, yesterday, this-week, next-week, end-of-week, end-of-month). Got: "${n}".`}),z.NEVER))}var bn="Get forecast-view tasks from OmniFocus grouped by category: overdue, dueToday, deferredToday, flagged. Use this for 'what's on my plate today' or multi-day planning queries. Do NOT use to list all tasks across all projects; prefer task_list instead. Supply date (ISO-8601 or shortcut like 'today', 'tomorrow') and days (1\u20137) for the ergonomic interface, or use from/to for exact ISO-8601 ranges. All include flags default to true; set to false to omit a category. When days > 1, response also includes byDate[] grouping tasks per calendar day. Returns { overdue[], dueToday[], deferredToday[], flagged[], byDate? }; safe to call repeatedly; no side effects.",sd=z.object({date:ae().optional().describe("Anchor date for the forecast (ISO-8601 or relative shortcut: today, tomorrow, yesterday, this-week, next-week). Mutually exclusive with from/to. Defaults to today."),days:z.number().int().min(1).max(7).optional().default(1).describe("Number of days to cover (1\u20137). Default 1. When > 1, byDate[] is included in the response."),from:ae().optional().describe("Start of date range (ISO-8601 or relative shortcut like 'today'). Use date/days for the ergonomic interface instead. Defaults to start of today."),to:ae().optional().describe("End of date range (ISO-8601 or relative shortcut like 'today'). Use date/days for the ergonomic interface instead. Defaults to end of today."),includeOverdue:z.boolean().optional().default(true).describe("Include tasks overdue before the start of the range. Default true."),includeDeferred:z.boolean().optional().default(true).describe("Include tasks whose defer date falls within the range. Default true."),includeFlagged:z.boolean().optional().default(true).describe("Include all flagged incomplete tasks. Default true.")});function id(n){let e;if(Ye(n))e=Qe(n);else if(Ze(n))e=n;else throw new I(`Invalid date: "${n}". Expected ISO-8601 or a relative shortcut.`,{details:{field:"date",value:n}});let t=new Date(e);return t.setHours(0,0,0,0),t}function cd(n){let e=n.date!==void 0,t=n.from!==void 0||n.to!==void 0;if(e&&t)throw new I("date and from/to are mutually exclusive \u2014 use one or the other.",{suggestion:"Remove from/to when using date, or remove date when using from/to.",details:{field:"date|from|to"}});let r=n.days??1;if(e){let i=id(n.date),l=new Date(i);return l.setDate(l.getDate()+r-1),l.setHours(23,59,59,999),{from:i.toISOString(),to:l.toISOString(),days:r}}if(t){let i=new Date,l=new Date(i);l.setHours(0,0,0,0);let u=new Date(i);return u.setHours(23,59,59,999),{from:n.from??l.toISOString(),to:n.to??u.toISOString(),days:r}}let o=new Date,a=new Date(o);a.setHours(0,0,0,0);let s=new Date(a);return s.setDate(s.getDate()+r-1),s.setHours(23,59,59,999),{from:a.toISOString(),to:s.toISOString(),days:r}}function dd(n){let e=new Map;for(let t of n){if(!t.dueDate)continue;let r=t.dueDate.slice(0,10),o=e.get(r)??[];o.push(t),e.set(r,o);}return [...e.entries()].sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>({date:t,tasks:r}))}async function ld(n,e){let{from:t,to:r,days:o}=cd(n),a=await e.forecastService.get({from:t,to:r,includeOverdue:n.includeOverdue,includeDeferred:n.includeDeferred,includeFlagged:n.includeFlagged}),s=e.makeMeta({cacheHit:a.cacheHit}),i={overdue:a.overdue,dueToday:a.dueToday,deferredToday:a.deferredToday,flagged:a.flagged};return o>1&&(i.byDate=dd(a.dueToday)),d(i,s)}function No(n,e){return n.registerTool("forecast_get",{description:bn,inputSchema:sd.shape},async t=>{let r=await ld(t,e);return p(r)})}function S(n,e={}){e.taskId!==void 0&&n.invalidate(`task:${e.taskId}`),e.projectId!==void 0&&e.projectId!==null&&n.invalidate(`project:${e.projectId}`),n.invalidate("forecast:*"),n.invalidate("perspective:*"),n.invalidate("search:*");}function M(n,e){n.invalidate(`project:${e.projectId}`),n.invalidate("forecast:*"),n.invalidate("perspective:*"),n.invalidate("search:*");}function Uo(n,e){n.invalidate(`tag:${e.tagId}`),n.invalidate("forecast:*"),n.invalidate("perspective:*"),n.invalidate("search:*");}function Lo(n,e){n.invalidate(`folder:${e.folderId}`),n.invalidate("perspective:*"),n.invalidate("search:*");}function Jo(n){n.clear();}var Pn="Append text to the plain-text note on a task or project. Adds a newline between existing content and the new text unless the note is empty. Do not use to replace the note entirely; prefer note_set instead. Returns { note } with the full note content after appending. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices.",pd=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to append to."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),text:z.string().min(1).describe("Text to append. A newline separator is inserted before the text if a note exists.")});async function ud(n,e){let t=n.targetKind==="task"?(await e.adapter.getTask(f.of(n.id))).note:(await e.adapter.getProject(k.of(n.id))).note,r=t?`${t}
|
|
69
|
-
${n.text}`:n.text;return n.targetKind==="task"?(await e.adapter.updateTask(f.of(n.id),{note:r}),e.cache!==void 0&&S(e.cache,{taskId:f.of(n.id)})):(await e.adapter.updateProject(k.of(n.id),{note:r}),e.cache!==void 0&&M(e.cache,{projectId:k.of(n.id)})),d({updated:true,id:n.id},e.makeMeta({syncPending:true}))}function Bo(n,e){return n.registerTool("note_append",{description:Pn,inputSchema:pd.shape},async t=>{let r=await ud(t,e);return p(r)})}var _n="Read the plain-text note from a task or project. Do not use when formatting fidelity matters; prefer note_get_html instead. Returns { note } \u2014 a string (may be empty) or null when no note exists. Set targetKind to 'task' and provide a task ID, or 'project' and a project ID. Safe to call repeatedly; no side effects.",md=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to read."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function fd(n,e){let t=n.targetKind==="task"?(await e.adapter.getTask(f.of(n.id))).note:(await e.adapter.getProject(k.of(n.id))).note;return d({note:t??null},e.makeMeta())}function Go(n,e){return n.registerTool("note_get",{description:_n,inputSchema:md.shape},async t=>{let r=await fd(t,e);return p(r)})}var An="Read the HTML fragment from a task or project note. Returns { noteHtml } \u2014 an HTML string (may be empty) or null when no note exists. Set targetKind to 'task' and provide a task ID, or 'project' and a project ID. For plain-text access without formatting, use note_get instead. Safe to call repeatedly; no side effects.",gd=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to read."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function hd(n,e){let t=n.targetKind==="task"?(await e.adapter.getTask(f.of(n.id))).noteHtml:(await e.adapter.getProject(k.of(n.id))).noteHtml;return d({noteHtml:t??null},e.makeMeta())}function Ho(n,e){return n.registerTool("note_get_html",{description:An,inputSchema:gd.shape},async t=>{let r=await hd(t,e);return p(r)})}var Dn="Replace the plain-text note on a task or project. Overwrites the existing note entirely. Pass note: null to clear the note. To add text without overwriting use note_append instead. Returns { note } with the final note content after writing. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices.",Id=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to set."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),note:z.string().nullable().describe("New note text. Pass null to clear the note entirely.")});async function kd(n,e){return n.targetKind==="task"?(await e.adapter.updateTask(f.of(n.id),{note:n.note}),e.cache!==void 0&&S(e.cache,{taskId:f.of(n.id)})):(await e.adapter.updateProject(k.of(n.id),{note:n.note}),e.cache!==void 0&&M(e.cache,{projectId:k.of(n.id)})),d({updated:true,id:n.id},e.makeMeta({syncPending:true}))}function $o(n,e){return n.registerTool("note_set",{description:Dn,inputSchema:Id.shape},async t=>{let r=await kd(t,e);return p(r)})}var Cn="Replace the HTML fragment note on a task or project. Overwrites the existing note entirely with the provided HTML. OmniFocus preserves its supported HTML subset (bold, italic, links, lists, inline images); unsupported elements may be stripped. Pass noteHtml: null to clear the note. For plain-text writes use note_set instead. Returns { noteHtml } with the final HTML content after writing. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices.",Td=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to set."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),noteHtml:z.string().nullable().describe("HTML fragment to set as the note. Pass null to clear the note entirely.")});async function yd(n,e){return n.targetKind==="task"?(await e.adapter.updateTask(f.of(n.id),{noteHtml:n.noteHtml}),e.cache!==void 0&&S(e.cache,{taskId:f.of(n.id)})):(await e.adapter.updateProject(k.of(n.id),{noteHtml:n.noteHtml}),e.cache!==void 0&&M(e.cache,{projectId:k.of(n.id)})),d({updated:true,id:n.id},e.makeMeta({syncPending:true}))}function zo(n,e){return n.registerTool("note_set_html",{description:Cn,inputSchema:Td.shape},async t=>{let r=await yd(t,e);return p(r)})}var Rn="Return a health snapshot of the running omnifocus-mcp server. Do NOT use this to read OmniFocus data \u2014 prefer task_list, project_list, sync_status, etc. Returns { uptimeMs, ofRunning, lastSync, cache, circuits, queueDepth }. uptimeMs is the milliseconds since the server process started. circuits lists each circuit-breaker name and state (closed/open/half_open). lastSync mirrors sync_status data; null if getLastSync throws. Read-only; no side effects.",Sd=z.object({});async function wd(n,e){let t=Date.now()-e.startedAt,r=null;try{let s=await e.adapter.getLastSync();r={lastSyncAt:s.lastSyncAt,inFlight:s.inFlight};}catch{r=null;}let o=e.circuitRegistry.snapshot();return d({uptimeMs:t,ofRunning:true,lastSync:r,cache:null,circuits:o,queueDepth:null},e.makeMeta())}function qo(n,e){return n.registerTool("internal_status",{description:Rn,inputSchema:Sd.shape},async t=>{let r=await wd(t,e);return p(r)})}var Mn="Evaluate an OmniFocus perspective and return its task list. Accepts both built-in ids (inbox, projects, tags, forecast, flagged, nearby, review) and custom-perspective ids obtained from perspective_list \u2014 the tool selects the correct transport internally (JXA for built-in, OmniJS for custom). Custom perspectives require OmniFocus Pro; otherwise returns an error with code OF_FEATURE_REQUIRES_PRO. Returns { tasks: Task[] }. For 'review', returns [] \u2014 use review_list_due instead. For 'nearby', returns [] (location unavailable). No side effects; read-only.",jd=z.object({perspectiveId:z.string().min(1).describe("OmniFocus perspective id. Accepts a built-in id (inbox, projects, tags, forecast, flagged, nearby, review) or a custom-perspective id from perspective_list (kind: custom).")});async function bd(n,e){let t=await e.perspectiveService.evaluate(n.perspectiveId),r=e.makeMeta({cacheHit:t.cacheHit});return d({tasks:t.tasks},r)}function Wo(n,e){return n.registerTool("perspective_evaluate",{description:Mn,inputSchema:jd.shape},async t=>{let r=await bd(t,e);return p(r)})}var Fn="List all perspectives in OmniFocus \u2014 both built-in (Inbox, Projects, Tags, Forecast, Flagged, Nearby, Review) and custom (OmniFocus Pro). Do not use to evaluate a perspective; prefer perspective_evaluate for that. Returns each perspective's id, name, kind (builtin|custom), and requiresPro flag. Safe to call repeatedly; no side effects, no writes.",Od=z.object({});async function _d(n,e){let t=await e.perspectiveService.list(),r=e.makeMeta({cacheHit:t.cacheHit});return d({perspectives:t.perspectives},r)}function Xo(n,e){return n.registerTool("perspective_list",{description:Fn,inputSchema:Od.shape},async t=>{let r=await _d(t,e);return p(r)})}var Ae=class{adapter;constructor({adapter:e}){this.adapter=e;}async invoke(e){let t={identifier:e.identifier,...e.arg!==void 0?{arg:e.arg}:{}};return {result:(await this.adapter.pluginInvoke(t)).result}}};var Nn="Invoke a named Omni Automation plug-in action in OmniFocus. Use this when you need to run a specific installed plug-in \u2014 not for built-in OmniFocus operations. Do NOT use to run arbitrary JavaScript; for raw scripting use run_omnijs_script (requires opt-in env var). `identifier` is the plug-in's bundle ID (e.g. `\"com.example.my-plugin\"`). `arg` is an optional JSON-serialisable value passed to the plug-in action as Action.args[0]. Returns { result } where result is the plug-in's return value (arbitrary JSON). Throws NotFound if the plug-in is not installed. Side effects: plug-in may mutate OmniFocus data; call sync_trigger if you need changes on other devices.",xd=z.object({identifier:z.string().min(1).describe('Bundle identifier of the Omni Automation plug-in to invoke (e.g. "com.example.my-plugin").'),arg:z.unknown().optional().describe("Optional JSON-serialisable argument forwarded to the plug-in action as Action.args[0]. Defaults to null.")});async function Ad(n,e){let t=new Ae({adapter:e.adapter}),{result:r}=await t.invoke({identifier:n.identifier,arg:n.arg}),o=e.makeMeta();return d({result:r},o)}function Ko(n,e){return n.registerTool("plugin_invoke",{description:Nn,inputSchema:xd.shape},async t=>{let r=await Ad(t,e);return p(r)})}var Ln="Mark many OmniFocus projects as completed in a single JXA round trip. Completed projects are hidden from active views and closed to new task entry. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each completion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated project_complete calls whenever completing more than one project. Each item is { id }. Returns { completed: [{index, value: projectId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",Dd=z.object({id:k.schema.describe("Persistent project ID.")}),Cd=z.object({items:z.array(Dd).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Rd(n,e){let t=await e.adapter.batchCompleteProjects(n.items.map(r=>({id:r.id})));if(e.cache!==void 0)for(let r of t.succeeded){let o=n.items[r.index];o!==void 0&&M(e.cache,{projectId:o.id});}return d({completed:t.succeeded,failed:t.failed},e.makeMeta({syncPending:t.succeeded.length>0}))}function Yo(n,e){return n.registerTool("project_batch_complete",{description:Ln,inputSchema:Cd.shape},async t=>{let r=await Rd(t,e);return p(r)})}var Bn="Cancel (drop) many OmniFocus projects in a single JXA round trip. Dropped projects remain in OmniFocus but are treated as cancelled/inactive \u2014 they do not appear in active project lists. Use project_delete for permanent removal. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each drop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated project_drop calls whenever dropping more than one project. Each item is { id }. Returns { dropped: [{index, value: projectId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",Md=z.object({id:k.schema.describe("Persistent project ID.")}),Fd=z.object({items:z.array(Md).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Ed(n,e){let t=await e.adapter.batchDropProjects(n.items.map(r=>({id:r.id})));if(e.cache!==void 0)for(let r of t.succeeded){let o=n.items[r.index];o!==void 0&&M(e.cache,{projectId:o.id});}return d({dropped:t.succeeded,failed:t.failed},e.makeMeta({syncPending:t.succeeded.length>0}))}function Qo(n,e){return n.registerTool("project_batch_drop",{description:Bn,inputSchema:Fd.shape},async t=>{let r=await Ed(t,e);return p(r)})}var Gn="Complete an OmniFocus project \u2014 marks it done with today's date and moves it out of the active view. Use when a project is finished. Do not use to archive or hide a project without completing it; prefer project_drop for that. Returns { completed: true, id }. Side effects: sets completionDate, removes from active projects, sets meta.syncPending = true.",Ud=z.object({id:k.schema.describe("Persistent ID of the project to complete.")});async function Ld(n,e){return await e.projectService.completeProject(n.id),e.cache!==void 0&&M(e.cache,{projectId:n.id}),d({completed:true,id:n.id},e.makeMeta({syncPending:true}))}function Zo(n,e){return n.registerTool("project_complete",{description:Gn,inputSchema:Ud.shape},async t=>{let r=await Ld(t,e);return p(r)})}var Hn=class{_ttlMs;_maxEntries;_now;_entries=new Map;constructor(e={}){this._ttlMs=e.ttlMs??6e5,this._maxEntries=e.maxEntries??1024,this._now=e.now??(()=>Date.now());}get(e){let t=this._entries.get(e);if(t){if(t.expiresAt<=this._now()){this._entries.delete(e);return}return this._entries.delete(e),this._entries.set(e,t),t.envelope}}set(e,t){for(this._entries.delete(e),this._entries.set(e,{envelope:t,expiresAt:this._now()+this._ttlMs});this._entries.size>this._maxEntries;){let r=this._entries.keys().next().value;if(r===void 0)break;this._entries.delete(r);}}get size(){return this._entries.size}clear(){this._entries.clear();}},ea=new WeakMap;function Jd(n){let e=ea.get(n);return e||(e=new Map,ea.set(n,e)),e}async function V(n,e,t){if(e===void 0)return t();let r=n.get(e);if(r)return ta(r);let o=Jd(n),a=o.get(e);if(a){let i=await a;return ta(i)}let s=(async()=>{let i=await t();return n.set(e,i),i})();o.set(e,s);try{return await s}finally{o.delete(e);}}function ta(n){return {...n,meta:{...n.meta,idempotentReplay:true}}}function na(n,e){let t=process.env[n];if(t===void 0||t==="")return e;let r=Number.parseInt(t,10);return !Number.isFinite(r)||r<=0?e:r}var W=new Hn({ttlMs:na("OMNIFOCUS_IDEMPOTENCY_TTL_MS",6e5),maxEntries:na("OMNIFOCUS_IDEMPOTENCY_MAX_ENTRIES",1024)});var $n="Create a new OmniFocus project. Optionally place it in a folder, assign tags, set completion criterion, status, defer/due dates, estimated minutes, flagged state, and review interval. Safety control: pass idempotency_key to make transport retries safe \u2014 identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate project. Returns { created: true, id }. Side effects: creates a project in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the project to appear on other devices.",Bd=z.object({name:z.string().min(1).describe("Project name. Required, must be non-empty."),folderId:A.schema.optional().describe("Folder ID to place the project in. Omit for root."),note:z.string().optional().describe("Plain-text note for the project."),status:z.enum(["active","on-hold"]).optional().describe("Initial project status. Default: active."),completionCriterion:z.enum(["parallel","sequential","singleActions"]).optional().describe("How the project's tasks are completed: parallel (any order), sequential (in order), or singleActions."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with UTC offset."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with UTC offset."),estimatedMinutes:z.number().int().min(1).optional().describe("Estimated total duration in minutes."),flagged:z.boolean().optional().describe("Flag the project."),tagIds:z.array(y.schema).optional().describe("Tag IDs to apply to the project."),reviewIntervalDays:z.number().int().min(1).optional().describe("Review interval in days. Omit to use OmniFocus default."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe creates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate project.")});async function Gd(n,e){let t=e.idempotencyStore??W;return V(t,n.idempotency_key,async()=>{let r={name:n.name,...n.folderId!==void 0&&{folderId:n.folderId},...n.note!==void 0&&{note:n.note},...n.status!==void 0&&{status:n.status},...n.completionCriterion!==void 0&&{completionCriterion:n.completionCriterion},...n.deferDate!==void 0&&{deferDate:n.deferDate},...n.dueDate!==void 0&&{dueDate:n.dueDate},...n.estimatedMinutes!==void 0&&{estimatedMinutes:n.estimatedMinutes},...n.flagged!==void 0&&{flagged:n.flagged},...n.tagIds!==void 0&&{tagIds:n.tagIds},...n.reviewIntervalDays!==void 0&&{reviewIntervalDays:n.reviewIntervalDays}},o=await e.adapter.createProject(r);return e.cache!==void 0&&M(e.cache,{projectId:o}),d({created:true,id:o},e.makeMeta({syncPending:true}))})}function ra(n,e){return n.registerTool("project_create",{description:$n,inputSchema:Bd.shape},async t=>{let r=await Gd(t,e);return p(r)})}function de(n,e,t){if(n===void 0)return;let r=Date.parse(n);if(Number.isNaN(r))throw new I(`expectedModifiedAt for ${t} is not a valid ISO-8601 timestamp.`,{details:{resource:t,expected:n}});let o=Date.parse(e);if(Number.isNaN(o))throw new I(`observed modifiedAt for ${t} is not a valid ISO-8601 timestamp.`,{details:{resource:t,observed:e}});if(r!==o)throw new be(`${t} was modified since expectedModifiedAt.`,{details:{resource:t,expected:n,observed:e}})}async function le(n,e,t){if(n!==true)return t();let r=await e();return Hd(r)}function Hd(n){return {...n,meta:{...n.meta,dryRun:true,syncPending:false}}}var zn="Permanently delete an OmniFocus project and ALL its contained tasks. IRREVERSIBLE \u2014 uses OmniFocus deleteObject; there is no undo. All tasks inside the project are also permanently deleted (cascade). Prefer project_drop when you want a recoverable status change. Only use project_delete when the agent has explicit user intent to permanently remove the project and its tasks. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent project_get) to reject the call if the project changed since you read it; pass idempotency_key to coalesce retries so the same delete is only performed once. Returns { deleted: true, id } on success. Side effects: removes the project and its tasks from OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the deletion to appear on other devices.",$d=z.object({id:k.schema.describe("Persistent ID of the project to delete. Get from project_list. Verify you have the correct ID before calling \u2014 this action is irreversible and deletes all contained tasks."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent project_get. If the project's current modifiedAt differs, the call fails with OF_CONFLICT and no delete is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe deletes. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-deleting (or re-raising NotFound on the second attempt).")});async function zd(n,e){let t=e.idempotencyStore??W;return V(t,n.idempotency_key,async()=>{let r=await e.adapter.getProject(n.id);de(n.expectedModifiedAt,r.modifiedAt,`project:${n.id}`);let o=()=>d({deleted:true,id:n.id},e.makeMeta({syncPending:false})),a=async()=>(await e.adapter.deleteProject(n.id),e.cache!==void 0&&M(e.cache,{projectId:n.id}),d({deleted:true,id:n.id},e.makeMeta({syncPending:true})));return le(n.dry_run,o,a)})}function oa(n,e){return n.registerTool("project_delete",{description:zn,inputSchema:$d.shape},async t=>{let r=await zd(t,e);return p(r)})}var qn="Drop an OmniFocus project \u2014 marks it as on-hold/dropped and removes it from the active view without completing it. Use to defer or abandon a project while keeping it recoverable. Do not use if the project is actually done; prefer project_complete for that. Returns { dropped: true, id }. Side effects: changes project status, sets meta.syncPending = true.",Vd=z.object({id:k.schema.describe("Persistent ID of the project to drop.")});async function Wd(n,e){return await e.projectService.dropProject(n.id),e.cache!==void 0&&M(e.cache,{projectId:n.id}),d({dropped:true,id:n.id},e.makeMeta({syncPending:true}))}function aa(n,e){return n.registerTool("project_drop",{description:qn,inputSchema:Vd.shape},async t=>{let r=await Wd(t,e);return p(r)})}var Vn="Fetch a single OmniFocus project by persistent ID. Do NOT use for queries across projects \u2014 use project_list. When includeTaskTree=true (default), the project's flat task list is attached. Returns { project, tasks? }; safe to call repeatedly; no side effects.",Xd=z.object({id:k.schema.describe("Persistent project ID. Get from project_list or search_query."),includeTaskTree:z.boolean().optional().describe("Whether to attach the project's tasks (flat array; clients rebuild the tree via parentId). Default true. Set to false for a fast project-only read.")});async function Kd(n,e){let t=await e.projectService.get({id:n.id,...n.includeTaskTree!==void 0?{includeTaskTree:n.includeTaskTree}:{}}),r=e.makeMeta({cacheHit:t.cacheHit}),o={project:t.project};return t.tasks!==void 0&&(o.tasks=t.tasks),d(o,r)}function ia(n,e){return n.registerTool("project_get",{description:Vn,inputSchema:Xd.shape},async t=>{let r=await Kd(t,e);return p(r)})}var Wn="Fetch up to 100 projects by persistent ID in a single OmniFocus round-trip. Use when you have a set of project IDs and need full project objects for all of them. Do NOT use for a single ID \u2014 use project_get instead. Returns Project[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry.",St=100,Yd=z.object({ids:z.array(k.schema).min(0).max(St).describe(`Array of project IDs to fetch (0..${St}). Get IDs from project_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function Qd(n,e){if(n.ids.length===0)return d({projects:[]},e.makeMeta());if(n.ids.length>St)throw new I(`ids array exceeds the maximum batch size of ${St} (got ${n.ids.length})`,{details:{field:"ids"}});let t=await e.adapter.getProjectsMany(n.ids),r=t.filter(i=>i!==null),o=n.ids.filter((i,l)=>t[l]===null),a=o.length>0?[_e(o)]:void 0,s=e.makeMeta({...a!==void 0?{warnings:a}:{}});return d({projects:r},s)}function da(n,e){return n.registerTool("project_get_many",{description:Wn,inputSchema:Yd.shape},async t=>{let r=await Qd(t,e);return p(r)})}var Xn="List projects in OmniFocus with optional filters. Use for queries across projects. Do NOT use for a known single project (use project_get). Filters: folderId, status, flagged, reviewDueBefore. Returns projects[] with pagination; safe to call repeatedly; no side effects.",Zd=z.object({folderId:A.schema.optional().describe("Restrict to projects inside this folder. Get the ID from folder_list. Omit for all folders."),status:z.enum(["active","on-hold","done","dropped"]).optional().describe("Restrict to projects with this status. 'active' = available; 'on-hold' = paused; 'done' = completed; 'dropped' = abandoned. Omit for any status."),flagged:z.boolean().optional().describe("true = flagged only; false = unflagged only; omit = both."),reviewDueBefore:z.string().optional().describe("Restrict to projects whose next review date is strictly before this moment. ISO-8601 with offset (e.g. '2026-05-01T00:00:00-07:00'). Projects without a review interval are excluded."),limit:z.number().int().min(1).max(1e3).optional().describe("Max projects per page (1..1000). Default 200. Use `cursor` to fetch subsequent pages."),cursor:z.string().optional().describe("Opaque cursor from a previous project_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")});async function el(n,e){let t=n,r=await e.projectService.list(t),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return d({projects:r.projects},a,o)}function la(n,e){return n.registerTool("project_list",{description:Xn,inputSchema:Zd.shape},async t=>{let r=await el(t,e);return p(r)})}var Kn="Move an OmniFocus project to a different folder. Pass folderId to move into a folder, or null to move to the root (no folder). Use when reorganizing projects. Do not use to complete or drop a project. Returns { moved: true, id }. Side effects: changes the project's folder, sets meta.syncPending = true.",nl=z.object({id:k.schema.describe("Persistent ID of the project to move."),folderId:A.schema.nullable().describe("Target folder ID, or null to move to root.")});async function rl(n,e){return await e.projectService.moveProject(n.id,{folderId:n.folderId}),e.cache!==void 0&&M(e.cache,{projectId:n.id}),d({moved:true,id:n.id},e.makeMeta({syncPending:true}))}function pa(n,e){return n.registerTool("project_move",{description:Kn,inputSchema:nl.shape},async t=>{let r=await rl(t,e);return p(r)})}var Yn="Partially update mutable fields on an OmniFocus project. Only supplied fields are changed; omit a field to leave it unchanged. Pass null for note, deferDate, dueDate, estimatedMinutes, or reviewIntervalDays to clear those fields. Do NOT use to create or delete projects; prefer project_create or project_delete instead. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent project_get) to reject the call if the project changed since you read it; pass idempotency_key to coalesce retries so the same update is only performed once. Returns { updated: true, id }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",ol=z.object({id:k.schema.describe("Persistent project ID. Get from project_list or project_get."),name:z.string().min(1).optional().describe("New project name. Must be non-empty if supplied."),note:z.string().nullable().optional().describe("Plain-text note. Pass null to clear."),noteHtml:z.string().nullable().optional().describe("HTML note. Pass null to clear. Prefer note for plain-text edits."),status:z.enum(["active","on-hold"]).optional().describe("Project status. Use project_complete or project_drop to close a project."),completionCriterion:z.enum(["parallel","sequential","singleActions"]).optional().describe("How the project's tasks are completed."),deferDate:z.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),dueDate:z.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated total duration in minutes. Pass null to clear."),flagged:z.boolean().optional().describe("Flag or unflag the project."),tagIds:z.array(y.schema).optional().describe("Full-replacement tag list. Replaces all existing tags."),reviewIntervalDays:z.number().int().positive().nullable().optional().describe("Review interval in days. Pass null to clear."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent project_get. If the project's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe updates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying the patch.")});async function al(n,e){let{id:t,...r}=n,o=e.idempotencyStore??W;return V(o,n.idempotency_key,async()=>{let a=await e.adapter.getProject(t);de(n.expectedModifiedAt,a.modifiedAt,`project:${t}`);let s={...r.name!==void 0&&{name:r.name},...r.note!==void 0&&{note:r.note},...r.noteHtml!==void 0&&{noteHtml:r.noteHtml},...r.status!==void 0&&{status:r.status},...r.completionCriterion!==void 0&&{completionCriterion:r.completionCriterion},...r.deferDate!==void 0&&{deferDate:r.deferDate},...r.dueDate!==void 0&&{dueDate:r.dueDate},...r.estimatedMinutes!==void 0&&{estimatedMinutes:r.estimatedMinutes},...r.flagged!==void 0&&{flagged:r.flagged},...r.tagIds!==void 0&&{tagIds:r.tagIds},...r.reviewIntervalDays!==void 0&&{reviewIntervalDays:r.reviewIntervalDays}},i=()=>d({updated:true,id:t},e.makeMeta({syncPending:false})),l=async()=>(await e.adapter.updateProject(t,s),e.cache!==void 0&&M(e.cache,{projectId:t}),d({updated:true,id:t},e.makeMeta({syncPending:true})));return le(n.dry_run,i,l)})}function ua(n,e){return n.registerTool("project_update",{description:Yn,inputSchema:ol.shape},async t=>{let r=await al(t,e);return p(r)})}var Zn="\u26A0 DANGEROUS \u2014 raw JXA escape hatch. Executes an arbitrary JavaScript-for-Automation script against OmniFocus with FULL Automation privileges (read, write, delete, move, sync). Only available when the server was started with OMNIFOCUS_ALLOW_RAW_SCRIPT=1. Every call is audit-logged with the full script body. Do NOT use this for operations covered by the typed tools (task_*, project_*, tag_*, folder_*, etc.) \u2014 typed tools are safer, idempotent, and return structured results. Use ONLY when you need a feature no typed tool exposes AND you control the environment. `script` must be a JXA program that defines `function run(argv)` and returns a JSON-encoded string. `arg` is an optional JSON-serialisable value passed as argv[0] (defaults to `{}`). Returns { result } where result is the parsed JSON output of the script (arbitrary shape). Side effects: may mutate, delete, or exfiltrate any OmniFocus data the user has access to. Use sync_trigger separately if the script mutated data and you need it to propagate.",sl=z.object({script:z.string().min(1).describe("Raw JXA script body. Must define `function run(argv)` and return a JSON-encoded string."),arg:z.unknown().optional().describe("Optional JSON-serialisable argument passed to `run()` as argv[0]. Defaults to `{}`.")});async function il(n,e){if(typeof e.adapter.runJxaScript!="function")throw new I("run_jxa_script is not available in this adapter configuration",{details:{reason:"raw-script-unavailable"},suggestion:"Start the server with OMNIFOCUS_ALLOW_RAW_SCRIPT=1 to enable raw-script tools."});(e.logger??j).info({event:"raw_script.invoked",tool:"run_jxa_script",scriptLength:n.script.length,script:n.script},"raw JXA script invoked");let r=await e.adapter.runJxaScript(n.script,n.arg);return d({result:r},e.makeMeta({syncPending:true}))}function ma(n,e,t){return t.allowRawScript?n.registerTool("run_jxa_script",{description:Zn,inputSchema:sl.shape},async r=>{let o=await il(r,e);return p(o)}):null}var tr="\u26A0 DANGEROUS \u2014 raw OmniJS escape hatch. Executes an arbitrary Omni Automation (OmniJS) script against OmniFocus with FULL Automation privileges (read, write, delete, move, sync, plug-in APIs). Only available when the server was started with OMNIFOCUS_ALLOW_RAW_SCRIPT=1. Every call is audit-logged with the full script body. Do NOT use this for operations covered by the typed tools (task_*, project_*, plugin_invoke, etc.) \u2014 typed tools are safer, idempotent, and return structured results. Use ONLY when you need a feature no typed tool exposes AND you control the environment. `script` is a raw OmniJS program; the serialised result must be JSON-encodable. `arg` is an optional JSON-serialisable value forwarded through the callback-file bridge (defaults to `{}`). Returns { result } where result is the parsed JSON output of the script (arbitrary shape). Side effects: may mutate, delete, or exfiltrate any OmniFocus data the user has access to. Use sync_trigger separately if the script mutated data and you need it to propagate.",cl=z.object({script:z.string().min(1).describe("Raw OmniJS script body. Must produce a JSON-encodable result."),arg:z.unknown().optional().describe("Optional JSON-serialisable argument forwarded through the callback-file bridge. Defaults to `{}`.")});async function dl(n,e){if(typeof e.adapter.runOmniJsScript!="function")throw new I("run_omnijs_script is not available in this adapter configuration",{details:{reason:"raw-script-unavailable"},suggestion:"Start the server with OMNIFOCUS_ALLOW_RAW_SCRIPT=1 to enable raw-script tools."});(e.logger??j).info({event:"raw_script.invoked",tool:"run_omnijs_script",scriptLength:n.script.length,script:n.script},"raw OmniJS script invoked");let r=await e.adapter.runOmniJsScript(n.script,n.arg);return d({result:r},e.makeMeta({syncPending:true}))}function fa(n,e,t){return t.allowRawScript?n.registerTool("run_omnijs_script",{description:tr,inputSchema:cl.shape},async r=>{let o=await dl(r,e);return p(o)}):null}var nr="List projects due for review in OmniFocus \u2014 those whose next review date is today or earlier, or has never been set. Sorted by next review date ascending (overdue first, never-reviewed first). Do not use to get all projects; prefer project_list for that. Returns each project's id, name, nextReviewDate, lastReviewDate, and reviewIntervalDays. Safe to call repeatedly; no side effects, no writes.",pl=z.object({});async function ul(n,e){let t=await e.reviewService.listDue(),r=e.makeMeta({cacheHit:t.cacheHit});return d({projects:t.projects},r)}function ga(n,e){return n.registerTool("review_list_due",{description:nr,inputSchema:pl.shape},async t=>{let r=await ul(t,e);return p(r)})}var rr="Mark a project as reviewed in OmniFocus \u2014 sets lastReviewDate to now and advances nextReviewDate by the project's review interval. Use this after completing a weekly review of a project. Do not use to change the review interval; prefer review_set_interval for that. Returns the project id. Side effects: writes to OmniFocus; sets syncPending = true.",ml=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function fl(n,e){return await e.reviewService.markReviewed(k.of(n.id)),d({id:n.id},e.makeMeta({syncPending:true}))}function Ia(n,e){return n.registerTool("review_mark_reviewed",{description:rr,inputSchema:ml.shape},async t=>{let r=await fl(t,e);return p(r)})}var or="Convenience alias for review_mark_reviewed \u2014 mark a single project as reviewed, setting lastReviewDate to now and advancing nextReviewDate. Use when you have a project id and want a single-call review operation. Do not use to list projects due for review; prefer review_list_due for that. Returns the project id. Side effects: writes to OmniFocus; sets syncPending = true.",gl=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function hl(n,e){return await e.reviewService.markReviewed(k.of(n.id)),d({id:n.id},e.makeMeta({syncPending:true}))}function Ta(n,e){return n.registerTool("project_mark_reviewed",{description:or,inputSchema:gl.shape},async t=>{let r=await hl(t,e);return p(r)})}var sr="Set a project's review interval in OmniFocus \u2014 updates how many days between reviews. Use null to remove the recurring schedule. Do not use to mark a project as reviewed; prefer review_mark_reviewed for that. Returns the project id. Side effects: writes to OmniFocus; sets syncPending = true.",Il=z.object({id:z.string().min(1).describe("Persistent ID of the project to update."),days:z.number().int().min(1).nullable().describe("Review interval in days. Pass null to remove the recurring review schedule.")});async function kl(n,e){return await e.reviewService.setInterval(k.of(n.id),n.days),d({id:n.id},e.makeMeta({syncPending:true}))}function ya(n,e){return n.registerTool("review_set_interval",{description:sr,inputSchema:Il.shape},async t=>{let r=await kl(t,e);return p(r)})}var ir="Full-text search across OmniFocus task names and/or notes. Use for finding tasks by content when you don't know the ID. Supports optional filters (project, tags, flagged, completion status) and cursor pagination. Do NOT use when a known task ID is available (use task_get instead). Returns tasks[] with pagination; safe to call repeatedly; no side effects.",Tl=z.object({q:z.string().describe("Search query. Case-insensitive substring match. Empty string matches all tasks (useful with filters)."),scope:z.enum(["name","note","all"]).optional().describe("'name' = search task names only; 'note' = search notes only; 'all' = both. Default 'all'."),projectId:k.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list."),tagIds:z.array(y.schema).optional().describe("Restrict to tasks carrying ALL of these tags. Get IDs from tag_list."),flagged:z.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed only; 'any' = both. Default 'any'."),limit:z.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 100."),cursor:z.string().optional().describe("Opaque cursor from a previous search_query response. Must use identical filters \u2014 changing filters returns a ValidationError.")});async function yl(n,e){let t={q:n.q,...n.scope!==void 0?{scope:n.scope}:{},...n.projectId!==void 0?{projectId:n.projectId}:{},...n.tagIds!==void 0?{tagIds:n.tagIds}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.completed!==void 0?{completed:n.completed}:{},...n.limit!==void 0?{limit:n.limit}:{},...n.cursor!==void 0?{cursor:n.cursor}:{}},r=await e.searchService.search(t),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return d({tasks:r.tasks},a,o)}function va(n,e){return n.registerTool("search_query",{description:ir,inputSchema:Tl.shape},async t=>{let r=await yl(t,e);return p(r)})}var cr="Return the last OmniFocus sync state without triggering a new sync. Do NOT call this to initiate a sync \u2014 use sync_trigger instead. Use to check whether a previous sync completed before querying cross-device data. Returns { lastSyncAt, inFlight }. lastSyncAt is null if OmniFocus has never synced in this session. Read-only; no side effects.",Sl=z.object({});async function wl(n,e){let t=await e.adapter.getLastSync();return d({lastSyncAt:t.lastSyncAt,inFlight:t.inFlight},e.makeMeta())}function Sa(n,e){return n.registerTool("sync_status",{description:cr,inputSchema:Sl.shape},async t=>{let r=await wl(t,e);return p(r)})}var dr="Kick off an OmniFocus sync with Omni Sync Server. Do not call when no mutations have been made; prefer checking meta.syncPending first. Call this after any sequence of mutations (task_create, task_update, folder_create, etc.) when you need changes to appear on other devices. The sync starts immediately but completes asynchronously \u2014 this tool does not block until done. Returns meta.syncPending = false to confirm the sync was initiated. Side effects: triggers a sync request to Omni Sync Server.",bl=z.object({});async function Pl(n,e){let t=await e.adapter.syncTrigger();e.cache!==void 0&&Jo(e.cache);let r=e.makeMeta({syncPending:false});return d({lastSyncAt:t.lastSyncAt,inFlight:t.inFlight},r)}function wa(n,e){return n.registerTool("sync_trigger",{description:dr,inputSchema:bl.shape},async t=>{let r=await Pl(t,e);return p(r)})}var lr="Create a new tag in OmniFocus. Optionally nest it under an existing parent tag (get IDs from tag_list). Do not use to move an existing tag; prefer tag_move instead. Returns the new tag's persistent ID. Triggers a sync; call sync_trigger after to propagate to other devices.",Ol=z.object({name:z.string().min(1).describe("Tag name. Must be non-empty."),parentId:y.schema.optional().describe("Parent tag ID to nest under. Omit for a root tag. Get from tag_list."),status:z.enum(["active","on-hold"]).optional().describe("Initial status. Defaults to 'active'. Cannot create a tag in 'dropped' state."),allowsNextAction:z.boolean().optional().describe("Whether the tag allows next-action selection. Defaults to true.")});async function _l(n,e){let t=await e.tagService.create({name:n.name,...n.parentId!==void 0?{parentId:n.parentId}:{},...n.status!==void 0?{status:n.status}:{},...n.allowsNextAction!==void 0?{allowsNextAction:n.allowsNextAction}:{}}),{tag:r}=await e.tagService.get(t.id),o=e.makeMeta({syncPending:true});return d({tag:r},o)}function ja(n,e){return n.registerTool("tag_create",{description:lr,inputSchema:Ol.shape},async t=>{let r=await _l(t,e);return p(r)})}var pr="Hard-delete a tag from OmniFocus. IRREVERSIBLE \u2014 the tag and all its children are removed. Tasks that carried this tag lose it. Get the tag ID from tag_list. Prefer tag_set_status with status='dropped' to preserve history. Returns the deleted tag's ID on success. Side effects: writes to OmniFocus, sets meta.syncPending = true.",Al=z.object({id:y.schema.describe("Persistent tag ID to delete. Get from tag_list.")});async function Dl(n,e){return await e.tagService.delete(n.id),d({deleted:true,id:n.id},e.makeMeta({syncPending:true}))}function ba(n,e){return n.registerTool("tag_delete",{description:pr,inputSchema:Al.shape},async t=>{let r=await Dl(t,e);return p(r)})}var ur="Fetch a single tag by its persistent ID, including task count. Do not use to list multiple tags; prefer tag_list instead. Returns tag details; no side effects.",Rl=z.object({id:y.schema.describe("Persistent tag ID. Get from tag_list. IDs are stable across renames.")});async function Ml(n,e){let t=await e.tagService.get(n.id),r=e.makeMeta({cacheHit:t.cacheHit});return d({tag:t.tag},r)}function Pa(n,e){return n.registerTool("tag_get",{description:ur,inputSchema:Rl.shape},async t=>{let r=await Ml(t,e);return p(r)})}var mr="Get the geographic location trigger currently set on a tag, or null if none. Do not use to set or clear a location; prefer tag_set_location instead. Location-based tags are an OmniFocus Pro feature. Get the tag ID from tag_list. Returns { location } with name, radius, and trigger direction, or null if unset. Safe to call repeatedly; no side effects.",El=z.object({id:y.schema.describe("Persistent tag ID. Get from tag_list.")});async function Nl(n,e){let t=await e.tagService.getLocation(n.id),r=e.makeMeta({cacheHit:t.cacheHit});return d({location:t.location},r)}function Oa(n,e){return n.registerTool("tag_get_location",{description:mr,inputSchema:El.shape},async t=>{let r=await Nl(t,e);return p(r)})}var fr="Fetch up to 100 tags by persistent ID in a single OmniFocus round-trip. Use when you have a set of tag IDs and need full tag objects for all of them. Do NOT use for a single ID \u2014 use tag_get instead. Returns Tag[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry.",jt=100,Ul=z.object({ids:z.array(y.schema).min(0).max(jt).describe(`Array of tag IDs to fetch (0..${jt}). Get IDs from tag_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function Ll(n,e){if(n.ids.length===0)return d({tags:[]},e.makeMeta());if(n.ids.length>jt)throw new I(`ids array exceeds the maximum batch size of ${jt} (got ${n.ids.length})`,{details:{field:"ids"}});let t=await e.adapter.getTagsMany(n.ids),r=t.filter(i=>i!==null),o=n.ids.filter((i,l)=>t[l]===null),a=o.length>0?[_e(o)]:void 0,s=e.makeMeta({...a!==void 0?{warnings:a}:{}});return d({tags:r},s)}function xa(n,e){return n.registerTool("tag_get_many",{description:fr,inputSchema:Ul.shape},async t=>{let r=await Ll(t,e);return p(r)})}var gr="List all tags in OmniFocus, optionally filtered by parent tag or status. Do not use to fetch a single tag by ID; prefer tag_get instead. Returns a flat array \u2014 use parentId to walk the hierarchy one level at a time. Safe to call repeatedly; no side effects.",Jl=z.object({parentId:y.schema.optional().describe("Return only direct children of this tag. Get the ID from a previous tag_list call. Omit for root tags."),status:z.enum(["active","on-hold","dropped"]).optional().describe("Filter by tag status. Omit to return tags of all statuses.")});async function Bl(n,e){let t={...n.parentId!==void 0?{parentId:n.parentId}:{},...n.status!==void 0?{status:n.status}:{}},r=await e.tagService.list(t),o=e.makeMeta({cacheHit:r.cacheHit});return d({tags:r.tags},o)}function Da(n,e){return n.registerTool("tag_list",{description:gr,inputSchema:Jl.shape},async t=>{let r=await Bl(t,e);return p(r)})}var hr="Move a tag to a new parent, or promote it to a root tag by passing parentId=null. Do not use to rename a tag; prefer tag_update instead. Get tag IDs from tag_list. Returns the updated tag's ID and new parentId on success. Triggers a sync; call sync_trigger after to propagate to other devices.",Hl=z.object({id:y.schema.describe("Persistent ID of the tag to move. Get from tag_list."),parentId:y.schema.nullable().describe("New parent tag ID, or null to promote the tag to root level.")});async function $l(n,e){await e.tagService.move(n.id,n.parentId);let{tag:t}=await e.tagService.get(n.id);return d({tag:t},e.makeMeta({syncPending:true}))}function Ca(n,e){return n.registerTool("tag_move",{description:hr,inputSchema:Hl.shape},async t=>{let r=await $l(t,e);return p(r)})}var Ir="Enable or disable next-action selection for a tag in OmniFocus. When true, tasks with this tag are eligible for next-action promotion. Do not use to change other tag properties; prefer tag_update instead. Get the tag ID from tag_list. Returns the updated tag with allowsNextAction confirmed. Triggers a sync; call sync_trigger after to propagate to other devices.",zl=z.object({id:y.schema.describe("Persistent tag ID. Get from tag_list."),allowsNextAction:z.boolean().describe("true to enable next-action selection; false to disable.")});async function ql(n,e){await e.tagService.setAllowsNextAction(n.id,n.allowsNextAction);let{tag:t}=await e.tagService.get(n.id);return d({tag:t},e.makeMeta({syncPending:true}))}function Ma(n,e){return n.registerTool("tag_set_allows_next_action",{description:Ir,inputSchema:zl.shape},async t=>{let r=await ql(t,e);return p(r)})}var kr="Set a geographic location trigger on a tag (OmniFocus Pro only). The trigger fires when arriving at, leaving, or both for the specified radius. Do not use to read the current location; prefer tag_get_location instead. Get the tag ID from tag_list. Returns FeatureRequiresPro on OmniFocus Standard installs. Triggers a sync; call sync_trigger after to propagate to other devices.",Vl=z.object({id:y.schema.describe("Persistent tag ID. Get from tag_list."),latitude:z.number().min(-90).max(90).describe("Latitude in decimal degrees (\u221290 to 90)."),longitude:z.number().min(-180).max(180).describe("Longitude in decimal degrees (\u2212180 to 180)."),radiusMeters:z.number().min(0).describe("Trigger radius in metres. Must be \u2265 0."),trigger:z.enum(["entering","leaving","both"]).describe("When to trigger: 'entering', 'leaving', or 'both'."),name:z.string().nullable().optional().describe("Optional human-readable name for the location (e.g. 'Home', 'Office').")});async function Wl(n,e){await e.tagService.setLocation(n.id,{name:n.name??null,latitude:n.latitude,longitude:n.longitude,radiusMeters:n.radiusMeters,trigger:n.trigger});let{tag:t}=await e.tagService.get(n.id);return d({tag:t},e.makeMeta({syncPending:true}))}function Fa(n,e){return n.registerTool("tag_set_location",{description:kr,inputSchema:Vl.shape},async t=>{let r=await Wl(t,e);return p(r)})}var Tr="Set the lifecycle status of a tag to active, on-hold, or dropped. Dropped tags are hidden in OmniFocus but not deleted. Do not use to permanently remove a tag; prefer tag_delete instead. Get the tag ID from tag_list. Returns the updated tag with the confirmed status. Triggers a sync; call sync_trigger after to propagate to other devices.",Xl=z.object({id:y.schema.describe("Persistent tag ID. Get from tag_list."),status:z.enum(["active","on-hold","dropped"]).describe("New lifecycle status for the tag.")});async function Kl(n,e){await e.tagService.setStatus(n.id,n.status);let{tag:t}=await e.tagService.get(n.id);return d({tag:t},e.makeMeta({syncPending:true}))}function Na(n,e){return n.registerTool("tag_set_status",{description:Tr,inputSchema:Xl.shape},async t=>{let r=await Kl(t,e);return p(r)})}var yr="Update mutable fields on an existing tag (partial patch). Only supplied fields are changed; omit a field to leave it unchanged. Do not use to move a tag to a different parent; prefer tag_move instead. Get the tag ID from tag_list. Returns the updated tag on success. Triggers a sync; call sync_trigger after to propagate to other devices.",Yl=z.object({id:y.schema.describe("Persistent tag ID. Get from tag_list."),name:z.string().min(1).optional().describe("New tag name. Must be non-empty if supplied."),parentId:y.schema.nullable().optional().describe("New parent tag ID. Pass null to promote to root. Get from tag_list."),status:z.enum(["active","on-hold","dropped"]).optional().describe("New lifecycle status."),allowsNextAction:z.boolean().optional().describe("Whether the tag allows next-action selection in OmniFocus.")});async function Ql(n,e){let{id:t,...r}=n;await e.tagService.update(t,{...r.name!==void 0?{name:r.name}:{},...r.parentId!==void 0?{parentId:r.parentId}:{},...r.status!==void 0?{status:r.status}:{},...r.allowsNextAction!==void 0?{allowsNextAction:r.allowsNextAction}:{}});let{tag:o}=await e.tagService.get(t);return d({tag:o},e.makeMeta({syncPending:true}))}function Ua(n,e){return n.registerTool("tag_update",{description:yr,inputSchema:Yl.shape},async t=>{let r=await Ql(t,e);return p(r)})}var vr="Mark many OmniFocus tasks complete in a single JXA round trip. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each completion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_complete calls whenever you are completing more than one task. Each item is { id, at? } where `at` is an optional ISO-8601 completion timestamp (defaults to now). Already-completed tasks are not treated specially here \u2014 use task_complete's idempotent noChange path if you need that per-item semantics. Returns { completed: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",Zl=z.object({id:f.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("Optional ISO-8601 completion time; defaults to now.")}),ep=z.object({items:z.array(Zl).min(1).describe("Array of { id, at? } items. Must contain at least one item.")});async function tp(n,e){let t=n.items.map(o=>({id:o.id,...o.at!==void 0&&{at:new Date(o.at)}})),r=await e.adapter.batchCompleteTasks(t);if(e.cache!==void 0)for(let o of r.succeeded){let a=n.items[o.index];a!==void 0&&S(e.cache,{taskId:a.id});}return d({completed:r.succeeded,failed:r.failed},e.makeMeta({syncPending:r.succeeded.length>0}))}function La(n,e){return n.registerTool("task_batch_complete",{description:vr,inputSchema:ep.shape},async t=>{let r=await tp(t,e);return p(r)})}var Sr="Create many OmniFocus tasks in a single JXA round trip. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: once the batch reaches OmniFocus, each task succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_create calls whenever you are creating more than one task. Each item accepts the same shape as task_create (name, optional projectId or parentTaskId, note, flagged, dueDate, deferDate, estimatedMinutes, tagIds, sequential, completedByChildren). Returns { created: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: creates tasks in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the tasks to appear on other devices.",np=z.object({name:z.string().min(1).describe("Task name. Required, non-empty."),projectId:k.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:f.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z.string().optional(),flagged:z.boolean().optional(),dueDate:z.string().datetime({offset:true}).optional(),deferDate:z.string().datetime({offset:true}).optional(),estimatedMinutes:z.number().int().positive().optional(),tagIds:z.array(y.schema).optional(),sequential:z.boolean().optional(),completedByChildren:z.boolean().optional()}).refine(n=>!(n.projectId!==void 0&&n.parentTaskId!==void 0),{message:"Supply at most one of projectId or parentTaskId",path:["projectId"]}),rp=z.object({items:z.array(np).min(1).describe("Array of task inputs. Must contain at least one item.")});async function op(n,e){let t=n.items.map(o=>({name:o.name,...o.projectId!==void 0&&{projectId:o.projectId},...o.parentTaskId!==void 0&&{parentId:o.parentTaskId},...o.note!==void 0&&{note:o.note},...o.flagged!==void 0&&{flagged:o.flagged},...o.dueDate!==void 0&&{dueDate:o.dueDate},...o.deferDate!==void 0&&{deferDate:o.deferDate},...o.estimatedMinutes!==void 0&&{estimatedMinutes:o.estimatedMinutes},...o.tagIds!==void 0&&{tagIds:o.tagIds},...o.sequential!==void 0&&{sequential:o.sequential},...o.completedByChildren!==void 0&&{completedByChildren:o.completedByChildren}})),r=await e.adapter.batchCreateTasks(t);if(e.cache!==void 0&&r.succeeded.length>0){let o=new Set;for(let a of r.succeeded){let i=n.items[a.index]?.projectId;i!==void 0&&!o.has(i)&&(o.add(i),S(e.cache,{projectId:i}));}o.size===0&&S(e.cache,{});}return d({created:r.succeeded,failed:r.failed},e.makeMeta({syncPending:r.succeeded.length>0}))}function Ja(n,e){return n.registerTool("task_batch_create",{description:Sr,inputSchema:rp.shape},async t=>{let r=await op(t,e);return p(r)})}var wr="Permanently delete many OmniFocus tasks in a single JXA round trip. IRREVERSIBLE \u2014 deleted tasks cannot be recovered. REQUIRED: pass confirm=true at the top level to acknowledge this action is irreversible; the entire batch is rejected without it. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each deletion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_delete calls whenever deleting more than one task. Each item is { id }. Returns { deleted: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",ap=z.object({id:f.schema.describe("Persistent task ID.")}),sp=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that all deletions are permanent and irreversible. Must be exactly true. The entire batch is rejected if this field is absent or false."),items:z.array(ap).min(1).describe("Array of { id } items. Must contain at least one item.")});async function ip(n,e){let t=await e.adapter.batchDeleteTasks(n.items.map(r=>({id:r.id})));if(e.cache!==void 0)for(let r of t.succeeded){let o=n.items[r.index];o!==void 0&&S(e.cache,{taskId:o.id});}return d({deleted:t.succeeded,failed:t.failed},e.makeMeta({syncPending:t.succeeded.length>0}))}function Ba(n,e){return n.registerTool("task_batch_delete",{description:wr,inputSchema:sp.shape},async t=>{let r=await ip(t,e);return p(r)})}var br="Cancel (drop) many OmniFocus tasks in a single JXA round trip. Dropped tasks remain in OmniFocus but are treated as cancelled/inactive \u2014 they do not appear in active task lists. Use task_batch_delete for permanent removal. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each drop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_drop calls whenever dropping more than one task. Each item is { id }. Returns { dropped: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",cp=z.object({id:f.schema.describe("Persistent task ID.")}),dp=z.object({items:z.array(cp).min(1).describe("Array of { id } items. Must contain at least one item.")});async function lp(n,e){let t=await e.adapter.batchDropTasks(n.items.map(r=>({id:r.id})));if(e.cache!==void 0)for(let r of t.succeeded){let o=n.items[r.index];o!==void 0&&S(e.cache,{taskId:o.id});}return d({dropped:t.succeeded,failed:t.failed},e.makeMeta({syncPending:t.succeeded.length>0}))}function Ga(n,e){return n.registerTool("task_batch_drop",{description:br,inputSchema:dp.shape},async t=>{let r=await lp(t,e);return p(r)})}var Pr="Move many OmniFocus tasks to new destinations in a single OmniJS round trip. Routes through OmniJS \u2014 not JXA \u2014 because JXA task.move() is unimplemented in OmniFocus 4.x. Each item specifies a task ID and exactly one destination: projectId (move into a project) or parentId (move under a parent task). Omit both to move to the inbox. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each move succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_move calls whenever moving more than one task. Returns { moved: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",pp=z.object({projectId:k.schema.optional().describe("Move into a project as a top-level action. Mutually exclusive with parentId."),parentId:f.schema.optional().describe("Move under a parent task. Mutually exclusive with projectId.")}).refine(n=>!(n.projectId!==void 0&&n.parentId!==void 0),{message:"Provide projectId OR parentId, not both"}),up=z.object({id:f.schema.describe("Persistent task ID."),destination:pp.describe("Where to move the task. Provide projectId, parentId, or neither (inbox).")}),mp=z.object({items:z.array(up).min(1).describe("Array of { id, destination } items. Must contain at least one item.")});async function fp(n,e){let t=await e.adapter.batchMoveTasks(n.items.map(r=>({id:r.id,destination:{...r.destination.projectId!==void 0&&{projectId:r.destination.projectId},...r.destination.parentId!==void 0&&{parentId:r.destination.parentId}}})));if(e.cache!==void 0)for(let r of t.succeeded){let o=n.items[r.index];o!==void 0&&S(e.cache,{taskId:o.id});}return d({moved:t.succeeded,failed:t.failed},e.makeMeta({syncPending:t.succeeded.length>0}))}function Ha(n,e){return n.registerTool("task_batch_move",{description:Pr,inputSchema:mp.shape},async t=>{let r=await fp(t,e);return p(r)})}var _r="Mark many OmniFocus tasks as incomplete in a single JXA round trip. Reverses a previous completion \u2014 useful when a task was completed by mistake or needs to be re-done. Uncompleted tasks return to active status. Use task_batch_complete to mark tasks as completed. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each uncomplete succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_uncomplete calls whenever uncompleting more than one task. Each item is { id }. Returns { uncompleted: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",gp=z.object({id:f.schema.describe("Persistent task ID.")}),hp=z.object({items:z.array(gp).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Ip(n,e){let t=await e.adapter.batchUncompleteTasks(n.items.map(r=>({id:r.id})));if(e.cache!==void 0)for(let r of t.succeeded){let o=n.items[r.index];o!==void 0&&S(e.cache,{taskId:o.id});}return d({uncompleted:t.succeeded,failed:t.failed},e.makeMeta({syncPending:t.succeeded.length>0}))}function $a(n,e){return n.registerTool("task_batch_uncomplete",{description:_r,inputSchema:hp.shape},async t=>{let r=await Ip(t,e);return p(r)})}var Ar="Restore (undrop) many cancelled OmniFocus tasks in a single JXA round trip. Undropped tasks are returned to active status and will reappear in active task lists. Use task_batch_drop to cancel tasks. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each undrop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_undrop calls whenever undropping more than one task. Each item is { id }. Returns { undropped: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",kp=z.object({id:f.schema.describe("Persistent task ID.")}),Tp=z.object({items:z.array(kp).min(1).describe("Array of { id } items. Must contain at least one item.")});async function yp(n,e){let t=await e.adapter.batchUndropTasks(n.items.map(r=>({id:r.id})));if(e.cache!==void 0)for(let r of t.succeeded){let o=n.items[r.index];o!==void 0&&S(e.cache,{taskId:o.id});}return d({undropped:t.succeeded,failed:t.failed},e.makeMeta({syncPending:t.succeeded.length>0}))}function za(n,e){return n.registerTool("task_batch_undrop",{description:Ar,inputSchema:Tp.shape},async t=>{let r=await yp(t,e);return p(r)})}var Dr="Partially update many OmniFocus tasks in a single JXA round trip. Validation is atomic: if any patch fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each update succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_update calls whenever you are updating more than one task. Each item is { id, patch } where patch accepts a subset of task_update's editable fields (name, note, flagged, dueDate, deferDate, estimatedMinutes, tagIds, sequential, completedByChildren). Additive tag diffs (addTags/removeTags) and safety primitives (dry_run, expectedModifiedAt, idempotency_key) are not supported in batch form; fall back to task_update for those. Returns { updated: [{index, value: taskId}], failed: [{index, errorCode, message}] }. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",vp=z.object({name:z.string().min(1).optional(),note:z.string().nullable().optional(),flagged:z.boolean().optional(),dueDate:z.string().datetime({offset:true}).nullable().optional(),deferDate:z.string().datetime({offset:true}).nullable().optional(),estimatedMinutes:z.number().int().positive().nullable().optional(),tagIds:z.array(y.schema).optional(),sequential:z.boolean().optional(),completedByChildren:z.boolean().optional()}).refine(n=>Object.keys(n).length>0,{message:"Patch must contain at least one field"}),Sp=z.object({id:f.schema.describe("Persistent task ID."),patch:vp.describe("Fields to change. At least one field required.")}),wp=z.object({items:z.array(Sp).min(1).describe("Array of { id, patch } pairs. Must contain at least one item.")});async function jp(n,e){let t=n.items.map(o=>({id:o.id,patch:o.patch})),r=await e.adapter.batchUpdateTasks(t);if(e.cache!==void 0)for(let o of r.succeeded){let a=n.items[o.index];a!==void 0&&S(e.cache,{taskId:a.id});}return d({updated:r.succeeded,failed:r.failed},e.makeMeta({syncPending:r.succeeded.length>0}))}function qa(n,e){return n.registerTool("task_batch_update",{description:Dr,inputSchema:wp.shape},async t=>{let r=await jp(t,e);return p(r)})}var Cr="Remove the repetition rule from an OmniFocus task. After clearing, the task becomes a one-time item. Use task_set_repetition to set or change a rule. Returns the updated task with repetitionRule confirmed as null. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters.",Pp=z.object({id:f.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function Op(n,e){await e.adapter.updateTask(n.id,{repetition:null});let t=await e.adapter.getTask(n.id);e.cache!==void 0&&S(e.cache,{taskId:n.id,projectId:t.projectId});let r=e.makeMeta({syncPending:true});return d({task:t},r)}function Va(n,e){return n.registerTool("task_clear_repetition",{description:Cr,inputSchema:Pp.shape},async t=>{let r=await Op(t,e);return p(r)})}var Rr="Complete an OmniFocus task \u2014 marks it done with a completion timestamp. Accepts an optional ISO-8601 date for the completion time; defaults to now. Idempotent: returns noChange: true if the task is already completed. Do not use to drop or delete a task. Returns { done: true, id } or { noChange: true, id }. Side effects: sets completedAt, sets meta.syncPending = true.",_p=z.object({id:f.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("ISO-8601 completion time. Defaults to now.")});async function xp(n,e){let t=await e.adapter.getTask(n.id);if(t.completed)return d({noChange:true,id:n.id},e.makeMeta());let r=n.at!==void 0?new Date(n.at):void 0;return await e.adapter.completeTask(n.id,r),e.cache!==void 0&&S(e.cache,{taskId:n.id,projectId:t.projectId}),d({done:true,id:n.id},e.makeMeta({syncPending:true}))}function Xa(n,e){return n.registerTool("task_complete",{description:Rr,inputSchema:_p.shape},async t=>{let r=await xp(t,e);return p(r)})}var Mr="Create a new task in OmniFocus \u2014 in the inbox, inside a project, or as a subtask of another task. Supply exactly one of: projectId (project task), parentTaskId (subtask), or neither (inbox). Do not use for bulk creation; prefer task_batch_create for that. Safety control: pass idempotency_key to make transport retries safe \u2014 identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate task. Returns the new task's id. Side effects: creates a task in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the task to appear on other devices.",Ka=z.object({name:z.string().min(1).describe("Task name. Required, must be non-empty."),projectId:k.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:f.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z.string().optional().describe("Plain-text note."),flagged:z.boolean().optional().describe("Flag the task."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset."),estimatedMinutes:z.number().int().min(1).optional().describe("Estimated duration in minutes."),tagIds:z.array(y.schema).optional().describe("Tag IDs to apply."),sequential:z.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Complete when all subtasks complete."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe creates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate task.")});Ka.refine(n=>!(n.projectId!==void 0&&n.parentTaskId!==void 0),{message:"Supply at most one of projectId or parentTaskId",path:["projectId"]}).refine(n=>!(n.dueDate!==void 0&&n.deferDate!==void 0&&new Date(n.dueDate)<new Date(n.deferDate)),{message:"dueDate must not be earlier than deferDate",path:["dueDate"]});async function Ap(n,e){let t=e.idempotencyStore??W;return V(t,n.idempotency_key,async()=>{let r={name:n.name,...n.projectId!==void 0&&{projectId:n.projectId},...n.parentTaskId!==void 0&&{parentId:n.parentTaskId},...n.note!==void 0&&{note:n.note},...n.flagged!==void 0&&{flagged:n.flagged},...n.dueDate!==void 0&&{dueDate:n.dueDate},...n.deferDate!==void 0&&{deferDate:n.deferDate},...n.estimatedMinutes!==void 0&&{estimatedMinutes:n.estimatedMinutes},...n.tagIds!==void 0&&{tagIds:n.tagIds},...n.sequential!==void 0&&{sequential:n.sequential},...n.completedByChildren!==void 0&&{completedByChildren:n.completedByChildren}},o=await e.adapter.createTask(r);return e.cache!==void 0&&S(e.cache,{...n.projectId!==void 0&&{projectId:n.projectId}}),d({id:o},e.makeMeta({syncPending:true}))})}function Ya(n,e){return n.registerTool("task_create",{description:Mr,inputSchema:Ka.shape},async t=>{let r=await Ap(t,e);return p(r)})}var Fr="Permanently delete an OmniFocus task. IRREVERSIBLE \u2014 uses OmniFocus deleteObject; there is no undo. Prefer task_drop when you want a recoverable status change. Only use task_delete when the agent has explicit user intent to permanently remove the task. REQUIRED: pass confirm=true to acknowledge this action is irreversible; the call is rejected without it. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent task_get) to reject the call if the task changed since you read it; pass idempotency_key to coalesce retries so the same delete is only performed once. Returns { deleted: true, id } on success. Side effects: removes the task from OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the deletion to appear on other devices.",Dp=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that this deletion is permanent and irreversible. Must be exactly true. The call is rejected if this field is absent or false."),id:f.schema.describe("Persistent ID of the task to delete. Get from task_list or search_query. Verify you have the correct ID before calling \u2014 this action is irreversible."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no delete is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe deletes. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-deleting (or re-raising NotFound on the second attempt).")});async function Cp(n,e){let t=e.idempotencyStore??W;return V(t,n.idempotency_key,async()=>{let r=await e.adapter.getTask(n.id);de(n.expectedModifiedAt,r.modifiedAt,`task:${n.id}`);let o=()=>d({deleted:true,id:n.id},e.makeMeta({syncPending:false})),a=async()=>(await e.adapter.deleteTask(n.id),e.cache!==void 0&&S(e.cache,{taskId:n.id,projectId:r.projectId}),d({deleted:true,id:n.id},e.makeMeta({syncPending:true})));return le(n.dry_run,o,a)})}function Qa(n,e){return n.registerTool("task_delete",{description:Fr,inputSchema:Dp.shape},async t=>{let r=await Cp(t,e);return p(r)})}var Er="Drop an OmniFocus task \u2014 marks it as dropped/deferred and removes it from active view. Reversible via task_undrop. Accepts an optional ISO-8601 date. Idempotent: returns noChange: true if already dropped. Do not use to complete or delete a task. Returns { done: true, id } or { noChange: true, id }. Side effects: sets droppedAt, sets meta.syncPending = true.",Rp=z.object({id:f.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("ISO-8601 drop time. Defaults to now.")});async function Mp(n,e){let t=await e.adapter.getTask(n.id);if(t.dropped)return d({noChange:true,id:n.id},e.makeMeta());let r=n.at!==void 0?new Date(n.at):void 0;return await e.adapter.dropTask(n.id,r),e.cache!==void 0&&S(e.cache,{taskId:n.id,projectId:t.projectId}),d({done:true,id:n.id},e.makeMeta({syncPending:true}))}function es(n,e){return n.registerTool("task_drop",{description:Er,inputSchema:Rp.shape},async t=>{let r=await Mp(t,e);return p(r)})}var Nr="Duplicate an OmniFocus task, optionally including its entire subtask subtree when recursive: true. Editable fields copy over (name, note, defer/due dates, flagged, tags, estimate, repetition); system fields (id, timestamps) regenerate; completed/dropped state is NOT carried \u2014 the duplicate is a fresh, active task. Do NOT use task_duplicate as a substitute for task_move (which reparents the existing task) or task_create (when the new task's fields differ from the source). By default the clone lands alongside the source. Provide destination with exactly one of projectId, parentId, or toInbox: true to place it elsewhere. Returns { duplicated: true, sourceId, newId, descendantCount }. Side effects: creates one new task (plus descendants if recursive) in OmniFocus, sets meta.syncPending = true.",Fp=z.union([z.object({projectId:k.schema}),z.object({parentId:f.schema}),z.object({toInbox:z.literal(true)})]).describe("Where to place the duplicate. Exactly one of projectId, parentId, or toInbox: true. Omit to clone alongside the source."),Ep=z.object({id:f.schema.describe("Persistent ID of the task to duplicate."),recursive:z.boolean().optional().default(false).describe("When true, clone the full subtask subtree depth-first. Default: false (clone only the task itself)."),destination:Fp.optional()}).describe("Duplicate options. `destination` overrides the default same-container placement.");async function Np(n,e){if(n.destination!==void 0){let a=n.destination,s=("projectId"in a?1:0)+("parentId"in a?1:0)+("toInbox"in a&&a.toInbox===true?1:0);if(s!==1)throw new I("task_duplicate: destination must set exactly one of projectId, parentId, or toInbox",{details:{field:"destination",provided:s},suggestion:"Set exactly one destination field or omit destination entirely."})}let t=await e.adapter.getTask(n.id),{newId:r,descendantCount:o}=await e.adapter.duplicateTask(n.id,{recursive:n.recursive,...n.destination!==void 0?{destination:n.destination}:{}});return e.cache!==void 0&&(S(e.cache,{taskId:r,projectId:t.projectId}),n.destination!==void 0&&"projectId"in n.destination&&n.destination.projectId!==t.projectId&&S(e.cache,{projectId:n.destination.projectId})),d({duplicated:true,sourceId:n.id,newId:r,descendantCount:o},e.makeMeta({syncPending:true}))}function ts(n,e){return n.registerTool("task_duplicate",{description:Nr,inputSchema:Ep.shape},async t=>{let r=await Np(t,e);return p(r)})}var Ur="Find tasks in OmniFocus by name. Returns ALL matching tasks (names are not unique in OmniFocus). Names collide in OmniFocus; prefer task_get with an ID when you have one. Use search_query instead when you need to search task notes as well, or want full-text content search. Zero matches returns an empty array \u2014 not an error. Returns tasks[]; safe to call repeatedly; no side effects.",Up=z.object({query:z.string().min(1).describe("Name to search for. Behaviour depends on mode: exact = full name match; prefix = name starts with this string; contains = substring match anywhere in name."),mode:z.enum(["exact","prefix","contains"]).optional().describe("'exact' = full task name must match (default); 'prefix' = name must start with query; 'contains' = query appears anywhere in name."),caseSensitive:z.boolean().optional().describe("true = match is case-sensitive; false = case-insensitive (default false)."),limit:z.number().int().min(1).max(500).optional().describe("Maximum number of results to return (1..500). Default 50.")});async function Lp(n,e){let t=n.mode??"exact",r=n.caseSensitive??false,o=n.limit??50,a=await e.adapter.listTasks({}),s=h=>r?h:h.toLowerCase(),i=s(n.query),l=a.filter(h=>{let m=s(h.name);switch(t){case "exact":return m===i;case "prefix":return m.startsWith(i);case "contains":return m.includes(i);default:return false}}),u=l.slice(0,o),c=e.makeMeta();return d({tasks:u,matchCount:l.length},c)}function ns(n,e){return n.registerTool("task_find_by_name",{description:Ur,inputSchema:Up.shape},async t=>{let r=await Lp(t,e);return p(r)})}var Lr="Fetch a single OmniFocus task by persistent ID. Use when you have a known task ID and need its full detail. Do NOT use for multiple IDs \u2014 use task_get_many instead. Returns the Task object plus its direct subtasks (when includeSubtasks=true, the default). Read-only; safe to retry.",Jp=z.object({id:f.schema.describe("Persistent ID of the task to fetch. Get from task_list or task_get_many."),includeSubtasks:z.boolean().optional().describe("Include direct subtasks in the response. Default true.")});async function Bp(n,e){let t=await e.taskService.get(n);return d({task:t.task,...t.subtasks!==void 0&&{subtasks:t.subtasks}},e.makeMeta({cacheHit:t.cacheHit}))}function os(n,e){return n.registerTool("task_get",{description:Lr,inputSchema:Jp.shape},async t=>{let r=await Bp(t,e);return p(r)})}var Jr="Fetch up to 100 tasks by persistent ID in a single OmniFocus round-trip. Use when you have a set of task IDs from multiple sources and need full task objects for all of them. Do NOT use for a single ID \u2014 use task_get instead. Do NOT use when you only have names \u2014 use task_find_by_name. Returns Task[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry.",xt=100,Gp=z.object({ids:z.array(f.schema).min(0).max(xt).describe(`Array of task IDs to fetch (0..${xt}). Get IDs from task_list, search_query, or task_find_by_name. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function Hp(n,e){if(n.ids.length===0)return d({tasks:[]},e.makeMeta());if(n.ids.length>xt)throw new I(`ids array exceeds the maximum batch size of ${xt} (got ${n.ids.length})`,{details:{field:"ids"}});let t=await e.adapter.getTasksMany(n.ids),r=t.filter(i=>i!==null),o=n.ids.filter((i,l)=>t[l]===null),a=o.length>0?[_e(o)]:void 0,s=e.makeMeta({...a!==void 0?{warnings:a}:{}});return d({tasks:r},s)}function ss(n,e){return n.registerTool("task_get_many",{description:Jr,inputSchema:Gp.shape},async t=>{let r=await Hp(t,e);return p(r)})}var is=z.object({self:z.string(),project:z.string().nullable(),parent:z.string().nullable(),tags:z.array(z.string())});z.object({self:z.string(),folder:z.string().nullable()});function ue(n){return {self:`omnifocus://task/${n.id}`,project:n.projectId!==null?`omnifocus://project/${n.projectId}`:null,parent:n.parentId!==null?`omnifocus://task/${n.parentId}`:null,tags:n.tagIds.map(e=>`omnifocus://tag/${e}`)}}function Br(n){return {self:`omnifocus://project/${n.id}`,folder:n.folderId!==null?`omnifocus://folder/${n.folderId}`:null}}function Re(n){let e=JSON.stringify(Object.fromEntries(Object.entries(n).filter(([,t])=>t!==void 0).sort(([t],[r])=>t.localeCompare(r))));return createHash("sha256").update(e).digest("hex")}function Me(n){let e=JSON.stringify(n);return Buffer.from(e,"utf8").toString("base64url")}function Fe(n,e){let t;try{t=Buffer.from(n,"base64url").toString("utf8");}catch{throw new I("Cursor is not valid base64url.",{suggestion:"Pass the cursor value exactly as returned by the previous response."})}let r;try{r=JSON.parse(t);}catch{throw new I("Cursor payload is not valid JSON.",{suggestion:"Pass the cursor value exactly as returned by the previous response."})}let o=r;if(typeof r!="object"||r===null||typeof o.lastId!="string"||o.lastSortValue!==null&&typeof o.lastSortValue!="string"||typeof o.filterHash!="string")throw new I("Cursor payload is missing required fields.",{suggestion:"Pass the cursor value exactly as returned by the previous response."});let a=r;if(a.filterHash!==e)throw new I("Cursor filter hash does not match the current query filters. Start a fresh query.",{suggestion:"Call the list tool without a cursor to begin a new page sequence with the updated filters.",details:{cursorFilterHash:a.filterHash,currentFilterHash:e}});return a}function Ee(n,e,t="asc"){let r=n.sortValue,o=e.lastSortValue;return r===null&&o===null?n.id>e.lastId:r===null?t==="asc":o===null?t==="desc":r!==o?t==="asc"?r>o:r<o:n.id>e.lastId}var ds=z.enum(["dueDate","createdAt","modifiedAt","name"]),cs=200,Gr=1e3,At=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async list(e){let t=this.resolveLimit(e);this.assertBounded(e,t);let r=this.normalize(e),o=Re(r),a=e.cursor!==void 0?Fe(e.cursor,o):void 0,s=this.cacheKeyFor(o,e.cursor),i=this.cache.has(s),{tasks:l,nextCursor:u}=await this.cache.wrap(s,async()=>this.fetchPage(e,r,a,t,o));return {tasks:l,nextCursor:u,hasMore:u!==null,cacheHit:i}}async get(e){let t=e.includeSubtasks??true,r=`task:${e.id}:${t?"with-subtasks":"solo"}`,o=this.cache.has(r);return {...await this.cache.wrap(r,async()=>{let s=await this.adapter.getTask(e.id),i={...s,_links:ue(s)};if(!t)return {task:i};let u=(await this.adapter.listTasks({parentId:e.id})).map(c=>({...c,_links:ue(c)}));return {task:i,subtasks:u}}),cacheHit:o}}async fetchPage(e,t,r,o,a){let s=this.toAdapterFilter(e,t),i=await this.adapter.listTasks(s),l=t.tagIds.length>1?i.filter(_=>t.tagIds.every(x=>_.tagIds.includes(x))):i,{updatedSince:u}=t,c=u!==void 0?l.filter(_=>_.modifiedAt>u):l,{sortBy:h,sortDirection:m}=t,g=_=>{switch(h){case "dueDate":return _.dueDate??null;case "modifiedAt":return _.modifiedAt;case "name":return _.name;default:return _.createdAt}},b=[...c].sort((_,x)=>{let U=g(_),$=g(x);if(U===null&&$===null)return _.id<x.id?-1:1;if(U===null)return 1;if($===null)return -1;if(U!==$){let fe=U<$?-1:1;return m==="asc"?fe:-fe}return _.id<x.id?-1:1}),w=r!==void 0?b.filter(_=>Ee({id:_.id,sortValue:g(_)},r,m)):b,O=w.slice(0,o),R=w.length>o?this.encodeNextCursor(O,a,g):null;return {tasks:O.map(_=>({..._,_links:ue(_)})),nextCursor:R}}resolveLimit(e){if(e.limit===void 0)return cs;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>Gr)throw new I(`limit must be an integer between 1 and ${Gr}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${Gr}, or omit to use the default of ${cs}.`,details:{field:"limit",value:e.limit}});return e.limit}assertBounded(e,t){if(!(e.limit!==void 0||e.cursor!==void 0)&&!this.hasAnyFilter(e))throw new I("task_list requires at least one filter, limit, or cursor. Unbounded queries are rejected.",{suggestion:"Provide a filter or a limit.",details:{field:"filter|limit|cursor"}})}hasAnyFilter(e){return e.projectId!==void 0||e.tagIds!==void 0&&e.tagIds.length>0||e.flagged!==void 0||e.available!==void 0||e.completed!==void 0||e.dueBefore!==void 0||e.dueAfter!==void 0||e.deferredBefore!==void 0||e.parentId!==void 0||e.updatedSince!==void 0||e.inbox===true}normalize(e){let t=e.tagIds!==void 0?[...new Set(e.tagIds)].sort((o,a)=>o.localeCompare(a)):[],r;if(e.updatedSince!==void 0)if(Ze(e.updatedSince))r=e.updatedSince;else if(Ye(e.updatedSince))r=Qe(e.updatedSince);else throw new I(`updatedSince must be an ISO-8601 timestamp with offset or a relative shortcut (today, yesterday, this-week, next-week, end-of-week, end-of-month). Got: "${e.updatedSince}".`,{details:{field:"updatedSince",value:e.updatedSince},suggestion:"Pass an ISO-8601 string with offset (e.g. '2026-04-21T10:00:00-07:00') or a shortcut like 'today'."});if(e.inbox&&(e.projectId!==void 0||e.parentId!==void 0))throw new I("inbox filter cannot be combined with projectId or parentId \u2014 inbox tasks have no project assignment.",{suggestion:"Remove projectId/parentId when filtering by inbox.",details:{field:"inbox"}});return {projectId:e.projectId,tagIds:t,flagged:e.flagged,available:e.available,completed:e.completed,dueBefore:e.dueBefore,dueAfter:e.dueAfter,deferredBefore:e.deferredBefore,parentId:e.parentId,sortBy:e.sortBy??"createdAt",sortDirection:e.sortDirection??"asc",updatedSince:r,inbox:e.inbox}}toAdapterFilter(e,t){let r={};if(t.projectId!==void 0&&(r.projectId=t.projectId),t.parentId!==void 0&&(r.parentId=t.parentId),t.tagIds.length===1){let o=t.tagIds[0];o!==void 0&&(r.tagId=o);}return t.flagged!==void 0&&(r.flagged=t.flagged),t.available!==void 0&&(r.available=t.available),t.dueBefore!==void 0&&(r.dueBefore=t.dueBefore),t.dueAfter!==void 0&&(r.dueAfter=t.dueAfter),t.deferredBefore!==void 0&&(r.deferredBefore=t.deferredBefore),t.completed==="only"?r.completed=true:t.completed==="exclude"&&(r.completed=false),t.inbox===true&&(r.inbox=true),r}cacheKeyFor(e,t){return `search:tasks:${e}:${t??"first"}`}encodeNextCursor(e,t,r){let o=e[e.length-1];if(o===void 0)throw new I("Internal: cannot encode cursor for empty page.");return Me({lastId:o.id,lastSortValue:r(o),filterHash:t})}};var Hr="List tasks in OmniFocus with optional filters (project, tag, inbox, flagged, completion, due dates). Use inbox=true to fetch unprocessed Inbox tasks. Use this for filter-based queries across tasks. Do NOT use for a known single task (use task_get). For name-based lookup, prefer task_find_by_name. For full-text content search across names and notes, prefer search_query. Returns tasks[] with pagination; safe to call repeatedly; no side effects.",qp=z.object({projectId:k.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list. Omit for all projects."),tagIds:z.array(y.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs. Get IDs from tag_list."),flagged:z.boolean().optional().describe("true = flagged only; false = unflagged only; omit = all."),available:z.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred). Omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed tasks only; 'any' = both. Omit for adapter default."),dueBefore:z.string().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset (e.g. '2026-04-21T17:00:00-04:00')."),dueAfter:z.string().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset."),deferredBefore:z.string().optional().describe("Tasks deferred until before this moment (already unlocked or soon). ISO-8601 with offset."),parentId:f.schema.optional().describe("Restrict to direct children of this task (subtasks). Get the ID from task_get or task_list."),limit:z.number().int().min(1).max(1e3).optional().describe("Max tasks per page (1..1000). Default 200. Use `cursor` to fetch subsequent pages."),sortBy:ds.optional().describe("Field to sort tasks by: 'createdAt' (default), 'dueDate', 'modifiedAt', or 'name'. Tasks with no value for the chosen field (e.g. no dueDate) sort last."),sortDirection:z.enum(["asc","desc"]).optional().describe("Sort direction: 'asc' (default, oldest/lowest first) or 'desc' (newest/highest first)."),updatedSince:ae().optional().describe("Return only tasks modified strictly after this timestamp. Accepts ISO-8601 with offset (e.g. '2026-04-21T10:00:00-07:00') or a relative shortcut: today, yesterday, this-week, next-week, end-of-week, end-of-month. Use this for incremental sync: call without updatedSince on session start, then pass the previous response timestamp on subsequent calls. Note: deleted tasks cannot be detected \u2014 use a snapshot resource for deletion detection."),inbox:z.boolean().optional().describe("true = Inbox tasks only (no project assignment). Cannot be combined with projectId or parentId. Use this to surface unprocessed captures without knowing their IDs."),cursor:z.string().optional().describe("Opaque cursor from a previous task_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")});async function Vp(n,e){let t=n,r=await e.taskService.list(t),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return d({tasks:r.tasks},a,o)}function ls(n,e){return n.registerTool("task_list",{description:Hr,inputSchema:qp.shape},async t=>{let r=await Vp(t,e);return p(r)})}var zr="Move an OmniFocus task to a new location \u2014 a different project, another task (as a subtask), or the inbox. Exactly one destination must be specified: projectId, parentId, or toInbox: true. Do NOT use task_move to reorder siblings within the same parent (task_reorder handles that); prefer task_update when you only need to change editable fields, not reparent. Idempotent: returns noChange: true when the task is already at the destination. Returns { moved: true, id, from, to } or { noChange: true, id, at }. Side effects: reparents the task in OmniFocus, sets meta.syncPending = true.",Wp=z.object({id:f.schema.describe("Persistent ID of the task to move."),projectId:k.schema.optional().describe("Move into this project. Mutually exclusive with parentId and toInbox."),parentId:f.schema.optional().describe("Move under this parent task (as a subtask). Mutually exclusive with projectId and toInbox."),toInbox:z.literal(true).optional().describe("Set to true to move the task to the inbox (clear any project or parent). Mutually exclusive with projectId and parentId.")}).describe("Exactly one of projectId, parentId, or toInbox must be set. Any other combination returns a ValidationError.");async function Xp(n,e){let t=(n.projectId!==void 0?1:0)+(n.parentId!==void 0?1:0)+(n.toInbox===true?1:0);if(t!==1)throw new I("task_move requires exactly one of projectId, parentId, or toInbox",{details:{field:"projectId|parentId|toInbox",provided:t},suggestion:"Set exactly one destination field \u2014 see tool schema."});let r=await e.adapter.getTask(n.id),o=n.projectId!==void 0&&r.projectId===n.projectId&&r.parentId===null,a=n.parentId!==void 0&&r.parentId===n.parentId&&r.projectId===null,s=n.toInbox===true&&r.projectId===null&&r.parentId===null;if(o||a||s)return d({noChange:true,id:n.id,at:$r(r.projectId,r.parentId)},e.makeMeta());let i=$r(r.projectId,r.parentId),l=n.projectId!==void 0?{projectId:n.projectId}:n.parentId!==void 0?{parentId:n.parentId}:{};await e.adapter.moveTask(n.id,l),e.cache!==void 0&&(S(e.cache,{taskId:n.id,projectId:r.projectId}),n.projectId!==void 0&&n.projectId!==r.projectId&&S(e.cache,{projectId:n.projectId}));let u=n.toInbox===true?{inbox:true}:$r(n.projectId??null,n.parentId??null);return d({moved:true,id:n.id,from:i,to:u},e.makeMeta({syncPending:true}))}function $r(n,e){return e!==null?{parentId:e}:n!==null?{projectId:n}:{inbox:true}}function us(n,e){return n.registerTool("task_move",{description:zr,inputSchema:Wp.shape},async t=>{let r=await Xp(t,e);return p(r)})}function qr(n){let e=(g,b=2)=>String(g).padStart(b,"0"),t=n.getFullYear(),r=e(n.getMonth()+1),o=e(n.getDate()),a=e(n.getHours()),s=e(n.getMinutes()),i=e(n.getSeconds()),l=-n.getTimezoneOffset(),u=l>=0?"+":"-",c=Math.abs(l),h=e(Math.floor(c/60)),m=e(c%60);return `${t}-${r}-${o}T${a}:${s}:${i}${u}${h}:${m}`}function ms(n,e,t){let r=n.toLowerCase(),o=new Date;if(r==="today"){let s=new Date(o.getFullYear(),o.getMonth(),o.getDate(),0,0,0);return {value:qr(s)}}if(r==="tomorrow"){let s=new Date(o.getFullYear(),o.getMonth(),o.getDate()+1,0,0,0);return {value:qr(s)}}if(/^\d{4}-\d{2}-\d{2}(T.*)?$/.test(n)){let s=new Date(n);if(!Number.isNaN(s.getTime())){if(/^\d{4}-\d{2}-\d{2}$/.test(n)){let i=n.split("-"),l=Number(i[0]),u=Number(i[1]),c=Number(i[2]),h=new Date(l,u-1,c,0,0,0);return {value:qr(h)}}return {value:n}}}return {value:n,warning:`Line ${e}: ${t} date '${n}' is not a recognized date format; passing through as-is`}}function fs(n){let e=n.split(`
|
|
70
|
-
`),t=[],r=[],o;for(let a=0;a<e.length;a++){let s=a+1,i=(e[a]??"").trim();if(i==="")continue;if(/^Project:\s*/i.test(i)){o=i.replace(/^Project:\s*/i,"").trim()||void 0;continue}let l=i,u=[],c,h,m=false,g,b=l.indexOf("//");b!==-1&&(g=l.slice(b+2).trim(),l=l.slice(0,b).trim());let w=l.split(/\s+/).filter(N=>N.length>0),O=[];for(let N of w)if(N==="!!")m=true;else if(N.startsWith("::")){let _=N.slice(2);if(_.length>0){let{value:x,warning:U}=ms(_,s,"Defer");h=x,U&&r.push(U);}}else if(N.startsWith("@")){let _=N.slice(1);_.length>0&&u.push(_);}else if(N.startsWith("#")){let _=N.slice(1);if(_.length>0){let{value:x,warning:U}=ms(_,s,"Due");c=x,U&&r.push(U);}}else O.push(N);let J=O.join(" ").trim();if(J===""){r.push(`Line ${s}: task line has no name after removing tokens; skipping`);continue}let R={name:J};g!==void 0&&g.length>0&&(R.note=g),m&&(R.flagged=true),c!==void 0&&(R.dueDate=c),h!==void 0&&(R.deferDate=h),u.length>0&&(R.tagNames=u),o!==void 0&&(R.projectName=o),t.push(R);}return {tasks:t,warnings:r}}var Vr="Parse OmniFocus transport text DSL into structured task objects \u2014 no tasks are created. Supports @tag, #due-date, ::defer-date, !!, and //note tokens; a leading 'Project: Name' line sets the project context for subsequent tasks. Do not use this tool to create tasks; pass the returned tasks[] to task_create separately. Returns tasks[] with name, tagNames, dueDate, deferDate, flagged, note, and projectName fields, plus count and an optional warnings[] for unparseable dates. Tag names and project names are raw strings \u2014 resolve to IDs with tag_list before passing to task_create. Read-only; no side effects.",Kp=z.object({text:z.string().min(1).describe("Transport text to parse. One task per line; 'Project: Name' prefix sets project context.")});async function Yp(n,e){let t=fs(n.text),r=e.makeMeta();return d({tasks:t.tasks,count:t.tasks.length,...t.warnings.length>0&&{warnings:t.warnings}},r)}function hs(n,e){return n.registerTool("task_parse_transport_text",{description:Vr,inputSchema:Kp.shape},async t=>{let r=await Yp(t,e);return p(r)})}var Wr="Reorder an OmniFocus task among its siblings. OmniFocus has no numeric sibling index \u2014 position is always expressed relative to another task (before / after) or as the absolute start / end of a container. Do NOT use task_reorder to reparent a task to a different project or parent (task_move handles reparenting); prefer task_move when the task needs to change containers without caring about sibling order. Exactly one positioning form must be set: { before }, { after }, or { at, in }. Returns { reordered: true, id, position }. Side effects: changes sibling order in OmniFocus, sets meta.syncPending = true.",Qp=z.union([z.object({projectId:k.schema}),z.object({parentId:f.schema}),z.object({inbox:z.literal(true)})]).describe("Container for start/end positioning. Exactly one of projectId, parentId, or inbox: true."),Zp=z.object({id:f.schema.describe("Persistent ID of the task to reorder."),before:f.schema.optional().describe("Position the task immediately before this sibling. Reference must share the same parent."),after:f.schema.optional().describe("Position the task immediately after this sibling. Reference must share the same parent."),at:z.enum(["start","end"]).optional().describe("Absolute position within a container. Requires `in` to identify the container."),in:Qp.optional().describe("Required when `at` is set; ignored otherwise.")}).describe("Exactly one positioning form: { before }, { after }, or { at, in }. Any other combination returns a ValidationError.");async function eu(n,e){let t=(n.before!==void 0?1:0)+(n.after!==void 0?1:0)+(n.at!==void 0?1:0);if(t!==1)throw new I("task_reorder requires exactly one positioning form: { before }, { after }, or { at, in }",{details:{field:"before|after|at",provided:t},suggestion:"Set exactly one positioning field."});if(n.at!==void 0&&n.in===void 0)throw new I("task_reorder: `in` is required when `at` is set",{details:{field:"in"},suggestion:"Provide `in: { projectId } | { parentId } | { inbox: true }`."});if(n.at===void 0&&n.in!==void 0)throw new I("task_reorder: `in` is only valid alongside `at`",{details:{field:"in"},suggestion:"Either add `at: 'start' | 'end'` or remove `in`."});let o=(await e.adapter.getTask(n.id)).projectId,a;if(n.before!==void 0)a={before:n.before};else if(n.after!==void 0)a={after:n.after};else if(n.at!==void 0&&n.in!==void 0)a={at:n.at,in:n.in};else throw new I("task_reorder: no positioning form matched",{details:{field:"before|after|at"}});return await e.adapter.reorderTask(n.id,a),e.cache!==void 0&&(S(e.cache,{taskId:n.id,projectId:o}),n.at!==void 0&&n.in!==void 0&&"projectId"in n.in&&n.in.projectId!==o&&S(e.cache,{projectId:n.in.projectId})),d({reordered:true,id:n.id,position:a},e.makeMeta({syncPending:true}))}function Is(n,e){return n.registerTool("task_reorder",{description:Wr,inputSchema:Zp.shape},async t=>{let r=await eu(t,e);return p(r)})}var Xr="Search OmniFocus tasks by keyword and/or structured filters, with cursor pagination. q is optional \u2014 omit it to filter by tag, project, date range, or availability alone. When q is supplied, scans task names and/or notes (controlled by scope) for a case-insensitive substring match. Narrow results with: projectId, tagIds (task must carry ALL listed tags), available, dueBefore, dueAfter, flagged, and completed. At least one of q, projectId, tagIds, available, dueBefore, or dueAfter must be provided. Do NOT use when you already have an ID \u2014 prefer task_get instead. Returns tasks[] with pagination (limit defaults to 100, max 500); safe to call repeatedly; no side effects.",ks={q:z.string().min(1).optional().describe("Search query. Case-insensitive substring match applied to the fields in scope. Optional \u2014 omit to filter by tags, project, date range, or availability alone."),scope:z.enum(["name","note","all"]).optional().describe("'name' = search task name only; 'note' = search note only; 'all' = both (default). Ignored when q is omitted."),projectId:k.schema.optional().describe("Restrict search to tasks within this project."),tagIds:z.array(y.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs."),available:z.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred, not completed). Omit = all."),dueBefore:ae().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset or relative shortcut."),dueAfter:ae().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset or relative shortcut."),flagged:z.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only (default); 'only' = completed tasks only; 'any' = both."),limit:z.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 100. Use cursor to fetch subsequent pages."),cursor:z.string().optional().describe("Opaque cursor from a previous task_search response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")};z.object(ks).refine(n=>n.q!==void 0||n.projectId!==void 0||n.tagIds!==void 0||n.available!==void 0||n.dueBefore!==void 0||n.dueAfter!==void 0,{message:"At least one of q, projectId, tagIds, available, dueBefore, or dueAfter must be provided."});async function tu(n,e){let t=await e.searchService.search({...n.q!==void 0&&{q:n.q},...n.scope!==void 0&&{scope:n.scope},...n.projectId!==void 0&&{projectId:n.projectId},...n.tagIds!==void 0&&{tagIds:n.tagIds},...n.available!==void 0&&{available:n.available},...n.dueBefore!==void 0&&{dueBefore:n.dueBefore},...n.dueAfter!==void 0&&{dueAfter:n.dueAfter},...n.flagged!==void 0&&{flagged:n.flagged},...n.completed!==void 0&&{completed:n.completed},...n.limit!==void 0&&{limit:n.limit},...n.cursor!==void 0&&{cursor:n.cursor}}),r={cursor:t.nextCursor,hasMore:t.hasMore};return d({tasks:t.tasks},e.makeMeta({cacheHit:t.cacheHit}),r)}function Ts(n,e){return n.registerTool("task_search",{description:Xr,inputSchema:ks},async t=>{let r=await tu(t,e);return p(r)})}var ys=z.enum(["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]),nu=z.union([z.object({day:z.number().int().min(1).max(31)}),z.object({weekday:ys,position:z.union([z.literal(1),z.literal(2),z.literal(3),z.literal(4),z.literal("last")])})]),Kr=z.object({method:z.enum(["fixed","start-again","due-again"]),unit:z.enum(["minutes","hours","days","weeks","months","years"]),steps:z.number().int().min(1),weekdays:z.array(ys).optional(),monthlyAnchor:nu.optional()}).refine(n=>n.weekdays===void 0||n.unit==="weeks",{message:"weekdays is only valid when unit is 'weeks'",path:["weekdays"]}).refine(n=>n.monthlyAnchor===void 0||n.unit==="months",{message:"monthlyAnchor is only valid when unit is 'months'",path:["monthlyAnchor"]}).refine(n=>!(n.weekdays!==void 0&&n.monthlyAnchor!==void 0),{message:"Only one of weekdays or monthlyAnchor may be set"});z.object({id:f.schema,name:z.string(),note:z.string().nullable(),noteHtml:z.string().nullable(),projectId:k.schema.nullable(),parentId:f.schema.nullable(),tagIds:z.array(y.schema),deferDate:Ie().nullable(),dueDate:Ie().nullable(),estimatedMinutes:z.number().int().min(1).nullable(),flagged:z.boolean(),completed:z.boolean(),completedAt:Ie().nullable(),dropped:z.boolean(),droppedAt:Ie().nullable(),available:z.boolean(),blocked:z.boolean(),sequential:z.boolean(),completedByChildren:z.boolean(),repetition:Kr.nullable(),createdAt:Ie(),modifiedAt:Ie(),_links:is.optional()});var Yr="Set the repetition rule on an OmniFocus task. Overwrites any existing rule. Use task_clear_repetition to remove a rule entirely. Returns the updated task ID; call task_get for the full object. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters.",ou=z.object({id:f.schema.describe("ID of the task to update. Get from task_list or search_query."),rule:Kr.describe("Repetition rule to apply. 'method': 'fixed' repeats from the due date, 'start-again' from completion, 'due-again' from due date (alias). 'unit': time unit for the interval. 'steps': how many units between occurrences (minimum 1). 'weekdays': optional array of day names \u2014 only valid when unit is 'weeks'. 'monthlyAnchor': optional day-of-month or weekday-position \u2014 only valid when unit is 'months'.")});async function au(n,e){await e.adapter.updateTask(n.id,{repetition:n.rule});let t=await e.adapter.getTask(n.id);e.cache!==void 0&&S(e.cache,{taskId:n.id,projectId:t.projectId});let r=e.makeMeta({syncPending:true});return d({task:t},r)}function vs(n,e){return n.registerTool("task_set_repetition",{description:Yr,inputSchema:ou.shape},async t=>{let r=await au(t,e);return p(r)})}var Qr="Mark an OmniFocus task as incomplete \u2014 removes its completion timestamp. Idempotent: returns noChange: true if the task is already incomplete. Do not use to drop or delete a task. Returns { done: true, id } or { noChange: true, id }. Side effects: clears completedAt, sets meta.syncPending = true.",iu=z.object({id:f.schema.describe("Persistent task ID.")});async function cu(n,e){let t=await e.adapter.getTask(n.id);return t.completed===false?d({noChange:true,id:n.id},e.makeMeta()):(await e.adapter.uncompleteTask(n.id),e.cache!==void 0&&S(e.cache,{taskId:n.id,projectId:t.projectId}),d({done:true,id:n.id},e.makeMeta({syncPending:true})))}function Ss(n,e){return n.registerTool("task_uncomplete",{description:Qr,inputSchema:iu.shape},async t=>{let r=await cu(t,e);return p(r)})}var Zr="Restore a dropped OmniFocus task \u2014 clears its dropped status and returns it to the active view. Idempotent: returns noChange: true if the task is not dropped. Do not use to complete a task. Returns { done: true, id } or { noChange: true, id }. Side effects: clears droppedAt, sets meta.syncPending = true.",lu=z.object({id:f.schema.describe("Persistent task ID.")});async function pu(n,e){let t=await e.adapter.getTask(n.id);return t.dropped===false?d({noChange:true,id:n.id},e.makeMeta()):(await e.adapter.undropTask(n.id),e.cache!==void 0&&S(e.cache,{taskId:n.id,projectId:t.projectId}),d({done:true,id:n.id},e.makeMeta({syncPending:true})))}function ws(n,e){return n.registerTool("task_undrop",{description:Zr,inputSchema:lu.shape},async t=>{let r=await pu(t,e);return p(r)})}var eo="Partially update mutable fields on an OmniFocus task. Only supplied fields are changed; omit a field to leave it unchanged. Do not use to complete or delete a task; prefer task_complete or task_delete instead. Two tag-update modes: (1) supply tagIds to replace the full tag set; (2) supply addTags and/or removeTags to apply a diff without reading first. Supplying tagIds together with addTags/removeTags is a ValidationError. setFlagged is a convenience alias for flagged. Safety controls: set dry_run=true to preview the patched task without mutating; pass expectedModifiedAt (from a recent task_get) to reject the call if the task changed since you read it; pass idempotency_key to coalesce retries so the same update is only performed once. Returns the updated task. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices.",js=z.object({id:f.schema.describe("Persistent task ID. Get from task_list or search_query."),name:z.string().min(1).optional().describe("New task name. Must be non-empty if supplied."),note:z.string().nullable().optional().describe("Plain-text note. Pass null to clear. HTML round-trip available in M3."),flagged:z.boolean().optional().describe("Flag or unflag the task. Alias: setFlagged."),setFlagged:z.boolean().optional().describe("Convenience alias for flagged. Use when your intent is specifically to set or clear the flag without touching other fields."),deferDate:z.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),dueDate:z.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated duration in minutes. Pass null to clear."),sequential:z.boolean().optional().describe("Whether subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Whether the task completes when all children are complete."),tagIds:z.array(y.schema).optional().describe("Full-replacement tag list. Replaces all existing tags. Mutually exclusive with addTags/removeTags."),addTags:z.array(y.schema).optional().describe("Tags to add. No-op for tags the task already has. Mutually exclusive with tagIds."),removeTags:z.array(y.schema).optional().describe("Tags to remove. No-op for tags the task doesn't have. Mutually exclusive with tagIds."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input, computes the patched task (pre-fetch merged with the supplied fields), and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe updates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying the patch.")});js.refine(n=>!(n.tagIds!==void 0&&(n.addTags!==void 0||n.removeTags!==void 0)),{message:"tagIds cannot be combined with addTags/removeTags. Use tagIds for full replacement, or addTags/removeTags for additive diff.",path:["tagIds"]}).refine(n=>!(n.dueDate!=null&&n.deferDate!=null&&new Date(n.dueDate)<new Date(n.deferDate)),{message:"dueDate must not be earlier than deferDate",path:["dueDate"]});async function uu(n,e){let{id:t,addTags:r,removeTags:o,setFlagged:a,tagIds:s,...i}=n,l=e.idempotencyStore??W;return V(l,n.idempotency_key,async()=>{let u=await e.adapter.getTask(t);de(n.expectedModifiedAt,u.modifiedAt,`task:${t}`);let c;if(r!==void 0||o!==void 0){let w=new Set(u.tagIds);for(let O of r??[])w.add(O);for(let O of o??[])w.delete(O);c=[...w];}else s!==void 0&&(c=s);let h=a!==void 0?a:i.flagged,m={...i.name!==void 0?{name:i.name}:{},...i.note!==void 0?{note:i.note}:{},...h!==void 0?{flagged:h}:{},...i.deferDate!==void 0?{deferDate:i.deferDate}:{},...i.dueDate!==void 0?{dueDate:i.dueDate}:{},...i.estimatedMinutes!==void 0?{estimatedMinutes:i.estimatedMinutes}:{},...i.sequential!==void 0?{sequential:i.sequential}:{},...i.completedByChildren!==void 0?{completedByChildren:i.completedByChildren}:{},...c!==void 0?{tagIds:c}:{}},g=()=>{let w={...u,...m};return d({task:w},e.makeMeta({syncPending:false}))},b=async()=>{await e.adapter.updateTask(t,m);let w=await e.adapter.getTask(t);return e.cache!==void 0&&S(e.cache,{taskId:t,projectId:w.projectId}),d({task:w},e.makeMeta({syncPending:true}))};return le(n.dry_run,g,b)})}function bs(n,e){return n.registerTool("task_update",{description:eo,inputSchema:js.shape},async t=>{let r=await uu(t,e);return p(r)})}var Ps={app_launch:sn,attachment_add:ln,attachment_list:dn,attachment_remove:pn,attachment_save_to_path:un,export_opml:fn,export_taskpaper:In,forecast_get:bn,folder_create:Tn,folder_delete:yn,folder_get:vn,folder_list:Sn,folder_move:wn,folder_update:jn,import_opml:hn,import_taskpaper:kn,internal_status:Rn,note_append:Pn,note_get:_n,note_get_html:An,note_set:Dn,note_set_html:Cn,perspective_evaluate:Mn,perspective_list:Fn,plugin_invoke:Nn,project_batch_complete:Ln,project_batch_drop:Bn,project_complete:Gn,project_create:$n,project_delete:zn,project_drop:qn,project_get:Vn,project_get_many:Wn,project_list:Xn,project_mark_reviewed:or,project_move:Kn,project_update:Yn,review_list_due:nr,review_mark_reviewed:rr,review_set_interval:sr,run_jxa_script:Zn,run_omnijs_script:tr,search_query:ir,sync_status:cr,sync_trigger:dr,tag_create:lr,tag_delete:pr,tag_get:ur,tag_get_many:fr,tag_get_location:mr,tag_list:gr,tag_move:hr,tag_set_allows_next_action:Ir,tag_set_location:kr,tag_set_status:Tr,tag_update:yr,task_batch_complete:vr,task_batch_create:Sr,task_batch_delete:wr,task_batch_drop:br,task_batch_move:Pr,task_batch_uncomplete:_r,task_batch_undrop:Ar,task_batch_update:Dr,task_complete:Rr,task_create:Mr,task_drop:Er,task_clear_repetition:Cr,task_delete:Fr,task_duplicate:Nr,task_find_by_name:Ur,task_get:Lr,task_get_many:Jr,task_list:Hr,task_move:zr,task_search:Xr,task_parse_transport_text:Vr,task_reorder:Wr,task_set_repetition:Yr,task_uncomplete:Qr,task_undrop:Zr,task_update:eo};var Iu=to.join(fu.homedir(),"Library","Application Support","OmniFocus","OmniFocus.ofocus");function ku(){let n=fileURLToPath(import.meta.url);return to.resolve(to.dirname(n),"../../bin/omnifocus-watcher")}var Dt=class{dbPath;debounceMs;onChange;binaryPath;started=false;debounceTimer=null;windowStartTs=null;windowPaths=[];swiftProcess=null;nodeWatcher=null;constructor(e,t={}){this.onChange=e,this.debounceMs=t.debounceMs??500,this.dbPath=t.dbPath??Iu,this.binaryPath=t.binaryPath!==void 0?t.binaryPath:ku();}start(){if(this.started)return;if(!rt.existsSync(this.dbPath)){j.warn({event:"database.watcher.path_not_found",dbPath:this.dbPath,message:"OmniFocus database path not found; change notifications will not fire."});return}this.tryStartSwift()||this.startNodeWatcher(),this.started=true;}stop(){this.clearDebounce(),this.swiftProcess!==null&&(this.swiftProcess.kill("SIGTERM"),this.swiftProcess=null),this.nodeWatcher!==null&&(this.nodeWatcher.close(),this.nodeWatcher=null),this.started=false,j.debug({event:"database.watcher.stopped"});}tryStartSwift(){if(this.binaryPath===null)return false;try{rt.accessSync(this.binaryPath,rt.constants.X_OK);}catch{return j.debug({event:"database.watcher.swift_unavailable",binaryPath:this.binaryPath,message:"Swift watcher binary not found or not executable; falling back to fs.watch."}),false}try{return this.swiftProcess=spawn(this.binaryPath,[this.dbPath],{stdio:["ignore","pipe","pipe"]}),this.swiftProcess.stderr?.on("data",t=>{j.debug({event:"database.watcher.swift_stderr",msg:t.toString().trim()});}),createInterface({input:this.swiftProcess.stdout}).on("line",t=>{this.handleSwiftLine(t);}),this.swiftProcess.on("exit",(t,r)=>{this.started&&(j.warn({event:"database.watcher.swift_exited",code:t,signal:r,message:"Swift watcher exited unexpectedly; falling back to fs.watch."}),this.swiftProcess=null,this.startNodeWatcher());}),this.swiftProcess.on("error",t=>{j.warn({event:"database.watcher.swift_error",err:t}),this.swiftProcess=null,this.startNodeWatcher();}),j.debug({event:"database.watcher.swift_started",dbPath:this.dbPath}),!0}catch(e){return j.warn({event:"database.watcher.swift_spawn_failed",err:e}),false}}handleSwiftLine(e){if(e.trim())try{let t=JSON.parse(e);if(t.event!=="change")return;this.scheduleNotify({source:"swift",ts:t.ts,paths:t.paths});}catch(t){j.debug({event:"database.watcher.swift_parse_error",line:e,err:t});}}startNodeWatcher(){if(rt.existsSync(this.dbPath))try{this.nodeWatcher=rt.watch(this.dbPath,{persistent:!1},(e,t)=>{this.scheduleNotify({source:"node",ts:new Date().toISOString()});}),this.nodeWatcher.on("error",e=>{j.warn({event:"database.watcher.node_error",err:e}),this.stop();}),j.debug({event:"database.watcher.node_started",dbPath:this.dbPath});}catch(e){j.warn({event:"database.watcher.node_start_failed",dbPath:this.dbPath,err:e});}}scheduleNotify(e){if(this.windowStartTs===null&&(this.windowStartTs=e.ts),e.paths)for(let t of e.paths)this.windowPaths.includes(t)||this.windowPaths.push(t);this.debounceTimer!==null&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null;let t={detectedAt:this.windowStartTs??new Date().toISOString(),source:e.source,...this.windowPaths.length>0?{changedPaths:[...this.windowPaths]}:{}};this.windowStartTs=null,this.windowPaths=[],j.debug({event:"database.watcher.change_detected",source:t.source,detectedAt:t.detectedAt,pathCount:t.changedPaths?.length??0}),this.onChange(t);},this.debounceMs);}clearDebounce(){this.debounceTimer!==null&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.windowStartTs=null,this.windowPaths=[];}};var no=class{_tool;_failureThreshold;_windowMs;_openDurationMs;_now;_state="closed";_failures=[];_openedAt=null;_probeInFlight=false;constructor(e,t={}){this._tool=e,this._failureThreshold=t.failureThreshold??3,this._windowMs=t.windowMs??6e4,this._openDurationMs=t.openDurationMs??6e4,this._now=t.now??(()=>Date.now());}get state(){return this._maybeTransitionToHalfOpen(),this._state}async call(e){if(this._maybeTransitionToHalfOpen(),this._state==="open"){let t=this._retryAfterMs();throw new Je(`Circuit for tool "${this._tool}" is open after repeated failures.`,{details:{tool:this._tool,retryAfterMs:t}})}if(this._state==="half_open"){if(this._probeInFlight)throw new Je(`Circuit for tool "${this._tool}" is half-open; probe already in flight.`,{details:{tool:this._tool,retryAfterMs:0}});this._probeInFlight=true;}try{let t=await e();return this._onSuccess(),t}catch(t){throw this._onFailure(),t}finally{this._state==="half_open"&&(this._probeInFlight=false);}}_maybeTransitionToHalfOpen(){if(this._state==="open"&&this._openedAt!==null){let e=this._now()-this._openedAt;e>=this._openDurationMs&&(this._state="half_open",this._probeInFlight=false,j.info({event:"circuit.half_open",tool:this._tool,elapsed:e},"circuit moved to half-open; probe allowed"));}}_onSuccess(){this._state==="half_open"?this._close("probe succeeded"):this._failures=[];}_onFailure(){let e=this._now();if(this._state==="half_open"){this._open(e,"probe failed");return}this._failures=this._failures.filter(t=>e-t<this._windowMs),this._failures.push(e),this._failures.length>=this._failureThreshold&&this._open(e,`${this._failures.length} failures within ${this._windowMs}ms`);}_open(e,t){this._state="open",this._openedAt=e,this._probeInFlight=false,j.warn({event:"circuit.opened",tool:this._tool,reason:t,openDurationMs:this._openDurationMs},"circuit opened \u2014 fast-failing calls");}_close(e){this._state="closed",this._openedAt=null,this._failures=[],this._probeInFlight=false,j.info({event:"circuit.closed",tool:this._tool,reason:e},"circuit closed \u2014 resuming normal operation");}_retryAfterMs(){if(this._openedAt===null)return this._openDurationMs;let e=this._now()-this._openedAt;return Math.max(0,this._openDurationMs-e)}},ro=class{_breakers=new Map;_defaults;constructor(e={}){this._defaults=e;}get(e){let t=this._breakers.get(e);return t||(t=new no(e,this._defaults),this._breakers.set(e,t)),t}clear(){this._breakers.clear();}get size(){return this._breakers.size}snapshot(){return Array.from(this._breakers.entries()).map(([e,t])=>({name:e,state:t.state}))}},oo=new ro;var Ct=["inbox","projects","tags","forecast","flagged","nearby","review"];z.object({id:z.string().min(1),name:z.string().min(1),kind:z.enum(["builtin","custom"]),requiresPro:z.boolean(),icon:z.string().nullable()});function Tu(n){return n instanceof Error?{errorCode:n.code??"OF_UNKNOWN",message:n.message}:{errorCode:"OF_UNKNOWN",message:String(n)}}async function ne(n,e){let t=[],r=[];for(let o=0;o<n.length;o++){let a=n[o];if(a!==void 0)try{let s=await e(a,o);t.push({index:o,value:s});}catch(s){r.push({index:o,...Tu(s)});}}return {succeeded:t,failed:r}}function F(n){return n.toISOString()}var Rt=class{now;idCounter;tasks=new Map;projects=new Map;tags=new Map;folders=new Map;attachments=new Map;lastSyncAt=null;constructor(e={}){this.now=e.now??(()=>new Date),this.idCounter=e.idSeed??0;}nextId(e,t){return this.idCounter+=1,t.of(`${e}_${this.idCounter.toString().padStart(6,"0")}`)}async listTasks(e){return Array.from(this.tasks.values()).filter(t=>this.matchesTask(t,e))}async getTask(e){let t=this.tasks.get(e);if(t===void 0)throw new P(`Task not found: ${e}`,{details:{resource:"task",id:e}});return t}async getTasksMany(e){return e.map(t=>this.tasks.get(t)??null)}async createTask(e){if(e.name.trim()==="")throw new I("Task name must be non-empty",{details:{field:"name"}});if(e.projectId!==void 0&&e.parentId!==void 0)throw new I("createTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(e.projectId!==void 0&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`,{details:{resource:"project",id:e.projectId}});if(e.parentId!==void 0&&!this.tasks.has(e.parentId))throw new P(`Parent task not found: ${e.parentId}`,{details:{resource:"task",id:e.parentId}});for(let a of e.tagIds??[])if(!this.tags.has(a))throw new P(`Tag not found: ${a}`,{details:{resource:"tag",id:a}});let t=this.nextId("task",f),r=F(this.now()),o={id:t,name:e.name,note:e.note??null,noteHtml:e.noteHtml??null,projectId:e.projectId??null,parentId:e.parentId??null,tagIds:[...e.tagIds??[]],deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,completed:false,completedAt:null,dropped:false,droppedAt:null,available:true,blocked:false,sequential:e.sequential??false,completedByChildren:e.completedByChildren??false,repetition:null,createdAt:r,modifiedAt:r};return this.tasks.set(t,o),this.bumpProjectTaskCount(o.projectId,1),t}async updateTask(e,t){let r=await this.getTask(e);if(t.name!==void 0&&t.name.trim()==="")throw new I("Task name must be non-empty",{details:{field:"name"}});if(t.tagIds!==void 0){for(let a of t.tagIds)if(!this.tags.has(a))throw new P(`Tag not found: ${a}`,{details:{resource:"tag",id:a}})}let o={...r,...t.name!==void 0?{name:t.name}:{},...t.note!==void 0?{note:t.note}:{},...t.noteHtml!==void 0?{noteHtml:t.noteHtml}:{},...t.flagged!==void 0?{flagged:t.flagged}:{},...t.deferDate!==void 0?{deferDate:t.deferDate}:{},...t.dueDate!==void 0?{dueDate:t.dueDate}:{},...t.estimatedMinutes!==void 0?{estimatedMinutes:t.estimatedMinutes}:{},...t.tagIds!==void 0?{tagIds:[...t.tagIds]}:{},...t.sequential!==void 0?{sequential:t.sequential}:{},...t.completedByChildren!==void 0?{completedByChildren:t.completedByChildren}:{},...t.repetition!==void 0?{repetition:t.repetition}:{},modifiedAt:F(this.now())};this.tasks.set(e,o);}async completeTask(e,t){return this.applyTaskCompletion(e,true,t)}async uncompleteTask(e){return this.applyTaskCompletion(e,false)}async dropTask(e,t){return this.applyTaskDropState(e,true,t)}async undropTask(e){return this.applyTaskDropState(e,false)}async applyTaskCompletion(e,t,r){let o=await this.getTask(e);if(!t&&!o.completed)return;let a=t?F(r??this.now()):null;this.tasks.set(e,{...o,completed:t,completedAt:a,modifiedAt:a??F(this.now())}),this.bumpProjectCompletedCount(o.projectId,t?1:-1);}async applyTaskDropState(e,t,r){let o=await this.getTask(e);if(!t&&!o.dropped)return;let a=t?F(r??this.now()):null;this.tasks.set(e,{...o,dropped:t,droppedAt:a,modifiedAt:a??F(this.now())});}async batchCreateTasks(e){return ne(e,t=>this.createTask(t))}async batchUpdateTasks(e){return ne(e,async({id:t,patch:r})=>(await this.updateTask(t,r),t))}async batchCompleteTasks(e){return ne(e,async({id:t,at:r})=>(await this.completeTask(t,r),t))}async batchUncompleteTasks(e){return ne(e,async({id:t})=>(await this.uncompleteTask(t),t))}async batchDeleteTasks(e){return ne(e,async({id:t})=>(await this.deleteTask(t),t))}async batchDropTasks(e){return ne(e,async({id:t})=>(await this.dropTask(t),t))}async batchUndropTasks(e){return ne(e,async({id:t})=>(await this.undropTask(t),t))}async deleteTask(e){let t=await this.getTask(e);this.tasks.delete(e),this.adjustProjectCountsForTask(t.projectId,t,-1);}async moveTask(e,t){let r=await this.getTask(e);if(t.projectId!==void 0&&t.parentId!==void 0)throw new I("moveTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(t.projectId!==void 0&&!this.projects.has(t.projectId))throw new P(`Project not found: ${t.projectId}`,{details:{resource:"project",id:t.projectId}});if(t.parentId!==void 0&&!this.tasks.has(t.parentId))throw new P(`Parent task not found: ${t.parentId}`,{details:{resource:"task",id:t.parentId}});this.adjustProjectCountsForTask(r.projectId,r,-1);let o=t.projectId??null;this.tasks.set(e,{...r,projectId:o,parentId:t.parentId??null,modifiedAt:F(this.now())}),this.adjustProjectCountsForTask(o,r,1);}async batchMoveTasks(e){return ne(e,async({id:t,destination:r})=>(await this.moveTask(t,r),t))}async duplicateTask(e,t){let r=await this.getTask(e),o,a;if(t.destination===void 0)o=r.projectId,a=r.parentId;else {let c=t.destination;if(("projectId"in c?1:0)+("parentId"in c?1:0)+("toInbox"in c&&c.toInbox===true?1:0)!==1)throw new I("duplicateTask: destination must specify exactly one of projectId, parentId, or toInbox",{details:{field:"destination"}});if("projectId"in c){if(!this.projects.has(c.projectId))throw new P(`Project not found: ${c.projectId}`,{details:{resource:"project",id:c.projectId}});o=c.projectId,a=null;}else if("parentId"in c){let m=this.tasks.get(c.parentId);if(m===void 0)throw new P(`Parent task not found: ${c.parentId}`,{details:{resource:"task",id:c.parentId}});o=m.projectId,a=c.parentId;}else o=null,a=null;}let s=c=>Array.from(this.tasks.values()).filter(h=>h.parentId===c),i=(c,h,m)=>{let g=this.nextId("task",f),b=F(this.now()),w={id:g,name:c.name,note:c.note,noteHtml:c.noteHtml,projectId:h,parentId:m,tagIds:[...c.tagIds],deferDate:c.deferDate,dueDate:c.dueDate,estimatedMinutes:c.estimatedMinutes,flagged:c.flagged,completed:false,completedAt:null,dropped:false,droppedAt:null,available:true,blocked:false,sequential:c.sequential,completedByChildren:c.completedByChildren,repetition:c.repetition,createdAt:b,modifiedAt:b};return this.tasks.set(g,w),this.bumpProjectTaskCount(h,1),g},l=i(r,o,a),u=0;if(t.recursive){let c=(h,m,g)=>{for(let b of s(h)){let w=i(b,g,m);u+=1,c(b.id,w,g);}};c(r.id,l,o);}return {newId:l,descendantCount:u}}async reorderTask(e,t){let r=await this.getTask(e),{newProjectId:o,newParentId:a,anchorMode:s,anchorId:i}=this.resolveReorderDestination(e,r,t);(o!==r.projectId||a!==r.parentId)&&(this.adjustProjectCountsForTask(r.projectId,r,-1),this.adjustProjectCountsForTask(o,r,1));let u={...r,projectId:o,parentId:a,modifiedAt:F(this.now())},c=[];for(let[g,b]of this.tasks)g!==e&&c.push([g,b]);let h=g=>g.projectId===o&&g.parentId===a,m;if(s==="start")m=c.findIndex(([,g])=>h(g)),m===-1&&(m=c.length);else if(s==="end"){let g=-1;c.forEach(([,b],w)=>{h(b)&&(g=w);}),m=g===-1?c.length:g+1;}else {let g=c.findIndex(([b])=>b===i);m=s==="before"?g:g+1;}this.tasks.clear(),c.forEach(([g,b],w)=>{w===m&&this.tasks.set(e,u),this.tasks.set(g,b);}),m>=c.length&&this.tasks.set(e,u);}resolveReorderDestination(e,t,r){if("before"in r||"after"in r){let l="before"in r?r.before:r.after,u=this.tasks.get(l);if(u===void 0)throw new P(`Reference task not found: ${l}`,{details:{resource:"task",id:l}});if(l===e)throw new I("reorderTask: reference must differ from the task id",{details:{field:"position"}});if(u.projectId!==t.projectId||u.parentId!==t.parentId)throw new I("reorderTask: reference task must share parent with the task being moved",{details:{field:"position"}});return {newProjectId:t.projectId,newParentId:t.parentId,anchorMode:"before"in r?"before":"after",anchorId:l}}let{at:o,in:a}=r,s,i;if("projectId"in a){if(!this.projects.has(a.projectId))throw new P(`Project not found: ${a.projectId}`,{details:{resource:"project",id:a.projectId}});s=a.projectId,i=null;}else if("parentId"in a){if(!this.tasks.has(a.parentId))throw new P(`Parent task not found: ${a.parentId}`,{details:{resource:"task",id:a.parentId}});if(a.parentId===e)throw new I("reorderTask: cannot reparent a task under itself",{details:{field:"position.in.parentId"}});s=this.tasks.get(a.parentId)?.projectId??null,i=a.parentId;}else s=null,i=null;return {newProjectId:s,newParentId:i,anchorMode:o,anchorId:null}}async listProjects(e={}){return Array.from(this.projects.values()).filter(t=>!(e.folderId!==void 0&&t.folderId!==e.folderId||e.status!==void 0&&t.status!==e.status))}async getProject(e){let t=this.projects.get(e);if(t===void 0)throw new P(`Project not found: ${e}`,{details:{resource:"project",id:e}});return t}async getProjectsMany(e){return e.map(t=>this.projects.get(t)??null)}async createProject(e){if(e.name.trim()==="")throw new I("Project name must be non-empty",{details:{field:"name"}});if(e.folderId!==void 0&&!this.folders.has(e.folderId))throw new P(`Folder not found: ${e.folderId}`,{details:{resource:"folder",id:e.folderId}});let t=this.nextId("proj",k),r=F(this.now()),o={id:t,name:e.name,note:e.note??null,noteHtml:e.noteHtml??null,folderId:e.folderId??null,tagIds:[...e.tagIds??[]],status:e.status??"active",completionCriterion:e.completionCriterion??"parallel",deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,reviewIntervalDays:e.reviewIntervalDays??null,nextReviewDate:null,lastReviewDate:null,completed:false,completedAt:null,dropped:false,droppedAt:null,taskCount:0,completedTaskCount:0,createdAt:r,modifiedAt:r};return this.projects.set(t,o),this.bumpFolderProjectCount(o.folderId,1),t}async updateProject(e,t){let r=await this.getProject(e);if(t.name!==void 0&&t.name.trim()==="")throw new I("Project name must be non-empty",{details:{field:"name"}});let o={...r,...t.name!==void 0?{name:t.name}:{},...t.note!==void 0?{note:t.note}:{},...t.noteHtml!==void 0?{noteHtml:t.noteHtml}:{},...t.status!==void 0?{status:t.status}:{},...t.completionCriterion!==void 0?{completionCriterion:t.completionCriterion}:{},...t.deferDate!==void 0?{deferDate:t.deferDate}:{},...t.dueDate!==void 0?{dueDate:t.dueDate}:{},...t.estimatedMinutes!==void 0?{estimatedMinutes:t.estimatedMinutes}:{},...t.flagged!==void 0?{flagged:t.flagged}:{},...t.tagIds!==void 0?{tagIds:[...t.tagIds]}:{},...t.reviewIntervalDays!==void 0?{reviewIntervalDays:t.reviewIntervalDays}:{},modifiedAt:F(this.now())};this.projects.set(e,o);}async completeProject(e,t){let r=await this.getProject(e),o=F(t??this.now());this.projects.set(e,{...r,status:"done",completed:true,completedAt:o,modifiedAt:o});}async dropProject(e,t){let r=await this.getProject(e),o=F(t??this.now());this.projects.set(e,{...r,status:"dropped",dropped:true,droppedAt:o,modifiedAt:o});}async batchCompleteProjects(e){return ne(e,async({id:t})=>(await this.completeProject(t),t))}async batchDropProjects(e){return ne(e,async({id:t})=>(await this.dropProject(t),t))}async moveProject(e,t){let r=await this.getProject(e);if(t.folderId!==null&&!this.folders.has(t.folderId))throw new P(`Folder not found: ${t.folderId}`,{details:{resource:"folder",id:t.folderId}});this.bumpFolderProjectCount(r.folderId,-1),this.projects.set(e,{...r,folderId:t.folderId,modifiedAt:F(this.now())}),this.bumpFolderProjectCount(t.folderId,1);}async deleteProject(e){let t=await this.getProject(e);for(let[r,o]of this.tasks)o.projectId===e&&this.tasks.set(r,{...o,projectId:null});this.projects.delete(e),this.bumpFolderProjectCount(t.folderId,-1);}async markProjectReviewed(e){let t=await this.getProject(e),r=this.now(),o=F(r),a=null;if(t.reviewIntervalDays!==null){let s=new Date(r);s.setUTCDate(s.getUTCDate()+t.reviewIntervalDays),a=F(s);}this.projects.set(e,{...t,lastReviewDate:o,nextReviewDate:a,modifiedAt:o});}async listProjectsDueForReview(){let e=new Date;return e.setUTCHours(23,59,59,999),[...this.projects.values()].filter(t=>t.nextReviewDate===null||new Date(t.nextReviewDate)<=e).sort((t,r)=>t.nextReviewDate===null&&r.nextReviewDate===null?0:t.nextReviewDate===null?-1:r.nextReviewDate===null?1:t.nextReviewDate.localeCompare(r.nextReviewDate))}async setProjectReviewInterval(e,t){let r=await this.getProject(e);this.projects.set(e,{...r,reviewIntervalDays:t});}async listTags(e={}){return Array.from(this.tags.values()).filter(t=>!(e.parentId!==void 0&&t.parentId!==e.parentId||e.status!==void 0&&t.status!==e.status))}async getTag(e){let t=this.tags.get(e);if(t===void 0)throw new P(`Tag not found: ${e}`,{details:{resource:"tag",id:e}});return t}async getTagsMany(e){return e.map(t=>this.tags.get(t)??null)}async createTag(e){if(e.name.trim()==="")throw new I("Tag name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.tags.has(e.parentId))throw new P(`Parent tag not found: ${e.parentId}`,{details:{resource:"tag",id:e.parentId}});let t=this.nextId("tag",y),r=F(this.now()),o={id:t,name:e.name,parentId:e.parentId??null,status:e.status??"active",location:null,allowsNextAction:e.allowsNextAction??true,taskCount:0,createdAt:r,modifiedAt:r};return this.tags.set(t,o),t}async updateTag(e,t){let r=await this.getTag(e);if(t.name!==void 0&&t.name.trim()==="")throw new I("Tag name must be non-empty",{details:{field:"name"}});if(t.parentId!==void 0&&t.parentId!==null&&!this.tags.has(t.parentId))throw new P(`Parent tag not found: ${t.parentId}`,{details:{resource:"tag",id:t.parentId}});this.tags.set(e,{...r,...t.name!==void 0?{name:t.name}:{},...t.parentId!==void 0?{parentId:t.parentId}:{},...t.status!==void 0?{status:t.status}:{},...t.allowsNextAction!==void 0?{allowsNextAction:t.allowsNextAction}:{},...t.location!==void 0?{location:t.location}:{},modifiedAt:F(this.now())});}async deleteTag(e){await this.getTag(e);for(let[t,r]of this.tasks)r.tagIds.includes(e)&&this.tasks.set(t,{...r,tagIds:r.tagIds.filter(o=>o!==e)});this.tags.delete(e);}async listFolders(e={}){return Array.from(this.folders.values()).filter(t=>!(e.parentId!==void 0&&t.parentId!==e.parentId))}async getFolder(e){let t=this.folders.get(e);if(t===void 0)throw new P(`Folder not found: ${e}`,{details:{resource:"folder",id:e}});return t}async createFolder(e){if(e.name.trim()==="")throw new I("Folder name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.folders.has(e.parentId))throw new P(`Parent folder not found: ${e.parentId}`,{details:{resource:"folder",id:e.parentId}});let t=this.nextId("fold",A),r=F(this.now()),o={id:t,name:e.name,parentId:e.parentId??null,projectCount:0,subfolderCount:0,createdAt:r,modifiedAt:r};return this.folders.set(t,o),o.parentId!==null&&this.bumpFolderSubfolderCount(o.parentId,1),t}async updateFolder(e,t){let r=await this.getFolder(e);if(t.name!==void 0&&t.name.trim()==="")throw new I("Folder name must be non-empty",{details:{field:"name"}});if(t.parentId!==void 0&&t.parentId!==null&&!this.folders.has(t.parentId))throw new P(`Parent folder not found: ${t.parentId}`,{details:{resource:"folder",id:t.parentId}});t.parentId!==void 0&&t.parentId!==r.parentId&&(r.parentId!==null&&this.bumpFolderSubfolderCount(r.parentId,-1),t.parentId!==null&&this.bumpFolderSubfolderCount(t.parentId,1)),this.folders.set(e,{...r,...t.name!==void 0?{name:t.name}:{},...t.parentId!==void 0?{parentId:t.parentId}:{},modifiedAt:F(this.now())});}async deleteFolder(e){let t=await this.getFolder(e);if(t.projectCount>0||t.subfolderCount>0)throw new I(`Folder is not empty (projects=${t.projectCount}, subfolders=${t.subfolderCount})`,{details:{resource:"folder",id:e}});this.folders.delete(e),t.parentId!==null&&this.bumpFolderSubfolderCount(t.parentId,-1);}async searchTasks(e){let t=e.q!==void 0?e.q.toLowerCase():null,r=e.scope??"all";return Array.from(this.tasks.values()).filter(o=>{if(t!==null){let s=r!=="note"&&o.name.toLowerCase().includes(t),i=r!=="name"&&(o.note??"").toLowerCase().includes(t);if(!s&&!i)return false}if(e.projectId!==void 0&&o.projectId!==e.projectId)return false;if(e.tagIds!==void 0&&e.tagIds.length>0){let s=new Set(o.tagIds);if(!e.tagIds.every(i=>s.has(i)))return false}if(e.available!==void 0&&o.available!==e.available)return false;if(e.dueBefore!==void 0||e.dueAfter!==void 0){if(o.dueDate===null)return false;let s=new Date(o.dueDate);if(e.dueBefore!==void 0&&s>=new Date(e.dueBefore)||e.dueAfter!==void 0&&s<=new Date(e.dueAfter))return false}if(e.flagged!==void 0&&o.flagged!==e.flagged)return false;let a=o.completedAt!==null;return !(e.completed==="only"&&!a||e.completed==="exclude"&&a)})}async syncTrigger(){return this.lastSyncAt=F(this.now()),{lastSyncAt:this.lastSyncAt,inFlight:false}}async getLastSync(){return {lastSyncAt:this.lastSyncAt,inFlight:false}}async listPerspectives(){let e={inbox:"Inbox",projects:"Projects",tags:"Tags",forecast:"Forecast",flagged:"Flagged",nearby:"Nearby",review:"Review"};return Ct.map(t=>({id:t,name:e[t]??t,kind:"builtin",requiresPro:false,icon:null}))}customPerspectives=new Map;seedCustomPerspective(e,t){this.customPerspectives.set(e,[...t]);}async evaluateCustomPerspective(e){let t=this.customPerspectives.get(e);if(t===void 0)throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});let r=[];for(let o of t){let a=this.tasks.get(o);a!==void 0&&r.push(a);}return r}async evaluatePerspective(e){let t=Array.from(this.tasks.values());if(e==="review"||e==="nearby")return [];if(e==="inbox")return t.filter(r=>r.projectId===null&&!r.completed&&!r.dropped);if(e==="flagged")return t.filter(r=>r.flagged&&!r.completed&&!r.dropped);if(e==="forecast"){let r=new Date;return r.setHours(23,59,59,999),t.filter(o=>o.dueDate!==null&&new Date(o.dueDate)<=r&&!o.completed&&!o.dropped)}return e==="projects"?t.filter(r=>r.projectId!==null&&!r.completed&&!r.dropped):e==="tags"?t.filter(r=>r.tagIds.length>0&&!r.completed&&!r.dropped):[]}async getForecast(e){let t=Array.from(this.tasks.values()).filter(m=>!m.completed&&!m.dropped),{from:r,to:o,includeOverdue:a=true,includeDeferred:s=true,includeFlagged:i=true}=e,l=a?t.filter(m=>m.dueDate!==null&&m.dueDate<r):[],u=t.filter(m=>m.dueDate!==null&&m.dueDate>=r&&m.dueDate<=o),c=s?t.filter(m=>m.deferDate!==null&&m.deferDate>=r&&m.deferDate<=o):[],h=i?t.filter(m=>m.flagged):[];return {overdue:l,dueToday:u,deferredToday:c,flagged:h}}ownerKey(e){return e.taskId?`task:${e.taskId}`:`project:${e.projectId}`}async listAttachments(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let t=this.ownerKey(e);return Array.from(this.attachments.get(t)?.values()??[])}async addAttachment(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let t=this.ownerKey(e);this.attachments.has(t)||this.attachments.set(t,new Map);let r=this.nextId("att",oe),o=e.filePath.split("/").pop()??"attachment",a={id:r,name:o,mimeType:null,sizeBytes:null,addedAt:F(this.now()),kind:"embedded"};return this.attachments.get(t).set(r,a),r}async removeAttachment(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let t=this.ownerKey(e),r=this.attachments.get(t);if(!r?.has(e.attachmentId))throw new P(`Attachment not found: ${e.attachmentId}`);r.delete(e.attachmentId);}async saveAttachmentToPath(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let t=this.ownerKey(e);if(!this.attachments.get(t)?.has(e.attachmentId))throw new P(`Attachment not found: ${e.attachmentId}`);return {saved:true,path:e.destPath,sizeBytes:0}}async appLaunch(){return {launched:false,alreadyRunning:true}}async pluginInvoke(e){throw new P("pluginInvoke is not supported by InMemoryAdapter \u2014 use a real OmniJsTransport for integration tests")}async getChangesSince(e){let t=new Date(e).getTime(),r=[...this.tasks.values()].filter(a=>new Date(a.modifiedAt).getTime()>=t).map(a=>a.id),o=[...this.projects.values()].filter(a=>new Date(a.modifiedAt).getTime()>=t).map(a=>a.id);return {taskIds:r,projectIds:o}}matchesTask(e,t){return !(t.projectId!==void 0&&e.projectId!==t.projectId||t.parentId!==void 0&&e.parentId!==t.parentId||t.tagId!==void 0&&!e.tagIds.includes(t.tagId)||t.flagged!==void 0&&e.flagged!==t.flagged||t.completed!==void 0&&e.completed!==t.completed||t.available!==void 0&&e.available!==t.available||t.blocked!==void 0&&e.blocked!==t.blocked||t.completedSince!==void 0&&(e.completedAt===null||e.completedAt<t.completedSince)||t.dueBefore!==void 0&&(e.dueDate===null||e.dueDate>=t.dueBefore)||t.dueAfter!==void 0&&(e.dueDate===null||e.dueDate<=t.dueAfter)||t.deferredBefore!==void 0&&(e.deferDate===null||e.deferDate>=t.deferredBefore)||t.deferredAfter!==void 0&&(e.deferDate===null||e.deferDate<=t.deferredAfter)||t.inbox===true&&e.projectId!==null)}bumpProjectTaskCount(e,t){if(e===null)return;let r=this.projects.get(e);r!==void 0&&this.projects.set(e,{...r,taskCount:Math.max(0,r.taskCount+t)});}adjustProjectCountsForTask(e,t,r){this.bumpProjectTaskCount(e,r),t.completed&&this.bumpProjectCompletedCount(e,r);}bumpProjectCompletedCount(e,t){if(e===null)return;let r=this.projects.get(e);r!==void 0&&this.projects.set(e,{...r,completedTaskCount:Math.max(0,r.completedTaskCount+t)});}bumpFolderProjectCount(e,t){if(e===null)return;let r=this.folders.get(e);r!==void 0&&this.folders.set(e,{...r,projectCount:Math.max(0,r.projectCount+t)});}bumpFolderSubfolderCount(e,t){let r=this.folders.get(e);r!==void 0&&this.folders.set(e,{...r,subfolderCount:Math.max(0,r.subfolderCount+t)});}};function ot(n){return typeof n=="object"&&n!==null&&"error"in n&&typeof n.error=="object"&&n.error!==null}function Q(n,e){return {succeeded:n.succeeded.map(t=>({index:t.index,value:e(t.value)})),failed:n.failed}}var Os=`/**
|
|
68
|
+
${e}`}function kh(){return `You are running an inbox-triage pass on OmniFocus. The goal is to clear the
|
|
69
|
+
inbox in one user confirmation, not ten clicks. Follow these steps in order:
|
|
70
|
+
|
|
71
|
+
1. **Load the inbox** \u2014 read \`omnifocus://inbox\`. If it is empty, report
|
|
72
|
+
"Inbox empty \u2014 nothing to triage" and stop.
|
|
73
|
+
|
|
74
|
+
2. **Load context** for routing decisions (one-time read; cache the results
|
|
75
|
+
in your working memory for this run):
|
|
76
|
+
- \`omnifocus://capabilities\` \u2014 server feature flags
|
|
77
|
+
- \`project_list\` \u2014 the agent should know what projects exist before
|
|
78
|
+
proposing routes
|
|
79
|
+
- \`tag_list\` \u2014 same, for tag IDs
|
|
80
|
+
|
|
81
|
+
3. **Propose an assignment for every inbox task.** For each task, derive:
|
|
82
|
+
- \`projectId\` \u2014 required; pick the most-likely existing project from
|
|
83
|
+
step 2's listing. If no project fits well, surface that in the table
|
|
84
|
+
(see step 4) and ask the user where it should go.
|
|
85
|
+
- \`addTagIds\` / \`removeTagIds\` \u2014 optional; suggest tags only when
|
|
86
|
+
the task name strongly implies them.
|
|
87
|
+
- \`deferDate\` / \`dueDate\` \u2014 optional; only if the task name carries
|
|
88
|
+
temporal intent ("by Friday", "next sprint", "after the offsite").
|
|
89
|
+
- \`flagged\` \u2014 optional; \`true\` only for items that should be on
|
|
90
|
+
today's plate.
|
|
91
|
+
|
|
92
|
+
4. **Present the proposals as a structured table** \u2014 one row per inbox task,
|
|
93
|
+
columns: task name, proposed project, tags (added / removed), defer, due,
|
|
94
|
+
flagged, and a one-sentence rationale. Format consistently. Do NOT
|
|
95
|
+
collapse multiple tasks into a single row.
|
|
96
|
+
|
|
97
|
+
5. **Wait for user confirmation.** Do NOT auto-fire \`task_batch_assign\`.
|
|
98
|
+
The user reviews the table and may:
|
|
99
|
+
- approve the whole batch \u2014 proceed to step 6
|
|
100
|
+
- approve with edits ("change task 3's project to X, drop the tag on
|
|
101
|
+
task 5") \u2014 apply the edits to your proposal set, re-render the table,
|
|
102
|
+
and ask again
|
|
103
|
+
- reject specific items ("skip task 2, the rest look good") \u2014 drop those
|
|
104
|
+
from the assignment list before proceeding
|
|
105
|
+
|
|
106
|
+
6. **Fire \`task_batch_assign\`** with the confirmed
|
|
107
|
+
\`{ assignments: [...] }\` array. The tool returns
|
|
108
|
+
\`{ assigned, failed }\` \u2014 both arrays carry the original-input index, so
|
|
109
|
+
you can map results back to the table you presented.
|
|
110
|
+
|
|
111
|
+
7. **Report** \u2014 summarise: how many tasks landed in which projects, how
|
|
112
|
+
many failed and why (the \`failed[].errorCode\` is prefixed \`move:\` or
|
|
113
|
+
\`update:\` to indicate which phase failed). If any failed, ask the user
|
|
114
|
+
whether to retry, skip, or hand off.`}function Ih(t,e){return `You are translating a natural-language request into a saved OmniFocus custom perspective.
|
|
115
|
+
|
|
116
|
+
User's description: "${t}"${e?`
|
|
117
|
+
|
|
118
|
+
Proposed name: "${e}"`:""}
|
|
119
|
+
|
|
120
|
+
Follow these steps in order. Do NOT skip the preview step \u2014 saving without a dry-run is the failure mode this flow exists to prevent.
|
|
121
|
+
|
|
122
|
+
## 1. Propose a rule tree
|
|
123
|
+
|
|
124
|
+
Translate the description into a \`PerspectiveRule[]\` tree. The shape is the same one \`perspective_create\` accepts. **If the description is genuinely ambiguous, ask exactly ONE disambiguating question and stop \u2014 do not guess.** Examples of ambiguity worth asking about:
|
|
125
|
+
- "nearby tasks" \u2192 nearby what? Location-based filtering is unsupported; clarify whether the user means *available* (\`actionAvailability: "available"\`) or something else.
|
|
126
|
+
- "due this week" \u2192 today only, or any time before Sunday?
|
|
127
|
+
- "high priority" \u2192 flagged, or due-soon, or both?
|
|
128
|
+
|
|
129
|
+
When the description is clear, propose the tree directly without asking.
|
|
130
|
+
|
|
131
|
+
### Rule-tree atom reference
|
|
132
|
+
|
|
133
|
+
Each atom is a single-key object. Combine atoms inside a top-level array \u2014 \`aggregation\` (\`"all"\` | \`"any"\` | \`"none"\`) controls how they combine. Use a nested \`{ aggregateType, aggregateRules }\` for compound logic.
|
|
134
|
+
|
|
135
|
+
| Atom key | Meaning |
|
|
136
|
+
|---|---|
|
|
137
|
+
| \`actionAvailability\` | \`"available"\` (not blocked, not deferred-future), \`"remaining"\` (not done), \`"completed"\`, \`"dropped"\`, \`"firstAvailable"\` |
|
|
138
|
+
| \`actionStatus\` | \`"flagged"\` or \`"due"\` |
|
|
139
|
+
| \`actionHasAllOfTags\` | \`string[]\` of tag IDs \u2014 task must carry every tag |
|
|
140
|
+
| \`actionHasAnyOfTags\` | \`string[]\` of tag IDs \u2014 task must carry at least one |
|
|
141
|
+
| \`actionHasNoProject\` | \`true\` for inbox-style filter |
|
|
142
|
+
| \`actionHasDueDate\` | \`true\` / \`false\` |
|
|
143
|
+
| \`actionHasDeferDate\` | \`true\` / \`false\` |
|
|
144
|
+
| \`actionIsLeaf\` | \`true\` for tasks with no children |
|
|
145
|
+
| \`actionIsProject\` | \`true\` to restrict to project-equivalent items |
|
|
146
|
+
| \`actionMatchingSearch\` | \`string[]\` substrings \u2014 searches name + note |
|
|
147
|
+
| \`actionWithinFocus\` | \`string[]\` of project / folder IDs \u2014 restrict to descendants |
|
|
148
|
+
|
|
149
|
+
Wrap a single atom in \`{ disabledRule: <atom> }\` to keep it in the tree but skip it during evaluation (useful when the user asks to toggle a constraint on/off).
|
|
150
|
+
|
|
151
|
+
### Common patterns
|
|
152
|
+
|
|
153
|
+
- **Available + flagged** (high-priority "actionable now" view):
|
|
154
|
+
\`\`\`json
|
|
155
|
+
{ "aggregation": "all", "rules": [{ "actionAvailability": "available" }, { "actionStatus": "flagged" }] }
|
|
156
|
+
\`\`\`
|
|
157
|
+
- **Inbox-only**:
|
|
158
|
+
\`\`\`json
|
|
159
|
+
{ "aggregation": "all", "rules": [{ "actionHasNoProject": true }] }
|
|
160
|
+
\`\`\`
|
|
161
|
+
- **At-home-or-phone, no due date, available** (the ticket's example shape):
|
|
162
|
+
\`\`\`json
|
|
163
|
+
{
|
|
164
|
+
"aggregation": "all",
|
|
165
|
+
"rules": [
|
|
166
|
+
{ "actionAvailability": "available" },
|
|
167
|
+
{ "actionHasAnyOfTags": ["<home-tag-id>", "<phone-tag-id>"] }
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
\`\`\`
|
|
171
|
+
Resolve the tag IDs via \`tag_list\` first if you don't have them.
|
|
172
|
+
|
|
173
|
+
## 2. Preview via dry-run
|
|
174
|
+
|
|
175
|
+
Call \`perspective_evaluate_dry_run({ aggregation, rules })\` with the proposed tree. Report the count of matched tasks and 3\u20135 sample names. **Do not advance to step 3 if:**
|
|
176
|
+
- The count is 0 (the rules don't match anything \u2014 re-propose, don't save an empty view).
|
|
177
|
+
- The count is suspiciously broad (e.g. > 100 when the user asked for "today's actionable tasks") \u2014 re-propose with tighter constraints.
|
|
178
|
+
|
|
179
|
+
Show the agent-friendly summary inline; the user can read the count and decide.
|
|
180
|
+
|
|
181
|
+
## 3. Save on user confirm
|
|
182
|
+
|
|
183
|
+
After the user confirms the preview matches their intent, call:
|
|
184
|
+
|
|
185
|
+
\`\`\`
|
|
186
|
+
perspective_create({ name: "<name>", aggregation: "<all|any|none>", rules: [...] })
|
|
187
|
+
\`\`\`
|
|
188
|
+
|
|
189
|
+
The \`rules\` value is the same tree you passed to \`perspective_evaluate_dry_run\` \u2014 pass it verbatim, no re-validation needed. \`perspective_create\` returns \`{ id }\`; surface that id to the user so they can reference it later (\`perspective_evaluate({ perspectiveId: "<id>" })\`).
|
|
190
|
+
|
|
191
|
+
After save, call \`perspective_list\` to confirm the new perspective is visible. Report the resulting count + name + id.
|
|
192
|
+
|
|
193
|
+
## Failure modes
|
|
194
|
+
|
|
195
|
+
- **OF Pro required.** Custom perspectives need OmniFocus Pro. \`perspective_evaluate_dry_run\` and \`perspective_create\` both return \`OF_FEATURE_REQUIRES_PRO\` on Standard. Surface that to the user \u2014 they cannot save a perspective without upgrading.
|
|
196
|
+
- **Duplicate name.** \`perspective_create\` rejects duplicate names with a typed validation error. Either ask the user for a different name or append a disambiguator (e.g. \` - 2\`).
|
|
197
|
+
- **Don't save without a name.** If the user did not supply a name, ask for one before step 3 \u2014 don't invent one.
|
|
198
|
+
|
|
199
|
+
Read-only verification first; mutation only on explicit user approval.`}function mc(t){t.registerPrompt(Ar,{description:"Run a daily OmniFocus triage: load snapshot + overdue + forecast/today, reschedule or drop overdue tasks, confirm due-today tasks, unflag low-priority flagged tasks, and process the inbox. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:fh()}}]})),t.registerPrompt(Cr,{description:"Run a weekly OmniFocus review: walk every project due for review, check its tasks, mark it reviewed or complete/drop it, and clean up stale tasks. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:gh()}}]})),t.registerPrompt(Er,{description:"Extract action items from meeting notes and create OmniFocus tasks. Pass raw notes as `notes`; optionally target a project with `projectId`. Tasks land in the inbox when projectId is omitted.",argsSchema:{notes:z.string().min(1).describe("Raw meeting notes to extract action items from."),projectId:z.string().optional().describe("Persistent OmniFocus project ID to assign tasks to. Omit to use the inbox.")}},async({notes:e,projectId:n})=>({messages:[{role:"user",content:{type:"text",text:hh(e,n)}}]})),t.registerPrompt(Mr,{description:"Create a new OmniFocus project and populate it with tasks derived from a brief. Pass `name` and `brief`; optionally place it in a folder with `folderId`.",argsSchema:{name:z.string().min(1).describe("Name of the new project."),brief:z.string().min(1).describe("One-paragraph description of the project goal. Used to derive subtasks."),folderId:z.string().optional().describe("Persistent OmniFocus folder ID to place the project in. Omit for top-level.")}},async({name:e,brief:n,folderId:r})=>({messages:[{role:"user",content:{type:"text",text:yh(e,n,r)}}]})),t.registerPrompt(uh,{description:"Triage the OmniFocus inbox in one user confirmation. The agent reads the inbox, proposes a structured assignment per task (project, tags, defer/due, flagged), presents the proposals as a table, and on user approval fires task_batch_assign. Does NOT auto-confirm \u2014 the user's approval is the gating step. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:kh()}}]})),t.registerPrompt(mh,{description:"Translate a free-text description into a saved OmniFocus custom perspective. Walks the agent through three steps: (1) propose a PerspectiveRule[] tree from the prose (asking ONE disambiguation question if genuinely ambiguous, else proposing directly), (2) preview the matched tasks via perspective_evaluate_dry_run, (3) save via perspective_create only after user confirmation. Embeds a reference card of common rule-tree atoms so the agent has the vocabulary without web access. Custom perspectives require OmniFocus Pro.",argsSchema:{description:z.string().min(1).describe("Free-text description of the perspective the user wants. Examples: 'everything I could do at home, on a phone, with under 15 minutes', 'flagged tasks that are available right now', 'inbox items with no defer date'."),name:z.string().min(1).optional().describe("Optional display name for the new perspective. When omitted, the agent asks the user for one before the save step.")}},async({description:e,name:n})=>({messages:[{role:"user",content:{type:"text",text:Ih(e,n)}}]}));}var _n=class{config;windows=new Map;constructor(e){this.config=e;}check(e){let n=Date.now(),r=n-this.config.windowSeconds*1e3,o=this.getAndPrune(e,r);if(o.length>=this.config.limit){let i=o[0]+this.config.windowSeconds*1e3-n+1;throw new Tn(`Rate limit exceeded for tool "${e}". Limit: ${this.config.limit} calls per ${this.config.windowSeconds}s.`,{details:{retryAfterMs:i}})}o.push(n);}remaining(e){let n=Date.now(),r=n-this.config.windowSeconds*1e3,o=this.getAndPrune(e,r),a=this.config.limit-o.length,i=o.length>0?new Date(o[0]+this.config.windowSeconds*1e3).toISOString():new Date(n+this.config.windowSeconds*1e3).toISOString();return {remaining:a,resetAt:i}}reset(e){this.windows.delete(e);}getAndPrune(e,n){this.windows.has(e)||this.windows.set(e,[]);let r=this.windows.get(e),o=0;for(;o<r.length&&r[o]<=n;)o++;return o>0&&r.splice(0,o),r}};function Sh(){let t=fileURLToPath(import.meta.url);return Zi.resolve(Zi.dirname(t),"../../bin/calendar-bridge")}var qe=class{binaryPath;spawn;existsSync;constructor(e={}){this.binaryPath=e.binaryPath??Sh(),this.spawn=e.spawn??spawn,this.existsSync=e.existsSync??dt.existsSync;}async ping(){return this.invoke(["ping"])}async getPermission(){return this.invoke(["permission"])}async requestAccess(){return this.invoke(["request-access"])}async readEvents(e,n,r){let o=r?{...process.env,OMNIFOCUS_CALENDAR_SOURCES:r}:process.env,a=await this.invoke(["calendar",e,n],o);if("error"in a)throw a.error==="permission-denied"?new vn({details:{permission:a.permission}}):new ge(`calendar-bridge returned error: ${a.error}`,{details:{error:a.error}});return a.events}async invoke(e,n){if(!this.existsSync(this.binaryPath))throw new ge(`calendar-bridge binary not found at ${this.binaryPath}`,{details:{binaryPath:this.binaryPath}});let r;try{r=this.spawn(this.binaryPath,e,n?{env:n}:{});}catch(o){throw new ge(`failed to spawn calendar-bridge: ${o.message}`,{cause:o,details:{binaryPath:this.binaryPath}})}return new Promise((o,a)=>{let i="",s="";r.stdout?.on("data",c=>{i+=typeof c=="string"?c:c.toString("utf8");}),r.stderr?.on("data",c=>{s+=typeof c=="string"?c:c.toString("utf8");}),r.on("error",c=>{a(new ge(`calendar-bridge spawn error: ${c.message}`,{cause:c}));}),r.on("close",c=>{if(c!==0){a(new ge(`calendar-bridge exited with code ${c}: ${s.trim()}`,{details:{exitCode:c,stderr:s.trim()}}));return}let d=i.trim().split(`
|
|
200
|
+
`)[0]??"";if(!d){a(new ge("calendar-bridge produced no output",{details:{stderr:s.trim()}}));return}try{o(JSON.parse(d));}catch(l){a(new ge(`calendar-bridge produced unparsable JSON: ${d.slice(0,200)}`,{cause:l,details:{line:d}}));}});})}};var bh=864e5;function gc(t,e={}){let{limit:n,windowSeconds:r}=t.OMNIFOCUS_TOOL_RATE_LIMIT,o=Math.round(n*60/r),a=e.ofEdition??"standard",i=a==="pro";return {ofVersion:e.ofVersion??"unknown",ofEdition:a,transports:{jxa:{available:true,timeoutMs:t.OMNIFOCUS_JXA_TIMEOUT_MS},omnijs:{available:true,timeoutMs:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS}},features:{customPerspectives:i,forecastTag:i,repetitionRules:i,pluginInvocation:i,rawScriptTools:t.OMNIFOCUS_ALLOW_RAW_SCRIPT},rateLimits:{defaultPerToolPerMinute:o},idempotencyTtlMs:bh,calendarAccess:e.calendarAccess??{available:false,permission:"unknown"},webhooks:e.webhooks??{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:0,names:[]}}}async function On(t=new qe){try{let{permission:e}=await t.getPermission();return {available:!0,permission:e}}catch(e){if(e instanceof ge)return R.debug({event:"capabilities.calendar_bridge_unavailable",message:e.message}),{available:false,permission:"unknown"};throw e}}var Pn="omnifocus://capabilities";function hc(t,e){t.registerResource("omnifocus-capabilities",Pn,{description:"Structured capabilities object for this omnifocus-mcp server instance. Read once at session start to discover available features (Pro vs Standard), transport timeouts, rate limits, calendar-bridge availability, and whether raw-script tools are enabled. ofVersion and ofEdition are 'unknown'/'standard' until the lazy OF probe runs.",mimeType:"application/json"},async n=>({contents:[{uri:Pn,mimeType:"application/json",text:JSON.stringify(await e(),null,2)}]}));}var yc=/^[A-Za-z0-9._-]{3,64}$/;function _h(t){return typeof t=="string"&&yc.test(t)}function Gt(t){let e=z.string().regex(yc,`Invalid ${t}: expected 3-64 alphanumeric / _ / - characters`).transform(n=>n);return {kind:t,of(n){return e.parse(n)},is(n){return _h(n)},schema:e}}var h=Gt("TaskId"),k=Gt("ProjectId"),S=Gt("TagId"),W=Gt("FolderId"),Oe=Gt("AttachmentId");var Tc="omnifocus://agenda{?date}",Oh=6e4;function kc(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0)}function xh(t){let e=new Date(t);if(Number.isNaN(e.getTime()))throw new y(`agenda: could not parse date as ISO-8601: ${t}`,{details:{raw:t}});return e}function Ic(t){return t.dueDate!==null}function vc(t,e){return {kind:"of-task",id:t.id,name:t.name,startsAt:e,dueDate:t.dueDate,deferDate:t.deferDate,flagged:t.flagged,projectId:t.projectId,parentId:t.parentId}}var Nr=class{entry=null;get(e,n){return !this.entry||this.entry.key!==e||this.entry.value.expiresAt<=n?null:this.entry.value.payload}set(e,n,r){this.entry={key:e,value:{payload:n,expiresAt:r}};}},Dh=new Nr;async function Rh(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??Oh,a=t.sources,i=e.date?kc(xh(e.date)):kc(r),s=new Date(i.getTime()+1440*60*1e3),c=i.toISOString(),d=s.toISOString(),l=t.cache??Dh,m=`${c}|${d}|${a??""}`,f=l.get(m,r.getTime());if(f)return f;let[g,v]=await Promise.all([t.bridge.readEvents(c,d,a),t.forecastService.get({from:c,to:d})]),I=new Set,T=[];for(let M of [...v.dueToday,...v.deferredToday,...v.flagged,...v.overdue]){let H=String(M.id);I.has(H)||(I.add(H),T.push(M));}let E=g.map(M=>({kind:"calendar-event",...M})),w=T.filter(Ic),D=T.filter(M=>!Ic(M)),j=w.map(M=>vc(M,M.dueDate)),B=[...E,...j].sort((M,H)=>M.startsAt!==H.startsAt?M.startsAt<H.startsAt?-1:1:xn(M).localeCompare(xn(H))),_=D.map(M=>vc(M,"")).sort((M,H)=>xn(M).localeCompare(xn(H))),U={items:B,floating:_};return l.set(m,U,r.getTime()+o),U}function xn(t){return t.kind==="calendar-event"?t.title:t.name}function wc(t,e,n=new qe){t.registerResource("omnifocus-agenda",new ResourceTemplate(Tc,{list:void 0}),{description:"Merged daily agenda: macOS calendar events from EventKit interleaved with the OmniFocus forecast for the same day. Returns { items, floating } where items[] is the sorted timeline (calendar-event and of-task entries by startsAt ASC) and floating[] is OF tasks with no dueDate. Each AgendaItem is tagged kind: 'calendar-event' | 'of-task'. date query param is ISO-8601; defaults to today (local zone). Cached 60s. Throws CalendarPermissionDenied when Calendar access has not been granted; throws CalendarBridgeUnavailable when the Swift binary is missing.",mimeType:"application/json"},async(r,o)=>{let a=o,i=await Rh({bridge:n,forecastService:e,sources:process.env.OMNIFOCUS_CALENDAR_SOURCES},{date:a.date});return {contents:[{uri:r.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Sc="omnifocus://burndown/{projectId}";function Fr(t){return Math.round(t*100)/100}async function Ch(t,e,n=new Date){let r;try{r=k.of(e);}catch{return {error:{code:"ProjectNotFound",message:`Project not found: ${e}`}}}let a=(await t.listProjects()).find(z=>String(z.id)===String(r));if(!a)return {error:{code:"ProjectNotFound",message:`Project not found: ${e}`}};if(!a.dueDate)return {error:{code:"NoDueDate",message:`Project ${String(a.id)} has no due date \u2014 burndown requires a deadline.`}};let[i,s]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true})]),c=String(a.id),d=i.filter(z=>!z.dropped&&z.projectId!==null&&String(z.projectId)===c),l=s.filter(z=>z.projectId!==null&&String(z.projectId)===c),m=d.length+l.length,f=l.length,g=d.length,v=[...d,...l],I=v[0],T=v.length>0&&I!==void 0?v.reduce((z,le)=>le.createdAt<z?le.createdAt:z,I.createdAt):a.createdAt,E=new Date(T).getTime(),w=new Date(a.dueDate).getTime(),D=n.getTime(),j=Math.max(1,(w-E)/864e5),B=Math.max(0,Math.min(j,(D-E)/864e5)),_=Fr(B/j*100),U=m===0?100:Fr(f/m*100),M=Fr((U-_)/100*j),H;return m===0?H="No tasks in project \u2014 burndown is trivially complete.":M>0?H=`Ahead of pace by ${M.toFixed(1)} day(s). Ideal: ${_.toFixed(1)}% done; actual: ${U.toFixed(1)}%.`:M<0?H=`Behind pace by ${Math.abs(M).toFixed(1)} day(s). Ideal: ${_.toFixed(1)}% done; actual: ${U.toFixed(1)}%.`:H=`On pace. Ideal: ${_.toFixed(1)}%; actual: ${U.toFixed(1)}%.`,H+=" (Ideal line is naive linear from earliest-task creation to project due date. Task weights and blockers are not modelled.)",{projectId:c,name:a.name,dueDate:a.dueDate,startDate:T,totalTasks:m,completedTasks:f,remainingTasks:g,idealLinePercent:_,actualPercent:U,deltaDays:M,note:H}}function bc(t,e){t.registerResource("omnifocus-burndown",new ResourceTemplate(Sc,{list:void 0}),{description:"Per-project burndown chart data \u2014 remaining vs completed tasks against a naive linear ideal line. Returns totalTasks, completedTasks, remainingTasks, idealLinePercent, actualPercent, and deltaDays (positive = ahead of pace; negative = behind). Requires the project to have a due date; returns a typed NoDueDate error otherwise. Returns ProjectNotFound when the projectId is unknown. Ideal line is a linear model from earliest-task creation to due date \u2014 task weights and blockers are not modelled. Pairs with omnifocus://velocity for macro-level and omnifocus://retrospective for qualitative review. Read-only.",mimeType:"application/json"},async(n,r)=>{let a=r.projectId??"",i=await Ch(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var jc="omnifocus://calendar{?from,to}",Mh=6e4;function Nh(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function Fh(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate()+1,0,0,0,0).toISOString()}var Ur=class{entry=null;get(e,n){return !this.entry||this.entry.key!==e||this.entry.value.expiresAt<=n?null:this.entry.value.payload}set(e,n,r){this.entry={key:e,value:{payload:n,expiresAt:r}};}clear(){this.entry=null;}};async function Uh(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??Mh,a=t.sources,i=e.from??Nh(r),s=e.to??Fh(r),c=t.cache??Lh,d=`${i}|${s}|${a??""}`,l=c.get(d,r.getTime());if(l)return l;let f={events:await t.bridge.readEvents(i,s,a)};return c.set(d,f,r.getTime()+o),f}var Lh=new Ur;function _c(t,e=new qe){t.registerResource("omnifocus-calendar",new ResourceTemplate(jc,{list:void 0}),{description:"macOS Calendar events from EventKit in the half-open interval [from, to). Both query params are ISO-8601; when omitted, defaults span the current local-zone day. Returns { events: CalendarEvent[] } where each event carries id, title, startsAt, endsAt, allDay, calendarName, calendarSource, optional location, status (confirmed|tentative|cancelled), and optional isAttendee. Read-only \u2014 does not write to EventKit. Filter calendar sources via the OMNIFOCUS_CALENDAR_SOURCES env var (comma-separated, substring match against calendar.title, case-insensitive). Cached 60s. First call may trigger the macOS Calendar TCC prompt; subsequent calls return the cached state. Throws CalendarPermissionDenied when access has not been granted.",mimeType:"application/json"},async(n,r)=>{let o=r,a=await Uh({bridge:e,sources:process.env.OMNIFOCUS_CALENDAR_SOURCES},{from:o.from,to:o.to});return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(a,null,2)}]}});}var Lr=[{phrase:"add a task",aliases:["create a task","new task","remind me to","todo"],description:"When I describe something I need to do, capture it as a task.",sequence:[{kind:"tool",name:"task_create"}],category:"capture"},{phrase:"save this meeting",aliases:["capture meeting notes","log this meeting","record this discussion"],description:"When I want to record a meeting and its action items, walk me through capture.",sequence:[{kind:"prompt",name:"capture-meeting"}],category:"capture"},{phrase:"plan a project",aliases:["scope a project","set up a project","plan out"],description:"When I want to scope a new project, walk me through structured planning.",sequence:[{kind:"prompt",name:"project-planning"}],category:"capture"},{phrase:"what's on my plate today",aliases:["what do I have today","today's tasks","what's due today","today's forecast"],description:"When I ask what's on my plate today, give me the forecast organized by category.",sequence:[{kind:"resource",uri:"omnifocus://snapshot"},{kind:"resource",uri:"omnifocus://forecast/today"}],category:"plan"},{phrase:"pack today's deep work",aliases:["plan a focused day","fit work into the day","schedule my day"],description:"When I want to fit my available work into a time budget, run forecast_pack.",sequence:[{kind:"tool",name:"forecast_pack"}],category:"plan"},{phrase:"what's coming up",aliases:["upcoming work","next few days","what's due soon"],description:"When I want the near-term outlook, pull the forecast for the next several days.",sequence:[{kind:"tool",name:"forecast_get",args:{days:7}}],category:"plan"},{phrase:"weekly review",aliases:["GTD weekly review","do my weekly","weekly catch-up"],description:"When I want to do my weekly review, walk me through it section by section.",sequence:[{kind:"prompt",name:"weekly-review"}],category:"review"},{phrase:"daily review",aliases:["start my day","daily catch-up","morning review"],description:"When I want to orient at the start of the day, walk me through the daily check-in.",sequence:[{kind:"prompt",name:"daily-review"}],category:"review"},{phrase:"what changed today",aliases:["recent activity","what's new","what happened today"],description:"When I want a chronological view of recent task activity, pull the recent-activity resource.",sequence:[{kind:"resource",uri:"omnifocus://recent-activity"}],category:"review"},{phrase:"what's overdue",aliases:["am I behind","show overdue","missed deadlines"],description:"When I want to see what's slipped, pull the overdue list.",sequence:[{kind:"resource",uri:"omnifocus://overdue"}],category:"review"},{phrase:"what needs review",aliases:["projects due for review","what to review","review queue"],description:"When I want to catch projects that haven't been reviewed in a while, pull the review-due list.",sequence:[{kind:"resource",uri:"omnifocus://review-due"}],category:"review"},{phrase:"process my inbox",aliases:["clear my inbox","triage inbox","inbox zero"],description:"When I want to triage the inbox, pull the inbox tasks so I can route each one.",sequence:[{kind:"resource",uri:"omnifocus://inbox"}],category:"triage"},{phrase:"what's flagged",aliases:["my flagged work","starred tasks","priority queue"],description:"When I want my self-curated priority queue, pull the flagged tasks.",sequence:[{kind:"resource",uri:"omnifocus://flagged"}],category:"triage"},{phrase:"what did I close last week",aliases:["what did I finish","completed work","closed last week"],description:"When I want to look back at finished work, pull the retrospective for the date range.",sequence:[{kind:"resource",uri:"omnifocus://retrospective"}],category:"retrospect"},{phrase:"how am I tracking",aliases:["my velocity","completion rate","throughput"],description:"When I want a macro-level signal on completion pace, pull the velocity resource.",sequence:[{kind:"resource",uri:"omnifocus://velocity"}],category:"retrospect"},{phrase:"is this project on pace",aliases:["project burndown","project progress","am I behind on"],description:"When I want to see whether one project is on pace toward its due date, pull burndown for that project.",sequence:[{kind:"resource",uri:"omnifocus://burndown/{projectId}"}],category:"retrospect"},{phrase:"export this project as text",aliases:["copy project to clipboard","project as taskpaper","share this project"],description:"When I want a portable text view of a project, export it as TaskPaper.",sequence:[{kind:"tool",name:"export_taskpaper"}],category:"share"},{phrase:"export as outline",aliases:["project as opml","outline export"],description:"When I want an outline-shaped export I can paste into another tool, use OPML.",sequence:[{kind:"tool",name:"export_opml"}],category:"share"},{phrase:"find duplicates",aliases:["taxonomy collisions","duplicate tags","duplicate projects"],description:"When I suspect taxonomy drift (same tag spelled two ways, etc.), pull the audit resource.",sequence:[{kind:"resource",uri:"omnifocus://taxonomy-audit"}],category:"audit"},{phrase:"what's stalled",aliases:["stalled projects","projects without next actions","blocked projects"],description:"When I want to find projects that look stuck, pull the project-health resource.",sequence:[{kind:"tool",name:"project_list",args:{status:"active"}}],category:"audit"},{phrase:"run an OmniFocus plug-in",aliases:["invoke automation","trigger plugin"],description:"When I name an installed Omni Automation plug-in, invoke it by identifier.",sequence:[{kind:"tool",name:"plugin_invoke"}],category:"automate"}];var Br="omnifocus://intents";function Bh(t=new Date){return {intents:Lr,count:Lr.length,generatedAt:t.toISOString()}}function Pc(t){t.registerResource("omnifocus-intents",Br,{description:"Curated routing table mapping human-style user phrases to canonical tool / prompt / resource sequences. Eighty registered tools, eight verbs (capture, plan, review, triage, retrospect, share, audit, automate). Read at session start \u2014 or when uncertain which tool fits the user's intent \u2014 to discover obvious paths. The agent is NOT constrained by this resource; it's a fallback for ambiguity, not a gatekeeper. Each intent has a phrase, aliases, a one-sentence description in the user's voice, and an ordered sequence of steps (tool calls, prompts, or resource reads). Steps may carry template `args` placeholders the agent fills. Read-only.",mimeType:"application/json"},async e=>({contents:[{uri:Br,mimeType:"application/json",text:JSON.stringify(Bh(),null,2)}]}));}var Jh=new Set(["today","tomorrow","yesterday","this-week","next-week","end-of-week","end-of-month"]);function qt(t){return typeof t=="string"&&Jh.has(t)}function ot(t){let e=-t.getTimezoneOffset(),n=e>=0?"+":"-",r=Math.abs(e),o=String(Math.floor(r/60)).padStart(2,"0"),a=String(r%60).padStart(2,"0"),i=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),c=String(t.getDate()).padStart(2,"0");return `${i}-${s}-${c}T00:00:00${n}${o}:${a}`}function Vt(t,e=new Date){let n=new Date(e);switch(n.setHours(0,0,0,0),t){case "today":return ot(n);case "tomorrow":{let r=new Date(n);return r.setDate(r.getDate()+1),ot(r)}case "yesterday":{let r=new Date(n);return r.setDate(r.getDate()-1),ot(r)}case "this-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?-6:1-o)),ot(r)}case "next-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?1:8-o)),ot(r)}case "end-of-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?0:7-o)),ot(r)}case "end-of-month":{let r=new Date(n);return r.setMonth(r.getMonth()+1,0),ot(r)}}}var Oc=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;function Kt(t){return typeof t=="string"&&Oc.test(t)&&!Number.isNaN(Date.parse(t))}function oe(){return z.string().regex(Oc,"Expected ISO-8601 with offset (e.g. 2026-04-19T12:00:00-05:00 or 2026-04-19T17:00:00Z). Bare local time is rejected.").refine(t=>!Number.isNaN(Date.parse(t)),{message:"Well-formed ISO-8601 but not a valid date (out-of-range month, hour, or minute)."})}function Le(){return z.string().transform((t,e)=>Kt(t)?t:qt(t)?Vt(t):(e.addIssue({code:z.ZodIssueCode.custom,message:`Expected ISO-8601 with offset or a relative shortcut (today, tomorrow, yesterday, this-week, next-week, end-of-week, end-of-month). Got: "${t}".`}),z.NEVER))}function Be(t,e){if(t===null||t.length===0)return;let n=`\`\`\`${e}`,o=new RegExp(`(^|\\n)${$h(n)}[ \\t]*\\n`).exec(t);if(o===null)return;let a=o.index+o[0].length,i=/\n```[ \t]*(?=\n|$)/;i.lastIndex=a;let s=t.slice(a),c=i.exec(s);return c===null?void 0:{body:s.slice(0,c.index),start:o.index+(o[1]===""?0:1),end:a+c.index+c[0].length}}function kt(t){let e={},n=t.split(`
|
|
201
|
+
`);for(let r of n){let o=r.trim();if(o==="")continue;let a=o.indexOf(":");if(a===-1)continue;let i=o.slice(0,a).trim();if(i==="")continue;let s=o.slice(a+1).trim();(s.startsWith('"')&&s.endsWith('"')&&s.length>=2||s.startsWith("'")&&s.endsWith("'")&&s.length>=2)&&(s=s.slice(1,-1)),e[i]=s;}return e}function It(t){let e=[];for(let[n,r]of Object.entries(t))r!==void 0&&e.push(`${n}: ${r}`);return e.join(`
|
|
202
|
+
`)}function vt(t,e,n){let r=`\`\`\`${e}
|
|
203
|
+
${n}
|
|
204
|
+
\`\`\``,o=Be(t,e);return o!==void 0&&t!==null?t.slice(0,o.start)+r+t.slice(o.end):t===null||t.length===0?r:`${r}
|
|
205
|
+
|
|
206
|
+
${t}`}function Rn(t,e){if(t===null)return null;let n=Be(t,e);if(n===void 0)return t.length===0?null:t;let r=t.slice(0,n.start).replace(/[ \t]*\n+$/,""),o=t.slice(n.end).replace(/^\n+[ \t]*/,""),a;return r===""||o===""?a=r+o:a=`${r}
|
|
207
|
+
|
|
208
|
+
${o}`,a===""?null:a}function $h(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var $r="decision-journal",Wr=["stall-is-intentional","deferred-by-choice","blocked-on-external","awaiting-decision","acknowledged-zombie"],Wh=z.object({kind:z.enum(Wr).describe("The kind of judgment recorded."),reason:z.string().min(1).describe("Human-readable reason for the decision."),recordedAt:oe().describe("When the decision was recorded (ISO-8601 with offset)."),until:oe().optional().describe("Optional auto-expiry. When set and in the past, the decision is treated as expired and downstream consumers re-surface the target.")});function Ae(t){let e=Be(t,$r);if(e===void 0)return;let n=kt(e.body),r={};typeof n.kind=="string"&&n.kind.length>0&&(r.kind=n.kind),typeof n.reason=="string"&&n.reason.length>0&&(r.reason=n.reason),typeof n.recordedAt=="string"&&n.recordedAt.length>0&&(r.recordedAt=n.recordedAt),typeof n.until=="string"&&n.until.length>0&&(r.until=n.until);let o=Wh.safeParse(r);return o.success?o.data:void 0}function xc(t,e=new Date){return t.until===void 0?true:new Date(t.until).getTime()>e.getTime()}function Hr(t,e){let n=It({kind:e.kind,reason:e.reason,recordedAt:e.recordedAt,until:e.until});return vt(t,$r,n)}function zr(t){return Rn(t,$r)}function Xt(t,e,n,r=14){if(t.status!=="active"||t.completed||t.dropped||t.deferDate&&new Date(t.deferDate).getTime()>n.getTime())return false;let o=e??t.modifiedAt,a=new Date(o).getTime();return (n.getTime()-a)/(1440*60*1e3)>=r}var Rc="omnifocus://project-health{?staleDays}";function zh(t){let e=new Map;for(let n of t){if(!n.projectId)continue;let r=String(n.projectId),o=e.get(r);o?o.push(n):e.set(r,[n]);}return e}function Gh(t){if(t.length===0)return null;let e=t[0]??null;for(let n=1;n<t.length;n++){let r=t[n];r&&(e===null||r>e)&&(e=r);}return e}function Dc(t,e){let n=e.getTime()-new Date(t).getTime();return Math.max(0,Math.floor(n/(1440*60*1e3)))}function qh(t,e,n){let r=e.filter(T=>!T.completed&&!T.dropped),o=e.map(T=>T.modifiedAt),a=Gh(o),i=a??t.modifiedAt,s=Dc(i,n),c=r.filter(T=>T.available).length,d=r.filter(T=>T.blocked).length,l=r.length===0,m=n.toISOString(),f=r.length>0&&r.every(T=>T.deferDate!==null&&T.deferDate>m),g=t.lastReviewDate,v=g?Dc(g,n):null,I;return t.nextReviewDate===null?I=true:I=t.nextReviewDate<=m,{lastTaskActivityAt:a,daysSinceActivity:s,availableTaskCount:c,blockedTaskCount:d,hasNoActions:l,deferredFutureTasks:f,lastReviewedAt:g,daysSinceReview:v,overdueForReview:I}}function Vh(t,e,n){return n?!!(e||t.availableTaskCount===0||t.overdueForReview||t.deferredFutureTasks):false}function Kh(t){let e=0;if(t.overdueForReview){let n=t.daysSinceReview??9999;e+=1e6+n;}return e+=1e3+t.daysSinceActivity,t.availableTaskCount===0&&(e+=50),t.hasNoActions&&(e+=25),e}async function Xh(t,e=14,n=new Date){let[r,o]=await Promise.all([t.listProjects(),t.listTasks({})]),a=zh(o),i=[],s=[];for(let d of r){let l=d.status==="active"&&!d.completed&&!d.dropped;if(!l)continue;let m=a.get(String(d.id))??[],f=qh(d,m,n),g=Xt(d,f.lastTaskActivityAt,n,e);if(!Vh(f,g,l))continue;let v=Ae(d.note),I=v!==void 0&&xc(v,n),T={projectId:String(d.id),name:d.name,status:d.status,signals:f,...v!==void 0&&{decision:v},_severity:Kh(f)};I?s.push(T):i.push(T);}let c=(d,l)=>d._severity!==l._severity?l._severity-d._severity:d.name.localeCompare(l.name);return i.sort(c),s.sort(c),{projects:i.map(({_severity:d,...l})=>l),acknowledged:s.map(({_severity:d,...l})=>l),staleDays:e,generatedAt:n.toISOString()}}function Yh(t){if(!t)return 14;let e=Number.parseInt(t,10);return !Number.isFinite(e)||e<1?14:e}function Ac(t,e){t.registerResource("omnifocus-project-health",new ResourceTemplate(Rc,{list:void 0}),{description:"Triage list of active projects flagged by health-warning conditions \u2014 the weekly-review answer to 'which active projects are stalled?' Returns per-project signals (lastTaskActivityAt, daysSinceActivity, availableTaskCount, blockedTaskCount, hasNoActions, deferredFutureTasks, lastReviewedAt, daysSinceReview, overdueForReview), filtered to projects matching \u22651 of: \u2265 staleDays since last activity (default 14, override with ?staleDays=N), zero available tasks, overdue for review, all tasks deferred into the future. Projects with an active decision-journal entry (#485) are partitioned into a separate `acknowledged` array rather than `projects` \u2014 auditable, not invisible. When a decision's `until` passes, the project re-emerges in `projects` automatically. Sorted by severity (review-overdue first, then longest no-activity, then no-available-tasks). Granular signals \u2014 leaves the judgment (blocked vs abandoned) to the agent. Read-only.",mimeType:"application/json"},async(n,r)=>{let a=Yh(r.staleDays),i=await Xh(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Cc="omnifocus://recent-activity{?hours}",Gr=24,Qh=168;function ey(t){if(t==null||t==="")return Gr;let e=Number(t);return !Number.isFinite(e)||e<0?Gr:Math.min(Math.max(1,Math.round(e)),Qh)}async function ty(t,e){let n=new Date(Date.now()-e*36e5).toISOString(),[r,o,a]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true,completedSince:n}),t.listProjects()]),i=r.filter(m=>!m.dropped&&m.createdAt>=n).map(m=>({taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,createdAt:m.createdAt})).sort((m,f)=>f.createdAt>m.createdAt?1:f.createdAt<m.createdAt?-1:0),s=o.filter(m=>m.completedAt!==null).map(m=>{let f=m.completedAt,g=new Date(f).getTime()-new Date(m.createdAt).getTime();return {taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,completedAt:f,age_days_at_completion:Math.max(0,Math.round(g/864e5))}}).sort((m,f)=>f.completedAt>m.completedAt?1:f.completedAt<m.completedAt?-1:0),c=r.filter(m=>m.dropped&&m.droppedAt!==null&&m.droppedAt>=n).map(m=>({taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,droppedAt:m.droppedAt})).sort((m,f)=>f.droppedAt>m.droppedAt?1:f.droppedAt<m.droppedAt?-1:0),d=r.filter(m=>!m.dropped&&m.deferDate!==null&&m.modifiedAt>=n).map(m=>({taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,deferDate:m.deferDate})).sort((m,f)=>f.deferDate>m.deferDate?1:f.deferDate<m.deferDate?-1:0),l=a.filter(m=>m.modifiedAt>=n).map(m=>({projectId:String(m.id),name:m.name,status:m.status,modifiedAt:m.modifiedAt})).sort((m,f)=>f.modifiedAt>m.modifiedAt?1:f.modifiedAt<m.modifiedAt?-1:0);return {window:{hours:e,since:n},tasksCreated:i,tasksCompleted:s,tasksDropped:c,tasksDeferred:d,projectsModified:l,summary:{taskCreatedCount:i.length,taskCompletedCount:s.length,taskDroppedCount:c.length,taskDeferredCount:d.length,projectsAffected:l.length}}}function Ec(t,e){t.registerResource("omnifocus-recent-activity",new ResourceTemplate(Cc,{list:void 0}),{description:"Session-priming activity feed for the last N hours (default 24, max 168). Returns tasks created, completed, dropped, and deferred, plus projects modified. Safe to read at every session start \u2014 designed for agent context priming. All sections sorted by timestamp descending. Empty sections return [], never omitted. Fidelity notes: tasksCreated includes active tasks only (completed-within-window appear in tasksCompleted); tasksDeferred uses modifiedAt as a proxy (tasks modified in window with a deferDate set); projectsModified includes any project modification, not status-change-only.",mimeType:"application/json"},async(n,r)=>{let o=ey(r.hours),a=o===Gr?"omnifocus://recent-activity":`omnifocus://recent-activity?hours=${o}`,i=await ty(e,o);return {contents:[{uri:a,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Nc="omnifocus://retrospective{?from,to}",ry=7;function oy(t,e,n=()=>new Date){let r=n(),o=r.toISOString(),a=new Date(r.getTime()-ry*864e5).toISOString(),i=Mc(t)?t:a,s=Mc(e)?e:o;return i>s?{from:s,to:i}:{from:i,to:s}}function Mc(t){if(t==null||t==="")return false;let e=new Date(t);return Number.isFinite(e.getTime())}async function ay(t,e){let{from:n,to:r}=e,[o,a]=await Promise.all([t.listTasks({completed:true,completedSince:n}),t.listTasks({completed:false})]),i=o.filter(l=>l.completedAt!==null&&l.completedAt<=r).map(l=>{let m=l.completedAt,f=new Date(m).getTime()-new Date(l.createdAt).getTime();return {taskId:String(l.id),name:l.name,projectId:l.projectId!==null?String(l.projectId):null,completedAt:m,age_days_at_completion:Math.max(0,Math.round(f/864e5))}}).sort((l,m)=>m.completedAt>l.completedAt?1:m.completedAt<l.completedAt?-1:0),s=a.filter(l=>l.dropped&&l.droppedAt!==null&&l.droppedAt>=n&&l.droppedAt<=r).map(l=>({taskId:String(l.id),name:l.name,projectId:l.projectId!==null?String(l.projectId):null,droppedAt:l.droppedAt})).sort((l,m)=>m.droppedAt>l.droppedAt?1:m.droppedAt<l.droppedAt?-1:0),c=a.filter(l=>!l.dropped&&l.deferDate!==null&&l.modifiedAt>=n&&l.modifiedAt<=r).map(l=>({taskId:String(l.id),name:l.name,projectId:l.projectId!==null?String(l.projectId):null,deferDate:l.deferDate})).sort((l,m)=>m.deferDate>l.deferDate?1:m.deferDate<l.deferDate?-1:0),d=new Set;for(let l of i)l.projectId!==null&&d.add(l.projectId);for(let l of s)l.projectId!==null&&d.add(l.projectId);for(let l of c)l.projectId!==null&&d.add(l.projectId);return {window:{from:n,to:r},completed:i,dropped:s,rolled:c,summary:{completedCount:i.length,droppedCount:s.length,rolledCount:c.length,projectsActive:d.size}}}function Fc(t,e){t.registerResource("omnifocus-retrospective",new ResourceTemplate(Nc,{list:void 0}),{description:"Retrospective for a date range \u2014 closes the weekly-review reflection loop. Returns tasks completed (with age-at-completion in days), tasks dropped, and tasks 'rolled' (deferred-forward heuristic) within the window, plus a summary count and the distinct-projects-active count. Defaults to the trailing 7 days when from/to are omitted; partial windows fill the missing side with the conventional default. Fidelity notes: 'rolled' is a heuristic \u2014 OF does not expose deferDate change history, so we use modifiedAt-in-window as a proxy for 'recently re-deferred'. Treat as a signal, not an exact count of defer hops. Cached \u2014 historical data doesn't change quickly. Read-only.",mimeType:"application/json"},async(n,r)=>{let o=r,a=oy(o.from,o.to),i=await ay(e,a);return {contents:[{uri:`omnifocus://retrospective?from=${encodeURIComponent(a.from)}&to=${encodeURIComponent(a.to)}`,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var qr="omnifocus://stats";function sy(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function iy(t){let e=new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0),n=e.getDay(),r=n===0?-6:1-n;return e.setDate(e.getDate()+r),e.toISOString()}function cy(t){let e=new Map;for(let n of t){if(!n.projectId)continue;let r=String(n.projectId),o=e.get(r);o?o.push(n):e.set(r,[n]);}return e}function dy(t){if(t.length===0)return null;let e=t[0]??null;for(let n=1;n<t.length;n++){let r=t[n];r&&(e===null||r>e)&&(e=r);}return e}async function ly(t,e=new Date){let[n,r,o,a,i,s]=await Promise.all([t.listTasks({}),t.listTasks({inbox:true,completed:false}),t.listProjects(),t.listProjectsDueForReview(),t.listTags(),t.getLastSync()]),c=sy(e),d=iy(e),l=e.toISOString(),m=0,f=0,g=0,v=0,I=0,T=0,E=0,w=0,D=0;for(let $ of n){if(m+=1,$.completed){$.completedAt&&($.completedAt>=d&&(T+=1),$.completedAt>=c&&(I+=1));continue}if($.dropped){$.droppedAt&&$.droppedAt>=c&&(D+=1);continue}$.available&&(f+=1),$.blocked&&(g+=1),$.flagged&&(w+=1),$.deferDate&&$.deferDate>l&&(v+=1),$.dueDate&&$.dueDate<l&&(E+=1);}let j=0,B=0,_=0,U=0,M=cy(n),H=0;for(let $ of o){$.status==="active"&&!$.completed&&!$.dropped&&(j+=1),$.status==="on-hold"&&(B+=1),($.completed||$.status==="done")&&(_+=1),($.dropped||$.status==="dropped")&&(U+=1);let fe=M.get(String($.id))??[],Ne=dy(fe.map(nt=>nt.modifiedAt));Xt($,Ne,e)&&(H+=1);}let z=null;if(r.length>0){let $=r.map(Ne=>Ne.createdAt).reduce((Ne,nt)=>nt<Ne?nt:Ne),fe=e.getTime()-new Date($).getTime();z=Math.max(0,Math.floor(fe/(1440*60*1e3)));}let le=new Set;for(let $ of n)for(let fe of $.tagIds)le.add(String(fe));let Ge=le.size,A=null;if(s.lastSyncAt){let $=e.getTime()-new Date(s.lastSyncAt).getTime();A=Math.max(0,Math.floor($/1e3));}return {tasks:{total:m,available:f,blocked:g,deferred:v,completed_today:I,completed_this_week:T,overdue_count:E,flagged_count:w,dropped_today:D},projects:{total:o.length,active:j,on_hold:B,completed:_,dropped:U,stalled_count:H,due_for_review_count:a.length},inbox:{count:r.length,oldest_age_days:z},tags:{total:i.length,with_tasks_count:Ge},database:{sync_age_seconds:A,last_sync_at:s.lastSyncAt}}}function Uc(t,e){t.registerResource("omnifocus-stats",qr,{description:"Server-side aggregate counts for the OmniFocus database \u2014 tasks, projects, inbox, tags, sync. Use for 'how is my system doing?' queries (weekly review, daily standup, capacity planning) instead of listing every task and tallying client-side. Returns tasks { total, available, blocked, deferred, completed_today, completed_this_week, overdue_count, flagged_count, dropped_today }, projects { total, active, on_hold, completed, dropped, stalled_count, due_for_review_count }, inbox { count, oldest_age_days }, tags { total, with_tasks_count }, database { sync_age_seconds, last_sync_at }. Stalled = active project with \u2265 14 days since last task activity and no future defer date. Read-only.",mimeType:"application/json"},async n=>{let r=await ly(e);return {contents:[{uri:qr,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function py(t,e){if(t===e)return 0;if(t.length===0)return e.length;if(e.length===0)return t.length;let n=Array.from({length:e.length+1},(o,a)=>a),r=new Array(e.length+1);for(let o=1;o<=t.length;o++){r[0]=o;for(let a=1;a<=e.length;a++){let i=t[o-1]===e[a-1]?0:1;r[a]=Math.min(r[a-1]+1,n[a]+1,n[a-1]+i);}[n,r]=[r,n];}return n[e.length]}function Yt(t){return t.toLowerCase().trim().replace(/\s+/g," ").replace(/^@/,"")}function Lc(t){return Yt(t).split(/[\s\-_]+/).filter(Boolean).sort()}function uy(t,e){let n=Lc(t),r=Lc(e);return n.length!==r.length?false:n.every((o,a)=>o===r[a])}function Vr(t,e){if(t===e)return "exact-duplicate";let n=Yt(t),r=Yt(e);return n===r?"case-difference":n===`${r}s`||r===`${n}s`||n===`${r}es`||r===`${n}es`?"plural-singular":py(n,r)<=2||uy(t,e)?"near-duplicate":null}var my=2,fy=new Set(["a","an","and","as","at","by","for","from","in","is","it","of","on","or","the","to","with"]),gy=.7,hy=.2,yy=.05,ky=.05;function Cn(t){return t?t.toLowerCase().split(/[^a-z0-9']+/i).filter(e=>e.length>=my&&!fy.has(e)):[]}function Bc(t,e){if(t.size===0&&e.size===0)return 0;let n=0;for(let o of t)e.has(o)&&(n+=1);let r=t.size+e.size-n;return r===0?0:n/r}function Jc(t,e){let n=new Set(Cn(t.name)),r=new Set(Cn(e.name)),o=Bc(n,r),a=0;if(t.note&&e.note){let m=new Set(Cn(t.note)),f=new Set(Cn(e.note));m.size>0&&f.size>0&&(a=Bc(m,f));}let i=[...n][0],s=[...r][0],c=i!==void 0&&i===s?1:0,d=Yt(t.name)===Yt(e.name)?1:0,l=o*gy+a*hy+c*yy+d*ky;return Math.max(0,Math.min(1,l))}var Kr="omnifocus://taxonomy-audit";async function Iy(t){let[e,n]=await Promise.all([t.listTags(),t.listProjects()]),r=vy(e),o=Ty(n);return {tagCollisions:r,projectCollisions:o}}function vy(t){let e=t.map(n=>({tagId:String(n.id),name:n.name,taskCount:n.taskCount}));return Hc(e,(n,r)=>Vr(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function Ty(t){let e=t.map(n=>({projectId:String(n.id),name:n.name,folderId:n.folderId!==null?String(n.folderId):null,taskCount:n.taskCount}));return Hc(e,(n,r)=>Vr(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function Hc(t,e,n){if(t.length<2)return [];let r=t.map((d,l)=>l),o=new Map;function a(d){for(;r[d]!==d;)r[d]=r[r[d]],d=r[d];return d}function i(d,l,m){let f=a(d),g=a(l);if(f===g){let I=o.get(f)??"near-duplicate";o.set(f,Wc(I,m));return}r[g]=f;let v=o.get(f)??o.get(g)??m;o.set(f,Wc(v,m));}for(let d=0;d<t.length;d++)for(let l=d+1;l<t.length;l++){let m=e(t[d],t[l]);m!==null&&i(d,l,m);}let s=new Map;for(let d=0;d<t.length;d++){let l=a(d),m=s.get(l);m?m.push(t[d]):s.set(l,[t[d]]);}let c=[];for(let[d,l]of s)if(l.length>=2){let m=o.get(d)??"near-duplicate";c.push(n(l,m));}return c}var $c={"exact-duplicate":0,"case-difference":1,"plural-singular":2,"near-duplicate":3};function Wc(t,e){return $c[t]<=$c[e]?t:e}function zc(t,e){t.registerResource("omnifocus-taxonomy-audit",Kr,{description:"Taxonomy audit: detects tag and project name collisions (exact duplicates, case differences, plural/singular variants, near-duplicates with Levenshtein \u2264 2 or token-set equality). Returns { tagCollisions: TagCollision[], projectCollisions: ProjectCollision[] }. Each collision lists the candidate names and a reason. Use to identify naming drift and plan merge operations. Empty sections return [], never omitted.",mimeType:"application/json"},async n=>{let r=await Iy(e);return {contents:[{uri:Kr,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function Xr(t){let e=new Date(t);e.setHours(0,0,0,0);let n=e.getDay(),r=n===0?6:n-1;return e.setDate(e.getDate()-r),e}function En(t){let e=new Date(t);return e.setDate(e.getDate()+7),e}function Gc(t,e=new Date){let n=Xr(e),r=[];for(let o=t-1;o>=0;o--){let a=new Date(n);a.setDate(a.getDate()-o*7),r.push(a);}return r}var Vc="omnifocus://velocity{?weeks}",qc=8,Sy=52;function by(t){if(t==null||t==="")return qc;let e=Number(t);return !Number.isFinite(e)||e<0?qc:Math.min(Sy,Math.max(1,Math.round(e)))}async function jy(t,e,n=new Date){let r=Gc(e,n),o=r[0],a=r[r.length-1],i=En(a),s=o.toISOString(),c=i.toISOString(),[d,l,m]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true,completedSince:s}),t.listProjects()]),f=new Map;for(let _ of m)f.set(String(_.id),_.name);let g=r.map(_=>{let U=En(_),M=_.toISOString(),H=U.toISOString(),z=d.filter(A=>A.createdAt>=M&&A.createdAt<H).length,le=l.filter(A=>A.completedAt!==null&&A.completedAt>=M&&A.completedAt<H).length,Ge=d.filter(A=>A.dropped&&A.droppedAt!==null&&A.droppedAt>=M&&A.droppedAt<H).length;return {weekStart:M,created:z,completed:le,dropped:Ge,netDelta:z-le-Ge}}),v=[];for(let _ of [4,8]){if(e<_)continue;let U=g.slice(-_),M=U.reduce((z,le)=>z+le.completed,0),H=U.reduce((z,le)=>z+le.created,0);v.push({window:_,completedPerWeek:Math.round(M/_*100)/100,createdPerWeek:Math.round(H/_*100)/100});}let I=Xr(a).toISOString(),T=En(a).toISOString(),E=r[Math.max(0,r.length-4)].toISOString(),w=new Map,D=new Map;for(let _ of l){if(_.completedAt===null||_.projectId===null)continue;let U=String(_.projectId);_.completedAt>=I&&_.completedAt<T&&w.set(U,(w.get(U)??0)+1),_.completedAt>=E&&_.completedAt<c&&D.set(U,(D.get(U)??0)+1);}let j=new Set([...w.keys(),...D.keys()]),B=Array.from(j).map(_=>({projectId:_,name:f.get(_)??_,closedThisWeek:w.get(_)??0,closedTrailing4:D.get(_)??0})).sort((_,U)=>U.closedThisWeek!==_.closedThisWeek?U.closedThisWeek-_.closedThisWeek:U.closedTrailing4-_.closedTrailing4).slice(0,5);return {window:{from:s,to:c},weeklyTotals:g,rollingAverages:v,topClosingProjects:B}}function Kc(t,e){t.registerResource("omnifocus-velocity",new ResourceTemplate(Vc,{list:void 0}),{description:"Rolling task velocity over trailing weeks \u2014 created, completed, dropped, and net-delta per week, plus rolling 4-week and 8-week completion/creation averages and the top-5 highest-closing projects. Default window: 8 weeks; max: 52 weeks. Use to answer 'am I getting better or worse?' alongside omnifocus://retrospective (per-range) and omnifocus://burndown/{projectId} (per-project). Read-only.",mimeType:"application/json"},async(n,r)=>{let a=by(r.weeks),i=await jy(e,a);return {contents:[{uri:`omnifocus://velocity?weeks=${a}`,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Zr="waiting-on",Qr=z.object({whom:z.string().min(1).describe("Person, team, or system being waited on."),what:z.string().min(1).optional().describe("Short description of what is being waited on."),since:oe().describe("When the wait started (ISO-8601 with offset)."),followUpAfter:oe().optional().describe("When the agent should nudge if still unresolved (ISO-8601 with offset).")});function Tt(t){let e=Be(t,Zr);if(e===void 0)return;let n=kt(e.body),r={};typeof n.whom=="string"&&n.whom.length>0&&(r.whom=n.whom),typeof n.what=="string"&&n.what.length>0&&(r.what=n.what),typeof n.since=="string"&&n.since.length>0&&(r.since=n.since),typeof n.followUpAfter=="string"&&n.followUpAfter.length>0&&(r.followUpAfter=n.followUpAfter);let o=Qr.safeParse(r);return o.success?o.data:void 0}function Xc(t,e){let n=It({whom:e.whom,what:e.what,since:e.since,followUpAfter:e.followUpAfter});return vt(t,Zr,n)}function Yc(t){return Rn(t,Zr)}function Zc(t,e=new Date){if(t.followUpAfter===void 0)return null;let n=new Date(t.followUpAfter).getTime();if(Number.isNaN(n))return null;let r=e.getTime()-n;return r<0?null:Math.floor(r/864e5)}var eo="omnifocus://waiting-on";async function _y(t,e=new Date){let n=await t.listTasks({completed:false}),r=[];for(let o of n){let a=Tt(o.note);a!==void 0&&r.push({taskId:String(o.id),name:o.name,whom:a.whom,...a.what!==void 0&&{what:a.what},since:a.since,...a.followUpAfter!==void 0&&{followUpAfter:a.followUpAfter},daysOverdue:Zc(a,e)});}return r.sort((o,a)=>{let i=o.daysOverdue,s=a.daysOverdue;return i===null&&s===null?o.since<a.since?-1:o.since>a.since?1:0:i===null?1:s===null?-1:i!==s?s-i:o.since<a.since?-1:o.since>a.since?1:0}),{items:r}}function Qc(t,e){t.registerResource("omnifocus-waiting-on",eo,{description:"All tasks with structured waiting-on metadata, sorted by daysOverdue descending. Each item: { taskId, name, whom, what?, since, followUpAfter?, daysOverdue }. daysOverdue is the integer number of whole days past followUpAfter (0 same-day, null when followUpAfter is unset or still in the future). Use to surface stalled follow-ups without scanning every task. Read-only; safe to retry. Set waiting-on with task_set_waiting_on; clear with task_clear_waiting_on.",mimeType:"application/json"},async n=>{let r=await _y(e);return {contents:[{uri:eo,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}var Qt="omnifocus://snapshot",en="omnifocus://inbox",tn="omnifocus://forecast/today",nn="omnifocus://overdue",rn="omnifocus://flagged",on="omnifocus://review-due",no="omnifocus://project/{id}",ro="omnifocus://tag/{id}",oo="omnifocus://perspective/{id}",ed="omnifocus://tasks/inbox",Py="omnifocus://tasks/project/{projectId}",Oy="omnifocus://tasks/tag/{tagId}";function to(){let t=new Date,e=new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0),n=new Date(t.getFullYear(),t.getMonth(),t.getDate(),23,59,59,999);return {from:e.toISOString(),to:n.toISOString()}}function Ie(t,e){return {contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(e,null,2)}]}}function td(t,e){let{adapter:n,projectService:r,reviewService:o,forecastService:a,perspectiveService:i}=e;t.registerResource("omnifocus-snapshot",Qt,{description:"Orientation snapshot of the current OmniFocus state: inboxCount, overdueCount, dueTodayCount, flaggedCount, reviewDueCount, and syncStatus { lastSyncAt, inFlight }. Read at session start to orient before calling task_list or forecast_get. Use syncStatus.lastSyncAt to detect stale data before making decisions.",mimeType:"application/json"},async s=>{let{from:c,to:d}=to(),[l,m,f,g]=await Promise.all([n.listTasks({completed:false}),n.getForecast({from:c,to:d,includeOverdue:true,includeFlagged:true}),n.listProjectsDueForReview(),n.getLastSync()]),v=l.filter(I=>I.projectId===null&&I.parentId===null).length;return Ie(Qt,{inboxCount:v,overdueCount:m.overdue.length,dueTodayCount:m.dueToday.length,flaggedCount:m.flagged.length,reviewDueCount:f.length,syncStatus:{lastSyncAt:g.lastSyncAt,inFlight:g.inFlight}})}),t.registerResource("omnifocus-inbox",en,{description:"Inbox tasks as Task[]. Incomplete tasks not assigned to any project or parent task. Use to triage the inbox without calling task_list.",mimeType:"application/json"},async s=>{let d=(await n.listTasks({completed:false})).filter(l=>l.projectId===null&&l.parentId===null);return Ie(en,d)}),t.registerResource("omnifocus-forecast-today",tn,{description:"Today's forecast tasks grouped by category: overdue[], dueToday[], deferredToday[], flagged[]. Equivalent to forecast_get with from/to=today. Use for 'what's on my plate today' without a tool call.",mimeType:"application/json"},async s=>{let{from:c,to:d}=to(),l=await a.get({from:c,to:d});return Ie(tn,{overdue:l.overdue,dueToday:l.dueToday,deferredToday:l.deferredToday,flagged:l.flagged})}),t.registerResource("omnifocus-overdue",nn,{description:"All overdue tasks as Task[], sorted by dueDate ascending. Tasks whose dueDate is in the past and are not completed/dropped.",mimeType:"application/json"},async s=>{let{from:c}=to(),l=[...(await n.getForecast({from:c,to:c,includeOverdue:true,includeFlagged:false,includeDeferred:false})).overdue].sort((m,f)=>m.dueDate?f.dueDate?m.dueDate<f.dueDate?-1:m.dueDate>f.dueDate?1:0:-1:1);return Ie(nn,l)}),t.registerResource("omnifocus-flagged",rn,{description:"All flagged available tasks as Task[]. Equivalent to task_list with flagged=true. Use to review the flagged list without a tool call.",mimeType:"application/json"},async s=>{let c=await n.listTasks({flagged:true,completed:false});return Ie(rn,c)}),t.registerResource("omnifocus-review-due",on,{description:"Projects due for review as Project[], sorted by nextReviewDate ascending. Equivalent to review_list_due. Use to start a review session without a tool call.",mimeType:"application/json"},async s=>{let d=[...(await o.listDue()).projects].sort((l,m)=>l.nextReviewDate?m.nextReviewDate?l.nextReviewDate<m.nextReviewDate?-1:l.nextReviewDate>m.nextReviewDate?1:0:-1:1);return Ie(on,d)}),t.registerResource("omnifocus-project",new ResourceTemplate(no,{list:void 0}),{description:"Single project with its full task tree as { project: Project, tasks: Task[] }. Get the project ID from project_list. Tasks are a flat array; rebuild the tree via task.parentId.",mimeType:"application/json"},async(s,c)=>{let d=k.of(c.id),l=await r.get({id:d,includeTaskTree:true});return Ie(`omnifocus://project/${d}`,{project:l.project,tasks:l.tasks??[]})}),t.registerResource("omnifocus-tag",new ResourceTemplate(ro,{list:void 0}),{description:"Single tag with its tasks as { tag: Tag, tasks: Task[] }. Get the tag ID from tag_list.",mimeType:"application/json"},async(s,c)=>{let d=S.of(c.id),[l,m]=await Promise.all([n.getTag(d),n.listTasks({tagId:d,completed:false})]);return Ie(`omnifocus://tag/${d}`,{tag:l,tasks:m})}),t.registerResource("omnifocus-perspective",new ResourceTemplate(oo,{list:void 0}),{description:"Perspective evaluation result as { perspectiveId: string, tasks: Task[] }. Get perspective IDs from perspective_list. Built-in IDs: inbox, projects, tags, forecast, flagged, nearby, review.",mimeType:"application/json"},async(s,c)=>{let d=c.id,l=await i.evaluate(d);return Ie(`omnifocus://perspective/${d}`,{perspectiveId:d,tasks:l.tasks})}),t.registerResource("omnifocus-tasks-inbox",ed,{description:"Inbox tasks as Task[]. Alias for omnifocus://inbox using the unified tasks namespace. Returns incomplete tasks not assigned to any project or parent task.",mimeType:"application/json"},async()=>{let c=(await n.listTasks({completed:false})).filter(d=>d.projectId===null&&d.parentId===null);return Ie(ed,c)}),t.registerResource("omnifocus-tasks-by-project",new ResourceTemplate(Py,{list:void 0}),{description:"Active tasks in a project as Task[]. Get the project ID from project_list or project_get. Returns incomplete tasks whose projectId matches the given ID.",mimeType:"application/json"},async(s,c)=>{let d=k.of(c.projectId),l=await n.listTasks({projectId:d,completed:false});return Ie(`omnifocus://tasks/project/${d}`,l)}),t.registerResource("omnifocus-tasks-by-tag",new ResourceTemplate(Oy,{list:void 0}),{description:"Active tasks with a specific tag as Task[]. Get the tag ID from tag_list or tag_get. Returns incomplete tasks that carry the given tag.",mimeType:"application/json"},async(s,c)=>{let d=S.of(c.tagId),l=await n.listTasks({tagId:d,completed:false});return Ie(`omnifocus://tasks/tag/${d}`,l)}),Ec(t,n),Qc(t,n),Fc(t,n),zc(t,n),Kc(t,n),bc(t,n),_c(t),wc(t,a),Pc(t),Uc(t,n),Ac(t,n);}var Dy=300*1e3,Ry=200,ao=class{_store=new Map;_ttlMs;constructor(e=Dy){this._ttlMs=e;}register(e,n){this._store.size>=Ry&&this._evictExpired();let r=randomBytes(16).toString("hex");return this._store.set(r,{options:e,callback:n,expiresAt:Date.now()+this._ttlMs}),r}get(e){let n=this._store.get(e);if(n){if(Date.now()>n.expiresAt){this._store.delete(e);return}return n}}consume(e){let n=this.get(e);return n&&this._store.delete(e),n}get size(){return this._store.size}_evictExpired(){let e=Date.now();for(let[n,r]of this._store)e>r.expiresAt&&this._store.delete(n);}},ve=new ao;function wt(t){return {code:"WARN_IDS_NOT_FOUND",message:`${t.length} requested ID(s) were not found and have been omitted.`,suggestion:"Verify the IDs are correct and that the items have not been deleted.",details:{missing:t}}}function p(t,e,n,r){let o={data:t,meta:e};return n!==void 0&&(o.pagination=n),r!==void 0&&r.length>0&&(o.hints=r),o}function so(t,e){return {error:t.toJSON(),meta:e}}function St(t,e,n,r,o){let a={kind:"clarification-needed",question:t,replayToken:e,meta:n};return r!==void 0&&r.length>0&&(a.options=r),o!==void 0&&Object.keys(o).length>0&&(a.partial=o),a}function u(t){return {content:[{type:"text",text:JSON.stringify(t)}],structuredContent:t}}var io="Explicitly launch OmniFocus. Do NOT call this automatically \u2014 only invoke when the user explicitly asks to open OmniFocus; prefer other tools when OF is already running. Safe to call when OmniFocus is already running (idempotent). Returns { launched, alreadyRunning } \u2014 launched=true means OmniFocus was not running and was started; alreadyRunning=true means it was already open. Side effects: may open OmniFocus and bring it to the foreground. Example: app_launch()",Cy=z.object({});async function Ey(t,e){let n=await e.adapter.appLaunch(),r=e.makeMeta();return p({launched:n.launched,alreadyRunning:n.alreadyRunning},r)}function nd(t,e){return t.registerTool("app_launch",{description:io,inputSchema:Cy.shape},async n=>{let r=await Ey(n,e);return u(r)})}var Mn=z.object({taskId:h.schema.optional().describe("Persistent ID of the task that owns the attachment. Provide exactly one of taskId or projectId."),projectId:k.schema.optional().describe("Persistent ID of the project that owns the attachment. Provide exactly one of taskId or projectId.")});function Nn(t){if(t.taskId)return {taskId:h.of(t.taskId)};if(t.projectId)return {projectId:k.of(t.projectId)};throw new y("Provide exactly one of taskId or projectId.",{})}var lo='List all file attachments on a task or project. Do not use to retrieve attachment content \u2014 use attachment_save_to_path instead. Returns { attachments } \u2014 array of objects with id, name, mimeType, sizeBytes, addedAt, and kind (embedded|alias). Provide exactly one of taskId or projectId. Read-only; safe to retry. Example: attachment_list({ taskId: "abc123" })',My=Mn;async function Ny(t,e){let n=Nn(t),r=await e.attachmentService.list(n);return p({attachments:r},e.makeMeta())}var po=`Add a file attachment to a task or project from a local file path. The file is embedded into the OmniFocus database. Path must be within the allowed scope (default: $HOME; override via OMNIFOCUS_ATTACHMENT_PATHS). File must not exceed the size cap (default 100 MB; override via OMNIFOCUS_MAX_ATTACHMENT_MB). Returns { id, ownerKind, ownerName } \u2014 ownerKind is 'task' or 'project' and ownerName is the parent's display name (null only if the parent was deleted between the add and the lookup) so the agent can describe the new attachment without a follow-up read. Mutations do not propagate until sync_trigger is called. Example: attachment_add({ taskId: "abc123", filePath: "/Users/me/report.pdf" })`,Fy=Mn.extend({filePath:z.string().min(1).describe("Absolute path to the source file to attach. Must be within the allowed attachment path scope.")});async function Uy(t,e){let n=Nn(t),{id:r,ownerKind:o,ownerName:a}=await e.attachmentService.add({...n,filePath:t.filePath});return p({id:r,ownerKind:o,ownerName:a},e.makeMeta())}var uo=`Remove an attachment from a task or project by attachment ID. Do not use to retrieve or export attachment content \u2014 use attachment_save_to_path instead. Returns { removed: true, attachmentId, ownerKind, ownerName } \u2014 ownerKind is 'task' or 'project' and ownerName is captured BEFORE the JXA call so it survives even if the lookup were to fail post-mutation; null only when the parent itself has been deleted. The agent can describe the removal without a follow-up read. Throws NotFound if the attachment or owner does not exist. Permanent \u2014 cannot be undone. Mutations do not propagate until sync_trigger is called. Example: attachment_remove({ taskId: "abc123", attachmentId: "att456" })`,Ly=Mn.extend({attachmentId:Oe.schema.describe("Persistent ID of the attachment to remove. Get from attachment_list.")});async function By(t,e){let n=Nn(t),r=Oe.of(t.attachmentId),{ownerKind:o,ownerName:a}=await e.attachmentService.remove({...n,attachmentId:r});return p({removed:true,attachmentId:r,ownerKind:o,ownerName:a},e.makeMeta())}var mo=`Copy an attachment's content to a local file path. Do not use to list or remove attachments \u2014 use attachment_list or attachment_remove instead. Returns { saved: true, path, sizeBytes } on success. Destination path must be within the allowed scope (default: $HOME). Writes the file to destPath (creates or overwrites); no side effects on OmniFocus data. Example: attachment_save_to_path({ taskId: "abc123", attachmentId: "att456", destPath: "/Users/me/report.pdf" })`,Jy=Mn.extend({attachmentId:Oe.schema.describe("Persistent ID of the attachment to save. Get from attachment_list."),destPath:z.string().min(1).describe("Absolute destination path where the attachment will be written. Must be within the allowed attachment path scope. Existing files are overwritten.")});async function $y(t,e){let n=Nn(t),r=await e.attachmentService.saveTo({...n,attachmentId:Oe.of(t.attachmentId),destPath:t.destPath});return p(r,e.makeMeta())}function rd(t,e){t.registerTool("attachment_list",{description:lo,inputSchema:My.shape},async n=>{let r=await Ny(n,e);return u(r)}),t.registerTool("attachment_add",{description:po,inputSchema:Fy.shape},async n=>{let r=await Uy(n,e);return u(r)}),t.registerTool("attachment_remove",{description:uo,inputSchema:Ly.shape},async n=>{let r=await By(n,e);return u(r)}),t.registerTool("attachment_save_to_path",{description:mo,inputSchema:Jy.shape},async n=>{let r=await $y(n,e);return u(r)});}function b(t,e={}){e.taskId!==void 0&&t.invalidate(`task:${e.taskId}`),e.projectId!==void 0&&e.projectId!==null&&t.invalidate(`project:${e.projectId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function J(t,e){t.invalidate(`project:${e.projectId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function od(t,e){t.invalidate(`tag:${e.tagId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function ad(t,e){t.invalidate(`folder:${e.folderId}`),t.invalidate("perspective:*"),t.invalidate("search:*");}function sd(t){t.clear();}function Fn(t){t.clear();}var fo="Re-apply the most recently undone mutation, identical to \u2318\u21E7Z in OmniFocus. Advances one entry on the document's redo stack. Any mutation between an undo and a redo invalidates the redo stack (matching UI semantics). Mandatory `confirm: true` mirrors database_undo's destructive-write pattern. Returns { redid: boolean } \u2014 true when an entry was redone, false when the stack was empty. Do NOT use this tool to re-apply a specific operation \u2014 the redo stack is opaque. Prefer database_redo only as a direct counterpart to database_undo when an undo was issued in error. Side effects: re-applies whatever entry is at the top of the document's redo stack; fully invalidates the read cache; does NOT trigger sync. Call sync_trigger when you need the change to appear on other devices. Example: database_redo({ confirm: true })",Wy=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that redo can re-apply a mutation that may now conflict with intervening edits. Must be exactly true. The call is rejected if this field is absent or false.")});async function Hy(t,e){let n=await e.adapter.redoLastMutation();return n.redid&&e.cache!==void 0&&Fn(e.cache),p(n,e.makeMeta({syncPending:n.redid}))}function cd(t,e){return t.registerTool("database_redo",{description:fo,inputSchema:Wy.shape},async n=>{let r=await Hy(n,e);return u(r)})}var go="Reverse the most recent document mutation, identical to \u2318Z in OmniFocus. Walks back one entry on the document's undo stack regardless of mutation source \u2014 an MCP undo can revert a manual UI edit if that was the most recent change. Mandatory `confirm: true` mirrors task_batch_delete's destructive-write pattern, since undo can silently revert changes the agent or another caller just made. Returns { undid: boolean } \u2014 true when an entry was undone, false when the stack was empty. Do NOT use this tool to roll back specific operations \u2014 the undo stack is opaque and you cannot inspect what would be reverted before calling. Prefer database_undo for: post-batch error recovery, retry-after-partial-failure cleanup, and integration-test teardown. Side effects: reverts whatever entry is at the top of the document's undo stack; fully invalidates the read cache (we don't know what was reverted); does NOT trigger sync. Call sync_trigger when you need the change to appear on other devices. Example: database_undo({ confirm: true })",zy=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that undo can revert mutations from any source \u2014 MCP, manual UI edit, or sync replay. Must be exactly true. The call is rejected if this field is absent or false.")});async function Gy(t,e){let n=await e.adapter.undoLastMutation();return n.undid&&e.cache!==void 0&&Fn(e.cache),p(n,e.makeMeta({syncPending:n.undid}))}function ld(t,e){return t.registerTool("database_undo",{description:go,inputSchema:zy.shape},async n=>{let r=await Gy(n,e);return u(r)})}var yo='Clear the decision-journal entry from a task or project\'s note. Strips only the `decision-journal` fenced block; any other user prose and sibling fences (e.g. waiting-on) are preserved. Idempotent: returns noChange:true when the target has no decision recorded. Do NOT use this to delete the target \u2014 prefer task_delete / project_delete. Returns { targetKind, targetId, cleared:true } or { targetKind, targetId, noChange:true }. Side effects: writes the target\'s note via task_update / project_update; sets meta.syncPending = true. Example: { "targetKind": "project", "targetId": "abc" }',qy=z.object({targetKind:z.enum(["task","project"]).describe("Whether the target is a task or a project."),targetId:z.string().min(1).describe("ID of the task or project.")});async function Vy(t,e){if(t.targetKind==="task"){let a=h.of(t.targetId),i=await e.adapter.getTask(a),s=zr(i.note);return s===i.note?p({targetKind:"task",targetId:a,noChange:true},e.makeMeta()):(await e.adapter.updateTask(a,{note:s}),e.cache!==void 0&&b(e.cache,{taskId:a,projectId:i.projectId}),p({targetKind:"task",targetId:a,cleared:true},e.makeMeta({syncPending:true})))}let n=k.of(t.targetId),r=await e.adapter.getProject(n),o=zr(r.note);return o===r.note?p({targetKind:"project",targetId:n,noChange:true},e.makeMeta()):(await e.adapter.updateProject(n,{note:o}),e.cache!==void 0&&J(e.cache,{projectId:n}),p({targetKind:"project",targetId:n,cleared:true},e.makeMeta({syncPending:true})))}function pd(t,e){return t.registerTool("decision_clear",{description:yo,inputSchema:qy.shape},async n=>{let r=await Vy(n,e);return u(r)})}var ko=`Record agent memory of user judgment on a task or project \u2014 kind, reason, and an optional auto-expiry. Writes a \`decision-journal\` fenced block to the target's note (preserving any existing user prose), so future scans (e.g. project_health) can honor the decision instead of re-litigating it. Discriminates on \`targetKind\`: 'task' or 'project'. Do NOT use this for short-lived state \u2014 prefer waiting-on for follow-ups, or task_update for routine field changes. Returns { targetKind, targetId, decision } with the persisted entry. Side effects: writes the target's note via task_update / project_update; sets meta.syncPending = true. Example: { "targetKind": "project", "targetId": "abc", "decision": { "kind": "stall-is-intentional", "reason": "Strategic pause until Q3" } }`,Ky=z.object({kind:z.enum(Wr).describe("The kind of judgment recorded."),reason:z.string().min(1).describe("Human-readable reason for the decision."),until:z.string().datetime({offset:true}).optional().describe("Optional ISO-8601 auto-expiry. When set and in the past, the decision is treated as expired and downstream consumers re-surface the target.")}),Xy=z.object({targetKind:z.enum(["task","project"]).describe("Whether the decision attaches to a task or a project."),targetId:z.string().min(1).describe("ID of the task or project. Must match `targetKind` \u2014 agent-side validation, but the adapter call surfaces NotFound if the ID is wrong."),decision:Ky.describe("The decision payload. `recordedAt` is set automatically on write.")});async function Yy(t,e){let n=e.now?e.now():new Date,r={kind:t.decision.kind,reason:t.decision.reason,recordedAt:n.toISOString(),...t.decision.until!==void 0&&{until:t.decision.until}};if(t.targetKind==="task"){let s=h.of(t.targetId),c=await e.adapter.getTask(s),d=Hr(c.note,r);return await e.adapter.updateTask(s,{note:d}),e.cache!==void 0&&b(e.cache,{taskId:s,projectId:c.projectId}),p({targetKind:"task",targetId:s,decision:r},e.makeMeta({syncPending:true}))}let o=k.of(t.targetId),a=await e.adapter.getProject(o),i=Hr(a.note,r);return await e.adapter.updateProject(o,{note:i}),e.cache!==void 0&&J(e.cache,{projectId:o}),p({targetKind:"project",targetId:o,decision:r},e.makeMeta({syncPending:true}))}function ud(t,e){return t.registerTool("decision_record",{description:ko,inputSchema:Xy.shape},async n=>{let r=await Yy(n,e);return u(r)})}var vo=`Export OmniFocus data as OPML XML \u2014 a structured outline format OmniFocus can import. Do NOT use to export a single task; OPML scope is project-level or broader. Three scopes: 'project' (one project + its tasks), 'folder' (all projects in a folder), or 'all' (all active projects). Returns { opml, projectCount, taskCount } where opml is a complete XML string. Safe to call repeatedly; no side effects. Example: export_opml({ scope: "project", id: "abc123" }) Example: export_opml({ scope: "all" })`,Zy=z.object({scope:z.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z.string().optional().describe("Required when scope='project' (project ID from project_list) or scope='folder' (folder ID from folder_list). Omit for scope='all'.")});function Qy(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new y(`scope='${t.scope}' requires an id`,{details:{field:"id",scope:t.scope}});return t.scope==="project"?{kind:"project",id:k.of(t.id)}:{kind:"folder",id:W.of(t.id)}}async function ek(t,e){let n=Qy(t),r=await e.exportService.exportOpml(n),o=e.makeMeta();return p({opml:r.opml,projectCount:r.projectCount,taskCount:r.taskCount},o)}function md(t,e){return t.registerTool("export_opml",{description:vo,inputSchema:Zy.shape},async n=>{let r=await ek(n,e);return u(r)})}function q(t){return `'${t}'`}function G(t){return t.length>140?`${t.slice(0,137)}\u2026`:t}function fd(t){return G(`Created task ${q(t)}.`)}function gd(t){return G(`Completed task ${q(t)}.`)}function hd(t){return G(`Uncompleted task ${q(t)}.`)}function yd(t){return G(`Deleted task ${q(t)}.`)}function kd(t){return G(`Dropped task ${q(t)}.`)}function Id(t){return G(`Restored task ${q(t)} from dropped.`)}function vd(t,e){return G(`Moved task ${q(t)} to ${e}.`)}function Td(t){return G(`Duplicated task ${q(t)}.`)}function wd(t){return G(`Reordered task ${q(t)}.`)}function Sd(t){return G(`Converted task ${q(t)} to a project.`)}function bd(t){return G(`Set repetition rule on task ${q(t)}.`)}function jd(t){return G(`Cleared repetition rule on task ${q(t)}.`)}function _d(t,e){return G(`Set ${e} ${e===1?"alarm":"alarms"} on task ${q(t)}.`)}function Pd(t){return G(`Cleared alarms on task ${q(t)}.`)}function bt(t){return `Created ${t} task${t===1?"":"s"}.`}function Od(t){return `Updated ${t} task${t===1?"":"s"}.`}function xd(t){return `Completed ${t} task${t===1?"":"s"}.`}function Dd(t){return `Uncompleted ${t} task${t===1?"":"s"}.`}function Rd(t){return `Deleted ${t} task${t===1?"":"s"}.`}function Ad(t){return `Dropped ${t} task${t===1?"":"s"}.`}function Cd(t){return `Restored ${t} dropped task${t===1?"":"s"}.`}function Ed(t,e){return G(`Moved ${t} task${t===1?"":"s"} to ${e}.`)}function Md(t){return G(`Created project ${q(t)}.`)}function Nd(t){return G(`Updated project ${q(t)}.`)}function Fd(t){return G(`Deleted project ${q(t)}.`)}function Ud(t){return `Completed ${t} project${t===1?"":"s"}.`}function Ld(t){return `Dropped ${t} project${t===1?"":"s"}.`}function Bd(t){return G(`Created tag ${q(t)}.`)}function Ve(t){return G(`Updated tag ${q(t)}.`)}function Jd(t,e){return G(`Moved tag ${q(t)} to ${e}.`)}function $d(t){return G(`Created folder ${q(t)}.`)}function Wd(t){return G(`Updated folder ${q(t)}.`)}function Hd(t,e){return G(`Moved folder ${q(t)} to ${e}.`)}function Un(t,e){return G(`Set note on ${t} ${q(e)}.`)}function zd(t,e){return G(`Appended to note on ${t} ${q(e)}.`)}function Gd(){return "Completed project."}function qd(){return "Dropped project."}function Vd(t){return G(`Moved project to ${t}.`)}function Kd(){return "Deleted tag."}function Xd(){return "Deleted folder."}function Ln(){return "Marked project as reviewed."}function Yd(t){return t===null?"Cleared project review interval.":`Set project review interval to ${t} day${t===1?"":"s"}.`}function Zd(t){return t===null?"Cleared project next review date.":G(`Set project next review date to ${t}.`)}var wo='Import tasks from an OPML XML string into OmniFocus. Parses the OPML produced by export_opml and recreates the task hierarchy. Top-level <outline type="omnifocus:project"> elements are matched to existing projects by OmniFocus ID (for round-trip) then by name; unmatched project outlines land in the Inbox. LOSSY: due dates, defer dates, and flagged state are preserved; tags, notes, attachments, and repetition rules are silently dropped (not encoded in OPML). Do NOT use to export data; prefer export_opml for that. Returns { imported, tasks: [{ id, name }] } \u2014 imported is the count of tasks created and tasks pairs each new id with its display name (resolved via a single getTasksMany batch, no N+1) so the agent can confirm what landed without a follow-up read. Orphan ids (rare; the task was deleted between import and lookup) are dropped from the array. Writes to OmniFocus; call sync_trigger after import to propagate changes to other devices. Example: import_opml({ opml: "<opml>...</opml>" })',tk=z.object({opml:z.string().min(1).describe("Well-formed OPML XML string to import. Use the output of export_opml for a round-trip."),destinationProjectId:z.string().optional().describe("When set, all tasks are created in this project regardless of project headings in the OPML. Get the ID from project_list. Omit to match projects by ID/name from the OPML structure.")});async function nk(t,e){let n=await e.exportService.importOpml(t.opml,{...t.destinationProjectId!==void 0?{destinationProjectId:k.of(t.destinationProjectId)}:{}}),r=n.taskIds.length>0?await e.adapter.getTasksMany(n.taskIds):[],o=n.taskIds.map((i,s)=>{let c=r[s];return c==null?null:{id:String(i),name:c.name}}).filter(i=>i!==null),a=e.makeMeta({syncPending:true,humanReadableSummary:bt(n.imported)});return p({imported:n.imported,tasks:o},a)}function Qd(t,e){return t.registerTool("import_opml",{description:wo,inputSchema:tk.shape},async n=>{let r=await nk(n,e);return u(r)})}var So=`Export OmniFocus data as TaskPaper plain text. Three scopes: 'project' (one project + its tasks), 'folder' (all projects in a folder), or 'all' (all active projects). Export is lossy \u2014 HTML notes are downgraded to plain text; tag locations, attachments, and complex repetition rules are omitted. Lossiness warnings are returned in meta.warnings. Do NOT use to import data; prefer import_taskpaper for that. Returns { taskpaper, projectCount, taskCount }. Safe to call repeatedly; no side effects. Example: export_taskpaper({ scope: "project", id: "abc123" }) Example: export_taskpaper({ scope: "all" })`,bo=`Import tasks from TaskPaper text into OmniFocus. Parses '- Task name @tag @due(2026-01-15) @defer(2026-01-10) @flagged' lines. Indented subtasks become children of the nearest parent task. Project headings ('Project name:') map to existing OF projects by name \u2014 unrecognised headings fall back to inbox (warning emitted). Unknown @tags are created automatically. Do NOT use to export data; prefer export_taskpaper for that. Returns { tasks: [{ id, name }], warnings: string[] } \u2014 tasks pairs each new id with its display name (resolved via a single getTasksMany batch, no N+1) so the agent can confirm what landed without a follow-up read. Orphan ids (rare; deleted between import and lookup) are dropped from the array. Writes to OmniFocus; call sync_trigger to propagate changes to other devices. Example: import_taskpaper({ text: "- Buy milk @errands\\n- Call dentist @due(2026-05-01)" })`,rk=z.object({scope:z.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z.string().optional().describe("Required when scope='project' (project ID from project_list) or scope='folder' (folder ID from folder_list). Omit for scope='all'.")}),ok=z.object({text:z.string().min(1).describe("TaskPaper-formatted text to import. Each '- Task name' line becomes a task."),targetProjectId:z.string().optional().describe("When set, all top-level tasks are created in this project regardless of project headings in the text. Get the ID from project_list.")});function ak(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new y(`scope="${t.scope}" requires an id \u2014 provide the ${t.scope==="project"?"project":"folder"} ID`,{details:{field:"id",scope:t.scope}});return t.scope==="project"?{kind:"project",id:k.of(t.id)}:{kind:"folder",id:W.of(t.id)}}function el(t,e){t.registerTool("export_taskpaper",{description:So,inputSchema:rk.shape},async n=>{let r=ak(n),o=await e.exportService.exportTaskPaper(r);return u(p({taskpaper:o.taskpaper,projectCount:o.projectCount,taskCount:o.taskCount,warnings:o.warnings},e.makeMeta()))}),t.registerTool("import_taskpaper",{description:bo,inputSchema:ok.shape},async n=>u(await sk(n,e)));}async function sk(t,e){let n=await e.exportService.importTaskPaper(t.text,t.targetProjectId===void 0?void 0:k.of(t.targetProjectId)),r=n.created.length>0?await e.adapter.getTasksMany(n.created):[],o=n.created.map((a,i)=>{let s=r[i];return s==null?null:{id:String(a),name:s.name}}).filter(a=>a!==null);return p({tasks:o,warnings:n.warnings},e.makeMeta())}var jo=`Create a new folder in OmniFocus. Optionally nest it inside an existing parent folder (get IDs from folder_list). Do not use to move an existing folder; prefer folder_move instead. Returns the new folder's persistent ID. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_create({ name: "Work" }) Example: folder_create({ name: "Archive", parentId: "fld123" })`,_o=z.object({name:z.string().min(1).describe("Folder name. Must be non-empty."),parentId:W.schema.optional().describe("Parent folder ID. Omit for a root-level folder. Get from folder_list.")});async function ik(t,e){let n=await e.folderService.create({name:t.name,...t.parentId!==void 0?{parentId:t.parentId}:{}}),{folder:r}=await e.folderService.get(n.id);return p({folder:r},e.makeMeta({syncPending:true,humanReadableSummary:$d(t.name)}))}function nl(t,e){return t.registerTool("folder_create",{description:jo,inputSchema:_o.shape},async n=>{let r=await ik(n,e);return u(r)})}function Te(t){let e=new Date(t),n=e.getUTCFullYear(),r=String(e.getUTCMonth()+1).padStart(2,"0"),o=String(e.getUTCDate()).padStart(2,"0"),a=e.getUTCHours(),i=e.getUTCMinutes();return a===0&&i===0?`${n}-${r}-${o}`:`${n}-${r}-${o} ${String(a).padStart(2,"0")}:${String(i).padStart(2,"0")}`}async function _t(t,e){try{return (await t.getProject(e)).name}catch{return e}}async function Pt(t,e){try{return (await t.getTask(e)).name}catch{return e}}async function he(t,e){try{return (await t.getTag(e)).name}catch{return e}}async function Ke(t,e){try{return (await t.getFolder(e)).name}catch{return e}}var Po="Preview what folder_create would do without making any changes. Do NOT use to actually create a folder \u2014 use folder_create instead. Returns { description, plannedChanges } describing the folder that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function ck(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await Ke(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),r.push(`inside '${a}'`);}else r.push("at root");let o=`Would create folder ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function rl(t,e){return t.registerTool("folder_create_describe",{description:Po,inputSchema:_o.shape},async n=>{let r=await ck(n,e);return u(r)})}var Oo='Delete a folder from OmniFocus. By default returns ValidationError when the folder contains projects or subfolders. Pass cascade=true to orphan all direct projects (move to no folder) and recursively delete subfolders before deleting. IRREVERSIBLE \u2014 do not use to archive; prefer folder_update to rename instead. Get the folder ID from folder_list. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_delete({ id: "fld123" }) Example: folder_delete({ id: "fld123", cascade: true })',xo=z.object({id:W.schema.describe("Persistent folder ID to delete. Get from folder_list."),cascade:z.boolean().optional().describe("When true, orphan all direct projects and recursively delete subfolders before deleting. Default false \u2014 returns an error if the folder is non-empty.")});async function dk(t,e){return await e.folderService.delete(t.id,t.cascade??false),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Xd()}))}function al(t,e){return t.registerTool("folder_delete",{description:Oo,inputSchema:xo.shape},async n=>{let r=await dk(n,e);return u(r)})}var Do="Preview what folder_delete would do without making any changes. Do NOT use to actually delete a folder \u2014 use folder_delete instead. Returns { description, plannedChanges } describing the deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function lk(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getFolder(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=t.cascade===true?" (cascade: orphan projects, delete subfolders)":"",a=`Would delete folder '${r}' (id: ${t.id})${o}. IRREVERSIBLE.`;return p({description:a,plannedChanges:n},e.makeMeta())}function sl(t,e){return t.registerTool("folder_delete_describe",{description:Do,inputSchema:xo.shape},async n=>{let r=await lk(n,e);return u(r)})}var Ro='Fetch a single folder by its persistent ID, including project and subfolder counts. Do not use to list multiple folders; prefer folder_list instead. Returns folder details including name, parentId, projectCount, and subfolderCount. Safe to call repeatedly; no side effects. Example: folder_get({ id: "fld123" })',uk=z.object({id:W.schema.describe("Persistent folder ID. Get from folder_list. IDs are stable across renames.")});async function mk(t,e){let n=await e.folderService.get(t.id);return p({folder:n.folder},e.makeMeta({cacheHit:n.cacheHit}))}function il(t,e){return t.registerTool("folder_get",{description:Ro,inputSchema:uk.shape},async n=>{let r=await mk(n,e);return u(r)})}var Ao='List folders in OmniFocus, optionally filtered by parent folder. Do not use to fetch a single folder by ID; prefer folder_get instead. Returns a flat array with projectCount and subfolderCount per folder. Use parentId to walk the hierarchy one level at a time. Safe to call repeatedly; no side effects. Example: folder_list({}) Example: folder_list({ parentId: "fld123" })',gk=z.object({parentId:W.schema.optional().describe("Return only direct children of this folder. Get the ID from a previous folder_list call. Omit for root folders.")});async function hk(t,e){let n={...t.parentId!==void 0?{parentId:t.parentId}:{}},r=await e.folderService.list(n);return p({folders:r.folders},e.makeMeta({cacheHit:r.cacheHit}))}function cl(t,e){return t.registerTool("folder_list",{description:Ao,inputSchema:gk.shape},async n=>{let r=await hk(n,e);return u(r)})}var Co=`Move a folder to a new parent, or promote it to a root folder by passing parentId=null. Do not use to rename a folder; prefer folder_update instead. Get folder IDs from folder_list. Returns the updated folder's ID and new parentId on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_move({ id: "fld123", parentId: "fld456" }) Example: folder_move({ id: "fld123", parentId: null })`,Eo=z.object({id:W.schema.describe("Persistent ID of the folder to move. Get from folder_list."),parentId:W.schema.nullable().describe("New parent folder ID, or null to promote the folder to root level.")});async function kk(t,e){await e.folderService.move(t.id,t.parentId);let{folder:n}=await e.folderService.get(t.id);return p({folder:n},e.makeMeta({syncPending:true,humanReadableSummary:Hd(n.name,t.parentId!=null?"folder":"library root")}))}function dl(t,e){return t.registerTool("folder_move",{description:Co,inputSchema:Eo.shape},async n=>{let r=await kk(n,e);return u(r)})}var Mo="Preview what folder_move would do without making any changes. Do NOT use to actually move a folder \u2014 use folder_move instead. Returns { description, plannedChanges } describing the reparenting that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Ik(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getFolder(t.id)).name;}catch{}let o;if(t.parentId!==null){let i=await Ke(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`inside '${i}'`;}else n.push({field:"parentId",newValue:null}),o="root level";let a=`Would move folder '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function ll(t,e){return t.registerTool("folder_move_describe",{description:Mo,inputSchema:Eo.shape},async n=>{let r=await Ik(n,e);return u(r)})}var No='Rename a folder (partial patch \u2014 only supplied fields are changed). To move a folder use folder_move instead. Get the folder ID from folder_list. Returns the updated folder on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_update({ id: "fld123", name: "Personal" })',Fo=z.object({id:W.schema.describe("Persistent folder ID. Get from folder_list."),name:z.string().min(1).optional().describe("New folder name. Must be non-empty if supplied.")});async function vk(t,e){let{id:n,...r}=t;await e.folderService.update(n,{...r.name!==void 0?{name:r.name}:{}});let{folder:o}=await e.folderService.get(n);return p({folder:o},e.makeMeta({syncPending:true,humanReadableSummary:Wd(o.name)}))}function ul(t,e){return t.registerTool("folder_update",{description:No,inputSchema:Fo.shape},async n=>{let r=await vk(n,e);return u(r)})}var Uo="Preview what folder_update would do without making any changes. Do NOT use to actually update a folder \u2014 use folder_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Tk(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getFolder(t.id);o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update folder '${o}': ${r.join(", ")}.`:`Would update folder '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function ml(t,e){return t.registerTool("folder_update_describe",{description:Uo,inputSchema:Fo.shape},async n=>{let r=await Tk(n,e);return u(r)})}var Lo=`Get forecast-view tasks from OmniFocus grouped by category: overdue, dueToday, deferredToday, flagged. Use this for 'what's on my plate today' or multi-day planning queries. Do NOT use to list all tasks across all projects; prefer task_list instead. Supply date (ISO-8601 or shortcut like 'today', 'tomorrow') and days (1\u20137) for the ergonomic interface, or use from/to for exact ISO-8601 ranges. All include flags default to true; set to false to omit a category. When days > 1, response also includes byDate[] grouping tasks per calendar day. Returns { overdue[], dueToday[], deferredToday[], flagged[], byDate? }; safe to call repeatedly; no side effects. Example: forecast_get({ date: "today" }) Example: forecast_get({ date: "today", days: 3, includeFlagged: false })`,wk=z.object({date:Le().optional().describe("Anchor date for the forecast (ISO-8601 or relative shortcut: today, tomorrow, yesterday, this-week, next-week). Mutually exclusive with from/to. Defaults to today."),days:z.number().int().min(1).max(7).optional().default(1).describe("Number of days to cover (1\u20137). Default 1. When > 1, byDate[] is included in the response."),from:Le().optional().describe("Start of date range (ISO-8601 or relative shortcut like 'today'). Use date/days for the ergonomic interface instead. Defaults to start of today."),to:Le().optional().describe("End of date range (ISO-8601 or relative shortcut like 'today'). Use date/days for the ergonomic interface instead. Defaults to end of today."),includeOverdue:z.boolean().optional().default(true).describe("Include tasks overdue before the start of the range. Default true."),includeDeferred:z.boolean().optional().default(true).describe("Include tasks whose defer date falls within the range. Default true."),includeFlagged:z.boolean().optional().default(true).describe("Include all flagged incomplete tasks. Default true.")});function Sk(t){let e;if(qt(t))e=Vt(t);else if(Kt(t))e=t;else throw new y(`Invalid date: "${t}". Expected ISO-8601 or a relative shortcut.`,{details:{field:"date",value:t}});let n=new Date(e);return n.setHours(0,0,0,0),n}function bk(t){let e=t.date!==void 0,n=t.from!==void 0||t.to!==void 0;if(e&&n)throw new y("date and from/to are mutually exclusive \u2014 use one or the other.",{suggestion:"Remove from/to when using date, or remove date when using from/to.",details:{field:"date|from|to"}});let r=t.days??1;if(e){let s=Sk(t.date),c=new Date(s);return c.setDate(c.getDate()+r-1),c.setHours(23,59,59,999),{from:s.toISOString(),to:c.toISOString(),days:r}}if(n){let s=new Date,c=new Date(s);c.setHours(0,0,0,0);let d=new Date(s);return d.setHours(23,59,59,999),{from:t.from??c.toISOString(),to:t.to??d.toISOString(),days:r}}let o=new Date,a=new Date(o);a.setHours(0,0,0,0);let i=new Date(a);return i.setDate(i.getDate()+r-1),i.setHours(23,59,59,999),{from:a.toISOString(),to:i.toISOString(),days:r}}function jk(t){let e=new Map;for(let n of t){if(!n.dueDate)continue;let r=n.dueDate.slice(0,10),o=e.get(r)??[];o.push(n),e.set(r,o);}return [...e.entries()].sort(([n],[r])=>n.localeCompare(r)).map(([n,r])=>({date:n,tasks:r}))}async function _k(t,e){let{from:n,to:r,days:o}=bk(t),a=await e.forecastService.get({from:n,to:r,includeOverdue:t.includeOverdue,includeDeferred:t.includeDeferred,includeFlagged:t.includeFlagged}),i=e.makeMeta({cacheHit:a.cacheHit}),s={overdue:a.overdue,dueToday:a.dueToday,deferredToday:a.deferredToday,flagged:a.flagged};return o>1&&(s.byDate=jk(a.dueToday)),p(s,i)}function fl(t,e){return t.registerTool("forecast_get",{description:Lo,inputSchema:wk.shape},async n=>{let r=await _k(n,e);return u(r)})}var Bo="Read the OmniFocus forecast-tag preference: the single tag whose tasks always appear on the Forecast view alongside dated items. Use when the agent needs to answer 'what tag is the user using as their daily agenda?' or to confirm a tag before composing follow-up queries against it. Do NOT use to list tags in general \u2014 prefer tag_list. Takes no arguments. Returns { tagId: string | null, name: string | null } \u2014 name is the tag's display name (or null when tagId is null or the tag has been deleted) so the agent can describe the forecast tag without a follow-up tag_get. Read-only; no side effects; safe to retry. Backed by OmniJS Database.forecastTag.Example: forecast_get_tag()",Ok=z.object({});async function xk(t,e){let n=await e.forecastService.getForecastTag();return p(n,e.makeMeta())}function gl(t,e){return t.registerTool("forecast_get_tag",{description:Bo,inputSchema:Ok.shape},async n=>{let r=await xk(n,e);return u(r)})}var Jo=`Pack today's forecast tasks into a time budget. Use when the user asks 'I have N hours; what should I do?' or wants a focused subset of forecast tasks that fit a limited window. Do NOT use for the full forecast \u2014 prefer forecast_get for that. Do NOT use to schedule work across multiple days \u2014 pass scope='next7' as a hint, but the pack is still budget-bounded; for true multi-day planning use forecast_get with days>1 and let the agent compose. Pass budgetMinutes (1\u20131440) and optional filter { tagIds?, scope? }; scope is 'today' (default) or 'next7'. Returns { selected[], totalMinutes, skipped[] }. selected[] are the picks in execution order (flagged first, then dueDate ascending, then stable by ID). skipped[] surfaces tasks the agent should ask the user about: { reason: 'no-estimate' } means the task has no estimatedMinutes so couldn't be packed; { reason: 'exceeds-budget' } means it would have fit individually but was bumped by earlier higher-priority picks. Read-only; no side effects; safe to retry. Pack algorithm is greedy \u2014 predictable and explainable beats optimal-by-1-minute. Example: forecast_pack({ budgetMinutes: 120 }) Example: forecast_pack({ budgetMinutes: 240, filter: { tagIds: ["tag123"], scope: "today" } })`,Dk=z.object({budgetMinutes:z.number().int().min(1).max(1440).describe("Time budget in minutes (1\u20131440 \u2014 i.e. up to 24 hours). Selected tasks' estimatedMinutes will sum to \u2264 this value."),filter:z.object({tagIds:z.array(S.schema).optional().describe("Restrict to tasks bearing at least one of these tag IDs. Empty array or omitted means no tag filter."),scope:z.enum(["today","next7"]).optional().default("today").describe("Forecast horizon to pack from: 'today' (overdue + dueToday + flagged) or 'next7' (everything in the next 7 days). Default 'today'.")}).optional().describe("Optional filter narrowing the candidate set before packing.")});function Rk(t,e){if(t.flagged!==e.flagged)return t.flagged?-1:1;let n=t.dueDate,r=e.dueDate;if(n!==r){if(n===null)return 1;if(r===null||n<r)return -1;if(n>r)return 1}return t.id<e.id?-1:t.id>e.id?1:0}function Ak(t,e,n){let r=n?.tagIds,a=[...r&&r.length>0?t.filter(d=>d.tagIds.some(l=>r.includes(l))):t].sort(Rk),i=[],s=[],c=0;for(let d of a){if(d.estimatedMinutes===null){s.push({taskId:d.id,name:d.name,estimatedMinutes:null,reason:"no-estimate"});continue}c+d.estimatedMinutes<=e?(i.push({taskId:d.id,name:d.name,estimatedMinutes:d.estimatedMinutes,flagged:d.flagged,dueDate:d.dueDate}),c+=d.estimatedMinutes):s.push({taskId:d.id,name:d.name,estimatedMinutes:d.estimatedMinutes,reason:"exceeds-budget"});}return {selected:i,totalMinutes:c,skipped:s}}function Ck(t){let e=new Date,n=new Date(e);n.setHours(0,0,0,0);let r=new Date(n);return r.setDate(r.getDate()+(t==="next7"?6:0)),r.setHours(23,59,59,999),{from:n.toISOString(),to:r.toISOString()}}function Ek(t){let e=new Set,n=[];for(let r of [t.overdue,t.dueToday,t.deferredToday,t.flagged])for(let o of r)e.has(o.id)||(e.add(o.id),n.push(o));return n}async function Mk(t,e){let n=t.filter?.scope??"today",{from:r,to:o}=Ck(n),a=await e.forecastService.get({from:r,to:o,includeOverdue:true,includeDeferred:n==="next7",includeFlagged:true}),i=Ek(a),s=Ak(i,t.budgetMinutes,t.filter),c=e.makeMeta({cacheHit:a.cacheHit});return p(s,c)}function hl(t,e){return t.registerTool("forecast_pack",{description:Jo,inputSchema:Dk.shape},async n=>{let r=await Mk(n,e);return u(r)})}var Wo=`Set or clear the OmniFocus forecast-tag preference. Use when the user wants to designate (or change) the tag whose tasks should always appear on Forecast \u2014 common during onboarding flows or context switches ('use @today as my agenda'). Do NOT use to add tags to a task \u2014 prefer task_update. Pass tagId as a TagId string to set, or null to clear. Returns { tagId: string | null, name: string | null } echoing what was applied \u2014 name is paired with the tag id so the agent can describe the change without a follow-up tag_get. Errors: NOT_FOUND when the supplied tagId does not exist. Side effects: mutation; invalidates the forecast read cache. Backed by OmniJS Database.forecastTag. Example: forecast_set_tag({ tagId: "tag123" }) Example: forecast_set_tag({ tagId: null })`,Nk=z.object({tagId:z.union([S.schema,z.null()]).describe("The TagId to designate as the forecast tag, or null to clear the preference. Use null to remove the forecast-tag binding entirely.")});async function Fk(t,e){let n=await e.forecastService.setForecastTag(t.tagId);return e.cache.invalidate("forecast:*"),p(n,e.makeMeta())}function yl(t,e){return t.registerTool("forecast_set_tag",{description:Wo,inputSchema:Nk.shape},async n=>{let r=await Fk(n,e);return u(r)})}var Ho=`Append text to the plain-text note on a task or project. Adds a newline between existing content and the new text unless the note is empty. Do not use to replace the note entirely; prefer note_set instead. Returns { updated: true, id, targetKind, name, note } \u2014 name is the parent task/project's display name (captured from the same read that fetched the existing note) so the agent can describe the change without a follow-up read; note is the full content after appending. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices. Example: note_append({ targetKind: "task", id: "abc123", text: "Follow up next week" })`,Uk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to append to."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),text:z.string().min(1).describe("Text to append. A newline separator is inserted before the text if a note exists.")});async function Lk(t,e){let n,r;if(t.targetKind==="task"){let a=await e.adapter.getTask(h.of(t.id));n=a.note,r=a.name;}else {let a=await e.adapter.getProject(k.of(t.id));n=a.note,r=a.name;}let o=n?`${n}
|
|
209
|
+
${t.text}`:t.text;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{note:o}),e.cache!==void 0&&b(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(k.of(t.id),{note:o}),e.cache!==void 0&&J(e.cache,{projectId:k.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:r,note:o},e.makeMeta({syncPending:true,humanReadableSummary:zd(t.targetKind,r)}))}function kl(t,e){return t.registerTool("note_append",{description:Ho,inputSchema:Uk.shape},async n=>{let r=await Lk(n,e);return u(r)})}var Go=`Read the plain-text note from a task or project. Do not use when formatting fidelity matters; prefer note_get_html instead. Returns { note } \u2014 a string (may be empty) or null when no note exists. Set targetKind to 'task' and provide a task ID, or 'project' and a project ID. Safe to call repeatedly; no side effects. Example: note_get({ targetKind: "task", id: "abc123" })`,Bk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to read."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function Jk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).note:(await e.adapter.getProject(k.of(t.id))).note;return p({note:n??null},e.makeMeta())}function Il(t,e){return t.registerTool("note_get",{description:Go,inputSchema:Bk.shape},async n=>{let r=await Jk(n,e);return u(r)})}var Vo=`Read the HTML fragment from a task or project note. Returns { noteHtml } \u2014 an HTML string (may be empty) or null when no note exists. Set targetKind to 'task' and provide a task ID, or 'project' and a project ID. For plain-text access without formatting, use note_get instead. Safe to call repeatedly; no side effects. Example: note_get_html({ targetKind: "task", id: "abc123" })`,$k=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to read."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function Wk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).noteHtml:(await e.adapter.getProject(k.of(t.id))).noteHtml;return p({noteHtml:n??null},e.makeMeta())}function vl(t,e){return t.registerTool("note_get_html",{description:Vo,inputSchema:$k.shape},async n=>{let r=await Wk(n,e);return u(r)})}var Ko=`Replace the plain-text note on a task or project. Overwrites the existing note entirely. Pass note: null to clear the note. To add text without overwriting use note_append instead. Returns { updated: true, id, targetKind, name, note } \u2014 name is the parent task/project's display name (pre-fetched so the response describes the change without a follow-up read); note echoes back the final content after writing (or null if cleared). Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices. Example: note_set({ targetKind: "task", id: "abc123", note: "Check with Alice first" })`,Hk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to set."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),note:z.string().nullable().describe("New note text. Pass null to clear the note entirely.")});async function zk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(k.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{note:t.note}),e.cache!==void 0&&b(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(k.of(t.id),{note:t.note}),e.cache!==void 0&&J(e.cache,{projectId:k.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,note:t.note},e.makeMeta({syncPending:true,humanReadableSummary:Un(t.targetKind,n)}))}function Tl(t,e){return t.registerTool("note_set",{description:Ko,inputSchema:Hk.shape},async n=>{let r=await zk(n,e);return u(r)})}var Xo=`Replace the HTML fragment note on a task or project. Overwrites the existing note entirely with the provided HTML. OmniFocus preserves its supported HTML subset (bold, italic, links, lists, inline images); unsupported elements may be stripped. Pass noteHtml: null to clear the note. For plain-text writes use note_set instead. Returns { updated: true, id, targetKind, name, noteHtml } \u2014 name is the parent task/project's display name (pre-fetched so the response describes the change without a follow-up read); noteHtml echoes back the requested HTML (or null if cleared). Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices. Example: note_set_html({ targetKind: "task", id: "abc123", noteHtml: "<b>Priority:</b> high" })`,Gk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to set."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),noteHtml:z.string().nullable().describe("HTML fragment to set as the note. Pass null to clear the note entirely.")});async function qk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(k.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&b(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(k.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&J(e.cache,{projectId:k.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,noteHtml:t.noteHtml},e.makeMeta({syncPending:true,humanReadableSummary:Un(t.targetKind,n)}))}function wl(t,e){return t.registerTool("note_set_html",{description:Xo,inputSchema:Gk.shape},async n=>{let r=await qk(n,e);return u(r)})}function Kk(){let t=fileURLToPath(import.meta.url);return Zi.resolve(Zi.dirname(t),"../../../reports/mutation/mutation.json")}function Xk(t){let e={Killed:0,Survived:0,Timeout:0,NoCoverage:0},n=t.files;if(!n)return e;for(let r of Object.values(n))for(let o of r.mutants??[]){let a=o.status;a==="Killed"?e.Killed++:a==="Survived"?e.Survived++:a==="Timeout"?e.Timeout++:a==="NoCoverage"&&e.NoCoverage++;}return e}function bl(t={}){let e=t.reportPath??Kk();if(!(t.existsSync??dt.existsSync)(e))return null;let r=t.readFile??(g=>dt.readFileSync(g,"utf8")),o=t.stat??dt.statSync,a;try{a=JSON.parse(r(e));}catch{return null}let{Killed:i,Survived:s,Timeout:c,NoCoverage:d}=Xk(a),l=i+s+c+d;if(l===0)return null;let m=(i+c)/l*100,f=o(e).mtime.toISOString();return {score:m,lastRunAt:f}}var Zo="Return a health snapshot of the running omnifocus-mcp server. Do NOT use this to read OmniFocus data \u2014 prefer task_list, project_list, sync_status, etc. Returns { uptimeMs, ofRunning, lastSync, calendarAccess, mutation, cache, circuits, queueDepth }. uptimeMs is the milliseconds since the server process started. circuits lists each circuit-breaker name and state (closed/open/half_open). lastSync mirrors sync_status data; null if getLastSync throws. calendarAccess reports the macOS Calendar bridge state \u2014 { available, permission } where available is true when the Swift binary is callable and permission is the live EventKit authorization status (granted | denied | restricted | not-determined), or 'unknown' when available is false. Read-only \u2014 does NOT trigger the macOS Calendar TCC prompt. mutation surfaces Stryker calibration freshness \u2014 { score, lastRunAt } where score is the latest mutation-testing score (0\u2013100) per ADR-0017 and lastRunAt is the report's mtime. Returns null when no report file is present (the published npm tarball ships without one). Read-only; no side effects. Example: internal_status()",Zk=z.object({});async function Qk(t,e){let n=Date.now()-e.startedAt,r=null;try{let c=await e.adapter.getLastSync();r={lastSyncAt:c.lastSyncAt,inFlight:c.inFlight};}catch{r=null;}let o=e.circuitRegistry.snapshot(),a=null;try{a=await(e.probeCalendarAccess??On)();}catch{a=null;}let i=null;try{i=(e.probeMutationScore??bl)();}catch{i=null;}return p({uptimeMs:n,ofRunning:true,lastSync:r,calendarAccess:a,mutation:i,cache:null,circuits:o,queueDepth:null},e.makeMeta())}function jl(t,e){return t.registerTool("internal_status",{description:Zo,inputSchema:Zk.shape},async n=>{let r=await Qk(n,e);return u(r)})}var _l=["actionAvailability","actionStatus","actionHasAllOfTags","actionHasAnyOfTags","actionHasNoProject","actionHasDueDate","actionHasDeferDate","actionIsLeaf","actionIsProject","actionMatchingSearch","actionWithinFocus"];function eI(t){let e=0;for(let n of _l)t[n]!==void 0&&(e+=1);return e}var Qo=z.array(z.string().min(1,"id must be non-empty")).min(1,"list must contain at least one id"),tI=z.enum(["all","any","none"]),nI=z.object({actionAvailability:z.enum(["available","remaining","completed","dropped","firstAvailable"]).optional(),actionStatus:z.enum(["flagged","due"]).optional(),actionHasAllOfTags:Qo.optional(),actionHasAnyOfTags:Qo.optional(),actionHasNoProject:z.boolean().optional(),actionHasDueDate:z.boolean().optional(),actionHasDeferDate:z.boolean().optional(),actionIsLeaf:z.boolean().optional(),actionIsProject:z.boolean().optional(),actionMatchingSearch:z.array(z.string().min(1)).min(1).optional(),actionWithinFocus:Qo.optional()}).strict(),rI=nI.superRefine((t,e)=>{let n=eI(t);if(n>1){let r=_l.filter(o=>t[o]!==void 0);e.addIssue({code:z.ZodIssueCode.custom,message:`Rule atom must set at most one action predicate; got ${n} (${r.join(", ")}). Combine predicates by wrapping atoms in a RuleAggregate with aggregateType: "all" / "any" / "none" instead.`,path:[]});}}),Xe=z.lazy(()=>z.union([z.object({aggregateType:tI,aggregateRules:z.array(Xe)}).strict(),z.object({disabledRule:Xe}).strict(),rI]));var ea='Create a new custom OmniFocus perspective with the given name, optional rule tree, optional aggregation, and optional icon color. The shell is created via JXA `make` (the only supported create path) and rules + aggregation + iconColor are written via OmniJS in the same transport hop \u2014 if rule writing throws, the shell is rolled back so the database is never left with a half-configured perspective. Use BEFORE composing complex authoring flows: pair with `perspective_get` to clone an existing perspective, or with `perspective_delete` to replace one. Do NOT use to update an existing perspective \u2014 prefer `perspective_update` (slice C) when it lands. rules is the same shape `perspective_get` returns (atom | aggregate | disabled wrapper) \u2014 round-trips are lossless. Each rule atom may set at most one action* predicate; combine predicates by wrapping atoms in a RuleAggregate with aggregateType all/any/none. Tag-id and focus-id arrays must be non-empty with non-empty entries. Returns { id } \u2014 the persistent identifier of the new custom perspective. Side effects: creates a perspective in OmniFocus; invalidates the perspective cache; sets meta.syncPending = true. Custom perspectives require OmniFocus Pro \u2014 without it, the adapter throws FeatureRequiresPro. Example: perspective_create({ name: "Today\'s plate", aggregation: "all", rules: [{ actionStatus: "flagged" }] })',oI=z.enum(["all","any","none"]),aI=z.object({r:z.number().min(0).max(1).describe("Red channel as a [0, 1] float."),g:z.number().min(0).max(1).describe("Green channel as a [0, 1] float."),b:z.number().min(0).max(1).describe("Blue channel as a [0, 1] float."),a:z.number().min(0).max(1).describe("Alpha channel as a [0, 1] float; 1 is opaque.")}).strict(),sI=z.object({name:z.string().min(1).describe("Display name for the new perspective. Must be non-empty and unique within the OmniFocus database \u2014 duplicate names are rejected with VALIDATION_ERROR."),aggregation:oI.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z.array(Xe).optional().describe("Top-level rule list. Empty array means 'show everything' (the default for fresh perspectives). Each rule is an atom (single action* predicate), an aggregate (compound rule with aggregateType + aggregateRules), or a disabled wrapper around either."),iconColor:aI.optional().describe("Custom icon color in [0, 1] floats { r, g, b, a }. Omit for the OmniFocus-assigned default.")});async function iI(t,e){let n=await e.adapter.createCustomPerspective({name:t.name,...t.aggregation!==void 0&&{aggregation:t.aggregation},...t.rules!==void 0&&{rules:t.rules},...t.iconColor!==void 0&&{iconColor:t.iconColor}});return e.cache!==void 0&&e.cache.invalidate("perspective:*"),p({id:n,name:t.name},e.makeMeta({syncPending:true,humanReadableSummary:`Created custom perspective "${t.name}".`}))}function Pl(t,e){return t.registerTool("perspective_create",{description:ea,inputSchema:sI.shape},async n=>{let r=await iI(n,e);return u(r)})}var ta='Delete a custom OmniFocus perspective by id. Use when a perspective is no longer needed \u2014 e.g. cleaning up after a templated workflow, or rotating out a stale view. Do not use on built-in perspectives (inbox, projects, tags, forecast, flagged, nearby, review) \u2014 they cannot be deleted; the call returns a validation error. Custom perspectives require OmniFocus Pro; without it the call returns OF_FEATURE_REQUIRES_PRO. Deletion is permanent \u2014 there is no undo for perspective removal in OmniFocus, so confirm with the user before invoking on a perspective they may want to keep. Recommend a sync_trigger after deletion so other devices observe the change. Returns { id } echoing the deleted identifier. Side effects: writes to OmniFocus (removes the perspective from the document), sets meta.syncPending = true. Example: { "perspectiveId": "fOpKrtZBLaZ" } \u2192 { id: "fOpKrtZBLaZ" }.',cI=z.object({perspectiveId:z.string().min(1).describe('Identifier of the custom perspective to delete. Obtain from perspective_list (look for kind: "custom"). Built-in ids are rejected with a validation error \u2014 built-ins are immutable.')});async function dI(t,e){await e.perspectiveService.delete(t.perspectiveId),e.cache.invalidate("perspective:*");let n=e.makeMeta({cacheHit:false});return p({id:t.perspectiveId},n)}function xl(t,e){return t.registerTool("perspective_delete",{description:ta,inputSchema:cI.shape},async n=>{let r=await dI(n,e);return u(r)})}var na=`Evaluate an OmniFocus perspective and return its task list. Accepts both built-in ids (inbox, projects, tags, forecast, flagged, nearby, review) and custom-perspective ids obtained from perspective_list \u2014 the tool selects the correct transport internally (JXA for built-in, OmniJS for custom). Custom perspectives require OmniFocus Pro; otherwise returns an error with code OF_FEATURE_REQUIRES_PRO. Returns { tasks: Task[] }. For 'review', returns [] \u2014 use review_list_due instead. For 'nearby', returns [] (location unavailable). No side effects; read-only. Example: perspective_evaluate({ perspectiveId: "flagged" }) Example: perspective_evaluate({ perspectiveId: "cust-abc123" })`,lI=z.object({perspectiveId:z.string().min(1).describe("OmniFocus perspective id. Accepts a built-in id (inbox, projects, tags, forecast, flagged, nearby, review) or a custom-perspective id from perspective_list (kind: custom).")});async function pI(t,e){let n=await e.perspectiveService.evaluate(t.perspectiveId),r=e.makeMeta({cacheHit:n.cacheHit});return p({tasks:n.tasks},r)}function Rl(t,e){return t.registerTool("perspective_evaluate",{description:na,inputSchema:lI.shape},async n=>{let r=await pI(n,e);return u(r)})}var oa="Preview a *proposed* OmniFocus perspective rule tree without persisting it. Creates a temporary perspective with the supplied rules, evaluates it, and always deletes the temp perspective inside one OmniJS execution. Pairs with perspective_create for the propose-then-save flow used by the perspective-author prompt: propose rules \u2192 preview matched tasks via this tool \u2192 commit via perspective_create. Custom perspectives require OmniFocus Pro; otherwise returns OF_FEATURE_REQUIRES_PRO. Do NOT use to evaluate a *saved* perspective \u2014 use perspective_evaluate. Returns { tasks: Task[] }. Side effects: creates and immediately deletes a sentinel-named temp perspective inside one OmniJS execution; the database state is unchanged after the call returns. Example: perspective_evaluate_dry_run({ aggregation: 'all', rules: [{ actionStatus: 'flagged' }] })",uI=z.enum(["all","any","none"]),mI=z.object({aggregation:uI.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z.array(Xe).describe("Top-level rule list to evaluate. Empty array means 'show everything' (matches every available task). Each rule is an atom (single action* predicate), an aggregate (compound rule with aggregateType + aggregateRules), or a disabled wrapper around either.")});async function fI(t,e){let n=await e.perspectiveService.evaluateRules(t.rules,t.aggregation);return p({tasks:n.tasks},e.makeMeta({cacheHit:false}))}function Al(t,e){return t.registerTool("perspective_evaluate_dry_run",{description:oa,inputSchema:mI.shape},async n=>{let r=await fI(n,e);return u(r)})}var aa='Read the full configuration of a custom OmniFocus perspective \u2014 name, top-level rule aggregation (all/any/none), the structured rule tree, and icon color (when set). Use to introspect what a perspective filters on before evaluating it, or as a building block for cloning / duplicating perspectives. Do not use on built-in perspectives (inbox, projects, tags, forecast, flagged, nearby, review) \u2014 they have no rule tree and the call returns a validation error. Use perspective_list instead to enumerate available perspectives. Custom perspectives require OmniFocus Pro; without it the call returns OF_FEATURE_REQUIRES_PRO. Returns { perspective: { id, name, aggregation, rules, iconColor } }. Safe to call repeatedly; no side effects, no writes. Example: { "perspectiveId": "fOpKrtZBLaZ" } \u2192 { perspective: { id, name: "Daily Triage", aggregation: "any", rules: [...], iconColor: { r: 0.2, g: 0.5, b: 0.9, a: 1 } } }.',gI=z.object({perspectiveId:z.string().min(1).describe('Identifier of the custom perspective to read. Obtain from perspective_list (look for kind: "custom"). Built-in ids (inbox, projects, tags, forecast, flagged, nearby, review) are rejected with a validation error \u2014 built-ins have no rule tree.')});async function hI(t,e){let n=await e.perspectiveService.get(t.perspectiveId),r=e.makeMeta({cacheHit:false});return p({perspective:n},r)}function El(t,e){return t.registerTool("perspective_get",{description:aa,inputSchema:gI.shape},async n=>{let r=await hI(n,e);return u(r)})}var sa="List all perspectives in OmniFocus \u2014 both built-in (Inbox, Projects, Tags, Forecast, Flagged, Nearby, Review) and custom (OmniFocus Pro). Do not use to evaluate a perspective; prefer perspective_evaluate for that. Returns each perspective's id, name, kind (builtin|custom), and requiresPro flag. Safe to call repeatedly; no side effects, no writes. Example: perspective_list()",kI=z.object({});async function II(t,e){let n=await e.perspectiveService.list(),r=e.makeMeta({cacheHit:n.cacheHit});return p({perspectives:n.perspectives},r)}function Ml(t,e){return t.registerTool("perspective_list",{description:sa,inputSchema:kI.shape},async n=>{let r=await II(n,e);return u(r)})}var st=["inbox","projects","tags","forecast","flagged","nearby","review"];z.object({id:z.string().min(1),name:z.string().min(1),kind:z.enum(["builtin","custom"]),requiresPro:z.boolean(),icon:z.string().nullable()});var Nl=z.enum(["all","any","none"]),vI=z.object({actionAvailability:z.enum(["available","remaining","completed","dropped","firstAvailable"]).optional(),actionStatus:z.enum(["flagged","due"]).optional(),actionHasAllOfTags:z.array(z.string()).optional(),actionHasAnyOfTags:z.array(z.string()).optional(),actionHasNoProject:z.boolean().optional(),actionHasDueDate:z.boolean().optional(),actionHasDeferDate:z.boolean().optional(),actionIsLeaf:z.boolean().optional(),actionIsProject:z.boolean().optional(),actionMatchingSearch:z.array(z.string()).optional(),actionWithinFocus:z.array(z.string()).optional(),unknown:z.record(z.string(),z.unknown()).optional()}),ia=z.lazy(()=>z.union([z.object({aggregateType:Nl,aggregateRules:z.array(ia)}),z.object({disabledRule:ia}),vI])),TI=z.object({r:z.number().min(0).max(1),g:z.number().min(0).max(1),b:z.number().min(0).max(1),a:z.number().min(0).max(1)});z.object({id:z.string().min(1),name:z.string().min(1),aggregation:Nl,rules:z.array(ia),iconColor:TI.nullable()});var ca='Partial-patch update of a custom OmniFocus perspective. Only fields present in the input are written \u2014 omitting a field leaves the existing value unchanged. Passing iconColor: null clears the custom color back to the OmniFocus default; passing rules: [] clears the rule tree to \'show everything\'. Use to rename a perspective, retune its rule tree, swap the aggregation, or recolor the icon. Do NOT use to create a new perspective (prefer `perspective_create`) or to alter built-in perspectives \u2014 built-in ids (Inbox, Forecast, Flagged, Projects, Tags, Nearby, Review) are rejected with VALIDATION_ERROR. rules is the same shape `perspective_get` returns (atom | aggregate | disabled wrapper) \u2014 round-trips are lossless. Each rule atom may set at most one action* predicate; combine predicates by wrapping atoms in a RuleAggregate with aggregateType all/any/none. Returns { id } \u2014 the persistent identifier of the patched perspective. Side effects: writes to OmniFocus; invalidates the perspective cache; sets meta.syncPending = true. Custom perspectives require OmniFocus Pro \u2014 without it, the adapter throws FeatureRequiresPro. Example: perspective_update({ perspectiveId: "abc123", name: "Today\'s plate", aggregation: "all" }) Example: perspective_update({ perspectiveId: "abc123", iconColor: null })',wI=z.enum(["all","any","none"]),SI=z.object({r:z.number().min(0).max(1).describe("Red channel as a [0, 1] float."),g:z.number().min(0).max(1).describe("Green channel as a [0, 1] float."),b:z.number().min(0).max(1).describe("Blue channel as a [0, 1] float."),a:z.number().min(0).max(1).describe("Alpha channel as a [0, 1] float; 1 is opaque.")}).strict(),bI=z.object({perspectiveId:z.string().min(1).describe("Persistent identifier of the custom perspective to patch. Get from `perspective_list`. Built-in perspective ids are rejected with VALIDATION_ERROR."),name:z.string().min(1).optional().describe("New display name. Must be non-empty when provided. OmniFocus rejects duplicate names."),aggregation:wI.optional().describe('New top-level rule aggregation. One of "all", "any", "none".'),rules:z.array(Xe).optional().describe("New top-level rule list. Empty array clears the rule tree to 'show everything'. Each rule is an atom (single action* predicate), an aggregate (compound rule with aggregateType + aggregateRules), or a disabled wrapper around either."),iconColor:z.union([SI,z.null()]).optional().describe("New custom icon color, or null to clear back to the OmniFocus default. Omit to leave the existing color unchanged.")});async function jI(t,e){if(st.includes(t.perspectiveId))throw new y(`Built-in perspectives cannot be updated: ${t.perspectiveId}. Built-in ids: ${st.join(", ")}.`,{details:{field:"perspectiveId",id:t.perspectiveId,kind:"builtin"},suggestion:"Use perspective_list to find a custom perspective id, or call perspective_create to create a new one."});return await e.adapter.updateCustomPerspective(t.perspectiveId,{...t.name!==void 0&&{name:t.name},...t.aggregation!==void 0&&{aggregation:t.aggregation},...t.rules!==void 0&&{rules:t.rules},...t.iconColor!==void 0&&{iconColor:t.iconColor}}),e.cache!==void 0&&e.cache.invalidate("perspective:*"),p({id:t.perspectiveId},e.makeMeta({syncPending:true,humanReadableSummary:`Updated custom perspective ${t.perspectiveId}.`}))}function Fl(t,e){return t.registerTool("perspective_update",{description:ca,inputSchema:bI.shape},async n=>{let r=await jI(n,e);return u(r)})}var Ot=class{adapter;constructor({adapter:e}){this.adapter=e;}async invoke(e){let n={identifier:e.identifier,...e.arg!==void 0?{arg:e.arg}:{}};return {result:(await this.adapter.pluginInvoke(n)).result}}};var la='Invoke a named Omni Automation plug-in action in OmniFocus. Use this when you need to run a specific installed plug-in \u2014 not for built-in OmniFocus operations. Do NOT use to run arbitrary JavaScript; for raw scripting use run_omnijs_script (requires opt-in env var). `identifier` is the plug-in\'s bundle ID (e.g. `"com.example.my-plugin"`). `arg` is an optional JSON-serialisable value passed to the plug-in action as Action.args[0]. Returns { result } where result is the plug-in\'s return value (arbitrary JSON). Throws NotFound if the plug-in is not installed. Side effects: plug-in may mutate OmniFocus data; call sync_trigger if you need changes on other devices. Example: plugin_invoke({ identifier: "com.example.my-plugin" }) Example: plugin_invoke({ identifier: "com.example.my-plugin", arg: { mode: "export" } })',_I=z.object({identifier:z.string().min(1).describe('Bundle identifier of the Omni Automation plug-in to invoke (e.g. "com.example.my-plugin").'),arg:z.unknown().optional().describe("Optional JSON-serialisable argument forwarded to the plug-in action as Action.args[0]. Defaults to null.")});async function PI(t,e){let n=new Ot({adapter:e.adapter}),{result:r}=await n.invoke({identifier:t.identifier,arg:t.arg}),o=e.makeMeta();return p({result:r},o)}function Ul(t,e){return t.registerTool("plugin_invoke",{description:la,inputSchema:_I.shape},async n=>{let r=await PI(n,e);return u(r)})}var ua='Mark many OmniFocus projects as completed in a single JXA round trip. Completed projects are hidden from active views and closed to new task entry. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each completion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated project_complete calls whenever completing more than one project. Each item is { id }. Returns { completed: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the project name so the agent can describe each completion without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: project_batch_complete({ items: [{ id: "prj123" }, { id: "prj456" }] })',OI=z.object({id:k.schema.describe("Persistent project ID.")}),xI=z.object({items:z.array(OI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function DI(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getProjectsMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchCompleteProjects(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&J(e.cache,{projectId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({completed:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ud(a.succeeded.length)}))}function Ll(t,e){return t.registerTool("project_batch_complete",{description:ua,inputSchema:xI.shape},async n=>{let r=await DI(n,e);return u(r)})}var fa='Cancel (drop) many OmniFocus projects in a single JXA round trip. Dropped projects remain in OmniFocus but are treated as cancelled/inactive \u2014 they do not appear in active project lists. Use project_delete for permanent removal. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each drop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated project_drop calls whenever dropping more than one project. Each item is { id }. Returns { dropped: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the project name so the agent can describe each drop without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: project_batch_drop({ items: [{ id: "prj123" }, { id: "prj456" }] })',RI=z.object({id:k.schema.describe("Persistent project ID.")}),AI=z.object({items:z.array(RI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function CI(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getProjectsMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchDropProjects(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&J(e.cache,{projectId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({dropped:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ld(a.succeeded.length)}))}function Bl(t,e){return t.registerTool("project_batch_drop",{description:fa,inputSchema:AI.shape},async n=>{let r=await CI(n,e);return u(r)})}var ga=`Complete an OmniFocus project \u2014 marks it done with today's date and moves it out of the active view. Use when a project is finished. Do not use to archive or hide a project without completing it; prefer project_drop for that. Returns { completed: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: sets completionDate, removes from active projects, sets meta.syncPending = true. Example: project_complete({ id: "prj123" })`,ha=z.object({id:k.schema.describe("Persistent ID of the project to complete.")});async function MI(t,e){let{project:n}=await e.projectService.get({id:t.id,includeTaskTree:false});return await e.projectService.completeProject(t.id),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({completed:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Gd()}))}function Jl(t,e){return t.registerTool("project_complete",{description:ga,inputSchema:ha.shape},async n=>{let r=await MI(n,e);return u(r)})}var ya="Preview what project_complete would do without making any changes. Do NOT use to actually complete a project \u2014 use project_complete instead. Returns { description, plannedChanges } describing the completion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function NI(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}n.push({field:"status",newValue:"done",oldValue:"active"});let o=`Would mark project '${r}' as completed.`;return p({description:o,plannedChanges:n},e.makeMeta())}function $l(t,e){return t.registerTool("project_complete_describe",{description:ya,inputSchema:ha.shape},async n=>{let r=await NI(n,e);return u(r)})}function ie(t,e,n){let r={};for(let[i,s]of Object.entries(e)){if(!t.includes(s))throw new TypeError(`aliasedEnum: alias '${i}' points at '${s}' which is not in canonical set ${JSON.stringify(t)}`);r[i.toLowerCase()]=s;}let o=Object.entries(e).map(([i,s])=>`'${i}' \u2192 ${s}`).join(", "),a=o?`${n} Accepts: ${o}.`:n;return z.preprocess(i=>typeof i!="string"?i:r[i.toLowerCase()]??i,z.enum(t)).describe(a)}function FI(t,e={}){return {kind:"missing-detail",reason:t,...e}}function ka(t,e={}){return {kind:"next-natural-step",reason:t,...e}}function UI(t,e={}){return {kind:"consider-alternative",reason:t,...e}}function LI(t,e=3){return t.length<=e?t:[...t].sort((r,o)=>{let a=(r.severity??"info")==="warn"?0:1,i=(o.severity??"info")==="warn"?0:1;return a-i}).slice(0,e)}function BI(t){return process.env.OMNIFOCUS_HINT_LEVEL==="warn"?t.filter(e=>(e.severity??"info")==="warn"):t}function xt(t,e=3){let n=BI(t),r=LI(n,e);return r.length>0?r:void 0}var JI=/\b(daily|weekly|monthly|every\s+(day|week|month|monday|tuesday|wednesday|thursday|friday|saturday|sunday|weekday|weekend))\b/i;function Hl(t,e){if(JI.test(e))return ka("Task name contains a recurrence cue \u2014 setting a repetition rule keeps it rescheduled automatically.",{suggestedTool:"task_set_repetition",suggestedArgs:{id:t},severity:"info"})}function zl(t,e,n){if(!(e===void 0||n!==void 0))return FI("Task has a due date but no time estimate \u2014 an estimate helps schedule the task accurately.",{suggestedTool:"task_update",suggestedArgs:{id:t},severity:"info"})}function Gl(t,e=5){if(!(t<e))return UI(`Inbox now has ${t} unrouted tasks \u2014 consider triaging to keep your inbox clear.`,{suggestedTool:"task_list",suggestedArgs:{inbox:true},severity:"info"})}function ql(t,e){if(e===void 0)return ka("Project has no review interval \u2014 setting one ensures it surfaces in regular review.",{suggestedTool:"project_update",suggestedArgs:{id:t,reviewIntervalDays:7},severity:"info"})}function Vl(t,e){return ka(`Project '${e}' now has no remaining tasks \u2014 consider completing or reviewing the project.`,{suggestedTool:"project_complete",suggestedArgs:{id:t},severity:"info"})}var Ia=class{_ttlMs;_maxEntries;_now;_entries=new Map;constructor(e={}){this._ttlMs=e.ttlMs??6e5,this._maxEntries=e.maxEntries??1024,this._now=e.now??(()=>Date.now());}get(e){let n=this._entries.get(e);if(n){if(n.expiresAt<=this._now()){this._entries.delete(e);return}return this._entries.delete(e),this._entries.set(e,n),n.envelope}}set(e,n){for(this._entries.delete(e),this._entries.set(e,{envelope:n,expiresAt:this._now()+this._ttlMs});this._entries.size>this._maxEntries;){let r=this._entries.keys().next().value;if(r===void 0)break;this._entries.delete(r);}}get size(){return this._entries.size}clear(){this._entries.clear();}},Kl=new WeakMap;function $I(t){let e=Kl.get(t);return e||(e=new Map,Kl.set(t,e)),e}async function ce(t,e,n){if(e===void 0)return n();let r=t.get(e);if(r)return Xl(r);let o=$I(t),a=o.get(e);if(a){let s=await a;return Xl(s)}let i=(async()=>{let s=await n();return t.set(e,s),s})();o.set(e,i);try{return await i}finally{o.delete(e);}}function Xl(t){return {...t,meta:{...t.meta,idempotentReplay:true}}}function Yl(t,e){let n=process.env[t];if(n===void 0||n==="")return e;let r=Number.parseInt(n,10);return !Number.isFinite(r)||r<=0?e:r}var de=new Ia({ttlMs:Yl("OMNIFOCUS_IDEMPOTENCY_TTL_MS",6e5),maxEntries:Yl("OMNIFOCUS_IDEMPOTENCY_MAX_ENTRIES",1024)});var Ta='Create a new OmniFocus project. Optionally place it in a folder, assign tags, set completion criterion, status, defer/due dates, estimated minutes, flagged state, and review interval. Safety control: pass idempotency_key to make transport retries safe \u2014 identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate project. Returns { created: true, id }. Side effects: creates a project in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the project to appear on other devices. Example: project_create({ name: "Website Redesign" }) Example: project_create({ name: "Q3 Planning", folderId: "fld123", flagged: true })',wa=z.object({name:z.string().min(1).describe("Project name. Required, must be non-empty."),folderId:W.schema.optional().describe("Folder ID to place the project in. Omit for root."),note:z.string().optional().describe("Plain-text note for the project."),status:ie(["active","on-hold"],{paused:"on-hold"},"Initial project status. Default: active.").optional(),completionCriterion:ie(["parallel","sequential","singleActions"],{"in-order":"sequential","in order":"sequential","any-order":"parallel","any order":"parallel"},"How the project's tasks are completed: parallel (any order), sequential (in order), or singleActions.").optional(),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with UTC offset."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with UTC offset."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().min(1).optional().describe("Estimated total duration in minutes."),flagged:z.boolean().optional().describe("Flag the project."),tagIds:z.array(S.schema).optional().describe("Tag IDs to apply to the project."),reviewIntervalDays:z.number().int().min(1).optional().describe("Review interval in days. Omit to use OmniFocus default."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe creates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate project.")});async function WI(t,e){let n=e.idempotencyStore??de,r=e.replayStore??ve;if(t.idempotency_key!==void 0&&n.get(t.idempotency_key)!==void 0)return va(t,e,n);let o=[];try{o=(await e.adapter.listProjects()).filter(i=>i.name.toLowerCase()===t.name.toLowerCase()&&i.status!=="dropped");}catch{}if(o.length>0){let a=e.makeMeta(),i=o[0],s=[`Use existing project "${i.name}" (id: ${i.id})`,"Create a new project with the same name"],c=r.register(s,async d=>d===0?p({created:false,id:i.id,existing:true},e.makeMeta()):va(t,e,n));return St(`A project named "${t.name}" already exists. What should happen?`,c,a,s.map((d,l)=>({index:l,label:d})),{name:t.name})}return va(t,e,n)}async function va(t,e,n){return ce(n,t.idempotency_key,async()=>{let r={name:t.name,...t.folderId!==void 0&&{folderId:t.folderId},...t.note!==void 0&&{note:t.note},...t.status!==void 0&&{status:t.status},...t.completionCriterion!==void 0&&{completionCriterion:t.completionCriterion},...t.deferDate!==void 0&&{deferDate:t.deferDate},...t.deferDateFloating!==void 0&&{deferDateFloating:t.deferDateFloating},...t.dueDate!==void 0&&{dueDate:t.dueDate},...t.dueDateFloating!==void 0&&{dueDateFloating:t.dueDateFloating},...t.estimatedMinutes!==void 0&&{estimatedMinutes:t.estimatedMinutes},...t.flagged!==void 0&&{flagged:t.flagged},...t.tagIds!==void 0&&{tagIds:t.tagIds},...t.reviewIntervalDays!==void 0&&{reviewIntervalDays:t.reviewIntervalDays}},o=await e.adapter.createProject(r);e.cache!==void 0&&J(e.cache,{projectId:o});let a=xt([ql(o,t.reviewIntervalDays)].filter(i=>i!=null));return p({created:true,id:o},e.makeMeta({syncPending:true,humanReadableSummary:Md(t.name)}),void 0,a)})}function Zl(t,e){return t.registerTool("project_create",{description:Ta,inputSchema:wa.shape},async n=>{let r=await WI(n,e);return u(r)})}var Sa="Preview what project_create would do without making any changes. Do NOT use to actually create a project \u2014 use project_create instead. Returns { description, plannedChanges } describing the project that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function HI(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.folderId!==void 0){let a=await Ke(e.adapter,t.folderId);n.push({field:"folderId",newValue:t.folderId}),r.push(`in folder '${a}'`);}else r.push("at root");if(t.status!==void 0&&(n.push({field:"status",newValue:t.status}),r.push(`status '${t.status}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate}),r.push(`due ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${Te(t.deferDate)}`)),t.flagged===true&&(n.push({field:"flagged",newValue:"true"}),r.push("flagged")),t.tagIds!==void 0&&t.tagIds.length>0){let a=await Promise.all(t.tagIds.map(i=>he(e.adapter,i)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`tagged ${a.map(i=>`'${i}'`).join(", ")}`);}t.note!==void 0&&n.push({field:"note",newValue:t.note.slice(0,50)});let o=`Would create project ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Ql(t,e){return t.registerTool("project_create_describe",{description:Sa,inputSchema:wa.shape},async n=>{let r=await HI(n,e);return u(r)})}function Ye(t,e,n){if(t===void 0)return;let r=Date.parse(t);if(Number.isNaN(r))throw new y(`expectedModifiedAt for ${n} is not a valid ISO-8601 timestamp.`,{details:{resource:n,expected:t}});let o=Date.parse(e);if(Number.isNaN(o))throw new y(`observed modifiedAt for ${n} is not a valid ISO-8601 timestamp.`,{details:{resource:n,observed:e}});if(r!==o)throw new ft(`${n} was modified since expectedModifiedAt.`,{details:{resource:n,expected:t,observed:e}})}async function Ce(t,e,n){if(t!==true)return n();let r=await e();return zI(r)}function zI(t){return {...t,meta:{...t.meta,dryRun:true,syncPending:false}}}var ba='Permanently delete an OmniFocus project and ALL its contained tasks. IRREVERSIBLE \u2014 uses OmniFocus deleteObject; there is no undo. All tasks inside the project are also permanently deleted (cascade). Prefer project_drop when you want a recoverable status change. Only use project_delete when the agent has explicit user intent to permanently remove the project and its tasks. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent project_get) to reject the call if the project changed since you read it; pass idempotency_key to coalesce retries so the same delete is only performed once. Returns { deleted: true, id } on success. Side effects: removes the project and its tasks from OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the deletion to appear on other devices. Example: project_delete({ id: "prj123", dry_run: true }) Example: project_delete({ id: "prj123", expectedModifiedAt: "2026-04-01T10:00:00Z" })',ja=z.object({id:k.schema.describe("Persistent ID of the project to delete. Get from project_list. Verify you have the correct ID before calling \u2014 this action is irreversible and deletes all contained tasks."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent project_get. If the project's current modifiedAt differs, the call fails with OF_CONFLICT and no delete is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe deletes. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-deleting (or re-raising NotFound on the second attempt).")});async function GI(t,e){let n=e.idempotencyStore??de;return ce(n,t.idempotency_key,async()=>{let r=await e.adapter.getProject(t.id);Ye(t.expectedModifiedAt,r.modifiedAt,`project:${t.id}`);let o=()=>p({deleted:true,id:t.id},e.makeMeta({syncPending:false})),a=async()=>(await e.adapter.deleteProject(t.id),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Fd(r.name)})));return Ce(t.dry_run,o,a)})}function ep(t,e){return t.registerTool("project_delete",{description:ba,inputSchema:ja.shape},async n=>{let r=await GI(n,e);return u(r)})}var _a="Preview what project_delete would do without making any changes. Do NOT use to actually delete a project \u2014 use project_delete instead. Returns { description, plannedChanges } describing the permanent deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function qI(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=`Would permanently delete project '${r}' (id: ${t.id}) and ALL its contained tasks. IRREVERSIBLE.`;return p({description:o,plannedChanges:n},e.makeMeta())}function tp(t,e){return t.registerTool("project_delete_describe",{description:_a,inputSchema:ja.shape},async n=>{let r=await qI(n,e);return u(r)})}var Pa='Drop an OmniFocus project \u2014 marks it as on-hold/dropped and removes it from the active view without completing it. Use to defer or abandon a project while keeping it recoverable. Do not use if the project is actually done; prefer project_complete for that. Returns { dropped: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: changes project status, sets meta.syncPending = true.Example: project_drop({ id: "prj123" })',Oa=z.object({id:k.schema.describe("Persistent ID of the project to drop.")});async function KI(t,e){let{project:n}=await e.projectService.get({id:t.id,includeTaskTree:false});return await e.projectService.dropProject(t.id),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({dropped:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:qd()}))}function np(t,e){return t.registerTool("project_drop",{description:Pa,inputSchema:Oa.shape},async n=>{let r=await KI(n,e);return u(r)})}var xa="Preview what project_drop would do without making any changes. Do NOT use to actually drop a project \u2014 use project_drop instead. Returns { description, plannedChanges } describing the status change that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function XI(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}n.push({field:"status",newValue:"dropped",oldValue:"active"});let o=`Would drop project '${r}' (mark as on-hold/abandoned).`;return p({description:o,plannedChanges:n},e.makeMeta())}function rp(t,e){return t.registerTool("project_drop_describe",{description:xa,inputSchema:Oa.shape},async n=>{let r=await XI(n,e);return u(r)})}var Da=`Fetch a single OmniFocus project by persistent ID. Do NOT use for queries across projects \u2014 use project_list. When includeTaskTree=true (default), the project's flat task list is attached. Returns { project, tasks? }; safe to call repeatedly; no side effects. Example: project_get({ id: "prj123" }) Example: project_get({ id: "prj123", includeTaskTree: false })`,YI=z.object({id:k.schema.describe("Persistent project ID. Get from project_list or search_query."),includeTaskTree:z.boolean().optional().describe("Whether to attach the project's tasks (flat array; clients rebuild the tree via parentId). Default true. Set to false for a fast project-only read.")});async function ZI(t,e){let n=await e.projectService.get({id:t.id,...t.includeTaskTree!==void 0?{includeTaskTree:t.includeTaskTree}:{}}),r=e.makeMeta({cacheHit:n.cacheHit}),o=Ae(n.project.note),a={project:n.project};return n.tasks!==void 0&&(a.tasks=n.tasks),o!==void 0&&(a.decision=o),p(a,r)}function ap(t,e){return t.registerTool("project_get",{description:Da,inputSchema:YI.shape},async n=>{let r=await ZI(n,e);return u(r)})}var Ra='Fetch up to 100 projects by persistent ID in a single OmniFocus round-trip. Use when you have a set of project IDs and need full project objects for all of them. Do NOT use for a single ID \u2014 use project_get instead. Returns Project[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry. Example: project_get_many({ ids: ["prj123", "prj456"] })',Hn=100,QI=z.object({ids:z.array(k.schema).min(0).max(Hn).describe(`Array of project IDs to fetch (0..${Hn}). Get IDs from project_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function ev(t,e){if(t.ids.length===0)return p({projects:[]},e.makeMeta());if(t.ids.length>Hn)throw new y(`ids array exceeds the maximum batch size of ${Hn} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getProjectsMany(t.ids),r=n.filter(d=>d!==null),o=t.ids.filter((d,l)=>n[l]===null),a={};for(let d of r){let l=Ae(d.note);l!==void 0&&(a[d.id]=l);}let i=Object.keys(a).length>0,s=o.length>0?[wt(o)]:void 0,c=e.makeMeta({...s!==void 0?{warnings:s}:{}});return p({projects:r,...i&&{decisions:a}},c)}function ip(t,e){return t.registerTool("project_get_many",{description:Ra,inputSchema:QI.shape},async n=>{let r=await ev(n,e);return u(r)})}var Aa='List projects in OmniFocus with optional filters. Use for queries across projects. Do NOT use for a known single project (use project_get). Filters: folderId, status, flagged, reviewDueBefore. Returns projects[] with pagination; safe to call repeatedly; no side effects. Example: project_list({}) Example: project_list({ status: "active", folderId: "fld123" })',tv=z.object({folderId:W.schema.optional().describe("Restrict to projects inside this folder. Get the ID from folder_list. Omit for all folders."),status:ie(["active","on-hold","done","dropped"],{paused:"on-hold",completed:"done",cancelled:"dropped"},"Restrict to projects with this status. 'active' = available; 'on-hold' = paused; 'done' = completed; 'dropped' = abandoned. Omit for any status.").optional(),flagged:z.boolean().optional().describe("true = flagged only; false = unflagged only; omit = both."),reviewDueBefore:z.string().optional().describe("Restrict to projects whose next review date is strictly before this moment. ISO-8601 with offset (e.g. '2026-05-01T00:00:00-07:00'). Projects without a review interval are excluded."),limit:z.number().int().min(1).max(1e3).optional().describe("Max projects per page (1..1000). Default 200. Use `cursor` to fetch subsequent pages."),cursor:z.string().optional().describe("Opaque cursor from a previous project_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")});async function nv(t,e){let n=t,r=await e.projectService.list(n),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return p({projects:r.projects},a,o)}function cp(t,e){return t.registerTool("project_list",{description:Aa,inputSchema:tv.shape},async n=>{let r=await nv(n,e);return u(r)})}var Ca=`Move an OmniFocus project to a different folder. Pass folderId to move into a folder, or null to move to the root (no folder). Use when reorganizing projects. Do not use to complete or drop a project. Returns { moved: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: changes the project's folder, sets meta.syncPending = true.Example: project_move({ id: "prj123", folderId: "fld456" }) Example: project_move({ id: "prj123", folderId: null })`,Ea=z.object({id:k.schema.describe("Persistent ID of the project to move."),folderId:W.schema.nullable().describe("Target folder ID, or null to move to root.")});async function ov(t,e){let{project:n}=await e.projectService.get({id:t.id,includeTaskTree:false});return await e.projectService.moveProject(t.id,{folderId:t.folderId}),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({moved:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Vd(t.folderId!=null?"folder":"library root")}))}function dp(t,e){return t.registerTool("project_move",{description:Ca,inputSchema:Ea.shape},async n=>{let r=await ov(n,e);return u(r)})}var Ma="Preview what project_move would do without making any changes. Do NOT use to actually move a project \u2014 use project_move instead. Returns { description, plannedChanges } describing the folder change that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function av(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}let o;if(t.folderId!==null){let i=await Ke(e.adapter,t.folderId);n.push({field:"folderId",newValue:t.folderId}),o=`folder '${i}'`;}else n.push({field:"folderId",newValue:null}),o="root (no folder)";let a=`Would move project '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function lp(t,e){return t.registerTool("project_move_describe",{description:Ma,inputSchema:Ea.shape},async n=>{let r=await av(n,e);return u(r)})}var Na="project-template",sv=z.object({name:z.string().min(1),parameterNames:z.array(z.string().min(1)),capturedAt:oe()});function Dt(t){let e=Be(t,Na);if(e===void 0)return;let n=kt(e.body),r={};typeof n.name=="string"&&n.name.length>0&&(r.name=n.name),typeof n.parameters=="string"?r.parameterNames=n.parameters.split(",").map(a=>a.trim()).filter(a=>a.length>0):r.parameterNames=[],typeof n.capturedAt=="string"&&n.capturedAt.length>0&&(r.capturedAt=n.capturedAt);let o=sv.safeParse(r);return o.success?o.data:void 0}function pp(t,e){let n=It({name:t.name,parameters:t.parameterNames.length>0?t.parameterNames.join(","):void 0,capturedAt:t.capturedAt}),r=vt(null,Na,n);return e.length===0?r:`${r}
|
|
210
|
+
|
|
211
|
+
${e}`}function up(t){let e=Be(t,Na);return e===void 0||t===null?"":t.slice(e.end).replace(/^\n+/,"")}function mp(t,e){return t.replace(/\{\{\s*([A-Za-z0-9_-]+)\s*\}\}/g,(n,r)=>Object.hasOwn(e,r)?e[r]:n)}function fp(t){let e=[];for(let n of t.matchAll(/@due\((\d{4}-\d{2}-\d{2})\)/g))e.push(n[1]);if(e.length!==0)return e.sort()[0]}function gp(t,e,n){let r=Date.UTC(Number(e.slice(0,4)),Number(e.slice(5,7))-1,Number(e.slice(8,10))),a=Date.UTC(Number(n.slice(0,4)),Number(n.slice(5,7))-1,Number(n.slice(8,10)))-r;if(a===0)return t;let i=s=>{let c=Date.UTC(Number(s.slice(0,4)),Number(s.slice(5,7))-1,Number(s.slice(8,10)))+a,d=new Date(c),l=d.getUTCFullYear().toString().padStart(4,"0"),m=(d.getUTCMonth()+1).toString().padStart(2,"0"),f=d.getUTCDate().toString().padStart(2,"0");return `${l}-${m}-${f}`};return t.replace(/@(due|defer)\((\d{4}-\d{2}-\d{2})\)/g,(s,c,d)=>`@${c}(${i(d)})`)}async function Gn(t,e){let n=await t.listTasks({projectId:e}),r=[...n],o=[...n];for(;;){let a=o.shift();if(a===void 0)break;let i=await t.listTasks({parentId:a.id});for(let s of i)r.push(s),o.push(s);}return r}function Rt(t){let e=new Set(t.map(o=>String(o.id))),n=[],r=new Map;for(let o of t)if(o.parentId===null||!e.has(String(o.parentId)))n.push(o);else {let i=String(o.parentId),s=r.get(i);s?s.push(o):r.set(i,[o]);}return {rootTasks:n,byParent:r}}function xe(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}function hp(t,e,n){let r=[`text="${xe(t.name)}"`,'type="omnifocus:task"',`id="${xe(String(t.id))}"`];t.dueDate&&r.push(`due="${xe(t.dueDate)}"`),t.deferDate&&r.push(`defer="${xe(t.deferDate)}"`),t.flagged&&r.push('flagged="true"'),t.completed&&r.push('completed="true"'),t.dropped&&r.push('dropped="true"'),t.note&&r.push(`note="${xe(t.note)}"`);let o=e.get(String(t.id))??[];if(o.length===0)return `${n}<outline ${r.join(" ")} />`;let a=`${n} `,i=o.map(s=>hp(s,e,a)).join(`
|
|
212
|
+
`);return `${n}<outline ${r.join(" ")}>
|
|
213
|
+
${i}
|
|
214
|
+
${n}</outline>`}function yp(t,e,n){let{rootTasks:r,byParent:o}=Rt(e),a=[`text="${xe(t.name)}"`,'type="omnifocus:project"',`id="${xe(String(t.id))}"`,`status="${xe(t.status)}"`];t.dueDate&&a.push(`due="${xe(t.dueDate)}"`),t.deferDate&&a.push(`defer="${xe(t.deferDate)}"`),t.flagged&&a.push('flagged="true"'),t.note&&a.push(`note="${xe(t.note)}"`);let i=`${n} `;if(r.length===0)return `${n}<outline ${a.join(" ")} />`;let s=r.map(c=>hp(c,o,i)).join(`
|
|
215
|
+
`);return `${n}<outline ${a.join(" ")}>
|
|
216
|
+
${s}
|
|
217
|
+
${n}</outline>`}function be(t,e){let r=new RegExp(`\\b${e}=(?:"([^"]*)"|'([^']*)')`,"i").exec(t);if(!r)return;let o=r[1]??r[2]??"";return iv(o)}function iv(t){return t.replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&")}function cv(t){let e=[],n=/<(!--[\s\S]*?--|[?!][^>]*|\/(\w[\w:-]*)\s*|(\w[\w:-]*)([^>]*?)(\/?))\s*>/g;for(let r=n.exec(t);r!==null;r=n.exec(t)){let o=r[0];if(o.startsWith("<!--")||o.startsWith("<?")||o.startsWith("<!"))continue;let a=r[2];if(a){e.push({kind:"close",tag:a.toLowerCase()});continue}let i=r[3];if(i){let s=(r[4]??"").trim(),c=(r[5]??"")==="/";e.push({kind:"open",tag:i.toLowerCase(),attrs:s,selfClose:c});}}return e}function kp(t,e,n){let o={text:be(n,"text")??"",children:[]},a=be(n,"type");a!==void 0&&(o.type=a);let i=be(n,"id");i!==void 0&&(o.id=i);let s=be(n,"due");s!==void 0&&(o.due=s);let c=be(n,"defer");c!==void 0&&(o.defer=c),be(n,"flagged")==="true"&&(o.flagged=true);let l=e;for(;l<t.length;){let m=t[l];if(!m)break;if(m.kind==="close"&&m.tag==="outline")return [o,l+1];if(m.kind==="open"&&m.tag==="outline")if(m.selfClose){let f=Ip(m.attrs);o.children.push(f),l++;}else {let[f,g]=kp(t,l+1,m.attrs);o.children.push(f),l=g;}else l++;}return [o,l]}function Ip(t){let n={text:be(t,"text")??"",children:[]},r=be(t,"type");r!==void 0&&(n.type=r);let o=be(t,"id");o!==void 0&&(n.id=o);let a=be(t,"due");a!==void 0&&(n.due=a);let i=be(t,"defer");return i!==void 0&&(n.defer=i),be(t,"flagged")==="true"&&(n.flagged=true),n}function vp(t){let e=t.trim();if(!/<opml\b/i.test(e))throw new y("Not valid OPML: missing <opml> root element.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});if(!/<body\b/i.test(e))throw new y("Not valid OPML: missing <body> element.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});let n=cv(e),r=-1;for(let i=0;i<n.length;i++){let s=n[i];if(s&&s.kind==="open"&&s.tag==="body"){r=i+1;break}}if(r===-1)throw new y("Not valid OPML: could not find <body> open tag in token stream.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});let o=[],a=r;for(;a<n.length;){let i=n[a];if(!i||i.kind==="close"&&i.tag==="body")break;if(i.kind==="open"&&i.tag==="outline")if(i.selfClose)o.push(Ip(i.attrs)),a++;else {let[s,c]=kp(n,a+1,i.attrs);o.push(s),a=c;}else a++;}return {body:o}}function dn(t,e,n,r,o){let a=" ".repeat(n),i=[];t.dueDate&&i.push(`@due(${t.dueDate.slice(0,10)})`),t.deferDate&&i.push(`@defer(${t.deferDate.slice(0,10)})`),t.flagged&&i.push("@flagged"),t.completed&&i.push("@done"),t.dropped&&i.push("@dropped");let s=i.length>0?` ${i.join(" ")}`:"";r.push(`${a}- ${t.name}${s}`);let c=t.note??(t.noteHtml?t.noteHtml.replace(/<[^>]*>/g,""):null);if(c){for(let l of c.split(`
|
|
218
|
+
`))l.trim()&&r.push(`${a} ${l}`);t.noteHtml&&!t.note&&o.push(`Task "${t.name}": HTML note downgraded to plain text`);}let d=e.get(String(t.id))??[];for(let l of d)dn(l,e,n+1,r,o);}function wp(t,e,n){let r=t,o,a,i=false,s=false,c=[];r=r.replace(/@due\(([^)]+)\)/g,(f,g)=>(o=Tp(g.trim(),e,n,"due"),"")),r=r.replace(/@defer\(([^)]+)\)/g,(f,g)=>(a=Tp(g.trim(),e,n,"defer"),"")),r=r.replace(/@flagged/g,()=>(i=true,"")),r=r.replace(/@done/g,()=>(s=true,"")),r=r.replace(/@dropped/g,()=>(s=true,"")),r=r.replace(/@([\w-]+)/g,(f,g)=>(g!=="due"&&g!=="defer"&&g!=="flagged"&&g!=="done"&&g!=="dropped"&&c.push(g),""));let d=r.split("//"),l=(d[0]??"").trim(),m=d[1]?d[1].trim():void 0;return l||n.push(`Line ${e}: empty task name after parsing tags \u2014 skipped`),{name:l||"(unnamed)",dueDate:o,deferDate:a,flagged:i,done:s,tagNames:c,note:m}}function Tp(t,e,n,r){if(/^\d{4}-\d{2}-\d{2}$/.test(t))return `${t}T00:00:00Z`;if(/^\d{4}-\d{2}-\d{2}T/.test(t))return t;n.push(`Line ${e}: unrecognised ${r} date format "${t}" \u2014 skipped`);}function Sp(t){let e=0;for(let n of t)if(n===" ")e++;else break;return e}var At=class{adapter;constructor(e){this.adapter=e.adapter;}async exportOpml(e){let n=await this.resolveProjects(e),r=await Promise.all(n.map(s=>this.adapter.listTasks({projectId:s.id}).then(c=>({project:s,tasks:c})))),o=r.map(({project:s,tasks:c})=>yp(s,c," ")).join(`
|
|
219
|
+
`),a=r.reduce((s,{tasks:c})=>s+c.length,0);return {opml:['<?xml version="1.0" encoding="UTF-8"?>','<opml version="2.0">'," <head>"," <title>OmniFocus Export</title>"," </head>"," <body>",o," </body>","</opml>"].join(`
|
|
220
|
+
`),projectCount:n.length,taskCount:a}}async resolveProjects(e){if(e.kind==="all")return this.adapter.listProjects({status:"active"});if(e.kind==="folder"){let r=await this.adapter.listProjects({folderId:e.id});if(r.length===0)try{await this.adapter.getFolder(e.id);}catch{throw new P(`Folder not found: ${String(e.id)}`,{details:{resource:"folder",id:String(e.id)}})}return r}return [await this.adapter.getProject(e.id)]}async exportTaskPaper(e){let n=await this.resolveProjects(e),r=[],o=[],a=0;for(let{project:i,tasks:s}of await Promise.all(n.map(c=>Gn(this.adapter,c.id).then(d=>({project:c,tasks:d}))))){let{rootTasks:c,byParent:d}=Rt(s);if(o.push(`${i.name}:`),i.note)for(let l of i.note.split(`
|
|
221
|
+
`))o.push(` ${l}`);for(let l of c)dn(l,d,1,o,r);a+=s.length,o.push("");}return {taskpaper:o.join(`
|
|
222
|
+
`),projectCount:n.length,taskCount:a,warnings:r}}async importTaskPaper(e,n){if(!e.trim())throw new y("text is empty",{suggestion:"Provide non-empty TaskPaper text."});let r=await this.adapter.listTags(),o=new Map(r.map(g=>[g.name.toLowerCase(),g.id])),a=async g=>{let v=g.toLowerCase(),I=o.get(v);if(I)return I;let T=await this.adapter.createTag({name:g});return o.set(v,T),T},i=await this.adapter.listProjects(),s=new Map(i.map(g=>[g.name.toLowerCase(),g.id])),c=[],d=[],l=[],m=n,f=e.split(`
|
|
223
|
+
`);for(let g=0;g<f.length;g++){let v=f[g];if(!v)continue;let I=Sp(v),T=v.trimStart();if(!T.startsWith("- ")&&!T.startsWith("- ")&&T.endsWith(":")&&I===0){if(!n){let U=T.slice(0,-1).trim(),M=s.get(U.toLowerCase());M?m=M:(d.push(`Project "${U}" not found in OmniFocus \u2014 tasks will land in inbox`),m=void 0);}l.length=0;continue}if(!T.startsWith("- ")&&!T.startsWith("- "))continue;for(;l.length>0&&(l[l.length-1]?.depth??0)>=I;)l.pop();let E=T.slice(2).trim(),w=wp(E,g+1,d),D=[];for(let U of w.tagNames)try{D.push(await a(U));}catch{d.push(`Line ${g+1}: could not create tag "${U}" \u2014 skipped`);}let j=l[l.length-1],B={name:w.name,...j?{parentId:j.id}:{},...m&&!j?{projectId:m}:{},...w.dueDate?{dueDate:w.dueDate}:{},...w.deferDate?{deferDate:w.deferDate}:{},...w.flagged?{flagged:true}:{},...w.note?{note:w.note}:{},...D.length>0?{tagIds:D}:{}},_=await this.adapter.createTask(B);c.push(_),w.done&&await this.adapter.completeTask(_),l.push({depth:I,id:_});}return {created:c,warnings:d}}async importOpml(e,n={}){if(!e.trim())throw new y("opml is empty",{suggestion:"Provide a non-empty OPML XML string."});let r=vp(e),o=await this.adapter.listProjects(),a=new Map(o.map(d=>[String(d.id),d.id])),i=new Map(o.map(d=>[d.name.toLowerCase(),d.id])),s=[],c=async(d,l,m)=>{for(let f of d){let g={name:f.text||"(untitled)",...m!==void 0?{parentId:m}:l!==void 0?{projectId:l}:{},...f.due!==void 0?{dueDate:f.due}:{},...f.defer!==void 0?{deferDate:f.defer}:{},...f.flagged===true?{flagged:true}:{}},v=await this.adapter.createTask(g);s.push(v),f.children.length>0&&await c(f.children,l,v);}};for(let d of r.body){if(n.destinationProjectId!==void 0){await c([d],n.destinationProjectId,void 0);continue}if(d.type==="omnifocus:project"){let l=(d.id!==void 0?a.get(d.id):void 0)??i.get(d.text.toLowerCase());await c(d.children,l,void 0);}else await c([d],void 0,void 0);}return {imported:s.length,taskIds:s}}};var Ua='Spawn a new project from a saved template under the Templates folder. Substitutes {{name}} placeholders with the supplied parameters and shifts @due / @defer dates relative to the optional dueDate anchor (the earliest @due in the template). Do NOT use to copy a one-off project \u2014 prefer task_duplicate. Returns { projectId, taskCount, importWarnings }. Side effects: writes a new project + tasks; sets meta.syncPending = true. Example: { templateName: "Client onboarding", parameters: { client: "Acme" }, dueDate: "2026-06-04" }.',dv=z.object({templateName:z.string().min(1).describe("Saved template to instantiate."),parameters:z.record(z.string(),z.string()).default({}).describe("Map of placeholder name \u2192 substitution value."),targetFolderId:W.schema.optional().describe("Folder to create the new project in. Defaults to the library root."),dueDate:z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"must be YYYY-MM-DD").optional().describe("Anchor for relative-date shifting. The earliest @due in the template becomes this date; every other @due/@defer shifts by the same delta.")}),ln=class extends Error{code="TEMPLATE_NOT_FOUND";constructor(e){super(`No template named "${e}" was found in the Templates folder.`),this.name="TemplateNotFoundError";}},Fa=class extends Error{code="MISSING_TEMPLATE_PARAMETER";missing;constructor(e){super(`Template requires parameters not supplied: ${e.map(n=>`"${n}"`).join(", ")}.`),this.name="MissingTemplateParameterError",this.missing=e;}};async function lv(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(v=>v.name.toLowerCase()===r);if(o===void 0)throw new ln(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),i=t.templateName.toLowerCase(),s=a.find(v=>v.name.toLowerCase()===i);if(s===void 0)throw new ln(t.templateName);let c=Dt(s.note);if(c===void 0)throw new ln(t.templateName);let d=c.parameterNames.filter(v=>!Object.hasOwn(t.parameters,v));if(d.length>0)throw new Fa(d);let l=up(s.note);if(l=mp(l,t.parameters),t.dueDate!==void 0){let v=fp(l);v!==void 0&&(l=gp(l,v,t.dueDate));}let m=await e.adapter.createProject({name:t.templateName,...t.targetFolderId!==void 0&&{folderId:t.targetFolderId}}),g=await new At({adapter:e.adapter}).importTaskPaper(l,m);return e.cache!==void 0&&J(e.cache,{projectId:m}),p({projectId:m,taskCount:g.created.length,...g.warnings.length>0&&{importWarnings:g.warnings}},e.makeMeta({syncPending:true}))}function bp(t,e){return t.registerTool("project_template_instantiate",{description:Ua,inputSchema:dv.shape},async n=>{let r=await lv(n,e);return u(r)})}var La="List saved project templates under the Templates folder. Projects without a parseable template fence are skipped. Do NOT use to enumerate ordinary projects \u2014 call project_list. Returns { templates: [{ templateId, templateName, parameterNames, capturedAt }] }, sorted by capturedAt desc. Read-only; safe to retry. Example: call with no args; receives [] when no Templates folder exists yet.",uv=z.object({});async function mv(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(s=>s.name.toLowerCase()===r);if(o===void 0)return p({templates:[]},e.makeMeta());let a=await e.adapter.listProjects({folderId:o.id}),i=[];for(let s of a){let c=Dt(s.note);c!==void 0&&i.push({templateId:s.id,templateName:c.name,parameterNames:c.parameterNames,capturedAt:c.capturedAt});}return i.sort((s,c)=>s.capturedAt!==c.capturedAt?s.capturedAt<c.capturedAt?1:-1:s.templateName<c.templateName?-1:s.templateName>c.templateName?1:0),p({templates:i},e.makeMeta())}function jp(t,e){return t.registerTool("project_template_list",{description:La,inputSchema:uv.shape},async n=>{let r=await mv(n,e);return u(r)})}var Ja='Capture a project as a reusable template under the Templates folder (env OMNIFOCUS_TEMPLATES_FOLDER_NAME). Metadata is stored in a fenced YAML block at the top of the template-project note; TaskPaper body sits below. Do NOT use to duplicate a one-off project \u2014 prefer task_duplicate. Returns { templateId, templateName, capturedAt }. Side effects: writes folder + project; sets meta.syncPending = true. Example: { projectId: "p_001", templateName: "Client onboarding", parameterNames: ["client"] }.',fv=z.object({projectId:k.schema.describe("Source project to capture."),templateName:z.string().min(1).describe("Display name; must be unique within the Templates folder."),parameterNames:z.array(z.string().min(1)).optional().describe("Optional placeholder names for future _instantiate substitution.")}),Ba=class extends Error{code="TEMPLATE_EXISTS";constructor(e){super(`A template named "${e}" already exists in the Templates folder.`),this.name="TemplateExistsError";}};async function gv(t,e){let n=await t.listFolders(),r=e.toLowerCase(),o=n.find(a=>a.name.toLowerCase()===r);return o!==void 0?o.id:t.createFolder({name:e})}async function hv(t,e){await e.adapter.getProject(t.projectId);let n=await gv(e.adapter,e.templatesFolderName),r=await e.adapter.listProjects({folderId:n}),o=t.templateName.toLowerCase();if(r.some(I=>I.name.toLowerCase()===o))throw new Ba(t.templateName);let a=await e.adapter.getProject(t.projectId),i=await Gn(e.adapter,t.projectId),{rootTasks:s,byParent:c}=Rt(i),d=[],l=[`${a.name}:`];if(a.note)for(let I of a.note.split(`
|
|
224
|
+
`))l.push(` ${I}`);for(let I of s)dn(I,c,1,l,d);let m=l.join(`
|
|
225
|
+
`),f={name:t.templateName,parameterNames:t.parameterNames??[],capturedAt:new Date().toISOString()},g=pp(f,m),v=await e.adapter.createProject({name:t.templateName,folderId:n,note:g});return e.cache!==void 0&&J(e.cache,{projectId:v}),p({templateId:v,templateName:t.templateName,capturedAt:f.capturedAt,...d.length>0&&{exportWarnings:d}},e.makeMeta({syncPending:true}))}function _p(t,e){return t.registerTool("project_template_save",{description:Ja,inputSchema:fv.shape},async n=>{let r=await hv(n,e);return u(r)})}var $a='Partially update mutable fields on an OmniFocus project. Only supplied fields are changed; omit a field to leave it unchanged. Pass null for note, deferDate, dueDate, estimatedMinutes, or reviewIntervalDays to clear those fields. Do NOT use to create or delete projects; prefer project_create or project_delete instead. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent project_get) to reject the call if the project changed since you read it; pass idempotency_key to coalesce retries so the same update is only performed once. Returns { updated: true, id, name } \u2014 name reflects the post-patch name. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: project_update({ id: "prj123", name: "New Name", flagged: true }) Example: project_update({ id: "prj123", status: "on-hold", dry_run: true })',Wa=z.object({id:k.schema.describe("Persistent project ID. Get from project_list or project_get."),name:z.string().min(1).optional().describe("New project name. Must be non-empty if supplied."),note:z.string().nullable().optional().describe("Plain-text note. Pass null to clear."),noteHtml:z.string().nullable().optional().describe("HTML note. Pass null to clear. Prefer note for plain-text edits."),status:ie(["active","on-hold"],{paused:"on-hold"},"Project status. Use project_complete or project_drop to close a project.").optional(),completionCriterion:ie(["parallel","sequential","singleActions"],{"in-order":"sequential","in order":"sequential","any-order":"parallel","any order":"parallel"},"How the project's tasks are completed.").optional(),deferDate:z.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated total duration in minutes. Pass null to clear."),flagged:z.boolean().optional().describe("Flag or unflag the project."),tagIds:z.array(S.schema).optional().describe("Full-replacement tag list. Replaces all existing tags."),reviewIntervalDays:z.number().int().positive().nullable().optional().describe("Review interval in days. Pass null to clear."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent project_get. If the project's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe updates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying the patch.")});async function yv(t,e){let{id:n,...r}=t,o=e.idempotencyStore??de;return ce(o,t.idempotency_key,async()=>{let a=await e.adapter.getProject(n);Ye(t.expectedModifiedAt,a.modifiedAt,`project:${n}`);let i={...r.name!==void 0&&{name:r.name},...r.note!==void 0&&{note:r.note},...r.noteHtml!==void 0&&{noteHtml:r.noteHtml},...r.status!==void 0&&{status:r.status},...r.completionCriterion!==void 0&&{completionCriterion:r.completionCriterion},...r.deferDate!==void 0&&{deferDate:r.deferDate},...r.deferDateFloating!==void 0&&{deferDateFloating:r.deferDateFloating},...r.dueDate!==void 0&&{dueDate:r.dueDate},...r.dueDateFloating!==void 0&&{dueDateFloating:r.dueDateFloating},...r.estimatedMinutes!==void 0&&{estimatedMinutes:r.estimatedMinutes},...r.flagged!==void 0&&{flagged:r.flagged},...r.tagIds!==void 0&&{tagIds:r.tagIds},...r.reviewIntervalDays!==void 0&&{reviewIntervalDays:r.reviewIntervalDays}},s=r.name??a.name,c=()=>p({updated:true,id:n,name:s},e.makeMeta({syncPending:false})),d=async()=>(await e.adapter.updateProject(n,i),e.cache!==void 0&&J(e.cache,{projectId:n}),p({updated:true,id:n,name:s},e.makeMeta({syncPending:true,humanReadableSummary:Nd(a.name)})));return Ce(t.dry_run,c,d)})}function Pp(t,e){return t.registerTool("project_update",{description:$a,inputSchema:Wa.shape},async n=>{let r=await yv(n,e);return u(r)})}var Ha="Preview what project_update would do without making any changes. Do NOT use to actually update a project \u2014 use project_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function kv(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getProject(t.id);if(o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`)),t.status!==void 0&&(n.push({field:"status",newValue:t.status,oldValue:i.status}),r.push(`set status to '${t.status}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate,oldValue:i.dueDate??null}),r.push(t.dueDate===null?"clear due date":`set due date to ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate,oldValue:i.deferDate??null}),r.push(t.deferDate===null?"clear defer date":`set defer date to ${Te(t.deferDate)}`)),t.flagged!==void 0&&(n.push({field:"flagged",newValue:String(t.flagged),oldValue:String(i.flagged)}),r.push(t.flagged?"flag":"unflag")),t.tagIds!==void 0){let s=await Promise.all(t.tagIds.map(c=>he(e.adapter,c)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`set tags to [${s.map(c=>`'${c}'`).join(", ")}]`);}t.note!==void 0&&(n.push({field:"note",newValue:t.note===null?null:t.note.slice(0,50)}),r.push(t.note===null?"clear note":"update note"));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update project '${o}': ${r.join(", ")}.`:`Would update project '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function Op(t,e){return t.registerTool("project_update_describe",{description:Ha,inputSchema:Wa.shape},async n=>{let r=await kv(n,e);return u(r)})}var Ga="\u26A0 DANGEROUS \u2014 raw JXA escape hatch. Executes an arbitrary JavaScript-for-Automation script against OmniFocus with FULL Automation privileges (read, write, delete, move, sync). Only available when the server was started with OMNIFOCUS_ALLOW_RAW_SCRIPT=1. Every call is audit-logged with the full script body. Do NOT use this for operations covered by the typed tools (task_*, project_*, tag_*, folder_*, etc.) \u2014 typed tools are safer, idempotent, and return structured results. Use ONLY when you need a feature no typed tool exposes AND you control the environment. `script` must be a JXA program that defines `function run(argv)` and returns a JSON-encoded string. `arg` is an optional JSON-serialisable value passed as argv[0] (defaults to `{}`). Returns { result } where result is the parsed JSON output of the script (arbitrary shape). Side effects: may mutate, delete, or exfiltrate any OmniFocus data the user has access to. Use sync_trigger separately if the script mutated data and you need it to propagate. Example: run_jxa_script({ script: \"function run(argv) { return JSON.stringify(Application('OmniFocus').version()); }\" })",Iv=z.object({script:z.string().min(1).describe("Raw JXA script body. Must define `function run(argv)` and return a JSON-encoded string."),arg:z.unknown().optional().describe("Optional JSON-serialisable argument passed to `run()` as argv[0]. Defaults to `{}`.")});async function vv(t,e){if(typeof e.adapter.runJxaScript!="function")throw new y("run_jxa_script is not available in this adapter configuration",{details:{reason:"raw-script-unavailable"},suggestion:"Start the server with OMNIFOCUS_ALLOW_RAW_SCRIPT=1 to enable raw-script tools."});(e.logger??R).info({event:"raw_script.invoked",tool:"run_jxa_script",scriptLength:t.script.length,script:t.script},"raw JXA script invoked");let r=await e.adapter.runJxaScript(t.script,t.arg);return p({result:r},e.makeMeta({syncPending:true,humanReadableSummary:"Ran JXA script."}))}function xp(t,e,n){return n.allowRawScript?t.registerTool("run_jxa_script",{description:Ga,inputSchema:Iv.shape},async r=>{let o=await vv(r,e);return u(o)}):null}var Va='\u26A0 DANGEROUS \u2014 raw OmniJS escape hatch. Executes an arbitrary Omni Automation (OmniJS) script against OmniFocus with FULL Automation privileges (read, write, delete, move, sync, plug-in APIs). Only available when the server was started with OMNIFOCUS_ALLOW_RAW_SCRIPT=1. Every call is audit-logged with the full script body. Do NOT use this for operations covered by the typed tools (task_*, project_*, plugin_invoke, etc.) \u2014 typed tools are safer, idempotent, and return structured results. Use ONLY when you need a feature no typed tool exposes AND you control the environment. `script` is a raw OmniJS program; the serialised result must be JSON-encodable. `arg` is an optional JSON-serialisable value forwarded through the callback-file bridge (defaults to `{}`). Returns { result } where result is the parsed JSON output of the script (arbitrary shape). Side effects: may mutate, delete, or exfiltrate any OmniFocus data the user has access to. Use sync_trigger separately if the script mutated data and you need it to propagate. Example: run_omnijs_script({ script: "flattenedProjects.length" })',Tv=z.object({script:z.string().min(1).describe("Raw OmniJS script body. Must produce a JSON-encodable result."),arg:z.unknown().optional().describe("Optional JSON-serialisable argument forwarded through the callback-file bridge. Defaults to `{}`.")});async function wv(t,e){if(typeof e.adapter.runOmniJsScript!="function")throw new y("run_omnijs_script is not available in this adapter configuration",{details:{reason:"raw-script-unavailable"},suggestion:"Start the server with OMNIFOCUS_ALLOW_RAW_SCRIPT=1 to enable raw-script tools."});(e.logger??R).info({event:"raw_script.invoked",tool:"run_omnijs_script",scriptLength:t.script.length,script:t.script},"raw OmniJS script invoked");let r=await e.adapter.runOmniJsScript(t.script,t.arg);return p({result:r},e.makeMeta({syncPending:true,humanReadableSummary:"Ran OmniJS script."}))}function Dp(t,e,n){return n.allowRawScript?t.registerTool("run_omnijs_script",{description:Va,inputSchema:Tv.shape},async r=>{let o=await wv(r,e);return u(o)}):null}var Sv=[{aliases:["sunday","sundays","sun"],weekday:"sunday"},{aliases:["monday","mondays","mon"],weekday:"monday"},{aliases:["tuesday","tuesdays","tue","tues"],weekday:"tuesday"},{aliases:["wednesday","wednesdays","wed"],weekday:"wednesday"},{aliases:["thursday","thursdays","thu","thurs"],weekday:"thursday"},{aliases:["friday","fridays","fri"],weekday:"friday"},{aliases:["saturday","saturdays","sat"],weekday:"saturday"}],Vn=new Map(Sv.flatMap(({aliases:t,weekday:e})=>t.map(n=>[n,e]))),bv=new Map([["a",1],["an",1],["one",1],["two",2],["three",3],["four",4],["five",5],["six",6],["seven",7],["eight",8],["nine",9],["ten",10],["eleven",11],["twelve",12]]),jv=new Map([["first",1],["1st",1],["second",2],["2nd",2],["third",3],["3rd",3],["fourth",4],["4th",4],["last","last"],["final","last"]]);function _v(t){return t.toLowerCase().replace(/\s+/g," ").trim()}function Xa(t){if(!t)return null;let e=Number.parseInt(t,10);if(Number.isFinite(e)&&e>=1)return e;let n=bv.get(t);return n!==void 0?n:null}var Rp=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];function Ap(t){return [...t].sort((e,n)=>Rp.indexOf(e)-Rp.indexOf(n))}function Pv(t){if(t.length===0)return "";if(t.length===7)return "every day";let e=Ap(t),n=new Set(e),r=new Set(["monday","tuesday","wednesday","thursday","friday"]);if(n.size===5&&[...r].every(i=>n.has(i)))return "every weekday";if(n.size===2&&n.has("saturday")&&n.has("sunday"))return "every weekend";let o=i=>i.charAt(0).toUpperCase()+i.slice(1),a=e.map(o);return a.length===1?`every ${a[0]}`:a.length===2?`every ${a[0]} and ${a[1]}`:`every ${a.slice(0,-1).join(", ")}, and ${a[a.length-1]}`}function Ov(t){return t==="last"?"last":["first","second","third","fourth"][t-1]??String(t)}function xv(t){return t==="start-again"?"after I complete it":t==="due-again"?"from the due date":""}function Ka(t,e){let n;if(t.weekdays&&t.weekdays.length>0)n=Pv(t.weekdays),t.steps!==1&&(n=`${n} every ${t.steps} weeks`);else if(t.monthlyAnchor){if("day"in t.monthlyAnchor)n=`the ${t.monthlyAnchor.day} of every month`;else {let a=t.monthlyAnchor,i=s=>s.charAt(0).toUpperCase()+s.slice(1);n=`the ${Ov(a.position)} ${i(a.weekday)} of every month`;}t.steps!==1&&(n=`${n.replace("every month",`every ${t.steps} months`)}`);}else {let a=t.steps===1?t.unit.replace(/s$/,""):t.unit;n=t.steps===1?`every ${a}`:`every ${t.steps} ${a}`;}let r=xv(t.method);return [n,e,r].filter(a=>a.length>0).join(", ")}function Dv(t){let e=[{re:/\bafter i complete (it|the task)?\b/,method:"start-again"},{re:/\bafter completion\b/,method:"start-again"},{re:/\bfrom completion\b/,method:"start-again"},{re:/\bonce completed\b/,method:"start-again"},{re:/\bfrom the due date\b/,method:"due-again"},{re:/\bfrom (its|the) due date\b/,method:"due-again"}];for(let{re:n,method:r}of e){let o=t.match(n);if(o)return {method:r,consumed:o[0]}}return {method:"fixed",consumed:""}}function Rv(t){let e=t.match(/\bat (\d{1,2})(?::(\d{2}))?\s*(am|pm)?\b/);if(!e)return "";let n=e[1],r=e[2]??"00",o=e[3]??"";return `at ${n}:${r}${o}`.replace(":00am","am").replace(":00pm","pm").trim()}function Av(t){let e=t.match(/\bfor (\d+|a|an|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+(day|week|month|year)s?\b/);if(e){let o=Xa(e[1]),a=e[2];if(o!==null)return `for ${o} ${a}${o===1?"":"s"}`}let n=t.match(/\buntil (\d{4}-\d{2}-\d{2}|\w+ \d{1,2}(?:,? \d{4})?)\b/);if(n)return `until ${n[1]}`;let r=t.match(/\b(\d+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+times\b/);if(r){let o=Xa(r[1]);if(o!==null)return `${o} times`}return ""}function Cv(t){if(/\bdaily\b/.test(t))return {rule:{unit:"days",steps:1}};if(/\bweekly\b/.test(t))return {rule:{unit:"weeks",steps:1}};if(/\bmonthly\b/.test(t))return {rule:{unit:"months",steps:1}};if(/\b(yearly|annually)\b/.test(t))return {rule:{unit:"years",steps:1}};if(/\b(biweekly|fortnightly)\b/.test(t))return {rule:{unit:"weeks",steps:2}};if(/\bbimonthly\b/.test(t))return {rule:{unit:"months",steps:2}};if(/\bevery weekday\b/.test(t))return {rule:{unit:"weeks",steps:1,weekdays:["monday","tuesday","wednesday","thursday","friday"]}};if(/\bevery weekend\b/.test(t))return {rule:{unit:"weeks",steps:1,weekdays:["saturday","sunday"]}};let e=t.match(/\bthe (first|second|third|fourth|last|final|1st|2nd|3rd|4th)\s+([a-z]+)\s+of (every|each)?\s*month\b/);if(e){let i=jv.get(e[1]??""),s=Vn.get(e[2]??"");if(i!==void 0&&s!==void 0)return {rule:{unit:"months",steps:1,monthlyAnchor:{weekday:s,position:i}}}}let n=t.match(/\bthe (\d{1,2})(?:st|nd|rd|th)?\s+of (every|each)?\s*month\b/);if(n){let i=Number.parseInt(n[1]??"",10);if(Number.isFinite(i)&&i>=1&&i<=31)return {rule:{unit:"months",steps:1,monthlyAnchor:{day:i}}}}let r=t.match(/\bevery (\d+|a|an|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+(minute|hour|day|week|month|year)s?\b/);if(r){let i=Xa(r[1]),s=`${r[2]}s`;if(i!==null&&i>=1)return {rule:{unit:s,steps:i}}}let o=t.match(/\bevery other\s+(minute|hour|day|week|month|year)\b/);if(o)return {rule:{unit:`${o[1]}s`,steps:2}};if(/\bevery other\s+([a-z]+)\b/.test(t)){let i=t.match(/\bevery other\s+([a-z]+)\b/),s=i&&Vn.get(i[1]??"");if(s)return {rule:{unit:"weeks",steps:2,weekdays:[s]}}}let a=t.match(/\bevery\s+([a-z, ]+?)(?=$|\s+(at|after|from|once|until|for))/);if(a){let s=(a[1]??"").replace(/\band\b/g,",").split(/[, ]+/).map(d=>d.trim()).filter(d=>d.length>0),c=[];for(let d of s){let l=Vn.get(d);l&&c.push(l);}if(c.length>0)return {rule:{unit:"weeks",steps:1,weekdays:Array.from(new Set(c))}}}return null}function Cp(t){if(!t?.trim())return {kind:"error",reason:"no-repetition-detected"};let e=_v(t),n=Dv(e),r=n.consumed?e.replace(n.consumed," ").replace(/\s+/g," ").trim():e,o=Rv(r),a=Av(r),i=Cv(r);if(!i)return {kind:"error",reason:"no-repetition-detected",suggestion:"Try a phrase like 'every Monday', 'weekly', 'every 3 days', or 'monthly'."};let s={method:n.method,unit:i.rule.unit,steps:i.rule.steps,...i.rule.weekdays?{weekdays:Ap(i.rule.weekdays)}:{},...i.rule.monthlyAnchor?{monthlyAnchor:i.rule.monthlyAnchor}:{}},c=r.match(/\bevery other\s+([a-z]+)\b/);if(c){let l=Vn.get(c[1]??"");if(l){let m=[o,a].filter(Boolean).join(", ");return {kind:"ambiguous",interpretations:[{rule:s,description:Ka(s,m)},{rule:{method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:l,position:1}},description:Ka({method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:l,position:1}},m)}]}}}let d=[o,a].filter(Boolean).join(", ");return {kind:"ok",rule:s,normalizedDescription:Ka(s,d)}}var Ya=`Deterministic prose-to-RepetitionRule helper. Pass a natural-language phrase ('every Monday', 'every 3 days', 'first Tuesday of every month') and receive a structured RepetitionRule plus a normalized description to confirm with the user. Returns one of three shapes: { kind: 'ok', rule, normalizedDescription } when the prose maps to one rule; { kind: 'ambiguous', interpretations[] } when prose admits multiple valid readings (typically 2-3) \u2014 agent picks one with the user; { kind: 'error', reason, suggestion? } for no-repetition-detected or unsupported-pattern. Supported patterns: daily/weekly/monthly/yearly, every-N-days/weeks/months/years, every weekday/weekend, every {Mon|Tue|...}, nth-weekday-of-month, nth-day-of-month, completion-relative phrasing ('after I complete it'). Time-of-day and end-conditions surface in normalizedDescription only \u2014 the canonical RepetitionRule schema doesn't carry those fields. Do NOT use this tool when the agent already has a structured RepetitionRule from another source \u2014 call task_set_repetition directly instead. Prefer this helper over ad-hoc LLM translation whenever the user's repetition phrasing is the only signal. No model calls; no side effects. Use with task_set_repetition or task_create. Example: repetition_from_prose({ prose: "every Monday" }) Example: repetition_from_prose({ prose: "every 3 days after I complete it" })`,Ev=z.object({prose:z.string().min(1).describe("Natural-language phrase describing a repetition cadence. Examples: 'every Monday', 'every other Tuesday at 10am', 'first Thursday of every month after I complete it'."),anchor:z.object({dueDate:z.string().optional().describe("Optional ISO-8601 due-date anchor."),deferDate:z.string().optional().describe("Optional ISO-8601 defer-date anchor.")}).optional().describe("Optional date anchor \u2014 currently informational. The grammar reads time-of-day from prose into normalizedDescription; embedding it into a date is the agent's responsibility once it has anchor context.")});async function Mv(t,e){let n=Cp(t.prose),r=e.makeMeta();if(n.kind!=="ambiguous")return p(n,r);let o=e.replayStore??ve,a=n.interpretations.map(s=>s.description),i=o.register(a,async s=>{let c=n.interpretations[s];return p({kind:"ok",rule:c.rule,normalizedDescription:c.description},e.makeMeta())});return St(`"${t.prose}" matches multiple repetition patterns. Which did you mean?`,i,r,a.map((s,c)=>({index:c,label:s})),t.anchor!==void 0?{prose:t.prose,anchor:t.anchor}:{prose:t.prose})}function Ep(t,e){return t.registerTool("repetition_from_prose",{description:Ya,inputSchema:Ev.shape},async n=>{let r=await Mv(n,e);return u(r)})}var Za="List projects due for review in OmniFocus \u2014 those whose next review date is today or earlier, or has never been set. Sorted by next review date ascending (overdue first, never-reviewed first). Do not use to get all projects; prefer project_list for that. Returns each project's id, name, nextReviewDate, lastReviewDate, and reviewIntervalDays. Safe to call repeatedly; no side effects, no writes. Example: review_list_due()",Fv=z.object({});async function Uv(t,e){let n=await e.reviewService.listDue(),r=e.makeMeta({cacheHit:n.cacheHit});return p({projects:n.projects},r)}function Mp(t,e){return t.registerTool("review_list_due",{description:Za,inputSchema:Fv.shape},async n=>{let r=await Uv(n,e);return u(r)})}var Qa=`Mark a project as reviewed in OmniFocus \u2014 sets lastReviewDate to now and advances nextReviewDate by the project's review interval. Use this after completing a weekly review of a project. Do not use to change the review interval; prefer review_set_interval for that. Returns { id, name, lastReviewDate, nextReviewDate } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted between write and read), and the dates echo back the new schedule so the agent can describe the result without a follow-up read. Side effects: writes to OmniFocus; sets syncPending = true. Example: review_mark_reviewed({ id: "prj123" })`,Lv=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function Bv(t,e){let n=await e.reviewService.markReviewed(k.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:Ln()}))}function Fp(t,e){return t.registerTool("review_mark_reviewed",{description:Qa,inputSchema:Lv.shape},async n=>{let r=await Bv(n,e);return u(r)})}var es=`Convenience alias for review_mark_reviewed \u2014 mark a single project as reviewed, setting lastReviewDate to now and advancing nextReviewDate. Use when you have a project id and want a single-call review operation. Do not use to list projects due for review; prefer review_list_due for that. Returns { id, name, lastReviewDate, nextReviewDate } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted between write and read), and the dates echo back the new schedule so the agent can describe the result without a follow-up read. Side effects: writes to OmniFocus; sets syncPending = true. Example: project_mark_reviewed({ id: "prj123" })`,Jv=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function $v(t,e){let n=await e.reviewService.markReviewed(k.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:Ln()}))}function Lp(t,e){return t.registerTool("project_mark_reviewed",{description:es,inputSchema:Jv.shape},async n=>{let r=await $v(n,e);return u(r)})}var ns=`Set a project's review interval in OmniFocus \u2014 updates how many days between reviews. Use null to remove the recurring schedule. Do not use to mark a project as reviewed; prefer review_mark_reviewed for that. Returns { id, name, reviewIntervalDays } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted), and reviewIntervalDays echoes back the new value (or null when cleared) so the agent can describe the change without a follow-up read. Side effects: writes to OmniFocus; sets syncPending = true. Example: review_set_interval({ id: "prj123", days: 7 }) Example: review_set_interval({ id: "prj123", days: null })`,Wv=z.object({id:z.string().min(1).describe("Persistent ID of the project to update."),days:z.number().int().min(1).nullable().describe("Review interval in days. Pass null to remove the recurring review schedule.")});async function Hv(t,e){let n=await e.reviewService.setInterval(k.of(t.id),t.days);return p({id:t.id,name:n.name,reviewIntervalDays:n.reviewIntervalDays},e.makeMeta({syncPending:true,humanReadableSummary:Yd(t.days)}))}function Bp(t,e){return t.registerTool("review_set_interval",{description:ns,inputSchema:Wv.shape},async n=>{let r=await Hv(n,e);return u(r)})}var rs=`Set or clear a project's next review date directly. Use when the user wants to reschedule a review independent of the recurring interval \u2014 'push the Q3 review to next Monday' without changing the cadence. Do NOT use to mark a project as reviewed (prefer review_mark_reviewed) or to change the recurring interval (prefer review_set_interval). Pass projectId and nextReviewDate (ISO-8601 date string), or pass null for nextReviewDate to clear (project becomes 'not scheduled'). Past-dated values are accepted and surface the project as overdue immediately \u2014 matches OmniFocus's own UX. Returns { id, name, nextReviewDate } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted), and nextReviewDate echoes back the new value (or null when cleared) so the agent can describe the change without a follow-up read. Errors: NOT_FOUND when projectId does not exist. Side effects: writes to OmniFocus; invalidates project + review caches; sets syncPending = true. Example: project_set_next_review_date({ projectId: "prj123", nextReviewDate: "2026-05-05" }) Example: project_set_next_review_date({ projectId: "prj123", nextReviewDate: null })`,zv=z.object({projectId:z.string().min(1).describe("Persistent ID of the project whose next review date should change."),nextReviewDate:z.union([oe(),z.null()]).describe("Next review date as ISO-8601 (with offset). Pass null to clear the schedule. Past-dated values are accepted and mark the project as overdue immediately.")});async function Gv(t,e){let n=await e.reviewService.setNextReviewDate(k.of(t.projectId),t.nextReviewDate);return p({id:t.projectId,name:n.name,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:Zd(t.nextReviewDate)}))}function Jp(t,e){return t.registerTool("project_set_next_review_date",{description:rs,inputSchema:zv.shape},async n=>{let r=await Gv(n,e);return u(r)})}var os=`Full-text search across OmniFocus task names and/or notes. Use for finding tasks by content when you don't know the ID. Supports optional filters (project, tags, flagged, completion status) and cursor pagination. Do NOT use when a known task ID is available (use task_get instead). Returns tasks[] with pagination; safe to call repeatedly; no side effects. Example: search_query({ q: "dentist" }) Example: search_query({ q: "report", projectId: "prj123", completed: "exclude" })`,qv=z.object({q:z.string().describe("Search query. Case-insensitive substring match. Empty string matches all tasks (useful with filters)."),scope:z.enum(["name","note","all"]).optional().describe("'name' = search task names only; 'note' = search notes only; 'all' = both. Default 'all'."),projectId:k.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list."),tagIds:z.array(S.schema).optional().describe("Restrict to tasks carrying ALL of these tags. Get IDs from tag_list."),flagged:z.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed only; 'any' = both. Default 'any'."),limit:z.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 100."),cursor:z.string().optional().describe("Opaque cursor from a previous search_query response. Must use identical filters \u2014 changing filters returns a ValidationError.")});async function Vv(t,e){let n={q:t.q,...t.scope!==void 0?{scope:t.scope}:{},...t.projectId!==void 0?{projectId:t.projectId}:{},...t.tagIds!==void 0?{tagIds:t.tagIds}:{},...t.flagged!==void 0?{flagged:t.flagged}:{},...t.completed!==void 0?{completed:t.completed}:{},...t.limit!==void 0?{limit:t.limit}:{},...t.cursor!==void 0?{cursor:t.cursor}:{}},r=await e.searchService.search(n),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return p({tasks:r.tasks},a,o)}function $p(t,e){return t.registerTool("search_query",{description:os,inputSchema:qv.shape},async n=>{let r=await Vv(n,e);return u(r)})}var as="Return the last OmniFocus sync state without triggering a new sync. Do NOT call this to initiate a sync \u2014 use sync_trigger instead. Use to check whether a previous sync completed before querying cross-device data. Returns { lastSyncAt, inFlight }. lastSyncAt is null if OmniFocus has never synced in this session. Read-only; no side effects. Example: sync_status()",Xv=z.object({});async function Yv(t,e){let n=await e.adapter.getLastSync();return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},e.makeMeta())}function Wp(t,e){return t.registerTool("sync_status",{description:as,inputSchema:Xv.shape},async n=>{let r=await Yv(n,e);return u(r)})}var ss="Kick off an OmniFocus sync with Omni Sync Server. Do not call when no mutations have been made; prefer checking meta.syncPending first. Call this after any sequence of mutations (task_create, task_update, folder_create, etc.) when you need changes to appear on other devices. The sync starts immediately but completes asynchronously \u2014 this tool does not block until done. Returns meta.syncPending = false to confirm the sync was initiated. Side effects: triggers a sync request to Omni Sync Server. Example: sync_trigger()",Qv=z.object({});async function eT(t,e){let n=await e.adapter.syncTrigger();e.cache!==void 0&&sd(e.cache);let r=e.makeMeta({syncPending:false});return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},r)}function Hp(t,e){return t.registerTool("sync_trigger",{description:ss,inputSchema:Qv.shape},async n=>{let r=await eT(n,e);return u(r)})}var cs=`Create a new tag in OmniFocus. Optionally nest it under an existing parent tag (get IDs from tag_list). Do not use to move an existing tag; prefer tag_move instead. Returns the new tag's persistent ID. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_create({ name: "errands" }) Example: tag_create({ name: "home", parentId: "tag123" })`,ds=z.object({name:z.string().min(1).describe("Tag name. Must be non-empty."),parentId:S.schema.optional().describe("Parent tag ID to nest under. Omit for a root tag. Get from tag_list."),status:ie(["active","on-hold"],{paused:"on-hold"},"Initial status. Defaults to 'active'. Cannot create a tag in 'dropped' state.").optional(),allowsNextAction:z.boolean().optional().describe("Whether the tag allows next-action selection. Defaults to true.")});async function tT(t,e){let n=await e.tagService.create({name:t.name,...t.parentId!==void 0?{parentId:t.parentId}:{},...t.status!==void 0?{status:t.status}:{},...t.allowsNextAction!==void 0?{allowsNextAction:t.allowsNextAction}:{}}),{tag:r}=await e.tagService.get(n.id),o=e.makeMeta({syncPending:true,humanReadableSummary:Bd(t.name)});return p({tag:r},o)}function zp(t,e){return t.registerTool("tag_create",{description:cs,inputSchema:ds.shape},async n=>{let r=await tT(n,e);return u(r)})}var ls="Preview what tag_create would do without making any changes. Do NOT use to actually create a tag \u2014 use tag_create instead. Returns { description, plannedChanges } describing the tag that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function nT(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await he(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),r.push(`under '${a}'`);}else r.push("at root");t.status!==void 0&&(n.push({field:"status",newValue:t.status}),r.push(`status '${t.status}'`));let o=`Would create tag ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Gp(t,e){return t.registerTool("tag_create_describe",{description:ls,inputSchema:ds.shape},async n=>{let r=await nT(n,e);return u(r)})}var ps=`Hard-delete a tag from OmniFocus. IRREVERSIBLE \u2014 the tag and all its children are removed. Tasks that carried this tag lose it. Get the tag ID from tag_list. Prefer tag_set_status with status='dropped' to preserve history. Returns the deleted tag's ID on success. Side effects: writes to OmniFocus, sets meta.syncPending = true. Example: tag_delete({ id: "tag123" })`,us=z.object({id:S.schema.describe("Persistent tag ID to delete. Get from tag_list.")});async function oT(t,e){return await e.tagService.delete(t.id),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Kd()}))}function qp(t,e){return t.registerTool("tag_delete",{description:ps,inputSchema:us.shape},async n=>{let r=await oT(n,e);return u(r)})}var ms="Preview what tag_delete would do without making any changes. Do NOT use to actually delete a tag \u2014 use tag_delete instead. Returns { description, plannedChanges } describing the permanent deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function aT(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTag(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=`Would permanently delete tag '${r}' (id: ${t.id}) and all its children. IRREVERSIBLE.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Vp(t,e){return t.registerTool("tag_delete_describe",{description:ms,inputSchema:us.shape},async n=>{let r=await aT(n,e);return u(r)})}var fs='Fetch a single tag by its persistent ID, including task count. Do not use to list multiple tags; prefer tag_list instead. Returns tag details; no side effects. Example: tag_get({ id: "tag123" })',iT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list. IDs are stable across renames.")});async function cT(t,e){let n=await e.tagService.get(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({tag:n.tag},r)}function Kp(t,e){return t.registerTool("tag_get",{description:fs,inputSchema:iT.shape},async n=>{let r=await cT(n,e);return u(r)})}var gs='Get the geographic location trigger currently set on a tag, or null if none. Do not use to set or clear a location; prefer tag_set_location instead. Location-based tags are an OmniFocus Pro feature. Get the tag ID from tag_list. Returns { location } with name, radius, and trigger direction, or null if unset. Safe to call repeatedly; no side effects. Example: tag_get_location({ id: "tag123" })',lT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list.")});async function pT(t,e){let n=await e.tagService.getLocation(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({location:n.location},r)}function Xp(t,e){return t.registerTool("tag_get_location",{description:gs,inputSchema:lT.shape},async n=>{let r=await pT(n,e);return u(r)})}var hs='Fetch up to 100 tags by persistent ID in a single OmniFocus round-trip. Use when you have a set of tag IDs and need full tag objects for all of them. Do NOT use for a single ID \u2014 use tag_get instead. Returns Tag[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry. Example: tag_get_many({ ids: ["tag123", "tag456"] })',Xn=100,uT=z.object({ids:z.array(S.schema).min(0).max(Xn).describe(`Array of tag IDs to fetch (0..${Xn}). Get IDs from tag_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function mT(t,e){if(t.ids.length===0)return p({tags:[]},e.makeMeta());if(t.ids.length>Xn)throw new y(`ids array exceeds the maximum batch size of ${Xn} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTagsMany(t.ids),r=n.filter(s=>s!==null),o=t.ids.filter((s,c)=>n[c]===null),a=o.length>0?[wt(o)]:void 0,i=e.makeMeta({...a!==void 0?{warnings:a}:{}});return p({tags:r},i)}function Zp(t,e){return t.registerTool("tag_get_many",{description:hs,inputSchema:uT.shape},async n=>{let r=await mT(n,e);return u(r)})}var ys='List all tags in OmniFocus, optionally filtered by parent tag or status. Do not use to fetch a single tag by ID; prefer tag_get instead. Returns a flat array \u2014 use parentId to walk the hierarchy one level at a time. Safe to call repeatedly; no side effects. Example: tag_list({}) Example: tag_list({ status: "active", parentId: "tag123" })',gT=z.object({parentId:S.schema.optional().describe("Return only direct children of this tag. Get the ID from a previous tag_list call. Omit for root tags."),status:ie(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"Filter by tag status. Omit to return tags of all statuses.").optional()});async function hT(t,e){let n={...t.parentId!==void 0?{parentId:t.parentId}:{},...t.status!==void 0?{status:t.status}:{}},r=await e.tagService.list(n),o=e.makeMeta({cacheHit:r.cacheHit});return p({tags:r.tags},o)}function Qp(t,e){return t.registerTool("tag_list",{description:ys,inputSchema:gT.shape},async n=>{let r=await hT(n,e);return u(r)})}var ks=`Move a tag to a new parent, or promote it to a root tag by passing parentId=null. Do not use to rename a tag; prefer tag_update instead. Get tag IDs from tag_list. Returns the updated tag's ID and new parentId on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_move({ id: "tag123", parentId: "tag456" }) Example: tag_move({ id: "tag123", parentId: null })`,Is=z.object({id:S.schema.describe("Persistent ID of the tag to move. Get from tag_list."),parentId:S.schema.nullable().describe("New parent tag ID, or null to promote the tag to root level.")});async function kT(t,e){await e.tagService.move(t.id,t.parentId);let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Jd(n.name,"root")}))}function eu(t,e){return t.registerTool("tag_move",{description:ks,inputSchema:Is.shape},async n=>{let r=await kT(n,e);return u(r)})}var vs="Preview what tag_move would do without making any changes. Do NOT use to actually move a tag \u2014 use tag_move instead. Returns { description, plannedChanges } describing the reparenting that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function IT(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTag(t.id)).name;}catch{}let o;if(t.parentId!==null){let i=await he(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`under '${i}'`;}else n.push({field:"parentId",newValue:null}),o="root level";let a=`Would move tag '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function tu(t,e){return t.registerTool("tag_move_describe",{description:vs,inputSchema:Is.shape},async n=>{let r=await IT(n,e);return u(r)})}var Ts='Enable or disable next-action selection for a tag in OmniFocus. When true, tasks with this tag are eligible for next-action promotion. Do not use to change other tag properties; prefer tag_update instead. Get the tag ID from tag_list. Returns the updated tag with allowsNextAction confirmed. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_set_allows_next_action({ id: "tag123", allowsNextAction: true })',vT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),allowsNextAction:z.boolean().describe("true to enable next-action selection; false to disable.")});async function TT(t,e){await e.tagService.setAllowsNextAction(t.id,t.allowsNextAction);let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Ve(n.name)}))}function ru(t,e){return t.registerTool("tag_set_allows_next_action",{description:Ts,inputSchema:vT.shape},async n=>{let r=await TT(n,e);return u(r)})}var ws='Set a geographic location trigger on a tag (OmniFocus Pro only). The trigger fires when arriving at, leaving, or both for the specified radius. Do not use to read the current location; prefer tag_get_location instead. Get the tag ID from tag_list. Returns FeatureRequiresPro on OmniFocus Standard installs. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_set_location({ id: "tag123", latitude: 37.785, longitude: -122.407, radiusMeters: 200, trigger: "arriving", name: "Office" })',wT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),latitude:z.number().min(-90).max(90).describe("Latitude in decimal degrees (\u221290 to 90)."),longitude:z.number().min(-180).max(180).describe("Longitude in decimal degrees (\u2212180 to 180)."),radiusMeters:z.number().min(0).describe("Trigger radius in metres. Must be \u2265 0."),trigger:z.enum(["entering","leaving","both"]).describe("When to trigger: 'entering', 'leaving', or 'both'."),name:z.string().nullable().optional().describe("Optional human-readable name for the location (e.g. 'Home', 'Office').")});async function ST(t,e){await e.tagService.setLocation(t.id,{name:t.name??null,latitude:t.latitude,longitude:t.longitude,radiusMeters:t.radiusMeters,trigger:t.trigger});let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Ve(n.name)}))}function ou(t,e){return t.registerTool("tag_set_location",{description:ws,inputSchema:wT.shape},async n=>{let r=await ST(n,e);return u(r)})}var Ss='Set the lifecycle status of a tag to active, on-hold, or dropped. Dropped tags are hidden in OmniFocus but not deleted. Do not use to permanently remove a tag; prefer tag_delete instead. Get the tag ID from tag_list. Returns the updated tag with the confirmed status. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_set_status({ id: "tag123", status: "on-hold" }) Example: tag_set_status({ id: "tag123", status: "active" })',jT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),status:ie(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status for the tag.")});async function _T(t,e){await e.tagService.setStatus(t.id,t.status);let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Ve(n.name)}))}function au(t,e){return t.registerTool("tag_set_status",{description:Ss,inputSchema:jT.shape},async n=>{let r=await _T(n,e);return u(r)})}var js='Update mutable fields on an existing tag (partial patch). Only supplied fields are changed; omit a field to leave it unchanged. Do not use to move a tag to a different parent; prefer tag_move instead. Get the tag ID from tag_list. Returns the updated tag on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_update({ id: "tag123", name: "shopping" }) Example: tag_update({ id: "tag123", status: "dropped" })',_s=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),name:z.string().min(1).optional().describe("New tag name. Must be non-empty if supplied."),parentId:S.schema.nullable().optional().describe("New parent tag ID. Pass null to promote to root. Get from tag_list."),status:ie(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status.").optional(),allowsNextAction:z.boolean().optional().describe("Whether the tag allows next-action selection in OmniFocus.")});async function PT(t,e){let{id:n,...r}=t;await e.tagService.update(n,{...r.name!==void 0?{name:r.name}:{},...r.parentId!==void 0?{parentId:r.parentId}:{},...r.status!==void 0?{status:r.status}:{},...r.allowsNextAction!==void 0?{allowsNextAction:r.allowsNextAction}:{}});let{tag:o}=await e.tagService.get(n);return p({tag:o},e.makeMeta({syncPending:true,humanReadableSummary:Ve(o.name)}))}function su(t,e){return t.registerTool("tag_update",{description:js,inputSchema:_s.shape},async n=>{let r=await PT(n,e);return u(r)})}var Ps="Preview what tag_update would do without making any changes. Do NOT use to actually update a tag \u2014 use tag_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function OT(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getTag(t.id);o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`)),t.status!==void 0&&(n.push({field:"status",newValue:t.status,oldValue:i.status}),r.push(`set status to '${t.status}'`)),t.allowsNextAction!==void 0&&(n.push({field:"allowsNextAction",newValue:String(t.allowsNextAction),oldValue:String(i.allowsNextAction)}),r.push(`set allowsNextAction to ${t.allowsNextAction}`));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update tag '${o}': ${r.join(", ")}.`:`Would update tag '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function iu(t,e){return t.registerTool("tag_update_describe",{description:Ps,inputSchema:_s.shape},async n=>{let r=await OT(n,e);return u(r)})}var Os=`Apply inbox-triage style assignments to many tasks in one batch \u2014 move to a project, diff tags additively, set defer/due/flagged. Tighter schema than task_batch_update; designed for the inbox-triage prompt's confirm step. Each assignment is { taskId, projectId?, addTagIds?, removeTagIds?, deferDate?, dueDate?, flagged? }. Tag diffs are resolved via a pre-read of current tagIds; specifying both addTagIds and removeTagIds for the same tag is a no-op (remove wins). Atomicity: best-effort, per-item \u2014 OF has no transactional batch. An item succeeds only if both its move (if requested) AND its non-move update succeed. Failures are reported with errorCode prefixed 'move:' or 'update:'. Returns { assigned: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each assignment without a follow-up read. Do NOT use this tool for full task replacement \u2014 use task_update or task_batch_update for those. Prefer task_batch_assign over a sequence of single task_update calls when you have a confirmed triage plan for multiple tasks. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_assign({ assignments: [{ taskId: "abc123", projectId: "prj456", flagged: true }, { taskId: "abc789", addTagIds: ["tag1"] }] })`,xT=z.object({taskId:h.schema.describe("Persistent task ID."),projectId:k.schema.optional().describe("If set, move the task to this project before applying other changes."),addTagIds:z.array(S.schema).optional().describe("Tag IDs to add. Combined with removeTagIds via current-tagIds pre-read."),removeTagIds:z.array(S.schema).optional().describe("Tag IDs to remove. Wins over addTagIds when the same ID appears in both."),deferDate:z.string().datetime({offset:true}).nullable().optional().describe("Defer date as ISO-8601 with offset. Null clears the date."),dueDate:z.string().datetime({offset:true}).nullable().optional().describe("Due date as ISO-8601 with offset. Null clears the date."),flagged:z.boolean().optional().describe("Flag or unflag the task.")}).refine(t=>t.projectId!==void 0||t.addTagIds!==void 0||t.removeTagIds!==void 0||t.deferDate!==void 0||t.dueDate!==void 0||t.flagged!==void 0,{message:"Each assignment must set at least one of projectId, addTagIds, removeTagIds, deferDate, dueDate, or flagged"}),DT=z.object({assignments:z.array(xT).min(1).describe("Triage assignments \u2014 one per task. Must contain at least one item.")});function RT(t){return t.map((e,n)=>e.projectId!==void 0?n:-1).filter(e=>e>=0)}function AT(t){return t.map((e,n)=>e.addTagIds!==void 0||e.removeTagIds!==void 0?n:-1).filter(e=>e>=0)}function xs(t,e,n){let r=new Set(t.map(String));for(let o of e??[])r.add(String(o));for(let o of n??[])r.delete(String(o));return Array.from(r).map(o=>S.of(o))}function CT(t,e){let n={};return t.deferDate!==void 0&&(n.deferDate=t.deferDate),t.dueDate!==void 0&&(n.dueDate=t.dueDate),t.flagged!==void 0&&(n.flagged=t.flagged),(t.addTagIds!==void 0||t.removeTagIds!==void 0)&&(n.tagIds=xs(e??[],t.addTagIds,t.removeTagIds)),Object.keys(n).length>0?n:null}async function Ds(t,e){let n=t.assignments,r=n.map(w=>w.taskId),o=await e.adapter.getTasksMany(r),a=new Map;for(let w=0;w<r.length;w++){let D=o[w];D!=null&&a.set(r[w],D.name);}let i=AT(n),s=new Map;if(i.length>0){let w=i.map(j=>n[j].taskId),D=await e.adapter.getTasksMany(w);for(let j=0;j<i.length;j++){let B=D[j];B&&s.set(i[j],B.tagIds);}}let c=RT(n),d=c.length>0?await e.adapter.batchMoveTasks(c.map(w=>({id:n[w].taskId,destination:{projectId:n[w].projectId}}))):{succeeded:[],failed:[]},l=new Map;for(let w of d.succeeded){let D=c[w.index];l.set(D,"ok");}for(let w of d.failed){let D=c[w.index];l.set(D,{errorCode:w.errorCode,message:w.message});}let m=[];for(let w=0;w<n.length;w++){let D=n[w],j=l.get(w);if(j!==void 0&&j!=="ok")continue;let B=CT(D,s.get(w));B!==null&&m.push({origIdx:w,id:D.taskId,patch:B});}let f=m.length>0?await e.adapter.batchUpdateTasks(m.map(w=>({id:w.id,patch:w.patch}))):{succeeded:[],failed:[]},g=new Set(f.succeeded.map(w=>w.index)),v=new Map;for(let w of f.failed)v.set(w.index,{errorCode:w.errorCode,message:w.message});let I=[],T=[];for(let w=0;w<n.length;w++){let D=n[w],j=l.get(w);if(j!==void 0&&j!=="ok"){T.push({index:w,errorCode:`move:${j.errorCode}`,message:j.message});continue}let B=m.findIndex(_=>_.origIdx===w);if(B>=0){if(g.has(B))I.push({index:w,value:D.taskId});else if(v.has(B)){let _=v.get(B);T.push({index:w,errorCode:`update:${_.errorCode}`,message:_.message});}}else I.push({index:w,value:D.taskId});}if(e.cache!==void 0&&I.length>0)for(let w of I){let D=n[w.index];b(e.cache,{taskId:D.taskId,...D.projectId!==void 0&&{projectId:D.projectId}});}let E=I.map(w=>({index:w.index,value:{id:w.value,name:a.get(w.value)??""}}));return p({assigned:E,failed:T},e.makeMeta({syncPending:I.length>0}))}function cu(t,e){return t.registerTool("task_batch_assign",{description:Os,inputSchema:DT.shape},async n=>{let r=await Ds(n,e);return u(r)})}var Rs='Mark many OmniFocus tasks complete in a single JXA round trip. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each completion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_complete calls whenever you are completing more than one task. Each item is { id, at? } where `at` is an optional ISO-8601 completion timestamp (defaults to now). Already-completed tasks are not treated specially here \u2014 use task_complete\'s idempotent noChange path if you need that per-item semantics. Returns { completed: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each completion without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_complete({ items: [{ id: "abc123" }, { id: "abc456" }] })',ET=z.object({id:h.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("Optional ISO-8601 completion time; defaults to now.")}),MT=z.object({items:z.array(ET).min(1).describe("Array of { id, at? } items. Must contain at least one item.")});async function NT(t,e){let n=t.items.map(c=>c.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let c=0;c<n.length;c++){let d=r[c];d!=null&&o.set(n[c],d.name);}let a=t.items.map(c=>({id:c.id,...c.at!==void 0&&{at:new Date(c.at)}})),i=await e.adapter.batchCompleteTasks(a);if(e.cache!==void 0)for(let c of i.succeeded){let d=t.items[c.index];d!==void 0&&b(e.cache,{taskId:d.id});}let s=i.succeeded.map(c=>({index:c.index,value:{id:c.value,name:o.get(c.value)??""}}));return p({completed:s,failed:i.failed},e.makeMeta({syncPending:i.succeeded.length>0,humanReadableSummary:xd(i.succeeded.length)}))}function du(t,e){return t.registerTool("task_batch_complete",{description:Rs,inputSchema:MT.shape},async n=>{let r=await NT(n,e);return u(r)})}var As='Create many OmniFocus tasks in a single JXA round trip. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: once the batch reaches OmniFocus, each task succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_create calls whenever you are creating more than one task. Each item accepts the same shape as task_create (name, optional projectId or parentTaskId, note, flagged, dueDate, deferDate, estimatedMinutes, tagIds, sequential, completedByChildren). Returns { created: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name (echoed from the input) so the agent can describe each new task without a follow-up read. Side effects: creates tasks in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the tasks to appear on other devices. Example: task_batch_create({ items: [{ name: "Buy milk" }, { name: "Call dentist", projectId: "prj123" }] })',FT=z.object({name:z.string().min(1).describe("Task name. Required, non-empty."),projectId:k.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:h.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z.string().optional().describe("Plain-text note."),flagged:z.boolean().optional().describe("Flag the task."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().optional().describe("Estimated duration in minutes."),tagIds:z.array(S.schema).optional().describe("Tag IDs to apply."),sequential:z.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Complete when all subtasks complete.")}).refine(t=>!(t.projectId!==void 0&&t.parentTaskId!==void 0),{message:"Supply at most one of projectId or parentTaskId",path:["projectId"]}),Cs=z.object({items:z.array(FT).min(1).describe("Array of task inputs. Must contain at least one item.")});async function UT(t,e){let n=t.items.map(a=>({name:a.name,...a.projectId!==void 0&&{projectId:a.projectId},...a.parentTaskId!==void 0&&{parentId:a.parentTaskId},...a.note!==void 0&&{note:a.note},...a.flagged!==void 0&&{flagged:a.flagged},...a.dueDate!==void 0&&{dueDate:a.dueDate},...a.dueDateFloating!==void 0&&{dueDateFloating:a.dueDateFloating},...a.deferDate!==void 0&&{deferDate:a.deferDate},...a.deferDateFloating!==void 0&&{deferDateFloating:a.deferDateFloating},...a.estimatedMinutes!==void 0&&{estimatedMinutes:a.estimatedMinutes},...a.tagIds!==void 0&&{tagIds:a.tagIds},...a.sequential!==void 0&&{sequential:a.sequential},...a.completedByChildren!==void 0&&{completedByChildren:a.completedByChildren}})),r=await e.adapter.batchCreateTasks(n);if(e.cache!==void 0&&r.succeeded.length>0){let a=new Set;for(let i of r.succeeded){let c=t.items[i.index]?.projectId;c!==void 0&&!a.has(c)&&(a.add(c),b(e.cache,{projectId:c}));}a.size===0&&b(e.cache,{});}let o=r.succeeded.map(a=>({index:a.index,value:{id:a.value,name:t.items[a.index]?.name??""}}));return p({created:o,failed:r.failed},e.makeMeta({syncPending:r.succeeded.length>0,humanReadableSummary:bt(r.succeeded.length)}))}function lu(t,e){return t.registerTool("task_batch_create",{description:As,inputSchema:Cs.shape},async n=>{let r=await UT(n,e);return u(r)})}var Es="Preview what task_batch_create would do without making any changes. Do NOT use to actually create tasks \u2014 use task_batch_create instead. Returns { description, plannedChanges } summarising all tasks that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function LT(t,e){let n=[],r=[];for(let a of t.items){let i;a.projectId!==void 0?i=`project '${await _t(e.adapter,a.projectId)}'`:a.parentTaskId!==void 0?i=`subtask of '${await Pt(e.adapter,a.parentTaskId)}'`:i="Inbox",r.push(`'${a.name}' (${i})`),n.push({field:"name",newValue:a.name}),a.projectId!==void 0?n.push({field:"projectId",newValue:a.projectId}):a.parentTaskId!==void 0&&n.push({field:"parentTaskId",newValue:a.parentTaskId});}let o=`Would create ${t.items.length} task${t.items.length===1?"":"s"}: ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function pu(t,e){return t.registerTool("task_batch_create_describe",{description:Es,inputSchema:Cs.shape},async n=>{let r=await LT(n,e);return u(r)})}var Zn=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function Qn(t,e){switch(t.kind){case "next-work-day":return BT(t.at??"morning",e);case "next-weekday":return JT(t.weekday,t.at??"morning",e);case "in-business-days":return $T(t.days,e);case "after-event":throw new ge("task_defer_smart `after-event` variant is not yet implemented \u2014 file a follow-up if you need calendar-event-anchored defers.");case "next-month-start":return WT(e);case "explicit-with-skip-weekends":return HT(t.date)}}function BT(t,e){let n=new Date(e.now);for(n.setDate(n.getDate()+1);Ms(n);)n.setDate(n.getDate()+1);return Ns(n,mu(t,e)),{resolvedDeferDate:un(n),reason:`next work ${t} (${Zn[n.getDay()]} ${fu(n)})`}}function JT(t,e,n){if(!Number.isInteger(t)||t<0||t>6)throw new y(`weekday must be an integer 0\u20136 (Sun\u2013Sat); got ${String(t)}`);let r=new Date(n.now),o=(t-r.getDay()+7)%7;return o===0&&(o=7),r.setDate(r.getDate()+o),Ns(r,mu(e,n)),{resolvedDeferDate:un(r),reason:`next ${Zn[t]} ${e} (${er(r)} ${fu(r)})`}}function $T(t,e){if(!Number.isInteger(t)||t<1)throw new y(`days must be a positive integer; got ${String(t)}`);let n=new Date(e.now),r=t;for(;r>0;)n.setDate(n.getDate()+1),Ms(n)||r--;return Ns(n,e.morningHour),{resolvedDeferDate:un(n),reason:`${t} business day${t===1?"":"s"} from now (${Zn[n.getDay()]} ${er(n)})`}}function WT(t){let e=new Date(t.now.getFullYear(),t.now.getMonth()+1,1,0,0,0,0);return {resolvedDeferDate:un(e),reason:`start of next month (${er(e)})`}}function HT(t,e){let n=new Date(t);if(Number.isNaN(n.getTime()))throw new y(`date must be a parseable ISO-8601 timestamp; got ${t}`);let r=new Date(n),o=false;for(;Ms(r);)r.setDate(r.getDate()+1),o=true;return {resolvedDeferDate:un(r),reason:o?`${t} \u2192 snapped to ${Zn[r.getDay()]} ${er(r)} (skipped weekend)`:`${t} (no weekend skip needed)`}}function Ms(t){let e=t.getDay();return e===0||e===6}function mu(t,e){return t==="morning"?e.morningHour:e.afternoonHour}function Ns(t,e){t.setHours(e,0,0,0);}function De(t){return t<10?`0${t}`:String(t)}function er(t){return `${t.getFullYear()}-${De(t.getMonth()+1)}-${De(t.getDate())}`}function fu(t){return `${De(t.getHours())}:${De(t.getMinutes())}`}function un(t){let e=-t.getTimezoneOffset(),n=e>=0?"+":"-",r=Math.abs(e),o=`${n}${De(Math.floor(r/60))}:${De(r%60)}`;return `${t.getFullYear()}-${De(t.getMonth()+1)}-${De(t.getDate())}T${De(t.getHours())}:${De(t.getMinutes())}:${De(t.getSeconds())}${o}`}var zT=9,GT=14;function tr(t=process.env){return {morningHour:uu(t.OMNIFOCUS_MORNING_HOUR,zT),afternoonHour:uu(t.OMNIFOCUS_AFTERNOON_HOUR,GT)}}function uu(t,e){if(t===void 0||t==="")return e;let n=Number(t);return !Number.isInteger(n)||n<0||n>23?e:n}var Fs="Batch variant of task_defer_smart: accepts an array of { taskId, intent } and resolves each intent independently. Same intent grammar as task_defer_smart. Do NOT use this for a single task \u2014 prefer task_defer_smart for one entry. Returns { results: [{ taskId, ok: true, resolvedDeferDate, reason } | { taskId, ok: false, error }] } \u2014 per-entry failures surface inline so one malformed entry does not abort the others. Side effects: writes the resolved deferDate to each successful task; dry_run skips writes. Triggers a sync when any entry succeeds. Example: task_batch_defer_smart({ entries: [{ taskId: '...', intent: { kind: 'next-work-day' } }] })",gu=z.enum(["morning","afternoon"]),qT=z.discriminatedUnion("kind",[z.object({kind:z.literal("next-work-day"),at:gu.optional()}),z.object({kind:z.literal("next-weekday"),weekday:z.number().int().min(0).max(6),at:gu.optional()}),z.object({kind:z.literal("in-business-days"),days:z.number().int().positive()}),z.object({kind:z.literal("after-event"),eventId:z.string().min(1)}),z.object({kind:z.literal("next-month-start")}),z.object({kind:z.literal("explicit-with-skip-weekends"),date:z.string().min(1)})]),VT=z.object({entries:z.array(z.object({taskId:h.schema,intent:qT})).min(1).describe("Array of { taskId, intent } pairs. Per-entry failures surface in the results array; one bad entry does not abort siblings."),dry_run:z.boolean().optional().describe("When true, resolves every intent but does NOT write to OmniFocus. Useful for previewing the batch's resolved dates before committing."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key. Identical subsequent calls within the TTL window replay the original results envelope with meta.idempotentReplay = true.")});async function KT(t,e){let n=e.idempotencyStore??de,r=e.now?e.now():new Date,o=e.hours??tr();return ce(n,t.idempotency_key,async()=>{let a=[];for(let s of t.entries)try{let c=Qn(s.intent,{now:r,morningHour:o.morningHour,afternoonHour:o.afternoonHour});if(!t.dry_run&&(await e.adapter.updateTask(s.taskId,{deferDate:c.resolvedDeferDate}),e.cache!==void 0)){let d=await e.adapter.getTask(s.taskId);b(e.cache,{taskId:s.taskId,projectId:d.projectId});}a.push({taskId:s.taskId,ok:!0,resolvedDeferDate:c.resolvedDeferDate,reason:c.reason});}catch(c){a.push({taskId:s.taskId,ok:false,error:c instanceof Error?c.message:String(c)});}let i={syncPending:!t.dry_run};return t.dry_run&&(i.dryRun=true),p({results:a},e.makeMeta(i))})}function hu(t,e){return t.registerTool("task_batch_defer_smart",{description:Fs,inputSchema:VT.shape},async n=>{let r=await KT(n,e);return u(r)})}var Us='Permanently delete many OmniFocus tasks in a single JXA round trip. IRREVERSIBLE \u2014 deleted tasks cannot be recovered. REQUIRED: pass confirm=true at the top level to acknowledge this action is irreversible; the entire batch is rejected without it. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each deletion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_delete calls whenever deleting more than one task. Each item is { id }. Returns { deleted: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name (captured pre-delete) so the agent can describe each removal without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_delete({ confirm: true, items: [{ id: "abc123" }, { id: "abc456" }] })',XT=z.object({id:h.schema.describe("Persistent task ID.")}),YT=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that all deletions are permanent and irreversible. Must be exactly true. The entire batch is rejected if this field is absent or false."),items:z.array(XT).min(1).describe("Array of { id } items. Must contain at least one item.")});async function ZT(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchDeleteTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({deleted:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Rd(a.succeeded.length)}))}function yu(t,e){return t.registerTool("task_batch_delete",{description:Us,inputSchema:YT.shape},async n=>{let r=await ZT(n,e);return u(r)})}var Bs='Cancel (drop) many OmniFocus tasks in a single JXA round trip. Dropped tasks remain in OmniFocus but are treated as cancelled/inactive \u2014 they do not appear in active task lists. Use task_batch_delete for permanent removal. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each drop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_drop calls whenever dropping more than one task. Each item is { id }. Returns { dropped: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each drop without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_drop({ items: [{ id: "abc123" }, { id: "abc456" }] })',QT=z.object({id:h.schema.describe("Persistent task ID.")}),ew=z.object({items:z.array(QT).min(1).describe("Array of { id } items. Must contain at least one item.")});async function tw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchDropTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({dropped:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ad(a.succeeded.length)}))}function ku(t,e){return t.registerTool("task_batch_drop",{description:Bs,inputSchema:ew.shape},async n=>{let r=await tw(n,e);return u(r)})}var Js='Move many OmniFocus tasks to new destinations in a single OmniJS round trip. Routes through OmniJS \u2014 not JXA \u2014 because JXA task.move() is unimplemented in OmniFocus 4.x. Each item specifies a task ID and exactly one destination: projectId (move into a project) or parentId (move under a parent task). Omit both to move to the inbox. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each move succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_move calls whenever moving more than one task. Returns { moved: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each move without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_move({ items: [{ id: "abc123", projectId: "prj456" }, { id: "abc789", parentId: "tsk111" }] })',nw=z.object({projectId:k.schema.optional().describe("Move into a project as a top-level action. Mutually exclusive with parentId."),parentId:h.schema.optional().describe("Move under a parent task. Mutually exclusive with projectId.")}).refine(t=>!(t.projectId!==void 0&&t.parentId!==void 0),{message:"Provide projectId OR parentId, not both"}),rw=z.object({id:h.schema.describe("Persistent task ID."),destination:nw.describe("Where to move the task. Provide projectId, parentId, or neither (inbox).")}),ow=z.object({items:z.array(rw).min(1).describe("Array of { id, destination } items. Must contain at least one item.")});async function aw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchMoveTasks(t.items.map(s=>({id:s.id,destination:{...s.destination.projectId!==void 0&&{projectId:s.destination.projectId},...s.destination.parentId!==void 0&&{parentId:s.destination.parentId}}})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({moved:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ed(a.succeeded.length,"destination")}))}function Iu(t,e){return t.registerTool("task_batch_move",{description:Js,inputSchema:ow.shape},async n=>{let r=await aw(n,e);return u(r)})}var Ws='Mark many OmniFocus tasks as incomplete in a single JXA round trip. Reverses a previous completion \u2014 useful when a task was completed by mistake or needs to be re-done. Uncompleted tasks return to active status. Use task_batch_complete to mark tasks as completed. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each uncomplete succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_uncomplete calls whenever uncompleting more than one task. Each item is { id }. Returns { uncompleted: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each restoration without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_uncomplete({ items: [{ id: "abc123" }, { id: "abc456" }] })',sw=z.object({id:h.schema.describe("Persistent task ID.")}),iw=z.object({items:z.array(sw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function cw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchUncompleteTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({uncompleted:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Dd(a.succeeded.length)}))}function vu(t,e){return t.registerTool("task_batch_uncomplete",{description:Ws,inputSchema:iw.shape},async n=>{let r=await cw(n,e);return u(r)})}var zs='Restore (undrop) many cancelled OmniFocus tasks in a single JXA round trip. Undropped tasks are returned to active status and will reappear in active task lists. Use task_batch_drop to cancel tasks. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each undrop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_undrop calls whenever undropping more than one task. Each item is { id }. Returns { undropped: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each restoration without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_undrop({ items: [{ id: "abc123" }, { id: "abc456" }] })',dw=z.object({id:h.schema.describe("Persistent task ID.")}),lw=z.object({items:z.array(dw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function pw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchUndropTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({undropped:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Cd(a.succeeded.length)}))}function Tu(t,e){return t.registerTool("task_batch_undrop",{description:zs,inputSchema:lw.shape},async n=>{let r=await pw(n,e);return u(r)})}var Gs=`Partially update many OmniFocus tasks in a single JXA round trip. Validation is atomic: if any patch fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each update succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_update calls whenever you are updating more than one task. Each item is { id, patch } where patch accepts a subset of task_update's editable fields (name, note, flagged, dueDate, deferDate, estimatedMinutes, tagIds, sequential, completedByChildren). Additive tag diffs (addTags/removeTags) and safety primitives (dry_run, expectedModifiedAt, idempotency_key) are not supported in batch form; fall back to task_update for those. Returns { updated: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 name reflects the post-patch name (uses patch.name when supplied, otherwise the existing name). Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_update({ items: [{ id: "abc123", patch: { flagged: true } }, { id: "abc456", patch: { dueDate: "2026-05-01T00:00:00Z" } }] })`,uw=z.object({name:z.string().min(1).optional().describe("New task name."),note:z.string().nullable().optional().describe("Plain-text note. Null clears the note."),flagged:z.boolean().optional().describe("Flag or unflag the task."),dueDate:z.string().datetime({offset:true}).nullable().optional().describe("Due date as ISO-8601 with offset. Null clears the date."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),deferDate:z.string().datetime({offset:true}).nullable().optional().describe("Defer date as ISO-8601 with offset. Null clears the date."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated duration in minutes. Null clears the estimate."),tagIds:z.array(S.schema).optional().describe("Full replacement tag ID list."),sequential:z.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Complete when all subtasks complete.")}).refine(t=>Object.keys(t).length>0,{message:"Patch must contain at least one field"}),mw=z.object({id:h.schema.describe("Persistent task ID."),patch:uw.describe("Fields to change. At least one field required.")}),qs=z.object({items:z.array(mw).min(1).describe("Array of { id, patch } pairs. Must contain at least one item.")});async function fw(t,e){let n=t.items.map(c=>c.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let c=0;c<n.length;c++){let d=r[c];d!=null&&o.set(n[c],d.name);}let a=t.items.map(c=>({id:c.id,patch:c.patch})),i=await e.adapter.batchUpdateTasks(a);if(e.cache!==void 0)for(let c of i.succeeded){let d=t.items[c.index];d!==void 0&&b(e.cache,{taskId:d.id});}let s=i.succeeded.map(c=>{let l=t.items[c.index]?.patch?.name??o.get(c.value)??"";return {index:c.index,value:{id:c.value,name:l}}});return p({updated:s,failed:i.failed},e.makeMeta({syncPending:i.succeeded.length>0,humanReadableSummary:Od(i.succeeded.length)}))}function wu(t,e){return t.registerTool("task_batch_update",{description:Gs,inputSchema:qs.shape},async n=>{let r=await fw(n,e);return u(r)})}var Vs="Preview what task_batch_update would do without making any changes. Do NOT use to actually update tasks \u2014 use task_batch_update instead. Returns { description, plannedChanges } summarising all patches that would be applied. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function gw(t,e){let n=[],r=[];for(let a of t.items){let i=String(a.id);try{i=(await e.adapter.getTask(a.id)).name;}catch{}let s=Object.keys(a.patch).join(", ");r.push(`'${i}' [${s}]`);for(let[c,d]of Object.entries(a.patch))n.push({field:`${a.id}.${c}`,newValue:d===null?null:String(d)});}let o=`Would update ${t.items.length} task${t.items.length===1?"":"s"}: ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Su(t,e){return t.registerTool("task_batch_update_describe",{description:Vs,inputSchema:qs.shape},async n=>{let r=await gw(n,e);return u(r)})}var Ks='Remove all alarms/notifications from an OmniFocus task. After clearing, the task has no scheduled notifications. Use task_set_alarms to install a new alarm set. Returns the updated task. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_clear_alarms({ id: "abc123" })',yw=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function kw(t,e){await e.adapter.clearTaskAlarms(t.id);let n=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:Pd(n.name)});return p({task:n},r)}function bu(t,e){return t.registerTool("task_clear_alarms",{description:Ks,inputSchema:yw.shape},async n=>{let r=await kw(n,e);return u(r)})}var Xs='Remove the repetition rule from an OmniFocus task. After clearing, the task becomes a one-time item. Use task_set_repetition to set or change a rule. Returns the updated task with repetitionRule confirmed as null. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_clear_repetition({ id: "abc123" })',vw=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function Tw(t,e){await e.adapter.updateTask(t.id,{repetition:null});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:jd(n.name)});return p({task:n},r)}function ju(t,e){return t.registerTool("task_clear_repetition",{description:Xs,inputSchema:vw.shape},async n=>{let r=await Tw(n,e);return u(r)})}var Ys='Complete an OmniFocus task \u2014 marks it done with a completion timestamp. Accepts an optional ISO-8601 date for the completion time; defaults to now. Idempotent: returns noChange: true if the task is already completed. When the task has incomplete children, returns clarification-needed asking whether to complete children too \u2014 call the `clarify` tool with the user\'s choice. Do not use to drop or delete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: sets completedAt, sets meta.syncPending = true. Example: task_complete({ id: "abc123" }) Example: task_complete({ id: "abc123", at: "2026-05-01T09:00:00Z" })',Zs=z.object({id:h.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("ISO-8601 completion time. Defaults to now.")});async function ww(t,e){let n=await e.adapter.getTask(t.id);if(n.completed)return p({noChange:true,id:t.id,name:n.name},e.makeMeta());let r=e.replayStore??ve,o=[];try{o=await e.adapter.listTasks({parentId:t.id,completed:!1});}catch{}if(o.length>0){let a=e.makeMeta(),i=["Complete this task and all incomplete children","Complete this task only \u2014 leave children incomplete"],s=o.map(d=>d.id),c=r.register(i,async d=>Pu(t,n,e,d===0?s:[]));return St(`"${n.name}" has ${o.length} incomplete child task(s). How should they be handled?`,c,a,i.map((d,l)=>({index:l,label:d})),{id:t.id,...t.at!==void 0?{at:t.at}:{}})}return Pu(t,n,e,[])}async function Pu(t,e,n,r){for(let i of r)try{await n.adapter.completeTask(i);}catch{}let o=t.at!==void 0?new Date(t.at):void 0;await n.adapter.completeTask(t.id,o),n.cache!==void 0&&b(n.cache,{taskId:t.id,projectId:e.projectId});let a;if(e.projectId!==null)try{if((await n.adapter.listTasks({projectId:e.projectId,completed:!1})).length===0){let s=await n.adapter.getProject(e.projectId);a=xt([Vl(e.projectId,s.name)]);}}catch{}return p({done:true,id:t.id,name:e.name},n.makeMeta({syncPending:true,humanReadableSummary:gd(e.name)}),void 0,a)}function Ou(t,e){return t.registerTool("task_complete",{description:Ys,inputSchema:Zs.shape},async n=>{let r=await ww(n,e);return u(r)})}var Qs="Preview what task_complete would do without making any changes. Do NOT use to actually complete a task \u2014 use task_complete instead. Returns { description, plannedChanges } describing the completion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Sw(t,e){let n=[],r=String(t.id),o=false;try{let s=await e.adapter.getTask(t.id);r=s.name,o=s.completed;}catch{}if(o){let s=`Task '${r}' is already completed \u2014 would be a no-op.`;return p({description:s,plannedChanges:n},e.makeMeta())}n.push({field:"completed",newValue:"true",oldValue:"false"}),t.at!==void 0&&n.push({field:"completedAt",newValue:t.at});let a=t.at!==void 0?` at ${t.at}`:"",i=`Would mark task '${r}' as done${a}.`;return p({description:i,plannedChanges:n},e.makeMeta())}function xu(t,e){return t.registerTool("task_complete_describe",{description:Qs,inputSchema:Zs.shape},async n=>{let r=await Sw(n,e);return u(r)})}var ei=`Promote an OmniFocus task to a first-class project via OmniJS Database.convertTasksToProjects(). The task's persistent identifier is preserved on the resulting project \u2014 agents can continue using the same ID as a project ID after conversion. Subtasks, notes, tags, and dates are carried over by OmniFocus automatically. Use this when a task has grown in scope and needs its own review interval, subtask hierarchy, or project-level metadata. Do NOT use on tasks already in a project \u2014 use task_move instead for reparenting; use project_create when starting from scratch. Returns { converted: true, projectId, taskId, name } \u2014 name is the task name (carried over to the new project) so the agent can describe the conversion without a follow-up read. Side effects: removes the task from the task list and adds a project; sets meta.syncPending = true. Example: task_convert_to_project({ id: "abc123" }) Example: task_convert_to_project({ id: "abc123", folderId: "fld456" })`,bw=z.object({id:h.schema.describe("Persistent ID of the task to promote."),folderId:W.schema.optional().describe("Place the new project inside this folder. Omit to place at the top of the library."),position:z.enum(["beginning","ending"]).optional().describe('Where within the folder or library to insert the new project. Defaults to "ending".')});async function jw(t,e){let n={};t.folderId!==void 0&&(n.folderId=t.folderId),t.position!==void 0&&(n.position=t.position);let r=await e.adapter.getTask(t.id),o=await e.adapter.convertTaskToProject(t.id,n);return e.cache!==void 0&&(b(e.cache,{taskId:t.id}),J(e.cache,{projectId:o})),p({converted:true,projectId:o,taskId:t.id,name:r.name},e.makeMeta({syncPending:true,humanReadableSummary:Sd(r.name)}))}function Ru(t,e){return t.registerTool("task_convert_to_project",{description:ei,inputSchema:bw.shape},async n=>{let r=await jw(n,e);return u(r)})}function Ee(t){if(t.length===0)return "<root>";let e="";for(let n of t)typeof n=="number"?e+=`[${n}]`:e+=e===""?String(n):`.${String(n)}`;return e}function Au(t,e){let n=t;for(let r of e){if(n==null)return;if(typeof n=="object")n=n[r];else return}return n}function _w(t,e=6){let r=t.slice(0,e).map(o=>typeof o=="string"?`"${o}"`:String(o));return t.length>e&&r.push(`\u2026 (${t.length-e} more)`),r.join(" | ")}function Pw(t){switch(t){case "datetime":return {expected:"ISO-8601 datetime, UTC (Z) or with offset",examples:["2025-03-01T09:00:00Z","2025-03-01T09:00:00-05:00"]};case "date":return {expected:"ISO-8601 date (YYYY-MM-DD)",examples:["2025-03-01"]};case "time":return {expected:"ISO-8601 time (HH:MM:SS)",examples:["09:00:00"]};case "email":return {expected:"email address",examples:["user@example.com"]};case "url":return {expected:"absolute URL",examples:["https://example.com/path"]};case "uuid":return {expected:"UUID v4",examples:["3fa85f64-5717-4562-b3fc-2c963f66afa6"]};case "ipv4":return {expected:"IPv4 address",examples:["192.0.2.42"]};case "ipv6":return {expected:"IPv6 address",examples:["2001:db8::1"]};case "regex":return {expected:"string matching the schema's regex"};default:return {expected:`string in "${t}" format`}}}function Cu(t,e,n,r){let o=t==="min"?n?"\u2265":">":n?"\u2264":"<";switch(r){case "string":return `string with ${o} ${e} character${e===1?"":"s"}`;case "array":return `array with ${o} ${e} item${e===1?"":"s"}`;case "set":return `set with ${o} ${e} member${e===1?"":"s"}`;default:return `value ${o} ${e}`}}function Eu(t,e){let n=r=>e===void 0?void 0:Au(e,r);switch(t.code){case "invalid_type":{let r=t.expected;return [{field:Ee(t.path),sent:n(t.path),expected:`${r}`}]}case "invalid_value":{let r=t.values;return [{field:Ee(t.path),sent:n(t.path),expected:`one of: ${_w(r)}`,...r.length>0&&{examples:r.slice(0,3)}}]}case "invalid_format":{let r=Pw(t.format);return [{field:Ee(t.path),sent:n(t.path),expected:r.expected,...r.examples&&{examples:r.examples}}]}case "too_small":return [{field:Ee(t.path),sent:n(t.path),expected:Cu("min",Number(t.minimum),t.inclusive??true,t.origin)}];case "too_big":return [{field:Ee(t.path),sent:n(t.path),expected:Cu("max",Number(t.maximum),t.inclusive??true,t.origin)}];case "unrecognized_keys":{let r=Ee(t.path);return t.keys.map(o=>({field:r==="<root>"?o:`${r}.${o}`,sent:Au(e,[...t.path,o]),expected:"key is not part of the schema; remove it"}))}case "not_multiple_of":return [{field:Ee(t.path),sent:n(t.path),expected:`multiple of ${String(t.divisor)}`}];case "invalid_union":{let r=[];for(let a of t.errors){let i=a.slice().sort((s,c)=>c.path.length-s.path.length)[0];i!==void 0&&r.push(...Eu(i,e));}if(r.length===0)return [{field:Ee(t.path),sent:n(t.path),expected:"value matching one of the schema's accepted shapes"}];let o=`one of: ${r.map(a=>a.expected).join("; or ")}`;return [{field:Ee(t.path),sent:n(t.path),expected:o}]}default:return [{field:Ee(t.path),sent:n(t.path),expected:t.message||`value satisfying schema constraint "${String(t.code)}"`}]}}function Mu(t,e){let n=[];for(let r of t.issues)n.push(...Eu(r,e));return n}function je(t,e,n="Cross-field validation failed"){let r=t.safeParse(e);if(r.success)return r.data;let o=Mu(r.error,e);throw new y(n,{details:{failures:o}})}var ti='Create a new task in OmniFocus \u2014 in the inbox, inside a project, or as a subtask of another task. Supply exactly one of: projectId (project task), parentTaskId (subtask), or neither (inbox). Do not use for bulk creation; prefer task_batch_create for that. Safety control: pass idempotency_key to make transport retries safe \u2014 identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate task. Returns { id, name } \u2014 name echoes the supplied name so the agent can describe the new task without a follow-up read. Side effects: creates a task in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the task to appear on other devices. Example: task_create({ name: "Buy milk" }) Example: task_create({ name: "Write report", projectId: "prj123", dueDate: "2026-05-01T00:00:00Z" })',or=z.object({name:z.string().min(1).describe("Task name. Required, must be non-empty."),projectId:k.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:h.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z.string().optional().describe("Plain-text note."),flagged:z.boolean().optional().describe("Flag the task."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset."),dueDateFloating:z.boolean().optional().describe("When true, the due time follows the user across time zones (floating) rather than being pinned to a fixed UTC instant. Use for recurring daily tasks where '9 AM' should mean 9 AM wherever the user is. Default: false (fixed-offset)."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().min(1).optional().describe("Estimated duration in minutes."),tagIds:z.array(S.schema).optional().describe("Tag IDs to apply."),sequential:z.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Complete when all subtasks complete."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe creates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate task.")}),Ow=or.refine(t=>!(t.projectId!==void 0&&t.parentTaskId!==void 0),{message:"Supply at most one of projectId or parentTaskId",path:["projectId"]}).refine(t=>!(t.dueDate!==void 0&&t.deferDate!==void 0&&new Date(t.dueDate)<new Date(t.deferDate)),{message:"dueDate must not be earlier than deferDate",path:["dueDate"]});async function xw(t,e){je(Ow,t);let n=e.idempotencyStore??de;return ce(n,t.idempotency_key,async()=>{let r={name:t.name,...t.projectId!==void 0&&{projectId:t.projectId},...t.parentTaskId!==void 0&&{parentId:t.parentTaskId},...t.note!==void 0&&{note:t.note},...t.flagged!==void 0&&{flagged:t.flagged},...t.dueDate!==void 0&&{dueDate:t.dueDate},...t.dueDateFloating!==void 0&&{dueDateFloating:t.dueDateFloating},...t.deferDate!==void 0&&{deferDate:t.deferDate},...t.deferDateFloating!==void 0&&{deferDateFloating:t.deferDateFloating},...t.estimatedMinutes!==void 0&&{estimatedMinutes:t.estimatedMinutes},...t.tagIds!==void 0&&{tagIds:t.tagIds},...t.sequential!==void 0&&{sequential:t.sequential},...t.completedByChildren!==void 0&&{completedByChildren:t.completedByChildren}},o=await e.adapter.createTask(r);e.cache!==void 0&&b(e.cache,{...t.projectId!==void 0&&{projectId:t.projectId}});let a=[Hl(o,t.name),zl(o,t.dueDate,t.estimatedMinutes)];if(t.projectId===void 0&&t.parentTaskId===void 0)try{let s=await e.adapter.listTasks({inbox:!0,completed:!1});a.push(Gl(s.length));}catch{}let i=xt(a.filter(s=>s!=null));return p({id:o,name:t.name},e.makeMeta({syncPending:true,humanReadableSummary:fd(t.name)}),void 0,i)})}function Nu(t,e){return t.registerTool("task_create",{description:ti,inputSchema:or.shape},async n=>{let r=await xw(n,e);return u(r)})}var ni="Preview what task_create would do without making any changes. Do NOT use to actually create a task \u2014 use task_create instead. Returns { description, plannedChanges } describing the task that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Dw(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.projectId!==void 0){let a=await _t(e.adapter,t.projectId);n.push({field:"projectId",newValue:t.projectId}),r.push(`in project '${a}'`);}else if(t.parentTaskId!==void 0){let a=await Pt(e.adapter,t.parentTaskId);n.push({field:"parentTaskId",newValue:t.parentTaskId}),r.push(`as subtask of '${a}'`);}else r.push("in Inbox");if(t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate}),r.push(`due ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${Te(t.deferDate)}`)),t.flagged===true&&(n.push({field:"flagged",newValue:"true"}),r.push("flagged")),t.estimatedMinutes!==void 0&&(n.push({field:"estimatedMinutes",newValue:String(t.estimatedMinutes)}),r.push(`estimated ${t.estimatedMinutes} min`)),t.tagIds!==void 0&&t.tagIds.length>0){let a=await Promise.all(t.tagIds.map(i=>he(e.adapter,i)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`tagged ${a.map(i=>`'${i}'`).join(", ")}`);}t.note!==void 0&&n.push({field:"note",newValue:t.note.slice(0,50)});let o=`Would create task ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Fu(t,e){return t.registerTool("task_create_describe",{description:ni,inputSchema:or.shape},async n=>{let r=await Dw(n,e);return u(r)})}var ri="Defer a task to a date computed from a high-level intent (e.g. 'next work morning', 'skip weekends', 'in 3 business days'), instead of guessing an ISO date that may land on a weekend or off-hours. Variants: next-work-day, next-weekday, in-business-days, after-event (gated on calendar bridge), next-month-start, explicit-with-skip-weekends. Morning/afternoon defaults are configurable via OMNIFOCUS_MORNING_HOUR / OMNIFOCUS_AFTERNOON_HOUR env (default 09:00 / 14:00). Do NOT use this for unconditional ISO-date defers \u2014 prefer task_update with deferDate. Returns { taskId, resolvedDeferDate, reason } so the agent can echo the resolved date verbatim. Side effects: writes the resolved deferDate via task_update; supports dry_run, idempotency_key, and expectedModifiedAt for safety. Triggers a sync. Example: task_defer_smart({ taskId: '...', intent: { kind: 'next-work-day' } })",Uu=z.enum(["morning","afternoon"]),Rw=z.discriminatedUnion("kind",[z.object({kind:z.literal("next-work-day"),at:Uu.optional()}),z.object({kind:z.literal("next-weekday"),weekday:z.number().int().min(0).max(6),at:Uu.optional()}),z.object({kind:z.literal("in-business-days"),days:z.number().int().positive()}),z.object({kind:z.literal("after-event"),eventId:z.string().min(1)}),z.object({kind:z.literal("next-month-start")}),z.object({kind:z.literal("explicit-with-skip-weekends"),date:z.string().min(1)})]),Aw=z.object({taskId:h.schema.describe("ID of the task to defer."),intent:Rw.describe("High-level defer intent. Discriminated union on `kind` \u2014 see tool description for variants."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and resolves the intent but does NOT write to OmniFocus. Returns the resolved date + reason in the response with meta.dryRun = true."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe defers. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying.")});async function Cw(t,e){let n=e.idempotencyStore??de,r=e.now?e.now():new Date,o=e.hours??tr();return ce(n,t.idempotency_key,async()=>{let a=Qn(t.intent,{now:r,morningHour:o.morningHour,afternoonHour:o.afternoonHour}),i=()=>p({taskId:t.taskId,resolvedDeferDate:a.resolvedDeferDate,reason:a.reason},e.makeMeta({syncPending:false})),s=async()=>{await e.adapter.updateTask(t.taskId,{deferDate:a.resolvedDeferDate});let c=await e.adapter.getTask(t.taskId);return e.cache!==void 0&&b(e.cache,{taskId:t.taskId,projectId:c.projectId}),p({taskId:t.taskId,resolvedDeferDate:a.resolvedDeferDate,reason:a.reason},e.makeMeta({syncPending:true}))};return Ce(t.dry_run,i,s)})}function Lu(t,e){return t.registerTool("task_defer_smart",{description:ri,inputSchema:Aw.shape},async n=>{let r=await Cw(n,e);return u(r)})}var oi='Permanently delete an OmniFocus task. IRREVERSIBLE \u2014 uses OmniFocus deleteObject; there is no undo. Prefer task_drop when you want a recoverable status change. Only use task_delete when the agent has explicit user intent to permanently remove the task. REQUIRED: pass confirm=true to acknowledge this action is irreversible; the call is rejected without it. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent task_get) to reject the call if the task changed since you read it; pass idempotency_key to coalesce retries so the same delete is only performed once. Returns { deleted: true, id } on success. Side effects: removes the task from OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the deletion to appear on other devices. Example: task_delete({ id: "abc123", confirm: true }) Example: task_delete({ id: "abc123", confirm: true, dry_run: true })',ai=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that this deletion is permanent and irreversible. Must be exactly true. The call is rejected if this field is absent or false."),id:h.schema.describe("Persistent ID of the task to delete. Get from task_list or search_query. Verify you have the correct ID before calling \u2014 this action is irreversible."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no delete is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe deletes. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-deleting (or re-raising NotFound on the second attempt).")});async function Ew(t,e){let n=e.idempotencyStore??de;return ce(n,t.idempotency_key,async()=>{let r=await e.adapter.getTask(t.id);Ye(t.expectedModifiedAt,r.modifiedAt,`task:${t.id}`);let o=()=>p({deleted:true,id:t.id},e.makeMeta({syncPending:false})),a=async()=>(await e.adapter.deleteTask(t.id),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:r.projectId}),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:yd(r.name)})));return Ce(t.dry_run,o,a)})}function Bu(t,e){return t.registerTool("task_delete",{description:oi,inputSchema:ai.shape},async n=>{let r=await Ew(n,e);return u(r)})}var si="Preview what task_delete would do without making any changes. Do NOT use to actually delete a task \u2014 use task_delete instead. Returns { description, plannedChanges } describing the permanent deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Mw(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTask(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=`Would permanently delete task '${r}' (id: ${t.id}). IRREVERSIBLE.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Ju(t,e){return t.registerTool("task_delete_describe",{description:si,inputSchema:ai.shape},async n=>{let r=await Mw(n,e);return u(r)})}var ii='Drop an OmniFocus task \u2014 marks it as dropped/deferred and removes it from active view. Reversible via task_undrop. Accepts an optional ISO-8601 date. Idempotent: returns noChange: true if already dropped. Do not use to complete or delete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: sets droppedAt, sets meta.syncPending = true.Example: task_drop({ id: "abc123" })',ci=z.object({id:h.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("ISO-8601 drop time. Defaults to now.")});async function Nw(t,e){let n=await e.adapter.getTask(t.id);if(n.dropped)return p({noChange:true,id:t.id,name:n.name},e.makeMeta());let r=t.at!==void 0?new Date(t.at):void 0;return await e.adapter.dropTask(t.id,r),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:kd(n.name)}))}function Wu(t,e){return t.registerTool("task_drop",{description:ii,inputSchema:ci.shape},async n=>{let r=await Nw(n,e);return u(r)})}var di="Preview what task_drop would do without making any changes. Do NOT use to actually drop a task \u2014 use task_drop instead. Returns { description, plannedChanges } describing the drop that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Fw(t,e){let n=[],r=String(t.id),o=false;try{let s=await e.adapter.getTask(t.id);r=s.name,o=s.dropped;}catch{}if(o){let s=`Task '${r}' is already dropped \u2014 would be a no-op.`;return p({description:s,plannedChanges:n},e.makeMeta())}n.push({field:"dropped",newValue:"true",oldValue:"false"}),t.at!==void 0&&n.push({field:"droppedAt",newValue:t.at});let a=t.at!==void 0?` at ${t.at}`:"",i=`Would drop task '${r}' (mark as dropped/on-hold)${a}.`;return p({description:i,plannedChanges:n},e.makeMeta())}function Hu(t,e){return t.registerTool("task_drop_describe",{description:di,inputSchema:ci.shape},async n=>{let r=await Fw(n,e);return u(r)})}var li=`Duplicate an OmniFocus task, optionally including its entire subtask subtree when recursive: true. Editable fields copy over (name, note, defer/due dates, flagged, tags, estimate, repetition); system fields (id, timestamps) regenerate; completed/dropped state is NOT carried \u2014 the duplicate is a fresh, active task. Do NOT use task_duplicate as a substitute for task_move (which reparents the existing task) or task_create (when the new task's fields differ from the source). By default the clone lands alongside the source. Provide destination with exactly one of projectId, parentId, or toInbox: true to place it elsewhere. Returns { duplicated: true, sourceId, newId, descendantCount, name } \u2014 name is the source task's name (the duplicate carries the same name) so the agent can describe the new task without a follow-up read. Side effects: creates one new task (plus descendants if recursive) in OmniFocus, sets meta.syncPending = true. Example: task_duplicate({ id: "abc123" }) Example: task_duplicate({ id: "abc123", recursive: true, destination: { projectId: "prj456" } })`,Uw=z.union([z.object({projectId:k.schema}),z.object({parentId:h.schema}),z.object({toInbox:z.literal(true)})]).describe("Where to place the duplicate. Exactly one of projectId, parentId, or toInbox: true. Omit to clone alongside the source."),Lw=z.object({id:h.schema.describe("Persistent ID of the task to duplicate."),recursive:z.boolean().optional().default(false).describe("When true, clone the full subtask subtree depth-first. Default: false (clone only the task itself)."),destination:Uw.optional()}).describe("Duplicate options. `destination` overrides the default same-container placement.");async function Bw(t,e){if(t.destination!==void 0){let a=t.destination,i=("projectId"in a?1:0)+("parentId"in a?1:0)+("toInbox"in a&&a.toInbox===true?1:0);if(i!==1)throw new y("task_duplicate: destination must set exactly one of projectId, parentId, or toInbox",{details:{field:"destination",provided:i},suggestion:"Set exactly one destination field or omit destination entirely."})}let n=await e.adapter.getTask(t.id),{newId:r,descendantCount:o}=await e.adapter.duplicateTask(t.id,{recursive:t.recursive,...t.destination!==void 0?{destination:t.destination}:{}});return e.cache!==void 0&&(b(e.cache,{taskId:r,projectId:n.projectId}),t.destination!==void 0&&"projectId"in t.destination&&t.destination.projectId!==n.projectId&&b(e.cache,{projectId:t.destination.projectId})),p({duplicated:true,sourceId:t.id,newId:r,descendantCount:o,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Td(n.name)}))}function zu(t,e){return t.registerTool("task_duplicate",{description:li,inputSchema:Lw.shape},async n=>{let r=await Bw(n,e);return u(r)})}var pi=[".png",".jpg",".jpeg",".heic",".heif",".gif",".webp",".pdf"];function qu(t){return pi.includes(extname(t).toLowerCase())}function $w(t){return t.mimeType!==null?t.mimeType.startsWith("image/")||t.mimeType==="application/pdf":qu(t.name)}var ui=`Capture tasks from an image \u2014 agent does vision, tool does plumbing. Source is a path or existing OF attachment; agent supplies proposed: ProposedTask[]. Two-phase: dryRun=true validates+echoes; dryRun=false with confirmation[] writes. attachSourceTo: 'parent-task' (default), 'each-task' (path-mode only), or 'none'. Path-mode: PNG/JPEG/HEIC/HEIF/GIF/WEBP/PDF; respects attachment-path-scope + size cap. Do NOT use when you already have structured tasks \u2014 call task_batch_create. Returns { phase, proposed?, parent?, created?, outcome? }. Side effects: dryRun=false creates tasks; call sync_trigger for cross-device. Example: task_extract_from_image({ source: { kind: "path", path: "/tmp/whiteboard.png" }, proposed: [{ name: "Follow up with Alice" }], dryRun: true })`,Gu=z.object({name:z.string().min(1).describe("Task name extracted from the image."),note:z.string().optional().describe("Additional context or plain-text note for the task."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset, if detected in the image."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset, if detected in the image.")}),Ww=z.discriminatedUnion("kind",[z.object({kind:z.literal("path"),imagePath:z.string().min(1).describe(`Absolute path within attachment-path-scope, with one of the supported image extensions (${pi.join(",")}). Subject to the configured size cap.`).refine(qu,{message:`imagePath must use one of ${pi.join(",")}`})}).describe("Image read from a filesystem path."),z.object({kind:z.literal("attachment"),attachmentId:Oe.schema.describe("Persistent ID of the OF attachment carrying the image."),ownerTaskId:h.schema.optional().describe("Owner task that holds the attachment. Mutually exclusive with ownerProjectId."),ownerProjectId:k.schema.optional().describe("Owner project that holds the attachment. Mutually exclusive with ownerTaskId.")}).describe("Image referenced via an existing OF attachment ID.")]).describe("Image source. attachment requires exactly one owner."),Hw=z.enum(["parent-task","each-task","none"]).default("parent-task").describe("Re-attachment mode after task creation."),Vu=z.object({source:Ww,targetProjectId:k.schema.describe("Project that receives the captured tasks (and the wrapper, if `parent-task` mode)."),proposed:z.array(Gu).min(1).describe("Agent-supplied extraction."),attachSourceTo:Hw,parentTaskName:z.string().min(1).optional().describe("Wrapper parent task name; default 'Captured from image'."),dryRun:z.boolean().default(true).describe("true (default) = preview; false requires confirmation[]."),confirmation:z.array(Gu).optional().describe("Required when dryRun=false. (Possibly-edited) confirmed tasks.")}),zw=Vu.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation[] is required when dryRun is false",path:["confirmation"]}).refine(t=>t.source.kind!=="attachment"||t.source.ownerTaskId!==void 0!=(t.source.ownerProjectId!==void 0),{message:"attachment source requires exactly one of source.ownerTaskId or source.ownerProjectId",path:["source"]}).refine(t=>t.source.kind!=="attachment"||t.attachSourceTo==="none",{message:"attachment-mode source requires attachSourceTo='none' (v1 limitation)",path:["attachSourceTo"]});async function Gw(t,e){if(t.kind==="path")return {kind:"path",imagePath:t.imagePath};let n=t.ownerTaskId?{taskId:t.ownerTaskId}:{projectId:t.ownerProjectId},r=(await e.attachmentService.list(n)).find(o=>o.id===t.attachmentId);if(!r)throw new y(`Attachment not found: ${t.attachmentId}`,{details:{field:"source.attachmentId"}});if(!$w(r))throw new y(`Attachment is not an image: ${r.name}`,{details:{field:"source.attachmentId"}});return {kind:"attachment",attachment:r}}function qw(t,e){return {name:t.name,...e,...t.note!==void 0&&{note:t.note},...t.deferDate!==void 0&&{deferDate:t.deferDate},...t.dueDate!==void 0&&{dueDate:t.dueDate}}}async function Vw(t,e){je(zw,t);let n=await Gw(t.source,e);if(t.dryRun||!t.confirmation)return p({phase:"dryRun",proposed:t.proposed,sourceKind:n.kind},e.makeMeta());let r=t.confirmation,o=n.kind==="path"?n.imagePath:void 0,a,i={projectId:t.targetProjectId};if(t.attachSourceTo==="parent-task"){let l=t.parentTaskName??"Captured from image",m=await e.adapter.createTask({name:l,projectId:t.targetProjectId});o!==void 0&&await e.attachmentService.add({taskId:m,filePath:o}),a={taskId:m,name:l,...o!==void 0&&{attachedSourcePath:o}},i={parentId:m};}let s=await e.adapter.batchCreateTasks(r.map(l=>qw(l,i))),c=[];for(let l of s.succeeded){let m=r[l.index],f;t.attachSourceTo==="each-task"&&o!==void 0&&(await e.attachmentService.add({taskId:l.value,filePath:o}),f=o),c.push({taskId:l.value,name:m?.name??"(unknown)",...f!==void 0&&{attachedSourcePath:f}});}e.cache!==void 0&&s.succeeded.length>0&&b(e.cache,{projectId:t.targetProjectId});let d=e.makeMeta({syncPending:s.succeeded.length>0});return p({phase:"created",parent:a,created:c,outcome:s},d)}function Ku(t,e){return t.registerTool("task_extract_from_image",{description:ui,inputSchema:Vu.shape},async n=>{let r=await Vw(n,e);return u(r)})}var Kw=["add","build","call","check","create","draft","email","file","finish","fix","follow up","plan","prepare","research","review","schedule","send","set up","start","write"],Xw=/^\s*(?:\(\d+\)|\d+[.)])\s+(.+)$/,Yw=/^\s*(?:[-*•–—])\s+(.+)$/,Zw=/^\s*(?:✅|⏰|📝|📌|✏️|📞|📧|📤|📩|🔔|🚨|⚡️|➡️|→)\s+(.+)$/u;function Qw(t){return t.replace(/[\s.;,:]+$/,"").trim()}function eS(t){let e=t.map(n=>n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(String.raw`^\s*(?:(?:i (?:should|will|need to|must)|please|we (?:need to|should))\s+)?(${e.join("|")})\b\s+(.+)$`,"i")}function Xu(t,e={}){let n=e.verbs??Kw,r=eS(n),o=[],a=[],i=t.replace(/\r\n?/g,`
|
|
226
|
+
`).split(`
|
|
227
|
+
`);for(let s=0;s<i.length;s++){let c=s+1,d=i[s]??"";if(!d.trim())continue;let l=Xw.exec(d),m=l?null:Yw.exec(d),f=!l&&!m?Zw.exec(d):null,g=!l&&!m&&!f?r.exec(d):null,v=null;if(l)v=l[1]??null;else if(m)v=m[1]??null;else if(f)v=f[1]??null;else if(g){let T=g[1]??"",E=g[2]??"";v=T&&E?`${T} ${E}`:null;}if(v===null){a.push(`L${c}: ${d.trim()}`);continue}let I=Qw(v);if(!I){a.push(`L${c}: ${d.trim()}`);continue}o.push({name:I,sourceLines:[c]});}return {proposed:o,unmappedLines:a}}var mi=`Mechanically split prose into a candidate-task list with source-line provenance. Source can be a task's note (kind: 'task'), a project's note (kind: 'project'), or inline text (kind: 'inline') \u2014 useful for piping a transcript through capture-meeting. Two-phase contract: dryRun=true returns { proposed, unmappedLines }; dryRun=false with confirmation: ProposedTask[] creates the (possibly-edited) tasks in targetProjectId via batchCreateTasks semantics. Returns { phase: 'dryRun', proposed, unmappedLines } or { phase: 'created', outcome: BatchOutcome<TaskId> } accordingly. Do NOT use this tool when you already have structured tasks \u2014 call task_batch_create directly instead. Prefer this helper when the input is a wall-of-text note that needs splitting. Side effects: dryRun=true is read-only; dryRun=false creates tasks in the target project. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_extract_from_note({ source: { kind: "task", id: "abc123" }, dryRun: true })`,tS=z.object({name:z.string().min(1).describe("Task name."),note:z.string().optional().describe("Optional note body for the created task."),deferDate:z.string().datetime({offset:true}).optional(),dueDate:z.string().datetime({offset:true}).optional(),tags:z.array(z.string()).optional().describe("Tag NAMES \u2014 resolved by the agent before passing here, since this tool does not look up tag IDs."),sourceLines:z.array(z.number().int().nonnegative()).optional().describe("1-based source line numbers from the original prose; preserved when surfacing proposals to the user.")}),nS=z.discriminatedUnion("kind",[z.object({kind:z.literal("task"),taskId:h.schema.describe("Task whose note will be parsed.")}),z.object({kind:z.literal("project"),projectId:k.schema.describe("Project whose note will be parsed.")}),z.object({kind:z.literal("inline"),text:z.string().min(1).describe("Raw prose to parse \u2014 agent supplies directly.")})]).describe("Where to read prose from."),Yu=z.object({source:nS,targetProjectId:k.schema.describe("Project that will receive created tasks on dryRun=false. Read-only on dryRun=true."),dryRun:z.boolean().default(true).describe("Default true \u2014 return proposals without creating. false requires confirmation[]."),confirmation:z.array(tS).optional().describe("Required when dryRun is false. The (possibly-edited) ProposedTask[] the agent has confirmed with the user.")}),rS=Yu.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation[] is required when dryRun is false",path:["confirmation"]});async function oS(t,e){return t.kind==="inline"?t.text:t.kind==="task"?(await e.getTask(t.taskId)).note??"":(await e.getProject(t.projectId)).note??""}async function aS(t,e){je(rS,t);let n=await oS(t.source,e.adapter),r=Xu(n);if(t.dryRun||!t.confirmation){let s=e.makeMeta();return p({phase:"dryRun",proposed:r.proposed,unmappedLines:r.unmappedLines},s)}let o=t.confirmation.map(s=>({name:s.name,projectId:t.targetProjectId,...s.note!==void 0&&{note:s.note},...s.deferDate!==void 0&&{deferDate:s.deferDate},...s.dueDate!==void 0&&{dueDate:s.dueDate}})),a=await e.adapter.batchCreateTasks(o);e.cache!==void 0&&a.succeeded.length>0&&b(e.cache,{projectId:t.targetProjectId});let i=e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:bt(a.succeeded.length)});return p({phase:"created",outcome:a},i)}function Zu(t,e){return t.registerTool("task_extract_from_note",{description:mi,inputSchema:Yu.shape},async n=>{let r=await aS(n,e);return u(r)})}var fi='Find tasks in OmniFocus by name. Returns ALL matching tasks (names are not unique in OmniFocus). Names collide in OmniFocus; prefer task_get with an ID when you have one. Use search_query instead when you need to search task notes as well, or want full-text content search. Zero matches returns an empty array \u2014 not an error. Returns tasks[]; safe to call repeatedly; no side effects. Example: task_find_by_name({ name: "Buy milk" }) Example: task_find_by_name({ name: "report", matchMode: "contains" })',sS=z.object({query:z.string().min(1).describe("Name to search for. Behaviour depends on mode: exact = full name match; prefix = name starts with this string; contains = substring match anywhere in name."),mode:z.enum(["exact","prefix","contains"]).optional().describe("'exact' = full task name must match (default); 'prefix' = name must start with query; 'contains' = query appears anywhere in name."),caseSensitive:z.boolean().optional().describe("true = match is case-sensitive; false = case-insensitive (default false)."),limit:z.number().int().min(1).max(500).optional().describe("Maximum number of results to return (1..500). Default 50.")});async function iS(t,e){let n=t.mode??"exact",r=t.caseSensitive??false,o=t.limit??50,a=await e.adapter.listTasks({}),i=m=>r?m:m.toLowerCase(),s=i(t.query),c=a.filter(m=>{let f=i(m.name);switch(n){case "exact":return f===s;case "prefix":return f.startsWith(s);case "contains":return f.includes(s);default:return false}}),d=c.slice(0,o),l=e.makeMeta();return p({tasks:d,matchCount:c.length},l)}function Qu(t,e){return t.registerTool("task_find_by_name",{description:fi,inputSchema:sS.shape},async n=>{let r=await iS(n,e);return u(r)})}var gi=`Lexical nearest-neighbour search for de-duplicating tasks. Pass a candidate name (and optional note) and receive the top-K most-similar existing tasks ranked by a deterministic [0, 1] lexical-signal score (Jaccard token-overlap + prefix bonus + exact-name boost). Title-dominant: a perfect title match outranks a perfect note match. Use BEFORE task_create when you suspect a duplicate; the agent inspects the candidates and decides whether to create new, link to existing, or merge. Excludes completed and dropped tasks by default; opt-in via includeCompleted: true. Optional scope { projectId } or { tagId } narrows the candidate set. Returns { candidates: [{ taskId, name, score, project, tags }] } sorted by score descending \u2014 project is { id, name } | null and tags is [{ id, name }, ...]. Names are paired alongside ids via a single getProjectsMany + single getTagsMany batch (no N+1) so the agent can describe each candidate without a follow-up read. An empty result is { candidates: [] }, not an error. Do NOT use this tool for general full-text search \u2014 call task_search for that. Prefer this helper when the question is 'is this task already in the system?'. No model calls; no side effects. Read-only. Example: task_find_similar({ name: "Call dentist" }) Example: task_find_similar({ name: "Write report", scope: { projectId: "prj123" }, topK: 5 })`,em=5,tm=50,cS=z.object({projectId:k.schema.optional(),tagId:S.schema.optional()}).refine(t=>!(t.projectId!==void 0&&t.tagId!==void 0),{message:"Supply at most one of projectId or tagId"}),dS=z.object({name:z.string().min(1).describe("The candidate task name to compare against existing tasks."),note:z.string().optional().describe("Optional note text. When both the candidate and an existing task have a note, note overlap contributes to the score as a tiebreaker."),scope:cS.optional().describe("Narrow the candidate set to one project or one tag. Mutually exclusive \u2014 supply at most one. Omit to search all open tasks."),limit:z.number().int().min(1).max(tm).default(em).describe(`Top-K candidates to return. Default ${em}, max ${tm}.`),includeCompleted:z.boolean().default(false).describe("When true, include completed and dropped tasks. Default false (open tasks only).")});async function lS(t,e){let n=t.includeCompleted?{}:{completed:false};t.scope?.projectId!==void 0&&(n.projectId=t.scope.projectId),t.scope?.tagId!==void 0&&(n.tagId=t.scope.tagId);let r=await e.adapter.listTasks(n),o={name:t.name,...t.note!==void 0&&{note:t.note}},a=r.map(I=>({task:I,s:Jc(o,{name:I.name,note:I.note})})).filter(I=>I.s>0).sort((I,T)=>T.s-I.s).slice(0,t.limit),i=new Set,s=new Set;for(let{task:I}of a){I.projectId!==null&&i.add(I.projectId);for(let T of I.tagIds)s.add(T);}let c=[...i],d=[...s],[l,m]=await Promise.all([c.length>0?e.adapter.getProjectsMany(c):Promise.resolve([]),d.length>0?e.adapter.getTagsMany(d):Promise.resolve([])]),f=new Map;c.forEach((I,T)=>{let E=l[T];E!=null&&f.set(String(I),E.name);});let g=new Map;d.forEach((I,T)=>{let E=m[T];E!=null&&g.set(String(I),E.name);});let v=a.map(({task:I,s:T})=>{let E=I.projectId===null?null:String(I.projectId),w=E===null?null:f.get(E)??null;return {taskId:String(I.id),name:I.name,score:T,project:E===null||w===null?null:{id:E,name:w},tags:I.tagIds.map(D=>{let j=String(D),B=g.get(j);return B===void 0?null:{id:j,name:B}}).filter(D=>D!==null)}});return p({candidates:v},e.makeMeta())}function nm(t,e){return t.registerTool("task_find_similar",{description:gi,inputSchema:dS.shape},async n=>{let r=await lS(n,e);return u(r)})}var hi='Fetch a single OmniFocus task by persistent ID. Use when you have a known task ID and need its full detail. Do NOT use for multiple IDs \u2014 use task_get_many instead. Returns the Task object plus its direct subtasks (when includeSubtasks=true, the default). Read-only; safe to retry. Example: task_get({ id: "abc123" })',pS=z.object({id:h.schema.describe("Persistent ID of the task to fetch. Get from task_list or task_get_many."),includeSubtasks:z.boolean().optional().describe("Include direct subtasks in the response. Default true.")});async function uS(t,e){let n=await e.taskService.get(t),r=Tt(n.task.note),o=Ae(n.task.note);return p({task:n.task,...n.subtasks!==void 0&&{subtasks:n.subtasks},...r!==void 0&&{waitingOn:r},...o!==void 0&&{decision:o}},e.makeMeta({cacheHit:n.cacheHit}))}function om(t,e){return t.registerTool("task_get",{description:hi,inputSchema:pS.shape},async n=>{let r=await uS(n,e);return u(r)})}var yi='Fetch up to 100 tasks by persistent ID in a single OmniFocus round-trip. Use when you have a set of task IDs from multiple sources and need full task objects for all of them. Do NOT use for a single ID \u2014 use task_get instead. Do NOT use when you only have names \u2014 use task_find_by_name. Returns Task[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry. Example: task_get_many({ ids: ["abc123", "abc456"] })',ar=100,mS=z.object({ids:z.array(h.schema).min(0).max(ar).describe(`Array of task IDs to fetch (0..${ar}). Get IDs from task_list, search_query, or task_find_by_name. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function fS(t,e){if(t.ids.length===0)return p({tasks:[]},e.makeMeta());if(t.ids.length>ar)throw new y(`ids array exceeds the maximum batch size of ${ar} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTasksMany(t.ids),r=n.filter(m=>m!==null),o=t.ids.filter((m,f)=>n[f]===null),a={},i={};for(let m of r){let f=Tt(m.note);f!==void 0&&(a[m.id]=f);let g=Ae(m.note);g!==void 0&&(i[m.id]=g);}let s=Object.keys(a).length>0,c=Object.keys(i).length>0,d=o.length>0?[wt(o)]:void 0,l=e.makeMeta({...d!==void 0?{warnings:d}:{}});return p({tasks:r,...s&&{waitingOn:a},...c&&{decisions:i}},l)}function sm(t,e){return t.registerTool("task_get_many",{description:yi,inputSchema:mS.shape},async n=>{let r=await fS(n,e);return u(r)})}var im=z.object({self:z.string(),project:z.string().nullable(),parent:z.string().nullable(),tags:z.array(z.string())});z.object({self:z.string(),folder:z.string().nullable()});function et(t){return {self:`omnifocus://task/${t.id}`,project:t.projectId!==null?`omnifocus://project/${t.projectId}`:null,parent:t.parentId!==null?`omnifocus://task/${t.parentId}`:null,tags:t.tagIds.map(e=>`omnifocus://tag/${e}`)}}function ki(t){return {self:`omnifocus://project/${t.id}`,folder:t.folderId!==null?`omnifocus://folder/${t.folderId}`:null}}function Nt(t){let e=JSON.stringify(Object.fromEntries(Object.entries(t).filter(([,n])=>n!==void 0).sort(([n],[r])=>n.localeCompare(r))));return createHash("sha256").update(e).digest("hex")}function Ft(t){let e=JSON.stringify(t);return Buffer.from(e,"utf8").toString("base64url")}function Ut(t,e){let n;try{n=Buffer.from(t,"base64url").toString("utf8");}catch{throw new y("Cursor is not valid base64url.",{suggestion:"Pass the cursor value exactly as returned by the previous response."})}let r;try{r=JSON.parse(n);}catch{throw new y("Cursor payload is not valid JSON.",{suggestion:"Pass the cursor value exactly as returned by the previous response."})}let o=r;if(typeof r!="object"||r===null||typeof o.lastId!="string"||o.lastSortValue!==null&&typeof o.lastSortValue!="string"||typeof o.filterHash!="string")throw new y("Cursor payload is missing required fields.",{suggestion:"Pass the cursor value exactly as returned by the previous response."});let a=r;if(a.filterHash!==e)throw new y("Cursor filter hash does not match the current query filters. Start a fresh query.",{suggestion:"Call the list tool without a cursor to begin a new page sequence with the updated filters.",details:{cursorFilterHash:a.filterHash,currentFilterHash:e}});return a}function Lt(t,e,n="asc"){let r=t.sortValue,o=e.lastSortValue;return r===null&&o===null?t.id>e.lastId:r===null?n==="asc":o===null?n==="desc":r!==o?n==="asc"?r>o:r<o:t.id>e.lastId}var dm=z.enum(["dueDate","createdAt","modifiedAt","name"]),cm=200,Ii=1e3,sr=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async list(e){let n=this.resolveLimit(e);this.assertBounded(e,n);let r=this.normalize(e),o=Nt(r),a=e.cursor!==void 0?Ut(e.cursor,o):void 0,i=this.cacheKeyFor(o,e.cursor),s=this.cache.has(i),{tasks:c,nextCursor:d}=await this.cache.wrap(i,async()=>this.fetchPage(e,r,a,n,o));return {tasks:c,nextCursor:d,hasMore:d!==null,cacheHit:s}}async get(e){let n=e.includeSubtasks??true,r=`task:${e.id}:${n?"with-subtasks":"solo"}`,o=this.cache.has(r);return {...await this.cache.wrap(r,async()=>{let i=await this.adapter.getTask(e.id),s={...i,_links:et(i)};if(!n)return {task:s};let d=(await this.adapter.listTasks({parentId:e.id})).map(l=>({...l,_links:et(l)}));return {task:s,subtasks:d}}),cacheHit:o}}async fetchPage(e,n,r,o,a){let i=this.toAdapterFilter(e,n),s=await this.adapter.listTasks(i),c=n.tagIds.length>1?s.filter(j=>n.tagIds.every(B=>j.tagIds.includes(B))):s,{updatedSince:d}=n,l=d!==void 0?c.filter(j=>j.modifiedAt>d):c,{sortBy:m,sortDirection:f}=n,g=j=>{switch(m){case "dueDate":return j.dueDate??null;case "modifiedAt":return j.modifiedAt;case "name":return j.name;default:return j.createdAt}},v=[...l].sort((j,B)=>{let _=g(j),U=g(B);if(_===null&&U===null)return j.id<B.id?-1:1;if(_===null)return 1;if(U===null)return -1;if(_!==U){let M=_<U?-1:1;return f==="asc"?M:-M}return j.id<B.id?-1:1}),I=r!==void 0?v.filter(j=>Lt({id:j.id,sortValue:g(j)},r,f)):v,T=I.slice(0,o),w=I.length>o?this.encodeNextCursor(T,a,g):null;return {tasks:T.map(j=>({...j,_links:et(j)})),nextCursor:w}}resolveLimit(e){if(e.limit===void 0)return cm;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>Ii)throw new y(`limit must be an integer between 1 and ${Ii}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${Ii}, or omit to use the default of ${cm}.`,details:{field:"limit",value:e.limit}});return e.limit}assertBounded(e,n){if(!(e.limit!==void 0||e.cursor!==void 0)&&!this.hasAnyFilter(e))throw new y("task_list requires at least one filter, limit, or cursor. Unbounded queries are rejected.",{suggestion:"Provide a filter or a limit.",details:{field:"filter|limit|cursor"}})}hasAnyFilter(e){return e.projectId!==void 0||e.tagIds!==void 0&&e.tagIds.length>0||e.flagged!==void 0||e.available!==void 0||e.completed!==void 0||e.dueBefore!==void 0||e.dueAfter!==void 0||e.deferredBefore!==void 0||e.parentId!==void 0||e.updatedSince!==void 0||e.inbox===true}normalize(e){let n=e.tagIds!==void 0?[...new Set(e.tagIds)].sort((o,a)=>o.localeCompare(a)):[],r;if(e.updatedSince!==void 0)if(Kt(e.updatedSince))r=e.updatedSince;else if(qt(e.updatedSince))r=Vt(e.updatedSince);else throw new y(`updatedSince must be an ISO-8601 timestamp with offset or a relative shortcut (today, yesterday, this-week, next-week, end-of-week, end-of-month). Got: "${e.updatedSince}".`,{details:{field:"updatedSince",value:e.updatedSince},suggestion:"Pass an ISO-8601 string with offset (e.g. '2026-04-21T10:00:00-07:00') or a shortcut like 'today'."});if(e.inbox&&(e.projectId!==void 0||e.parentId!==void 0))throw new y("inbox filter cannot be combined with projectId or parentId \u2014 inbox tasks have no project assignment.",{suggestion:"Remove projectId/parentId when filtering by inbox.",details:{field:"inbox"}});return {projectId:e.projectId,tagIds:n,flagged:e.flagged,available:e.available,completed:e.completed,dueBefore:e.dueBefore,dueAfter:e.dueAfter,deferredBefore:e.deferredBefore,parentId:e.parentId,sortBy:e.sortBy??"createdAt",sortDirection:e.sortDirection??"asc",updatedSince:r,inbox:e.inbox}}toAdapterFilter(e,n){let r={};if(n.projectId!==void 0&&(r.projectId=n.projectId),n.parentId!==void 0&&(r.parentId=n.parentId),n.tagIds.length===1){let o=n.tagIds[0];o!==void 0&&(r.tagId=o);}return n.flagged!==void 0&&(r.flagged=n.flagged),n.available!==void 0&&(r.available=n.available),n.dueBefore!==void 0&&(r.dueBefore=n.dueBefore),n.dueAfter!==void 0&&(r.dueAfter=n.dueAfter),n.deferredBefore!==void 0&&(r.deferredBefore=n.deferredBefore),n.completed==="only"?r.completed=true:n.completed==="exclude"&&(r.completed=false),n.inbox===true&&(r.inbox=true),r}cacheKeyFor(e,n){return `search:tasks:${e}:${n??"first"}`}encodeNextCursor(e,n,r){let o=e[e.length-1];if(o===void 0)throw new y("Internal: cannot encode cursor for empty page.");return Ft({lastId:o.id,lastSortValue:r(o),filterHash:n})}};var vi='List tasks in OmniFocus with optional filters (project, tag, inbox, flagged, completion, due dates). Use inbox=true to fetch unprocessed Inbox tasks. Use this for filter-based queries across tasks. Do NOT use for a known single task (use task_get). For name-based lookup, prefer task_find_by_name. For full-text content search across names and notes, prefer search_query. Returns tasks[] with pagination; safe to call repeatedly; no side effects. Example: task_list({ inbox: true }) Example: task_list({ projectId: "prj123", flagged: true }) Example: task_list({ dueBefore: "2026-05-01T00:00:00Z", completed: "exclude" })',yS=z.object({projectId:k.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list. Omit for all projects."),tagIds:z.array(S.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs. Get IDs from tag_list."),flagged:z.boolean().optional().describe("true = flagged only; false = unflagged only; omit = all."),available:z.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred). Omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed tasks only; 'any' = both. Omit for adapter default."),dueBefore:z.string().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset (e.g. '2026-04-21T17:00:00-04:00')."),dueAfter:z.string().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset."),deferredBefore:z.string().optional().describe("Tasks deferred until before this moment (already unlocked or soon). ISO-8601 with offset."),parentId:h.schema.optional().describe("Restrict to direct children of this task (subtasks). Get the ID from task_get or task_list."),limit:z.number().int().min(1).max(1e3).optional().describe("Max tasks per page (1..1000). Default 200. Use `cursor` to fetch subsequent pages."),sortBy:dm.optional().describe("Field to sort tasks by: 'createdAt' (default), 'dueDate', 'modifiedAt', or 'name'. Tasks with no value for the chosen field (e.g. no dueDate) sort last."),sortDirection:z.enum(["asc","desc"]).optional().describe("Sort direction: 'asc' (default, oldest/lowest first) or 'desc' (newest/highest first)."),updatedSince:Le().optional().describe("Return only tasks modified strictly after this timestamp. Accepts ISO-8601 with offset (e.g. '2026-04-21T10:00:00-07:00') or a relative shortcut: today, yesterday, this-week, next-week, end-of-week, end-of-month. Use this for incremental sync: call without updatedSince on session start, then pass the previous response timestamp on subsequent calls. Note: deleted tasks cannot be detected \u2014 use a snapshot resource for deletion detection."),inbox:z.boolean().optional().describe("true = Inbox tasks only (no project assignment). Cannot be combined with projectId or parentId. Use this to surface unprocessed captures without knowing their IDs."),cursor:z.string().optional().describe("Opaque cursor from a previous task_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")});async function kS(t,e){let n=t,r=await e.taskService.list(n),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return p({tasks:r.tasks},a,o)}function lm(t,e){return t.registerTool("task_list",{description:vi,inputSchema:yS.shape},async n=>{let r=await kS(n,e);return u(r)})}var wi='Move an OmniFocus task to a new location \u2014 a different project, another task (as a subtask), or the inbox. Exactly one destination must be specified: projectId, parentId, or toInbox: true. Do NOT use task_move to reorder siblings within the same parent (task_reorder handles that); prefer task_update when you only need to change editable fields, not reparent. Idempotent: returns noChange: true when the task is already at the destination. Returns { moved: true, id, from, to } or { noChange: true, id, at }. Side effects: reparents the task in OmniFocus, sets meta.syncPending = true. Example: task_move({ id: "abc123", projectId: "prj456" }) Example: task_move({ id: "abc123", parentId: "tsk789" })',Si=z.object({id:h.schema.describe("Persistent ID of the task to move."),projectId:k.schema.optional().describe("Move into this project. Mutually exclusive with parentId and toInbox."),parentId:h.schema.optional().describe("Move under this parent task (as a subtask). Mutually exclusive with projectId and toInbox."),toInbox:z.literal(true).optional().describe("Set to true to move the task to the inbox (clear any project or parent). Mutually exclusive with projectId and parentId.")}).describe("Exactly one of projectId, parentId, or toInbox must be set. Any other combination returns a ValidationError.");async function IS(t,e){let n=(t.projectId!==void 0?1:0)+(t.parentId!==void 0?1:0)+(t.toInbox===true?1:0);if(n!==1)throw new y("task_move requires exactly one of projectId, parentId, or toInbox",{details:{field:"projectId|parentId|toInbox",provided:n},suggestion:"Set exactly one destination field \u2014 see tool schema."});let r=await e.adapter.getTask(t.id),o=t.projectId!==void 0&&r.projectId===t.projectId&&r.parentId===null,a=t.parentId!==void 0&&r.parentId===t.parentId&&r.projectId===null,i=t.toInbox===true&&r.projectId===null&&r.parentId===null;if(o||a||i)return p({noChange:true,id:t.id,at:Ti(r.projectId,r.parentId)},e.makeMeta());let s=Ti(r.projectId,r.parentId),c=t.projectId!==void 0?{projectId:t.projectId}:t.parentId!==void 0?{parentId:t.parentId}:{};await e.adapter.moveTask(t.id,c),e.cache!==void 0&&(b(e.cache,{taskId:t.id,projectId:r.projectId}),t.projectId!==void 0&&t.projectId!==r.projectId&&b(e.cache,{projectId:t.projectId}));let d=t.toInbox===true?{inbox:true}:Ti(t.projectId??null,t.parentId??null);return p({moved:true,id:t.id,from:s,to:d},e.makeMeta({syncPending:true,humanReadableSummary:vd(r.name,t.toInbox===true?"inbox":t.projectId!=null?"project":"parent task")}))}function Ti(t,e){return e!==null?{parentId:e}:t!==null?{projectId:t}:{inbox:true}}function um(t,e){return t.registerTool("task_move",{description:wi,inputSchema:Si.shape},async n=>{let r=await IS(n,e);return u(r)})}var bi="Preview what task_move would do without making any changes. Do NOT use to actually move a task \u2014 use task_move instead. Returns { description, plannedChanges } describing the reparenting that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function vS(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTask(t.id)).name;}catch{}let o;if(t.projectId!==void 0){let i=await _t(e.adapter,t.projectId);n.push({field:"projectId",newValue:t.projectId}),o=`project '${i}'`;}else if(t.parentId!==void 0){let i=await Pt(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`subtask of '${i}'`;}else n.push({field:"location",newValue:"inbox"}),o="Inbox";let a=`Would move task '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function mm(t,e){return t.registerTool("task_move_describe",{description:bi,inputSchema:Si.shape},async n=>{let r=await vS(n,e);return u(r)})}function ji(t){let e=(g,v=2)=>String(g).padStart(v,"0"),n=t.getFullYear(),r=e(t.getMonth()+1),o=e(t.getDate()),a=e(t.getHours()),i=e(t.getMinutes()),s=e(t.getSeconds()),c=-t.getTimezoneOffset(),d=c>=0?"+":"-",l=Math.abs(c),m=e(Math.floor(l/60)),f=e(l%60);return `${n}-${r}-${o}T${a}:${i}:${s}${d}${m}:${f}`}function fm(t,e,n){let r=t.toLowerCase(),o=new Date;if(r==="today"){let i=new Date(o.getFullYear(),o.getMonth(),o.getDate(),0,0,0);return {value:ji(i)}}if(r==="tomorrow"){let i=new Date(o.getFullYear(),o.getMonth(),o.getDate()+1,0,0,0);return {value:ji(i)}}if(/^\d{4}-\d{2}-\d{2}(T.*)?$/.test(t)){let i=new Date(t);if(!Number.isNaN(i.getTime())){if(/^\d{4}-\d{2}-\d{2}$/.test(t)){let s=t.split("-"),c=Number(s[0]),d=Number(s[1]),l=Number(s[2]),m=new Date(c,d-1,l,0,0,0);return {value:ji(m)}}return {value:t}}}return {value:t,warning:`Line ${e}: ${n} date '${t}' is not a recognized date format; passing through as-is`}}function gm(t){let e=t.split(`
|
|
228
|
+
`),n=[],r=[],o;for(let a=0;a<e.length;a++){let i=a+1,s=(e[a]??"").trim();if(s==="")continue;if(/^Project:\s*/i.test(s)){o=s.replace(/^Project:\s*/i,"").trim()||void 0;continue}let c=s,d=[],l,m,f=false,g,v=c.indexOf("//");v!==-1&&(g=c.slice(v+2).trim(),c=c.slice(0,v).trim());let I=c.split(/\s+/).filter(D=>D.length>0),T=[];for(let D of I)if(D==="!!")f=true;else if(D.startsWith("::")){let j=D.slice(2);if(j.length>0){let{value:B,warning:_}=fm(j,i,"Defer");m=B,_&&r.push(_);}}else if(D.startsWith("@")){let j=D.slice(1);j.length>0&&d.push(j);}else if(D.startsWith("#")){let j=D.slice(1);if(j.length>0){let{value:B,warning:_}=fm(j,i,"Due");l=B,_&&r.push(_);}}else T.push(D);let E=T.join(" ").trim();if(E===""){r.push(`Line ${i}: task line has no name after removing tokens; skipping`);continue}let w={name:E};g!==void 0&&g.length>0&&(w.note=g),f&&(w.flagged=true),l!==void 0&&(w.dueDate=l),m!==void 0&&(w.deferDate=m),d.length>0&&(w.tagNames=d),o!==void 0&&(w.projectName=o),n.push(w);}return {tasks:n,warnings:r}}var _i=`Parse OmniFocus transport text DSL into structured task objects \u2014 no tasks are created. Supports @tag, #due-date, ::defer-date, !!, and //note tokens; a leading 'Project: Name' line sets the project context for subsequent tasks. Do not use this tool to create tasks; pass the returned tasks[] to task_create separately. Returns tasks[] with name, tagNames, dueDate, deferDate, flagged, note, and projectName fields, plus count and an optional warnings[] for unparseable dates. Tag names and project names are raw strings \u2014 resolve to IDs with tag_list before passing to task_create. Read-only; no side effects. Example: task_parse_transport_text({ text: "Buy milk @errands !!\\nWrite report #2026-05-01" })`,TS=z.object({text:z.string().min(1).describe("Transport text to parse. One task per line; 'Project: Name' prefix sets project context.")});async function wS(t,e){let n=gm(t.text),r=e.makeMeta();return p({tasks:n.tasks,count:n.tasks.length,...n.warnings.length>0&&{warnings:n.warnings}},r)}function ym(t,e){return t.registerTool("task_parse_transport_text",{description:_i,inputSchema:TS.shape},async n=>{let r=await wS(n,e);return u(r)})}function gn(t,e){switch(t.kind){case "title-contains":{let n=t.caseSensitive?e.name:e.name.toLowerCase(),r=t.caseSensitive?t.value:t.value.toLowerCase();return n.includes(r)}case "tag":return e.tagIds.some(n=>String(n)===String(t.tagId));case "project":return e.projectId!==null&&String(e.projectId)===String(t.projectId);case "and":return t.predicates.every(n=>gn(n,e));case "or":return t.predicates.some(n=>gn(n,e));case "not":return !gn(t.predicate,e)}}var Oi=`Predicate-driven bulk task reclassification with a mandatory two-phase contract. Phase 1 (dryRun: true): match tasks by predicate, return { matched, proposed: [{taskId, before, after}] } with no mutations. Phase 2 (dryRun: false): require \`confirmation\` echoing the matched count from the prior dry-run; mismatch fails fast. Hard cap: dryRun: false rejects > 200 matches (use task_batch_update with explicit IDs for larger sets). Predicate is a discriminated-union AST: { kind: 'title-contains', value, caseSensitive? } | { kind: 'tag', tagId } | { kind: 'project', projectId } | { kind: 'and', predicates: [] } | { kind: 'or', predicates: [] } | { kind: 'not', predicate }. Changes apply uniformly to every match: addTags, removeTags, setProject, setFlagged. Do NOT use this tool when you have explicit task IDs \u2014 call task_batch_update directly. Prefer task_reclassify whenever the targets are described by a rule rather than a list, so the dry-run diff surfaces to the user before any write. Side effects (apply phase only): writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_reclassify({ predicate: { kind: "tag", tagId: "tag123" }, changes: { setFlagged: true }, dryRun: true }) Example: task_reclassify({ predicate: { kind: "tag", tagId: "tag123" }, changes: { setFlagged: true }, dryRun: false, confirmation: "3" })`,Pi=200,ir=z.lazy(()=>z.discriminatedUnion("kind",[z.object({kind:z.literal("title-contains"),value:z.string().describe("Substring to search for in task names."),caseSensitive:z.boolean().optional().describe("When true, exact-case match. Default false (case-insensitive).")}),z.object({kind:z.literal("tag"),tagId:S.schema.describe("Match tasks carrying this tag.")}),z.object({kind:z.literal("project"),projectId:k.schema.describe("Match tasks in this project.")}),z.object({kind:z.literal("and"),predicates:z.array(ir).describe("All children must match.")}),z.object({kind:z.literal("or"),predicates:z.array(ir).describe("Any child match suffices.")}),z.object({kind:z.literal("not"),predicate:ir.describe("Inverts the inner predicate's result.")})])),SS=z.object({addTags:z.array(S.schema).optional().describe("Tag IDs to add to every match."),removeTags:z.array(S.schema).optional().describe("Tag IDs to remove from every match."),setProject:k.schema.optional().describe("Move every match to this project."),setFlagged:z.boolean().optional().describe("Set the flagged state on every match.")}).refine(t=>t.addTags!==void 0||t.removeTags!==void 0||t.setProject!==void 0||t.setFlagged!==void 0,{message:"changes must set at least one of addTags, removeTags, setProject, or setFlagged"}),km=z.object({predicate:ir.describe("AST for selecting tasks. Composable via and/or/not. Always evaluated against open (non-completed, non-dropped) tasks."),changes:SS.describe("Changes applied uniformly to every matched task."),dryRun:z.boolean().default(true).describe("Default true \u2014 return the diff without mutating. false requires `confirmation` echoing the matched count from a prior dry-run."),confirmation:z.string().optional().describe('When dryRun is false, the matched count from the most recent dry-run, as a string (e.g. "42"). Mismatch with the actual current match count fails the call fast.')}),bS=km.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation is required when dryRun is false",path:["confirmation"]});function jS(t,e){let n=e.setProject!==void 0?String(e.setProject):t.projectId===null?null:String(t.projectId),r=xs(t.tagIds,e.addTags,e.removeTags).map(String),o=e.setFlagged!==void 0?e.setFlagged:t.flagged;return {projectId:n,tagIds:r,flagged:o}}function _S(t){return {projectId:t.projectId===null?null:String(t.projectId),tagIds:t.tagIds.map(String),flagged:t.flagged}}async function PS(t,e){je(bS,t);let r=(await e.adapter.listTasks({completed:false})).filter(s=>gn(t.predicate,s));if(t.dryRun){let s=r.map(c=>({taskId:String(c.id),name:c.name,before:_S(c),after:jS(c,t.changes)}));return p({phase:"dryRun",matched:r.length,proposed:s},e.makeMeta())}let o=String(r.length);if(t.confirmation!==o)return p({phase:"stale-confirmation",matched:r.length,confirmation:t.confirmation,message:`confirmation must equal the current match count (${o}); the underlying task set may have changed since the dry-run`},e.makeMeta());if(r.length>Pi)return p({phase:"over-cap",matched:r.length,cap:Pi,message:`${r.length} matches exceeds the ${Pi}-task hard cap; use task_batch_update with explicit IDs for larger sets`},e.makeMeta());let a=r.map(s=>({taskId:h.of(String(s.id)),...t.changes.setProject!==void 0&&{projectId:t.changes.setProject},...t.changes.addTags!==void 0&&{addTagIds:t.changes.addTags},...t.changes.removeTags!==void 0&&{removeTagIds:t.changes.removeTags},...t.changes.setFlagged!==void 0&&{flagged:t.changes.setFlagged}}));if(a.length===0)return p({phase:"applied",matched:0,assigned:[],failed:[]},e.makeMeta());let i=await Ds({assignments:a},e);return "data"in i?p({phase:"applied",matched:r.length,assigned:i.data.assigned,failed:i.data.failed},e.makeMeta({syncPending:i.data.assigned.length>0})):i}function Im(t,e){return t.registerTool("task_reclassify",{description:Oi,inputSchema:km.shape},async n=>{let r=await PS(n,e);return u(r)})}var xi='Reorder an OmniFocus task among its siblings. OmniFocus has no numeric sibling index \u2014 position is always expressed relative to another task (before / after) or as the absolute start / end of a container. Do NOT use task_reorder to reparent a task to a different project or parent (task_move handles reparenting); prefer task_move when the task needs to change containers without caring about sibling order. Exactly one positioning form must be set: { before }, { after }, or { at, in }. Returns { reordered: true, id, position }. Side effects: changes sibling order in OmniFocus, sets meta.syncPending = true. Example: task_reorder({ id: "abc123", before: "abc456" }) Example: task_reorder({ id: "abc123", at: "start", in: { projectId: "prj456" } })',OS=z.union([z.object({projectId:k.schema}),z.object({parentId:h.schema}),z.object({inbox:z.literal(true)})]).describe("Container for start/end positioning. Exactly one of projectId, parentId, or inbox: true."),xS=z.object({id:h.schema.describe("Persistent ID of the task to reorder."),before:h.schema.optional().describe("Position the task immediately before this sibling. Reference must share the same parent."),after:h.schema.optional().describe("Position the task immediately after this sibling. Reference must share the same parent."),at:z.enum(["start","end"]).optional().describe("Absolute position within a container. Requires `in` to identify the container."),in:OS.optional().describe("Required when `at` is set; ignored otherwise.")}).describe("Exactly one positioning form: { before }, { after }, or { at, in }. Any other combination returns a ValidationError.");async function DS(t,e){let n=(t.before!==void 0?1:0)+(t.after!==void 0?1:0)+(t.at!==void 0?1:0);if(n!==1)throw new y("task_reorder requires exactly one positioning form: { before }, { after }, or { at, in }",{details:{field:"before|after|at",provided:n},suggestion:"Set exactly one positioning field."});if(t.at!==void 0&&t.in===void 0)throw new y("task_reorder: `in` is required when `at` is set",{details:{field:"in"},suggestion:"Provide `in: { projectId } | { parentId } | { inbox: true }`."});if(t.at===void 0&&t.in!==void 0)throw new y("task_reorder: `in` is only valid alongside `at`",{details:{field:"in"},suggestion:"Either add `at: 'start' | 'end'` or remove `in`."});let r=await e.adapter.getTask(t.id),o=r.projectId,a;if(t.before!==void 0)a={before:t.before};else if(t.after!==void 0)a={after:t.after};else if(t.at!==void 0&&t.in!==void 0)a={at:t.at,in:t.in};else throw new y("task_reorder: no positioning form matched",{details:{field:"before|after|at"}});return await e.adapter.reorderTask(t.id,a),e.cache!==void 0&&(b(e.cache,{taskId:t.id,projectId:o}),t.at!==void 0&&t.in!==void 0&&"projectId"in t.in&&t.in.projectId!==o&&b(e.cache,{projectId:t.in.projectId})),p({reordered:true,id:t.id,position:a},e.makeMeta({syncPending:true,humanReadableSummary:wd(r.name)}))}function vm(t,e){return t.registerTool("task_reorder",{description:xi,inputSchema:xS.shape},async n=>{let r=await DS(n,e);return u(r)})}var Di='Search OmniFocus tasks by keyword and/or structured filters, with cursor pagination. q is optional \u2014 omit it to filter by tag, project, date range, or availability alone. When q is supplied, scans task names and/or notes (controlled by scope) for a case-insensitive substring match. Narrow results with: projectId, tagIds (task must carry ALL listed tags), available, dueBefore, dueAfter, flagged, and completed. At least one of q, projectId, tagIds, available, dueBefore, or dueAfter must be provided. Do NOT use when you already have an ID \u2014 prefer task_get instead. Returns tasks[] with pagination (limit defaults to 100, max 500); safe to call repeatedly; no side effects. Example: task_search({ q: "dentist" }) Example: task_search({ tagIds: ["tag123"], available: true, dueBefore: "2026-05-01T00:00:00Z" })',Tm={q:z.string().min(1).optional().describe("Search query. Case-insensitive substring match applied to the fields in scope. Optional \u2014 omit to filter by tags, project, date range, or availability alone."),scope:z.enum(["name","note","all"]).optional().describe("'name' = search task name only; 'note' = search note only; 'all' = both (default). Ignored when q is omitted."),projectId:k.schema.optional().describe("Restrict search to tasks within this project."),tagIds:z.array(S.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs."),available:z.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred, not completed). Omit = all."),dueBefore:Le().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset or relative shortcut."),dueAfter:Le().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset or relative shortcut."),flagged:z.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only (default); 'only' = completed tasks only; 'any' = both."),limit:z.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 100. Use cursor to fetch subsequent pages."),cursor:z.string().optional().describe("Opaque cursor from a previous task_search response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")},RS=z.object(Tm).refine(t=>t.q!==void 0||t.projectId!==void 0||t.tagIds!==void 0||t.available!==void 0||t.dueBefore!==void 0||t.dueAfter!==void 0,{message:"At least one of q, projectId, tagIds, available, dueBefore, or dueAfter must be provided."});async function AS(t,e){je(RS,t);let n=await e.searchService.search({...t.q!==void 0&&{q:t.q},...t.scope!==void 0&&{scope:t.scope},...t.projectId!==void 0&&{projectId:t.projectId},...t.tagIds!==void 0&&{tagIds:t.tagIds},...t.available!==void 0&&{available:t.available},...t.dueBefore!==void 0&&{dueBefore:t.dueBefore},...t.dueAfter!==void 0&&{dueAfter:t.dueAfter},...t.flagged!==void 0&&{flagged:t.flagged},...t.completed!==void 0&&{completed:t.completed},...t.limit!==void 0&&{limit:t.limit},...t.cursor!==void 0&&{cursor:t.cursor}}),r={cursor:n.nextCursor,hasMore:n.hasMore};return p({tasks:n.tasks},e.makeMeta({cacheHit:n.cacheHit}),r)}function wm(t,e){return t.registerTool("task_search",{description:Di,inputSchema:Tm},async n=>{let r=await AS(n,e);return u(r)})}var Sm=z.enum(["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]),CS=z.union([z.object({day:z.number().int().min(1).max(31)}),z.object({weekday:Sm,position:z.union([z.literal(1),z.literal(2),z.literal(3),z.literal(4),z.literal("last")])})]),Ri=z.discriminatedUnion("kind",[z.object({kind:z.literal("due-relative"),offsetSeconds:z.number().int()}),z.object({kind:z.literal("defer-relative"),offsetSeconds:z.number().int()}),z.object({kind:z.literal("absolute"),fireAt:oe()})]),Ai=z.object({method:z.enum(["fixed","start-again","due-again"]),unit:z.enum(["minutes","hours","days","weeks","months","years"]),steps:z.number().int().min(1),weekdays:z.array(Sm).optional(),monthlyAnchor:CS.optional()}).refine(t=>t.weekdays===void 0||t.unit==="weeks",{message:"weekdays is only valid when unit is 'weeks'",path:["weekdays"]}).refine(t=>t.monthlyAnchor===void 0||t.unit==="months",{message:"monthlyAnchor is only valid when unit is 'months'",path:["monthlyAnchor"]}).refine(t=>!(t.weekdays!==void 0&&t.monthlyAnchor!==void 0),{message:"Only one of weekdays or monthlyAnchor may be set"});z.object({id:h.schema,name:z.string(),note:z.string().nullable(),noteHtml:z.string().nullable(),projectId:k.schema.nullable(),parentId:h.schema.nullable(),tagIds:z.array(S.schema),deferDate:oe().nullable(),deferDateFloating:z.boolean().optional(),dueDate:oe().nullable(),dueDateFloating:z.boolean().optional(),estimatedMinutes:z.number().int().min(1).nullable(),flagged:z.boolean(),completed:z.boolean(),completedAt:oe().nullable(),dropped:z.boolean(),droppedAt:oe().nullable(),available:z.boolean(),blocked:z.boolean(),sequential:z.boolean(),completedByChildren:z.boolean(),repetition:Ai.nullable(),notifications:z.array(Ri).optional(),createdAt:oe(),modifiedAt:oe(),_links:im.optional()});var Ci=`Replace the alarm/notification set on an OmniFocus task atomically. Pass an array of alarms; this overwrites any existing alarms in full. Each alarm is one of: {kind:'due-relative', offsetSeconds:N} (positive = before due date, negative = after), {kind:'defer-relative', offsetSeconds:N} (relative to defer date), or {kind:'absolute', fireAt:ISO-8601 string}. Relative kinds require the task to already have the corresponding date set, or the call returns a VALIDATION error. Use task_clear_alarms to remove all alarms with no payload. Returns the updated task. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_set_alarms({ id: "abc123", alarms: [{ kind: "due-relative", offsetSeconds: 3600 }] }) Example: task_set_alarms({ id: "abc123", alarms: [{ kind: "absolute", fireAt: "2026-05-01T09:00:00Z" }] })`,ES=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),alarms:z.array(Ri).describe("Full replacement set of alarms. Empty array is permitted and equivalent to task_clear_alarms.")});async function MS(t,e){let n=await e.adapter.getTask(t.id);for(let a of t.alarms){if(a.kind==="due-relative"&&n.dueDate===null)throw new y("Cannot set a due-relative alarm on a task with no dueDate. Set the task's due date first, or use an absolute alarm.",{details:{field:"alarms",taskId:t.id,alarmKind:a.kind}});if(a.kind==="defer-relative"&&n.deferDate===null)throw new y("Cannot set a defer-relative alarm on a task with no deferDate. Set the task's defer date first, or use an absolute alarm.",{details:{field:"alarms",taskId:t.id,alarmKind:a.kind}})}await e.adapter.setTaskAlarms(t.id,t.alarms);let r=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:r.projectId});let o=e.makeMeta({syncPending:true,humanReadableSummary:_d(n.name,t.alarms.length)});return p({task:r},o)}function jm(t,e){return t.registerTool("task_set_alarms",{description:Ci,inputSchema:ES.shape},async n=>{let r=await MS(n,e);return u(r)})}var Ei='Set the repetition rule on an OmniFocus task. Overwrites any existing rule. Use task_clear_repetition to remove a rule entirely. Returns the updated task ID; call task_get for the full object. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_set_repetition({ id: "abc123", rule: { method: "fixed", unit: "days", steps: 7 } }) Example: task_set_repetition({ id: "abc123", rule: { method: "start-again", unit: "weeks", steps: 1 } })',FS=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),rule:Ai.describe("Repetition rule to apply. 'method': 'fixed' repeats from the due date, 'start-again' from completion, 'due-again' from due date (alias). 'unit': time unit for the interval. 'steps': how many units between occurrences (minimum 1). 'weekdays': optional array of day names \u2014 only valid when unit is 'weeks'. 'monthlyAnchor': optional day-of-month or weekday-position \u2014 only valid when unit is 'months'.")});async function US(t,e){await e.adapter.updateTask(t.id,{repetition:t.rule});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:bd(n.name)});return p({task:n},r)}function _m(t,e){return t.registerTool("task_set_repetition",{description:Ei,inputSchema:FS.shape},async n=>{let r=await US(n,e);return u(r)})}var Mi='Mark an OmniFocus task as incomplete \u2014 removes its completion timestamp. Idempotent: returns noChange: true if the task is already incomplete. Do not use to drop or delete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: clears completedAt, sets meta.syncPending = true.Example: task_uncomplete({ id: "abc123" })',BS=z.object({id:h.schema.describe("Persistent task ID.")});async function JS(t,e){let n=await e.adapter.getTask(t.id);return n.completed===false?p({noChange:true,id:t.id,name:n.name},e.makeMeta()):(await e.adapter.uncompleteTask(t.id),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:hd(n.name)})))}function Pm(t,e){return t.registerTool("task_uncomplete",{description:Mi,inputSchema:BS.shape},async n=>{let r=await JS(n,e);return u(r)})}var Ni='Restore a dropped OmniFocus task \u2014 clears its dropped status and returns it to the active view. Idempotent: returns noChange: true if the task is not dropped. Do not use to complete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: clears droppedAt, sets meta.syncPending = true.Example: task_undrop({ id: "abc123" })',WS=z.object({id:h.schema.describe("Persistent task ID.")});async function HS(t,e){let n=await e.adapter.getTask(t.id);return n.dropped===false?p({noChange:true,id:t.id,name:n.name},e.makeMeta()):(await e.adapter.undropTask(t.id),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Id(n.name)})))}function Om(t,e){return t.registerTool("task_undrop",{description:Ni,inputSchema:WS.shape},async n=>{let r=await HS(n,e);return u(r)})}var Fi='Partially update mutable fields on an OmniFocus task. Only supplied fields are changed; omit a field to leave it unchanged. Do not use to complete or delete a task; prefer task_complete or task_delete instead. Two tag-update modes: (1) supply tagIds to replace the full tag set; (2) supply addTags and/or removeTags to apply a diff without reading first. Supplying tagIds together with addTags/removeTags is a ValidationError. setFlagged is a convenience alias for flagged. Safety controls: set dry_run=true to preview the patched task without mutating; pass expectedModifiedAt (from a recent task_get) to reject the call if the task changed since you read it; pass idempotency_key to coalesce retries so the same update is only performed once. Returns the updated task. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_update({ id: "abc123", flagged: true }) Example: task_update({ id: "abc123", dueDate: "2026-05-01T00:00:00Z", addTags: ["tag456"] })',cr=z.object({id:h.schema.describe("Persistent task ID. Get from task_list or search_query."),name:z.string().min(1).optional().describe("New task name. Must be non-empty if supplied."),note:z.string().nullable().optional().describe("Plain-text note. Pass null to clear. HTML round-trip available in M3."),flagged:z.boolean().optional().describe("Flag or unflag the task. Alias: setFlagged."),setFlagged:z.boolean().optional().describe("Convenience alias for flagged. Use when your intent is specifically to set or clear the flag without touching other fields."),deferDate:z.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated duration in minutes. Pass null to clear."),sequential:z.boolean().optional().describe("Whether subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Whether the task completes when all children are complete."),tagIds:z.array(S.schema).optional().describe("Full-replacement tag list. Replaces all existing tags. Mutually exclusive with addTags/removeTags."),addTags:z.array(S.schema).optional().describe("Tags to add. No-op for tags the task already has. Mutually exclusive with tagIds."),removeTags:z.array(S.schema).optional().describe("Tags to remove. No-op for tags the task doesn't have. Mutually exclusive with tagIds."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input, computes the patched task (pre-fetch merged with the supplied fields), and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe updates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying the patch.")}),zS=cr.refine(t=>!(t.tagIds!==void 0&&(t.addTags!==void 0||t.removeTags!==void 0)),{message:"tagIds cannot be combined with addTags/removeTags. Use tagIds for full replacement, or addTags/removeTags for additive diff.",path:["tagIds"]}).refine(t=>!(t.dueDate!=null&&t.deferDate!=null&&new Date(t.dueDate)<new Date(t.deferDate)),{message:"dueDate must not be earlier than deferDate",path:["dueDate"]});async function GS(t,e){je(zS,t);let{id:n,addTags:r,removeTags:o,setFlagged:a,tagIds:i,...s}=t,c=e.idempotencyStore??de;return ce(c,t.idempotency_key,async()=>{let d=await e.adapter.getTask(n);Ye(t.expectedModifiedAt,d.modifiedAt,`task:${n}`);let l;if(r!==void 0||o!==void 0){let I=new Set(d.tagIds);for(let T of r??[])I.add(T);for(let T of o??[])I.delete(T);l=[...I];}else i!==void 0&&(l=i);let m=a!==void 0?a:s.flagged,f={...s.name!==void 0?{name:s.name}:{},...s.note!==void 0?{note:s.note}:{},...m!==void 0?{flagged:m}:{},...s.deferDate!==void 0?{deferDate:s.deferDate}:{},...s.deferDateFloating!==void 0?{deferDateFloating:s.deferDateFloating}:{},...s.dueDate!==void 0?{dueDate:s.dueDate}:{},...s.dueDateFloating!==void 0?{dueDateFloating:s.dueDateFloating}:{},...s.estimatedMinutes!==void 0?{estimatedMinutes:s.estimatedMinutes}:{},...s.sequential!==void 0?{sequential:s.sequential}:{},...s.completedByChildren!==void 0?{completedByChildren:s.completedByChildren}:{},...l!==void 0?{tagIds:l}:{}},g=()=>{let I={...d,...f};return p({task:I},e.makeMeta({syncPending:false}))},v=async()=>{await e.adapter.updateTask(n,f);let I=await e.adapter.getTask(n);return e.cache!==void 0&&b(e.cache,{taskId:n,projectId:I.projectId}),p({task:I},e.makeMeta({syncPending:true}))};return Ce(t.dry_run,g,v)})}function xm(t,e){return t.registerTool("task_update",{description:Fi,inputSchema:cr.shape},async n=>{let r=await GS(n,e);return u(r)})}var Ui="Preview what task_update would do without making any changes. Do NOT use to actually update a task \u2014 use task_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function qS(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getTask(t.id);o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate,oldValue:i.dueDate??null}),r.push(t.dueDate===null?"clear due date":`set due date to ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate,oldValue:i.deferDate??null}),r.push(t.deferDate===null?"clear defer date":`set defer date to ${Te(t.deferDate)}`));let s=t.setFlagged!==void 0?t.setFlagged:t.flagged;if(s!==void 0&&(n.push({field:"flagged",newValue:String(s),oldValue:String(i.flagged)}),r.push(s?"flag":"unflag")),t.estimatedMinutes!==void 0&&(n.push({field:"estimatedMinutes",newValue:t.estimatedMinutes===null?null:String(t.estimatedMinutes),oldValue:i.estimatedMinutes!=null?String(i.estimatedMinutes):null}),r.push(t.estimatedMinutes===null?"clear estimated minutes":`set estimated minutes to ${t.estimatedMinutes}`)),t.tagIds!==void 0){let c=await Promise.all(t.tagIds.map(d=>he(e.adapter,d)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`set tags to [${c.map(d=>`'${d}'`).join(", ")}]`);}else if(t.addTags!==void 0||t.removeTags!==void 0){if(t.addTags!==void 0&&t.addTags.length>0){let c=await Promise.all(t.addTags.map(d=>he(e.adapter,d)));n.push({field:"addTags",newValue:t.addTags.join(",")}),r.push(`add tags [${c.map(d=>`'${d}'`).join(", ")}]`);}if(t.removeTags!==void 0&&t.removeTags.length>0){let c=await Promise.all(t.removeTags.map(d=>he(e.adapter,d)));n.push({field:"removeTags",newValue:t.removeTags.join(",")}),r.push(`remove tags [${c.map(d=>`'${d}'`).join(", ")}]`);}}t.note!==void 0&&(n.push({field:"note",newValue:t.note===null?null:t.note.slice(0,50)}),r.push(t.note===null?"clear note":"update note"));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update task '${o}': ${r.join(", ")}.`:`Would update task '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function Dm(t,e){return t.registerTool("task_update_describe",{description:Ui,inputSchema:cr.shape},async n=>{let r=await qS(n,e);return u(r)})}var Li='Record that an OmniFocus task is waiting on someone or something. Tags the task with the configured @waiting tag (creating the tag if absent) and writes a structured `waiting-on` fenced block to the top of the task note. The fence preserves any existing user prose in the note. Round-trips through task_get / task_get_many as a structured `waitingOn` field. Surfaces in the omnifocus://waiting-on resource sorted by days overdue. Use to systematize follow-ups; do NOT use for task completion or scheduling. Returns { id, waitingOn } with the persisted entry. Side effects: writes tag + note; sets meta.syncPending = true. Example: { "taskId": "abc123", "whom": "Alex", "what": "design review", "followUpAfter": "2026-05-05T17:00:00Z" }',Bi='Clear waiting-on tracking from an OmniFocus task. Strips the `waiting-on` fenced block from the task note (preserving any other user prose) and removes the configured @waiting tag from the task. Idempotent: returns noChange:true when the task has no waiting-on data. Do NOT use to delete the task or remove unrelated tags \u2014 prefer task_delete or task_update instead. Returns { id, cleared:true } or { id, noChange:true }. Side effects: writes tag + note; sets meta.syncPending = true. Example: { "taskId": "abc123" }',VS=z.object({taskId:h.schema.describe("Persistent task ID."),whom:z.string().min(1).describe("Person, team, or system being waited on. Required."),what:z.string().min(1).optional().describe("Optional short description of what is being waited on."),since:z.string().datetime({offset:true}).optional().describe("ISO-8601 date the wait began. Defaults to now. Use to backfill historical waits."),followUpAfter:z.string().datetime({offset:true}).optional().describe("ISO-8601 date past which the agent should nudge if still unresolved. Drives daysOverdue in the omnifocus://waiting-on resource.")}),KS=z.object({taskId:h.schema.describe("Persistent task ID.")});async function XS(t,e){let n=await t.listTags(),r=e.toLowerCase(),o=n.find(a=>a.name.toLowerCase()===r);return o!==void 0?o.id:t.createTag({name:e})}async function YS(t,e){let n=await e.adapter.getTask(t.taskId),r=Qr.parse({whom:t.whom,...t.what!==void 0&&{what:t.what},since:t.since??new Date().toISOString(),...t.followUpAfter!==void 0&&{followUpAfter:t.followUpAfter}}),o=Xc(n.note,r),a=await XS(e.adapter,e.waitingTagName),i=n.tagIds.includes(a)?n.tagIds:[...n.tagIds,a];return await e.adapter.updateTask(t.taskId,{note:o,tagIds:i}),e.cache!==void 0&&b(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,waitingOn:r},e.makeMeta({syncPending:true}))}function Rm(t,e){return t.registerTool("task_set_waiting_on",{description:Li,inputSchema:VS.shape},async n=>{let r=await YS(n,e);return u(r)})}async function ZS(t,e){let n=await e.adapter.getTask(t.taskId),r=Yc(n.note),o=await e.adapter.listTags(),a=e.waitingTagName.toLowerCase(),i=o.find(l=>l.name.toLowerCase()===a)?.id,s=i!==void 0&&n.tagIds.includes(i),c=r!==n.note;if(!s&&!c)return p({id:t.taskId,noChange:true},e.makeMeta());let d={};return c&&(d.note=r),s&&i!==void 0&&(d.tagIds=n.tagIds.filter(l=>l!==i)),await e.adapter.updateTask(t.taskId,d),e.cache!==void 0&&b(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,cleared:true},e.makeMeta({syncPending:true}))}function Am(t,e){return t.registerTool("task_clear_waiting_on",{description:Bi,inputSchema:KS.shape},async n=>{let r=await ZS(n,e);return u(r)})}var Cm=z.object({tagId:z.string().min(1).optional().describe("Restrict to tasks carrying this tag."),projectId:z.string().min(1).optional().describe("Restrict to tasks in this project.")}).optional().describe("Optional filter narrowing which task events fire this webhook."),QS=z.object({projectId:z.string().min(1).optional().describe("Restrict to status changes on this project.")}).optional().describe("Optional filter narrowing which project events fire this webhook."),Em=z.discriminatedUnion("on",[z.object({on:z.literal("task-completed"),filter:Cm}),z.object({on:z.literal("task-created"),filter:Cm}),z.object({on:z.literal("project-status-changed"),filter:QS})]);function Ji(t){return {name:t.name,trigger:t.trigger,secretSet:t.secret!==void 0&&t.secret.length>0,createdAt:t.createdAt}}var Jt=z.string().min(1).max(64).regex(/^\S+$/,"must not contain whitespace").describe("Stable name for the webhook. Unique within the registry; used as the lookup key."),Mm=z.string().min(1).refine(t=>t.startsWith("https://"),{message:"url must use https:// (http:// rejected per ADR-0016 \xA74b)"}).describe("Outbound HTTPS URL. http:// is rejected."),Nm=z.string().min(8,"secret must be at least 8 characters when supplied").max(256).optional().describe("Optional HMAC seed for signature header. Stored on disk only; never echoed back. Receivers verify via X-OmniFocus-Signature: sha256=<hex> using the same secret.");var $i='Register an outbound webhook that fires when an OmniFocus state change matches the supplied trigger. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1 in the environment, mirroring the raw-script gating. URLs must use https:// (http:// is rejected at registration). An optional secret enables HMAC-SHA256 signature verification by the receiver via X-OmniFocus-Signature: sha256=<hex>; the secret is stored on disk only and is never echoed back through any tool response. Do NOT use this to call this MCP server itself \u2014 webhooks are outbound only. Returns { webhook: WebhookSummary } where the summary omits both URL and secret. Side effects: writes to the registry config file at ~/Library/Application Support/omnifocus-mcp/webhooks.json (mode 0600). Example: webhook_register({ name: "slack-billing", url: "https://hooks.slack.com/services/...", trigger: { on: "task-completed", filter: { tagId: "tag_xyz" } } })',tb=z.object({name:Jt.describe("Stable name for the webhook. Unique within the registry; used as the lookup key. \u226464 chars, no whitespace."),url:Mm.describe("Outbound HTTPS URL. http:// is rejected at registration (per ADR-0016 \xA74b)."),trigger:Em.describe("What triggers a webhook fire \u2014 one of task-completed, task-created, or project-status-changed. Each variant accepts an optional filter narrowing which entities count."),secret:Nm.describe("Optional HMAC seed (8\u2013256 chars). When set, every delivery includes an X-OmniFocus-Signature: sha256=<hex> header so the receiver can verify authenticity. Stored on disk only; never echoed.")});function hn(t){if(!t.enabled)throw new y("Webhook subsystem is disabled. Set OMNIFOCUS_WEBHOOKS_ENABLED=1 in the environment to enable.",{details:{field:"OMNIFOCUS_WEBHOOKS_ENABLED"}})}async function nb(t,e){hn(e);let n=e.registry.register({name:t.name,url:t.url,trigger:t.trigger,...t.secret!==void 0&&{secret:t.secret}});return p({webhook:n},e.makeMeta())}function Fm(t,e){return t.registerTool("webhook_register",{description:$i,inputSchema:tb.shape},async n=>{let r=await nb(n,e);return u(r)})}var Wi='Delete a registered outbound webhook by name. Idempotent \u2014 returns noChange:true when the named webhook does not exist. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1. Do NOT use this for bulk-clear operations; this tool removes exactly one entry. Returns { name, deleted:true } or { name, noChange:true }. Side effects: rewrites the registry config file at ~/Library/Application Support/omnifocus-mcp/webhooks.json. Example: webhook_delete({ name: "slack-billing" })',ob=z.object({name:Jt.describe("Name of the registered webhook to delete. Idempotent \u2014 unknown names return noChange:true.")});async function ab(t,e){return hn(e),e.registry.delete(t.name)?p({name:t.name,deleted:true},e.makeMeta()):p({name:t.name,noChange:true},e.makeMeta())}function Um(t,e){return t.registerTool("webhook_delete",{description:Wi,inputSchema:ob.shape},async n=>{let r=await ab(n,e);return u(r)})}var Hi="List every registered outbound webhook by name, trigger, and createdAt timestamp. URLs and secrets are NEVER surfaced \u2014 only metadata safe to display. Use this to confirm what's wired up; delete unwanted entries via webhook_delete. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1. Do NOT use this to retrieve URLs or secrets \u2014 by design they remain on-disk only. Returns { webhooks: WebhookSummary[] } in registration order. Read-only; safe to call repeatedly. Example: webhook_list()",ib=z.object({});async function cb(t,e){return hn(e),p({webhooks:e.registry.list()},e.makeMeta())}function Lm(t,e){return t.registerTool("webhook_list",{description:Hi,inputSchema:ib.shape},async n=>{let r=await cb(n,e);return u(r)})}var zi=`Fire a synthetic event through a registered webhook to verify it's wired correctly. Goes through the same HTTPS POST + HMAC + retry + circuit-breaker path as a real delivery \u2014 if the receiver doesn't see this event, it won't see real ones either. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1. Do NOT use this for load testing \u2014 circuit-breaker counters apply to synthetic events too. Returns { name, delivered: true } on dispatch success, { name, error } when the webhook is not registered. Note: 'delivered' means the dispatcher attempted delivery; the receiver's actual response is not surfaced (per ADR-0016 \xA74e: failures log to stderr, never throw upward). Side effects: makes one outbound HTTPS POST to the registered URL with a synthetic event payload. Example: webhook_test({ name: "slack-billing" })`,lb=z.object({name:Jt.describe("Name of the registered webhook to fire a synthetic event through.")});async function pb(t,e){if(!e.enabled)throw new y("Webhook subsystem is disabled. Set OMNIFOCUS_WEBHOOKS_ENABLED=1 in the environment to enable.",{details:{field:"OMNIFOCUS_WEBHOOKS_ENABLED"}});let n=await e.orchestrator.fireSynthetic(t.name);return "delivered"in n?p({name:t.name,delivered:true},e.makeMeta()):p({name:t.name,error:n.error},e.makeMeta())}function Bm(t,e){return t.registerTool("webhook_test",{description:zi,inputSchema:lb.shape},async n=>{let r=await pb(n,e);return u(r)})}var Gi="Read the active perspective and focus container of the front OmniFocus window. **UI-affecting tool family** \u2014 only meaningful in pair-assistant flows where the user is looking at OmniFocus. Headless agents should ignore. Use when the agent needs to know what view the user currently sees, or to confirm that a prior `window_set_*` took effect. Do NOT use to evaluate a perspective's data \u2014 prefer `perspective_evaluate`, which doesn't depend on UI state. Takes no arguments. Returns { perspectiveName: string | null, focusContainerIds: string[] } \u2014 perspectiveName is null when no perspective is bound; focusContainerIds is [] when the window isn't focused on a project or folder. Errors: OF_WINDOW_UNAVAILABLE when OmniFocus has no front window. Read-only; safe to retry. Example: window_get_state()",ub=z.object({});async function mb(t,e){let n=await e.adapter.getWindowState();return p(n,e.makeMeta())}function Jm(t,e){return t.registerTool("window_get_state",{description:Gi,inputSchema:ub.shape},async n=>u(await mb(n,e)))}var qi=`Switch the front OmniFocus window to a named perspective (built-in or custom). **UI-affecting tool** \u2014 only meaningful when the user can see OmniFocus. Headless agents should not fire this. Use when the user asks 'show me my flagged tasks' or a guided weekly-review prompt wants to navigate the user's UI. Do NOT use to evaluate a perspective's results \u2014 prefer perspective_evaluate, which doesn't touch the user's UI. Pass perspectiveName (case-sensitive, matches OF's UX). Built-in names: Inbox, Projects, Tags, Forecast, Flagged, Review, Nearby, Completed, Changed. Returns { perspectiveName }. Errors: OF_WINDOW_UNAVAILABLE (no front window), OF_NOT_FOUND (no perspective with this name). Side effects: changes the user's visible window state; no data caches invalidated. Example: window_set_perspective({ perspectiveName: "Flagged" })`,fb=z.object({perspectiveName:z.string().min(1).describe("Name of the perspective to activate. Case-sensitive. Built-in or custom perspectives both work.")});async function gb(t,e){let n=await e.adapter.setWindowPerspective(t.perspectiveName);return p(n,e.makeMeta())}function $m(t,e){return t.registerTool("window_set_perspective",{description:qi,inputSchema:fb.shape},async n=>u(await gb(n,e)))}var Vi="Set or clear the front OmniFocus window's focus container (a project or folder). **UI-affecting tool** \u2014 only meaningful when the user can see OmniFocus. Headless agents should not fire this. Use when the user asks 'focus on this project' or a guided flow wants to scope the visible view. Do NOT use to filter task data \u2014 prefer `task_list { projectId }` or `perspective_evaluate` instead, both of which work without touching the user's UI. Pass containerId (a ProjectId or FolderId) to focus, or null to clear focus. Returns { focusContainerIds: string[] } \u2014 single-element array when focused, [] when cleared. Errors: OF_WINDOW_UNAVAILABLE (no front window), OF_NOT_FOUND (containerId is neither a project nor a folder). Side effects: changes the user's visible window state; no data caches invalidated. Example: window_set_focus({ containerId: \"prj123\" }) Example: window_set_focus({ containerId: null })",hb=z.object({containerId:z.union([z.string().min(1),z.null()]).describe("ProjectId or FolderId to focus the front window on, or null to clear focus.")});async function yb(t,e){let n=await e.adapter.setWindowFocus(t.containerId);return p(n,e.makeMeta())}function Wm(t,e){return t.registerTool("window_set_focus",{description:Vi,inputSchema:hb.shape},async n=>u(await yb(n,e)))}var Ki="Open a new OmniFocus window via OmniJS document.newWindow(). **UI-affecting tool** \u2014 only meaningful when OmniFocus is running. Headless agents should not fire this. Use when the user asks 'open a new window' or a flow needs a fresh, unfocused OmniFocus window. Do NOT use to read task or project data \u2014 prefer task_list or project_list instead. Takes no arguments. Returns { perspectiveName: string | null, focusContainerIds: string[] } describing the new window's initial state. Errors: WINDOW_OPEN_FAILED when the window could not be created. Side effects: opens a new OmniFocus window; no data caches invalidated. Example: app_window_new()",kb=z.object({});async function Ib(t,e){let n=await e.adapter.appWindowNew();return p(n,e.makeMeta())}function Hm(t,e){return t.registerTool("app_window_new",{description:Ki,inputSchema:kb.shape},async n=>u(await Ib(n,e)))}var Xi="Open a new tab on the front OmniFocus window via OmniJS document.newTabOnWindow(). **UI-affecting tool** \u2014 only meaningful when OmniFocus has an open window. Headless agents should not fire this. Use when the user asks 'open a new tab' or a flow needs an additional view in the existing window. Do NOT use to open a standalone window \u2014 prefer app_window_new instead. Takes no arguments. Returns { perspectiveName: string | null, focusContainerIds: string[] } describing the new tab's initial state. Errors: WINDOW_UNAVAILABLE when there is no open OmniFocus window; WINDOW_OPEN_FAILED when the tab could not be created. Side effects: opens a new tab in the front OmniFocus window; no data caches invalidated. Example: app_window_new_tab()",vb=z.object({});async function Tb(t,e){let n=await e.adapter.appWindowNewTab();return p(n,e.makeMeta())}function zm(t,e){return t.registerTool("app_window_new_tab",{description:Xi,inputSchema:vb.shape},async n=>u(await Tb(n,e)))}var Gm={app_launch:io,attachment_add:po,attachment_list:lo,attachment_remove:uo,attachment_save_to_path:mo,database_redo:fo,database_undo:go,decision_clear:yo,decision_record:ko,export_opml:vo,export_taskpaper:So,forecast_get:Lo,forecast_get_tag:Bo,forecast_pack:Jo,forecast_set_tag:Wo,folder_create:jo,folder_create_describe:Po,folder_delete:Oo,folder_delete_describe:Do,folder_get:Ro,folder_list:Ao,folder_move:Co,folder_move_describe:Mo,folder_update:No,folder_update_describe:Uo,import_opml:wo,import_taskpaper:bo,internal_status:Zo,note_append:Ho,note_get:Go,note_get_html:Vo,note_set:Ko,note_set_html:Xo,perspective_create:ea,perspective_delete:ta,perspective_evaluate:na,perspective_evaluate_dry_run:oa,perspective_get:aa,perspective_list:sa,perspective_update:ca,plugin_invoke:la,project_batch_complete:ua,project_batch_drop:fa,project_complete:ga,project_complete_describe:ya,project_create:Ta,project_create_describe:Sa,project_delete:ba,project_delete_describe:_a,project_drop:Pa,project_drop_describe:xa,project_get:Da,project_get_many:Ra,project_list:Aa,project_mark_reviewed:es,project_move:Ca,project_move_describe:Ma,project_template_instantiate:Ua,project_template_list:La,project_template_save:Ja,project_update:$a,repetition_from_prose:Ya,project_update_describe:Ha,review_list_due:Za,review_mark_reviewed:Qa,project_set_next_review_date:rs,review_set_interval:ns,run_jxa_script:Ga,run_omnijs_script:Va,search_query:os,sync_status:as,sync_trigger:ss,tag_create:cs,tag_create_describe:ls,tag_delete:ps,tag_delete_describe:ms,tag_get:fs,tag_get_many:hs,tag_get_location:gs,tag_list:ys,tag_move:ks,tag_move_describe:vs,tag_set_allows_next_action:Ts,tag_set_location:ws,tag_set_status:Ss,tag_update:js,task_batch_assign:Os,tag_update_describe:Ps,task_batch_complete:Rs,task_batch_create:As,task_batch_create_describe:Es,task_batch_defer_smart:Fs,task_batch_delete:Us,task_batch_drop:Bs,task_batch_move:Js,task_batch_uncomplete:Ws,task_batch_undrop:zs,task_batch_update:Gs,task_batch_update_describe:Vs,task_complete:Ys,task_complete_describe:Qs,task_create:ti,task_create_describe:ni,task_defer_smart:ri,task_drop:ii,task_drop_describe:di,task_clear_alarms:Ks,task_clear_repetition:Xs,task_delete:oi,task_delete_describe:si,task_duplicate:li,task_extract_from_image:ui,task_extract_from_note:mi,task_find_by_name:fi,task_find_similar:gi,task_get:hi,task_get_many:yi,task_list:vi,task_convert_to_project:ei,task_move:wi,task_move_describe:bi,task_search:Di,task_parse_transport_text:_i,task_reclassify:Oi,task_reorder:xi,task_clear_waiting_on:Bi,task_set_alarms:Ci,task_set_repetition:Ei,task_set_waiting_on:Li,task_uncomplete:Mi,task_undrop:Ni,task_update:Fi,app_window_new:Ki,app_window_new_tab:Xi,window_get_state:Gi,window_set_focus:Vi,window_set_perspective:qi,task_update_describe:Ui,webhook_delete:Wi,webhook_list:Hi,webhook_register:$i,webhook_test:zi};var wb=`Replay dispatcher for clarification-needed responses. When a tool returns { kind: 'clarification-needed' }, present the question and options to the user, then call this tool with the replayToken from that response and the zero-based index of the option the user selected. The server resumes the original tool call with the disambiguation applied and returns the final result envelope. Tokens are single-use and expire after 5 minutes \u2014 call this tool promptly after the user responds. Passing an expired or unknown token returns a NotFound error. Passing a choice index outside the valid range returns an InvalidInput error. Example: clarify({ replayToken: "tok_abc", choice: 0 })`,Sb=z.object({replayToken:z.string().min(1).describe("Opaque token from the clarification-needed envelope's replayToken field."),choice:z.number().int().min(0).describe("Zero-based index of the option the user selected (matches ClarificationOption.index).")});async function bb(t,e){let n=e.replayStore??ve,r=e.makeMeta(),o=n.consume(t.replayToken);return o===void 0?so(new P(`Replay token not found or expired: ${t.replayToken.slice(0,8)}\u2026`,{suggestion:"The token may have expired (5 min TTL) or already been used. Re-invoke the original tool to get a fresh token."}),r):t.choice<0||t.choice>=o.options.length?so(new y(`choice ${t.choice} is out of range; valid indices are 0\u2013${o.options.length-1}.`,{suggestion:`Valid options: ${o.options.map((i,s)=>`${s}: ${i}`).join(", ")}.`}),r):await o.callback(t.choice)}function qm(t,e){return t.registerTool("clarify",{description:wb,inputSchema:Sb.shape},async n=>{let r=await bb(n,e);return u(r)})}var jb=`Delete a saved project template by name from the Templates folder. Returns { deleted: true, templateName } on success. Returns TemplateNotFoundError when no matching template exists \u2014 callers can distinguish 'deleted' from 'never existed'. Side effects: removes the template project; sets meta.syncPending = true. Do NOT use to delete ordinary projects \u2014 call project_delete. Example: { templateName: "Client onboarding" }.`,_b=z.object({templateName:z.string().min(1).describe("Name of the template to delete. Matched case-insensitively within the Templates folder.")}),dr=class extends P{constructor(e){super(`No template named "${e}" was found in the Templates folder.`,{details:{templateName:e}});}};async function Pb(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(c=>c.name.toLowerCase()===r);if(o===void 0)throw new dr(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),i=t.templateName.toLowerCase(),s=a.find(c=>c.name.toLowerCase()!==i?false:Dt(c.note)!==void 0);if(s===void 0)throw new dr(t.templateName);return await e.adapter.deleteProject(s.id),e.cache!==void 0&&J(e.cache,{projectId:s.id}),p({deleted:true,templateName:t.templateName},e.makeMeta({syncPending:true}))}function Km(t,e){return t.registerTool("project_template_delete",{description:jb,inputSchema:_b.shape},async n=>{let r=await Pb(n,e);return u(r)})}var Ab=Zi.join(xb.homedir(),"Library","Application Support","OmniFocus","OmniFocus.ofocus");function Cb(){let t=fileURLToPath(import.meta.url);return Zi.resolve(Zi.dirname(t),"../../bin/omnifocus-watcher")}var lr=class{dbPath;debounceMs;onChange;binaryPath;started=false;debounceTimer=null;windowStartTs=null;windowPaths=[];swiftProcess=null;nodeWatcher=null;constructor(e,n={}){this.onChange=e,this.debounceMs=n.debounceMs??500,this.dbPath=n.dbPath??Ab,this.binaryPath=n.binaryPath!==void 0?n.binaryPath:Cb();}start(){if(this.started)return;if(!dt.existsSync(this.dbPath)){R.warn({event:"database.watcher.path_not_found",dbPath:this.dbPath,message:"OmniFocus database path not found; change notifications will not fire."});return}this.tryStartSwift()||this.startNodeWatcher(),this.started=true;}stop(){this.clearDebounce(),this.swiftProcess!==null&&(this.swiftProcess.kill("SIGTERM"),this.swiftProcess=null),this.nodeWatcher!==null&&(this.nodeWatcher.close(),this.nodeWatcher=null),this.started=false,R.debug({event:"database.watcher.stopped"});}tryStartSwift(){if(this.binaryPath===null)return false;try{dt.accessSync(this.binaryPath,dt.constants.X_OK);}catch{return R.debug({event:"database.watcher.swift_unavailable",binaryPath:this.binaryPath,message:"Swift watcher binary not found or not executable; falling back to fs.watch."}),false}try{return this.swiftProcess=spawn(this.binaryPath,[this.dbPath],{stdio:["ignore","pipe","pipe"]}),this.swiftProcess.stderr?.on("data",n=>{R.debug({event:"database.watcher.swift_stderr",msg:n.toString().trim()});}),createInterface({input:this.swiftProcess.stdout}).on("line",n=>{this.handleSwiftLine(n);}),this.swiftProcess.on("exit",(n,r)=>{this.started&&(R.warn({event:"database.watcher.swift_exited",code:n,signal:r,message:"Swift watcher exited unexpectedly; falling back to fs.watch."}),this.swiftProcess=null,this.startNodeWatcher());}),this.swiftProcess.on("error",n=>{R.warn({event:"database.watcher.swift_error",err:n}),this.swiftProcess=null,this.startNodeWatcher();}),R.debug({event:"database.watcher.swift_started",dbPath:this.dbPath}),!0}catch(e){return R.warn({event:"database.watcher.swift_spawn_failed",err:e}),false}}handleSwiftLine(e){if(e.trim())try{let n=JSON.parse(e);if(n.event!=="change")return;this.scheduleNotify({source:"swift",ts:n.ts,paths:n.paths});}catch(n){R.debug({event:"database.watcher.swift_parse_error",line:e,err:n});}}startNodeWatcher(){if(dt.existsSync(this.dbPath))try{this.nodeWatcher=dt.watch(this.dbPath,{persistent:!1},(e,n)=>{this.scheduleNotify({source:"node",ts:new Date().toISOString()});}),this.nodeWatcher.on("error",e=>{R.warn({event:"database.watcher.node_error",err:e}),this.stop();}),R.debug({event:"database.watcher.node_started",dbPath:this.dbPath});}catch(e){R.warn({event:"database.watcher.node_start_failed",dbPath:this.dbPath,err:e});}}scheduleNotify(e){if(this.windowStartTs===null&&(this.windowStartTs=e.ts),e.paths)for(let n of e.paths)this.windowPaths.includes(n)||this.windowPaths.push(n);this.debounceTimer!==null&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null;let n={detectedAt:this.windowStartTs??new Date().toISOString(),source:e.source,...this.windowPaths.length>0?{changedPaths:[...this.windowPaths]}:{}};this.windowStartTs=null,this.windowPaths=[],R.debug({event:"database.watcher.change_detected",source:n.source,detectedAt:n.detectedAt,pathCount:n.changedPaths?.length??0}),this.onChange(n);},this.debounceMs);}clearDebounce(){this.debounceTimer!==null&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.windowStartTs=null,this.windowPaths=[];}};var Nb=5e3,kn=[1e3,5e3,3e4],Xm=10,Ym=3600*1e3,Fb="X-OmniFocus-Signature",Ub=({url:t,body:e,headers:n,timeoutMs:r})=>new Promise((o,a)=>{let i=new URL(t),s=Mb.request({method:"POST",host:i.host,path:`${i.pathname}${i.search}`,headers:{"Content-Length":Buffer.byteLength(e),...n},timeout:r},c=>{c.on("data",()=>{}),c.on("end",()=>{o({statusCode:c.statusCode??0});});});s.on("error",a),s.on("timeout",()=>{s.destroy(new Error(`request timeout after ${r}ms`));}),s.write(e),s.end();}),pr=class{request;now;sleep;write;circuits=new Map;constructor(e={}){this.request=e.request??Ub,this.now=e.now??Date.now,this.sleep=e.sleep??(n=>new Promise(r=>setTimeout(r,n))),this.write=e.write??(n=>process.stderr.write(n));}async deliver(e,n){let r;try{r=n(e.webhookName);}catch(l){this.write(`[webhook] ${e.kind} for "${e.webhookName}" \u2014 registry lookup threw: ${Zm(l)}; dropping.
|
|
229
|
+
`);return}if(r===void 0){this.write(`[webhook] ${e.kind} for "${e.webhookName}" \u2014 webhook not found in registry; dropping.
|
|
230
|
+
`);return}let o=this.getCircuit(r.name);if(o.openUntil!==null&&this.now()<o.openUntil){this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 circuit open (auto-resume in ${Math.ceil((o.openUntil-this.now())/1e3)}s); dropping.
|
|
231
|
+
`);return}o.openUntil!==null&&(o.openUntil=null,this.write(`[webhook] circuit auto-resumed for "${r.name}".
|
|
232
|
+
`));let a=JSON.stringify(e),i={"Content-Type":"application/json","User-Agent":"omnifocus-mcp-webhook/1"};if(r.secret!==void 0&&r.secret.length>0){let l=createHmac("sha256",r.secret).update(a).digest("hex");i[Fb]=`sha256=${l}`;}let s="",c=0;for(let l=0;l<=kn.length;l++){try{let{statusCode:f}=await this.request({url:r.url,body:a,headers:i,timeoutMs:Nb});if(f>=200&&f<300){o.consecutiveFailures=0;return}c=f,s=`non-2xx status ${f}`;}catch(f){s=Zm(f);}if(l===kn.length)break;let m=kn[l];await this.sleep(m);}o.consecutiveFailures++,o.consecutiveFailures>=Xm?(o.openUntil=this.now()+Ym,this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 failed after ${kn.length+1} attempts (last: ${s}). Circuit OPEN for ${Ym/1e3}s.
|
|
233
|
+
`),o.consecutiveFailures=0):this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 failed after ${kn.length+1} attempts (last: ${s}). ${o.consecutiveFailures}/${Xm} consecutive failures.
|
|
234
|
+
`);}getCircuit(e){let n=this.circuits.get(e);if(n!==void 0)return n;let r={consecutiveFailures:0,openUntil:null};return this.circuits.set(e,r),r}inspectCircuit(e){let n=this.circuits.get(e);return n?{...n}:void 0}};function Zm(t){return (t instanceof Error?t.message:String(t)).slice(0,80)}function ef(t){return {id:String(t.id),name:t.name,completed:!!t.completed,projectId:t.projectId?String(t.projectId):null,tagIds:t.tagIds.map(e=>String(e))}}function tf(t){return {id:String(t.id),name:t.name,status:t.status}}function Lb(t,e){return e===void 0?true:!(e.projectId!==void 0&&t.projectId!==e.projectId||e.tagId!==void 0&&!t.tagIds.includes(e.tagId))}function Bb(t,e){return e===void 0?true:!(e.projectId!==void 0&&t.id!==e.projectId)}function nf(t,e,n,r={}){let o=(r.now??new Date).toISOString(),a=[],i=new Map(t.tasks.map(c=>[c.id,c])),s=new Map(t.projects.map(c=>[c.id,c]));for(let c of e.tasks){let d=i.get(c.id);if(d===void 0){Qm(c,"task-created",n,o,a);continue}!d.completed&&c.completed&&Qm(c,"task-completed",n,o,a);}for(let c of e.projects){let d=s.get(c.id);if(d!==void 0&&d.status!==c.status)for(let l of n)l.trigger.on==="project-status-changed"&&Bb(c,l.trigger.filter)&&a.push({kind:"project-status-changed",webhookName:l.name,projectId:c.id,projectName:c.name,previousStatus:d.status,currentStatus:c.status,occurredAt:o});}return a}function Qm(t,e,n,r,o){for(let a of n){if(a.trigger.on!==e)continue;let i=a.trigger.filter;Lb(t,i)&&o.push({kind:e,webhookName:a.name,taskId:t.id,taskName:t.name,projectId:t.projectId,tagIds:t.tagIds,occurredAt:r});}}var ur=class{registry;dispatcher;now;lastTasks=[];lastProjects=[];hasSeenInitialSnapshot=false;constructor(e){this.registry=e.registry,this.dispatcher=e.dispatcher,this.now=e.now??(()=>new Date);}shouldObserve(){return this.registry.listFull().length>0}async observeSnapshot(e,n){let r=e.map(ef),o=n.map(tf);if(!this.hasSeenInitialSnapshot){this.lastTasks=r,this.lastProjects=o,this.hasSeenInitialSnapshot=true;return}let a=this.registry.listFull();if(a.length===0){this.lastTasks=r,this.lastProjects=o;return}let i=nf({tasks:this.lastTasks,projects:this.lastProjects},{tasks:r,projects:o},a,{now:this.now()});this.lastTasks=r,this.lastProjects=o,await Promise.all(i.map(s=>this.dispatcher.deliver(s,c=>this.lookupForDispatch(c))));}async fireSynthetic(e){let n=this.registry.listFull().find(a=>a.name===e);if(n===void 0)return {error:`webhook not found: ${e}`};let r=this.now().toISOString(),o=n.trigger.on==="project-status-changed"?{kind:"project-status-changed",webhookName:n.name,projectId:"synthetic-project",projectName:"Synthetic test event",previousStatus:"active",currentStatus:"completed",occurredAt:r}:{kind:n.trigger.on,webhookName:n.name,taskId:"synthetic-task",taskName:"Synthetic test event",projectId:null,tagIds:[],occurredAt:r};return await this.dispatcher.deliver(o,a=>this.lookupForDispatch(a)),{delivered:true}}lookupForDispatch(e){return this.registry.listFull().find(n=>n.name===e)}};function $b(){return Zi.join(homedir(),"Library","Application Support","omnifocus-mcp","webhooks.json")}var rf=1,mr=class{filePath;webhooks=[];constructor(e={}){this.filePath=e.filePath??$b(),this.load();}path(){return this.filePath}listFull(){return this.webhooks}list(){return this.webhooks.map(Ji)}has(e){return this.webhooks.some(n=>n.name===e)}register(e){if(this.has(e.name))throw new y(`webhook name already registered: ${e.name}`,{details:{field:"name",value:e.name}});let n={name:e.name,url:e.url,trigger:e.trigger,...e.secret!==void 0&&{secret:e.secret},createdAt:new Date().toISOString()};return this.webhooks=[...this.webhooks,n],this.persist(),Ji(n)}delete(e){let n=this.webhooks.length;this.webhooks=this.webhooks.filter(o=>o.name!==e);let r=this.webhooks.length!==n;return r&&this.persist(),r}load(){if(!dt.existsSync(this.filePath)){this.webhooks=[];return}try{let e=dt.readFileSync(this.filePath,"utf8"),n=JSON.parse(e);if(n.version!==rf||!Array.isArray(n.webhooks)){this.webhooks=[];return}this.webhooks=n.webhooks;}catch{this.webhooks=[];}}persist(){let e=Zi.dirname(this.filePath);dt.existsSync(e)||dt.mkdirSync(e,{recursive:true,mode:448});let n={version:rf,webhooks:this.webhooks},r=JSON.stringify(n,null,2),o=`${this.filePath}.tmp`;dt.writeFileSync(o,r,{mode:384}),dt.renameSync(o,this.filePath);try{dt.chmodSync(this.filePath,384);}catch{}}};var Qi=class{_tool;_failureThreshold;_windowMs;_openDurationMs;_now;_state="closed";_failures=[];_openedAt=null;_probeInFlight=false;constructor(e,n={}){this._tool=e,this._failureThreshold=n.failureThreshold??3,this._windowMs=n.windowMs??6e4,this._openDurationMs=n.openDurationMs??6e4,this._now=n.now??(()=>Date.now());}get state(){return this._maybeTransitionToHalfOpen(),this._state}async call(e){if(this._maybeTransitionToHalfOpen(),this._state==="open"){let n=this._retryAfterMs();throw new Ht(`Circuit for tool "${this._tool}" is open after repeated failures.`,{details:{tool:this._tool,retryAfterMs:n}})}if(this._state==="half_open"){if(this._probeInFlight)throw new Ht(`Circuit for tool "${this._tool}" is half-open; probe already in flight.`,{details:{tool:this._tool,retryAfterMs:0}});this._probeInFlight=true;}try{let n=await e();return this._onSuccess(),n}catch(n){throw this._onFailure(),n}finally{this._state==="half_open"&&(this._probeInFlight=false);}}_maybeTransitionToHalfOpen(){if(this._state==="open"&&this._openedAt!==null){let e=this._now()-this._openedAt;e>=this._openDurationMs&&(this._state="half_open",this._probeInFlight=false,R.info({event:"circuit.half_open",tool:this._tool,elapsed:e},"circuit moved to half-open; probe allowed"));}}_onSuccess(){this._state==="half_open"?this._close("probe succeeded"):this._failures=[];}_onFailure(){let e=this._now();if(this._state==="half_open"){this._open(e,"probe failed");return}this._failures=this._failures.filter(n=>e-n<this._windowMs),this._failures.push(e),this._failures.length>=this._failureThreshold&&this._open(e,`${this._failures.length} failures within ${this._windowMs}ms`);}_open(e,n){this._state="open",this._openedAt=e,this._probeInFlight=false,R.warn({event:"circuit.opened",tool:this._tool,reason:n,openDurationMs:this._openDurationMs},"circuit opened \u2014 fast-failing calls");}_close(e){this._state="closed",this._openedAt=null,this._failures=[],this._probeInFlight=false,R.info({event:"circuit.closed",tool:this._tool,reason:e},"circuit closed \u2014 resuming normal operation");}_retryAfterMs(){if(this._openedAt===null)return this._openDurationMs;let e=this._now()-this._openedAt;return Math.max(0,this._openDurationMs-e)}},ec=class{_breakers=new Map;_defaults;constructor(e={}){this._defaults=e;}get(e){let n=this._breakers.get(e);return n||(n=new Qi(e,this._defaults),this._breakers.set(e,n)),n}clear(){this._breakers.clear();}get size(){return this._breakers.size}snapshot(){return Array.from(this._breakers.entries()).map(([e,n])=>({name:e,state:n.state}))}},tc=new ec;function Wb(t){return t instanceof Error?{errorCode:t.code??"OF_UNKNOWN",message:t.message}:{errorCode:"OF_UNKNOWN",message:String(t)}}async function Me(t,e){let n=[],r=[];for(let o=0;o<t.length;o++){let a=t[o];if(a!==void 0)try{let i=await e(a,o);n.push({index:o,value:i});}catch(i){r.push({index:o,...Wb(i)});}}return {succeeded:n,failed:r}}function X(t){return t.toISOString()}var fr=class{now;idCounter;tasks=new Map;projects=new Map;tags=new Map;folders=new Map;attachments=new Map;lastSyncAt=null;forecastTagId=null;constructor(e={}){this.now=e.now??(()=>new Date),this.idCounter=e.idSeed??0;}nextId(e,n){return this.idCounter+=1,n.of(`${e}_${this.idCounter.toString().padStart(6,"0")}`)}async listTasks(e){return Array.from(this.tasks.values()).filter(n=>this.matchesTask(n,e)).map(n=>this.withAlarms(n))}async getTask(e){let n=this.tasks.get(e);if(n===void 0)throw new P(`Task not found: ${e}`,{details:{resource:"task",id:e}});return this.withAlarms(n)}async getTasksMany(e){return e.map(n=>{let r=this.tasks.get(n);return r===void 0?null:this.withAlarms(r)})}withAlarms(e){let n=this.alarmsByTaskId.get(e.id);return n===void 0||n.length===0?e:{...e,notifications:n.map(r=>({...r}))}}async createTask(e){if(e.name.trim()==="")throw new y("Task name must be non-empty",{details:{field:"name"}});if(e.projectId!==void 0&&e.parentId!==void 0)throw new y("createTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(e.projectId!==void 0&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`,{details:{resource:"project",id:e.projectId}});if(e.parentId!==void 0&&!this.tasks.has(e.parentId))throw new P(`Parent task not found: ${e.parentId}`,{details:{resource:"task",id:e.parentId}});for(let a of e.tagIds??[])if(!this.tags.has(a))throw new P(`Tag not found: ${a}`,{details:{resource:"tag",id:a}});let n=this.nextId("task",h),r=X(this.now()),o={id:n,name:e.name,note:e.note??null,noteHtml:e.noteHtml??null,projectId:e.projectId??null,parentId:e.parentId??null,tagIds:[...e.tagIds??[]],deferDate:e.deferDate??null,...e.deferDateFloating?{deferDateFloating:true}:{},dueDate:e.dueDate??null,...e.dueDateFloating?{dueDateFloating:true}:{},estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,completed:false,completedAt:null,dropped:false,droppedAt:null,available:true,blocked:false,sequential:e.sequential??false,completedByChildren:e.completedByChildren??false,repetition:null,createdAt:r,modifiedAt:r};return this.tasks.set(n,o),this.bumpProjectTaskCount(o.projectId,1),n}async updateTask(e,n){let r=await this.getTask(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Task name must be non-empty",{details:{field:"name"}});if(n.tagIds!==void 0){for(let a of n.tagIds)if(!this.tags.has(a))throw new P(`Tag not found: ${a}`,{details:{resource:"tag",id:a}})}let o={...r,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.noteHtml!==void 0?{noteHtml:n.noteHtml}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.deferDateFloating===true?{deferDateFloating:true}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.dueDateFloating===true?{dueDateFloating:true}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.tagIds!==void 0?{tagIds:[...n.tagIds]}:{},...n.sequential!==void 0?{sequential:n.sequential}:{},...n.completedByChildren!==void 0?{completedByChildren:n.completedByChildren}:{},...n.repetition!==void 0?{repetition:n.repetition}:{},modifiedAt:X(this.now())};n.deferDateFloating===false&&delete o.deferDateFloating,n.dueDateFloating===false&&delete o.dueDateFloating,this.tasks.set(e,o);}async completeTask(e,n){return this.applyTaskCompletion(e,true,n)}async uncompleteTask(e){return this.applyTaskCompletion(e,false)}async dropTask(e,n){return this.applyTaskDropState(e,true,n)}async undropTask(e){return this.applyTaskDropState(e,false)}async applyTaskCompletion(e,n,r){let o=await this.getTask(e);if(!n&&!o.completed)return;let a=n?X(r??this.now()):null;this.tasks.set(e,{...o,completed:n,completedAt:a,modifiedAt:a??X(this.now())}),this.bumpProjectCompletedCount(o.projectId,n?1:-1);}async applyTaskDropState(e,n,r){let o=await this.getTask(e);if(!n&&!o.dropped)return;let a=n?X(r??this.now()):null;this.tasks.set(e,{...o,dropped:n,droppedAt:a,modifiedAt:a??X(this.now())});}async batchCreateTasks(e){return Me(e,n=>this.createTask(n))}async batchUpdateTasks(e){return Me(e,async({id:n,patch:r})=>(await this.updateTask(n,r),n))}async batchCompleteTasks(e){return Me(e,async({id:n,at:r})=>(await this.completeTask(n,r),n))}async batchUncompleteTasks(e){return Me(e,async({id:n})=>(await this.uncompleteTask(n),n))}async batchDeleteTasks(e){return Me(e,async({id:n})=>(await this.deleteTask(n),n))}async batchDropTasks(e){return Me(e,async({id:n})=>(await this.dropTask(n),n))}async batchUndropTasks(e){return Me(e,async({id:n})=>(await this.undropTask(n),n))}async deleteTask(e){let n=await this.getTask(e);this.tasks.delete(e),this.adjustProjectCountsForTask(n.projectId,n,-1);}async moveTask(e,n){let r=await this.getTask(e);if(n.projectId!==void 0&&n.parentId!==void 0)throw new y("moveTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(n.projectId!==void 0&&!this.projects.has(n.projectId))throw new P(`Project not found: ${n.projectId}`,{details:{resource:"project",id:n.projectId}});if(n.parentId!==void 0&&!this.tasks.has(n.parentId))throw new P(`Parent task not found: ${n.parentId}`,{details:{resource:"task",id:n.parentId}});this.adjustProjectCountsForTask(r.projectId,r,-1);let o=n.projectId??null;this.tasks.set(e,{...r,projectId:o,parentId:n.parentId??null,modifiedAt:X(this.now())}),this.adjustProjectCountsForTask(o,r,1);}async convertTaskToProject(e,n){let r=await this.getTask(e),o=k.of(e),a=X(this.now());return this.projects.set(o,{id:o,name:r.name,note:r.note??null,noteHtml:null,folderId:n.folderId??null,tagIds:r.tagIds??[],status:"active",completionCriterion:"parallel",flagged:r.flagged??false,deferDate:r.deferDate??null,dueDate:r.dueDate??null,estimatedMinutes:null,reviewIntervalDays:null,nextReviewDate:null,lastReviewDate:null,completed:false,completedAt:null,dropped:false,droppedAt:null,taskCount:0,completedTaskCount:0,createdAt:a,modifiedAt:a}),this.tasks.delete(e),o}async batchMoveTasks(e){return Me(e,async({id:n,destination:r})=>(await this.moveTask(n,r),n))}async duplicateTask(e,n){let r=await this.getTask(e),o,a;if(n.destination===void 0)o=r.projectId,a=r.parentId;else {let l=n.destination;if(("projectId"in l?1:0)+("parentId"in l?1:0)+("toInbox"in l&&l.toInbox===true?1:0)!==1)throw new y("duplicateTask: destination must specify exactly one of projectId, parentId, or toInbox",{details:{field:"destination"}});if("projectId"in l){if(!this.projects.has(l.projectId))throw new P(`Project not found: ${l.projectId}`,{details:{resource:"project",id:l.projectId}});o=l.projectId,a=null;}else if("parentId"in l){let f=this.tasks.get(l.parentId);if(f===void 0)throw new P(`Parent task not found: ${l.parentId}`,{details:{resource:"task",id:l.parentId}});o=f.projectId,a=l.parentId;}else o=null,a=null;}let i=l=>Array.from(this.tasks.values()).filter(m=>m.parentId===l),s=(l,m,f)=>{let g=this.nextId("task",h),v=X(this.now()),I={id:g,name:l.name,note:l.note,noteHtml:l.noteHtml,projectId:m,parentId:f,tagIds:[...l.tagIds],deferDate:l.deferDate,dueDate:l.dueDate,estimatedMinutes:l.estimatedMinutes,flagged:l.flagged,completed:false,completedAt:null,dropped:false,droppedAt:null,available:true,blocked:false,sequential:l.sequential,completedByChildren:l.completedByChildren,repetition:l.repetition,createdAt:v,modifiedAt:v};return this.tasks.set(g,I),this.bumpProjectTaskCount(m,1),g},c=s(r,o,a),d=0;if(n.recursive){let l=(m,f,g)=>{for(let v of i(m)){let I=s(v,g,f);d+=1,l(v.id,I,g);}};l(r.id,c,o);}return {newId:c,descendantCount:d}}async reorderTask(e,n){let r=await this.getTask(e),{newProjectId:o,newParentId:a,anchorMode:i,anchorId:s}=this.resolveReorderDestination(e,r,n);(o!==r.projectId||a!==r.parentId)&&(this.adjustProjectCountsForTask(r.projectId,r,-1),this.adjustProjectCountsForTask(o,r,1));let d={...r,projectId:o,parentId:a,modifiedAt:X(this.now())},l=[];for(let[g,v]of this.tasks)g!==e&&l.push([g,v]);let m=g=>g.projectId===o&&g.parentId===a,f;if(i==="start")f=l.findIndex(([,g])=>m(g)),f===-1&&(f=l.length);else if(i==="end"){let g=-1;l.forEach(([,v],I)=>{m(v)&&(g=I);}),f=g===-1?l.length:g+1;}else {let g=l.findIndex(([v])=>v===s);f=i==="before"?g:g+1;}this.tasks.clear(),l.forEach(([g,v],I)=>{I===f&&this.tasks.set(e,d),this.tasks.set(g,v);}),f>=l.length&&this.tasks.set(e,d);}resolveReorderDestination(e,n,r){if("before"in r||"after"in r){let c="before"in r?r.before:r.after,d=this.tasks.get(c);if(d===void 0)throw new P(`Reference task not found: ${c}`,{details:{resource:"task",id:c}});if(c===e)throw new y("reorderTask: reference must differ from the task id",{details:{field:"position"}});if(d.projectId!==n.projectId||d.parentId!==n.parentId)throw new y("reorderTask: reference task must share parent with the task being moved",{details:{field:"position"}});return {newProjectId:n.projectId,newParentId:n.parentId,anchorMode:"before"in r?"before":"after",anchorId:c}}let{at:o,in:a}=r,i,s;if("projectId"in a){if(!this.projects.has(a.projectId))throw new P(`Project not found: ${a.projectId}`,{details:{resource:"project",id:a.projectId}});i=a.projectId,s=null;}else if("parentId"in a){if(!this.tasks.has(a.parentId))throw new P(`Parent task not found: ${a.parentId}`,{details:{resource:"task",id:a.parentId}});if(a.parentId===e)throw new y("reorderTask: cannot reparent a task under itself",{details:{field:"position.in.parentId"}});i=this.tasks.get(a.parentId)?.projectId??null,s=a.parentId;}else i=null,s=null;return {newProjectId:i,newParentId:s,anchorMode:o,anchorId:null}}async listProjects(e={}){return Array.from(this.projects.values()).filter(n=>!(e.folderId!==void 0&&n.folderId!==e.folderId||e.status!==void 0&&n.status!==e.status))}async getProject(e){let n=this.projects.get(e);if(n===void 0)throw new P(`Project not found: ${e}`,{details:{resource:"project",id:e}});return n}async getProjectsMany(e){return e.map(n=>this.projects.get(n)??null)}async createProject(e){if(e.name.trim()==="")throw new y("Project name must be non-empty",{details:{field:"name"}});if(e.folderId!==void 0&&!this.folders.has(e.folderId))throw new P(`Folder not found: ${e.folderId}`,{details:{resource:"folder",id:e.folderId}});let n=this.nextId("proj",k),r=X(this.now()),o={id:n,name:e.name,note:e.note??null,noteHtml:e.noteHtml??null,folderId:e.folderId??null,tagIds:[...e.tagIds??[]],status:e.status??"active",completionCriterion:e.completionCriterion??"parallel",deferDate:e.deferDate??null,...e.deferDateFloating?{deferDateFloating:true}:{},dueDate:e.dueDate??null,...e.dueDateFloating?{dueDateFloating:true}:{},estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,reviewIntervalDays:e.reviewIntervalDays??null,nextReviewDate:null,lastReviewDate:null,completed:false,completedAt:null,dropped:false,droppedAt:null,taskCount:0,completedTaskCount:0,createdAt:r,modifiedAt:r};return this.projects.set(n,o),this.bumpFolderProjectCount(o.folderId,1),n}async updateProject(e,n){let r=await this.getProject(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Project name must be non-empty",{details:{field:"name"}});let o={...r,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.noteHtml!==void 0?{noteHtml:n.noteHtml}:{},...n.status!==void 0?{status:n.status}:{},...n.completionCriterion!==void 0?{completionCriterion:n.completionCriterion}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.deferDateFloating===true?{deferDateFloating:true}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.dueDateFloating===true?{dueDateFloating:true}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.tagIds!==void 0?{tagIds:[...n.tagIds]}:{},...n.reviewIntervalDays!==void 0?{reviewIntervalDays:n.reviewIntervalDays}:{},modifiedAt:X(this.now())};n.deferDateFloating===false&&delete o.deferDateFloating,n.dueDateFloating===false&&delete o.dueDateFloating,this.projects.set(e,o);}async completeProject(e,n){let r=await this.getProject(e),o=X(n??this.now());this.projects.set(e,{...r,status:"done",completed:true,completedAt:o,modifiedAt:o});}async dropProject(e,n){let r=await this.getProject(e),o=X(n??this.now());this.projects.set(e,{...r,status:"dropped",dropped:true,droppedAt:o,modifiedAt:o});}async batchCompleteProjects(e){return Me(e,async({id:n})=>(await this.completeProject(n),n))}async batchDropProjects(e){return Me(e,async({id:n})=>(await this.dropProject(n),n))}async moveProject(e,n){let r=await this.getProject(e);if(n.folderId!==null&&!this.folders.has(n.folderId))throw new P(`Folder not found: ${n.folderId}`,{details:{resource:"folder",id:n.folderId}});this.bumpFolderProjectCount(r.folderId,-1),this.projects.set(e,{...r,folderId:n.folderId,modifiedAt:X(this.now())}),this.bumpFolderProjectCount(n.folderId,1);}async deleteProject(e){let n=await this.getProject(e);for(let[r,o]of this.tasks)o.projectId===e&&this.tasks.set(r,{...o,projectId:null});this.projects.delete(e),this.bumpFolderProjectCount(n.folderId,-1);}async markProjectReviewed(e){let n=await this.getProject(e),r=this.now(),o=X(r),a=null;if(n.reviewIntervalDays!==null){let i=new Date(r);i.setUTCDate(i.getUTCDate()+n.reviewIntervalDays),a=X(i);}this.projects.set(e,{...n,lastReviewDate:o,nextReviewDate:a,modifiedAt:o});}async listProjectsDueForReview(){let e=new Date;return e.setUTCHours(23,59,59,999),[...this.projects.values()].filter(n=>n.nextReviewDate===null||new Date(n.nextReviewDate)<=e).sort((n,r)=>n.nextReviewDate===null&&r.nextReviewDate===null?0:n.nextReviewDate===null?-1:r.nextReviewDate===null?1:n.nextReviewDate.localeCompare(r.nextReviewDate))}async setProjectReviewInterval(e,n){let r=await this.getProject(e);this.projects.set(e,{...r,reviewIntervalDays:n});}async setProjectNextReviewDate(e,n){let r=await this.getProject(e);this.projects.set(e,{...r,nextReviewDate:n});}async listTags(e={}){return Array.from(this.tags.values()).filter(n=>!(e.parentId!==void 0&&n.parentId!==e.parentId||e.status!==void 0&&n.status!==e.status))}async getTag(e){let n=this.tags.get(e);if(n===void 0)throw new P(`Tag not found: ${e}`,{details:{resource:"tag",id:e}});return n}async getTagsMany(e){return e.map(n=>this.tags.get(n)??null)}async createTag(e){if(e.name.trim()==="")throw new y("Tag name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.tags.has(e.parentId))throw new P(`Parent tag not found: ${e.parentId}`,{details:{resource:"tag",id:e.parentId}});let n=this.nextId("tag",S),r=X(this.now()),o={id:n,name:e.name,parentId:e.parentId??null,status:e.status??"active",location:null,allowsNextAction:e.allowsNextAction??true,taskCount:0,createdAt:r,modifiedAt:r};return this.tags.set(n,o),n}async updateTag(e,n){let r=await this.getTag(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Tag name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.tags.has(n.parentId))throw new P(`Parent tag not found: ${n.parentId}`,{details:{resource:"tag",id:n.parentId}});this.tags.set(e,{...r,...n.name!==void 0?{name:n.name}:{},...n.parentId!==void 0?{parentId:n.parentId}:{},...n.status!==void 0?{status:n.status}:{},...n.allowsNextAction!==void 0?{allowsNextAction:n.allowsNextAction}:{},...n.location!==void 0?{location:n.location}:{},modifiedAt:X(this.now())});}async deleteTag(e){await this.getTag(e);for(let[n,r]of this.tasks)r.tagIds.includes(e)&&this.tasks.set(n,{...r,tagIds:r.tagIds.filter(o=>o!==e)});this.tags.delete(e);}async listFolders(e={}){return Array.from(this.folders.values()).filter(n=>!(e.parentId!==void 0&&n.parentId!==e.parentId))}async getFolder(e){let n=this.folders.get(e);if(n===void 0)throw new P(`Folder not found: ${e}`,{details:{resource:"folder",id:e}});return n}async createFolder(e){if(e.name.trim()==="")throw new y("Folder name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.folders.has(e.parentId))throw new P(`Parent folder not found: ${e.parentId}`,{details:{resource:"folder",id:e.parentId}});let n=this.nextId("fold",W),r=X(this.now()),o={id:n,name:e.name,parentId:e.parentId??null,projectCount:0,subfolderCount:0,createdAt:r,modifiedAt:r};return this.folders.set(n,o),o.parentId!==null&&this.bumpFolderSubfolderCount(o.parentId,1),n}async updateFolder(e,n){let r=await this.getFolder(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Folder name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.folders.has(n.parentId))throw new P(`Parent folder not found: ${n.parentId}`,{details:{resource:"folder",id:n.parentId}});n.parentId!==void 0&&n.parentId!==r.parentId&&(r.parentId!==null&&this.bumpFolderSubfolderCount(r.parentId,-1),n.parentId!==null&&this.bumpFolderSubfolderCount(n.parentId,1)),this.folders.set(e,{...r,...n.name!==void 0?{name:n.name}:{},...n.parentId!==void 0?{parentId:n.parentId}:{},modifiedAt:X(this.now())});}async deleteFolder(e){let n=await this.getFolder(e);if(n.projectCount>0||n.subfolderCount>0)throw new y(`Folder is not empty (projects=${n.projectCount}, subfolders=${n.subfolderCount})`,{details:{resource:"folder",id:e}});this.folders.delete(e),n.parentId!==null&&this.bumpFolderSubfolderCount(n.parentId,-1);}async searchTasks(e){let n=e.q!==void 0?e.q.toLowerCase():null,r=e.scope??"all";return Array.from(this.tasks.values()).filter(o=>{if(n!==null){let i=r!=="note"&&o.name.toLowerCase().includes(n),s=r!=="name"&&(o.note??"").toLowerCase().includes(n);if(!i&&!s)return false}if(e.projectId!==void 0&&o.projectId!==e.projectId)return false;if(e.tagIds!==void 0&&e.tagIds.length>0){let i=new Set(o.tagIds);if(!e.tagIds.every(s=>i.has(s)))return false}if(e.available!==void 0&&o.available!==e.available)return false;if(e.dueBefore!==void 0||e.dueAfter!==void 0){if(o.dueDate===null)return false;let i=new Date(o.dueDate);if(e.dueBefore!==void 0&&i>=new Date(e.dueBefore)||e.dueAfter!==void 0&&i<=new Date(e.dueAfter))return false}if(e.flagged!==void 0&&o.flagged!==e.flagged)return false;let a=o.completedAt!==null;return !(e.completed==="only"&&!a||e.completed==="exclude"&&a)})}async syncTrigger(){return this.lastSyncAt=X(this.now()),{lastSyncAt:this.lastSyncAt,inFlight:false}}async getLastSync(){return {lastSyncAt:this.lastSyncAt,inFlight:false}}alarmsByTaskId=new Map;async setTaskAlarms(e,n){if(!this.tasks.has(e))throw new P(`Task not found: ${e}`,{details:{resource:"task",id:e}});if(n.length===0){this.alarmsByTaskId.delete(e);return}this.alarmsByTaskId.set(e,n.map(r=>({...r})));}async clearTaskAlarms(e){if(!this.tasks.has(e))throw new P(`Task not found: ${e}`,{details:{resource:"task",id:e}});this.alarmsByTaskId.delete(e);}undoStackDepth=0;redoStackDepth=0;async undoLastMutation(){return this.undoStackDepth===0?{undid:false}:(this.undoStackDepth-=1,this.redoStackDepth+=1,{undid:true})}async redoLastMutation(){return this.redoStackDepth===0?{redid:false}:(this.redoStackDepth-=1,this.undoStackDepth+=1,{redid:true})}async listPerspectives(){let e={inbox:"Inbox",projects:"Projects",tags:"Tags",forecast:"Forecast",flagged:"Flagged",nearby:"Nearby",review:"Review"};return st.map(n=>({id:n,name:e[n]??n,kind:"builtin",requiresPro:false,icon:null}))}customPerspectives=new Map;seedCustomPerspective(e,n){this.customPerspectives.set(e,[...n]);}async evaluateCustomPerspective(e){let n=this.customPerspectives.get(e);if(n===void 0)throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});let r=[];for(let o of n){let a=this.tasks.get(o);a!==void 0&&r.push(a);}return r}dryRunRulesResults=new Map;seedPerspectiveRulesEvaluation(e,n,r){let o=JSON.stringify({aggregation:r??null,rules:e});this.dryRunRulesResults.set(o,[...n]);}async evaluatePerspectiveRules(e,n){let r=JSON.stringify({aggregation:n??null,rules:e}),o=this.dryRunRulesResults.get(r)??[],a=[];for(let i of o){let s=this.tasks.get(i);s!==void 0&&a.push(s);}return a}customPerspectiveDetails=new Map;seedCustomPerspectiveDetail(e){this.customPerspectiveDetails.set(e.id,e);}async getCustomPerspective(e){let n=this.customPerspectiveDetails.get(e);if(n===void 0)throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});return n}async deleteCustomPerspective(e){if(!(this.customPerspectives.delete(e)||this.customPerspectiveDetails.delete(e)))throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}})}async createCustomPerspective(e){if(e.name.length===0)throw new y("name is required and must be non-empty",{details:{field:"name"}});for(let o of this.customPerspectiveDetails.values())if(o.name===e.name)throw new y(`Duplicate perspective name: ${e.name}`,{details:{field:"name",existingId:o.id}});this.idCounter+=1;let n=`persp_${this.idCounter.toString().padStart(6,"0")}`,r={id:n,name:e.name,aggregation:e.aggregation??"all",rules:e.rules??[],iconColor:e.iconColor??null};return this.customPerspectiveDetails.set(n,r),this.customPerspectives.set(n,[]),n}async updateCustomPerspective(e,n){let r=this.customPerspectiveDetails.get(e);if(r===void 0)throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});if(n.name!==void 0){if(n.name.length===0)throw new y("name must be non-empty",{details:{field:"name"}});for(let a of this.customPerspectiveDetails.values())if(a.id!==e&&a.name===n.name)throw new y(`Duplicate perspective name: ${n.name}`,{details:{field:"name",existingId:a.id}})}let o={id:r.id,name:n.name??r.name,aggregation:n.aggregation??r.aggregation,rules:n.rules??r.rules,iconColor:n.iconColor===void 0?r.iconColor:n.iconColor===null?null:n.iconColor};this.customPerspectiveDetails.set(e,o);}async evaluatePerspective(e){let n=Array.from(this.tasks.values());if(e==="review"||e==="nearby")return [];if(e==="inbox")return n.filter(r=>r.projectId===null&&!r.completed&&!r.dropped);if(e==="flagged")return n.filter(r=>r.flagged&&!r.completed&&!r.dropped);if(e==="forecast"){let r=new Date;return r.setHours(23,59,59,999),n.filter(o=>o.dueDate!==null&&new Date(o.dueDate)<=r&&!o.completed&&!o.dropped)}return e==="projects"?n.filter(r=>r.projectId!==null&&!r.completed&&!r.dropped):e==="tags"?n.filter(r=>r.tagIds.length>0&&!r.completed&&!r.dropped):[]}async getForecast(e){let n=Array.from(this.tasks.values()).filter(f=>!f.completed&&!f.dropped),{from:r,to:o,includeOverdue:a=true,includeDeferred:i=true,includeFlagged:s=true}=e,c=a?n.filter(f=>f.dueDate!==null&&f.dueDate<r):[],d=n.filter(f=>f.dueDate!==null&&f.dueDate>=r&&f.dueDate<=o),l=i?n.filter(f=>f.deferDate!==null&&f.deferDate>=r&&f.deferDate<=o):[],m=s?n.filter(f=>f.flagged):[];return {overdue:c,dueToday:d,deferredToday:l,flagged:m}}async getForecastTag(){return {tagId:this.forecastTagId}}async setForecastTag(e){if(e!==null&&!this.tags.has(e))throw new P(`Tag not found: ${e}`);return this.forecastTagId=e,{tagId:e}}ownerKey(e){return e.taskId?`task:${e.taskId}`:`project:${e.projectId}`}async listAttachments(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);return Array.from(this.attachments.get(n)?.values()??[])}async addAttachment(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);this.attachments.has(n)||this.attachments.set(n,new Map);let r=this.nextId("att",Oe),o=e.filePath.split("/").pop()??"attachment",a={id:r,name:o,mimeType:null,sizeBytes:null,addedAt:X(this.now()),kind:"embedded"};return this.attachments.get(n).set(r,a),r}async removeAttachment(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e),r=this.attachments.get(n);if(!r?.has(e.attachmentId))throw new P(`Attachment not found: ${e.attachmentId}`);r.delete(e.attachmentId);}async saveAttachmentToPath(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);if(!this.attachments.get(n)?.has(e.attachmentId))throw new P(`Attachment not found: ${e.attachmentId}`);return {saved:true,path:e.destPath,sizeBytes:0}}async appLaunch(){return {launched:false,alreadyRunning:true}}windowPerspectiveName=null;windowFocusContainerIds=[];async getWindowState(){return {perspectiveName:this.windowPerspectiveName,focusContainerIds:[...this.windowFocusContainerIds]}}async setWindowPerspective(e){return this.windowPerspectiveName=e,{perspectiveName:e}}async setWindowFocus(e){if(e===null)return this.windowFocusContainerIds=[],{focusContainerIds:[]};let n=false;for(let r of this.projects.keys())if(String(r)===e){n=true;break}if(!n){for(let r of this.folders.keys())if(String(r)===e){n=true;break}}if(!n)throw new P(`Container not found (project or folder): ${e}`);return this.windowFocusContainerIds=[e],{focusContainerIds:[e]}}async appWindowNew(){return {perspectiveName:null,focusContainerIds:[]}}async appWindowNewTab(){return {perspectiveName:null,focusContainerIds:[]}}async pluginInvoke(e){throw new P("pluginInvoke is not supported by InMemoryAdapter \u2014 use a real OmniJsTransport for integration tests")}async getChangesSince(e){let n=new Date(e).getTime(),r=[...this.tasks.values()].filter(a=>new Date(a.modifiedAt).getTime()>=n).map(a=>a.id),o=[...this.projects.values()].filter(a=>new Date(a.modifiedAt).getTime()>=n).map(a=>a.id);return {taskIds:r,projectIds:o}}matchesTask(e,n){return !(n.projectId!==void 0&&e.projectId!==n.projectId||n.parentId!==void 0&&e.parentId!==n.parentId||n.tagId!==void 0&&!e.tagIds.includes(n.tagId)||n.flagged!==void 0&&e.flagged!==n.flagged||n.completed!==void 0&&e.completed!==n.completed||n.available!==void 0&&e.available!==n.available||n.blocked!==void 0&&e.blocked!==n.blocked||n.completedSince!==void 0&&(e.completedAt===null||e.completedAt<n.completedSince)||n.dueBefore!==void 0&&(e.dueDate===null||e.dueDate>=n.dueBefore)||n.dueAfter!==void 0&&(e.dueDate===null||e.dueDate<=n.dueAfter)||n.deferredBefore!==void 0&&(e.deferDate===null||e.deferDate>=n.deferredBefore)||n.deferredAfter!==void 0&&(e.deferDate===null||e.deferDate<=n.deferredAfter)||n.inbox===true&&e.projectId!==null)}bumpProjectTaskCount(e,n){if(e===null)return;let r=this.projects.get(e);r!==void 0&&this.projects.set(e,{...r,taskCount:Math.max(0,r.taskCount+n)});}adjustProjectCountsForTask(e,n,r){this.bumpProjectTaskCount(e,r),n.completed&&this.bumpProjectCompletedCount(e,r);}bumpProjectCompletedCount(e,n){if(e===null)return;let r=this.projects.get(e);r!==void 0&&this.projects.set(e,{...r,completedTaskCount:Math.max(0,r.completedTaskCount+n)});}bumpFolderProjectCount(e,n){if(e===null)return;let r=this.folders.get(e);r!==void 0&&this.folders.set(e,{...r,projectCount:Math.max(0,r.projectCount+n)});}bumpFolderSubfolderCount(e,n){let r=this.folders.get(e);r!==void 0&&this.folders.set(e,{...r,subfolderCount:Math.max(0,r.subfolderCount+n)});}};function ne(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"&&t.error!==null}function Pe(t,e){return {succeeded:t.succeeded.map(n=>({index:n.index,value:e(n.value)})),failed:t.failed}}var af=`/**
|
|
71
235
|
* app_launch.js \u2014 launch OmniFocus explicitly (never automatic).
|
|
72
236
|
*
|
|
73
237
|
* Per SPEC resolved-decisions: automatic launch is out of scope \u2014 agents must
|
|
@@ -95,7 +259,7 @@ function run(_argv) {
|
|
|
95
259
|
|
|
96
260
|
return JSON.stringify({ launched: !alreadyRunning, alreadyRunning });
|
|
97
261
|
}
|
|
98
|
-
`;var
|
|
262
|
+
`;var sf=`/**
|
|
99
263
|
* JXA: add an attachment to a task or project from a local file path.
|
|
100
264
|
*
|
|
101
265
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string, filePath: string }
|
|
@@ -143,7 +307,7 @@ function run(argv) {
|
|
|
143
307
|
const newAtt = atts[atts.length - 1];
|
|
144
308
|
return JSON.stringify({ id: newAtt.id() });
|
|
145
309
|
}
|
|
146
|
-
`;var
|
|
310
|
+
`;var cf=`/**
|
|
147
311
|
* JXA: list all attachments on a task or project.
|
|
148
312
|
*
|
|
149
313
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string }
|
|
@@ -229,7 +393,7 @@ function run(argv) {
|
|
|
229
393
|
|
|
230
394
|
return JSON.stringify({ attachments: result });
|
|
231
395
|
}
|
|
232
|
-
`;var
|
|
396
|
+
`;var df=`/**
|
|
233
397
|
* JXA: remove an attachment by ID from a task or project.
|
|
234
398
|
*
|
|
235
399
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string }
|
|
@@ -281,7 +445,7 @@ function run(argv) {
|
|
|
281
445
|
|
|
282
446
|
return JSON.stringify({});
|
|
283
447
|
}
|
|
284
|
-
`;var
|
|
448
|
+
`;var lf=`/**
|
|
285
449
|
* JXA: copy an attachment's content to a local file path.
|
|
286
450
|
*
|
|
287
451
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string, destPath: string }
|
|
@@ -380,7 +544,7 @@ function run(argv) {
|
|
|
380
544
|
|
|
381
545
|
return JSON.stringify({ saved: true, path: destPath, sizeBytes: sizeBytes });
|
|
382
546
|
}
|
|
383
|
-
`;var
|
|
547
|
+
`;var pf=`/**
|
|
384
548
|
* JXA: return task and project IDs modified since a given timestamp.
|
|
385
549
|
*
|
|
386
550
|
* This script powers the richer change-semantics layer: when the
|
|
@@ -454,7 +618,7 @@ function run(argv) {
|
|
|
454
618
|
|
|
455
619
|
return JSON.stringify({ tasks, projects });
|
|
456
620
|
}
|
|
457
|
-
`;var
|
|
621
|
+
`;var uf=`/**
|
|
458
622
|
* JXA: create a folder, optionally under a parent folder.
|
|
459
623
|
*
|
|
460
624
|
* Args (argv[0] JSON): { name: string, parentId?: string }
|
|
@@ -519,7 +683,7 @@ function run(argv) {
|
|
|
519
683
|
|
|
520
684
|
return JSON.stringify({ folder: buildFolder(newFolder) });
|
|
521
685
|
}
|
|
522
|
-
`;var
|
|
686
|
+
`;var mf=`/**
|
|
523
687
|
* JXA: delete a folder by ID. Refuses if the folder has projects or subfolders.
|
|
524
688
|
*
|
|
525
689
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -557,7 +721,7 @@ function run(argv) {
|
|
|
557
721
|
|
|
558
722
|
return JSON.stringify({ id: args.id });
|
|
559
723
|
}
|
|
560
|
-
`;var
|
|
724
|
+
`;var ff=`/**
|
|
561
725
|
* JXA: fetch one folder by ID.
|
|
562
726
|
*
|
|
563
727
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -604,7 +768,7 @@ function run(argv) {
|
|
|
604
768
|
|
|
605
769
|
throw new Error(\`Folder not found: \${args.id}\`);
|
|
606
770
|
}
|
|
607
|
-
`;var
|
|
771
|
+
`;var gf=`/**
|
|
608
772
|
* JXA: list all folders, optionally filtered by parentId.
|
|
609
773
|
*
|
|
610
774
|
* Args (argv[0] JSON): { parentId?: string }
|
|
@@ -645,27 +809,51 @@ function run(argv) {
|
|
|
645
809
|
id: id,
|
|
646
810
|
name: folder.name(),
|
|
647
811
|
parentId: parentId,
|
|
648
|
-
projectCount:
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
812
|
+
projectCount: (() => {
|
|
813
|
+
try {
|
|
814
|
+
return folder.projects().length;
|
|
815
|
+
} catch (_e) {
|
|
816
|
+
return 0;
|
|
817
|
+
}
|
|
818
|
+
})(),
|
|
819
|
+
subfolderCount: (() => {
|
|
820
|
+
try {
|
|
821
|
+
return folder.folders().length;
|
|
822
|
+
} catch (_e) {
|
|
823
|
+
return 0;
|
|
824
|
+
}
|
|
825
|
+
})(),
|
|
826
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
827
|
+
createdAt: (() => {
|
|
828
|
+
try {
|
|
829
|
+
return folder.creationDate().toISOString();
|
|
830
|
+
} catch (_e) {
|
|
831
|
+
return new Date().toISOString();
|
|
832
|
+
}
|
|
833
|
+
})(),
|
|
834
|
+
modifiedAt: (() => {
|
|
835
|
+
try {
|
|
836
|
+
return folder.modificationDate().toISOString();
|
|
837
|
+
} catch (_e) {
|
|
838
|
+
return new Date().toISOString();
|
|
839
|
+
}
|
|
840
|
+
})(),
|
|
656
841
|
};
|
|
657
842
|
}
|
|
658
843
|
|
|
659
844
|
const result = [];
|
|
660
845
|
for (let i = 0; i < allFolders.length; i++) {
|
|
661
846
|
const built = buildFolder(allFolders[i]);
|
|
662
|
-
|
|
847
|
+
// JxaTransport may send \`parentId: null\` for "no filter" \u2014 treat null and
|
|
848
|
+
// undefined identically so those calls don't filter every folder out.
|
|
849
|
+
// See #515 (tag_list had the same bug).
|
|
850
|
+
if (args.parentId != null && built.parentId !== args.parentId) continue;
|
|
663
851
|
result.push(built);
|
|
664
852
|
}
|
|
665
853
|
|
|
666
854
|
return JSON.stringify({ folders: result });
|
|
667
855
|
}
|
|
668
|
-
`;var
|
|
856
|
+
`;var hf=`/**
|
|
669
857
|
* JXA: rename a folder.
|
|
670
858
|
*
|
|
671
859
|
* Args (argv[0] JSON): { id: string, name: string }
|
|
@@ -717,7 +905,7 @@ function run(argv) {
|
|
|
717
905
|
|
|
718
906
|
return JSON.stringify({ folder: buildFolder(target) });
|
|
719
907
|
}
|
|
720
|
-
`;var
|
|
908
|
+
`;var yf=`/**
|
|
721
909
|
* JXA: get forecast-view tasks grouped by category (overdue, dueToday,
|
|
722
910
|
* deferredToday, flagged).
|
|
723
911
|
*
|
|
@@ -867,51 +1055,111 @@ function run(argv) {
|
|
|
867
1055
|
completedByChildren,
|
|
868
1056
|
estimatedMinutes: null,
|
|
869
1057
|
repetition: buildRepetition(task),
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1058
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
1059
|
+
|
|
1060
|
+
createdAt: (() => {
|
|
1061
|
+
try {
|
|
1062
|
+
return task.creationDate().toISOString();
|
|
1063
|
+
} catch (_e) {
|
|
1064
|
+
return new Date().toISOString();
|
|
1065
|
+
}
|
|
1066
|
+
})(),
|
|
1067
|
+
|
|
1068
|
+
modifiedAt: (() => {
|
|
1069
|
+
try {
|
|
1070
|
+
return task.modificationDate().toISOString();
|
|
1071
|
+
} catch (_e) {
|
|
1072
|
+
return new Date().toISOString();
|
|
1073
|
+
}
|
|
1074
|
+
})(),
|
|
874
1075
|
};
|
|
875
1076
|
}
|
|
876
1077
|
|
|
877
1078
|
// ---------------------------------------------------------------------------
|
|
878
|
-
// Collect
|
|
1079
|
+
// Collect candidates and bucket them \u2014 see #500.
|
|
1080
|
+
//
|
|
1081
|
+
// Push every filter into OF's runtime via \`whose()\`, one query per bucket.
|
|
1082
|
+
// Each query returns only the tasks that actually match \u2014 typically a
|
|
1083
|
+
// handful \u2014 so we never pay the per-task JXA accessor cost on the long
|
|
1084
|
+
// tail of unrelated tasks.
|
|
1085
|
+
//
|
|
1086
|
+
// Empirical comparison (~240 active tasks, OF 4.8.x):
|
|
1087
|
+
//
|
|
1088
|
+
// Original (build every task) ~12 s
|
|
1089
|
+
// whose() + per-task probe of 3 accessors ~12 s (probe wins
|
|
1090
|
+
// nothing \u2014
|
|
1091
|
+
// JXA per-call
|
|
1092
|
+
// overhead is
|
|
1093
|
+
// the limit)
|
|
1094
|
+
// Four whose() queries (this implementation) <0.5 s
|
|
1095
|
+
//
|
|
1096
|
+
// OF's whose() supports equality and date range comparisons (\`_lessThan\`,
|
|
1097
|
+
// \`_greaterThan\`, \`_greaterThanEquals\`, \`_lessThanEquals\`) on individual
|
|
1098
|
+
// properties, but rejects \`_or\` / \`_isnt: null\`. Hence one query per bucket
|
|
1099
|
+
// rather than a single \`_or\`-combined query.
|
|
1100
|
+
//
|
|
1101
|
+
// Tasks that match multiple buckets are built once and pushed into each
|
|
1102
|
+
// matching bucket. Dedup is keyed on persistent ID.
|
|
879
1103
|
// ---------------------------------------------------------------------------
|
|
880
1104
|
|
|
881
|
-
const
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1105
|
+
const fromDate = new Date(from);
|
|
1106
|
+
const toDate = new Date(to);
|
|
1107
|
+
|
|
1108
|
+
// ID \u2192 built task, populated lazily so each task is constructed once.
|
|
1109
|
+
const builtById = {};
|
|
1110
|
+
function builtFor(task) {
|
|
1111
|
+
const id = task.id();
|
|
1112
|
+
if (builtById[id] !== undefined) return builtById[id];
|
|
1113
|
+
const b = buildTask(task);
|
|
1114
|
+
builtById[id] = b;
|
|
1115
|
+
return b;
|
|
889
1116
|
}
|
|
890
1117
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1118
|
+
function runQuery(predicate) {
|
|
1119
|
+
try {
|
|
1120
|
+
return ofApp.defaultDocument.flattenedTasks.whose(predicate)();
|
|
1121
|
+
} catch (_e) {
|
|
1122
|
+
return [];
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
894
1125
|
|
|
895
1126
|
const overdue = [];
|
|
896
1127
|
const dueToday = [];
|
|
897
1128
|
const deferredToday = [];
|
|
898
1129
|
const flaggedTasks = [];
|
|
899
1130
|
|
|
900
|
-
|
|
901
|
-
const
|
|
1131
|
+
if (includeOverdue) {
|
|
1132
|
+
const matches = runQuery({
|
|
1133
|
+
completed: false,
|
|
1134
|
+
dropped: false,
|
|
1135
|
+
dueDate: { _lessThan: fromDate },
|
|
1136
|
+
});
|
|
1137
|
+
for (let i = 0; i < matches.length; i++) overdue.push(builtFor(matches[i]));
|
|
1138
|
+
}
|
|
902
1139
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1140
|
+
// dueToday is always populated regardless of include flags (mirrors the
|
|
1141
|
+
// pre-#500 behaviour).
|
|
1142
|
+
{
|
|
1143
|
+
const matches = runQuery({
|
|
1144
|
+
completed: false,
|
|
1145
|
+
dropped: false,
|
|
1146
|
+
dueDate: { _greaterThanEquals: fromDate, _lessThanEquals: toDate },
|
|
1147
|
+
});
|
|
1148
|
+
for (let i = 0; i < matches.length; i++) dueToday.push(builtFor(matches[i]));
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (includeDeferred) {
|
|
1152
|
+
const matches = runQuery({
|
|
1153
|
+
completed: false,
|
|
1154
|
+
dropped: false,
|
|
1155
|
+
deferDate: { _greaterThanEquals: fromDate, _lessThanEquals: toDate },
|
|
1156
|
+
});
|
|
1157
|
+
for (let i = 0; i < matches.length; i++) deferredToday.push(builtFor(matches[i]));
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (includeFlagged) {
|
|
1161
|
+
const matches = runQuery({ completed: false, dropped: false, flagged: true });
|
|
1162
|
+
for (let i = 0; i < matches.length; i++) flaggedTasks.push(builtFor(matches[i]));
|
|
915
1163
|
}
|
|
916
1164
|
|
|
917
1165
|
return JSON.stringify({
|
|
@@ -921,7 +1169,7 @@ function run(argv) {
|
|
|
921
1169
|
flagged: flaggedTasks,
|
|
922
1170
|
});
|
|
923
1171
|
}
|
|
924
|
-
`;var
|
|
1172
|
+
`;var kf=`/**
|
|
925
1173
|
* JXA: evaluate a built-in OmniFocus perspective and return its task list.
|
|
926
1174
|
*
|
|
927
1175
|
* Args (argv[0] JSON): { "perspectiveId": "inbox" | "projects" | "tags" | "forecast" | "flagged" | "nearby" | "review" }
|
|
@@ -1070,10 +1318,23 @@ function run(argv) {
|
|
|
1070
1318
|
sequential: sequential,
|
|
1071
1319
|
completedByChildren: completedByChildren,
|
|
1072
1320
|
repetition: buildRepetition(task),
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1321
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
1322
|
+
|
|
1323
|
+
createdAt: (() => {
|
|
1324
|
+
try {
|
|
1325
|
+
return task.creationDate().toISOString();
|
|
1326
|
+
} catch (_e) {
|
|
1327
|
+
return new Date().toISOString();
|
|
1328
|
+
}
|
|
1329
|
+
})(),
|
|
1330
|
+
|
|
1331
|
+
modifiedAt: (() => {
|
|
1332
|
+
try {
|
|
1333
|
+
return task.modificationDate().toISOString();
|
|
1334
|
+
} catch (_e) {
|
|
1335
|
+
return new Date().toISOString();
|
|
1336
|
+
}
|
|
1337
|
+
})(),
|
|
1077
1338
|
};
|
|
1078
1339
|
}
|
|
1079
1340
|
|
|
@@ -1137,7 +1398,7 @@ function run(argv) {
|
|
|
1137
1398
|
return JSON.stringify({ error: String(e) });
|
|
1138
1399
|
}
|
|
1139
1400
|
}
|
|
1140
|
-
`;var
|
|
1401
|
+
`;var If=`/**
|
|
1141
1402
|
* JXA: list all perspectives (built-in + custom).
|
|
1142
1403
|
*
|
|
1143
1404
|
* Args (argv[0] JSON): {} (no arguments)
|
|
@@ -1224,7 +1485,7 @@ function run(_argv) {
|
|
|
1224
1485
|
|
|
1225
1486
|
return JSON.stringify({ perspectives });
|
|
1226
1487
|
}
|
|
1227
|
-
`;var
|
|
1488
|
+
`;var vf=`/**
|
|
1228
1489
|
* JXA: batch-complete projects in a single round-trip.
|
|
1229
1490
|
*
|
|
1230
1491
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -1284,7 +1545,7 @@ function run(argv) {
|
|
|
1284
1545
|
|
|
1285
1546
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
1286
1547
|
}
|
|
1287
|
-
`;var
|
|
1548
|
+
`;var Tf=`/**
|
|
1288
1549
|
* JXA: batch-drop projects in a single round-trip.
|
|
1289
1550
|
*
|
|
1290
1551
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -1345,7 +1606,7 @@ function run(argv) {
|
|
|
1345
1606
|
|
|
1346
1607
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
1347
1608
|
}
|
|
1348
|
-
`;var
|
|
1609
|
+
`;var wf=`/**
|
|
1349
1610
|
* JXA: mark a project complete.
|
|
1350
1611
|
*
|
|
1351
1612
|
* Args (argv[0] JSON): { id: string, completionDate?: string|null }
|
|
@@ -1381,7 +1642,7 @@ function run(argv) {
|
|
|
1381
1642
|
|
|
1382
1643
|
return JSON.stringify({ id: args.id });
|
|
1383
1644
|
}
|
|
1384
|
-
`;var
|
|
1645
|
+
`;var Sf=`/**
|
|
1385
1646
|
* JXA: create a new project.
|
|
1386
1647
|
*
|
|
1387
1648
|
* Args (argv[0] JSON): { name: string, folderId?: string|null, note?: string|null,
|
|
@@ -1524,10 +1785,23 @@ function run(argv) {
|
|
|
1524
1785
|
droppedAt: status === "dropped" ? completedAt : null,
|
|
1525
1786
|
taskCount: taskCount,
|
|
1526
1787
|
completedTaskCount: completedTaskCount,
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1788
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
1789
|
+
|
|
1790
|
+
createdAt: (() => {
|
|
1791
|
+
try {
|
|
1792
|
+
return proj.creationDate().toISOString();
|
|
1793
|
+
} catch (_e) {
|
|
1794
|
+
return new Date().toISOString();
|
|
1795
|
+
}
|
|
1796
|
+
})(),
|
|
1797
|
+
|
|
1798
|
+
modifiedAt: (() => {
|
|
1799
|
+
try {
|
|
1800
|
+
return proj.modificationDate().toISOString();
|
|
1801
|
+
} catch (_e) {
|
|
1802
|
+
return new Date().toISOString();
|
|
1803
|
+
}
|
|
1804
|
+
})(),
|
|
1531
1805
|
};
|
|
1532
1806
|
}
|
|
1533
1807
|
|
|
@@ -1561,7 +1835,7 @@ function run(argv) {
|
|
|
1561
1835
|
|
|
1562
1836
|
return JSON.stringify({ project: buildProject(newProj) });
|
|
1563
1837
|
}
|
|
1564
|
-
`;var
|
|
1838
|
+
`;var bf=`/**
|
|
1565
1839
|
* JXA: delete a project by ID.
|
|
1566
1840
|
*
|
|
1567
1841
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -1591,7 +1865,7 @@ function run(argv) {
|
|
|
1591
1865
|
|
|
1592
1866
|
return JSON.stringify({ id: args.id });
|
|
1593
1867
|
}
|
|
1594
|
-
`;var
|
|
1868
|
+
`;var jf=`/**
|
|
1595
1869
|
* JXA: mark a project dropped.
|
|
1596
1870
|
*
|
|
1597
1871
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -1621,7 +1895,7 @@ function run(argv) {
|
|
|
1621
1895
|
|
|
1622
1896
|
return JSON.stringify({ id: args.id });
|
|
1623
1897
|
}
|
|
1624
|
-
`;var
|
|
1898
|
+
`;var _f=`/**
|
|
1625
1899
|
* JXA: fetch one project by ID.
|
|
1626
1900
|
*
|
|
1627
1901
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -1762,10 +2036,23 @@ function run(argv) {
|
|
|
1762
2036
|
droppedAt: status === "dropped" ? completedAt : null,
|
|
1763
2037
|
taskCount: taskCount,
|
|
1764
2038
|
completedTaskCount: completedTaskCount,
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
2039
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
2040
|
+
|
|
2041
|
+
createdAt: (() => {
|
|
2042
|
+
try {
|
|
2043
|
+
return proj.creationDate().toISOString();
|
|
2044
|
+
} catch (_e) {
|
|
2045
|
+
return new Date().toISOString();
|
|
2046
|
+
}
|
|
2047
|
+
})(),
|
|
2048
|
+
|
|
2049
|
+
modifiedAt: (() => {
|
|
2050
|
+
try {
|
|
2051
|
+
return proj.modificationDate().toISOString();
|
|
2052
|
+
} catch (_e) {
|
|
2053
|
+
return new Date().toISOString();
|
|
2054
|
+
}
|
|
2055
|
+
})(),
|
|
1769
2056
|
};
|
|
1770
2057
|
}
|
|
1771
2058
|
|
|
@@ -1778,7 +2065,7 @@ function run(argv) {
|
|
|
1778
2065
|
|
|
1779
2066
|
throw new Error(\`Project not found: \${args.id}\`);
|
|
1780
2067
|
}
|
|
1781
|
-
`;var
|
|
2068
|
+
`;var Pf=`/**
|
|
1782
2069
|
* JXA: fetch multiple projects by IDs.
|
|
1783
2070
|
*
|
|
1784
2071
|
* Args (argv[0] JSON): { ids: string[] }
|
|
@@ -1917,10 +2204,23 @@ function run(argv) {
|
|
|
1917
2204
|
droppedAt: status === "dropped" ? completedAt : null,
|
|
1918
2205
|
taskCount: taskCount,
|
|
1919
2206
|
completedTaskCount: completedTaskCount,
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2207
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
2208
|
+
|
|
2209
|
+
createdAt: (() => {
|
|
2210
|
+
try {
|
|
2211
|
+
return proj.creationDate().toISOString();
|
|
2212
|
+
} catch (_e) {
|
|
2213
|
+
return new Date().toISOString();
|
|
2214
|
+
}
|
|
2215
|
+
})(),
|
|
2216
|
+
|
|
2217
|
+
modifiedAt: (() => {
|
|
2218
|
+
try {
|
|
2219
|
+
return proj.modificationDate().toISOString();
|
|
2220
|
+
} catch (_e) {
|
|
2221
|
+
return new Date().toISOString();
|
|
2222
|
+
}
|
|
2223
|
+
})(),
|
|
1924
2224
|
};
|
|
1925
2225
|
}
|
|
1926
2226
|
|
|
@@ -1944,7 +2244,7 @@ function run(argv) {
|
|
|
1944
2244
|
|
|
1945
2245
|
return JSON.stringify({ projects: results });
|
|
1946
2246
|
}
|
|
1947
|
-
`;var
|
|
2247
|
+
`;var Of=`/**
|
|
1948
2248
|
* JXA: list projects, optionally filtered by folderId or status.
|
|
1949
2249
|
*
|
|
1950
2250
|
* Args (argv[0] JSON): { folderId?: string|null, status?: string|null }
|
|
@@ -2085,10 +2385,23 @@ function run(argv) {
|
|
|
2085
2385
|
droppedAt: status === "dropped" ? completedAt : null,
|
|
2086
2386
|
taskCount: taskCount,
|
|
2087
2387
|
completedTaskCount: completedTaskCount,
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2388
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
2389
|
+
|
|
2390
|
+
createdAt: (() => {
|
|
2391
|
+
try {
|
|
2392
|
+
return proj.creationDate().toISOString();
|
|
2393
|
+
} catch (_e) {
|
|
2394
|
+
return new Date().toISOString();
|
|
2395
|
+
}
|
|
2396
|
+
})(),
|
|
2397
|
+
|
|
2398
|
+
modifiedAt: (() => {
|
|
2399
|
+
try {
|
|
2400
|
+
return proj.modificationDate().toISOString();
|
|
2401
|
+
} catch (_e) {
|
|
2402
|
+
return new Date().toISOString();
|
|
2403
|
+
}
|
|
2404
|
+
})(),
|
|
2092
2405
|
};
|
|
2093
2406
|
}
|
|
2094
2407
|
|
|
@@ -2113,7 +2426,7 @@ function run(argv) {
|
|
|
2113
2426
|
|
|
2114
2427
|
return JSON.stringify({ projects: result });
|
|
2115
2428
|
}
|
|
2116
|
-
`;var
|
|
2429
|
+
`;var xf=`/**
|
|
2117
2430
|
* JXA: mark a project as reviewed (sets lastReviewDate to now).
|
|
2118
2431
|
*
|
|
2119
2432
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -2144,7 +2457,7 @@ function run(argv) {
|
|
|
2144
2457
|
|
|
2145
2458
|
return JSON.stringify({ id: args.id });
|
|
2146
2459
|
}
|
|
2147
|
-
`;var
|
|
2460
|
+
`;var Df=`/**
|
|
2148
2461
|
* JXA: move a project to a folder (or to the root if folderId is null).
|
|
2149
2462
|
*
|
|
2150
2463
|
* Args (argv[0] JSON): { id: string, folderId?: string|null }
|
|
@@ -2180,7 +2493,49 @@ function run(argv) {
|
|
|
2180
2493
|
|
|
2181
2494
|
return JSON.stringify({ id: args.id });
|
|
2182
2495
|
}
|
|
2183
|
-
`;var
|
|
2496
|
+
`;var Rf=`/**
|
|
2497
|
+
* JXA: set a project's next review date.
|
|
2498
|
+
*
|
|
2499
|
+
* The third axis of OmniFocus's review schedule (the others being
|
|
2500
|
+
* reviewIntervalDays and lastReviewDate). Setting nextReviewDate directly
|
|
2501
|
+
* lets agents reschedule a review independent of the recurring interval \u2014
|
|
2502
|
+
* "push the Q3 review to next Monday" without mutating the cadence.
|
|
2503
|
+
*
|
|
2504
|
+
* Args (argv[0] JSON):
|
|
2505
|
+
* { id: string, nextReviewDate: string | null }
|
|
2506
|
+
* - \`nextReviewDate\` ISO-8601 \u2192 set to that date
|
|
2507
|
+
* - \`nextReviewDate\` null \u2192 clear (Project shows up as not scheduled)
|
|
2508
|
+
*
|
|
2509
|
+
* Returns JSON: { id: string }
|
|
2510
|
+
*
|
|
2511
|
+
* Past-dated values are allowed by OmniFocus and surface the project as
|
|
2512
|
+
* overdue for review immediately \u2014 matches the app UX, no special handling.
|
|
2513
|
+
*
|
|
2514
|
+
* @see #467
|
|
2515
|
+
* @see src/adapter/jxa/JxaTransport.ts \u2014 caller
|
|
2516
|
+
*/
|
|
2517
|
+
|
|
2518
|
+
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
|
|
2519
|
+
function run(argv) {
|
|
2520
|
+
const args = JSON.parse(argv[0]);
|
|
2521
|
+
const ofApp = Application("OmniFocus");
|
|
2522
|
+
ofApp.includeStandardAdditions = false;
|
|
2523
|
+
|
|
2524
|
+
const allProjects = ofApp.defaultDocument.flattenedProjects();
|
|
2525
|
+
let target = null;
|
|
2526
|
+
for (let i = 0; i < allProjects.length; i++) {
|
|
2527
|
+
if (allProjects[i].id() === args.id) {
|
|
2528
|
+
target = allProjects[i];
|
|
2529
|
+
break;
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
if (!target) throw new Error(\`Project not found: \${args.id}\`);
|
|
2533
|
+
|
|
2534
|
+
target.nextReviewDate = args.nextReviewDate === null ? null : new Date(args.nextReviewDate);
|
|
2535
|
+
|
|
2536
|
+
return JSON.stringify({ id: args.id });
|
|
2537
|
+
}
|
|
2538
|
+
`;var Af=`/**
|
|
2184
2539
|
* JXA: set a project's review interval.
|
|
2185
2540
|
*
|
|
2186
2541
|
* Args (argv[0] JSON): { id: string, days: number | null }
|
|
@@ -2210,7 +2565,7 @@ function run(argv) {
|
|
|
2210
2565
|
|
|
2211
2566
|
return JSON.stringify({ id: args.id });
|
|
2212
2567
|
}
|
|
2213
|
-
`;var
|
|
2568
|
+
`;var Cf=`/**
|
|
2214
2569
|
* JXA: update mutable fields on an existing project.
|
|
2215
2570
|
*
|
|
2216
2571
|
* Args (argv[0] JSON): { id: string, name?: string, note?: string|null,
|
|
@@ -2354,10 +2709,23 @@ function run(argv) {
|
|
|
2354
2709
|
droppedAt: status === "dropped" ? completedAt : null,
|
|
2355
2710
|
taskCount: taskCount,
|
|
2356
2711
|
completedTaskCount: completedTaskCount,
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2712
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
2713
|
+
|
|
2714
|
+
createdAt: (() => {
|
|
2715
|
+
try {
|
|
2716
|
+
return proj.creationDate().toISOString();
|
|
2717
|
+
} catch (_e) {
|
|
2718
|
+
return new Date().toISOString();
|
|
2719
|
+
}
|
|
2720
|
+
})(),
|
|
2721
|
+
|
|
2722
|
+
modifiedAt: (() => {
|
|
2723
|
+
try {
|
|
2724
|
+
return proj.modificationDate().toISOString();
|
|
2725
|
+
} catch (_e) {
|
|
2726
|
+
return new Date().toISOString();
|
|
2727
|
+
}
|
|
2728
|
+
})(),
|
|
2361
2729
|
};
|
|
2362
2730
|
}
|
|
2363
2731
|
|
|
@@ -2393,7 +2761,7 @@ function run(argv) {
|
|
|
2393
2761
|
|
|
2394
2762
|
return JSON.stringify({ project: buildProject(target) });
|
|
2395
2763
|
}
|
|
2396
|
-
`;var
|
|
2764
|
+
`;var Ef=`/**
|
|
2397
2765
|
* JXA: list projects due for review (nextReviewDate <= today, or null).
|
|
2398
2766
|
*
|
|
2399
2767
|
* Args (argv[0] JSON): {}
|
|
@@ -2460,7 +2828,7 @@ function run(argv) {
|
|
|
2460
2828
|
|
|
2461
2829
|
return JSON.stringify({ projects: due });
|
|
2462
2830
|
}
|
|
2463
|
-
`;var
|
|
2831
|
+
`;var Mf=`/**
|
|
2464
2832
|
* JXA: trigger Omni Sync.
|
|
2465
2833
|
*
|
|
2466
2834
|
* \`Application("OmniFocus").defaultDocument.synchronize()\` kicks off a sync
|
|
@@ -2483,7 +2851,7 @@ function run(_argv) {
|
|
|
2483
2851
|
const lastSyncAt = new Date().toISOString();
|
|
2484
2852
|
return JSON.stringify({ lastSyncAt: lastSyncAt, inFlight: false });
|
|
2485
2853
|
}
|
|
2486
|
-
`;var
|
|
2854
|
+
`;var Nf=`/**
|
|
2487
2855
|
* JXA: create a tag, optionally under a parent tag.
|
|
2488
2856
|
*
|
|
2489
2857
|
* Args (argv[0] JSON): { name: string, parentId?: string }
|
|
@@ -2534,10 +2902,23 @@ function run(argv) {
|
|
|
2534
2902
|
location: location,
|
|
2535
2903
|
allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
|
|
2536
2904
|
taskCount: tag.tasks ? tag.tasks().length : 0,
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2905
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
2906
|
+
|
|
2907
|
+
createdAt: (() => {
|
|
2908
|
+
try {
|
|
2909
|
+
return tag.creationDate().toISOString();
|
|
2910
|
+
} catch (_e) {
|
|
2911
|
+
return new Date().toISOString();
|
|
2912
|
+
}
|
|
2913
|
+
})(),
|
|
2914
|
+
|
|
2915
|
+
modifiedAt: (() => {
|
|
2916
|
+
try {
|
|
2917
|
+
return tag.modificationDate().toISOString();
|
|
2918
|
+
} catch (_e) {
|
|
2919
|
+
return new Date().toISOString();
|
|
2920
|
+
}
|
|
2921
|
+
})(),
|
|
2541
2922
|
};
|
|
2542
2923
|
}
|
|
2543
2924
|
|
|
@@ -2568,7 +2949,7 @@ function run(argv) {
|
|
|
2568
2949
|
const fetchedTag = doc.flattenedTags.byId(tagId);
|
|
2569
2950
|
return JSON.stringify({ tag: buildTag(fetchedTag) });
|
|
2570
2951
|
}
|
|
2571
|
-
`;var
|
|
2952
|
+
`;var Ff=`/**
|
|
2572
2953
|
* JXA: delete a tag by ID.
|
|
2573
2954
|
*
|
|
2574
2955
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -2598,7 +2979,7 @@ function run(argv) {
|
|
|
2598
2979
|
|
|
2599
2980
|
return JSON.stringify({ id: args.id });
|
|
2600
2981
|
}
|
|
2601
|
-
`;var
|
|
2982
|
+
`;var Uf=`/**
|
|
2602
2983
|
* JXA: fetch one tag by ID.
|
|
2603
2984
|
*
|
|
2604
2985
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -2649,10 +3030,23 @@ function run(argv) {
|
|
|
2649
3030
|
location: location,
|
|
2650
3031
|
allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
|
|
2651
3032
|
taskCount: tag.tasks ? tag.tasks().length : 0,
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
3033
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
3034
|
+
|
|
3035
|
+
createdAt: (() => {
|
|
3036
|
+
try {
|
|
3037
|
+
return tag.creationDate().toISOString();
|
|
3038
|
+
} catch (_e) {
|
|
3039
|
+
return new Date().toISOString();
|
|
3040
|
+
}
|
|
3041
|
+
})(),
|
|
3042
|
+
|
|
3043
|
+
modifiedAt: (() => {
|
|
3044
|
+
try {
|
|
3045
|
+
return tag.modificationDate().toISOString();
|
|
3046
|
+
} catch (_e) {
|
|
3047
|
+
return new Date().toISOString();
|
|
3048
|
+
}
|
|
3049
|
+
})(),
|
|
2656
3050
|
};
|
|
2657
3051
|
}
|
|
2658
3052
|
|
|
@@ -2665,7 +3059,7 @@ function run(argv) {
|
|
|
2665
3059
|
|
|
2666
3060
|
throw new Error(\`Tag not found: \${args.id}\`);
|
|
2667
3061
|
}
|
|
2668
|
-
`;var
|
|
3062
|
+
`;var Lf=`/**
|
|
2669
3063
|
* JXA: fetch multiple tags by IDs.
|
|
2670
3064
|
*
|
|
2671
3065
|
* Args (argv[0] JSON): { ids: string[] }
|
|
@@ -2716,10 +3110,23 @@ function run(argv) {
|
|
|
2716
3110
|
location: location,
|
|
2717
3111
|
allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
|
|
2718
3112
|
taskCount: tag.tasks ? tag.tasks().length : 0,
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
3113
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
3114
|
+
|
|
3115
|
+
createdAt: (() => {
|
|
3116
|
+
try {
|
|
3117
|
+
return tag.creationDate().toISOString();
|
|
3118
|
+
} catch (_e) {
|
|
3119
|
+
return new Date().toISOString();
|
|
3120
|
+
}
|
|
3121
|
+
})(),
|
|
3122
|
+
|
|
3123
|
+
modifiedAt: (() => {
|
|
3124
|
+
try {
|
|
3125
|
+
return tag.modificationDate().toISOString();
|
|
3126
|
+
} catch (_e) {
|
|
3127
|
+
return new Date().toISOString();
|
|
3128
|
+
}
|
|
3129
|
+
})(),
|
|
2723
3130
|
};
|
|
2724
3131
|
}
|
|
2725
3132
|
|
|
@@ -2743,7 +3150,7 @@ function run(argv) {
|
|
|
2743
3150
|
|
|
2744
3151
|
return JSON.stringify({ tags: results });
|
|
2745
3152
|
}
|
|
2746
|
-
`;var
|
|
3153
|
+
`;var Bf=`/**
|
|
2747
3154
|
* JXA: list all tags, optionally filtered by parentId or status.
|
|
2748
3155
|
*
|
|
2749
3156
|
* Args (argv[0] JSON): { parentId?: string, status?: string }
|
|
@@ -2786,18 +3193,43 @@ function run(argv) {
|
|
|
2786
3193
|
} catch (_e) {}
|
|
2787
3194
|
const status = rawStatus === "on hold" ? "on-hold" : rawStatus;
|
|
2788
3195
|
|
|
3196
|
+
// creationDate/modificationDate are present as functions on every Tag, but
|
|
3197
|
+
// invoking them throws "Can't get object." for tags that lack the
|
|
3198
|
+
// timestamp in the document \u2014 see #498. Truthiness on the property
|
|
3199
|
+
// reference is not enough; we have to guard the call.
|
|
3200
|
+
let createdAt;
|
|
3201
|
+
try {
|
|
3202
|
+
createdAt = tag.creationDate().toISOString();
|
|
3203
|
+
} catch (_e) {
|
|
3204
|
+
createdAt = new Date().toISOString();
|
|
3205
|
+
}
|
|
3206
|
+
let modifiedAt;
|
|
3207
|
+
try {
|
|
3208
|
+
modifiedAt = tag.modificationDate().toISOString();
|
|
3209
|
+
} catch (_e) {
|
|
3210
|
+
modifiedAt = new Date().toISOString();
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
let allowsNextAction = false;
|
|
3214
|
+
try {
|
|
3215
|
+
allowsNextAction = tag.allowsNextAction();
|
|
3216
|
+
} catch (_e) {}
|
|
3217
|
+
|
|
3218
|
+
let taskCount = 0;
|
|
3219
|
+
try {
|
|
3220
|
+
taskCount = tag.tasks().length;
|
|
3221
|
+
} catch (_e) {}
|
|
3222
|
+
|
|
2789
3223
|
return {
|
|
2790
3224
|
id: tag.id(),
|
|
2791
3225
|
name: tag.name(),
|
|
2792
3226
|
parentId: parentId,
|
|
2793
3227
|
status: status,
|
|
2794
3228
|
location: location,
|
|
2795
|
-
allowsNextAction:
|
|
2796
|
-
taskCount:
|
|
2797
|
-
createdAt:
|
|
2798
|
-
modifiedAt:
|
|
2799
|
-
? tag.modificationDate().toISOString()
|
|
2800
|
-
: new Date().toISOString(),
|
|
3229
|
+
allowsNextAction: allowsNextAction,
|
|
3230
|
+
taskCount: taskCount,
|
|
3231
|
+
createdAt: createdAt,
|
|
3232
|
+
modifiedAt: modifiedAt,
|
|
2801
3233
|
};
|
|
2802
3234
|
}
|
|
2803
3235
|
|
|
@@ -2808,15 +3240,18 @@ function run(argv) {
|
|
|
2808
3240
|
const tag = allTags[i];
|
|
2809
3241
|
const built = buildTag(tag);
|
|
2810
3242
|
|
|
2811
|
-
|
|
2812
|
-
|
|
3243
|
+
// JxaTransport sends \`parentId: null\` / \`status: null\` for "no filter"
|
|
3244
|
+
// (rather than omitting). Treat null and undefined identically here so
|
|
3245
|
+
// those calls don't filter every tag out \u2014 see #515.
|
|
3246
|
+
if (args.parentId != null && built.parentId !== args.parentId) continue;
|
|
3247
|
+
if (args.status != null && built.status !== args.status) continue;
|
|
2813
3248
|
|
|
2814
3249
|
result.push(built);
|
|
2815
3250
|
}
|
|
2816
3251
|
|
|
2817
3252
|
return JSON.stringify({ tags: result });
|
|
2818
3253
|
}
|
|
2819
|
-
`;var
|
|
3254
|
+
`;var Jf=`/**
|
|
2820
3255
|
* JXA: update mutable fields on an existing tag.
|
|
2821
3256
|
*
|
|
2822
3257
|
* Args (argv[0] JSON): { id: string, name?: string, status?: string, allowsNextAction?: boolean }
|
|
@@ -2867,10 +3302,23 @@ function run(argv) {
|
|
|
2867
3302
|
location: location,
|
|
2868
3303
|
allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
|
|
2869
3304
|
taskCount: tag.tasks ? tag.tasks().length : 0,
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
3305
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
3306
|
+
|
|
3307
|
+
createdAt: (() => {
|
|
3308
|
+
try {
|
|
3309
|
+
return tag.creationDate().toISOString();
|
|
3310
|
+
} catch (_e) {
|
|
3311
|
+
return new Date().toISOString();
|
|
3312
|
+
}
|
|
3313
|
+
})(),
|
|
3314
|
+
|
|
3315
|
+
modifiedAt: (() => {
|
|
3316
|
+
try {
|
|
3317
|
+
return tag.modificationDate().toISOString();
|
|
3318
|
+
} catch (_e) {
|
|
3319
|
+
return new Date().toISOString();
|
|
3320
|
+
}
|
|
3321
|
+
})(),
|
|
2874
3322
|
};
|
|
2875
3323
|
}
|
|
2876
3324
|
|
|
@@ -2894,7 +3342,7 @@ function run(argv) {
|
|
|
2894
3342
|
|
|
2895
3343
|
return JSON.stringify({ tag: buildTag(target) });
|
|
2896
3344
|
}
|
|
2897
|
-
`;var
|
|
3345
|
+
`;var $f=`/**
|
|
2898
3346
|
* JXA: batch-complete tasks in a single round-trip.
|
|
2899
3347
|
*
|
|
2900
3348
|
* Args (argv[0] JSON): { items: Array<{ id, at? }> }
|
|
@@ -2941,7 +3389,7 @@ function run(argv) {
|
|
|
2941
3389
|
|
|
2942
3390
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
2943
3391
|
}
|
|
2944
|
-
`;var
|
|
3392
|
+
`;var Wf=`/**
|
|
2945
3393
|
* JXA: batch-create tasks in a single round-trip.
|
|
2946
3394
|
*
|
|
2947
3395
|
* Args (argv[0] JSON): { inputs: CreateTaskInput[] }
|
|
@@ -3017,7 +3465,7 @@ function run(argv) {
|
|
|
3017
3465
|
|
|
3018
3466
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
3019
3467
|
}
|
|
3020
|
-
`;var
|
|
3468
|
+
`;var Hf=`/**
|
|
3021
3469
|
* JXA: batch-delete tasks permanently in a single round-trip.
|
|
3022
3470
|
*
|
|
3023
3471
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -3057,7 +3505,7 @@ function run(argv) {
|
|
|
3057
3505
|
|
|
3058
3506
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
3059
3507
|
}
|
|
3060
|
-
`;var
|
|
3508
|
+
`;var zf=`/**
|
|
3061
3509
|
* JXA: batch-drop (cancel) tasks in a single round-trip.
|
|
3062
3510
|
*
|
|
3063
3511
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -3100,7 +3548,7 @@ function run(argv) {
|
|
|
3100
3548
|
|
|
3101
3549
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
3102
3550
|
}
|
|
3103
|
-
`;var
|
|
3551
|
+
`;var Gf=`/**
|
|
3104
3552
|
* JXA: batch-uncomplete (mark incomplete) tasks in a single round-trip.
|
|
3105
3553
|
*
|
|
3106
3554
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -3162,7 +3610,7 @@ function run(argv) {
|
|
|
3162
3610
|
|
|
3163
3611
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
3164
3612
|
}
|
|
3165
|
-
`;var
|
|
3613
|
+
`;var qf=`/**
|
|
3166
3614
|
* JXA: batch-undrop (restore) tasks in a single round-trip.
|
|
3167
3615
|
*
|
|
3168
3616
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -3206,7 +3654,7 @@ function run(argv) {
|
|
|
3206
3654
|
|
|
3207
3655
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
3208
3656
|
}
|
|
3209
|
-
`;var
|
|
3657
|
+
`;var Vf=`/**
|
|
3210
3658
|
* JXA: batch-update tasks in a single round-trip.
|
|
3211
3659
|
*
|
|
3212
3660
|
* Args (argv[0] JSON): { updates: Array<{ id, patch }> }
|
|
@@ -3279,7 +3727,7 @@ function run(argv) {
|
|
|
3279
3727
|
|
|
3280
3728
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
3281
3729
|
}
|
|
3282
|
-
`;var
|
|
3730
|
+
`;var Kf=`/**
|
|
3283
3731
|
* JXA: mark a task complete.
|
|
3284
3732
|
*
|
|
3285
3733
|
* Args (argv[0] JSON): { id: string, completionDate?: string|null }
|
|
@@ -3314,7 +3762,7 @@ function run(argv) {
|
|
|
3314
3762
|
|
|
3315
3763
|
return JSON.stringify({ id: args.id });
|
|
3316
3764
|
}
|
|
3317
|
-
`;var
|
|
3765
|
+
`;var Xf=`/**
|
|
3318
3766
|
* JXA: create a new task.
|
|
3319
3767
|
*
|
|
3320
3768
|
* Args (argv[0] JSON): {
|
|
@@ -3460,10 +3908,23 @@ function run(argv) {
|
|
|
3460
3908
|
sequential: sequential,
|
|
3461
3909
|
completedByChildren: completedByChildren,
|
|
3462
3910
|
repetition: buildRepetition(task),
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3911
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
3912
|
+
|
|
3913
|
+
createdAt: (() => {
|
|
3914
|
+
try {
|
|
3915
|
+
return task.creationDate().toISOString();
|
|
3916
|
+
} catch (_e) {
|
|
3917
|
+
return new Date().toISOString();
|
|
3918
|
+
}
|
|
3919
|
+
})(),
|
|
3920
|
+
|
|
3921
|
+
modifiedAt: (() => {
|
|
3922
|
+
try {
|
|
3923
|
+
return task.modificationDate().toISOString();
|
|
3924
|
+
} catch (_e) {
|
|
3925
|
+
return new Date().toISOString();
|
|
3926
|
+
}
|
|
3927
|
+
})(),
|
|
3467
3928
|
};
|
|
3468
3929
|
}
|
|
3469
3930
|
|
|
@@ -3533,7 +3994,7 @@ function run(argv) {
|
|
|
3533
3994
|
}
|
|
3534
3995
|
return JSON.stringify({ task: buildTask(newTask) });
|
|
3535
3996
|
}
|
|
3536
|
-
`;var
|
|
3997
|
+
`;var Yf=`/**
|
|
3537
3998
|
* JXA: delete a task permanently.
|
|
3538
3999
|
*
|
|
3539
4000
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -3562,7 +4023,7 @@ function run(argv) {
|
|
|
3562
4023
|
|
|
3563
4024
|
return JSON.stringify({ id: args.id });
|
|
3564
4025
|
}
|
|
3565
|
-
`;var
|
|
4026
|
+
`;var Zf=`/**
|
|
3566
4027
|
* JXA: mark a task as dropped.
|
|
3567
4028
|
*
|
|
3568
4029
|
* Args (argv[0] JSON): { id: string, droppedAt?: string|null }
|
|
@@ -3586,7 +4047,7 @@ function run(argv) {
|
|
|
3586
4047
|
|
|
3587
4048
|
return JSON.stringify({ id: args.id });
|
|
3588
4049
|
}
|
|
3589
|
-
`;var
|
|
4050
|
+
`;var Qf=`/**
|
|
3590
4051
|
* JXA: duplicate a task. Editable fields copy; completed/dropped state reset.
|
|
3591
4052
|
* When recursive=true, subtask tree is cloned depth-first, preserving order.
|
|
3592
4053
|
*
|
|
@@ -3703,7 +4164,7 @@ function run(argv) {
|
|
|
3703
4164
|
|
|
3704
4165
|
return JSON.stringify({ newId: rootClone.id(), descendantCount: descendantCount });
|
|
3705
4166
|
}
|
|
3706
|
-
`;var
|
|
4167
|
+
`;var eg=`/**
|
|
3707
4168
|
* JXA: fetch one task by ID.
|
|
3708
4169
|
*
|
|
3709
4170
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -3844,10 +4305,23 @@ function run(argv) {
|
|
|
3844
4305
|
sequential: sequential,
|
|
3845
4306
|
completedByChildren: completedByChildren,
|
|
3846
4307
|
repetition: buildRepetition(task),
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
4308
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
4309
|
+
|
|
4310
|
+
createdAt: (() => {
|
|
4311
|
+
try {
|
|
4312
|
+
return task.creationDate().toISOString();
|
|
4313
|
+
} catch (_e) {
|
|
4314
|
+
return new Date().toISOString();
|
|
4315
|
+
}
|
|
4316
|
+
})(),
|
|
4317
|
+
|
|
4318
|
+
modifiedAt: (() => {
|
|
4319
|
+
try {
|
|
4320
|
+
return task.modificationDate().toISOString();
|
|
4321
|
+
} catch (_e) {
|
|
4322
|
+
return new Date().toISOString();
|
|
4323
|
+
}
|
|
4324
|
+
})(),
|
|
3851
4325
|
};
|
|
3852
4326
|
}
|
|
3853
4327
|
|
|
@@ -3860,7 +4334,7 @@ function run(argv) {
|
|
|
3860
4334
|
|
|
3861
4335
|
throw new Error(\`Task not found: \${args.id}\`);
|
|
3862
4336
|
}
|
|
3863
|
-
`;var
|
|
4337
|
+
`;var tg=`/**
|
|
3864
4338
|
* JXA: fetch multiple tasks by IDs.
|
|
3865
4339
|
*
|
|
3866
4340
|
* Args (argv[0] JSON): { ids: string[] }
|
|
@@ -4001,10 +4475,23 @@ function run(argv) {
|
|
|
4001
4475
|
sequential: sequential,
|
|
4002
4476
|
completedByChildren: completedByChildren,
|
|
4003
4477
|
repetition: buildRepetition(task),
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4478
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
4479
|
+
|
|
4480
|
+
createdAt: (() => {
|
|
4481
|
+
try {
|
|
4482
|
+
return task.creationDate().toISOString();
|
|
4483
|
+
} catch (_e) {
|
|
4484
|
+
return new Date().toISOString();
|
|
4485
|
+
}
|
|
4486
|
+
})(),
|
|
4487
|
+
|
|
4488
|
+
modifiedAt: (() => {
|
|
4489
|
+
try {
|
|
4490
|
+
return task.modificationDate().toISOString();
|
|
4491
|
+
} catch (_e) {
|
|
4492
|
+
return new Date().toISOString();
|
|
4493
|
+
}
|
|
4494
|
+
})(),
|
|
4008
4495
|
};
|
|
4009
4496
|
}
|
|
4010
4497
|
|
|
@@ -4028,7 +4515,7 @@ function run(argv) {
|
|
|
4028
4515
|
|
|
4029
4516
|
return JSON.stringify({ tasks: results });
|
|
4030
4517
|
}
|
|
4031
|
-
`;var
|
|
4518
|
+
`;var ng=`/**
|
|
4032
4519
|
* JXA: list tasks, optionally filtered.
|
|
4033
4520
|
*
|
|
4034
4521
|
* Args (argv[0] JSON): {
|
|
@@ -4155,6 +4642,22 @@ function run(argv) {
|
|
|
4155
4642
|
blocked = task.blocked();
|
|
4156
4643
|
} catch (_e) {}
|
|
4157
4644
|
|
|
4645
|
+
// See #498 \u2014 JXA reports creationDate/modificationDate as truthy
|
|
4646
|
+
// functions even on tasks where invocation throws "Can't get object."
|
|
4647
|
+
// The call must be guarded, not just the property reference.
|
|
4648
|
+
let createdAt;
|
|
4649
|
+
try {
|
|
4650
|
+
createdAt = task.creationDate().toISOString();
|
|
4651
|
+
} catch (_e) {
|
|
4652
|
+
createdAt = new Date().toISOString();
|
|
4653
|
+
}
|
|
4654
|
+
let modifiedAt;
|
|
4655
|
+
try {
|
|
4656
|
+
modifiedAt = task.modificationDate().toISOString();
|
|
4657
|
+
} catch (_e) {
|
|
4658
|
+
modifiedAt = new Date().toISOString();
|
|
4659
|
+
}
|
|
4660
|
+
|
|
4158
4661
|
return {
|
|
4159
4662
|
id: task.id(),
|
|
4160
4663
|
name: task.name(),
|
|
@@ -4176,10 +4679,8 @@ function run(argv) {
|
|
|
4176
4679
|
sequential: sequential,
|
|
4177
4680
|
completedByChildren: completedByChildren,
|
|
4178
4681
|
repetition: buildRepetition(task),
|
|
4179
|
-
createdAt:
|
|
4180
|
-
modifiedAt:
|
|
4181
|
-
? task.modificationDate().toISOString()
|
|
4182
|
-
: new Date().toISOString(),
|
|
4682
|
+
createdAt: createdAt,
|
|
4683
|
+
modifiedAt: modifiedAt,
|
|
4183
4684
|
};
|
|
4184
4685
|
}
|
|
4185
4686
|
|
|
@@ -4254,7 +4755,7 @@ function run(argv) {
|
|
|
4254
4755
|
|
|
4255
4756
|
return JSON.stringify({ tasks: result });
|
|
4256
4757
|
}
|
|
4257
|
-
`;var
|
|
4758
|
+
`;var rg=`/**
|
|
4258
4759
|
* JXA: move a task to a different project or parent task.
|
|
4259
4760
|
*
|
|
4260
4761
|
* Args (argv[0] JSON): { id: string, projectId?: string|null, parentId?: string|null }
|
|
@@ -4293,7 +4794,7 @@ function run(argv) {
|
|
|
4293
4794
|
|
|
4294
4795
|
return JSON.stringify({ id: args.id });
|
|
4295
4796
|
}
|
|
4296
|
-
`;var
|
|
4797
|
+
`;var og=`/**
|
|
4297
4798
|
* JXA: search tasks by keyword and/or structured filters.
|
|
4298
4799
|
*
|
|
4299
4800
|
* Args (argv[0] JSON): {
|
|
@@ -4440,10 +4941,23 @@ function run(argv) {
|
|
|
4440
4941
|
completedByChildren,
|
|
4441
4942
|
estimatedMinutes: null,
|
|
4442
4943
|
repetition: buildRepetition(task),
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4944
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
4945
|
+
|
|
4946
|
+
createdAt: (() => {
|
|
4947
|
+
try {
|
|
4948
|
+
return task.creationDate().toISOString();
|
|
4949
|
+
} catch (_e) {
|
|
4950
|
+
return new Date().toISOString();
|
|
4951
|
+
}
|
|
4952
|
+
})(),
|
|
4953
|
+
|
|
4954
|
+
modifiedAt: (() => {
|
|
4955
|
+
try {
|
|
4956
|
+
return task.modificationDate().toISOString();
|
|
4957
|
+
} catch (_e) {
|
|
4958
|
+
return new Date().toISOString();
|
|
4959
|
+
}
|
|
4960
|
+
})(),
|
|
4447
4961
|
};
|
|
4448
4962
|
}
|
|
4449
4963
|
|
|
@@ -4512,7 +5026,7 @@ function run(argv) {
|
|
|
4512
5026
|
|
|
4513
5027
|
return JSON.stringify({ tasks: result });
|
|
4514
5028
|
}
|
|
4515
|
-
`;var
|
|
5029
|
+
`;var ag=`/**
|
|
4516
5030
|
* JXA: mark a task incomplete.
|
|
4517
5031
|
*
|
|
4518
5032
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -4541,7 +5055,7 @@ function run(argv) {
|
|
|
4541
5055
|
|
|
4542
5056
|
return JSON.stringify({ id: args.id });
|
|
4543
5057
|
}
|
|
4544
|
-
`;var
|
|
5058
|
+
`;var sg=`/**
|
|
4545
5059
|
* JXA: remove dropped status from a task.
|
|
4546
5060
|
*
|
|
4547
5061
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -4565,7 +5079,7 @@ function run(argv) {
|
|
|
4565
5079
|
|
|
4566
5080
|
return JSON.stringify({ id: args.id });
|
|
4567
5081
|
}
|
|
4568
|
-
`;var
|
|
5082
|
+
`;var ig=`/**
|
|
4569
5083
|
* JXA: update task fields.
|
|
4570
5084
|
*
|
|
4571
5085
|
* Args (argv[0] JSON): {
|
|
@@ -4712,10 +5226,23 @@ function run(argv) {
|
|
|
4712
5226
|
sequential: sequential,
|
|
4713
5227
|
completedByChildren: completedByChildren,
|
|
4714
5228
|
repetition: buildRepetition(task),
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
5229
|
+
// Guard against "Can't get object." thrown when invoking these \u2014 see #498.
|
|
5230
|
+
|
|
5231
|
+
createdAt: (() => {
|
|
5232
|
+
try {
|
|
5233
|
+
return task.creationDate().toISOString();
|
|
5234
|
+
} catch (_e) {
|
|
5235
|
+
return new Date().toISOString();
|
|
5236
|
+
}
|
|
5237
|
+
})(),
|
|
5238
|
+
|
|
5239
|
+
modifiedAt: (() => {
|
|
5240
|
+
try {
|
|
5241
|
+
return task.modificationDate().toISOString();
|
|
5242
|
+
} catch (_e) {
|
|
5243
|
+
return new Date().toISOString();
|
|
5244
|
+
}
|
|
5245
|
+
})(),
|
|
4719
5246
|
};
|
|
4720
5247
|
}
|
|
4721
5248
|
|
|
@@ -4767,29 +5294,1567 @@ function run(argv) {
|
|
|
4767
5294
|
|
|
4768
5295
|
return JSON.stringify({ task: buildTask(found) });
|
|
4769
5296
|
}
|
|
4770
|
-
`;var xi=new AsyncLocalStorage;function Ai(n,e){let t=ulid();return xi.run(t,n)}function ce(){return xi.getStore()}function Di(){return ulid()}function Su(n){let t=typeof n=="object"&&n!==null&&!Array.isArray(n)?JSON.stringify(n,Object.keys(n).sort()):JSON.stringify(n??null);return createHash("sha1").update(t).digest("hex").slice(0,16)}function Mt(n,e,t,r,o){j.debug({event:"transport.call",transport:n,scriptName:e,argsHash:Su(t),durationMs:r,outcome:o,correlationId:ce()},"transport call");}var ju=16*1024*1024,bu=(n,e,t)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-",e],{timeout:t,maxBuffer:ju,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,s,i)=>{let l=s,u=i,c=a!==null&&a.killed===true,h=a&&a.code==="ENOENT"?a:void 0;r({stdout:l,stderr:u,exitCode:a===null?0:a.code??1,timedOut:c,...h!==void 0?{spawnError:h}:{}});});o.stdin!==null&&o.stdin.end(n,"utf8");});async function T(n,e={},t={}){let r=t.spawner??bu,o=t.timeoutMs??3e4,a=JSON.stringify(e??{}),s=t.scriptName,i=performance.now(),l=await r(n,a,o),u=Math.round(performance.now()-i),c=l.spawnError!==void 0||l.timedOut||l.exitCode!==0||l.stdout.trim()===""?"error":"ok";if(Mt("jxa",s,e,u,c),l.spawnError!==void 0)throw new Oe("Failed to spawn osascript",{cause:l.spawnError,details:{transport:"jxa",reason:l.spawnError.code??"spawn-failed",...s!==void 0?{scriptName:s}:{}}});if(l.timedOut){let m=s!==void 0?` (script: ${s})`:"";throw new Pe(`JXA script exceeded ${o}ms timeout${m}`,{details:{transport:"jxa",timeoutMs:o,...s!==void 0?{scriptName:s}:{}}})}if(l.exitCode!==0){let m=Pu(l.stderr,s);if(m!==null)throw m;let g=s!==void 0?` [${s}]`:"";throw new L(`JXA script failed (exit ${l.exitCode})${g}`,{details:{transport:"jxa",exitCode:l.exitCode,stderr:ye(l.stderr,1024),...s!==void 0?{scriptName:s}:{}}})}let h=l.stdout.trim();if(h==="")throw new L("JXA script returned empty stdout \u2014 `run()` must return a JSON-encoded string",{details:{transport:"jxa",...s!==void 0?{scriptName:s}:{}}});try{return JSON.parse(h)}catch(m){throw new L("JXA script returned malformed JSON",{cause:m,details:{transport:"jxa",stdoutPreview:ye(h,200),...s!==void 0?{scriptName:s}:{}}})}}function Pu(n,e){return /Application can't be found/i.test(n)||/Application isn['’]t running/i.test(n)||/OmniFocus(?:.*)not running/i.test(n)?new we({details:{transport:"jxa",stderr:ye(n,512),...e!==void 0?{scriptName:e}:{}}}):/-1743\b/.test(n)||/not authori[sz]ed to send Apple events/i.test(n)||/errAEEventNotPermitted/i.test(n)||/not allowed assistive access/i.test(n)?new je({details:{transport:"jxa",stderr:ye(n,512),...e!==void 0?{scriptName:e}:{}}}):/\bnot found\b/i.test(n)||/^OF_NOT_FOUND\b/m.test(n)?new P(n,{details:{transport:"jxa",stderr:ye(n,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_VALIDATION\b/m.test(n)||/\bValidationError:/m.test(n)||/\bis required\b/i.test(n)?new I(n,{details:{transport:"jxa",stderr:ye(n,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_CONFLICT\b/m.test(n)?new be(n,{details:{transport:"jxa",stderr:ye(n,512),...e!==void 0?{scriptName:e}:{}}}):null}function ye(n,e){return n.length<=e?n:`${n.slice(0,e)}\u2026`}var Ft=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return (await T(Si,{projectId:e.projectId??null,tagId:e.tagId??null,parentId:e.parentId??null,flagged:e.flagged??null,available:e.available??null,blocked:e.blocked??null,completed:e.completed??null,completedSince:e.completedSince??null,dueBefore:e.dueBefore??null,dueAfter:e.dueAfter??null,deferredBefore:e.deferredBefore??null,deferredAfter:e.deferredAfter??null},{...this.runOpts,scriptName:"task_list"})).tasks.map(r=>({...r,id:f.of(r.id)}))}async getTask(e){let t=await T(yi,{id:e},{...this.runOpts,scriptName:"task_get"});return {...t.task,id:f.of(t.task.id)}}async getTasksMany(e){return (await T(vi,{ids:e},{...this.runOpts,scriptName:"task_get_many"})).tasks.map(r=>r?{...r,id:f.of(r.id)}:null)}async createTask(e){let t=await T(hi,{name:e.name,projectId:e.projectId??null,parentId:e.parentId??null,note:e.note??null,flagged:e.flagged??false,deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,tagIds:e.tagIds??[],sequential:e.sequential??false,completedByChildren:e.completedByChildren??false},{...this.runOpts,scriptName:"task_create"});return f.of(t.task.id)}async updateTask(e,t){await T(Oi,{id:e,...t.name!==void 0?{name:t.name}:{},...t.note!==void 0?{note:t.note}:{},...t.flagged!==void 0?{flagged:t.flagged}:{},...t.deferDate!==void 0?{deferDate:t.deferDate}:{},...t.dueDate!==void 0?{dueDate:t.dueDate}:{},...t.estimatedMinutes!==void 0?{estimatedMinutes:t.estimatedMinutes}:{},...t.tagIds!==void 0?{tagIds:t.tagIds}:{},...t.sequential!==void 0?{sequential:t.sequential}:{},...t.completedByChildren!==void 0?{completedByChildren:t.completedByChildren}:{}},{...this.runOpts,scriptName:"task_update"});}async completeTask(e,t){await T(gi,{id:e,completionDate:t?.toISOString()??null},{...this.runOpts,scriptName:"task_complete"});}async uncompleteTask(e){await T(bi,{id:e},{...this.runOpts,scriptName:"task_uncomplete"});}async dropTask(e,t){await T(ki,{id:e,droppedAt:t?.toISOString()??null},{...this.runOpts,scriptName:"task_drop"});}async undropTask(e){await T(Pi,{id:e},{...this.runOpts,scriptName:"task_undrop"});}async deleteTask(e){await T(Ii,{id:e},{...this.runOpts,scriptName:"task_delete"});}async moveTask(e,t){await T(wi,{id:e,projectId:t.projectId??null,parentId:t.parentId??null},{...this.runOpts,scriptName:"task_move"});}async batchMoveTasks(e){throw new L("batchMoveTasks routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"batchMoveTasks"}})}async reorderTask(e,t){throw new L("reorderTask routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"reorderTask"}})}async duplicateTask(e,t){let r=t.destination===void 0?void 0:"projectId"in t.destination?{projectId:t.destination.projectId}:"parentId"in t.destination?{parentId:t.destination.parentId}:{toInbox:true},o=await T(Ti,{id:e,recursive:t.recursive,destination:r},{...this.runOpts,scriptName:"task_duplicate"});return {newId:f.of(o.newId),descendantCount:o.descendantCount}}async batchCreateTasks(e){let t=await T(di,{inputs:e.map(r=>({name:r.name,projectId:r.projectId??null,parentId:r.parentId??null,note:r.note??null,flagged:r.flagged??false,deferDate:r.deferDate??null,dueDate:r.dueDate??null,estimatedMinutes:r.estimatedMinutes??null,tagIds:r.tagIds??[],sequential:r.sequential??false,completedByChildren:r.completedByChildren??false}))},{...this.runOpts,scriptName:"task_batch_create"});return Q(t,f.of)}async batchUpdateTasks(e){let t=await T(fi,{updates:e},{...this.runOpts,scriptName:"task_batch_update"});return Q(t,f.of)}async batchCompleteTasks(e){let t=await T(ci,{items:e.map(r=>({id:r.id,at:r.at?.toISOString()??null}))},{...this.runOpts,scriptName:"task_batch_complete"});return Q(t,f.of)}async batchUncompleteTasks(e){let t=await T(ui,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_uncomplete"});return Q(t,f.of)}async batchDeleteTasks(e){let t=await T(li,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_delete"});return Q(t,f.of)}async batchDropTasks(e){let t=await T(pi,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_drop"});return Q(t,f.of)}async batchUndropTasks(e){let t=await T(mi,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_undrop"});return Q(t,f.of)}async listProjects(e){return (await T(Xs,{folderId:e?.folderId??null,status:e?.status??null},{...this.runOpts,scriptName:"project_list"})).projects.map(r=>({...r,id:k.of(r.id)}))}async getProject(e){let t=await T(Vs,{id:e},{...this.runOpts,scriptName:"project_get"});return {...t.project,id:k.of(t.project.id)}}async getProjectsMany(e){return (await T(Ws,{ids:e},{...this.runOpts,scriptName:"project_get_many"})).projects.map(r=>r?{...r,id:k.of(r.id)}:null)}async createProject(e){let t=await T($s,{name:e.name,folderId:e.folderId??null,note:e.note??null,deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,status:e.status??null},{...this.runOpts,scriptName:"project_create"});return k.of(t.project.id)}async updateProject(e,t){await T(Zs,{id:e,...t.name!==void 0?{name:t.name}:{},...t.note!==void 0?{note:t.note}:{},...t.flagged!==void 0?{flagged:t.flagged}:{},...t.estimatedMinutes!==void 0?{estimatedMinutes:t.estimatedMinutes}:{},...t.deferDate!==void 0?{deferDate:t.deferDate}:{},...t.dueDate!==void 0?{dueDate:t.dueDate}:{},...t.status!==void 0?{status:t.status}:{}},{...this.runOpts,scriptName:"project_update"});}async completeProject(e,t){await T(Hs,{id:e,completionDate:t?.toISOString()??null},{...this.runOpts,scriptName:"project_complete"});}async dropProject(e){await T(qs,{id:e},{...this.runOpts,scriptName:"project_drop"});}async batchCompleteProjects(e){let t=await T(Bs,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_complete"});return Q(t,k.of)}async batchDropProjects(e){let t=await T(Gs,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_drop"});return Q(t,k.of)}async moveProject(e,t){await T(Ys,{id:e,folderId:t.folderId??null},{...this.runOpts,scriptName:"project_move"});}async deleteProject(e){await T(zs,{id:e},{...this.runOpts,scriptName:"project_delete"});}async markProjectReviewed(e){await T(Ks,{id:e},{...this.runOpts,scriptName:"project_mark_reviewed"});}async listProjectsDueForReview(){return (await T(ei,{},{...this.runOpts,scriptName:"review_list_due"})).projects}async setProjectReviewInterval(e,t){await T(Qs,{id:e,days:t},{...this.runOpts,scriptName:"project_set_review_interval"});}async listTags(e){return (await T(si,{parentId:e?.parentId??null,status:e?.status??null},{...this.runOpts,scriptName:"tag_list"})).tags.map(r=>({...r,id:y.of(r.id)}))}async getTag(e){let t=await T(oi,{id:e},{...this.runOpts,scriptName:"tag_get"});return {...t.tag,id:y.of(t.tag.id)}}async getTagsMany(e){return (await T(ai,{ids:e},{...this.runOpts,scriptName:"tag_get_many"})).tags.map(r=>r?{...r,id:y.of(r.id)}:null)}async createTag(e){let t=await T(ni,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"tag_create"});return y.of(t.tag.id)}async updateTag(e,t){await T(ii,{id:e,...t.name!==void 0?{name:t.name}:{},...t.status!==void 0?{status:t.status}:{},...t.allowsNextAction!==void 0?{allowsNextAction:t.allowsNextAction}:{}},{...this.runOpts,scriptName:"tag_update"});}async deleteTag(e){await T(ri,{id:e},{...this.runOpts,scriptName:"tag_delete"});}async listFolders(e){return (await T(Es,{parentId:e?.parentId??null},{...this.runOpts,scriptName:"folder_list"})).folders.map(r=>({...r,id:A.of(r.id)}))}async getFolder(e){let t=await T(Fs,{id:e},{...this.runOpts,scriptName:"folder_get"});return {...t.folder,id:A.of(t.folder.id)}}async createFolder(e){let t=await T(Rs,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"folder_create"});return A.of(t.folder.id)}async updateFolder(e,t){await T(Ns,{id:e,...t.name!==void 0?{name:t.name}:{}},{...this.runOpts,scriptName:"folder_update"});}async deleteFolder(e){await T(Ms,{id:e},{...this.runOpts,scriptName:"folder_delete"});}async listPerspectives(){return (await T(Js,{},{...this.runOpts,scriptName:"perspective_list"})).perspectives}async evaluatePerspective(e){return (await T(Ls,{perspectiveId:e},{...this.runOpts,scriptName:"perspective_evaluate"})).tasks.map(r=>({...r,id:f.of(r.id)}))}async evaluateCustomPerspective(e){throw new L("evaluateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluateCustomPerspective"}})}async searchTasks(e){return (await T(ji,{q:e.q??null,scope:e.scope??"all",projectId:e.projectId??null,tagIds:e.tagIds??null,available:e.available??null,dueBefore:e.dueBefore??null,dueAfter:e.dueAfter??null,flagged:e.flagged??null,completed:e.completed??"exclude"},{...this.runOpts,scriptName:"task_search"})).tasks.map(r=>({...r,id:f.of(r.id)}))}async getForecast(e){let t=await T(Us,{from:e.from,to:e.to,includeOverdue:e.includeOverdue??true,includeDeferred:e.includeDeferred??true,includeFlagged:e.includeFlagged??true},{...this.runOpts,scriptName:"forecast_get"});return {overdue:t.overdue.map(r=>({...r,id:f.of(r.id)})),dueToday:t.dueToday.map(r=>({...r,id:f.of(r.id)})),deferredToday:t.deferredToday.map(r=>({...r,id:f.of(r.id)})),flagged:t.flagged.map(r=>({...r,id:f.of(r.id)}))}}async listAttachments(e){return (await T(xs,e,{...this.runOpts,scriptName:"attachment_list"})).attachments}async addAttachment(e){let t=await T(_s,e,{...this.runOpts,scriptName:"attachment_add"});return oe.of(t.id)}async removeAttachment(e){await T(As,e,{...this.runOpts,scriptName:"attachment_remove"});}async saveAttachmentToPath(e){return T(Ds,e,{...this.runOpts,scriptName:"attachment_save_to_path"})}async appLaunch(){return T(Os,{},{...this.runOpts,scriptName:"app_launch"})}async pluginInvoke(e){throw new L("pluginInvoke is handled by OmniJsTransport; JxaTransport is not in the routing path for this method",{details:{transport:"jxa",reason:"routed-to-omnijs"}})}async syncTrigger(){return await T(ti,{},{...this.runOpts,scriptName:"sync_trigger"})}async getLastSync(){return {lastSyncAt:null,inFlight:false}}async getChangesSince(e){let t=await T(Cs,{sinceIso:e},{...this.runOpts,scriptName:"changes_since"});return {taskIds:t.tasks.map(r=>r.id),projectIds:t.projects.map(r=>r.id)}}async runJxaScript(e,t){return T(e,t??{},{...this.runOpts,scriptName:"raw"})}};var _u=16*1024*1024,xu=(n,e,t)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-"],{timeout:t,maxBuffer:_u,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,s,i)=>{let l=s,u=i,c=a!==null&&a.killed===true,h=a&&a.code==="ENOENT"?a:void 0;r({stdout:l,stderr:u,exitCode:a===null?0:a.code??1,timedOut:c,...h!==void 0?{spawnError:h}:{}});});o.stdin!==null&&o.stdin.end(n,"utf8");});async function ve(n,e={},t={}){let r=t.spawner??xu,o=t.timeoutMs??45e3,a=JSON.stringify(e??{}),s=t.scriptName,i=Au(n,a),l=performance.now(),u=await r(i,a,o),c=Math.round(performance.now()-l),h=u.spawnError!==void 0||u.timedOut||u.exitCode!==0||u.stdout.trim()===""?"error":"ok";if(Mt("omnijs",s,e,c,h),u.spawnError!==void 0)throw new Oe("Failed to spawn osascript",{cause:u.spawnError,details:{transport:"omnijs",reason:u.spawnError.code??"spawn-failed",...s!==void 0?{scriptName:s}:{}}});if(u.timedOut){let g=s!==void 0?` (script: ${s})`:"";throw new Pe(`OmniJS script exceeded ${o}ms timeout${g}`,{details:{transport:"omnijs",timeoutMs:o,...s!==void 0?{scriptName:s}:{}}})}if(u.exitCode!==0){let g=Du(u.stderr,s);if(g!==null)throw g;let b=s!==void 0?` [${s}]`:"";throw new L(`OmniJS script failed (exit ${u.exitCode})${b}`,{details:{transport:"omnijs",exitCode:u.exitCode,stderr:Et(u.stderr,1024),...s!==void 0?{scriptName:s}:{}}})}let m=u.stdout.trim();if(m==="")throw new L("OmniJS script returned empty stdout \u2014 the IIFE must return a JSON-encoded string",{details:{transport:"omnijs",...s!==void 0?{scriptName:s}:{}}});try{return JSON.parse(m)}catch(g){throw new L("OmniJS script returned malformed JSON",{cause:g,details:{transport:"omnijs",stdoutPreview:Et(m,200),...s!==void 0?{scriptName:s}:{}}})}}function Au(n,e){let t=`globalThis.__args = ${e};
|
|
4771
|
-
|
|
4772
|
-
`)}function Du(n,e){return /Application can't be found/i.test(n)||/Application isn['’]t running/i.test(n)||/OmniFocus(?:.*)not running/i.test(n)?new we({details:{transport:"omnijs",stderr:Et(n,512),...e!==void 0?{scriptName:e}:{}}}):/-1743\b/.test(n)||/not authori[sz]ed to send Apple events/i.test(n)||/errAEEventNotPermitted/i.test(n)||/not allowed assistive access/i.test(n)?new je({details:{transport:"omnijs",stderr:Et(n,512),...e!==void 0?{scriptName:e}:{}}}):null}function Et(n,e){return n.length<=e?n:`${n.slice(0,e)}\u2026`}function v(n){throw new L(`OmniJsTransport.${n} is not wired yet`,{details:{transport:"omnijs",reason:"not-yet-wired",method:n}})}var Nt=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return v("listTasks")}async getTask(e){return v("getTask")}async getTasksMany(e){return v("getTasksMany")}async createTask(e){return v("createTask")}async updateTask(e,t){return v("updateTask")}async completeTask(e,t){return v("completeTask")}async uncompleteTask(e){return v("uncompleteTask")}async dropTask(e,t){return v("dropTask")}async undropTask(e){return v("undropTask")}async deleteTask(e){return v("deleteTask")}async moveTask(e,t){let r=await import('fs/promises').then(a=>a.readFile(join(import.meta.dirname,"../../scripts/omnijs/task_move.js"),"utf8")),o=await ve(r,{id:e,projectId:t.projectId??null,parentId:t.parentId??null},{...this.runOpts,scriptName:"task_move"});if(ot(o))throw o.error.code==="NOT_FOUND"?new P(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}}):new I(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}})}async batchMoveTasks(e){let t=await import('fs/promises').then(o=>o.readFile(join(import.meta.dirname,"../../scripts/omnijs/task_batch_move.js"),"utf8")),r=await ve(t,{items:e.map(o=>({id:o.id,projectId:o.destination.projectId??null,parentId:o.destination.parentId??null}))},{...this.runOpts,scriptName:"task_batch_move"});if(ot(r))throw new I(r.error.message,{details:{transport:"omnijs",scriptName:"task_batch_move"}});return Q(r,f.of)}async reorderTask(e,t){let r=await import('fs/promises').then(s=>s.readFile(join(import.meta.dirname,"../../scripts/omnijs/task_reorder.js"),"utf8")),o;if("before"in t)o={id:e,mode:"before",refId:t.before};else if("after"in t)o={id:e,mode:"after",refId:t.after};else {let s="projectId"in t.in?{projectId:t.in.projectId}:"parentId"in t.in?{parentId:t.in.parentId}:{inbox:true};o={id:e,mode:t.at,container:s};}let a=await ve(r,o,{...this.runOpts,scriptName:"task_reorder"});if(ot(a))throw a.error.code==="NOT_FOUND"?new P(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}}):new I(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}})}async duplicateTask(e,t){return v("duplicateTask")}async batchCreateTasks(e){return v("batchCreateTasks")}async batchUpdateTasks(e){return v("batchUpdateTasks")}async batchCompleteTasks(e){return v("batchCompleteTasks")}async batchUncompleteTasks(e){return v("batchUncompleteTasks")}async batchDeleteTasks(e){return v("batchDeleteTasks")}async batchDropTasks(e){return v("batchDropTasks")}async batchUndropTasks(e){return v("batchUndropTasks")}async listProjects(e){return v("listProjects")}async getProject(e){return v("getProject")}async getProjectsMany(e){return v("getProjectsMany")}async createProject(e){return v("createProject")}async updateProject(e,t){return v("updateProject")}async completeProject(e,t){return v("completeProject")}async dropProject(e,t){return v("dropProject")}async batchCompleteProjects(e){return v("batchCompleteProjects")}async batchDropProjects(e){return v("batchDropProjects")}async moveProject(e,t){return v("moveProject")}async deleteProject(e){return v("deleteProject")}async markProjectReviewed(e){return v("markProjectReviewed")}async listProjectsDueForReview(){return v("listProjectsDueForReview")}async setProjectReviewInterval(e,t){return v("setProjectReviewInterval")}async listTags(e){return v("listTags")}async getTag(e){return v("getTag")}async getTagsMany(e){return v("getTagsMany")}async createTag(e){return v("createTag")}async updateTag(e,t){return v("updateTag")}async deleteTag(e){return v("deleteTag")}async listFolders(e){return v("listFolders")}async getFolder(e){return v("getFolder")}async createFolder(e){return v("createFolder")}async updateFolder(e,t){return v("updateFolder")}async deleteFolder(e){return v("deleteFolder")}async searchTasks(e){return v("searchTasks")}async getForecast(e){return v("getForecast")}async listAttachments(e){return v("listAttachments")}async addAttachment(e){return v("addAttachment")}async removeAttachment(e){return v("removeAttachment")}async saveAttachmentToPath(e){return v("saveAttachmentToPath")}async appLaunch(){return v("appLaunch")}async pluginInvoke(e){let t=await import('fs/promises').then(r=>r.readFile(join(import.meta.dirname,"../../scripts/omnijs/plugin_invoke.js"),"utf8"));return ve(t,{identifier:e.identifier,arg:e.arg??null},{...this.runOpts,scriptName:"plugin_invoke"})}async listPerspectives(){return v("listPerspectives")}async evaluatePerspective(e){return v("evaluatePerspective")}async evaluateCustomPerspective(e){let t=await import('fs/promises').then(o=>o.readFile(join(import.meta.dirname,"../../scripts/omnijs/perspective_evaluate.js"),"utf8")),r=await ve(t,{identifier:e},{...this.runOpts,scriptName:"perspective_evaluate"});if(ot(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new it(r.error.message,{details:{feature:"custom-perspectives"}}):new P(r.error.message,{details:{resource:"perspective",id:e}});return r.tasks.map(o=>({...o,id:f.of(o.id)}))}async syncTrigger(){return v("syncTrigger")}async getLastSync(){return v("getLastSync")}async getChangesSince(e){return {taskIds:[],projectIds:[]}}async runOmniJsScript(e,t){return ve(e,t??{},{...this.runOpts,scriptName:"raw"})}};var Ut=class extends EventEmitter{cache;inflight=new Map;hits=0;misses=0;coalesced=0;evictions=0;constructor({capacity:e=256,ttlMs:t=3e4}={}){super(),this.cache=new LRUCache({max:e,ttl:t,disposeAfter:()=>{this.evictions++;}});}async wrap(e,t){let r=this.cache.get(e);if(r!==void 0)return this.hits++,r.v;let o=this.inflight.get(e);if(o!==void 0)return this.coalesced++,await o.promise;this.misses++;let a=Symbol(e),s=(async()=>{try{let i=await t();return this.inflight.get(e)?.token===a&&this.cache.set(e,{v:i}),i}finally{this.inflight.get(e)?.token===a&&this.inflight.delete(e);}})();return this.inflight.set(e,{token:a,promise:s}),await s}invalidate(e){let t=e.endsWith(":*")?e.slice(0,-1):`${e}:`,r=[];for(let i of this.cache.keys())(i.startsWith(t)||i===e)&&r.push(i);for(let i of r)this.cache.delete(i);for(let i of this.inflight.keys())(i.startsWith(t)||i===e)&&this.inflight.delete(i);let o=r.length,a=ce(),s={event:"cache.invalidated",scopes:[e],evicted:o,...a!==void 0?{correlationId:a}:{}};o>0&&j.info(s,"cache.invalidated"),this.emit("cache.invalidated",s);}stats(){return {size:this.cache.size,hits:this.hits,misses:this.misses,evictions:this.evictions,coalesced:this.coalesced}}set(e,t){this.cache.set(e,{v:t});}has(e){return this.cache.has(e)}clear(){this.cache.clear(),this.inflight.clear();}};var Fu=["/System/","/private/System/","/Library/","/private/Library/"];async function ao(n,e){let t;try{t=await realpath(n);}catch{throw new I(`Attachment file not found or cannot be resolved: ${n}`,{suggestion:"Verify the file path is correct and the file exists.",details:{field:"filePath",value:n}})}let r=t.endsWith(sep)?t:t+sep;for(let a of Fu)if(r.startsWith(a))throw new I(`Attachment path resolves to a blocked system directory: ${t}`,{suggestion:"Attachment files must be under your home directory or an explicitly allowed path (OMNIFOCUS_ATTACHMENT_PATHS).",details:{field:"filePath",value:n,resolvedPath:t}});if(!e.some(a=>{let s=a.endsWith(sep)?a:a+sep;return r.startsWith(s)}))throw new I(`Attachment path is outside the allowed scope: ${t}`,{suggestion:"Move the file to your home directory, or add its parent directory to OMNIFOCUS_ATTACHMENT_PATHS (colon-separated list of absolute paths).",details:{field:"filePath",value:n,resolvedPath:t,allowedPaths:[...e]}})}var Ci=1024*1024;async function Ri(n,e){if(e<=0)return;let t;try{t=await stat(n);}catch{throw new I(`Attachment file not found or not accessible: ${n}`,{suggestion:"Verify the file path is correct and the file is readable.",details:{field:"filePath",value:n}})}let r=t.size/Ci;if(r>e)throw new I(`Attachment file exceeds the ${e} MB size cap (file is ${r.toFixed(2)} MB): ${n}`,{suggestion:`Reduce the file size to below ${e} MB, or increase the cap via the OMNIFOCUS_MAX_ATTACHMENT_MB environment variable.`,details:{field:"filePath",value:n,fileSizeBytes:t.size,capBytes:e*Ci}})}var Jt=class{adapter;allowedPaths;maxMb;constructor(e){this.adapter=e.adapter,this.allowedPaths=e.allowedPaths,this.maxMb=e.maxAttachmentMb;}async list(e){return this.adapter.listAttachments(e)}async add(e){return await ao(e.filePath,this.allowedPaths),await Ri(e.filePath,this.maxMb),this.adapter.addAttachment(e)}async remove(e){return this.adapter.removeAttachment(e)}async saveTo(e){let t=e.destPath.substring(0,e.destPath.lastIndexOf("/"))||"/";return await ao(t,this.allowedPaths),this.adapter.saveAttachmentToPath(e)}};async function Mi(n,e){let t=await n.listTasks({projectId:e}),r=[...t],o=[...t];for(;;){let a=o.shift();if(a===void 0)break;let s=await n.listTasks({parentId:a.id});for(let i of s)r.push(i),o.push(i);}return r}function Bt(n){let e=[],t=new Map;for(let r of n)if(r.parentId===null)e.push(r);else {let o=String(r.parentId),a=t.get(o);a?a.push(r):t.set(o,[r]);}return {rootTasks:e,byParent:t}}function te(n){return n.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}function Fi(n,e,t){let r=[`text="${te(n.name)}"`,'type="omnifocus:task"',`id="${te(String(n.id))}"`];n.dueDate&&r.push(`due="${te(n.dueDate)}"`),n.deferDate&&r.push(`defer="${te(n.deferDate)}"`),n.flagged&&r.push('flagged="true"'),n.completed&&r.push('completed="true"'),n.dropped&&r.push('dropped="true"'),n.note&&r.push(`note="${te(n.note)}"`);let o=e.get(String(n.id))??[];if(o.length===0)return `${t}<outline ${r.join(" ")} />`;let a=`${t} `,s=o.map(i=>Fi(i,e,a)).join(`
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
`);for(let g=0;g<m.length;g++){let b=m[g];if(!b)continue;let w=Gi(b),O=b.trimStart();if(!O.startsWith("- ")&&!O.startsWith("- ")&&O.endsWith(":")&&w===0){if(!t){let $=O.slice(0,-1).trim(),fe=i.get($.toLowerCase());fe?h=fe:(u.push(`Project "${$}" not found in OmniFocus \u2014 tasks will land in inbox`),h=void 0);}c.length=0;continue}if(!O.startsWith("- ")&&!O.startsWith("- "))continue;for(;c.length>0&&(c[c.length-1]?.depth??0)>=w;)c.pop();let J=O.slice(2).trim(),R=Bi(J,g+1,u),N=[];for(let $ of R.tagNames)try{N.push(await a($));}catch{u.push(`Line ${g+1}: could not create tag "${$}" \u2014 skipped`);}let _=c[c.length-1],x={name:R.name,..._?{parentId:_.id}:{},...h&&!_?{projectId:h}:{},...R.dueDate?{dueDate:R.dueDate}:{},...R.deferDate?{deferDate:R.deferDate}:{},...R.flagged?{flagged:true}:{},...R.note?{note:R.note}:{},...N.length>0?{tagIds:N}:{}},U=await this.adapter.createTask(x);l.push(U),R.done&&await this.adapter.completeTask(U),c.push({depth:w,id:U});}return {created:l,warnings:u}}async importOpml(e,t={}){if(!e.trim())throw new I("opml is empty",{suggestion:"Provide a non-empty OPML XML string."});let r=Li(e),o=await this.adapter.listProjects(),a=new Map(o.map(u=>[String(u.id),u.id])),s=new Map(o.map(u=>[u.name.toLowerCase(),u.id])),i=[],l=async(u,c,h)=>{for(let m of u){let g={name:m.text||"(untitled)",...h!==void 0?{parentId:h}:c!==void 0?{projectId:c}:{},...m.due!==void 0?{dueDate:m.due}:{},...m.defer!==void 0?{deferDate:m.defer}:{},...m.flagged===true?{flagged:true}:{}},b=await this.adapter.createTask(g);i.push(b),m.children.length>0&&await l(m.children,c,b);}};for(let u of r.body){if(t.destinationProjectId!==void 0){await l([u],t.destinationProjectId,void 0);continue}if(u.type==="omnifocus:project"){let c=(u.id!==void 0?a.get(u.id):void 0)??s.get(u.text.toLowerCase());await l(u.children,c,void 0);}else await l([u],void 0,void 0);}return {imported:i.length,taskIds:i}}};function Ht(n,e){n!==void 0&&Lo(n,{folderId:e});}var $t=class{adapter;cache;constructor({adapter:e,cache:t}){this.adapter=e,this.cache=t;}async list(e={}){return {folders:await this.adapter.listFolders(e.parentId!==void 0?{parentId:e.parentId}:{}),cacheHit:false}}async get(e){return {folder:await this.adapter.getFolder(e),cacheHit:false}}async create(e){let t=await this.adapter.createFolder(e);return Ht(this.cache,t),{id:t}}async update(e,t){await this.adapter.updateFolder(e,t),Ht(this.cache,e);}async delete(e,t=false){t&&await this._cascadeEmpty(e),await this.adapter.deleteFolder(e),Ht(this.cache,e);}async move(e,t){await this.adapter.updateFolder(e,{parentId:t}),Ht(this.cache,e);}async _cascadeEmpty(e){let t=await this.adapter.listProjects({folderId:e});await Promise.all(t.map(o=>this.adapter.moveProject(o.id,{folderId:null})));let r=await this.adapter.listFolders({parentId:e});for(let o of r)await this._cascadeEmpty(o.id),await this.adapter.deleteFolder(o.id);}};var zt=class{adapter;constructor(e){this.adapter=e.adapter;}async get(e){return {...await this.adapter.getForecast(e),cacheHit:false}}};function Lu(n){return Ct.includes(n)}var qt=class{adapter;constructor(e){this.adapter=e.adapter;}async list(){return {perspectives:await this.adapter.listPerspectives(),cacheHit:false}}async evaluate(e){return {tasks:Lu(e)?await this.adapter.evaluatePerspective(e):await this.adapter.evaluateCustomPerspective(e),cacheHit:false}}};var Hi=200,io=1e3,Vt=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async list(e){let t=this.resolveLimit(e);this.assertBounded(e);let r=this.normalize(e),o=Re(r),a=e.cursor!==void 0?Fe(e.cursor,o):void 0,s=this.listCacheKey(o,e.cursor),i=this.cache.has(s),{projects:l,nextCursor:u}=await this.cache.wrap(s,async()=>this.fetchPage(r,a,t,o));return {projects:l,nextCursor:u,hasMore:u!==null,cacheHit:i}}async completeProject(e){await this.adapter.completeProject(e);}async dropProject(e){await this.adapter.dropProject(e);}async moveProject(e,t){await this.adapter.moveProject(e,t);}async createProject(e){return this.adapter.createProject(e)}async updateProject(e,t){return this.adapter.updateProject(e,t)}async get(e){let t=e.includeTaskTree??true,r=this.getCacheKey(e.id,t),o=this.cache.has(r);return {...await this.cache.wrap(r,async()=>{let s=await this.adapter.getProject(e.id),i={...s,_links:Br(s)};if(!t)return {project:i};let u=(await this.adapter.listTasks({projectId:e.id})).map(c=>({...c,_links:ue(c)}));return {project:i,tasks:u}}),cacheHit:o}}async fetchPage(e,t,r,o){let a={};e.folderId!==void 0&&(a.folderId=e.folderId),e.status!==void 0&&(a.status=e.status);let s=await this.adapter.listProjects(a),{flagged:i,reviewDueBefore:l}=e,c=[...s.filter(O=>!(i!==void 0&&O.flagged!==i||l!==void 0&&(O.nextReviewDate===null||O.nextReviewDate>=l)))].sort((O,J)=>O.createdAt!==J.createdAt?O.createdAt<J.createdAt?-1:1:O.id<J.id?-1:1),h=t!==void 0?c.filter(O=>Ee({id:O.id,sortValue:O.createdAt},t,"asc")):c,m=h.slice(0,r),b=h.length>r?this.encodeNextCursor(m,o):null;return {projects:m.map(O=>({...O,_links:Br(O)})),nextCursor:b}}resolveLimit(e){if(e.limit===void 0)return Hi;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>io)throw new I(`limit must be an integer between 1 and ${io}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${io}, or omit to use the default of ${Hi}.`,details:{field:"limit",value:e.limit}});return e.limit}assertBounded(e){if(!(e.limit!==void 0||e.cursor!==void 0)&&!this.hasAnyFilter(e))throw new I("project_list requires at least one filter, limit, or cursor. Unbounded queries are rejected.",{suggestion:"Provide a filter or a limit.",details:{field:"filter|limit|cursor"}})}hasAnyFilter(e){return e.folderId!==void 0||e.status!==void 0||e.flagged!==void 0||e.reviewDueBefore!==void 0}normalize(e){return {folderId:e.folderId,status:e.status,flagged:e.flagged,reviewDueBefore:e.reviewDueBefore}}listCacheKey(e,t){return `search:projects:${e}:${t??"first"}`}getCacheKey(e,t){return `project:${e}:${t?"with-tasks":"solo"}`}encodeNextCursor(e,t){let r=e[e.length-1];if(r===void 0)throw new I("Internal: cannot encode cursor for empty page.");return Me({lastId:r.id,lastSortValue:r.createdAt,filterHash:t})}};var Wt=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async listDue(){return {projects:await this.adapter.listProjectsDueForReview(),cacheHit:false}}async markReviewed(e){await this.adapter.markProjectReviewed(e),this.cache!==void 0&&M(this.cache,{projectId:e});}async setInterval(e,t){await this.adapter.setProjectReviewInterval(e,t),this.cache!==void 0&&M(this.cache,{projectId:e});}};var Ju=100,Bu=500,Xt=class{adapter;constructor({adapter:e}){this.adapter=e;}async search(e){let t=Math.min(e.limit??Ju,Bu),r={...e.q!==void 0?{q:e.q}:{},scope:e.scope??"all",...e.projectId!==void 0?{projectId:e.projectId}:{},...e.tagIds!==void 0?{tagIds:[...e.tagIds].sort()}:{},...e.available!==void 0?{available:e.available}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.dueAfter!==void 0?{dueAfter:e.dueAfter}:{},...e.flagged!==void 0?{flagged:e.flagged}:{},...e.completed!==void 0?{completed:e.completed}:{}},o=Re(r),a=null;e.cursor&&(a=Fe(e.cursor,o));let s={...e.q!==void 0?{q:e.q}:{},...e.scope!==void 0?{scope:e.scope}:{},...e.projectId!==void 0?{projectId:e.projectId}:{},...e.tagIds!==void 0?{tagIds:e.tagIds}:{},...e.available!==void 0?{available:e.available}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.dueAfter!==void 0?{dueAfter:e.dueAfter}:{},...e.flagged!==void 0?{flagged:e.flagged}:{},...e.completed!==void 0?{completed:e.completed}:{}},l=[...await this.adapter.searchTasks(s)].sort((w,O)=>{let J=w.createdAt.localeCompare(O.createdAt);return J!==0?J:w.id.localeCompare(O.id)}),c=(a?l.filter(w=>Ee({id:w.id,sortValue:w.createdAt},a,"asc")):l).slice(0,t+1),h=c.length>t,m=c.slice(0,t).map(w=>({...w,_links:ue(w)})),g=m.at(-1),b=h&&g?Me({lastId:g.id,lastSortValue:g.createdAt,filterHash:o}):null;return {tasks:m,nextCursor:b,hasMore:h,cacheHit:false}}};function me(n,e){n!==void 0&&Uo(n,{tagId:e});}var Kt=class{adapter;cache;constructor({adapter:e,cache:t}){this.adapter=e,this.cache=t;}async list(e={}){return {tags:await this.adapter.listTags({...e.parentId!==void 0?{parentId:e.parentId}:{},...e.status!==void 0?{status:e.status}:{}}),cacheHit:false}}async get(e){return {tag:await this.adapter.getTag(e),cacheHit:false}}async create(e){let t=await this.adapter.createTag(e);return me(this.cache,t),{id:t}}async update(e,t){await this.adapter.updateTag(e,t),me(this.cache,e);}async delete(e){await this.adapter.deleteTag(e),me(this.cache,e);}async move(e,t){await this.adapter.updateTag(e,{parentId:t}),me(this.cache,e);}async setStatus(e,t){await this.adapter.updateTag(e,{status:t}),me(this.cache,e);}async setAllowsNextAction(e,t){await this.adapter.updateTag(e,{allowsNextAction:t}),me(this.cache,e);}async setLocation(e,t){await this.adapter.updateTag(e,{location:t}),me(this.cache,e);}async clearLocation(e){await this.adapter.updateTag(e,{location:null}),me(this.cache,e);}async getLocation(e){return {location:(await this.adapter.getTag(e)).location,cacheHit:false}}};function $i(n){if(n.OMNIFOCUS_E2E_USE_MEMORY){let r=new Rt;return new Le({jxa:r,omnijs:r})}let e=new Ft({timeoutMs:n.OMNIFOCUS_JXA_TIMEOUT_MS}),t=new Nt({timeoutMs:n.OMNIFOCUS_OMNIJS_TIMEOUT_MS});return Le.fromTransports(e,t)}function zi(n,e){let t=new Ut({capacity:e.OMNIFOCUS_CACHE_CAPACITY,ttlMs:e.OMNIFOCUS_CACHE_TTL_MS});return {cache:t,taskService:new At({adapter:n,cache:t}),projectService:new Vt({adapter:n,cache:t}),tagService:new Kt({adapter:n,cache:t}),folderService:new $t({adapter:n,cache:t}),attachmentService:new Jt({adapter:n,allowedPaths:e.OMNIFOCUS_ATTACHMENT_PATHS,maxAttachmentMb:e.OMNIFOCUS_MAX_ATTACHMENT_MB}),exportService:new Gt({adapter:n}),forecastService:new zt({adapter:n}),perspectiveService:new qt({adapter:n}),pluginService:new Ae({adapter:n}),reviewService:new Wt({adapter:n,cache:t}),searchService:new Xt({adapter:n})}}var Gu="jxa",Hu="unknown";function C(n={}){return {correlationId:ce()??Di(),durationMs:0,cacheHit:false,transport:Gu,ofVersion:Hu,...n}}function qi(n){let{adapter:e,cache:t,server:r,aggregateUris:o}=n;return async a=>{let s=new Date(a.detectedAt).getTime()-200,i=new Date(s).toISOString(),l={taskIds:[],projectIds:[]},u=false;try{l=await e.getChangesSince(i),u=!0;}catch(c){j.debug({event:"database.changed.query_failed",err:c});}if(u&&(l.taskIds.length>0||l.projectIds.length>0)){for(let c of l.taskIds)t.invalidate(`task:${c}`);for(let c of l.projectIds)t.invalidate(`project:${c}`);}else t.clear();for(let c of l.taskIds)r.server.sendResourceUpdated({uri:`omnifocus://task/${c}`}).catch(()=>{});for(let c of l.projectIds)r.server.sendResourceUpdated({uri:`omnifocus://project/${c}`}).catch(()=>{});for(let c of o)r.server.sendResourceUpdated({uri:c}).catch(()=>{});j.debug({event:"database.changed",source:a.source,detectedAt:a.detectedAt,changedTasks:l.taskIds.length,changedProjects:l.projectIds.length,cacheStrategy:u?"targeted":"full-clear"});}}async function Vi(n,e){let t=performance.now();try{let r=await e(),o=Math.round(performance.now()-t);return j.info({event:"tool.invoked",tool:n,correlationId:ce(),durationMs:o,transport:r.meta.transport,cacheHit:r.meta.cacheHit},"tool invoked"),r}catch(r){let o=Math.round(performance.now()-t),a=uo(r)?r.code:"UNKNOWN";throw j.warn({event:"tool.error",tool:n,correlationId:ce(),durationMs:o,code:a,err:r},"tool error"),r}}function Wi(n,e,t,r){let o=t.record(n,e);if(o!==void 0){let{level:a,warning:s}=o,i=s.details?.count??0,l=(s.details?.windowSeconds??60)*1e3;if(j.warn({event:"loop.detected",tool:n,callCount:i,windowMs:l,level:a}),a==="error")return Promise.reject(new pt(n,i,s.details?.windowSeconds??60))}return r().then(a=>{if(o===void 0)return a;let s=a.meta.warnings??[];return {...a,meta:{...a.meta,warnings:[...s,o.warning]}}})}function Xi(n,e,t){return e.check(n),t().then(r=>{let o=e.remaining(n);return {...r,meta:{...r.meta,rateLimit:o}}})}function $u(n,e,t){return (r,o)=>Ai(async()=>(t.shutdown.assertNotShuttingDown(),t.circuitRegistry.get(n).call(async()=>{let s=async()=>(await e(r,o)).structuredContent,i=await Xi(n,t.rateLimiter,()=>Wi(n,r,t.loopDetector,()=>Vi(n,s)));return p(i)})))}function Ki(n,e){let t=n;if(t.__omnifocusMiddlewareInstalled===true)return;let r=n.registerTool.bind(n),o=(...a)=>{let[s,i,l]=a,u=$u(s,l,e);return r(s,i,u)};n.registerTool=o,t.__omnifocusMiddlewareInstalled=true;}var zu=5e3,qu=1e4,Vu=50,co=class{_shuttingDown=false;_queues=[];_readGraceMs;_writeGraceMs;constructor(e={}){this._readGraceMs=e.readGraceMs??Number(process.env.OMNIFOCUS_READ_GRACE_MS??zu),this._writeGraceMs=e.writeGraceMs??Number(process.env.OMNIFOCUS_WRITE_GRACE_MS??qu);}get isShuttingDown(){return this._shuttingDown}assertNotShuttingDown(){if(this._shuttingDown)throw new lt}registerQueue(e){this._queues.includes(e)||this._queues.push(e);}async initiate(e,t=process.exit){if(this._shuttingDown)return;this._shuttingDown=true,j.info({event:"server.shutdown",reason:e,readGraceMs:this._readGraceMs,writeGraceMs:this._writeGraceMs},"graceful shutdown initiated");let r=this._readGraceMs+this._writeGraceMs;await this._drainAll(r),j.flush(),t(0);}async _drainAll(e){if(this._queues.length===0)return;let t=Date.now()+e;for(;Date.now()<t;){if(this._queues.filter(a=>a.pendingCount()>0).length===0)return;await new Promise(a=>setTimeout(a,Vu));}let r=this._queues.filter(o=>o.pendingCount()>0);for(let o of r)j.warn({event:"server.shutdown.drain_timeout",queue:o.name,pending:o.pendingCount()},"queue did not drain within grace window; forcing shutdown");}},Se=new co;var Wu=["@modelcontextprotocol/sdk","node_modules/@modelcontextprotocol"];function Xu(n){return Wu.some(e=>n.includes(e))}var Yi=null,Qi=false;function Zi(){if(Qi)return;Yi=process.stdout.write,Qi=true;let n=Yi;process.stdout.write=function(t,r,o){let a=new Error().stack??"";if(Xu(a))return typeof r=="function"?n.call(process.stdout,t,r):n.call(process.stdout,t,r,o);let s=typeof t=="string"?t.slice(0,120):`<${t.byteLength} bytes>`;throw new E("OF_STRAY_STDOUT",`Stray stdout write detected \u2014 stdout is reserved for MCP transport. Use process.stderr / logger for diagnostics. Attempted: ${JSON.stringify(s)}`,{suggestion:"Replace console.log / process.stdout.write with process.stderr.write or logger."})};}var ec=re.version,Qu=re.name.split("/").pop()??"omnifocus-mcp",Zu=new Set(["run_jxa_script","run_omnijs_script"]),em=Date.now();function tm(){return new McpServer({name:Qu,version:ec})}async function tc(){Zi();let n=mo();j.level=n.OMNIFOCUS_LOG_LEVEL;let e=tm(),t=new mt(n.OMNIFOCUS_TOOL_RATE_LIMIT),r=new ut;Ki(e,{rateLimiter:t,loopDetector:r,circuitRegistry:oo,shutdown:Se});let o=new StdioServerTransport,a=$i(n),s=new st({size:n.OMNIFOCUS_READ_POOL_SIZE,name:"jxa-read"}),i=new Be({cap:n.OMNIFOCUS_WRITE_QUEUE_CAP,name:"jxa-write"}),l=new Be({cap:n.OMNIFOCUS_WRITE_QUEUE_CAP,name:"omnijs"});Se.registerQueue(s),Se.registerQueue(i),Se.registerQueue(l);let u=po(a,{readPool:s,jxaWriteQueue:i,omniJsQueue:l}),c=zi(u,n);qo(e,{startedAt:em,adapter:u,circuitRegistry:oo,makeMeta:C}),ho(e),ko(e,()=>Io(n)),vo(e,{adapter:u,projectService:c.projectService,reviewService:c.reviewService,forecastService:c.forecastService,perspectiveService:c.perspectiveService});let h={folderService:c.folderService,makeMeta:C};_o(e,h),Ao(e,h),Do(e,h),Co(e,h),Ro(e,h),Fo(e,h);let m={tagService:c.tagService,makeMeta:C};ja(e,m),ba(e,m),xa(e,{adapter:u,makeMeta:C}),Pa(e,m),Oa(e,m),Da(e,m),Ca(e,m),Ma(e,m),Fa(e,m),Na(e,m),Ua(e,m);let g={adapter:u,makeMeta:C,cache:c.cache};Bo(e,g),Go(e,g),Ho(e,g),$o(e,g),zo(e,g),va(e,{searchService:c.searchService,makeMeta:C}),No(e,{forecastService:c.forecastService,makeMeta:C});let b={perspectiveService:c.perspectiveService,makeMeta:C};Xo(e,b),Wo(e,b),Ko(e,{adapter:u,makeMeta:C}),Sa(e,{adapter:u,makeMeta:C}),wa(e,{adapter:u,makeMeta:C,cache:c.cache});let w={reviewService:c.reviewService,makeMeta:C};ga(e,w),Ia(e,w),Ta(e,w),ya(e,w);let O={exportService:c.exportService,makeMeta:C};jo(e,O),bo(e,O),Po(e,O),So(e,{adapter:u,makeMeta:C});let J={projectService:c.projectService,makeMeta:C,cache:c.cache},R={adapter:u,makeMeta:C,cache:c.cache};Yo(e,R),Qo(e,R),Zo(e,J),ra(e,R),oa(e,R),aa(e,J),da(e,{adapter:u,makeMeta:C}),ia(e,{projectService:c.projectService,makeMeta:C}),la(e,{projectService:c.projectService,makeMeta:C}),pa(e,J),ua(e,R);let N={taskService:c.taskService,makeMeta:C},_={adapter:u,makeMeta:C},x={adapter:u,makeMeta:C,cache:c.cache};os(e,N),ls(e,N),ns(e,_),Ts(e,{searchService:c.searchService,makeMeta:C}),ss(e,_),hs(e,{makeMeta:C}),La(e,x),Ja(e,x),Ba(e,x),Ga(e,x),Ha(e,x),$a(e,x),za(e,x),qa(e,x),Va(e,x),Xa(e,x),es(e,x),ts(e,x),us(e,x),Is(e,x),vs(e,x),Ss(e,x),ws(e,x),Ya(e,x),Qa(e,x),bs(e,x),wo(e,{attachmentService:c.attachmentService,makeMeta:C});let U={adapter:u,makeMeta:C},$={allowRawScript:n.OMNIFOCUS_ALLOW_RAW_SCRIPT};ma(e,U,$),fa(e,U,$),process.on("SIGINT",()=>{Se.initiate("SIGINT");}),process.on("SIGTERM",()=>{Se.initiate("SIGTERM");}),process.on("unhandledRejection",ge=>{j.fatal({event:"server.unhandled_rejection",reason:ge},"unhandled rejection"),j.flush(),process.exit(1);}),process.on("uncaughtException",ge=>{j.fatal({event:"server.uncaught_exception",err:ge},"uncaught exception"),j.flush(),process.exit(1);}),await e.connect(o);let fe=qi({adapter:u,cache:c.cache,server:e,aggregateUris:[ze,qe,Ve,We,Xe,Ke]}),lo=new Dt(ge=>{fe(ge).catch(rc=>{j.error({event:"database.changed.handler_error",err:rc});});});lo.start(),process.on("exit",()=>lo.stop());let nc=Object.keys(Ps).filter(ge=>n.OMNIFOCUS_ALLOW_RAW_SCRIPT||!Zu.has(ge)).sort();j.info({event:"server.started",version:ec,config:fo(n),tools:nc,prompts:[Qt,Zt,en,tn],resources:[ft,ze,qe,Ve,We,Xe,Ke,rn,on,an]},"server started");}var Yt=process.argv.slice(2);(Yt.includes("--version")||Yt.includes("-v"))&&(process.stdout.write(`${re.version}
|
|
4785
|
-
|
|
4786
|
-
|
|
5297
|
+
`;var cg=`/**
|
|
5298
|
+
* JXA: read the current state of OmniFocus's front window.
|
|
5299
|
+
*
|
|
5300
|
+
* Returns JSON: { perspectiveName, focusContainerIds }
|
|
5301
|
+
* - \`perspectiveName\` \u2014 name of the active perspective (e.g. "Forecast")
|
|
5302
|
+
* - \`focusContainerIds[]\` \u2014 IDs of projects/folders the window is focused on
|
|
5303
|
+
* (empty array when nothing is focused)
|
|
5304
|
+
* or { error: { code: "NO_FRONT_WINDOW", message } } when OF has no front window
|
|
5305
|
+
*
|
|
5306
|
+
* @see #466
|
|
5307
|
+
* @see src/adapter/jxa/JxaTransport.ts \u2014 getWindowState() caller
|
|
5308
|
+
*/
|
|
5309
|
+
|
|
5310
|
+
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run() by convention.
|
|
5311
|
+
function run(_argv) {
|
|
5312
|
+
const ofApp = Application("OmniFocus");
|
|
5313
|
+
ofApp.includeStandardAdditions = false;
|
|
5314
|
+
|
|
5315
|
+
const wins = ofApp.windows();
|
|
5316
|
+
if (!wins || wins.length === 0) {
|
|
5317
|
+
return JSON.stringify({
|
|
5318
|
+
error: { code: "NO_FRONT_WINDOW", message: "OmniFocus has no front window" },
|
|
5319
|
+
});
|
|
5320
|
+
}
|
|
5321
|
+
|
|
5322
|
+
const w = wins[0];
|
|
5323
|
+
let perspectiveName = null;
|
|
5324
|
+
try {
|
|
5325
|
+
perspectiveName = String(w.perspectiveName());
|
|
5326
|
+
} catch (_e) {
|
|
5327
|
+
perspectiveName = null;
|
|
5328
|
+
}
|
|
5329
|
+
|
|
5330
|
+
// Focus is a (possibly empty) array of containers (sections / projects /
|
|
5331
|
+
// folders). We surface the IDs in input order; \`[]\` means nothing is focused.
|
|
5332
|
+
const focusContainerIds = [];
|
|
5333
|
+
try {
|
|
5334
|
+
const focus = w.focus();
|
|
5335
|
+
if (focus?.length) {
|
|
5336
|
+
for (let i = 0; i < focus.length; i++) {
|
|
5337
|
+
try {
|
|
5338
|
+
focusContainerIds.push(String(focus[i].id()));
|
|
5339
|
+
} catch (_e) {
|
|
5340
|
+
// Skip containers we can't introspect (defensive \u2014 shouldn't happen).
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
} catch (_e) {
|
|
5345
|
+
// No focus set \u2014 leave focusContainerIds as []
|
|
5346
|
+
}
|
|
5347
|
+
|
|
5348
|
+
return JSON.stringify({ perspectiveName, focusContainerIds });
|
|
5349
|
+
}
|
|
5350
|
+
`;var dg=`/**
|
|
5351
|
+
* JXA: set or clear the front window's focus container.
|
|
5352
|
+
*
|
|
5353
|
+
* Args (argv[0] JSON): { containerId: string | null }
|
|
5354
|
+
* - \`containerId\` non-null \u2192 set focus to that project or folder
|
|
5355
|
+
* - \`containerId\` null \u2192 clear focus (window shows the perspective's
|
|
5356
|
+
* default scope)
|
|
5357
|
+
*
|
|
5358
|
+
* Returns JSON: { focusContainerIds }
|
|
5359
|
+
* - When set: array containing the supplied containerId (so callers can
|
|
5360
|
+
* verify in one round-trip)
|
|
5361
|
+
* - When cleared: empty array
|
|
5362
|
+
* or { error: { code: "NO_FRONT_WINDOW" | "NOT_FOUND", message } }
|
|
5363
|
+
*
|
|
5364
|
+
* Container resolution: tries projects first, then folders. If the same ID
|
|
5365
|
+
* matches both (shouldn't happen but defensive), the project wins.
|
|
5366
|
+
*
|
|
5367
|
+
* @see #466
|
|
5368
|
+
* @see src/adapter/jxa/JxaTransport.ts \u2014 setWindowFocus() caller
|
|
5369
|
+
*/
|
|
5370
|
+
|
|
5371
|
+
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
|
|
5372
|
+
function run(argv) {
|
|
5373
|
+
const args = JSON.parse(argv[0]);
|
|
5374
|
+
const ofApp = Application("OmniFocus");
|
|
5375
|
+
ofApp.includeStandardAdditions = false;
|
|
5376
|
+
|
|
5377
|
+
const wins = ofApp.windows();
|
|
5378
|
+
if (!wins || wins.length === 0) {
|
|
5379
|
+
return JSON.stringify({
|
|
5380
|
+
error: { code: "NO_FRONT_WINDOW", message: "OmniFocus has no front window" },
|
|
5381
|
+
});
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5384
|
+
const w = wins[0];
|
|
5385
|
+
|
|
5386
|
+
if (args.containerId === null || args.containerId === undefined) {
|
|
5387
|
+
// Clear focus \u2014 assigning an empty array unfocuses the window.
|
|
5388
|
+
w.focus = [];
|
|
5389
|
+
return JSON.stringify({ focusContainerIds: [] });
|
|
5390
|
+
}
|
|
5391
|
+
|
|
5392
|
+
// Resolve to a project or folder.
|
|
5393
|
+
const doc = ofApp.defaultDocument;
|
|
5394
|
+
let target = null;
|
|
5395
|
+
|
|
5396
|
+
const projects = doc.flattenedProjects();
|
|
5397
|
+
for (let i = 0; i < projects.length; i++) {
|
|
5398
|
+
if (projects[i].id() === args.containerId) {
|
|
5399
|
+
target = projects[i];
|
|
5400
|
+
break;
|
|
5401
|
+
}
|
|
5402
|
+
}
|
|
5403
|
+
|
|
5404
|
+
if (!target) {
|
|
5405
|
+
const folders = doc.flattenedFolders();
|
|
5406
|
+
for (let i = 0; i < folders.length; i++) {
|
|
5407
|
+
if (folders[i].id() === args.containerId) {
|
|
5408
|
+
target = folders[i];
|
|
5409
|
+
break;
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5414
|
+
if (!target) {
|
|
5415
|
+
return JSON.stringify({
|
|
5416
|
+
error: {
|
|
5417
|
+
code: "NOT_FOUND",
|
|
5418
|
+
message: \`Container not found (project or folder): \${args.containerId}\`,
|
|
5419
|
+
},
|
|
5420
|
+
});
|
|
5421
|
+
}
|
|
5422
|
+
|
|
5423
|
+
w.focus = [target];
|
|
5424
|
+
return JSON.stringify({ focusContainerIds: [args.containerId] });
|
|
5425
|
+
}
|
|
5426
|
+
`;var lg=`/**
|
|
5427
|
+
* JXA: switch the front window to a named perspective.
|
|
5428
|
+
*
|
|
5429
|
+
* Args (argv[0] JSON): { perspectiveName: string }
|
|
5430
|
+
* Returns JSON: { perspectiveName }
|
|
5431
|
+
* or { error: { code: "NO_FRONT_WINDOW" | "NOT_FOUND", message } }
|
|
5432
|
+
*
|
|
5433
|
+
* Built-in perspectives (Inbox, Projects, Tags, Forecast, Flagged, Review, etc.)
|
|
5434
|
+
* and custom perspectives both work \u2014 JXA \`Window.perspective\` accepts either.
|
|
5435
|
+
*
|
|
5436
|
+
* @see #466
|
|
5437
|
+
* @see src/adapter/jxa/JxaTransport.ts \u2014 setWindowPerspective() caller
|
|
5438
|
+
*/
|
|
5439
|
+
|
|
5440
|
+
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
|
|
5441
|
+
function run(argv) {
|
|
5442
|
+
const args = JSON.parse(argv[0]);
|
|
5443
|
+
const ofApp = Application("OmniFocus");
|
|
5444
|
+
ofApp.includeStandardAdditions = false;
|
|
5445
|
+
|
|
5446
|
+
const wins = ofApp.windows();
|
|
5447
|
+
if (!wins || wins.length === 0) {
|
|
5448
|
+
return JSON.stringify({
|
|
5449
|
+
error: { code: "NO_FRONT_WINDOW", message: "OmniFocus has no front window" },
|
|
5450
|
+
});
|
|
5451
|
+
}
|
|
5452
|
+
|
|
5453
|
+
const w = wins[0];
|
|
5454
|
+
|
|
5455
|
+
// Look up the perspective by name. Both built-in and custom perspectives
|
|
5456
|
+
// appear in \`perspectives()\`. We match exact name \u2014 case-sensitive, matches
|
|
5457
|
+
// OF's own UX.
|
|
5458
|
+
const all = ofApp.perspectives();
|
|
5459
|
+
let target = null;
|
|
5460
|
+
for (let i = 0; i < all.length; i++) {
|
|
5461
|
+
if (all[i].name() === args.perspectiveName) {
|
|
5462
|
+
target = all[i];
|
|
5463
|
+
break;
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
if (!target) {
|
|
5467
|
+
return JSON.stringify({
|
|
5468
|
+
error: {
|
|
5469
|
+
code: "NOT_FOUND",
|
|
5470
|
+
message: \`Perspective not found: \${args.perspectiveName}\`,
|
|
5471
|
+
},
|
|
5472
|
+
});
|
|
5473
|
+
}
|
|
5474
|
+
|
|
5475
|
+
w.perspective = target;
|
|
5476
|
+
return JSON.stringify({ perspectiveName: args.perspectiveName });
|
|
5477
|
+
}
|
|
5478
|
+
`;var ug=new AsyncLocalStorage;function mg(t,e){let n=ulid();return ug.run(n,t)}function ze(){return ug.getStore()}function fg(){return ulid()}function Gb(t){let n=typeof t=="object"&&t!==null&&!Array.isArray(t)?JSON.stringify(t,Object.keys(t).sort()):JSON.stringify(t??null);return createHash("sha1").update(n).digest("hex").slice(0,16)}function gr(t,e,n,r,o){R.debug({event:"transport.call",transport:t,scriptName:e,argsHash:Gb(n),durationMs:r,outcome:o,correlationId:ze()},"transport call");}var Vb=16*1024*1024,Kb=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-",e],{timeout:n,maxBuffer:Vb,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,i,s)=>{let c=i,d=s,l=a!==null&&a.killed===true,m=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:d,exitCode:a===null?0:a.code??1,timedOut:l,...m!==void 0?{spawnError:m}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");});async function O(t,e={},n={}){let r=n.spawner??Kb,o=n.timeoutMs??3e4,a=JSON.stringify(e??{}),i=n.scriptName,s=performance.now(),c=await r(t,a,o),d=Math.round(performance.now()-s),l=c.spawnError!==void 0||c.timedOut||c.exitCode!==0||c.stdout.trim()===""?"error":"ok";if(gr("jxa",i,e,d,l),c.spawnError!==void 0)throw new ht("Failed to spawn osascript",{cause:c.spawnError,details:{transport:"jxa",reason:c.spawnError.code??"spawn-failed",...i!==void 0?{scriptName:i}:{}}});if(c.timedOut){let f=i!==void 0?` (script: ${i})`:"";throw new gt(`JXA script exceeded ${o}ms timeout${f}`,{details:{transport:"jxa",timeoutMs:o,...i!==void 0?{scriptName:i}:{}}})}if(c.exitCode!==0){let f=Xb(c.stderr,i);if(f!==null)throw f;let g=i!==void 0?` [${i}]`:"";throw new N(`JXA script failed (exit ${c.exitCode})${g}`,{details:{transport:"jxa",exitCode:c.exitCode,stderr:lt(c.stderr,1024),...i!==void 0?{scriptName:i}:{}}})}let m=c.stdout.trim();if(m==="")throw new N("JXA script returned empty stdout \u2014 `run()` must return a JSON-encoded string",{details:{transport:"jxa",...i!==void 0?{scriptName:i}:{}}});try{return JSON.parse(m)}catch(f){throw new N("JXA script returned malformed JSON",{cause:f,details:{transport:"jxa",stdoutPreview:lt(m,200),...i!==void 0?{scriptName:i}:{}}})}}function Xb(t,e){return /Application can't be found/i.test(t)||/Application isn['’]t running/i.test(t)||/OmniFocus(?:.*)not running/i.test(t)?new ut({details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/-1743\b/.test(t)||/not authori[sz]ed to send Apple events/i.test(t)||/errAEEventNotPermitted/i.test(t)||/not allowed assistive access/i.test(t)?new mt({details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/\bnot found\b/i.test(t)||/^OF_NOT_FOUND\b/m.test(t)?new P(t,{details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_VALIDATION\b/m.test(t)||/\bValidationError:/m.test(t)||/\bis required\b/i.test(t)?new y(t,{details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_CONFLICT\b/m.test(t)?new ft(t,{details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function lt(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function nc(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"}var hr=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return (await O(ng,{projectId:e.projectId??null,tagId:e.tagId??null,parentId:e.parentId??null,flagged:e.flagged??null,available:e.available??null,blocked:e.blocked??null,completed:e.completed??null,completedSince:e.completedSince??null,dueBefore:e.dueBefore??null,dueAfter:e.dueAfter??null,deferredBefore:e.deferredBefore??null,deferredAfter:e.deferredAfter??null},{...this.runOpts,scriptName:"task_list"})).tasks.map(r=>({...r,id:h.of(r.id)}))}async getTask(e){let n=await O(eg,{id:e},{...this.runOpts,scriptName:"task_get"});return {...n.task,id:h.of(n.task.id)}}async getTasksMany(e){return (await O(tg,{ids:e},{...this.runOpts,scriptName:"task_get_many"})).tasks.map(r=>r?{...r,id:h.of(r.id)}:null)}async createTask(e){let n=await O(Xf,{name:e.name,projectId:e.projectId??null,parentId:e.parentId??null,note:e.note??null,flagged:e.flagged??false,deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,tagIds:e.tagIds??[],sequential:e.sequential??false,completedByChildren:e.completedByChildren??false},{...this.runOpts,scriptName:"task_create"});return h.of(n.task.id)}async updateTask(e,n){await O(ig,{id:e,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.tagIds!==void 0?{tagIds:n.tagIds}:{},...n.sequential!==void 0?{sequential:n.sequential}:{},...n.completedByChildren!==void 0?{completedByChildren:n.completedByChildren}:{}},{...this.runOpts,scriptName:"task_update"});}async completeTask(e,n){await O(Kf,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"task_complete"});}async uncompleteTask(e){await O(ag,{id:e},{...this.runOpts,scriptName:"task_uncomplete"});}async dropTask(e,n){await O(Zf,{id:e,droppedAt:n?.toISOString()??null},{...this.runOpts,scriptName:"task_drop"});}async undropTask(e){await O(sg,{id:e},{...this.runOpts,scriptName:"task_undrop"});}async deleteTask(e){await O(Yf,{id:e},{...this.runOpts,scriptName:"task_delete"});}async moveTask(e,n){await O(rg,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});}async convertTaskToProject(e,n){throw new N("convertTaskToProject routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"convertTaskToProject"}})}async batchMoveTasks(e){throw new N("batchMoveTasks routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"batchMoveTasks"}})}async reorderTask(e,n){throw new N("reorderTask routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"reorderTask"}})}async duplicateTask(e,n){let r=n.destination===void 0?void 0:"projectId"in n.destination?{projectId:n.destination.projectId}:"parentId"in n.destination?{parentId:n.destination.parentId}:{toInbox:true},o=await O(Qf,{id:e,recursive:n.recursive,destination:r},{...this.runOpts,scriptName:"task_duplicate"});return {newId:h.of(o.newId),descendantCount:o.descendantCount}}async batchCreateTasks(e){let n=await O(Wf,{inputs:e.map(r=>({name:r.name,projectId:r.projectId??null,parentId:r.parentId??null,note:r.note??null,flagged:r.flagged??false,deferDate:r.deferDate??null,dueDate:r.dueDate??null,estimatedMinutes:r.estimatedMinutes??null,tagIds:r.tagIds??[],sequential:r.sequential??false,completedByChildren:r.completedByChildren??false}))},{...this.runOpts,scriptName:"task_batch_create"});return Pe(n,h.of)}async batchUpdateTasks(e){let n=await O(Vf,{updates:e},{...this.runOpts,scriptName:"task_batch_update"});return Pe(n,h.of)}async batchCompleteTasks(e){let n=await O($f,{items:e.map(r=>({id:r.id,at:r.at?.toISOString()??null}))},{...this.runOpts,scriptName:"task_batch_complete"});return Pe(n,h.of)}async batchUncompleteTasks(e){let n=await O(Gf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_uncomplete"});return Pe(n,h.of)}async batchDeleteTasks(e){let n=await O(Hf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_delete"});return Pe(n,h.of)}async batchDropTasks(e){let n=await O(zf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_drop"});return Pe(n,h.of)}async batchUndropTasks(e){let n=await O(qf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_undrop"});return Pe(n,h.of)}async listProjects(e){return (await O(Of,{folderId:e?.folderId??null,status:e?.status??null},{...this.runOpts,scriptName:"project_list"})).projects.map(r=>({...r,id:k.of(r.id)}))}async getProject(e){let n=await O(_f,{id:e},{...this.runOpts,scriptName:"project_get"});return {...n.project,id:k.of(n.project.id)}}async getProjectsMany(e){return (await O(Pf,{ids:e},{...this.runOpts,scriptName:"project_get_many"})).projects.map(r=>r?{...r,id:k.of(r.id)}:null)}async createProject(e){let n=await O(Sf,{name:e.name,folderId:e.folderId??null,note:e.note??null,deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,status:e.status??null},{...this.runOpts,scriptName:"project_create"});return k.of(n.project.id)}async updateProject(e,n){await O(Cf,{id:e,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.status!==void 0?{status:n.status}:{}},{...this.runOpts,scriptName:"project_update"});}async completeProject(e,n){await O(wf,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"project_complete"});}async dropProject(e){await O(jf,{id:e},{...this.runOpts,scriptName:"project_drop"});}async batchCompleteProjects(e){let n=await O(vf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_complete"});return Pe(n,k.of)}async batchDropProjects(e){let n=await O(Tf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_drop"});return Pe(n,k.of)}async moveProject(e,n){await O(Df,{id:e,folderId:n.folderId??null},{...this.runOpts,scriptName:"project_move"});}async deleteProject(e){await O(bf,{id:e},{...this.runOpts,scriptName:"project_delete"});}async markProjectReviewed(e){await O(xf,{id:e},{...this.runOpts,scriptName:"project_mark_reviewed"});}async listProjectsDueForReview(){return (await O(Ef,{},{...this.runOpts,scriptName:"review_list_due"})).projects}async setProjectReviewInterval(e,n){await O(Af,{id:e,days:n},{...this.runOpts,scriptName:"project_set_review_interval"});}async setProjectNextReviewDate(e,n){await O(Rf,{id:e,nextReviewDate:n},{...this.runOpts,scriptName:"project_set_next_review_date"});}async listTags(e){return (await O(Bf,{parentId:e?.parentId??null,status:e?.status??null},{...this.runOpts,scriptName:"tag_list"})).tags.map(r=>({...r,id:S.of(r.id)}))}async getTag(e){let n=await O(Uf,{id:e},{...this.runOpts,scriptName:"tag_get"});return {...n.tag,id:S.of(n.tag.id)}}async getTagsMany(e){return (await O(Lf,{ids:e},{...this.runOpts,scriptName:"tag_get_many"})).tags.map(r=>r?{...r,id:S.of(r.id)}:null)}async createTag(e){let n=await O(Nf,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"tag_create"});return S.of(n.tag.id)}async updateTag(e,n){await O(Jf,{id:e,...n.name!==void 0?{name:n.name}:{},...n.status!==void 0?{status:n.status}:{},...n.allowsNextAction!==void 0?{allowsNextAction:n.allowsNextAction}:{}},{...this.runOpts,scriptName:"tag_update"});}async deleteTag(e){await O(Ff,{id:e},{...this.runOpts,scriptName:"tag_delete"});}async listFolders(e){return (await O(gf,{parentId:e?.parentId??null},{...this.runOpts,scriptName:"folder_list"})).folders.map(r=>({...r,id:W.of(r.id)}))}async getFolder(e){let n=await O(ff,{id:e},{...this.runOpts,scriptName:"folder_get"});return {...n.folder,id:W.of(n.folder.id)}}async createFolder(e){let n=await O(uf,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"folder_create"});return W.of(n.folder.id)}async updateFolder(e,n){await O(hf,{id:e,...n.name!==void 0?{name:n.name}:{}},{...this.runOpts,scriptName:"folder_update"});}async deleteFolder(e){await O(mf,{id:e},{...this.runOpts,scriptName:"folder_delete"});}async listPerspectives(){return (await O(If,{},{...this.runOpts,scriptName:"perspective_list"})).perspectives}async evaluatePerspective(e){return (await O(kf,{perspectiveId:e},{...this.runOpts,scriptName:"perspective_evaluate"})).tasks.map(r=>({...r,id:h.of(r.id)}))}async evaluateCustomPerspective(e){throw new N("evaluateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluateCustomPerspective"}})}async evaluatePerspectiveRules(e,n){throw new N("evaluatePerspectiveRules requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluatePerspectiveRules"}})}async getCustomPerspective(e){throw new N("getCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"getCustomPerspective"}})}async deleteCustomPerspective(e){throw new N("deleteCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"deleteCustomPerspective"}})}async createCustomPerspective(e){throw new N("createCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"createCustomPerspective"}})}async updateCustomPerspective(e,n){throw new N("updateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"updateCustomPerspective"}})}async searchTasks(e){return (await O(og,{q:e.q??null,scope:e.scope??"all",projectId:e.projectId??null,tagIds:e.tagIds??null,available:e.available??null,dueBefore:e.dueBefore??null,dueAfter:e.dueAfter??null,flagged:e.flagged??null,completed:e.completed??"exclude"},{...this.runOpts,scriptName:"task_search"})).tasks.map(r=>({...r,id:h.of(r.id)}))}async getForecast(e){let n=await O(yf,{from:e.from,to:e.to,includeOverdue:e.includeOverdue??true,includeDeferred:e.includeDeferred??true,includeFlagged:e.includeFlagged??true},{...this.runOpts,scriptName:"forecast_get"});return {overdue:n.overdue.map(r=>({...r,id:h.of(r.id)})),dueToday:n.dueToday.map(r=>({...r,id:h.of(r.id)})),deferredToday:n.deferredToday.map(r=>({...r,id:h.of(r.id)})),flagged:n.flagged.map(r=>({...r,id:h.of(r.id)}))}}async getForecastTag(){throw new N("getForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"getForecastTag"}})}async setForecastTag(e){throw new N("setForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setForecastTag"}})}async undoLastMutation(){throw new N("undoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"undoLastMutation"}})}async redoLastMutation(){throw new N("redoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"redoLastMutation"}})}async setTaskAlarms(e,n){throw new N("setTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setTaskAlarms"}})}async clearTaskAlarms(e){throw new N("clearTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"clearTaskAlarms"}})}async listAttachments(e){return (await O(cf,e,{...this.runOpts,scriptName:"attachment_list"})).attachments}async addAttachment(e){let n=await O(sf,e,{...this.runOpts,scriptName:"attachment_add"});return Oe.of(n.id)}async removeAttachment(e){await O(df,e,{...this.runOpts,scriptName:"attachment_remove"});}async saveAttachmentToPath(e){return O(lf,e,{...this.runOpts,scriptName:"attachment_save_to_path"})}async appLaunch(){return O(af,{},{...this.runOpts,scriptName:"app_launch"})}async getWindowState(){let e=await O(cg,{},{...this.runOpts,scriptName:"window_get_state"});if(nc(e))throw new yt(e.error.message,{details:{transport:"jxa",scriptName:"window_get_state"}});return e}async setWindowPerspective(e){let n=await O(lg,{perspectiveName:e},{...this.runOpts,scriptName:"window_set_perspective"});if(nc(n))throw n.error.code==="NO_FRONT_WINDOW"?new yt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}}):new P(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}});return n}async setWindowFocus(e){let n=await O(dg,{containerId:e},{...this.runOpts,scriptName:"window_set_focus"});if(nc(n))throw n.error.code==="NO_FRONT_WINDOW"?new yt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}}):new P(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}});return n}async appWindowNew(){throw new N("appWindowNew routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNew"}})}async appWindowNewTab(){throw new N("appWindowNewTab routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNewTab"}})}async pluginInvoke(e){throw new N("pluginInvoke is handled by OmniJsTransport; JxaTransport is not in the routing path for this method",{details:{transport:"jxa",reason:"routed-to-omnijs"}})}async syncTrigger(){return await O(Mf,{},{...this.runOpts,scriptName:"sync_trigger"})}async getLastSync(){return {lastSyncAt:null,inFlight:false}}async getChangesSince(e){let n=await O(pf,{sinceIso:e},{...this.runOpts,scriptName:"changes_since"});return {taskIds:n.tasks.map(r=>r.id),projectIds:n.projects.map(r=>r.id)}}async runJxaScript(e,n){return O(e,n??{},{...this.runOpts,scriptName:"raw"})}};var gg=`/**
|
|
5479
|
+
* OmniJS: open a new OmniFocus window.
|
|
5480
|
+
*
|
|
5481
|
+
* Wraps \`document.newWindow()\`. The new window opens in the foreground with
|
|
5482
|
+
* the app's default perspective. No args.
|
|
5483
|
+
*
|
|
5484
|
+
* Returns JSON: { perspectiveName: string | null, focusContainerIds: [] }
|
|
5485
|
+
*
|
|
5486
|
+
* @see #527
|
|
5487
|
+
*/
|
|
5488
|
+
(() => {
|
|
5489
|
+
try {
|
|
5490
|
+
const w = document.newWindow();
|
|
5491
|
+
const perspectiveName = w.perspective ? w.perspective.name : null;
|
|
5492
|
+
return JSON.stringify({ perspectiveName, focusContainerIds: [] });
|
|
5493
|
+
} catch (e) {
|
|
5494
|
+
const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
|
|
5495
|
+
return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
|
|
5496
|
+
}
|
|
5497
|
+
})();
|
|
5498
|
+
`;var hg=`/**
|
|
5499
|
+
* OmniJS: add a new tab to the front OmniFocus window.
|
|
5500
|
+
*
|
|
5501
|
+
* Wraps \`document.newTabOnWindow(window)\` using the front window (index 0).
|
|
5502
|
+
* The new tab opens with the app's default perspective.
|
|
5503
|
+
*
|
|
5504
|
+
* Args injected as \`globalThis.__args\`: {} (no input for now)
|
|
5505
|
+
*
|
|
5506
|
+
* Returns JSON: { perspectiveName: string | null, focusContainerIds: [] }
|
|
5507
|
+
*
|
|
5508
|
+
* @see #527
|
|
5509
|
+
*/
|
|
5510
|
+
(() => {
|
|
5511
|
+
try {
|
|
5512
|
+
const windows = document.windows;
|
|
5513
|
+
if (!windows || windows.length === 0) {
|
|
5514
|
+
return JSON.stringify({
|
|
5515
|
+
error: { code: "WINDOW_UNAVAILABLE", message: "No open OmniFocus window" },
|
|
5516
|
+
});
|
|
5517
|
+
}
|
|
5518
|
+
const frontWindow = windows[0];
|
|
5519
|
+
const tab = document.newTabOnWindow(frontWindow);
|
|
5520
|
+
const perspectiveName = tab.perspective ? tab.perspective.name : null;
|
|
5521
|
+
return JSON.stringify({ perspectiveName, focusContainerIds: [] });
|
|
5522
|
+
} catch (e) {
|
|
5523
|
+
const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
|
|
5524
|
+
return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
|
|
5525
|
+
}
|
|
5526
|
+
})();
|
|
5527
|
+
`;var yg=`/**
|
|
5528
|
+
* OmniJS: redo the most recently undone mutation.
|
|
5529
|
+
*
|
|
5530
|
+
* Wraps \`Database.redo()\`. Behaves identically to \u2318\u21E7Z in the OmniFocus UI:
|
|
5531
|
+
* advances one entry on the document's redo stack. Any mutation between
|
|
5532
|
+
* undo and redo invalidates the redo stack (matching UI semantics).
|
|
5533
|
+
*
|
|
5534
|
+
* Args injected as \`globalThis.__args\`: {} (no input)
|
|
5535
|
+
*
|
|
5536
|
+
* Returns JSON: { redid: boolean }
|
|
5537
|
+
* - \`true\` \u2014 an entry was redone
|
|
5538
|
+
* - \`false\` \u2014 redo stack was empty
|
|
5539
|
+
*
|
|
5540
|
+
* @see #526
|
|
5541
|
+
*/
|
|
5542
|
+
(() => {
|
|
5543
|
+
try {
|
|
5544
|
+
Database.redo();
|
|
5545
|
+
return JSON.stringify({ redid: true });
|
|
5546
|
+
} catch (e) {
|
|
5547
|
+
const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
|
|
5548
|
+
if (/nothing to redo|empty/i.test(msg)) {
|
|
5549
|
+
return JSON.stringify({ redid: false });
|
|
5550
|
+
}
|
|
5551
|
+
return JSON.stringify({ error: { code: "REDO_FAILED", message: msg } });
|
|
5552
|
+
}
|
|
5553
|
+
})();
|
|
5554
|
+
`;var kg=`/**
|
|
5555
|
+
* OmniJS: undo the most recent document mutation.
|
|
5556
|
+
*
|
|
5557
|
+
* Wraps \`Database.undo()\`. Behaves identically to \u2318Z in the OmniFocus UI:
|
|
5558
|
+
* walks back one entry on the document's undo stack, regardless of whether
|
|
5559
|
+
* the mutation was issued by the MCP or another caller (manual edit, sync
|
|
5560
|
+
* replay, etc.). The agent contract is documented at the tool layer.
|
|
5561
|
+
*
|
|
5562
|
+
* Args injected as \`globalThis.__args\`: {} (no input)
|
|
5563
|
+
*
|
|
5564
|
+
* Returns JSON: { undid: boolean }
|
|
5565
|
+
* - \`true\` \u2014 an entry was undone
|
|
5566
|
+
* - \`false\` \u2014 undo stack was empty (nothing to undo)
|
|
5567
|
+
*
|
|
5568
|
+
* @see #526
|
|
5569
|
+
*/
|
|
5570
|
+
(() => {
|
|
5571
|
+
// Database.undo() returns void in the OmniJS docs but throws when the
|
|
5572
|
+
// stack is empty in some versions. Belt-and-suspenders: catch and report
|
|
5573
|
+
// the empty-stack case as \`undid: false\` rather than propagating.
|
|
5574
|
+
try {
|
|
5575
|
+
Database.undo();
|
|
5576
|
+
return JSON.stringify({ undid: true });
|
|
5577
|
+
} catch (e) {
|
|
5578
|
+
const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
|
|
5579
|
+
if (/nothing to undo|empty/i.test(msg)) {
|
|
5580
|
+
return JSON.stringify({ undid: false });
|
|
5581
|
+
}
|
|
5582
|
+
return JSON.stringify({ error: { code: "UNDO_FAILED", message: msg } });
|
|
5583
|
+
}
|
|
5584
|
+
})();
|
|
5585
|
+
`;var Ig=`/**
|
|
5586
|
+
* OmniJS: read the forecast-tag preference (the tag whose tasks always
|
|
5587
|
+
* appear on the Forecast view alongside dated items).
|
|
5588
|
+
*
|
|
5589
|
+
* Args injected as \`globalThis.__args\`: {} (no input)
|
|
5590
|
+
*
|
|
5591
|
+
* Returns JSON: { tagId: string | null }
|
|
5592
|
+
* - \`tagId\` is the configured forecast tag's primary-key string when set
|
|
5593
|
+
* - \`null\` when no forecast tag is configured (fresh OF install or cleared)
|
|
5594
|
+
*
|
|
5595
|
+
* @see #465
|
|
5596
|
+
*/
|
|
5597
|
+
(() => {
|
|
5598
|
+
// OmniJS exposes the preference as \`Database.forecastTag\`. Older docs
|
|
5599
|
+
// reference \`Database.forecastTagID\`; the property here is the actual Tag
|
|
5600
|
+
// object (or null), so we read \`.id.primaryKey\` from it.
|
|
5601
|
+
const tag = Database.forecastTag;
|
|
5602
|
+
return JSON.stringify({ tagId: tag === null ? null : tag.id.primaryKey });
|
|
5603
|
+
})();
|
|
5604
|
+
`;var vg=`/**
|
|
5605
|
+
* OmniJS: set or clear the forecast-tag preference.
|
|
5606
|
+
*
|
|
5607
|
+
* Args injected as \`globalThis.__args\`:
|
|
5608
|
+
* { tagId: string | null }
|
|
5609
|
+
* - \`tagId\` non-null \u2192 set Database.forecastTag = <that tag>
|
|
5610
|
+
* - \`tagId\` null \u2192 clear (Database.forecastTag = null)
|
|
5611
|
+
*
|
|
5612
|
+
* Returns JSON: { tagId: string | null } \u2014 echoes what was applied
|
|
5613
|
+
* or { error: { code, message } } on failure
|
|
5614
|
+
*
|
|
5615
|
+
* @see #465
|
|
5616
|
+
*/
|
|
5617
|
+
(() => {
|
|
5618
|
+
const { tagId } = globalThis.__args;
|
|
5619
|
+
|
|
5620
|
+
if (tagId === null || tagId === undefined) {
|
|
5621
|
+
Database.forecastTag = null;
|
|
5622
|
+
return JSON.stringify({ tagId: null });
|
|
5623
|
+
}
|
|
5624
|
+
|
|
5625
|
+
if (typeof tagId !== "string") {
|
|
5626
|
+
return JSON.stringify({
|
|
5627
|
+
error: { code: "VALIDATION", message: "tagId must be a string or null" },
|
|
5628
|
+
});
|
|
5629
|
+
}
|
|
5630
|
+
|
|
5631
|
+
const tag = flattenedTags.filter((t) => t.id.primaryKey === tagId)[0];
|
|
5632
|
+
if (!tag) {
|
|
5633
|
+
return JSON.stringify({
|
|
5634
|
+
error: { code: "NOT_FOUND", message: \`Tag not found: \${tagId}\` },
|
|
5635
|
+
});
|
|
5636
|
+
}
|
|
5637
|
+
|
|
5638
|
+
Database.forecastTag = tag;
|
|
5639
|
+
return JSON.stringify({ tagId });
|
|
5640
|
+
})();
|
|
5641
|
+
`;var Tg=`/**
|
|
5642
|
+
* perspective_create.js \u2014 create a new custom OmniFocus perspective.
|
|
5643
|
+
*
|
|
5644
|
+
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
5645
|
+
* { name, aggregation?, rules?, iconColor? }
|
|
5646
|
+
*
|
|
5647
|
+
* Returns JSON: \`{ id: string }\` on success (the new persistent identifier),
|
|
5648
|
+
* or \`{ error: { code, message } }\` for typed failures.
|
|
5649
|
+
*
|
|
5650
|
+
* Two-step write:
|
|
5651
|
+
* 1. JXA \`make({ new: "perspective", withProperties: { name } })\` to create
|
|
5652
|
+
* the shell. JXA is the only API that creates a custom perspective \u2014
|
|
5653
|
+
* OmniJS has no factory for \`Perspective.Custom\`.
|
|
5654
|
+
* 2. OmniJS configures \`archivedTopLevelFilterAggregation\`,
|
|
5655
|
+
* \`archivedFilterRules\`, and \`iconColor\` on the resulting object.
|
|
5656
|
+
*
|
|
5657
|
+
* Atomic rollback:
|
|
5658
|
+
* If step 2 throws, the script calls \`deleteObject(persp)\` so the user is
|
|
5659
|
+
* never left with a half-configured perspective. The rollback runs inside
|
|
5660
|
+
* this same OmniJS execution \u2014 no TS-level try/finally that could itself
|
|
5661
|
+
* fail between transport hops.
|
|
5662
|
+
*
|
|
5663
|
+
* Rule serialization:
|
|
5664
|
+
* The input rule shape mirrors the read-side shape in \`perspective_get.js\`
|
|
5665
|
+
* (#523/#569) so round-trips are lossless. Atom keys, aggregate
|
|
5666
|
+
* \`aggregateType\` / \`aggregateRules\`, and \`disabledRule\` wrappers are
|
|
5667
|
+
* passed through verbatim \u2014 \`archivedFilterRules\` accepts plain JS arrays
|
|
5668
|
+
* of plain JS objects.
|
|
5669
|
+
*
|
|
5670
|
+
* iconColor:
|
|
5671
|
+
* \`{ r, g, b, a }\` floats in [0, 1] are mapped to \`Color.RGB(r, g, b, a)\`,
|
|
5672
|
+
* symmetric with the read serialization.
|
|
5673
|
+
*
|
|
5674
|
+
* @see #577, #617
|
|
5675
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 createCustomPerspective()
|
|
5676
|
+
* @see src/scripts/omnijs/perspective_get.js \u2014 serialization mirror
|
|
5677
|
+
*/
|
|
5678
|
+
(() => {
|
|
5679
|
+
const { name, aggregation, rules, iconColor } = globalThis.__args;
|
|
5680
|
+
|
|
5681
|
+
if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
|
|
5682
|
+
return JSON.stringify({
|
|
5683
|
+
error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
|
|
5684
|
+
});
|
|
5685
|
+
}
|
|
5686
|
+
|
|
5687
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
5688
|
+
return JSON.stringify({
|
|
5689
|
+
error: { code: "VALIDATION_ERROR", message: "name is required and must be non-empty" },
|
|
5690
|
+
});
|
|
5691
|
+
}
|
|
5692
|
+
|
|
5693
|
+
// ----- Step 1: JXA make ----------------------------------------------------
|
|
5694
|
+
// Application("OmniFocus") inside OmniJS bridges to the JXA application
|
|
5695
|
+
// object. \`make\` is the only documented way to create a custom perspective.
|
|
5696
|
+
let persp;
|
|
5697
|
+
let identifier;
|
|
5698
|
+
try {
|
|
5699
|
+
const app = Application("OmniFocus");
|
|
5700
|
+
const made = app.make({
|
|
5701
|
+
new: "perspective",
|
|
5702
|
+
withProperties: { name },
|
|
5703
|
+
});
|
|
5704
|
+
// \`make\` returns a JXA reference. The persistent identifier is exposed
|
|
5705
|
+
// via the OmniJS-side wrapper, not the JXA reference, so re-fetch by
|
|
5706
|
+
// name to get the OmniJS object. Names are unique inside a database, so
|
|
5707
|
+
// a fresh lookup by name is unambiguous *immediately* after creation.
|
|
5708
|
+
if (made === null || made === undefined) {
|
|
5709
|
+
return JSON.stringify({
|
|
5710
|
+
error: { code: "SCRIPT_ERROR", message: "JXA make returned no object" },
|
|
5711
|
+
});
|
|
5712
|
+
}
|
|
5713
|
+
// Locate the OmniJS-side perspective by name. \`Perspective.Custom.all\`
|
|
5714
|
+
// is the documented enumeration; we filter by name to find the one we
|
|
5715
|
+
// just created.
|
|
5716
|
+
const all = Perspective.Custom.all;
|
|
5717
|
+
for (let i = 0; i < all.length; i++) {
|
|
5718
|
+
if (all[i].name === name) {
|
|
5719
|
+
persp = all[i];
|
|
5720
|
+
identifier = persp.identifier;
|
|
5721
|
+
break;
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
if (persp === undefined) {
|
|
5725
|
+
return JSON.stringify({
|
|
5726
|
+
error: {
|
|
5727
|
+
code: "SCRIPT_ERROR",
|
|
5728
|
+
message: \`created perspective named "\${name}" but could not locate it via Perspective.Custom.all\`,
|
|
5729
|
+
},
|
|
5730
|
+
});
|
|
5731
|
+
}
|
|
5732
|
+
} catch (e) {
|
|
5733
|
+
const msg = String(e?.message ? e.message : e);
|
|
5734
|
+
// OmniFocus rejects duplicate names with a recognisable message; surface
|
|
5735
|
+
// as VALIDATION_ERROR so the agent can react accordingly.
|
|
5736
|
+
if (msg.toLowerCase().indexOf("already") >= 0 || msg.toLowerCase().indexOf("duplicate") >= 0) {
|
|
5737
|
+
return JSON.stringify({
|
|
5738
|
+
error: { code: "VALIDATION_ERROR", message: \`Duplicate perspective name: \${name}\` },
|
|
5739
|
+
});
|
|
5740
|
+
}
|
|
5741
|
+
return JSON.stringify({
|
|
5742
|
+
error: { code: "SCRIPT_ERROR", message: \`JXA make failed: \${msg}\` },
|
|
5743
|
+
});
|
|
5744
|
+
}
|
|
5745
|
+
|
|
5746
|
+
// ----- Step 2: OmniJS configure (with rollback on failure) -----------------
|
|
5747
|
+
try {
|
|
5748
|
+
if (typeof aggregation === "string") {
|
|
5749
|
+
persp.archivedTopLevelFilterAggregation = aggregation;
|
|
5750
|
+
}
|
|
5751
|
+
if (Array.isArray(rules)) {
|
|
5752
|
+
// archivedFilterRules accepts plain JS arrays of plain JS objects \u2014
|
|
5753
|
+
// the same shape produced by perspective_get.js. Pass through verbatim.
|
|
5754
|
+
persp.archivedFilterRules = rules;
|
|
5755
|
+
}
|
|
5756
|
+
if (
|
|
5757
|
+
iconColor !== null &&
|
|
5758
|
+
iconColor !== undefined &&
|
|
5759
|
+
typeof iconColor === "object" &&
|
|
5760
|
+
typeof iconColor.r === "number" &&
|
|
5761
|
+
typeof iconColor.g === "number" &&
|
|
5762
|
+
typeof iconColor.b === "number" &&
|
|
5763
|
+
typeof iconColor.a === "number"
|
|
5764
|
+
) {
|
|
5765
|
+
persp.iconColor = Color.RGB(iconColor.r, iconColor.g, iconColor.b, iconColor.a);
|
|
5766
|
+
}
|
|
5767
|
+
} catch (e) {
|
|
5768
|
+
// Atomic rollback \u2014 the shell exists but the configure step failed, so
|
|
5769
|
+
// delete the shell so the user is never left with a malformed
|
|
5770
|
+
// perspective. If rollback also fails, surface both errors.
|
|
5771
|
+
const configureMsg = String(e?.message ? e.message : e);
|
|
5772
|
+
try {
|
|
5773
|
+
deleteObject(persp);
|
|
5774
|
+
} catch (rollbackErr) {
|
|
5775
|
+
const rollbackMsg = String(rollbackErr?.message ? rollbackErr.message : rollbackErr);
|
|
5776
|
+
return JSON.stringify({
|
|
5777
|
+
error: {
|
|
5778
|
+
code: "SCRIPT_ERROR",
|
|
5779
|
+
message: \`configure failed (\${configureMsg}); rollback also failed (\${rollbackMsg}). Shell perspective "\${name}" may need manual deletion.\`,
|
|
5780
|
+
},
|
|
5781
|
+
});
|
|
5782
|
+
}
|
|
5783
|
+
return JSON.stringify({
|
|
5784
|
+
error: {
|
|
5785
|
+
code: "SCRIPT_ERROR",
|
|
5786
|
+
message: \`configure failed: \${configureMsg}. Shell perspective rolled back.\`,
|
|
5787
|
+
},
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
|
|
5791
|
+
return JSON.stringify({ id: identifier });
|
|
5792
|
+
})();
|
|
5793
|
+
`;var wg=`/**
|
|
5794
|
+
* perspective_delete.js \u2014 delete a custom OmniFocus perspective by identifier.
|
|
5795
|
+
*
|
|
5796
|
+
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
5797
|
+
* { identifier: string }
|
|
5798
|
+
*
|
|
5799
|
+
* Returns JSON: \`{ id: string }\` on success (echoes the deleted identifier),
|
|
5800
|
+
* or \`{ error: { code, message } }\` for typed failures.
|
|
5801
|
+
*
|
|
5802
|
+
* Built-in perspectives cannot be deleted \u2014 \`Perspective.Custom.byIdentifier\`
|
|
5803
|
+
* returns null for them, which the script reports as NOT_FOUND. JXA cannot
|
|
5804
|
+
* delete custom perspectives; only OmniJS \`deleteObject\` works.
|
|
5805
|
+
*
|
|
5806
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 deleteCustomPerspective()
|
|
5807
|
+
*/
|
|
5808
|
+
(() => {
|
|
5809
|
+
const { identifier } = globalThis.__args;
|
|
5810
|
+
|
|
5811
|
+
if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
|
|
5812
|
+
return JSON.stringify({
|
|
5813
|
+
error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
|
|
5817
|
+
const persp = Perspective.Custom.byIdentifier(identifier);
|
|
5818
|
+
if (persp === null || persp === undefined) {
|
|
5819
|
+
return JSON.stringify({
|
|
5820
|
+
error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
|
|
5821
|
+
});
|
|
5822
|
+
}
|
|
5823
|
+
|
|
5824
|
+
try {
|
|
5825
|
+
deleteObject(persp);
|
|
5826
|
+
} catch (e) {
|
|
5827
|
+
return JSON.stringify({
|
|
5828
|
+
error: { code: "SCRIPT_ERROR", message: String(e?.message ? e.message : e) },
|
|
5829
|
+
});
|
|
5830
|
+
}
|
|
5831
|
+
|
|
5832
|
+
return JSON.stringify({ id: identifier });
|
|
5833
|
+
})();
|
|
5834
|
+
`;var Sg=`/**
|
|
5835
|
+
* perspective_evaluate.js \u2014 evaluate a custom OmniFocus perspective and
|
|
5836
|
+
* return its task list as domain \`Task\` objects.
|
|
5837
|
+
*
|
|
5838
|
+
* Called via the OmniJS transport (evaluateJavascript bridge).
|
|
5839
|
+
* Args injected as \`globalThis.__args\`:
|
|
5840
|
+
* { identifier: string }
|
|
5841
|
+
*
|
|
5842
|
+
* Returns a JSON string: { tasks: Task[] } on success, or a JSON error
|
|
5843
|
+
* envelope \`{ error: { code, message } }\` for typed failures (Pro-gating,
|
|
5844
|
+
* unknown perspective identifier) that the caller maps to typed errors.
|
|
5845
|
+
*
|
|
5846
|
+
* Evaluation strategy (per Omni Automation): the perspective is set on the
|
|
5847
|
+
* front document window, and the resulting \`content.rootNode.descendants\`
|
|
5848
|
+
* are walked to collect every \`Task\` object. This mirrors what the user
|
|
5849
|
+
* would see if they switched to the perspective in the UI \u2014 the only API
|
|
5850
|
+
* surface OmniFocus exposes for perspective evaluation.
|
|
5851
|
+
*
|
|
5852
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 evaluateCustomPerspective()
|
|
5853
|
+
* @see src/domain/task.ts \u2014 Task domain type
|
|
5854
|
+
* @see docs/adr/0005-scripts-as-first-class-files.md
|
|
5855
|
+
*/
|
|
5856
|
+
(() => {
|
|
5857
|
+
const { identifier } = globalThis.__args;
|
|
5858
|
+
|
|
5859
|
+
if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
|
|
5860
|
+
return JSON.stringify({
|
|
5861
|
+
error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
|
|
5862
|
+
});
|
|
5863
|
+
}
|
|
5864
|
+
|
|
5865
|
+
const persp = Perspective.Custom.byIdentifier(identifier);
|
|
5866
|
+
if (persp === null || persp === undefined) {
|
|
5867
|
+
return JSON.stringify({
|
|
5868
|
+
error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
|
|
5869
|
+
});
|
|
5870
|
+
}
|
|
5871
|
+
|
|
5872
|
+
const win = document.windows[0];
|
|
5873
|
+
win.perspective = persp;
|
|
5874
|
+
|
|
5875
|
+
function isoOrNull(d) {
|
|
5876
|
+
return d ? d.toISOString() : null;
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
function buildRepetition(task) {
|
|
5880
|
+
try {
|
|
5881
|
+
const rr = task.repetitionRule;
|
|
5882
|
+
if (!rr) return null;
|
|
5883
|
+
return { method: String(rr.method), unit: String(rr.unit), steps: rr.steps };
|
|
5884
|
+
} catch (_e) {
|
|
5885
|
+
return null;
|
|
5886
|
+
}
|
|
5887
|
+
}
|
|
5888
|
+
|
|
5889
|
+
function buildTask(task) {
|
|
5890
|
+
const tagIds = [];
|
|
5891
|
+
try {
|
|
5892
|
+
const tags = task.tags;
|
|
5893
|
+
for (let i = 0; i < tags.length; i++) tagIds.push(tags[i].id.primaryKey);
|
|
5894
|
+
} catch (_e) {}
|
|
5895
|
+
|
|
5896
|
+
let projectId = null;
|
|
5897
|
+
try {
|
|
5898
|
+
if (task.containingProject) projectId = task.containingProject.id.primaryKey;
|
|
5899
|
+
} catch (_e) {}
|
|
5900
|
+
|
|
5901
|
+
let parentId = null;
|
|
5902
|
+
try {
|
|
5903
|
+
if (task.parent && task.parent instanceof Task) parentId = task.parent.id.primaryKey;
|
|
5904
|
+
} catch (_e) {}
|
|
5905
|
+
|
|
5906
|
+
return {
|
|
5907
|
+
id: task.id.primaryKey,
|
|
5908
|
+
name: task.name,
|
|
5909
|
+
note: task.note || null,
|
|
5910
|
+
noteHtml: null,
|
|
5911
|
+
projectId,
|
|
5912
|
+
parentId,
|
|
5913
|
+
tagIds,
|
|
5914
|
+
deferDate: isoOrNull(task.deferDate),
|
|
5915
|
+
dueDate: isoOrNull(task.dueDate),
|
|
5916
|
+
estimatedMinutes: task.estimatedMinutes ?? null,
|
|
5917
|
+
flagged: !!task.flagged,
|
|
5918
|
+
completed: !!task.completed,
|
|
5919
|
+
completedAt: isoOrNull(task.completionDate),
|
|
5920
|
+
dropped: !!task.dropped,
|
|
5921
|
+
droppedAt: isoOrNull(task.dropDate),
|
|
5922
|
+
available: !task.blocked && !task.completed && !task.dropped,
|
|
5923
|
+
blocked: !!task.blocked,
|
|
5924
|
+
sequential: !!task.sequential,
|
|
5925
|
+
completedByChildren: !!task.completedByChildren,
|
|
5926
|
+
repetition: buildRepetition(task),
|
|
5927
|
+
createdAt: isoOrNull(task.added) ?? new Date().toISOString(),
|
|
5928
|
+
modifiedAt: isoOrNull(task.modified) ?? new Date().toISOString(),
|
|
5929
|
+
};
|
|
5930
|
+
}
|
|
5931
|
+
|
|
5932
|
+
const tasks = [];
|
|
5933
|
+
const seen = new Set();
|
|
5934
|
+
const walk = (node) => {
|
|
5935
|
+
if (!node) return;
|
|
5936
|
+
try {
|
|
5937
|
+
const obj = node.object;
|
|
5938
|
+
if (obj instanceof Task && !seen.has(obj.id.primaryKey)) {
|
|
5939
|
+
seen.add(obj.id.primaryKey);
|
|
5940
|
+
tasks.push(buildTask(obj));
|
|
5941
|
+
}
|
|
5942
|
+
} catch (_e) {}
|
|
5943
|
+
const children = node.children || [];
|
|
5944
|
+
for (let i = 0; i < children.length; i++) walk(children[i]);
|
|
5945
|
+
};
|
|
5946
|
+
walk(win.content.rootNode);
|
|
5947
|
+
|
|
5948
|
+
return JSON.stringify({ tasks });
|
|
5949
|
+
})();
|
|
5950
|
+
`;var bg=`/**
|
|
5951
|
+
* perspective_evaluate_dry_run.js \u2014 evaluate a *proposed* perspective rule
|
|
5952
|
+
* tree and return its task list, without persisting the perspective.
|
|
5953
|
+
*
|
|
5954
|
+
* The OmniJS perspective evaluator (window.perspective + content.rootNode)
|
|
5955
|
+
* only operates on saved Perspective.Custom objects \u2014 there is no "evaluate
|
|
5956
|
+
* ad-hoc rules" surface. We work around this by creating a temporary
|
|
5957
|
+
* perspective with a sentinel name, evaluating it, and **always** deleting
|
|
5958
|
+
* it inside the same OmniJS execution. The rollback runs in this single
|
|
5959
|
+
* script so a TS-level retry that drops between transport hops cannot leave
|
|
5960
|
+
* an orphan perspective behind (per the existing perspective_create.js
|
|
5961
|
+
* rollback comment).
|
|
5962
|
+
*
|
|
5963
|
+
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
5964
|
+
* { aggregation?: string, rules?: Array<unknown> }
|
|
5965
|
+
*
|
|
5966
|
+
* Returns JSON: \`{ tasks: Task[] }\` on success, or
|
|
5967
|
+
* \`{ error: { code, message } }\` for typed failures (Pro-gating, JXA
|
|
5968
|
+
* failure, configure failure, walk failure). The caller maps codes to
|
|
5969
|
+
* typed errors.
|
|
5970
|
+
*
|
|
5971
|
+
* @see #659 \u2014 issue
|
|
5972
|
+
* @see src/scripts/omnijs/perspective_evaluate.js \u2014 saved-perspective evaluator
|
|
5973
|
+
* @see src/scripts/omnijs/perspective_create.js \u2014 JXA-make + configure pattern
|
|
5974
|
+
* @see src/scripts/omnijs/perspective_delete.js \u2014 deleteObject pattern
|
|
5975
|
+
*/
|
|
5976
|
+
(() => {
|
|
5977
|
+
const { aggregation, rules } = globalThis.__args;
|
|
5978
|
+
|
|
5979
|
+
if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
|
|
5980
|
+
return JSON.stringify({
|
|
5981
|
+
error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
|
|
5982
|
+
});
|
|
5983
|
+
}
|
|
5984
|
+
|
|
5985
|
+
// Sentinel name \u2014 long random suffix avoids collision with user perspectives
|
|
5986
|
+
// even under concurrent dry-run calls. The leading underscore + "dry-run"
|
|
5987
|
+
// marker makes orphans (if any) trivially identifiable.
|
|
5988
|
+
const sentinelName = \`__omnifocus-mcp-dry-run-\${Date.now()}-\${Math.floor(Math.random() * 1e9).toString(36)}\`;
|
|
5989
|
+
|
|
5990
|
+
// ----- Step 1: JXA make ----------------------------------------------------
|
|
5991
|
+
let persp;
|
|
5992
|
+
try {
|
|
5993
|
+
const app = Application("OmniFocus");
|
|
5994
|
+
const made = app.make({ new: "perspective", withProperties: { name: sentinelName } });
|
|
5995
|
+
if (made === null || made === undefined) {
|
|
5996
|
+
return JSON.stringify({
|
|
5997
|
+
error: { code: "SCRIPT_ERROR", message: "JXA make returned no object" },
|
|
5998
|
+
});
|
|
5999
|
+
}
|
|
6000
|
+
const all = Perspective.Custom.all;
|
|
6001
|
+
for (let i = 0; i < all.length; i++) {
|
|
6002
|
+
if (all[i].name === sentinelName) {
|
|
6003
|
+
persp = all[i];
|
|
6004
|
+
break;
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
if (persp === undefined) {
|
|
6008
|
+
return JSON.stringify({
|
|
6009
|
+
error: {
|
|
6010
|
+
code: "SCRIPT_ERROR",
|
|
6011
|
+
message: \`temp perspective "\${sentinelName}" created but not located via Perspective.Custom.all\`,
|
|
6012
|
+
},
|
|
6013
|
+
});
|
|
6014
|
+
}
|
|
6015
|
+
} catch (e) {
|
|
6016
|
+
return JSON.stringify({
|
|
6017
|
+
error: {
|
|
6018
|
+
code: "SCRIPT_ERROR",
|
|
6019
|
+
message: \`JXA make failed: \${String(e?.message ? e.message : e)}\`,
|
|
6020
|
+
},
|
|
6021
|
+
});
|
|
6022
|
+
}
|
|
6023
|
+
|
|
6024
|
+
// From here on, \`persp\` exists and MUST be deleted before returning.
|
|
6025
|
+
// Defensive \`safeDelete\` swallows secondary errors so the primary error
|
|
6026
|
+
// (or success) reaches the caller.
|
|
6027
|
+
function safeDelete() {
|
|
6028
|
+
try {
|
|
6029
|
+
deleteObject(persp);
|
|
6030
|
+
} catch (_e) {
|
|
6031
|
+
// Surface in the message if the primary path was a success \u2014 caller
|
|
6032
|
+
// sees "tasks but cleanup failed". On primary failure we don't double-report.
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
|
|
6036
|
+
// ----- Step 2: configure rules + aggregation -------------------------------
|
|
6037
|
+
try {
|
|
6038
|
+
if (typeof aggregation === "string") {
|
|
6039
|
+
persp.archivedTopLevelFilterAggregation = aggregation;
|
|
6040
|
+
}
|
|
6041
|
+
if (Array.isArray(rules)) {
|
|
6042
|
+
persp.archivedFilterRules = rules;
|
|
6043
|
+
}
|
|
6044
|
+
} catch (e) {
|
|
6045
|
+
safeDelete();
|
|
6046
|
+
return JSON.stringify({
|
|
6047
|
+
error: {
|
|
6048
|
+
code: "SCRIPT_ERROR",
|
|
6049
|
+
message: \`configure failed: \${String(e?.message ? e.message : e)}. Temp perspective rolled back.\`,
|
|
6050
|
+
},
|
|
6051
|
+
});
|
|
6052
|
+
}
|
|
6053
|
+
|
|
6054
|
+
// ----- Step 3: switch window + walk tasks ----------------------------------
|
|
6055
|
+
function isoOrNull(d) {
|
|
6056
|
+
return d ? d.toISOString() : null;
|
|
6057
|
+
}
|
|
6058
|
+
|
|
6059
|
+
function buildRepetition(task) {
|
|
6060
|
+
try {
|
|
6061
|
+
const rr = task.repetitionRule;
|
|
6062
|
+
if (!rr) return null;
|
|
6063
|
+
return { method: String(rr.method), unit: String(rr.unit), steps: rr.steps };
|
|
6064
|
+
} catch (_e) {
|
|
6065
|
+
return null;
|
|
6066
|
+
}
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
function buildTask(task) {
|
|
6070
|
+
const tagIds = [];
|
|
6071
|
+
try {
|
|
6072
|
+
const tags = task.tags;
|
|
6073
|
+
for (let i = 0; i < tags.length; i++) tagIds.push(tags[i].id.primaryKey);
|
|
6074
|
+
} catch (_e) {}
|
|
6075
|
+
|
|
6076
|
+
let projectId = null;
|
|
6077
|
+
try {
|
|
6078
|
+
if (task.containingProject) projectId = task.containingProject.id.primaryKey;
|
|
6079
|
+
} catch (_e) {}
|
|
6080
|
+
|
|
6081
|
+
let parentId = null;
|
|
6082
|
+
try {
|
|
6083
|
+
if (task.parent && task.parent instanceof Task) parentId = task.parent.id.primaryKey;
|
|
6084
|
+
} catch (_e) {}
|
|
6085
|
+
|
|
6086
|
+
return {
|
|
6087
|
+
id: task.id.primaryKey,
|
|
6088
|
+
name: task.name,
|
|
6089
|
+
note: task.note || null,
|
|
6090
|
+
noteHtml: null,
|
|
6091
|
+
projectId,
|
|
6092
|
+
parentId,
|
|
6093
|
+
tagIds,
|
|
6094
|
+
deferDate: isoOrNull(task.deferDate),
|
|
6095
|
+
dueDate: isoOrNull(task.dueDate),
|
|
6096
|
+
estimatedMinutes: task.estimatedMinutes ?? null,
|
|
6097
|
+
flagged: !!task.flagged,
|
|
6098
|
+
completed: !!task.completed,
|
|
6099
|
+
completedAt: isoOrNull(task.completionDate),
|
|
6100
|
+
dropped: !!task.dropped,
|
|
6101
|
+
droppedAt: isoOrNull(task.dropDate),
|
|
6102
|
+
available: !task.blocked && !task.completed && !task.dropped,
|
|
6103
|
+
blocked: !!task.blocked,
|
|
6104
|
+
sequential: !!task.sequential,
|
|
6105
|
+
completedByChildren: !!task.completedByChildren,
|
|
6106
|
+
repetition: buildRepetition(task),
|
|
6107
|
+
createdAt: isoOrNull(task.added) ?? new Date().toISOString(),
|
|
6108
|
+
modifiedAt: isoOrNull(task.modified) ?? new Date().toISOString(),
|
|
6109
|
+
};
|
|
6110
|
+
}
|
|
6111
|
+
|
|
6112
|
+
let tasks;
|
|
6113
|
+
try {
|
|
6114
|
+
const win = document.windows[0];
|
|
6115
|
+
win.perspective = persp;
|
|
6116
|
+
|
|
6117
|
+
tasks = [];
|
|
6118
|
+
const seen = new Set();
|
|
6119
|
+
const walk = (node) => {
|
|
6120
|
+
if (!node) return;
|
|
6121
|
+
try {
|
|
6122
|
+
const obj = node.object;
|
|
6123
|
+
if (obj instanceof Task && !seen.has(obj.id.primaryKey)) {
|
|
6124
|
+
seen.add(obj.id.primaryKey);
|
|
6125
|
+
tasks.push(buildTask(obj));
|
|
6126
|
+
}
|
|
6127
|
+
} catch (_e) {}
|
|
6128
|
+
const children = node.children || [];
|
|
6129
|
+
for (let i = 0; i < children.length; i++) walk(children[i]);
|
|
6130
|
+
};
|
|
6131
|
+
walk(win.content.rootNode);
|
|
6132
|
+
} catch (e) {
|
|
6133
|
+
safeDelete();
|
|
6134
|
+
return JSON.stringify({
|
|
6135
|
+
error: {
|
|
6136
|
+
code: "SCRIPT_ERROR",
|
|
6137
|
+
message: \`evaluate walk failed: \${String(e?.message ? e.message : e)}. Temp perspective rolled back.\`,
|
|
6138
|
+
},
|
|
6139
|
+
});
|
|
6140
|
+
}
|
|
6141
|
+
|
|
6142
|
+
// ----- Step 4: cleanup (always) --------------------------------------------
|
|
6143
|
+
safeDelete();
|
|
6144
|
+
|
|
6145
|
+
return JSON.stringify({ tasks });
|
|
6146
|
+
})();
|
|
6147
|
+
`;var jg=`/**
|
|
6148
|
+
* perspective_get.js \u2014 read a custom OmniFocus perspective's full configuration.
|
|
6149
|
+
*
|
|
6150
|
+
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
6151
|
+
* { identifier: string }
|
|
6152
|
+
*
|
|
6153
|
+
* Returns JSON on success:
|
|
6154
|
+
* { perspective: { id, name, aggregation, rules, iconColor } }
|
|
6155
|
+
*
|
|
6156
|
+
* Or a typed error envelope:
|
|
6157
|
+
* { error: { code, message } }
|
|
6158
|
+
*
|
|
6159
|
+
* Built-in perspectives are not supported here \u2014 this surface only exposes
|
|
6160
|
+
* the rich rule-tree for custom perspectives. The caller (router) must
|
|
6161
|
+
* gate on \`kind: "custom"\` from \`perspective_list\` before calling.
|
|
6162
|
+
*
|
|
6163
|
+
* Rule serialization: \`archivedFilterRules\` is already a plain-JS array of
|
|
6164
|
+
* rule atoms. Walk it recursively, copying only known own-property keys so
|
|
6165
|
+
* the output is stable across OmniFocus versions and JSON-safe.
|
|
6166
|
+
*
|
|
6167
|
+
* iconColor: OmniJS exposes \`Color\` objects with \`red\`/\`green\`/\`blue\`/\`alpha\`
|
|
6168
|
+
* accessors; serialize as \`{ r, g, b, a }\` in [0, 1] floats, or null when
|
|
6169
|
+
* the perspective has no custom color.
|
|
6170
|
+
*
|
|
6171
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 getCustomPerspective()
|
|
6172
|
+
* @see src/domain/perspective.ts \u2014 PerspectiveDetail
|
|
6173
|
+
*/
|
|
6174
|
+
(() => {
|
|
6175
|
+
const { identifier } = globalThis.__args;
|
|
6176
|
+
|
|
6177
|
+
if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
|
|
6178
|
+
return JSON.stringify({
|
|
6179
|
+
error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
|
|
6183
|
+
const persp = Perspective.Custom.byIdentifier(identifier);
|
|
6184
|
+
if (persp === null || persp === undefined) {
|
|
6185
|
+
return JSON.stringify({
|
|
6186
|
+
error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
|
|
6187
|
+
});
|
|
6188
|
+
}
|
|
6189
|
+
|
|
6190
|
+
// Rule atom keys observed on real OmniFocus perspectives. Unknown keys are
|
|
6191
|
+
// preserved verbatim under \`unknown\` so a future OF release exposing a new
|
|
6192
|
+
// rule type doesn't silently drop data.
|
|
6193
|
+
const KNOWN_ATOM_KEYS = [
|
|
6194
|
+
"actionAvailability",
|
|
6195
|
+
"actionStatus",
|
|
6196
|
+
"actionHasAllOfTags",
|
|
6197
|
+
"actionHasAnyOfTags",
|
|
6198
|
+
"actionHasNoProject",
|
|
6199
|
+
"actionHasDueDate",
|
|
6200
|
+
"actionHasDeferDate",
|
|
6201
|
+
"actionIsLeaf",
|
|
6202
|
+
"actionIsProject",
|
|
6203
|
+
"actionMatchingSearch",
|
|
6204
|
+
"actionWithinFocus",
|
|
6205
|
+
];
|
|
6206
|
+
|
|
6207
|
+
function serializeRule(rule) {
|
|
6208
|
+
if (rule === null || rule === undefined || typeof rule !== "object") {
|
|
6209
|
+
return null;
|
|
6210
|
+
}
|
|
6211
|
+
// Disabled-rule wrapper.
|
|
6212
|
+
if (Object.hasOwn(rule, "disabledRule")) {
|
|
6213
|
+
const inner = serializeRule(rule.disabledRule);
|
|
6214
|
+
return inner === null ? null : { disabledRule: inner };
|
|
6215
|
+
}
|
|
6216
|
+
// Aggregate (compound) rule.
|
|
6217
|
+
if (Object.hasOwn(rule, "aggregateType")) {
|
|
6218
|
+
const children = Array.isArray(rule.aggregateRules) ? rule.aggregateRules : [];
|
|
6219
|
+
return {
|
|
6220
|
+
aggregateType: String(rule.aggregateType),
|
|
6221
|
+
aggregateRules: children.map(serializeRule).filter((r) => r !== null),
|
|
6222
|
+
};
|
|
6223
|
+
}
|
|
6224
|
+
// Atom rule \u2014 copy known keys; preserve unknown keys under \`unknown\`.
|
|
6225
|
+
const out = {};
|
|
6226
|
+
const unknown = {};
|
|
6227
|
+
let hasUnknown = false;
|
|
6228
|
+
for (const key of Object.keys(rule)) {
|
|
6229
|
+
const value = rule[key];
|
|
6230
|
+
if (KNOWN_ATOM_KEYS.indexOf(key) >= 0) {
|
|
6231
|
+
out[key] = value;
|
|
6232
|
+
} else {
|
|
6233
|
+
unknown[key] = value;
|
|
6234
|
+
hasUnknown = true;
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
if (hasUnknown) out.unknown = unknown;
|
|
6238
|
+
return out;
|
|
6239
|
+
}
|
|
6240
|
+
|
|
6241
|
+
function serializeColor(c) {
|
|
6242
|
+
if (c === null || c === undefined) return null;
|
|
6243
|
+
try {
|
|
6244
|
+
const r = typeof c.red === "number" ? c.red : null;
|
|
6245
|
+
const g = typeof c.green === "number" ? c.green : null;
|
|
6246
|
+
const b = typeof c.blue === "number" ? c.blue : null;
|
|
6247
|
+
const a = typeof c.alpha === "number" ? c.alpha : 1;
|
|
6248
|
+
if (r === null || g === null || b === null) return null;
|
|
6249
|
+
return { r, g, b, a };
|
|
6250
|
+
} catch (_e) {
|
|
6251
|
+
return null;
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6254
|
+
|
|
6255
|
+
let rawRules;
|
|
6256
|
+
try {
|
|
6257
|
+
rawRules = persp.archivedFilterRules;
|
|
6258
|
+
} catch (_e) {
|
|
6259
|
+
rawRules = null;
|
|
6260
|
+
}
|
|
6261
|
+
const rules = Array.isArray(rawRules)
|
|
6262
|
+
? rawRules.map(serializeRule).filter((r) => r !== null)
|
|
6263
|
+
: [];
|
|
6264
|
+
|
|
6265
|
+
let aggregation = null;
|
|
6266
|
+
try {
|
|
6267
|
+
const a = persp.archivedTopLevelFilterAggregation;
|
|
6268
|
+
aggregation = a === null || a === undefined ? "all" : String(a);
|
|
6269
|
+
} catch (_e) {
|
|
6270
|
+
aggregation = "all";
|
|
6271
|
+
}
|
|
6272
|
+
|
|
6273
|
+
let iconColor = null;
|
|
6274
|
+
try {
|
|
6275
|
+
iconColor = serializeColor(persp.iconColor);
|
|
6276
|
+
} catch (_e) {
|
|
6277
|
+
iconColor = null;
|
|
6278
|
+
}
|
|
6279
|
+
|
|
6280
|
+
let id;
|
|
6281
|
+
try {
|
|
6282
|
+
id = persp.identifier;
|
|
6283
|
+
} catch (_e) {
|
|
6284
|
+
id = identifier;
|
|
6285
|
+
}
|
|
6286
|
+
|
|
6287
|
+
return JSON.stringify({
|
|
6288
|
+
perspective: {
|
|
6289
|
+
id: String(id),
|
|
6290
|
+
name: String(persp.name),
|
|
6291
|
+
aggregation,
|
|
6292
|
+
rules,
|
|
6293
|
+
iconColor,
|
|
6294
|
+
},
|
|
6295
|
+
});
|
|
6296
|
+
})();
|
|
6297
|
+
`;var _g=`/**
|
|
6298
|
+
* perspective_update.js \u2014 partial-patch update of a custom OmniFocus
|
|
6299
|
+
* perspective.
|
|
6300
|
+
*
|
|
6301
|
+
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
6302
|
+
* { identifier, name?, aggregation?, rules?, iconColor? }
|
|
6303
|
+
*
|
|
6304
|
+
* Returns JSON: \`{ id: string }\` on success (echoes the identifier),
|
|
6305
|
+
* or \`{ error: { code, message } }\` for typed failures.
|
|
6306
|
+
*
|
|
6307
|
+
* Patch semantics:
|
|
6308
|
+
* - Only fields present in args are written. Omitted fields leave the
|
|
6309
|
+
* existing value unchanged.
|
|
6310
|
+
* - \`iconColor: null\` clears any custom color back to the OmniFocus default.
|
|
6311
|
+
* - \`iconColor: { r, g, b, a }\` writes a new color via Color.RGB(...).
|
|
6312
|
+
* - \`rules: []\` clears the rule tree to "show everything" (the default).
|
|
6313
|
+
*
|
|
6314
|
+
* Built-in perspectives have no rule tree to patch \u2014 \`Perspective.Custom
|
|
6315
|
+
* .byIdentifier\` returns null for them, which the script reports as
|
|
6316
|
+
* NOT_FOUND. The MCP-tool layer rejects built-in ids as VALIDATION_ERROR
|
|
6317
|
+
* before calling the adapter, so callers should rarely reach this branch.
|
|
6318
|
+
*
|
|
6319
|
+
* No rollback dance: unlike create, an update does not have a shell to
|
|
6320
|
+
* roll back \u2014 failures leave the perspective in whatever state OmniFocus
|
|
6321
|
+
* persisted before the throw. Tests that simulate mid-patch failure should
|
|
6322
|
+
* gate on real OmniFocus behavior.
|
|
6323
|
+
*
|
|
6324
|
+
* @see #577, #618
|
|
6325
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 updateCustomPerspective()
|
|
6326
|
+
* @see src/scripts/omnijs/perspective_create.js \u2014 sibling create script
|
|
6327
|
+
*/
|
|
6328
|
+
(() => {
|
|
6329
|
+
const { identifier, name, aggregation, rules, iconColor } = globalThis.__args;
|
|
6330
|
+
|
|
6331
|
+
if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
|
|
6332
|
+
return JSON.stringify({
|
|
6333
|
+
error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
|
|
6337
|
+
if (typeof identifier !== "string" || identifier.length === 0) {
|
|
6338
|
+
return JSON.stringify({
|
|
6339
|
+
error: { code: "VALIDATION_ERROR", message: "identifier is required and must be non-empty" },
|
|
6340
|
+
});
|
|
6341
|
+
}
|
|
6342
|
+
|
|
6343
|
+
const persp = Perspective.Custom.byIdentifier(identifier);
|
|
6344
|
+
if (persp === null || persp === undefined) {
|
|
6345
|
+
return JSON.stringify({
|
|
6346
|
+
error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
|
|
6347
|
+
});
|
|
6348
|
+
}
|
|
6349
|
+
|
|
6350
|
+
try {
|
|
6351
|
+
if (typeof name === "string") {
|
|
6352
|
+
if (name.length === 0) {
|
|
6353
|
+
return JSON.stringify({
|
|
6354
|
+
error: { code: "VALIDATION_ERROR", message: "name must be non-empty" },
|
|
6355
|
+
});
|
|
6356
|
+
}
|
|
6357
|
+
persp.name = name;
|
|
6358
|
+
}
|
|
6359
|
+
if (typeof aggregation === "string") {
|
|
6360
|
+
persp.archivedTopLevelFilterAggregation = aggregation;
|
|
6361
|
+
}
|
|
6362
|
+
if (Array.isArray(rules)) {
|
|
6363
|
+
// Same passthrough convention as perspective_create.js \u2014 the input
|
|
6364
|
+
// shape mirrors the read-side shape in perspective_get.js so
|
|
6365
|
+
// archivedFilterRules accepts it verbatim.
|
|
6366
|
+
persp.archivedFilterRules = rules;
|
|
6367
|
+
}
|
|
6368
|
+
if (iconColor === null) {
|
|
6369
|
+
// Explicit null clears the custom color back to the OmniFocus default.
|
|
6370
|
+
persp.iconColor = null;
|
|
6371
|
+
} else if (
|
|
6372
|
+
iconColor !== undefined &&
|
|
6373
|
+
typeof iconColor === "object" &&
|
|
6374
|
+
typeof iconColor.r === "number" &&
|
|
6375
|
+
typeof iconColor.g === "number" &&
|
|
6376
|
+
typeof iconColor.b === "number" &&
|
|
6377
|
+
typeof iconColor.a === "number"
|
|
6378
|
+
) {
|
|
6379
|
+
persp.iconColor = Color.RGB(iconColor.r, iconColor.g, iconColor.b, iconColor.a);
|
|
6380
|
+
}
|
|
6381
|
+
} catch (e) {
|
|
6382
|
+
const msg = String(e?.message ? e.message : e);
|
|
6383
|
+
if (msg.toLowerCase().indexOf("already") >= 0 || msg.toLowerCase().indexOf("duplicate") >= 0) {
|
|
6384
|
+
return JSON.stringify({
|
|
6385
|
+
error: { code: "VALIDATION_ERROR", message: \`Duplicate perspective name: \${name}\` },
|
|
6386
|
+
});
|
|
6387
|
+
}
|
|
6388
|
+
return JSON.stringify({
|
|
6389
|
+
error: { code: "SCRIPT_ERROR", message: msg },
|
|
6390
|
+
});
|
|
6391
|
+
}
|
|
6392
|
+
|
|
6393
|
+
return JSON.stringify({ id: identifier });
|
|
6394
|
+
})();
|
|
6395
|
+
`;var Pg=`/**
|
|
6396
|
+
* plugin_invoke.js \u2014 invoke a named Omni Automation plug-in action.
|
|
6397
|
+
*
|
|
6398
|
+
* Called via the OmniJS transport (evaluateJavascript bridge).
|
|
6399
|
+
* Args injected as \`globalThis.__args\`:
|
|
6400
|
+
* { identifier: string, arg?: unknown }
|
|
6401
|
+
*
|
|
6402
|
+
* Returns a JSON string: { result: <plug-in return value> }
|
|
6403
|
+
*
|
|
6404
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 pluginInvoke()
|
|
6405
|
+
* @see docs/adr/0005-scripts-as-first-class-files.md
|
|
6406
|
+
*/
|
|
6407
|
+
(() => {
|
|
6408
|
+
const { identifier, arg = null } = globalThis.__args;
|
|
6409
|
+
|
|
6410
|
+
const plugin = PlugIn.find(identifier);
|
|
6411
|
+
if (plugin === null) {
|
|
6412
|
+
throw new Error(\`PlugIn not found: \${identifier}\`);
|
|
6413
|
+
}
|
|
6414
|
+
|
|
6415
|
+
// The PlugIn.Action runtime is available from OmniFocus Standard+.
|
|
6416
|
+
// \`plugin.action(identifier)\` returns the default action when no name is
|
|
6417
|
+
// supplied; per the Omni Automation spec the default action is the one
|
|
6418
|
+
// whose \`name\` matches the plug-in's bundle identifier.
|
|
6419
|
+
const action = plugin.action(identifier);
|
|
6420
|
+
if (action === null) {
|
|
6421
|
+
throw new Error(\`No default action found in PlugIn: \${identifier}\`);
|
|
6422
|
+
}
|
|
6423
|
+
|
|
6424
|
+
const rawResult = action.perform([arg]);
|
|
6425
|
+
return JSON.stringify({ result: rawResult ?? null });
|
|
6426
|
+
})();
|
|
6427
|
+
`;var Og=`/**
|
|
6428
|
+
* OmniJS: batch-move tasks to different destinations in a single round-trip.
|
|
6429
|
+
*
|
|
6430
|
+
* JXA's \`task.move()\` is unimplemented in OmniFocus 4.x (error 9 "Replacement
|
|
6431
|
+
* not supported currently"). The OmniJS \`moveTasks(tasks, location)\` API performs
|
|
6432
|
+
* genuine reparenting while preserving persistent IDs \u2014 hence this script routes
|
|
6433
|
+
* through OmniJS.
|
|
6434
|
+
*
|
|
6435
|
+
* Args injected as \`globalThis.__args\`:
|
|
6436
|
+
* { items: Array<{ id: string, projectId?: string|null, parentId?: string|null }> }
|
|
6437
|
+
* For each item: pass projectId to move into a project, parentId to move under a
|
|
6438
|
+
* parent task, or neither to move to the inbox.
|
|
6439
|
+
*
|
|
6440
|
+
* Returns JSON: { succeeded: [{index, value}], failed: [{index, errorCode, message}] }
|
|
6441
|
+
*
|
|
6442
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 batchMoveTasks() caller
|
|
6443
|
+
* @see src/scripts/omnijs/task_move.js \u2014 singular counterpart
|
|
6444
|
+
* @see docs/adr/0002-omnifocus-transport-dual.md \u2014 JXA/OmniJS split rationale
|
|
6445
|
+
*/
|
|
6446
|
+
(() => {
|
|
6447
|
+
const { items } = globalThis.__args;
|
|
6448
|
+
|
|
6449
|
+
if (!items || !Array.isArray(items)) {
|
|
6450
|
+
return JSON.stringify({
|
|
6451
|
+
error: { code: "VALIDATION", message: "items array is required" },
|
|
6452
|
+
});
|
|
6453
|
+
}
|
|
6454
|
+
|
|
6455
|
+
const succeeded = [];
|
|
6456
|
+
const failed = [];
|
|
6457
|
+
|
|
6458
|
+
for (let i = 0; i < items.length; i++) {
|
|
6459
|
+
const it = items[i];
|
|
6460
|
+
try {
|
|
6461
|
+
const task = flattenedTasks.filter((t) => t.id.primaryKey === it.id)[0];
|
|
6462
|
+
if (!task) {
|
|
6463
|
+
failed.push({ index: i, errorCode: "OF_NOT_FOUND", message: \`Task not found: \${it.id}\` });
|
|
6464
|
+
continue;
|
|
6465
|
+
}
|
|
6466
|
+
|
|
6467
|
+
if (it.parentId != null) {
|
|
6468
|
+
const parent = flattenedTasks.filter((t) => t.id.primaryKey === it.parentId)[0];
|
|
6469
|
+
if (!parent) {
|
|
6470
|
+
failed.push({
|
|
6471
|
+
index: i,
|
|
6472
|
+
errorCode: "OF_NOT_FOUND",
|
|
6473
|
+
message: \`Parent task not found: \${it.parentId}\`,
|
|
6474
|
+
});
|
|
6475
|
+
continue;
|
|
6476
|
+
}
|
|
6477
|
+
moveTasks([task], parent);
|
|
6478
|
+
} else if (it.projectId != null) {
|
|
6479
|
+
const proj = flattenedProjects.filter((p) => p.id.primaryKey === it.projectId)[0];
|
|
6480
|
+
if (!proj) {
|
|
6481
|
+
failed.push({
|
|
6482
|
+
index: i,
|
|
6483
|
+
errorCode: "OF_NOT_FOUND",
|
|
6484
|
+
message: \`Project not found: \${it.projectId}\`,
|
|
6485
|
+
});
|
|
6486
|
+
continue;
|
|
6487
|
+
}
|
|
6488
|
+
moveTasks([task], proj);
|
|
6489
|
+
} else {
|
|
6490
|
+
// No destination \u2014 move to inbox.
|
|
6491
|
+
moveTasks([task], inbox.beginning);
|
|
6492
|
+
}
|
|
6493
|
+
|
|
6494
|
+
succeeded.push({ index: i, value: it.id });
|
|
6495
|
+
} catch (e) {
|
|
6496
|
+
const msg = String(e?.message ?? e);
|
|
6497
|
+
failed.push({ index: i, errorCode: "OF_UNKNOWN", message: msg });
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
|
|
6501
|
+
return JSON.stringify({ succeeded, failed });
|
|
6502
|
+
})();
|
|
6503
|
+
`;var xg=`/**
|
|
6504
|
+
* OmniJS: clear all alarms/notifications from a task.
|
|
6505
|
+
*
|
|
6506
|
+
* Args injected as \`globalThis.__args\`: { taskId: string }
|
|
6507
|
+
*
|
|
6508
|
+
* Returns JSON: { ok: true } | { error: { code, message } }
|
|
6509
|
+
*
|
|
6510
|
+
* Equivalent to \`task_set_alarms\` with an empty \`alarms\` array, but
|
|
6511
|
+
* exposed as a dedicated verb so the tool surface is consistent with
|
|
6512
|
+
* \`task_clear_repetition\`.
|
|
6513
|
+
*
|
|
6514
|
+
* @see #461
|
|
6515
|
+
*/
|
|
6516
|
+
(() => {
|
|
6517
|
+
const args = globalThis.__args || {};
|
|
6518
|
+
const taskId = args.taskId;
|
|
6519
|
+
|
|
6520
|
+
if (typeof taskId !== "string" || !taskId) {
|
|
6521
|
+
return JSON.stringify({
|
|
6522
|
+
error: { code: "VALIDATION", message: "taskId must be a non-empty string" },
|
|
6523
|
+
});
|
|
6524
|
+
}
|
|
6525
|
+
|
|
6526
|
+
const task = Task.byIdentifier(taskId);
|
|
6527
|
+
if (!task) {
|
|
6528
|
+
return JSON.stringify({
|
|
6529
|
+
error: { code: "NOT_FOUND", message: \`Task \${taskId} not found\` },
|
|
6530
|
+
});
|
|
6531
|
+
}
|
|
6532
|
+
|
|
6533
|
+
const existing = Array.from(task.notifications);
|
|
6534
|
+
for (const n of existing) {
|
|
6535
|
+
try {
|
|
6536
|
+
n.removeFromContainer();
|
|
6537
|
+
} catch (_e) {
|
|
6538
|
+
// Best-effort; continue
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
|
|
6542
|
+
return JSON.stringify({ ok: true });
|
|
6543
|
+
})();
|
|
6544
|
+
`;var Dg=`/**
|
|
6545
|
+
* OmniJS: convert a task to a project via Database.convertTasksToProjects().
|
|
6546
|
+
*
|
|
6547
|
+
* This operation is only available via OmniJS \u2014 JXA has no equivalent.
|
|
6548
|
+
* The task's persistent identifier is preserved on the resulting project
|
|
6549
|
+
* (i.e. \`Project.byIdentifier(task.id.primaryKey)\` resolves after conversion).
|
|
6550
|
+
*
|
|
6551
|
+
* Args injected as \`globalThis.__args\`:
|
|
6552
|
+
* {
|
|
6553
|
+
* id: string, // task primary key
|
|
6554
|
+
* folderId?: string, // if set, place in this folder
|
|
6555
|
+
* position?: "beginning" | "ending" // default "ending"
|
|
6556
|
+
* }
|
|
6557
|
+
*
|
|
6558
|
+
* Returns JSON: { projectId: string }
|
|
6559
|
+
*
|
|
6560
|
+
* @see src/tools/task/convertToProject.ts \u2014 handler
|
|
6561
|
+
* @see https://github.com/torsday/omnifocus-mcp/issues/525
|
|
6562
|
+
*/
|
|
6563
|
+
(() => {
|
|
6564
|
+
const { id, folderId, position } = globalThis.__args;
|
|
6565
|
+
|
|
6566
|
+
if (!id) {
|
|
6567
|
+
return JSON.stringify({ error: { code: "VALIDATION", message: "id is required" } });
|
|
6568
|
+
}
|
|
6569
|
+
|
|
6570
|
+
const task = flattenedTasks.filter((t) => t.id.primaryKey === id)[0];
|
|
6571
|
+
if (!task) {
|
|
6572
|
+
return JSON.stringify({ error: { code: "NOT_FOUND", message: \`Task not found: \${id}\` } });
|
|
6573
|
+
}
|
|
6574
|
+
|
|
6575
|
+
// Resolve the position argument for convertTasksToProjects.
|
|
6576
|
+
// Valid positions: library.beginning, library.ending,
|
|
6577
|
+
// folder.children.beginning, folder.children.ending
|
|
6578
|
+
let pos;
|
|
6579
|
+
const atBeginning = position === "beginning";
|
|
6580
|
+
|
|
6581
|
+
if (folderId != null) {
|
|
6582
|
+
const folder =
|
|
6583
|
+
library.folders.filter((f) => f.id.primaryKey === folderId)[0] ??
|
|
6584
|
+
flattenedFolders.filter((f) => f.id.primaryKey === folderId)[0];
|
|
6585
|
+
if (!folder) {
|
|
6586
|
+
return JSON.stringify({
|
|
6587
|
+
error: { code: "NOT_FOUND", message: \`Folder not found: \${folderId}\` },
|
|
6588
|
+
});
|
|
6589
|
+
}
|
|
6590
|
+
pos = atBeginning ? folder.children.beginning : folder.children.ending;
|
|
6591
|
+
} else {
|
|
6592
|
+
pos = atBeginning ? library.beginning : library.ending;
|
|
6593
|
+
}
|
|
6594
|
+
|
|
6595
|
+
const results = convertTasksToProjects([task], pos);
|
|
6596
|
+
|
|
6597
|
+
// convertTasksToProjects returns an array of Projects parallel to the input.
|
|
6598
|
+
const project = results[0];
|
|
6599
|
+
if (!project) {
|
|
6600
|
+
return JSON.stringify({
|
|
6601
|
+
error: { code: "CONVERSION_FAILED", message: "convertTasksToProjects returned no project" },
|
|
6602
|
+
});
|
|
6603
|
+
}
|
|
6604
|
+
|
|
6605
|
+
return JSON.stringify({ projectId: project.id.primaryKey });
|
|
6606
|
+
})();
|
|
6607
|
+
`;var Rg=`/**
|
|
6608
|
+
* OmniJS: move a task to a different project, parent task, or the inbox.
|
|
6609
|
+
*
|
|
6610
|
+
* JXA's \`task.move({ to: container })\` is unimplemented in OmniFocus 4.x
|
|
6611
|
+
* (error 9 "Replacement not supported currently"). The OmniJS
|
|
6612
|
+
* \`Database.moveTasks(tasks, location)\` API performs genuine reparenting
|
|
6613
|
+
* while preserving the task's persistent ID \u2014 hence this script routes
|
|
6614
|
+
* through the OmniJS transport instead.
|
|
6615
|
+
*
|
|
6616
|
+
* Args injected as \`globalThis.__args\`:
|
|
6617
|
+
* { id: string, projectId?: string|null, parentId?: string|null }
|
|
6618
|
+
* Pass neither projectId nor parentId to move the task to the inbox.
|
|
6619
|
+
*
|
|
6620
|
+
* Returns JSON: { id: string }
|
|
6621
|
+
*
|
|
6622
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 moveTask() caller
|
|
6623
|
+
* @see docs/adr/0002-omnifocus-transport-dual.md \u2014 JXA/OmniJS split rationale
|
|
6624
|
+
*/
|
|
6625
|
+
(() => {
|
|
6626
|
+
const { id, projectId, parentId } = globalThis.__args;
|
|
6627
|
+
|
|
6628
|
+
if (!id) {
|
|
6629
|
+
return JSON.stringify({ error: { code: "VALIDATION", message: "id is required" } });
|
|
6630
|
+
}
|
|
6631
|
+
|
|
6632
|
+
const task = flattenedTasks.filter((t) => t.id.primaryKey === id)[0];
|
|
6633
|
+
if (!task) {
|
|
6634
|
+
return JSON.stringify({ error: { code: "NOT_FOUND", message: \`Task not found: \${id}\` } });
|
|
6635
|
+
}
|
|
6636
|
+
|
|
6637
|
+
if (parentId != null) {
|
|
6638
|
+
// Move under a parent task.
|
|
6639
|
+
const parent = flattenedTasks.filter((t) => t.id.primaryKey === parentId)[0];
|
|
6640
|
+
if (!parent) {
|
|
6641
|
+
return JSON.stringify({
|
|
6642
|
+
error: { code: "NOT_FOUND", message: \`Parent task not found: \${parentId}\` },
|
|
6643
|
+
});
|
|
6644
|
+
}
|
|
6645
|
+
moveTasks([task], parent);
|
|
6646
|
+
} else if (projectId != null) {
|
|
6647
|
+
// Move into a project (as a top-level action of that project).
|
|
6648
|
+
const proj = flattenedProjects.filter((p) => p.id.primaryKey === projectId)[0];
|
|
6649
|
+
if (!proj) {
|
|
6650
|
+
return JSON.stringify({
|
|
6651
|
+
error: { code: "NOT_FOUND", message: \`Project not found: \${projectId}\` },
|
|
6652
|
+
});
|
|
6653
|
+
}
|
|
6654
|
+
moveTasks([task], proj);
|
|
6655
|
+
} else {
|
|
6656
|
+
// No destination specified \u2014 move to inbox.
|
|
6657
|
+
// \`inbox\` alone is not a valid moveTasks position; use inbox.beginning.
|
|
6658
|
+
moveTasks([task], inbox.beginning);
|
|
6659
|
+
}
|
|
6660
|
+
|
|
6661
|
+
return JSON.stringify({ id });
|
|
6662
|
+
})();
|
|
6663
|
+
`;var Ag=`/**
|
|
6664
|
+
* OmniJS: reorder a task among its siblings.
|
|
6665
|
+
*
|
|
6666
|
+
* JXA's \`task.move({ to: ref, positioned: ... })\` shares the same broken
|
|
6667
|
+
* code path as \`task.move({ to: container })\` \u2014 both throw error 9
|
|
6668
|
+
* ("Replacement not supported currently") in OmniFocus 4.x. The OmniJS
|
|
6669
|
+
* \`Database.moveTasks(tasks, ChildInsertionLocation)\` API supports precise
|
|
6670
|
+
* sibling positioning via \`.before\` / \`.after\` / \`.beginning\` / \`.ending\`
|
|
6671
|
+
* and is the Omni-recommended automation route \u2014 hence this script.
|
|
6672
|
+
*
|
|
6673
|
+
* Args injected as \`globalThis.__args\`:
|
|
6674
|
+
* {
|
|
6675
|
+
* id: string, // task to reorder
|
|
6676
|
+
* mode: "before" | "after" | "start" | "end",
|
|
6677
|
+
* refId?: string, // required for before/after
|
|
6678
|
+
* container?: { // required for start/end
|
|
6679
|
+
* projectId?: string,
|
|
6680
|
+
* parentId?: string,
|
|
6681
|
+
* inbox?: true,
|
|
6682
|
+
* }
|
|
6683
|
+
* }
|
|
6684
|
+
*
|
|
6685
|
+
* Returns JSON: { id: string }
|
|
6686
|
+
*
|
|
6687
|
+
* @see src/adapter/omnijs/OmniJsTransport.ts \u2014 reorderTask() caller
|
|
6688
|
+
* @see docs/spikes/2026-04-task-reorder.md \u2014 Route A vs Route B rationale
|
|
6689
|
+
* @see docs/adr/0002-omnifocus-transport-dual.md \u2014 JXA/OmniJS split
|
|
6690
|
+
*/
|
|
6691
|
+
(() => {
|
|
6692
|
+
const { id, mode, refId, container } = globalThis.__args;
|
|
6693
|
+
|
|
6694
|
+
if (!id) {
|
|
6695
|
+
return JSON.stringify({ error: { code: "VALIDATION", message: "id is required" } });
|
|
6696
|
+
}
|
|
6697
|
+
if (!mode) {
|
|
6698
|
+
return JSON.stringify({ error: { code: "VALIDATION", message: "mode is required" } });
|
|
6699
|
+
}
|
|
6700
|
+
|
|
6701
|
+
const task = flattenedTasks.filter((t) => t.id.primaryKey === id)[0];
|
|
6702
|
+
if (!task) {
|
|
6703
|
+
return JSON.stringify({ error: { code: "NOT_FOUND", message: \`Task not found: \${id}\` } });
|
|
6704
|
+
}
|
|
6705
|
+
|
|
6706
|
+
let location;
|
|
6707
|
+
|
|
6708
|
+
if (mode === "before" || mode === "after") {
|
|
6709
|
+
if (!refId) {
|
|
6710
|
+
return JSON.stringify({
|
|
6711
|
+
error: { code: "VALIDATION", message: \`refId is required for mode "\${mode}"\` },
|
|
6712
|
+
});
|
|
6713
|
+
}
|
|
6714
|
+
const ref = flattenedTasks.filter((t) => t.id.primaryKey === refId)[0];
|
|
6715
|
+
if (!ref) {
|
|
6716
|
+
return JSON.stringify({
|
|
6717
|
+
error: { code: "NOT_FOUND", message: \`Reference task not found: \${refId}\` },
|
|
6718
|
+
});
|
|
6719
|
+
}
|
|
6720
|
+
location = mode === "before" ? ref.before : ref.after;
|
|
6721
|
+
} else if (mode === "start" || mode === "end") {
|
|
6722
|
+
let c;
|
|
6723
|
+
if (container?.projectId) {
|
|
6724
|
+
c = flattenedProjects.filter((p) => p.id.primaryKey === container.projectId)[0];
|
|
6725
|
+
if (!c) {
|
|
6726
|
+
return JSON.stringify({
|
|
6727
|
+
error: { code: "NOT_FOUND", message: \`Project not found: \${container.projectId}\` },
|
|
6728
|
+
});
|
|
6729
|
+
}
|
|
6730
|
+
} else if (container?.parentId) {
|
|
6731
|
+
c = flattenedTasks.filter((t) => t.id.primaryKey === container.parentId)[0];
|
|
6732
|
+
if (!c) {
|
|
6733
|
+
return JSON.stringify({
|
|
6734
|
+
error: { code: "NOT_FOUND", message: \`Parent task not found: \${container.parentId}\` },
|
|
6735
|
+
});
|
|
6736
|
+
}
|
|
6737
|
+
} else {
|
|
6738
|
+
// inbox.beginning / inbox.ending
|
|
6739
|
+
c = inbox;
|
|
6740
|
+
}
|
|
6741
|
+
location = mode === "start" ? c.beginning : c.ending;
|
|
6742
|
+
} else {
|
|
6743
|
+
return JSON.stringify({
|
|
6744
|
+
error: { code: "VALIDATION", message: \`Unknown mode: \${mode}\` },
|
|
6745
|
+
});
|
|
6746
|
+
}
|
|
6747
|
+
|
|
6748
|
+
moveTasks([task], location);
|
|
6749
|
+
return JSON.stringify({ id });
|
|
6750
|
+
})();
|
|
6751
|
+
`;var Cg=`/**
|
|
6752
|
+
* OmniJS: replace a task's alarm/notification set atomically.
|
|
6753
|
+
*
|
|
6754
|
+
* Args injected as \`globalThis.__args\`: { taskId: string, alarms: TaskAlarm[] }
|
|
6755
|
+
*
|
|
6756
|
+
* TaskAlarm =
|
|
6757
|
+
* | { kind: "due-relative", offsetSeconds: number }
|
|
6758
|
+
* | { kind: "defer-relative", offsetSeconds: number }
|
|
6759
|
+
* | { kind: "absolute", fireAt: ISO-8601 string }
|
|
6760
|
+
*
|
|
6761
|
+
* Algorithm:
|
|
6762
|
+
* 1. Resolve the task by primary-key.
|
|
6763
|
+
* 2. Drop every existing notification on the task (full-replace; the
|
|
6764
|
+
* MCP tool layer already enforced this is the intended semantics).
|
|
6765
|
+
* 3. Add each requested alarm via \`Task.addNotification\`. OmniJS's
|
|
6766
|
+
* addNotification accepts either a Date (for absolute) or a Number
|
|
6767
|
+
* of seconds (for relative; positive = before, negative = after).
|
|
6768
|
+
* 4. Return JSON: { ok: true } on success, or
|
|
6769
|
+
* { error: { code, message } } on transport-level failures.
|
|
6770
|
+
*
|
|
6771
|
+
* The tool-layer pre-validates that relative alarms have a corresponding
|
|
6772
|
+
* date anchor; the script trusts its inputs.
|
|
6773
|
+
*
|
|
6774
|
+
* @see #461
|
|
6775
|
+
*/
|
|
6776
|
+
(() => {
|
|
6777
|
+
const args = globalThis.__args || {};
|
|
6778
|
+
const taskId = args.taskId;
|
|
6779
|
+
const alarms = Array.isArray(args.alarms) ? args.alarms : [];
|
|
6780
|
+
|
|
6781
|
+
if (typeof taskId !== "string" || !taskId) {
|
|
6782
|
+
return JSON.stringify({
|
|
6783
|
+
error: { code: "VALIDATION", message: "taskId must be a non-empty string" },
|
|
6784
|
+
});
|
|
6785
|
+
}
|
|
6786
|
+
|
|
6787
|
+
const task = Task.byIdentifier(taskId);
|
|
6788
|
+
if (!task) {
|
|
6789
|
+
return JSON.stringify({
|
|
6790
|
+
error: { code: "NOT_FOUND", message: \`Task \${taskId} not found\` },
|
|
6791
|
+
});
|
|
6792
|
+
}
|
|
6793
|
+
|
|
6794
|
+
// Phase 1: clear all existing notifications.
|
|
6795
|
+
// Iterate over a snapshot \u2014 modifying the live collection during the loop
|
|
6796
|
+
// would skip entries.
|
|
6797
|
+
const existing = Array.from(task.notifications);
|
|
6798
|
+
for (const n of existing) {
|
|
6799
|
+
try {
|
|
6800
|
+
n.removeFromContainer();
|
|
6801
|
+
} catch (_e) {
|
|
6802
|
+
// Continue best-effort; some notifications resist removal but the
|
|
6803
|
+
// subsequent addNotification calls re-derive state from scratch.
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6807
|
+
// Phase 2: add each new alarm.
|
|
6808
|
+
try {
|
|
6809
|
+
for (const alarm of alarms) {
|
|
6810
|
+
if (alarm.kind === "absolute") {
|
|
6811
|
+
const fireAt = new Date(alarm.fireAt);
|
|
6812
|
+
if (Number.isNaN(fireAt.getTime())) {
|
|
6813
|
+
return JSON.stringify({
|
|
6814
|
+
error: { code: "VALIDATION", message: \`invalid fireAt: \${alarm.fireAt}\` },
|
|
6815
|
+
});
|
|
6816
|
+
}
|
|
6817
|
+
task.addNotification(fireAt);
|
|
6818
|
+
} else if (alarm.kind === "due-relative" || alarm.kind === "defer-relative") {
|
|
6819
|
+
const seconds = Number(alarm.offsetSeconds);
|
|
6820
|
+
if (!Number.isFinite(seconds)) {
|
|
6821
|
+
return JSON.stringify({
|
|
6822
|
+
error: { code: "VALIDATION", message: \`invalid offsetSeconds: \${alarm.offsetSeconds}\` },
|
|
6823
|
+
});
|
|
6824
|
+
}
|
|
6825
|
+
// OmniJS's addNotification(Number) treats the value as seconds-from-now
|
|
6826
|
+
// for absolute; relative-to-due uses Task.dueDate / deferDate offsets.
|
|
6827
|
+
// The OmniJS API accepts a fireOffset relative to the task's anchor
|
|
6828
|
+
// when called with a Number. The kind distinguishes which anchor
|
|
6829
|
+
// OmniFocus uses internally \u2014 \`addNotification\` reads the task's
|
|
6830
|
+
// dueDate / deferDate when constructing the relative offset.
|
|
6831
|
+
task.addNotification(seconds);
|
|
6832
|
+
} else {
|
|
6833
|
+
return JSON.stringify({
|
|
6834
|
+
error: { code: "VALIDATION", message: \`unknown alarm kind: \${alarm.kind}\` },
|
|
6835
|
+
});
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
} catch (e) {
|
|
6839
|
+
const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
|
|
6840
|
+
return JSON.stringify({
|
|
6841
|
+
error: { code: "ADD_FAILED", message: msg },
|
|
6842
|
+
});
|
|
6843
|
+
}
|
|
6844
|
+
|
|
6845
|
+
return JSON.stringify({ ok: true });
|
|
6846
|
+
})();
|
|
6847
|
+
`;var Zb=16*1024*1024,Qb=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-"],{timeout:n,maxBuffer:Zb,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,i,s)=>{let c=i,d=s,l=a!==null&&a.killed===true,m=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:d,exitCode:a===null?0:a.code??1,timedOut:l,...m!==void 0?{spawnError:m}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");});async function Q(t,e={},n={}){let r=n.spawner??Qb,o=n.timeoutMs??45e3,a=JSON.stringify(e??{}),i=n.scriptName,s=ej(t,a),c=performance.now(),d=await r(s,a,o),l=Math.round(performance.now()-c),m=d.spawnError!==void 0||d.timedOut||d.exitCode!==0||d.stdout.trim()===""?"error":"ok";if(gr("omnijs",i,e,l,m),d.spawnError!==void 0)throw new ht("Failed to spawn osascript",{cause:d.spawnError,details:{transport:"omnijs",reason:d.spawnError.code??"spawn-failed",...i!==void 0?{scriptName:i}:{}}});if(d.timedOut){let g=i!==void 0?` (script: ${i})`:"";throw new gt(`OmniJS script exceeded ${o}ms timeout${g}`,{details:{transport:"omnijs",timeoutMs:o,...i!==void 0?{scriptName:i}:{}}})}if(d.exitCode!==0){let g=tj(d.stderr,i);if(g!==null)throw g;let v=i!==void 0?` [${i}]`:"";throw new N(`OmniJS script failed (exit ${d.exitCode})${v}`,{details:{transport:"omnijs",exitCode:d.exitCode,stderr:yr(d.stderr,1024),...i!==void 0?{scriptName:i}:{}}})}let f=d.stdout.trim();if(f==="")throw new N("OmniJS script returned empty stdout \u2014 the IIFE must return a JSON-encoded string",{details:{transport:"omnijs",...i!==void 0?{scriptName:i}:{}}});try{return JSON.parse(f)}catch(g){throw new N("OmniJS script returned malformed JSON",{cause:g,details:{transport:"omnijs",stdoutPreview:yr(f,200),...i!==void 0?{scriptName:i}:{}}})}}function ej(t,e){let n=`globalThis.__args = ${e};
|
|
6848
|
+
${t}`;return ["function run(_argv) {",' const ofApp = Application("OmniFocus");'," ofApp.includeStandardAdditions = false;",` const __omnijs = ${JSON.stringify(n)};`," const __result = ofApp.evaluateJavascript(__omnijs);"," return __result;","}"].join(`
|
|
6849
|
+
`)}function tj(t,e){return /Application can't be found/i.test(t)||/Application isn['’]t running/i.test(t)||/OmniFocus(?:.*)not running/i.test(t)?new ut({details:{transport:"omnijs",stderr:yr(t,512),...e!==void 0?{scriptName:e}:{}}}):/-1743\b/.test(t)||/not authori[sz]ed to send Apple events/i.test(t)||/errAEEventNotPermitted/i.test(t)||/not allowed assistive access/i.test(t)?new mt({details:{transport:"omnijs",stderr:yr(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function yr(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function x(t){throw new N(`OmniJsTransport.${t} is not wired yet`,{details:{transport:"omnijs",reason:"not-yet-wired",method:t}})}var kr=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return x("listTasks")}async getTask(e){return x("getTask")}async getTasksMany(e){return x("getTasksMany")}async createTask(e){return x("createTask")}async updateTask(e,n){return x("updateTask")}async completeTask(e,n){return x("completeTask")}async uncompleteTask(e){return x("uncompleteTask")}async dropTask(e,n){return x("dropTask")}async undropTask(e){return x("undropTask")}async deleteTask(e){return x("deleteTask")}async moveTask(e,n){let o=await Q(Rg,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});if(ne(o))throw o.error.code==="NOT_FOUND"?new P(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}}):new y(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}})}async convertTaskToProject(e,n){let r=await Q(Dg,{id:e,folderId:n.folderId??null,position:n.position??"ending"},{...this.runOpts,scriptName:"task_convert_to_project"});if(ne(r))throw r.error.code==="NOT_FOUND"?new P(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}}):new y(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}});return k.of(r.projectId)}async batchMoveTasks(e){let r=await Q(Og,{items:e.map(o=>({id:o.id,projectId:o.destination.projectId??null,parentId:o.destination.parentId??null}))},{...this.runOpts,scriptName:"task_batch_move"});if(ne(r))throw new y(r.error.message,{details:{transport:"omnijs",scriptName:"task_batch_move"}});return Pe(r,h.of)}async reorderTask(e,n){let r=Ag,o;if("before"in n)o={id:e,mode:"before",refId:n.before};else if("after"in n)o={id:e,mode:"after",refId:n.after};else {let i="projectId"in n.in?{projectId:n.in.projectId}:"parentId"in n.in?{parentId:n.in.parentId}:{inbox:true};o={id:e,mode:n.at,container:i};}let a=await Q(r,o,{...this.runOpts,scriptName:"task_reorder"});if(ne(a))throw a.error.code==="NOT_FOUND"?new P(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}}):new y(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}})}async duplicateTask(e,n){return x("duplicateTask")}async batchCreateTasks(e){return x("batchCreateTasks")}async batchUpdateTasks(e){return x("batchUpdateTasks")}async batchCompleteTasks(e){return x("batchCompleteTasks")}async batchUncompleteTasks(e){return x("batchUncompleteTasks")}async batchDeleteTasks(e){return x("batchDeleteTasks")}async batchDropTasks(e){return x("batchDropTasks")}async batchUndropTasks(e){return x("batchUndropTasks")}async listProjects(e){return x("listProjects")}async getProject(e){return x("getProject")}async getProjectsMany(e){return x("getProjectsMany")}async createProject(e){return x("createProject")}async updateProject(e,n){return x("updateProject")}async completeProject(e,n){return x("completeProject")}async dropProject(e,n){return x("dropProject")}async batchCompleteProjects(e){return x("batchCompleteProjects")}async batchDropProjects(e){return x("batchDropProjects")}async moveProject(e,n){return x("moveProject")}async deleteProject(e){return x("deleteProject")}async markProjectReviewed(e){return x("markProjectReviewed")}async listProjectsDueForReview(){return x("listProjectsDueForReview")}async setProjectReviewInterval(e,n){return x("setProjectReviewInterval")}async setProjectNextReviewDate(e,n){return x("setProjectNextReviewDate")}async listTags(e){return x("listTags")}async getTag(e){return x("getTag")}async getTagsMany(e){return x("getTagsMany")}async createTag(e){return x("createTag")}async updateTag(e,n){return x("updateTag")}async deleteTag(e){return x("deleteTag")}async listFolders(e){return x("listFolders")}async getFolder(e){return x("getFolder")}async createFolder(e){return x("createFolder")}async updateFolder(e,n){return x("updateFolder")}async deleteFolder(e){return x("deleteFolder")}async searchTasks(e){return x("searchTasks")}async getForecast(e){return x("getForecast")}async getForecastTag(){let n=await Q(Ig,{},{...this.runOpts,scriptName:"forecast_get_tag"});if(ne(n))throw new N(n.error.message,{details:{transport:"omnijs",scriptName:"forecast_get_tag"}});return {tagId:n.tagId===null?null:S.of(n.tagId)}}async setForecastTag(e){let r=await Q(vg,{tagId:e},{...this.runOpts,scriptName:"forecast_set_tag"});if(ne(r))throw r.error.code==="NOT_FOUND"?new P(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}}):new y(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}});return {tagId:r.tagId===null?null:S.of(r.tagId)}}async undoLastMutation(){let e=await Q(kg,{},{...this.runOpts,scriptName:"database_undo"});if(ne(e))throw new N(e.error.message,{details:{transport:"omnijs",scriptName:"database_undo",code:e.error.code}});return {undid:e.undid}}async redoLastMutation(){let e=await Q(yg,{},{...this.runOpts,scriptName:"database_redo"});if(ne(e))throw new N(e.error.message,{details:{transport:"omnijs",scriptName:"database_redo",code:e.error.code}});return {redid:e.redid}}async setTaskAlarms(e,n){let r=await Q(Cg,{taskId:String(e),alarms:n},{...this.runOpts,scriptName:"task_set_alarms"});if(ne(r))throw r.error.code==="NOT_FOUND"?new P(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms"}}):r.error.code==="VALIDATION"?new y(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms"}}):new N(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms",code:r.error.code}})}async clearTaskAlarms(e){let n=await Q(xg,{taskId:String(e)},{...this.runOpts,scriptName:"task_clear_alarms"});if(ne(n))throw n.error.code==="NOT_FOUND"?new P(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms"}}):new N(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms",code:n.error.code}})}async listAttachments(e){return x("listAttachments")}async addAttachment(e){return x("addAttachment")}async removeAttachment(e){return x("removeAttachment")}async saveAttachmentToPath(e){return x("saveAttachmentToPath")}async appLaunch(){return x("appLaunch")}async getWindowState(){return x("getWindowState")}async setWindowPerspective(e){return x("setWindowPerspective")}async setWindowFocus(e){return x("setWindowFocus")}async appWindowNew(){let e=await Q(gg,{},{...this.runOpts,scriptName:"app_window_new"});if(ne(e))throw new N(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new",code:e.error.code}});return e}async appWindowNewTab(){let e=await Q(hg,{},{...this.runOpts,scriptName:"app_window_new_tab"});if(ne(e))throw e.error.code==="WINDOW_UNAVAILABLE"?new N(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}}):new N(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}});return e}async pluginInvoke(e){return Q(Pg,{identifier:e.identifier,arg:e.arg??null},{...this.runOpts,scriptName:"plugin_invoke"})}async listPerspectives(){return x("listPerspectives")}async evaluatePerspective(e){return x("evaluatePerspective")}async evaluateCustomPerspective(e){let r=await Q(Sg,{identifier:e},{...this.runOpts,scriptName:"perspective_evaluate"});if(ne(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Ue(r.error.message,{details:{feature:"custom-perspectives"}}):new P(r.error.message,{details:{resource:"perspective",id:e}});return r.tasks.map(o=>({...o,id:h.of(o.id)}))}async evaluatePerspectiveRules(e,n){let r=await Q(bg,{rules:e,...n!==void 0&&{aggregation:n}},{...this.runOpts,scriptName:"perspective_evaluate_dry_run"});if(ne(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Ue(r.error.message,{details:{feature:"custom-perspectives"}}):new N(r.error.message,{details:{script:"perspective_evaluate_dry_run"}});return r.tasks.map(o=>({...o,id:h.of(o.id)}))}async getCustomPerspective(e){let n=await Q(jg,{identifier:e},{...this.runOpts,scriptName:"perspective_get"});if(ne(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):new P(n.error.message,{details:{resource:"perspective",id:e}});return n.perspective}async deleteCustomPerspective(e){let n=await Q(wg,{identifier:e},{...this.runOpts,scriptName:"perspective_delete"});if(ne(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="NOT_FOUND"?new P(n.error.message,{details:{resource:"perspective",id:e}}):new N(n.error.message,{details:{transport:"omnijs",script:"perspective_delete",id:e}})}async createCustomPerspective(e){let n=await Q(Tg,{name:e.name,...e.aggregation!==void 0&&{aggregation:e.aggregation},...e.rules!==void 0&&{rules:e.rules},...e.iconColor!==void 0&&{iconColor:e.iconColor}},{...this.runOpts,scriptName:"perspective_create"});if(ne(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="VALIDATION_ERROR"?new y(n.error.message,{details:{name:e.name}}):new N(n.error.message,{details:{transport:"omnijs",script:"perspective_create",name:e.name}});return n.id}async updateCustomPerspective(e,n){let r={identifier:e};n.name!==void 0&&(r.name=n.name),n.aggregation!==void 0&&(r.aggregation=n.aggregation),n.rules!==void 0&&(r.rules=n.rules),n.iconColor!==void 0&&(r.iconColor=n.iconColor);let o=await Q(_g,r,{...this.runOpts,scriptName:"perspective_update"});if(ne(o))throw o.error.code==="FEATURE_REQUIRES_PRO"?new Ue(o.error.message,{details:{feature:"custom-perspectives"}}):o.error.code==="NOT_FOUND"?new P(o.error.message,{details:{resource:"perspective",id:e}}):o.error.code==="VALIDATION_ERROR"?new y(o.error.message,{details:{id:e}}):new N(o.error.message,{details:{transport:"omnijs",script:"perspective_update",id:e}})}async syncTrigger(){return x("syncTrigger")}async getLastSync(){return x("getLastSync")}async getChangesSince(e){return {taskIds:[],projectIds:[]}}async runOmniJsScript(e,n){return Q(e,n??{},{...this.runOpts,scriptName:"raw"})}};var Ir=class extends EventEmitter{cache;inflight=new Map;hits=0;misses=0;coalesced=0;evictions=0;constructor({capacity:e=256,ttlMs:n=3e4}={}){super(),this.cache=new LRUCache({max:e,ttl:n,disposeAfter:()=>{this.evictions++;}});}async wrap(e,n){let r=this.cache.get(e);if(r!==void 0)return this.hits++,r.v;let o=this.inflight.get(e);if(o!==void 0)return this.coalesced++,await o.promise;this.misses++;let a=Symbol(e),i=(async()=>{try{let s=await n();return this.inflight.get(e)?.token===a&&this.cache.set(e,{v:s}),s}finally{this.inflight.get(e)?.token===a&&this.inflight.delete(e);}})();return this.inflight.set(e,{token:a,promise:i}),await i}invalidate(e){let n=e.endsWith(":*")?e.slice(0,-1):`${e}:`,r=[];for(let s of this.cache.keys())(s.startsWith(n)||s===e)&&r.push(s);for(let s of r)this.cache.delete(s);for(let s of this.inflight.keys())(s.startsWith(n)||s===e)&&this.inflight.delete(s);let o=r.length,a=ze(),i={event:"cache.invalidated",scopes:[e],evicted:o,...a!==void 0?{correlationId:a}:{}};o>0&&R.info(i,"cache.invalidated"),this.emit("cache.invalidated",i);}stats(){return {size:this.cache.size,hits:this.hits,misses:this.misses,evictions:this.evictions,coalesced:this.coalesced}}set(e,n){this.cache.set(e,{v:n});}has(e){return this.cache.has(e)}clear(){this.cache.clear(),this.inflight.clear();}};var aj=["/System/","/private/System/","/Library/","/private/Library/"];async function rc(t,e){let n;try{n=await realpath(t);}catch{throw new y(`Attachment file not found or cannot be resolved: ${t}`,{suggestion:"Verify the file path is correct and the file exists.",details:{field:"filePath",value:t}})}let r=n.endsWith(sep)?n:n+sep;for(let a of aj)if(r.startsWith(a))throw new y(`Attachment path resolves to a blocked system directory: ${n}`,{suggestion:"Attachment files must be under your home directory or an explicitly allowed path (OMNIFOCUS_ATTACHMENT_PATHS).",details:{field:"filePath",value:t,resolvedPath:n}});if(!e.some(a=>{let i=a.endsWith(sep)?a:a+sep;return r.startsWith(i)}))throw new y(`Attachment path is outside the allowed scope: ${n}`,{suggestion:"Move the file to your home directory, or add its parent directory to OMNIFOCUS_ATTACHMENT_PATHS (colon-separated list of absolute paths).",details:{field:"filePath",value:t,resolvedPath:n,allowedPaths:[...e]}})}var Eg=1024*1024;async function Mg(t,e){if(e<=0)return;let n;try{n=await stat(t);}catch{throw new y(`Attachment file not found or not accessible: ${t}`,{suggestion:"Verify the file path is correct and the file is readable.",details:{field:"filePath",value:t}})}let r=n.size/Eg;if(r>e)throw new y(`Attachment file exceeds the ${e} MB size cap (file is ${r.toFixed(2)} MB): ${t}`,{suggestion:`Reduce the file size to below ${e} MB, or increase the cap via the OMNIFOCUS_MAX_ATTACHMENT_MB environment variable.`,details:{field:"filePath",value:t,fileSizeBytes:n.size,capBytes:e*Eg}})}function Ng(t){return "taskId"in t&&t.taskId!==void 0?{taskId:t.taskId}:{projectId:t.projectId}}function Fg(t){return "taskId"in t?"task":"project"}var Tr=class{adapter;allowedPaths;maxMb;constructor(e){this.adapter=e.adapter,this.allowedPaths=e.allowedPaths,this.maxMb=e.maxAttachmentMb;}async list(e){return this.adapter.listAttachments(e)}async add(e){await rc(e.filePath,this.allowedPaths),await Mg(e.filePath,this.maxMb);let n=await this.adapter.addAttachment(e),r=Ng(e);return {id:n,ownerKind:Fg(r),ownerName:await this.lookupOwnerName(r)}}async remove(e){let n=Ng(e),r=await this.lookupOwnerName(n);return await this.adapter.removeAttachment(e),{ownerKind:Fg(n),ownerName:r}}async lookupOwnerName(e){try{return "taskId"in e?(await this.adapter.getTask(e.taskId)).name:(await this.adapter.getProject(e.projectId)).name}catch{return null}}async saveTo(e){let n=e.destPath.substring(0,e.destPath.lastIndexOf("/"))||"/";return await rc(n,this.allowedPaths),this.adapter.saveAttachmentToPath(e)}};function wr(t,e){t!==void 0&&ad(t,{folderId:e});}var Sr=class{adapter;cache;constructor({adapter:e,cache:n}){this.adapter=e,this.cache=n;}async list(e={}){return {folders:await this.adapter.listFolders(e.parentId!==void 0?{parentId:e.parentId}:{}),cacheHit:false}}async get(e){return {folder:await this.adapter.getFolder(e),cacheHit:false}}async create(e){let n=await this.adapter.createFolder(e);return wr(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateFolder(e,n),wr(this.cache,e);}async delete(e,n=false){n&&await this._cascadeEmpty(e),await this.adapter.deleteFolder(e),wr(this.cache,e);}async move(e,n){await this.adapter.updateFolder(e,{parentId:n}),wr(this.cache,e);}async _cascadeEmpty(e){let n=await this.adapter.listProjects({folderId:e});await Promise.all(n.map(o=>this.adapter.moveProject(o.id,{folderId:null})));let r=await this.adapter.listFolders({parentId:e});for(let o of r)await this._cascadeEmpty(o.id),await this.adapter.deleteFolder(o.id);}};var br=class{adapter;constructor(e){this.adapter=e.adapter;}async get(e){return {...await this.adapter.getForecast(e),cacheHit:false}}async getForecastTag(){let{tagId:e}=await this.adapter.getForecastTag();return {tagId:e,name:await this.lookupTagName(e)}}async setForecastTag(e){let{tagId:n}=await this.adapter.setForecastTag(e);return {tagId:n,name:await this.lookupTagName(n)}}async lookupTagName(e){if(e===null)return null;try{return (await this.adapter.getTag(e)).name}catch{return null}}};function oc(t){return st.includes(t)}var jr=class{adapter;constructor(e){this.adapter=e.adapter;}async list(){return {perspectives:await this.adapter.listPerspectives(),cacheHit:false}}async evaluate(e){return {tasks:oc(e)?await this.adapter.evaluatePerspective(e):await this.adapter.evaluateCustomPerspective(e),cacheHit:false}}async evaluateRules(e,n){return {tasks:await this.adapter.evaluatePerspectiveRules(e,n),cacheHit:false}}async get(e){if(oc(e))throw new y(`perspective_get only supports custom perspectives; got built-in id "${e}"`,{details:{field:"perspectiveId",value:e,kind:"builtin"}});return this.adapter.getCustomPerspective(e)}async delete(e){if(oc(e))throw new y(`perspective_delete cannot delete built-in perspectives; got "${e}"`,{details:{field:"perspectiveId",value:e,kind:"builtin"}});await this.adapter.deleteCustomPerspective(e);}};var Ug=200,ac=1e3,_r=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async list(e){let n=this.resolveLimit(e);this.assertBounded(e);let r=this.normalize(e),o=Nt(r),a=e.cursor!==void 0?Ut(e.cursor,o):void 0,i=this.listCacheKey(o,e.cursor),s=this.cache.has(i),{projects:c,nextCursor:d}=await this.cache.wrap(i,async()=>this.fetchPage(r,a,n,o));return {projects:c,nextCursor:d,hasMore:d!==null,cacheHit:s}}async completeProject(e){await this.adapter.completeProject(e);}async dropProject(e){await this.adapter.dropProject(e);}async moveProject(e,n){await this.adapter.moveProject(e,n);}async createProject(e){return this.adapter.createProject(e)}async updateProject(e,n){return this.adapter.updateProject(e,n)}async get(e){let n=e.includeTaskTree??true,r=this.getCacheKey(e.id,n),o=this.cache.has(r);return {...await this.cache.wrap(r,async()=>{let i=await this.adapter.getProject(e.id),s={...i,_links:ki(i)};if(!n)return {project:s};let d=(await this.adapter.listTasks({projectId:e.id})).map(l=>({...l,_links:et(l)}));return {project:s,tasks:d}}),cacheHit:o}}async fetchPage(e,n,r,o){let a={};e.folderId!==void 0&&(a.folderId=e.folderId),e.status!==void 0&&(a.status=e.status);let i=await this.adapter.listProjects(a),{flagged:s,reviewDueBefore:c}=e,l=[...i.filter(T=>!(s!==void 0&&T.flagged!==s||c!==void 0&&(T.nextReviewDate===null||T.nextReviewDate>=c)))].sort((T,E)=>T.createdAt!==E.createdAt?T.createdAt<E.createdAt?-1:1:T.id<E.id?-1:1),m=n!==void 0?l.filter(T=>Lt({id:T.id,sortValue:T.createdAt},n,"asc")):l,f=m.slice(0,r),v=m.length>r?this.encodeNextCursor(f,o):null;return {projects:f.map(T=>({...T,_links:ki(T)})),nextCursor:v}}resolveLimit(e){if(e.limit===void 0)return Ug;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>ac)throw new y(`limit must be an integer between 1 and ${ac}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${ac}, or omit to use the default of ${Ug}.`,details:{field:"limit",value:e.limit}});return e.limit}assertBounded(e){if(!(e.limit!==void 0||e.cursor!==void 0)&&!this.hasAnyFilter(e))throw new y("project_list requires at least one filter, limit, or cursor. Unbounded queries are rejected.",{suggestion:"Provide a filter or a limit.",details:{field:"filter|limit|cursor"}})}hasAnyFilter(e){return e.folderId!==void 0||e.status!==void 0||e.flagged!==void 0||e.reviewDueBefore!==void 0}normalize(e){return {folderId:e.folderId,status:e.status,flagged:e.flagged,reviewDueBefore:e.reviewDueBefore}}listCacheKey(e,n){return `search:projects:${e}:${n??"first"}`}getCacheKey(e,n){return `project:${e}:${n?"with-tasks":"solo"}`}encodeNextCursor(e,n){let r=e[e.length-1];if(r===void 0)throw new y("Internal: cannot encode cursor for empty page.");return Ft({lastId:r.id,lastSortValue:r.createdAt,filterHash:n})}};var Pr=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async listDue(){return {projects:await this.adapter.listProjectsDueForReview(),cacheHit:false}}async markReviewed(e){await this.adapter.markProjectReviewed(e),this.cache!==void 0&&J(this.cache,{projectId:e});let n=await this.lookupProject(e);return {name:n?.name??null,lastReviewDate:n?.lastReviewDate??null,nextReviewDate:n?.nextReviewDate??null}}async setInterval(e,n){await this.adapter.setProjectReviewInterval(e,n),this.cache!==void 0&&J(this.cache,{projectId:e});let r=await this.lookupProject(e);return {name:r?.name??null,reviewIntervalDays:r?.reviewIntervalDays??n}}async setNextReviewDate(e,n){await this.adapter.setProjectNextReviewDate(e,n),this.cache!==void 0&&J(this.cache,{projectId:e});let r=await this.lookupProject(e);return {name:r?.name??null,nextReviewDate:r?.nextReviewDate??n}}async lookupProject(e){try{return await this.adapter.getProject(e)}catch{return null}}};var ij=100,cj=500,Or=class{adapter;constructor({adapter:e}){this.adapter=e;}async search(e){let n=Math.min(e.limit??ij,cj),r={...e.q!==void 0?{q:e.q}:{},scope:e.scope??"all",...e.projectId!==void 0?{projectId:e.projectId}:{},...e.tagIds!==void 0?{tagIds:[...e.tagIds].sort()}:{},...e.available!==void 0?{available:e.available}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.dueAfter!==void 0?{dueAfter:e.dueAfter}:{},...e.flagged!==void 0?{flagged:e.flagged}:{},...e.completed!==void 0?{completed:e.completed}:{}},o=Nt(r),a=null;e.cursor&&(a=Ut(e.cursor,o));let i={...e.q!==void 0?{q:e.q}:{},...e.scope!==void 0?{scope:e.scope}:{},...e.projectId!==void 0?{projectId:e.projectId}:{},...e.tagIds!==void 0?{tagIds:e.tagIds}:{},...e.available!==void 0?{available:e.available}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.dueAfter!==void 0?{dueAfter:e.dueAfter}:{},...e.flagged!==void 0?{flagged:e.flagged}:{},...e.completed!==void 0?{completed:e.completed}:{}},c=[...await this.adapter.searchTasks(i)].sort((I,T)=>{let E=I.createdAt.localeCompare(T.createdAt);return E!==0?E:I.id.localeCompare(T.id)}),l=(a?c.filter(I=>Lt({id:I.id,sortValue:I.createdAt},a,"asc")):c).slice(0,n+1),m=l.length>n,f=l.slice(0,n).map(I=>({...I,_links:et(I)})),g=f.at(-1),v=m&&g?Ft({lastId:g.id,lastSortValue:g.createdAt,filterHash:o}):null;return {tasks:f,nextCursor:v,hasMore:m,cacheHit:false}}};function tt(t,e){t!==void 0&&od(t,{tagId:e});}var xr=class{adapter;cache;constructor({adapter:e,cache:n}){this.adapter=e,this.cache=n;}async list(e={}){return {tags:await this.adapter.listTags({...e.parentId!==void 0?{parentId:e.parentId}:{},...e.status!==void 0?{status:e.status}:{}}),cacheHit:false}}async get(e){return {tag:await this.adapter.getTag(e),cacheHit:false}}async create(e){let n=await this.adapter.createTag(e);return tt(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateTag(e,n),tt(this.cache,e);}async delete(e){await this.adapter.deleteTag(e),tt(this.cache,e);}async move(e,n){await this.adapter.updateTag(e,{parentId:n}),tt(this.cache,e);}async setStatus(e,n){await this.adapter.updateTag(e,{status:n}),tt(this.cache,e);}async setAllowsNextAction(e,n){await this.adapter.updateTag(e,{allowsNextAction:n}),tt(this.cache,e);}async setLocation(e,n){await this.adapter.updateTag(e,{location:n}),tt(this.cache,e);}async clearLocation(e){await this.adapter.updateTag(e,{location:null}),tt(this.cache,e);}async getLocation(e){return {location:(await this.adapter.getTag(e)).location,cacheHit:false}}};function Lg(t){if(t.OMNIFOCUS_E2E_USE_MEMORY){let r=new fr;return new Wt({jxa:r,omnijs:r})}let e=new hr({timeoutMs:t.OMNIFOCUS_JXA_TIMEOUT_MS}),n=new kr({timeoutMs:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS});return Wt.fromTransports(e,n)}function Bg(t,e){let n=new Ir({capacity:e.OMNIFOCUS_CACHE_CAPACITY,ttlMs:e.OMNIFOCUS_CACHE_TTL_MS});return {cache:n,taskService:new sr({adapter:t,cache:n}),projectService:new _r({adapter:t,cache:n}),tagService:new xr({adapter:t,cache:n}),folderService:new Sr({adapter:t,cache:n}),attachmentService:new Tr({adapter:t,allowedPaths:e.OMNIFOCUS_ATTACHMENT_PATHS,maxAttachmentMb:e.OMNIFOCUS_MAX_ATTACHMENT_MB}),exportService:new At({adapter:t}),forecastService:new br({adapter:t}),perspectiveService:new jr({adapter:t}),pluginService:new Ot({adapter:t}),reviewService:new Pr({adapter:t,cache:n}),searchService:new Or({adapter:t})}}var dj="jxa",lj="unknown";function C(t={}){return {correlationId:ze()??fg(),durationMs:0,cacheHit:false,transport:dj,ofVersion:lj,...t}}function Jg(t){let{adapter:e,cache:n,server:r,aggregateUris:o,orchestrator:a}=t;return async i=>{let s=new Date(i.detectedAt).getTime()-200,c=new Date(s).toISOString(),d={taskIds:[],projectIds:[]},l=false;try{d=await e.getChangesSince(c),l=!0;}catch(m){R.debug({event:"database.changed.query_failed",err:m});}if(l&&(d.taskIds.length>0||d.projectIds.length>0)){for(let m of d.taskIds)n.invalidate(`task:${m}`);for(let m of d.projectIds)n.invalidate(`project:${m}`);}else n.clear();if(a?.shouldObserve())try{let[m,f]=await Promise.all([e.listTasks({}),e.listProjects()]);await a.observeSnapshot(m,f);}catch(m){R.debug({event:"database.changed.webhook_observe_failed",err:m});}for(let m of d.taskIds)r.server.sendResourceUpdated({uri:`omnifocus://task/${m}`}).catch(()=>{});for(let m of d.projectIds)r.server.sendResourceUpdated({uri:`omnifocus://project/${m}`}).catch(()=>{});for(let m of o)r.server.sendResourceUpdated({uri:m}).catch(()=>{});R.debug({event:"database.changed",source:i.source,detectedAt:i.detectedAt,changedTasks:d.taskIds.length,changedProjects:d.projectIds.length,cacheStrategy:l?"targeted":"full-clear"});}}async function $g(t,e){let n=performance.now();try{let r=await e(),o=Math.round(performance.now()-n);return R.info({event:"tool.invoked",tool:t,correlationId:ze(),durationMs:o,transport:r.meta.transport,cacheHit:r.meta.cacheHit},"tool invoked"),r}catch(r){let o=Math.round(performance.now()-n),a=dc(r)?r.code:"UNKNOWN";throw R.warn({event:"tool.error",tool:t,correlationId:ze(),durationMs:o,code:a,err:r},"tool error"),r}}function Wg(t,e,n,r){let o=n.record(t,e);if(o!==void 0){let{level:a,warning:i}=o,s=i.details?.count??0,c=(i.details?.windowSeconds??60)*1e3;if(R.warn({event:"loop.detected",tool:t,callCount:s,windowMs:c,level:a}),a==="error")return Promise.reject(new bn(t,s,i.details?.windowSeconds??60))}return r().then(a=>{if(o===void 0)return a;let i=a.meta.warnings??[];return {...a,meta:{...a.meta,warnings:[...i,o.warning]}}})}function Hg(t,e,n){return e.check(t),n().then(r=>{let o=e.remaining(t);return {...r,meta:{...r.meta,rateLimit:o}}})}function pj(t,e,n){return (r,o)=>mg(async()=>(n.shutdown.assertNotShuttingDown(),n.circuitRegistry.get(t).call(async()=>{let i=async()=>(await e(r,o)).structuredContent,s=await Hg(t,n.rateLimiter,()=>Wg(t,r,n.loopDetector,()=>$g(t,i)));return u(s)})))}function zg(t,e){let n=t;if(n.__omnifocusMiddlewareInstalled===true)return;let r=t.registerTool.bind(t),o=(...a)=>{let[i,s,c]=a,d=pj(i,c,e);return r(i,s,d)};t.registerTool=o,n.__omnifocusMiddlewareInstalled=true;}var uj=5e3,mj=1e4,fj=50,sc=class{_shuttingDown=false;_queues=[];_readGraceMs;_writeGraceMs;constructor(e={}){this._readGraceMs=e.readGraceMs??Number(process.env.OMNIFOCUS_READ_GRACE_MS??uj),this._writeGraceMs=e.writeGraceMs??Number(process.env.OMNIFOCUS_WRITE_GRACE_MS??mj);}get isShuttingDown(){return this._shuttingDown}assertNotShuttingDown(){if(this._shuttingDown)throw new Sn}registerQueue(e){this._queues.includes(e)||this._queues.push(e);}async initiate(e,n=process.exit){if(this._shuttingDown)return;this._shuttingDown=true,R.info({event:"server.shutdown",reason:e,readGraceMs:this._readGraceMs,writeGraceMs:this._writeGraceMs},"graceful shutdown initiated");let r=this._readGraceMs+this._writeGraceMs;await this._drainAll(r),R.flush(),n(0);}async _drainAll(e){if(this._queues.length===0)return;let n=Date.now()+e;for(;Date.now()<n;){if(this._queues.filter(a=>a.pendingCount()>0).length===0)return;await new Promise(a=>setTimeout(a,fj));}let r=this._queues.filter(o=>o.pendingCount()>0);for(let o of r)R.warn({event:"server.shutdown.drain_timeout",queue:o.name,pending:o.pendingCount()},"queue did not drain within grace window; forcing shutdown");}},pt=new sc;var gj=["@modelcontextprotocol/sdk","node_modules/@modelcontextprotocol"];function hj(t){return gj.some(e=>t.includes(e))}var Gg=null,qg=false;function Vg(){if(qg)return;Gg=process.stdout.write,qg=true;let t=Gg;process.stdout.write=function(n,r,o){let a=new Error().stack??"";if(hj(a))return typeof r=="function"?t.call(process.stdout,n,r):t.call(process.stdout,n,r,o);let i=typeof n=="string"?n.slice(0,120):`<${n.byteLength} bytes>`;throw new Y("OF_STRAY_STDOUT",`Stray stdout write detected \u2014 stdout is reserved for MCP transport. Use process.stderr / logger for diagnostics. Attempted: ${JSON.stringify(i)}`,{suggestion:"Replace console.log / process.stdout.write with process.stderr.write or logger."})};}var Kg=Fe.version,Ij=Fe.name.split("/").pop()??"omnifocus-mcp",vj=new Set(["run_jxa_script","run_omnijs_script"]),Tj=Date.now();function wj(){return new McpServer({name:Ij,version:Kg})}async function Xg(){Vg();let t=lc();R.level=t.OMNIFOCUS_LOG_LEVEL;let e=wj(),n=new _n(t.OMNIFOCUS_TOOL_RATE_LIMIT),r=new jn;zg(e,{rateLimiter:n,loopDetector:r,circuitRegistry:tc,shutdown:pt});let o=new StdioServerTransport,a=Lg(t),i=new In({size:t.OMNIFOCUS_READ_POOL_SIZE,name:"jxa-read"}),s=new zt({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"jxa-write"}),c=new zt({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"omnijs"});pt.registerQueue(i),pt.registerQueue(s),pt.registerQueue(c);let d=cc(a,{readPool:i,jxaWriteQueue:s,omniJsQueue:c}),l=Bg(d,t);jl(e,{startedAt:Tj,adapter:d,circuitRegistry:tc,makeMeta:C}),mc(e);let m=new mr,f={registry:m,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:C};Fm(e,f),Lm(e,f),Um(e,f);let g=new ur({registry:m,dispatcher:new pr});Bm(e,{orchestrator:g,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:C}),hc(e,async()=>{let Re=m.list();return gc(t,{calendarAccess:await On(),webhooks:{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:Re.length,names:Re.map(Rr=>Rr.name)}})}),td(e,{adapter:d,projectService:l.projectService,reviewService:l.reviewService,forecastService:l.forecastService,perspectiveService:l.perspectiveService});let v={folderService:l.folderService,makeMeta:C};nl(e,v),al(e,v),il(e,v),cl(e,v),dl(e,v),ul(e,v);let I={adapter:d,makeMeta:C};rl(e,I),sl(e,I),ll(e,I),ml(e,I);let T={tagService:l.tagService,makeMeta:C};zp(e,T),qp(e,T),Zp(e,{adapter:d,makeMeta:C}),Kp(e,T),Xp(e,T),Qp(e,T),eu(e,T),ru(e,T),ou(e,T),au(e,T),su(e,T);let E={adapter:d,makeMeta:C};Gp(e,E),Vp(e,E),tu(e,E),iu(e,E);let w={adapter:d,makeMeta:C,cache:l.cache};kl(e,w),Il(e,w),vl(e,w),Tl(e,w),wl(e,w),$p(e,{searchService:l.searchService,makeMeta:C}),fl(e,{forecastService:l.forecastService,makeMeta:C}),hl(e,{forecastService:l.forecastService,makeMeta:C}),gl(e,{forecastService:l.forecastService,makeMeta:C}),yl(e,{forecastService:l.forecastService,cache:l.cache,makeMeta:C});let D={perspectiveService:l.perspectiveService,makeMeta:C};Ml(e,D),Rl(e,D),Al(e,D),El(e,D),xl(e,{perspectiveService:l.perspectiveService,cache:l.cache,makeMeta:C}),Pl(e,{adapter:d,cache:l.cache,makeMeta:C}),Fl(e,{adapter:d,cache:l.cache,makeMeta:C}),Ul(e,{adapter:d,makeMeta:C}),Wp(e,{adapter:d,makeMeta:C}),Hp(e,{adapter:d,makeMeta:C,cache:l.cache}),ld(e,{adapter:d,makeMeta:C,cache:l.cache}),cd(e,{adapter:d,makeMeta:C,cache:l.cache});let j={reviewService:l.reviewService,makeMeta:C};Mp(e,j),Fp(e,j),Lp(e,j),Bp(e,j),Jp(e,j);let B={exportService:l.exportService,adapter:d,makeMeta:C};md(e,B),Qd(e,B),el(e,B),nd(e,{adapter:d,makeMeta:C});let _={adapter:d,makeMeta:C};Jm(e,_),$m(e,_),Wm(e,_),Hm(e,_),zm(e,_);let U={projectService:l.projectService,makeMeta:C,cache:l.cache},M={adapter:d,makeMeta:C,cache:l.cache};Ll(e,M),Bl(e,M),Jl(e,U),Zl(e,{...M,replayStore:ve}),ep(e,M),np(e,U),ip(e,{adapter:d,makeMeta:C}),ap(e,{projectService:l.projectService,makeMeta:C}),cp(e,{projectService:l.projectService,makeMeta:C}),dp(e,U),Pp(e,M);let H={adapter:d,makeMeta:C,cache:l.cache,templatesFolderName:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME};_p(e,H),jp(e,H),Km(e,H),bp(e,H);let z={adapter:d,makeMeta:C};$l(e,z),Ql(e,z),tp(e,z),rp(e,z),lp(e,z),Op(e,z);let le={taskService:l.taskService,makeMeta:C},Ge={adapter:d,makeMeta:C},A={adapter:d,makeMeta:C,cache:l.cache};om(e,le),lm(e,le),Qu(e,Ge),nm(e,Ge),wm(e,{searchService:l.searchService,makeMeta:C}),sm(e,Ge),ym(e,{makeMeta:C}),qm(e,{makeMeta:C}),Ep(e,{makeMeta:C,replayStore:ve}),ud(e,A),pd(e,A),Lu(e,A),hu(e,A),Im(e,A),cu(e,A),du(e,A),lu(e,A),yu(e,A),ku(e,A),Iu(e,A),vu(e,A),Tu(e,A),wu(e,A),bu(e,A),ju(e,A),Ou(e,{...A,replayStore:ve}),Wu(e,A),zu(e,A),Ku(e,{...A,attachmentService:l.attachmentService}),Zu(e,A),Ru(e,A);let $={adapter:d,makeMeta:C,cache:l.cache,waitingTagName:t.OMNIFOCUS_WAITING_TAG_NAME};Rm(e,$),Am(e,$),um(e,A),vm(e,A),jm(e,A),_m(e,A),Pm(e,A),Om(e,A),Nu(e,A),Bu(e,A),xm(e,A);let fe={adapter:d,makeMeta:C};pu(e,fe),Su(e,fe),xu(e,fe),Fu(e,fe),Ju(e,fe),Hu(e,fe),mm(e,fe),Dm(e,fe),rd(e,{attachmentService:l.attachmentService,makeMeta:C});let Ne={adapter:d,makeMeta:C},nt={allowRawScript:t.OMNIFOCUS_ALLOW_RAW_SCRIPT};xp(e,Ne,nt),Dp(e,Ne,nt),process.on("SIGINT",()=>{pt.initiate("SIGINT");}),process.on("SIGTERM",()=>{pt.initiate("SIGTERM");}),process.on("unhandledRejection",Re=>{R.fatal({event:"server.unhandled_rejection",reason:Re},"unhandled rejection"),R.flush(),process.exit(1);}),process.on("uncaughtException",Re=>{R.fatal({event:"server.uncaught_exception",err:Re},"uncaught exception"),R.flush(),process.exit(1);}),await e.connect(o);let Yg=Jg({adapter:d,cache:l.cache,server:e,aggregateUris:[Qt,en,tn,nn,rn,on],orchestrator:g}),ic=new lr(Re=>{Yg(Re).catch(Rr=>{R.error({event:"database.changed.handler_error",err:Rr});});});ic.start(),process.on("exit",()=>ic.stop());let Zg=Object.keys(Gm).filter(Re=>t.OMNIFOCUS_ALLOW_RAW_SCRIPT||!vj.has(Re)).sort();R.info({event:"server.started",version:Kg,config:pc(t),tools:Zg,prompts:[Ar,Cr,Er,Mr],resources:[Pn,Qt,en,tn,nn,rn,on,no,ro,oo]},"server started");}var Dr=process.argv.slice(2);(Dr.includes("--version")||Dr.includes("-v"))&&(process.stdout.write(`${Fe.version}
|
|
6850
|
+
`),process.exit(0));(Dr.includes("--help")||Dr.includes("-h"))&&(process.stdout.write(`${Fe.name} v${Fe.version}
|
|
6851
|
+
${Fe.description}
|
|
4787
6852
|
|
|
4788
6853
|
Usage: omnifocus-mcp
|
|
4789
6854
|
|
|
4790
6855
|
Speaks Model Context Protocol over stdio. Add to your MCP client's
|
|
4791
6856
|
configuration (Claude Desktop, Claude Code, etc.) \u2014 see the README
|
|
4792
|
-
for client-specific setup: ${
|
|
6857
|
+
for client-specific setup: ${Fe.homepage}
|
|
4793
6858
|
|
|
4794
6859
|
Options:
|
|
4795
6860
|
-v, --version Print version and exit
|
|
@@ -4800,5 +6865,5 @@ Environment:
|
|
|
4800
6865
|
OMNIFOCUS_ALLOW_RAW_SCRIPT Enable raw-script tools (off by default)
|
|
4801
6866
|
OMNIFOCUS_JXA_TIMEOUT_MS Per-call JXA timeout (default: 30000)
|
|
4802
6867
|
OMNIFOCUS_OMNIJS_TIMEOUT_MS Per-call OmniJS timeout (default: 45000)
|
|
4803
|
-
`),process.exit(0));
|
|
6868
|
+
`),process.exit(0));Xg().catch(t=>{process.stderr.write(`[omnifocus-mcp] Fatal startup error: ${String(t)}
|
|
4804
6869
|
`),process.exit(1);});
|