@torsday/omnifocus-mcp 1.0.2 → 1.1.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/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 t=lc.safeParse({OMNIFOCUS_LOG_LEVEL:n.OMNIFOCUS_LOG_LEVEL,OMNIFOCUS_INTEGRATION:n.OMNIFOCUS_INTEGRATION,OMNIFOCUS_E2E:n.OMNIFOCUS_E2E,OMNIFOCUS_E2E_USE_MEMORY:n.OMNIFOCUS_E2E_USE_MEMORY,OMNIFOCUS_ALLOW_RAW_SCRIPT:n.OMNIFOCUS_ALLOW_RAW_SCRIPT,OMNIFOCUS_CACHE_TTL_MS:n.OMNIFOCUS_CACHE_TTL_MS,OMNIFOCUS_CACHE_CAPACITY:n.OMNIFOCUS_CACHE_CAPACITY,OMNIFOCUS_READ_POOL_SIZE:n.OMNIFOCUS_READ_POOL_SIZE,OMNIFOCUS_WRITE_QUEUE_CAP:n.OMNIFOCUS_WRITE_QUEUE_CAP,OMNIFOCUS_JXA_TIMEOUT_MS:n.OMNIFOCUS_JXA_TIMEOUT_MS,OMNIFOCUS_OMNIJS_TIMEOUT_MS:n.OMNIFOCUS_OMNIJS_TIMEOUT_MS,OMNIFOCUS_ATTACHMENT_PATHS:n.OMNIFOCUS_ATTACHMENT_PATHS,OMNIFOCUS_MAX_ATTACHMENT_MB:n.OMNIFOCUS_MAX_ATTACHMENT_MB,OMNIFOCUS_TOOL_RATE_LIMIT:n.OMNIFOCUS_TOOL_RATE_LIMIT});if(!t.success){let r=t.error.issues.map(o=>` ${o.path.join(".")}: ${o.message}`);return e(`Invalid environment configuration:
2
+ import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {createHash}from'crypto';import tv,{homedir}from'os';import {z}from'zod';import ri from'pino';import Ws,{sep,extname}from'path';import {spawn,execFile}from'child_process';import Vt from'fs';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 _e={name:"@torsday/omnifocus-mcp",version:"1.1.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 Tt=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",getCustomPerspective:"omnijs",deleteCustomPerspective:"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 vt=class t{jxa;omnijs;constructor(e){this.jxa=e.jxa,this.omnijs=e.omnijs;}get routingTable(){return Tt}static fromTransports(e,n){return new t({jxa:e,omnijs:n})}pick(e){return Tt[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)}getCustomPerspective(e){return this.pick("getCustomPerspective").getCustomPerspective(e)}deleteCustomPerspective(e){return this.pick("deleteCustomPerspective").deleteCustomPerspective(e)}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 Ym=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 Qm(t,e){return Tt[t]==="omnijs"?e.omniJsQueue:Ym.has(t)?e.jxaWriteQueue:e.readPool}function Zs(t,e){return new Proxy(t,{get(n,r,o){if(typeof r!="string"||!(r in Tt))return Reflect.get(n,r,o);let a=r,i=Qm(a,e),c=Reflect.get(n,a,o).bind(n);return (...l)=>i.run(()=>c(...l))}})}var qt=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 q=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 ei(t){return t instanceof q}var Ke=class extends q{constructor(e={}){super("OF_NOT_RUNNING","OmniFocus is not running.",{remediationClass:"environment",suggestion:"Launch OmniFocus and retry.",...e});}},Xe=class extends q{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});}},Ye=class extends q{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 I=class extends q{constructor(e,n={}){super("OF_VALIDATION",e,{remediationClass:"input",suggestion:"Fix the input and retry. See `details` for field-level reasons.",...n});}},O=class extends q{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});}},Qe=class extends q{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});}},Ze=class extends q{constructor(e,n={}){super("OF_TIMEOUT",e,{remediationClass:"transient",suggestion:"Retry once. If repeated, OmniFocus may be wedged \u2014 relaunch it.",...n});}},Kt=class extends q{constructor(e,n={}){super("OF_RATE_LIMITED",e,{remediationClass:"transient",suggestion:"Wait details.retryAfterMs milliseconds then retry.",...n,details:{retryAfterMs:6e4,...n.details}});}},Xt=class extends q{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});}},wt=class extends q{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}});}},et=class extends q{constructor(e,n={}){super("OF_TRANSPORT_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"The required transport is unreachable. Verify OmniFocus is running and responsive.",...n});}},tt=class extends q{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 q{constructor(e,n={}){super("OF_SCRIPT_ERROR",e,{remediationClass:"infrastructure",suggestion:"The OmniFocus script failed. Inspect `details.transport` and `details.reason` for context.",...n});}},Yt=class extends q{constructor(e={}){super("OF_SHUTTING_DOWN","Server is shutting down; not accepting new requests.",{remediationClass:"lifecycle",suggestion:"Reconnect to a fresh server instance.",...e});}},Qt=class extends q{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 St=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 Xt(`${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 tf=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}}),nf=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_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:tf.prefault("120/60"),OMNIFOCUS_WAITING_TAG_NAME:z.string().min(1).default("waiting"),OMNIFOCUS_TEMPLATES_FOLDER_NAME:z.string().min(1).default("Templates")});function ti(t=process.env,e=n=>{process.stderr.write(`[omnifocus-mcp] Config error: ${n}
3
+ `),process.exit(1);}){let n=nf.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_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 t.data}function pc(n){return createHash("sha256").update(n).digest("hex").slice(0,12)}function fo(n){return {OMNIFOCUS_LOG_LEVEL:n.OMNIFOCUS_LOG_LEVEL,OMNIFOCUS_INTEGRATION:n.OMNIFOCUS_INTEGRATION,OMNIFOCUS_E2E:n.OMNIFOCUS_E2E,OMNIFOCUS_E2E_USE_MEMORY:n.OMNIFOCUS_E2E_USE_MEMORY,OMNIFOCUS_ALLOW_RAW_SCRIPT:n.OMNIFOCUS_ALLOW_RAW_SCRIPT,OMNIFOCUS_CACHE_TTL_MS:n.OMNIFOCUS_CACHE_TTL_MS,OMNIFOCUS_CACHE_CAPACITY:n.OMNIFOCUS_CACHE_CAPACITY,OMNIFOCUS_READ_POOL_SIZE:n.OMNIFOCUS_READ_POOL_SIZE,OMNIFOCUS_WRITE_QUEUE_CAP:n.OMNIFOCUS_WRITE_QUEUE_CAP,OMNIFOCUS_JXA_TIMEOUT_MS:n.OMNIFOCUS_JXA_TIMEOUT_MS,OMNIFOCUS_OMNIJS_TIMEOUT_MS:n.OMNIFOCUS_OMNIJS_TIMEOUT_MS,OMNIFOCUS_ATTACHMENT_PATHS:n.OMNIFOCUS_ATTACHMENT_PATHS.map(pc),OMNIFOCUS_MAX_ATTACHMENT_MB:n.OMNIFOCUS_MAX_ATTACHMENT_MB,OMNIFOCUS_TOOL_RATE_LIMIT:n.OMNIFOCUS_TOOL_RATE_LIMIT}}var uc=["name","note","noteHtml","tagNames","tagNames[*]","data.name","data.note","data.noteHtml","data.tagNames","data.tagNames[*]","*.name","*.note","*.noteHtml","*.tagNames"];function mc(n="info"){return go({level:n,redact:{paths:uc,censor:"[redacted]"},formatters:{level(e){return {level:e}}},timestamp:go.stdTimeFunctions.epochTime},process.stderr)}var j=mc(process.env.OMNIFOCUS_LOG_LEVEL??"info");var gc={threshold:5,errorThreshold:10,windowSeconds:60};function hc(n,e){let t=JSON.stringify(e,Object.keys(e).sort()),r=createHash("sha1").update(t).digest("hex").slice(0,16);return `${n}:${r}`}var ut=class{config;windows=new Map;constructor(e={}){this.config={...gc,...e};}record(e,t){let r=hc(e,t),o=Date.now(),a=o-this.config.windowSeconds*1e3,s=this.getAndPrune(r,a);s.push(o);let i=s.length;if(i<this.config.threshold)return;let l={code:"WARN_LOOP_DETECTED",message:`Tool "${e}" has been called ${i} 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:i,windowSeconds:this.config.windowSeconds}};return i>=this.config.errorThreshold?{level:"error",warning:l}:{level:"warn",warning:l}}reset(e){e===void 0?this.windows.clear():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}};var Qt="daily-review",Zt="weekly-review",en="capture-meeting",tn="project-planning";function Ic(){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 kc(){return `You are running a weekly OmniFocus review. Follow these steps in order:
6
+ See DESIGN \xA722 for allowed values.`)}return n.data}function rf(t){return createHash("sha256").update(t).digest("hex").slice(0,12)}function ni(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_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(rf),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 of=["name","note","noteHtml","tagNames","tagNames[*]","data.name","data.note","data.noteHtml","data.tagNames","data.tagNames[*]","*.name","*.note","*.noteHtml","*.tagNames"];function af(t="info"){return ri({level:t,redact:{paths:of,censor:"[redacted]"},formatters:{level(e){return {level:e}}},timestamp:ri.stdTimeFunctions.epochTime},process.stderr)}var C=af(process.env.OMNIFOCUS_LOG_LEVEL??"info");var cf={threshold:5,errorThreshold:10,windowSeconds:60};function df(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 Zt=class{config;windows=new Map;constructor(e={}){this.config={...cf,...e};}record(e,n){let r=df(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 er="daily-review",tr="weekly-review",nr="capture-meeting",rr="project-planning",lf="inbox-triage";function pf(){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 uf(){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 Tc(n,e){return `You are capturing action items from meeting notes. Follow these steps:
25
+ how many tasks rescheduled or cleaned up.`}function mf(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
- ${n}`}function yc(n,e,t){let r=t?` - \`folderId\`: \`${t}\``:" - No folder (top-level project)";return `You are setting up a new OmniFocus project. Follow these steps:
45
+ ${t}`}function ff(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\`: \`${n}\`
48
+ - \`name\`: \`${t}\`
49
49
  ${r}
50
50
  - \`note\`: the brief below, verbatim, as the project note.
51
51
 
@@ -65,9 +65,81 @@ ${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 gf(){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 oi(t){t.registerPrompt(er,{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:pf()}}]})),t.registerPrompt(tr,{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:uf()}}]})),t.registerPrompt(nr,{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:mf(e,n)}}]})),t.registerPrompt(rr,{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:ff(e,n,r)}}]})),t.registerPrompt(lf,{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:gf()}}]}));}var en=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 Kt(`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 ai(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:864e5}}var tn="omnifocus://capabilities";function si(t,e){t.registerResource("omnifocus-capabilities",tn,{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 n=>({contents:[{uri:tn,mimeType:"application/json",text:JSON.stringify(e(),null,2)}]}));}var ii=/^[A-Za-z0-9._-]{3,64}$/;function yf(t){return typeof t=="string"&&ii.test(t)}function bt(t){let e=z.string().regex(ii,`Invalid ${t}: expected 3-64 alphanumeric / _ / - characters`).transform(n=>n);return {kind:t,of(n){return e.parse(n)},is(n){return yf(n)},schema:e}}var h=bt("TaskId"),y=bt("ProjectId"),w=bt("TagId"),J=bt("FolderId"),ve=bt("AttachmentId");var ci="omnifocus://burndown/{projectId}";function or(t){return Math.round(t*100)/100}async function kf(t,e,n=new Date){let r;try{r=y.of(e);}catch{return {error:{code:"ProjectNotFound",message:`Project not found: ${e}`}}}let a=(await t.listProjects()).find(A=>String(A.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),l=i.filter(A=>!A.dropped&&A.projectId!==null&&String(A.projectId)===c),d=s.filter(A=>A.projectId!==null&&String(A.projectId)===c),m=l.length+d.length,f=d.length,g=l.length,k=[...l,...d],v=k[0],S=k.length>0&&v!==void 0?k.reduce((A,oe)=>oe.createdAt<A?oe.createdAt:A,v.createdAt):a.createdAt,$=new Date(S).getTime(),T=new Date(a.dueDate).getTime(),D=n.getTime(),P=Math.max(1,(T-$)/864e5),U=Math.max(0,Math.min(P,(D-$)/864e5)),b=or(U/P*100),F=m===0?100:or(f/m*100),z=or((F-b)/100*P),X;return m===0?X="No tasks in project \u2014 burndown is trivially complete.":z>0?X=`Ahead of pace by ${z.toFixed(1)} day(s). Ideal: ${b.toFixed(1)}% done; actual: ${F.toFixed(1)}%.`:z<0?X=`Behind pace by ${Math.abs(z).toFixed(1)} day(s). Ideal: ${b.toFixed(1)}% done; actual: ${F.toFixed(1)}%.`:X=`On pace. Ideal: ${b.toFixed(1)}%; actual: ${F.toFixed(1)}%.`,X+=" (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:S,totalTasks:m,completedTasks:f,remainingTasks:g,idealLinePercent:b,actualPercent:F,deltaDays:z,note:X}}function di(t,e){t.registerResource("omnifocus-burndown",new ResourceTemplate(ci,{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 kf(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var ar=[{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 sr="omnifocus://intents";function Tf(t=new Date){return {intents:ar,count:ar.length,generatedAt:t.toISOString()}}function li(t){t.registerResource("omnifocus-intents",sr,{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:sr,mimeType:"application/json",text:JSON.stringify(Tf(),null,2)}]}));}function _t(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 ui="omnifocus://project-health{?staleDays}";function wf(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 Sf(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 pi(t,e){let n=e.getTime()-new Date(t).getTime();return Math.max(0,Math.floor(n/(1440*60*1e3)))}function jf(t,e,n){let r=e.filter(S=>!S.completed&&!S.dropped),o=e.map(S=>S.modifiedAt),a=Sf(o),i=a??t.modifiedAt,s=pi(i,n),c=r.filter(S=>S.available).length,l=r.filter(S=>S.blocked).length,d=r.length===0,m=n.toISOString(),f=r.length>0&&r.every(S=>S.deferDate!==null&&S.deferDate>m),g=t.lastReviewDate,k=g?pi(g,n):null,v;return t.nextReviewDate===null?v=true:v=t.nextReviewDate<=m,{lastTaskActivityAt:a,daysSinceActivity:s,availableTaskCount:c,blockedTaskCount:l,hasNoActions:d,deferredFutureTasks:f,lastReviewedAt:g,daysSinceReview:k,overdueForReview:v}}function bf(t,e,n){return n?!!(e||t.availableTaskCount===0||t.overdueForReview||t.deferredFutureTasks):false}function _f(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 Pf(t,e=14,n=new Date){let[r,o]=await Promise.all([t.listProjects(),t.listTasks({})]),a=wf(o),i=[];for(let s of r){let c=s.status==="active"&&!s.completed&&!s.dropped;if(!c)continue;let l=a.get(String(s.id))??[],d=jf(s,l,n),m=_t(s,d.lastTaskActivityAt,n,e);bf(d,m,c)&&i.push({projectId:String(s.id),name:s.name,status:s.status,signals:d,_severity:_f(d)});}return i.sort((s,c)=>s._severity!==c._severity?c._severity-s._severity:s.name.localeCompare(c.name)),{projects:i.map(({_severity:s,...c})=>c),staleDays:e,generatedAt:n.toISOString()}}function Of(t){if(!t)return 14;let e=Number.parseInt(t,10);return !Number.isFinite(e)||e<1?14:e}function mi(t,e){t.registerResource("omnifocus-project-health",new ResourceTemplate(ui,{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. 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=Of(r.staleDays),i=await Pf(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var fi="omnifocus://recent-activity{?hours}",ir=24,Df=168;function Af(t){if(t==null||t==="")return ir;let e=Number(t);return !Number.isFinite(e)||e<0?ir:Math.min(Math.max(1,Math.round(e)),Df)}async function Cf(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),l=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),d=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:l,projectsModified:d,summary:{taskCreatedCount:i.length,taskCompletedCount:s.length,taskDroppedCount:c.length,taskDeferredCount:l.length,projectsAffected:d.length}}}function gi(t,e){t.registerResource("omnifocus-recent-activity",new ResourceTemplate(fi,{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=Af(r.hours),a=o===ir?"omnifocus://recent-activity":`omnifocus://recent-activity?hours=${o}`,i=await Cf(e,o);return {contents:[{uri:a,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var yi="omnifocus://retrospective{?from,to}",Mf=7;function Ff(t,e,n=()=>new Date){let r=n(),o=r.toISOString(),a=new Date(r.getTime()-Mf*864e5).toISOString(),i=hi(t)?t:a,s=hi(e)?e:o;return i>s?{from:s,to:i}:{from:i,to:s}}function hi(t){if(t==null||t==="")return false;let e=new Date(t);return Number.isFinite(e.getTime())}async function Ef(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(d=>d.completedAt!==null&&d.completedAt<=r).map(d=>{let m=d.completedAt,f=new Date(m).getTime()-new Date(d.createdAt).getTime();return {taskId:String(d.id),name:d.name,projectId:d.projectId!==null?String(d.projectId):null,completedAt:m,age_days_at_completion:Math.max(0,Math.round(f/864e5))}}).sort((d,m)=>m.completedAt>d.completedAt?1:m.completedAt<d.completedAt?-1:0),s=a.filter(d=>d.dropped&&d.droppedAt!==null&&d.droppedAt>=n&&d.droppedAt<=r).map(d=>({taskId:String(d.id),name:d.name,projectId:d.projectId!==null?String(d.projectId):null,droppedAt:d.droppedAt})).sort((d,m)=>m.droppedAt>d.droppedAt?1:m.droppedAt<d.droppedAt?-1:0),c=a.filter(d=>!d.dropped&&d.deferDate!==null&&d.modifiedAt>=n&&d.modifiedAt<=r).map(d=>({taskId:String(d.id),name:d.name,projectId:d.projectId!==null?String(d.projectId):null,deferDate:d.deferDate})).sort((d,m)=>m.deferDate>d.deferDate?1:m.deferDate<d.deferDate?-1:0),l=new Set;for(let d of i)d.projectId!==null&&l.add(d.projectId);for(let d of s)d.projectId!==null&&l.add(d.projectId);for(let d of c)d.projectId!==null&&l.add(d.projectId);return {window:{from:n,to:r},completed:i,dropped:s,rolled:c,summary:{completedCount:i.length,droppedCount:s.length,rolledCount:c.length,projectsActive:l.size}}}function Ii(t,e){t.registerResource("omnifocus-retrospective",new ResourceTemplate(yi,{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=Ff(o.from,o.to),i=await Ef(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 cr="omnifocus://stats";function Nf(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function Uf(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 Lf(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 Jf(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 Bf(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=Nf(e),l=Uf(e),d=e.toISOString(),m=0,f=0,g=0,k=0,v=0,S=0,$=0,T=0,D=0;for(let L of n){if(m+=1,L.completed){L.completedAt&&(L.completedAt>=l&&(S+=1),L.completedAt>=c&&(v+=1));continue}if(L.dropped){L.droppedAt&&L.droppedAt>=c&&(D+=1);continue}L.available&&(f+=1),L.blocked&&(g+=1),L.flagged&&(T+=1),L.deferDate&&L.deferDate>d&&(k+=1),L.dueDate&&L.dueDate<d&&($+=1);}let P=0,U=0,b=0,F=0,z=Lf(n),X=0;for(let L of o){L.status==="active"&&!L.completed&&!L.dropped&&(P+=1),L.status==="on-hold"&&(U+=1),(L.completed||L.status==="done")&&(b+=1),(L.dropped||L.status==="dropped")&&(F+=1);let Je=z.get(String(L.id))??[],be=Jf(Je.map(qe=>qe.modifiedAt));_t(L,be,e)&&(X+=1);}let A=null;if(r.length>0){let L=r.map(be=>be.createdAt).reduce((be,qe)=>qe<be?qe:be),Je=e.getTime()-new Date(L).getTime();A=Math.max(0,Math.floor(Je/(1440*60*1e3)));}let oe=new Set;for(let L of n)for(let Je of L.tagIds)oe.add(String(Je));let le=oe.size,Z=null;if(s.lastSyncAt){let L=e.getTime()-new Date(s.lastSyncAt).getTime();Z=Math.max(0,Math.floor(L/1e3));}return {tasks:{total:m,available:f,blocked:g,deferred:k,completed_today:v,completed_this_week:S,overdue_count:$,flagged_count:T,dropped_today:D},projects:{total:o.length,active:P,on_hold:U,completed:b,dropped:F,stalled_count:X,due_for_review_count:a.length},inbox:{count:r.length,oldest_age_days:A},tags:{total:i.length,with_tasks_count:le},database:{sync_age_seconds:Z,last_sync_at:s.lastSyncAt}}}function ki(t,e){t.registerResource("omnifocus-stats",cr,{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 Bf(e);return {contents:[{uri:cr,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function $f(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 Pt(t){return t.toLowerCase().trim().replace(/\s+/g," ").replace(/^@/,"")}function Ti(t){return Pt(t).split(/[\s\-_]+/).filter(Boolean).sort()}function Wf(t,e){let n=Ti(t),r=Ti(e);return n.length!==r.length?false:n.every((o,a)=>o===r[a])}function dr(t,e){if(t===e)return "exact-duplicate";let n=Pt(t),r=Pt(e);return n===r?"case-difference":n===`${r}s`||r===`${n}s`||n===`${r}es`||r===`${n}es`?"plural-singular":$f(n,r)<=2||Wf(t,e)?"near-duplicate":null}var Hf=2,Gf=new Set(["a","an","and","as","at","by","for","from","in","is","it","of","on","or","the","to","with"]),zf=.7,Vf=.2,qf=.05,Kf=.05;function rn(t){return t?t.toLowerCase().split(/[^a-z0-9']+/i).filter(e=>e.length>=Hf&&!Gf.has(e)):[]}function vi(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 wi(t,e){let n=new Set(rn(t.name)),r=new Set(rn(e.name)),o=vi(n,r),a=0;if(t.note&&e.note){let m=new Set(rn(t.note)),f=new Set(rn(e.note));m.size>0&&f.size>0&&(a=vi(m,f));}let i=[...n][0],s=[...r][0],c=i!==void 0&&i===s?1:0,l=Pt(t.name)===Pt(e.name)?1:0,d=o*zf+a*Vf+c*qf+l*Kf;return Math.max(0,Math.min(1,d))}var lr="omnifocus://taxonomy-audit";async function Xf(t){let[e,n]=await Promise.all([t.listTags(),t.listProjects()]),r=Yf(e),o=Qf(n);return {tagCollisions:r,projectCollisions:o}}function Yf(t){let e=t.map(n=>({tagId:String(n.id),name:n.name,taskCount:n.taskCount}));return bi(e,(n,r)=>dr(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function Qf(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 bi(e,(n,r)=>dr(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function bi(t,e,n){if(t.length<2)return [];let r=t.map((l,d)=>d),o=new Map;function a(l){for(;r[l]!==l;)r[l]=r[r[l]],l=r[l];return l}function i(l,d,m){let f=a(l),g=a(d);if(f===g){let v=o.get(f)??"near-duplicate";o.set(f,ji(v,m));return}r[g]=f;let k=o.get(f)??o.get(g)??m;o.set(f,ji(k,m));}for(let l=0;l<t.length;l++)for(let d=l+1;d<t.length;d++){let m=e(t[l],t[d]);m!==null&&i(l,d,m);}let s=new Map;for(let l=0;l<t.length;l++){let d=a(l),m=s.get(d);m?m.push(t[l]):s.set(d,[t[l]]);}let c=[];for(let[l,d]of s)if(d.length>=2){let m=o.get(l)??"near-duplicate";c.push(n(d,m));}return c}var Si={"exact-duplicate":0,"case-difference":1,"plural-singular":2,"near-duplicate":3};function ji(t,e){return Si[t]<=Si[e]?t:e}function _i(t,e){t.registerResource("omnifocus-taxonomy-audit",lr,{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 Xf(e);return {contents:[{uri:lr,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function pr(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 on(t){let e=new Date(t);return e.setDate(e.getDate()+7),e}function Pi(t,e=new Date){let n=pr(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 xi="omnifocus://velocity{?weeks}",Oi=8,eg=52;function tg(t){if(t==null||t==="")return Oi;let e=Number(t);return !Number.isFinite(e)||e<0?Oi:Math.min(eg,Math.max(1,Math.round(e)))}async function ng(t,e,n=new Date){let r=Pi(e,n),o=r[0],a=r[r.length-1],i=on(a),s=o.toISOString(),c=i.toISOString(),[l,d,m]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true,completedSince:s}),t.listProjects()]),f=new Map;for(let b of m)f.set(String(b.id),b.name);let g=r.map(b=>{let F=on(b),z=b.toISOString(),X=F.toISOString(),A=l.filter(Z=>Z.createdAt>=z&&Z.createdAt<X).length,oe=d.filter(Z=>Z.completedAt!==null&&Z.completedAt>=z&&Z.completedAt<X).length,le=l.filter(Z=>Z.dropped&&Z.droppedAt!==null&&Z.droppedAt>=z&&Z.droppedAt<X).length;return {weekStart:z,created:A,completed:oe,dropped:le,netDelta:A-oe-le}}),k=[];for(let b of [4,8]){if(e<b)continue;let F=g.slice(-b),z=F.reduce((A,oe)=>A+oe.completed,0),X=F.reduce((A,oe)=>A+oe.created,0);k.push({window:b,completedPerWeek:Math.round(z/b*100)/100,createdPerWeek:Math.round(X/b*100)/100});}let v=pr(a).toISOString(),S=on(a).toISOString(),$=r[Math.max(0,r.length-4)].toISOString(),T=new Map,D=new Map;for(let b of d){if(b.completedAt===null||b.projectId===null)continue;let F=String(b.projectId);b.completedAt>=v&&b.completedAt<S&&T.set(F,(T.get(F)??0)+1),b.completedAt>=$&&b.completedAt<c&&D.set(F,(D.get(F)??0)+1);}let P=new Set([...T.keys(),...D.keys()]),U=Array.from(P).map(b=>({projectId:b,name:f.get(b)??b,closedThisWeek:T.get(b)??0,closedTrailing4:D.get(b)??0})).sort((b,F)=>F.closedThisWeek!==b.closedThisWeek?F.closedThisWeek-b.closedThisWeek:F.closedTrailing4-b.closedTrailing4).slice(0,5);return {window:{from:s,to:c},weeklyTotals:g,rollingAverages:k,topClosingProjects:U}}function Di(t,e){t.registerResource("omnifocus-velocity",new ResourceTemplate(xi,{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=tg(r.weeks),i=await ng(e,a);return {contents:[{uri:`omnifocus://velocity?weeks=${a}`,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var rg=new Set(["today","tomorrow","yesterday","this-week","next-week","end-of-week","end-of-month"]);function Ot(t){return typeof t=="string"&&rg.has(t)}function $e(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 xt(t,e=new Date){let n=new Date(e);switch(n.setHours(0,0,0,0),t){case "today":return $e(n);case "tomorrow":{let r=new Date(n);return r.setDate(r.getDate()+1),$e(r)}case "yesterday":{let r=new Date(n);return r.setDate(r.getDate()-1),$e(r)}case "this-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?-6:1-o)),$e(r)}case "next-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?1:8-o)),$e(r)}case "end-of-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?0:7-o)),$e(r)}case "end-of-month":{let r=new Date(n);return r.setMonth(r.getMonth()+1,0),$e(r)}}}var Ai=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;function Dt(t){return typeof t=="string"&&Ai.test(t)&&!Number.isNaN(Date.parse(t))}function ae(){return z.string().regex(Ai,"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 Pe(){return z.string().transform((t,e)=>Dt(t)?t:Ot(t)?xt(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 We(t,e){if(t===null||t.length===0)return;let n=`\`\`\`${e}`,o=new RegExp(`(^|\\n)${og(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 sn(t){let e={},n=t.split(`
115
+ `);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 cn(t){let e=[];for(let[n,r]of Object.entries(t))r!==void 0&&e.push(`${n}: ${r}`);return e.join(`
116
+ `)}function dn(t,e,n){let r=`\`\`\`${e}
117
+ ${n}
118
+ \`\`\``,o=We(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}
119
+
120
+ ${t}`}function Ci(t,e){if(t===null)return null;let n=We(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}
121
+
122
+ ${o}`,a===""?null:a}function og(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var mr="waiting-on",fr=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:ae().describe("When the wait started (ISO-8601 with offset)."),followUpAfter:ae().optional().describe("When the agent should nudge if still unresolved (ISO-8601 with offset).")});function nt(t){let e=We(t,mr);if(e===void 0)return;let n=sn(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=fr.safeParse(r);return o.success?o.data:void 0}function Ri(t,e){let n=cn({whom:e.whom,what:e.what,since:e.since,followUpAfter:e.followUpAfter});return dn(t,mr,n)}function Mi(t){return Ci(t,mr)}function Fi(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 gr="omnifocus://waiting-on";async function ag(t,e=new Date){let n=await t.listTasks({completed:false}),r=[];for(let o of n){let a=nt(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:Fi(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 Ei(t,e){t.registerResource("omnifocus-waiting-on",gr,{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 ag(e);return {contents:[{uri:gr,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}var Ct="omnifocus://snapshot",Rt="omnifocus://inbox",Mt="omnifocus://forecast/today",Ft="omnifocus://overdue",Et="omnifocus://flagged",Nt="omnifocus://review-due",yr="omnifocus://project/{id}",Ir="omnifocus://tag/{id}",kr="omnifocus://perspective/{id}",Ni="omnifocus://tasks/inbox",sg="omnifocus://tasks/project/{projectId}",ig="omnifocus://tasks/tag/{tagId}";function hr(){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 ue(t,e){return {contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(e,null,2)}]}}function Ui(t,e){let{adapter:n,projectService:r,reviewService:o,forecastService:a,perspectiveService:i}=e;t.registerResource("omnifocus-snapshot",Ct,{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:l}=hr(),[d,m,f,g]=await Promise.all([n.listTasks({completed:false}),n.getForecast({from:c,to:l,includeOverdue:true,includeFlagged:true}),n.listProjectsDueForReview(),n.getLastSync()]),k=d.filter(v=>v.projectId===null&&v.parentId===null).length;return ue(Ct,{inboxCount:k,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",Rt,{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 l=(await n.listTasks({completed:false})).filter(d=>d.projectId===null&&d.parentId===null);return ue(Rt,l)}),t.registerResource("omnifocus-forecast-today",Mt,{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:l}=hr(),d=await a.get({from:c,to:l});return ue(Mt,{overdue:d.overdue,dueToday:d.dueToday,deferredToday:d.deferredToday,flagged:d.flagged})}),t.registerResource("omnifocus-overdue",Ft,{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}=hr(),d=[...(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 ue(Ft,d)}),t.registerResource("omnifocus-flagged",Et,{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 ue(Et,c)}),t.registerResource("omnifocus-review-due",Nt,{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 l=[...(await o.listDue()).projects].sort((d,m)=>d.nextReviewDate?m.nextReviewDate?d.nextReviewDate<m.nextReviewDate?-1:d.nextReviewDate>m.nextReviewDate?1:0:-1:1);return ue(Nt,l)}),t.registerResource("omnifocus-project",new ResourceTemplate(yr,{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 l=y.of(c.id),d=await r.get({id:l,includeTaskTree:true});return ue(`omnifocus://project/${l}`,{project:d.project,tasks:d.tasks??[]})}),t.registerResource("omnifocus-tag",new ResourceTemplate(Ir,{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 l=w.of(c.id),[d,m]=await Promise.all([n.getTag(l),n.listTasks({tagId:l,completed:false})]);return ue(`omnifocus://tag/${l}`,{tag:d,tasks:m})}),t.registerResource("omnifocus-perspective",new ResourceTemplate(kr,{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 l=c.id,d=await i.evaluate(l);return ue(`omnifocus://perspective/${l}`,{perspectiveId:l,tasks:d.tasks})}),t.registerResource("omnifocus-tasks-inbox",Ni,{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(l=>l.projectId===null&&l.parentId===null);return ue(Ni,c)}),t.registerResource("omnifocus-tasks-by-project",new ResourceTemplate(sg,{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 l=y.of(c.projectId),d=await n.listTasks({projectId:l,completed:false});return ue(`omnifocus://tasks/project/${l}`,d)}),t.registerResource("omnifocus-tasks-by-tag",new ResourceTemplate(ig,{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 l=w.of(c.tagId),d=await n.listTasks({tagId:l,completed:false});return ue(`omnifocus://tasks/tag/${l}`,d)}),gi(t,n),Ei(t,n),Ii(t,n),_i(t,n),Di(t,n),di(t,n),li(t),ki(t,n),mi(t,n);}function rt(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 u(t){return {content:[{type:"text",text:JSON.stringify(t)}],structuredContent:t}}var Tr="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()",dg=z.object({});async function lg(t,e){let n=await e.adapter.appLaunch(),r=e.makeMeta();return p({launched:n.launched,alreadyRunning:n.alreadyRunning},r)}function Li(t,e){return t.registerTool("app_launch",{description:Tr,inputSchema:dg.shape},async n=>{let r=await lg(n,e);return u(r)})}var ln=z.object({taskId:h.schema.optional().describe("Persistent ID of the task that owns the attachment. Provide exactly one of taskId or projectId."),projectId:y.schema.optional().describe("Persistent ID of the project that owns the attachment. Provide exactly one of taskId or projectId.")});function pn(t){if(t.taskId)return {taskId:h.of(t.taskId)};if(t.projectId)return {projectId:y.of(t.projectId)};throw new I("Provide exactly one of taskId or projectId.",{})}var wr='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" })',pg=ln;async function ug(t,e){let n=pn(t),r=await e.attachmentService.list(n);return p({attachments:r},e.makeMeta())}var Sr=`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" })`,mg=ln.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 fg(t,e){let n=pn(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 jr=`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" })`,gg=ln.extend({attachmentId:ve.schema.describe("Persistent ID of the attachment to remove. Get from attachment_list.")});async function hg(t,e){let n=pn(t),r=ve.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 br=`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" })`,yg=ln.extend({attachmentId:ve.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 Ig(t,e){let n=pn(t),r=await e.attachmentService.saveTo({...n,attachmentId:ve.of(t.attachmentId),destPath:t.destPath});return p(r,e.makeMeta())}function Ji(t,e){t.registerTool("attachment_list",{description:wr,inputSchema:pg.shape},async n=>{let r=await ug(n,e);return u(r)}),t.registerTool("attachment_add",{description:Sr,inputSchema:mg.shape},async n=>{let r=await fg(n,e);return u(r)}),t.registerTool("attachment_remove",{description:jr,inputSchema:gg.shape},async n=>{let r=await hg(n,e);return u(r)}),t.registerTool("attachment_save_to_path",{description:br,inputSchema:yg.shape},async n=>{let r=await Ig(n,e);return u(r)});}function j(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 B(t,e){t.invalidate(`project:${e.projectId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function Bi(t,e){t.invalidate(`tag:${e.tagId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function $i(t,e){t.invalidate(`folder:${e.folderId}`),t.invalidate("perspective:*"),t.invalidate("search:*");}function Wi(t){t.clear();}function un(t){t.clear();}var _r="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 })",kg=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 Tg(t,e){let n=await e.adapter.redoLastMutation();return n.redid&&e.cache!==void 0&&un(e.cache),p(n,e.makeMeta({syncPending:n.redid}))}function Gi(t,e){return t.registerTool("database_redo",{description:_r,inputSchema:kg.shape},async n=>{let r=await Tg(n,e);return u(r)})}var Pr="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 })",vg=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 wg(t,e){let n=await e.adapter.undoLastMutation();return n.undid&&e.cache!==void 0&&un(e.cache),p(n,e.makeMeta({syncPending:n.undid}))}function Vi(t,e){return t.registerTool("database_undo",{description:Pr,inputSchema:vg.shape},async n=>{let r=await wg(n,e);return u(r)})}var xr=`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" })`,Sg=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 jg(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new I(`scope='${t.scope}' requires an id`,{details:{field:"id",scope:t.scope}});return t.scope==="project"?{kind:"project",id:y.of(t.id)}:{kind:"folder",id:J.of(t.id)}}async function bg(t,e){let n=jg(t),r=await e.exportService.exportOpml(n),o=e.makeMeta();return p({opml:r.opml,projectCount:r.projectCount,taskCount:r.taskCount},o)}function qi(t,e){return t.registerTool("export_opml",{description:xr,inputSchema:Sg.shape},async n=>{let r=await bg(n,e);return u(r)})}function H(t){return `'${t}'`}function W(t){return t.length>140?`${t.slice(0,137)}\u2026`:t}function Ki(t){return W(`Created task ${H(t)}.`)}function Xi(t){return W(`Completed task ${H(t)}.`)}function Yi(t){return W(`Uncompleted task ${H(t)}.`)}function Qi(t){return W(`Deleted task ${H(t)}.`)}function Zi(t){return W(`Dropped task ${H(t)}.`)}function ec(t){return W(`Restored task ${H(t)} from dropped.`)}function tc(t,e){return W(`Moved task ${H(t)} to ${e}.`)}function nc(t){return W(`Duplicated task ${H(t)}.`)}function rc(t){return W(`Reordered task ${H(t)}.`)}function oc(t){return W(`Converted task ${H(t)} to a project.`)}function ac(t){return W(`Set repetition rule on task ${H(t)}.`)}function sc(t){return W(`Cleared repetition rule on task ${H(t)}.`)}function ic(t,e){return W(`Set ${e} ${e===1?"alarm":"alarms"} on task ${H(t)}.`)}function cc(t){return W(`Cleared alarms on task ${H(t)}.`)}function ot(t){return `Created ${t} task${t===1?"":"s"}.`}function dc(t){return `Updated ${t} task${t===1?"":"s"}.`}function lc(t){return `Completed ${t} task${t===1?"":"s"}.`}function pc(t){return `Uncompleted ${t} task${t===1?"":"s"}.`}function uc(t){return `Deleted ${t} task${t===1?"":"s"}.`}function mc(t){return `Dropped ${t} task${t===1?"":"s"}.`}function fc(t){return `Restored ${t} dropped task${t===1?"":"s"}.`}function gc(t,e){return W(`Moved ${t} task${t===1?"":"s"} to ${e}.`)}function hc(t){return W(`Created project ${H(t)}.`)}function yc(t){return W(`Updated project ${H(t)}.`)}function Ic(t){return W(`Deleted project ${H(t)}.`)}function kc(t){return `Completed ${t} project${t===1?"":"s"}.`}function Tc(t){return `Dropped ${t} project${t===1?"":"s"}.`}function vc(t){return W(`Created tag ${H(t)}.`)}function Ce(t){return W(`Updated tag ${H(t)}.`)}function wc(t,e){return W(`Moved tag ${H(t)} to ${e}.`)}function Sc(t){return W(`Created folder ${H(t)}.`)}function jc(t){return W(`Updated folder ${H(t)}.`)}function bc(t,e){return W(`Moved folder ${H(t)} to ${e}.`)}function mn(t,e){return W(`Set note on ${t} ${H(e)}.`)}function _c(t,e){return W(`Appended to note on ${t} ${H(e)}.`)}function Pc(){return "Completed project."}function Oc(){return "Dropped project."}function xc(t){return W(`Moved project to ${t}.`)}function Dc(){return "Deleted tag."}function Ac(){return "Deleted folder."}function fn(){return "Marked project as reviewed."}function Cc(t){return t===null?"Cleared project review interval.":`Set project review interval to ${t} day${t===1?"":"s"}.`}function Rc(t){return t===null?"Cleared project next review date.":W(`Set project next review date to ${t}.`)}var Ar='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. Example: import_opml({ opml: "<opml>...</opml>" })',_g=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 Pg(t,e){let n=await e.exportService.importOpml(t.opml,{...t.destinationProjectId!==void 0?{destinationProjectId:y.of(t.destinationProjectId)}:{}}),r=e.makeMeta({syncPending:true,humanReadableSummary:ot(n.imported)});return p({imported:n.imported,taskIds:n.taskIds.map(String)},r)}function Mc(t,e){return t.registerTool("import_opml",{description:Ar,inputSchema:_g.shape},async n=>{let r=await Pg(n,e);return u(r)})}var Cr=`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" })`,Rr=`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. Example: import_taskpaper({ text: "- Buy milk @errands\\n- Call dentist @due(2026-05-01)" })`,Og=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'.")}),xg=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 Dg(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new I(`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:y.of(t.id)}:{kind:"folder",id:J.of(t.id)}}function Fc(t,e){t.registerTool("export_taskpaper",{description:Cr,inputSchema:Og.shape},async n=>{let r=Dg(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:Rr,inputSchema:xg.shape},async n=>{let r=await e.exportService.importTaskPaper(n.text,n.targetProjectId===void 0?void 0:y.of(n.targetProjectId));return u(p({created:r.created,warnings:r.warnings},e.makeMeta()))});}var Mr=`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" })`,Fr=z.object({name:z.string().min(1).describe("Folder name. Must be non-empty."),parentId:J.schema.optional().describe("Parent folder ID. Omit for a root-level folder. Get from folder_list.")});async function Ag(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:Sc(t.name)}))}function Nc(t,e){return t.registerTool("folder_create",{description:Mr,inputSchema:Fr.shape},async n=>{let r=await Ag(n,e);return u(r)})}function me(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 st(t,e){try{return (await t.getProject(e)).name}catch{return e}}async function it(t,e){try{return (await t.getTask(e)).name}catch{return e}}async function ce(t,e){try{return (await t.getTag(e)).name}catch{return e}}async function Re(t,e){try{return (await t.getFolder(e)).name}catch{return e}}var Er="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 Cg(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await Re(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 Uc(t,e){return t.registerTool("folder_create_describe",{description:Er,inputSchema:Fr.shape},async n=>{let r=await Cg(n,e);return u(r)})}var Nr='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 })',Ur=z.object({id:J.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 Rg(t,e){return await e.folderService.delete(t.id,t.cascade??false),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Ac()}))}function Jc(t,e){return t.registerTool("folder_delete",{description:Nr,inputSchema:Ur.shape},async n=>{let r=await Rg(n,e);return u(r)})}var Lr="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 Mg(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 Bc(t,e){return t.registerTool("folder_delete_describe",{description:Lr,inputSchema:Ur.shape},async n=>{let r=await Mg(n,e);return u(r)})}var Jr='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" })',Eg=z.object({id:J.schema.describe("Persistent folder ID. Get from folder_list. IDs are stable across renames.")});async function Ng(t,e){let n=await e.folderService.get(t.id);return p({folder:n.folder},e.makeMeta({cacheHit:n.cacheHit}))}function $c(t,e){return t.registerTool("folder_get",{description:Jr,inputSchema:Eg.shape},async n=>{let r=await Ng(n,e);return u(r)})}var Br='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" })',Lg=z.object({parentId:J.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 Jg(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 Wc(t,e){return t.registerTool("folder_list",{description:Br,inputSchema:Lg.shape},async n=>{let r=await Jg(n,e);return u(r)})}var $r=`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 })`,Wr=z.object({id:J.schema.describe("Persistent ID of the folder to move. Get from folder_list."),parentId:J.schema.nullable().describe("New parent folder ID, or null to promote the folder to root level.")});async function $g(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:bc(n.name,t.parentId!=null?"folder":"library root")}))}function Hc(t,e){return t.registerTool("folder_move",{description:$r,inputSchema:Wr.shape},async n=>{let r=await $g(n,e);return u(r)})}var Hr="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 Wg(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 Re(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 Gc(t,e){return t.registerTool("folder_move_describe",{description:Hr,inputSchema:Wr.shape},async n=>{let r=await Wg(n,e);return u(r)})}var Gr='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" })',zr=z.object({id:J.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 Hg(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:jc(o.name)}))}function Vc(t,e){return t.registerTool("folder_update",{description:Gr,inputSchema:zr.shape},async n=>{let r=await Hg(n,e);return u(r)})}var Vr="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 Gg(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 qc(t,e){return t.registerTool("folder_update_describe",{description:Vr,inputSchema:zr.shape},async n=>{let r=await Gg(n,e);return u(r)})}var qr=`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 })`,zg=z.object({date:Pe().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:Pe().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:Pe().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 Vg(t){let e;if(Ot(t))e=xt(t);else if(Dt(t))e=t;else throw new I(`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 qg(t){let e=t.date!==void 0,n=t.from!==void 0||t.to!==void 0;if(e&&n)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=t.days??1;if(e){let s=Vg(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 l=new Date(s);return l.setHours(23,59,59,999),{from:t.from??c.toISOString(),to:t.to??l.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 Kg(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 Xg(t,e){let{from:n,to:r,days:o}=qg(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=Kg(a.dueToday)),p(s,i)}function Kc(t,e){return t.registerTool("forecast_get",{description:qr,inputSchema:zg.shape},async n=>{let r=await Xg(n,e);return u(r)})}var Kr="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()",Qg=z.object({});async function Zg(t,e){let n=await e.forecastService.getForecastTag();return p(n,e.makeMeta())}function Xc(t,e){return t.registerTool("forecast_get_tag",{description:Kr,inputSchema:Qg.shape},async n=>{let r=await Zg(n,e);return u(r)})}var Xr=`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" } })`,eh=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(w.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 th(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 nh(t,e,n){let r=n?.tagIds,a=[...r&&r.length>0?t.filter(l=>l.tagIds.some(d=>r.includes(d))):t].sort(th),i=[],s=[],c=0;for(let l of a){if(l.estimatedMinutes===null){s.push({taskId:l.id,name:l.name,estimatedMinutes:null,reason:"no-estimate"});continue}c+l.estimatedMinutes<=e?(i.push({taskId:l.id,name:l.name,estimatedMinutes:l.estimatedMinutes,flagged:l.flagged,dueDate:l.dueDate}),c+=l.estimatedMinutes):s.push({taskId:l.id,name:l.name,estimatedMinutes:l.estimatedMinutes,reason:"exceeds-budget"});}return {selected:i,totalMinutes:c,skipped:s}}function rh(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 oh(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 ah(t,e){let n=t.filter?.scope??"today",{from:r,to:o}=rh(n),a=await e.forecastService.get({from:r,to:o,includeOverdue:true,includeDeferred:n==="next7",includeFlagged:true}),i=oh(a),s=nh(i,t.budgetMinutes,t.filter),c=e.makeMeta({cacheHit:a.cacheHit});return p(s,c)}function Yc(t,e){return t.registerTool("forecast_pack",{description:Xr,inputSchema:eh.shape},async n=>{let r=await ah(n,e);return u(r)})}var Qr=`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 })`,sh=z.object({tagId:z.union([w.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 ih(t,e){let n=await e.forecastService.setForecastTag(t.tagId);return e.cache.invalidate("forecast:*"),p(n,e.makeMeta())}function Qc(t,e){return t.registerTool("forecast_set_tag",{description:Qr,inputSchema:sh.shape},async n=>{let r=await ih(n,e);return u(r)})}var Zr=`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" })`,ch=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 dh(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(y.of(t.id));n=a.note,r=a.name;}let o=n?`${n}
123
+ ${t.text}`:t.text;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{note:o}),e.cache!==void 0&&j(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(y.of(t.id),{note:o}),e.cache!==void 0&&B(e.cache,{projectId:y.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:r,note:o},e.makeMeta({syncPending:true,humanReadableSummary:_c(t.targetKind,r)}))}function Zc(t,e){return t.registerTool("note_append",{description:Zr,inputSchema:ch.shape},async n=>{let r=await dh(n,e);return u(r)})}var to=`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" })`,lh=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 ph(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).note:(await e.adapter.getProject(y.of(t.id))).note;return p({note:n??null},e.makeMeta())}function ed(t,e){return t.registerTool("note_get",{description:to,inputSchema:lh.shape},async n=>{let r=await ph(n,e);return u(r)})}var ro=`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" })`,uh=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 mh(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).noteHtml:(await e.adapter.getProject(y.of(t.id))).noteHtml;return p({noteHtml:n??null},e.makeMeta())}function td(t,e){return t.registerTool("note_get_html",{description:ro,inputSchema:uh.shape},async n=>{let r=await mh(n,e);return u(r)})}var oo=`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" })`,fh=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 gh(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(y.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{note:t.note}),e.cache!==void 0&&j(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(y.of(t.id),{note:t.note}),e.cache!==void 0&&B(e.cache,{projectId:y.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,note:t.note},e.makeMeta({syncPending:true,humanReadableSummary:mn(t.targetKind,n)}))}function nd(t,e){return t.registerTool("note_set",{description:oo,inputSchema:fh.shape},async n=>{let r=await gh(n,e);return u(r)})}var ao=`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" })`,hh=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 yh(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(y.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&j(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(y.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&B(e.cache,{projectId:y.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,noteHtml:t.noteHtml},e.makeMeta({syncPending:true,humanReadableSummary:mn(t.targetKind,n)}))}function rd(t,e){return t.registerTool("note_set_html",{description:ao,inputSchema:hh.shape},async n=>{let r=await yh(n,e);return u(r)})}var so="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. Example: internal_status()",kh=z.object({});async function Th(t,e){let n=Date.now()-e.startedAt,r=null;try{let i=await e.adapter.getLastSync();r={lastSyncAt:i.lastSyncAt,inFlight:i.inFlight};}catch{r=null;}let o=e.circuitRegistry.snapshot();return p({uptimeMs:n,ofRunning:true,lastSync:r,cache:null,circuits:o,queueDepth:null},e.makeMeta())}function od(t,e){return t.registerTool("internal_status",{description:so,inputSchema:kh.shape},async n=>{let r=await Th(n,e);return u(r)})}var io='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" }.',vh=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 wh(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 sd(t,e){return t.registerTool("perspective_delete",{description:io,inputSchema:vh.shape},async n=>{let r=await wh(n,e);return u(r)})}var co=`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" })`,Sh=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 jh(t,e){let n=await e.perspectiveService.evaluate(t.perspectiveId),r=e.makeMeta({cacheHit:n.cacheHit});return p({tasks:n.tasks},r)}function cd(t,e){return t.registerTool("perspective_evaluate",{description:co,inputSchema:Sh.shape},async n=>{let r=await jh(n,e);return u(r)})}var lo='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 } } }.',bh=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 _h(t,e){let n=await e.perspectiveService.get(t.perspectiveId),r=e.makeMeta({cacheHit:false});return p({perspective:n},r)}function ld(t,e){return t.registerTool("perspective_get",{description:lo,inputSchema:bh.shape},async n=>{let r=await _h(n,e);return u(r)})}var po="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()",Oh=z.object({});async function xh(t,e){let n=await e.perspectiveService.list(),r=e.makeMeta({cacheHit:n.cacheHit});return p({perspectives:n.perspectives},r)}function pd(t,e){return t.registerTool("perspective_list",{description:po,inputSchema:Oh.shape},async n=>{let r=await xh(n,e);return u(r)})}var ct=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 mo='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" } })',Dh=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 Ah(t,e){let n=new ct({adapter:e.adapter}),{result:r}=await n.invoke({identifier:t.identifier,arg:t.arg}),o=e.makeMeta();return p({result:r},o)}function ud(t,e){return t.registerTool("plugin_invoke",{description:mo,inputSchema:Dh.shape},async n=>{let r=await Ah(n,e);return u(r)})}var go='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" }] })',Ch=z.object({id:y.schema.describe("Persistent project ID.")}),Rh=z.object({items:z.array(Ch).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Mh(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&&B(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:kc(a.succeeded.length)}))}function md(t,e){return t.registerTool("project_batch_complete",{description:go,inputSchema:Rh.shape},async n=>{let r=await Mh(n,e);return u(r)})}var yo='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" }] })',Fh=z.object({id:y.schema.describe("Persistent project ID.")}),Eh=z.object({items:z.array(Fh).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Nh(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&&B(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:Tc(a.succeeded.length)}))}function fd(t,e){return t.registerTool("project_batch_drop",{description:yo,inputSchema:Eh.shape},async n=>{let r=await Nh(n,e);return u(r)})}var Io=`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" })`,ko=z.object({id:y.schema.describe("Persistent ID of the project to complete.")});async function Lh(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&&B(e.cache,{projectId:t.id}),p({completed:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Pc()}))}function gd(t,e){return t.registerTool("project_complete",{description:Io,inputSchema:ko.shape},async n=>{let r=await Lh(n,e);return u(r)})}var To="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 Jh(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 hd(t,e){return t.registerTool("project_complete_describe",{description:To,inputSchema:ko.shape},async n=>{let r=await Jh(n,e);return u(r)})}function ne(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 Bh(t,e={}){return {kind:"missing-detail",reason:t,...e}}function vo(t,e={}){return {kind:"next-natural-step",reason:t,...e}}function $h(t,e={}){return {kind:"consider-alternative",reason:t,...e}}function Wh(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 Hh(t){return process.env.OMNIFOCUS_HINT_LEVEL==="warn"?t.filter(e=>(e.severity??"info")==="warn"):t}function dt(t,e=3){let n=Hh(t),r=Wh(n,e);return r.length>0?r:void 0}var Gh=/\b(daily|weekly|monthly|every\s+(day|week|month|monday|tuesday|wednesday|thursday|friday|saturday|sunday|weekday|weekend))\b/i;function Id(t,e){if(Gh.test(e))return vo("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 kd(t,e,n){if(!(e===void 0||n!==void 0))return Bh("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 Td(t,e=5){if(!(t<e))return $h(`Inbox now has ${t} unrouted tasks \u2014 consider triaging to keep your inbox clear.`,{suggestedTool:"task_list",suggestedArgs:{inbox:true},severity:"info"})}function vd(t,e){if(e===void 0)return vo("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 wd(t,e){return vo(`Project '${e}' now has no remaining tasks \u2014 consider completing or reviewing the project.`,{suggestedTool:"project_complete",suggestedArgs:{id:t},severity:"info"})}var wo=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();}},Sd=new WeakMap;function zh(t){let e=Sd.get(t);return e||(e=new Map,Sd.set(t,e)),e}async function fe(t,e,n){if(e===void 0)return n();let r=t.get(e);if(r)return jd(r);let o=zh(t),a=o.get(e);if(a){let s=await a;return jd(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 jd(t){return {...t,meta:{...t.meta,idempotentReplay:true}}}function bd(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 ge=new wo({ttlMs:bd("OMNIFOCUS_IDEMPOTENCY_TTL_MS",6e5),maxEntries:bd("OMNIFOCUS_IDEMPOTENCY_MAX_ENTRIES",1024)});var So='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 })',jo=z.object({name:z.string().min(1).describe("Project name. Required, must be non-empty."),folderId:J.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:ne(["active","on-hold"],{paused:"on-hold"},"Initial project status. Default: active.").optional(),completionCriterion:ne(["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(w.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 Vh(t,e){let n=e.idempotencyStore??ge;return fe(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&&B(e.cache,{projectId:o});let a=dt([vd(o,t.reviewIntervalDays)].filter(i=>i!=null));return p({created:true,id:o},e.makeMeta({syncPending:true,humanReadableSummary:hc(t.name)}),void 0,a)})}function _d(t,e){return t.registerTool("project_create",{description:So,inputSchema:jo.shape},async n=>{let r=await Vh(n,e);return u(r)})}var bo="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 qh(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.folderId!==void 0){let a=await Re(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 ${me(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${me(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=>ce(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 Pd(t,e){return t.registerTool("project_create_describe",{description:bo,inputSchema:jo.shape},async n=>{let r=await qh(n,e);return u(r)})}function Me(t,e,n){if(t===void 0)return;let r=Date.parse(t);if(Number.isNaN(r))throw new I(`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 I(`observed modifiedAt for ${n} is not a valid ISO-8601 timestamp.`,{details:{resource:n,observed:e}});if(r!==o)throw new Qe(`${n} was modified since expectedModifiedAt.`,{details:{resource:n,expected:t,observed:e}})}async function Fe(t,e,n){if(t!==true)return n();let r=await e();return Kh(r)}function Kh(t){return {...t,meta:{...t.meta,dryRun:true,syncPending:false}}}var _o='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" })',Po=z.object({id:y.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 Xh(t,e){let n=e.idempotencyStore??ge;return fe(n,t.idempotency_key,async()=>{let r=await e.adapter.getProject(t.id);Me(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&&B(e.cache,{projectId:t.id}),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Ic(r.name)})));return Fe(t.dry_run,o,a)})}function Od(t,e){return t.registerTool("project_delete",{description:_o,inputSchema:Po.shape},async n=>{let r=await Xh(n,e);return u(r)})}var Oo="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 Yh(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 xd(t,e){return t.registerTool("project_delete_describe",{description:Oo,inputSchema:Po.shape},async n=>{let r=await Yh(n,e);return u(r)})}var xo='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" })',Do=z.object({id:y.schema.describe("Persistent ID of the project to drop.")});async function Zh(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&&B(e.cache,{projectId:t.id}),p({dropped:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Oc()}))}function Dd(t,e){return t.registerTool("project_drop",{description:xo,inputSchema:Do.shape},async n=>{let r=await Zh(n,e);return u(r)})}var Ao="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 ey(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 Ad(t,e){return t.registerTool("project_drop_describe",{description:Ao,inputSchema:Do.shape},async n=>{let r=await ey(n,e);return u(r)})}var Co=`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 })`,ty=z.object({id:y.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 ny(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={project:n.project};return n.tasks!==void 0&&(o.tasks=n.tasks),p(o,r)}function Rd(t,e){return t.registerTool("project_get",{description:Co,inputSchema:ty.shape},async n=>{let r=await ny(n,e);return u(r)})}var Ro='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"] })',kn=100,ry=z.object({ids:z.array(y.schema).min(0).max(kn).describe(`Array of project IDs to fetch (0..${kn}). Get IDs from project_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function oy(t,e){if(t.ids.length===0)return p({projects:[]},e.makeMeta());if(t.ids.length>kn)throw new I(`ids array exceeds the maximum batch size of ${kn} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getProjectsMany(t.ids),r=n.filter(s=>s!==null),o=t.ids.filter((s,c)=>n[c]===null),a=o.length>0?[rt(o)]:void 0,i=e.makeMeta({...a!==void 0?{warnings:a}:{}});return p({projects:r},i)}function Fd(t,e){return t.registerTool("project_get_many",{description:Ro,inputSchema:ry.shape},async n=>{let r=await oy(n,e);return u(r)})}var Mo='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" })',ay=z.object({folderId:J.schema.optional().describe("Restrict to projects inside this folder. Get the ID from folder_list. Omit for all folders."),status:ne(["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 sy(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 Ed(t,e){return t.registerTool("project_list",{description:Mo,inputSchema:ay.shape},async n=>{let r=await sy(n,e);return u(r)})}var Fo=`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 })`,Eo=z.object({id:y.schema.describe("Persistent ID of the project to move."),folderId:J.schema.nullable().describe("Target folder ID, or null to move to root.")});async function cy(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&&B(e.cache,{projectId:t.id}),p({moved:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:xc(t.folderId!=null?"folder":"library root")}))}function Nd(t,e){return t.registerTool("project_move",{description:Fo,inputSchema:Eo.shape},async n=>{let r=await cy(n,e);return u(r)})}var No="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 dy(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 Re(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 Ud(t,e){return t.registerTool("project_move_describe",{description:No,inputSchema:Eo.shape},async n=>{let r=await dy(n,e);return u(r)})}var Uo="project-template",ly=z.object({name:z.string().min(1),parameterNames:z.array(z.string().min(1)),capturedAt:ae()});function vn(t){let e=We(t,Uo);if(e===void 0)return;let n=sn(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=ly.safeParse(r);return o.success?o.data:void 0}function Ld(t,e){let n=cn({name:t.name,parameters:t.parameterNames.length>0?t.parameterNames.join(","):void 0,capturedAt:t.capturedAt}),r=dn(null,Uo,n);return e.length===0?r:`${r}
124
+
125
+ ${e}`}function Jd(t){let e=We(t,Uo);return e===void 0||t===null?"":t.slice(e.end).replace(/^\n+/,"")}function Bd(t,e){return t.replace(/\{\{\s*([A-Za-z0-9_-]+)\s*\}\}/g,(n,r)=>Object.hasOwn(e,r)?e[r]:n)}function $d(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 Wd(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,l=new Date(c),d=l.getUTCFullYear().toString().padStart(4,"0"),m=(l.getUTCMonth()+1).toString().padStart(2,"0"),f=l.getUTCDate().toString().padStart(2,"0");return `${d}-${m}-${f}`};return t.replace(/@(due|defer)\((\d{4}-\d{2}-\d{2})\)/g,(s,c,l)=>`@${c}(${i(l)})`)}async function wn(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 lt(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 we(t){return t.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Hd(t,e,n){let r=[`text="${we(t.name)}"`,'type="omnifocus:task"',`id="${we(String(t.id))}"`];t.dueDate&&r.push(`due="${we(t.dueDate)}"`),t.deferDate&&r.push(`defer="${we(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="${we(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=>Hd(s,e,a)).join(`
126
+ `);return `${n}<outline ${r.join(" ")}>
127
+ ${i}
128
+ ${n}</outline>`}function Gd(t,e,n){let{rootTasks:r,byParent:o}=lt(e),a=[`text="${we(t.name)}"`,'type="omnifocus:project"',`id="${we(String(t.id))}"`,`status="${we(t.status)}"`];t.dueDate&&a.push(`due="${we(t.dueDate)}"`),t.deferDate&&a.push(`defer="${we(t.deferDate)}"`),t.flagged&&a.push('flagged="true"'),t.note&&a.push(`note="${we(t.note)}"`);let i=`${n} `;if(r.length===0)return `${n}<outline ${a.join(" ")} />`;let s=r.map(c=>Hd(c,o,i)).join(`
129
+ `);return `${n}<outline ${a.join(" ")}>
130
+ ${s}
131
+ ${n}</outline>`}function ye(t,e){let r=new RegExp(`\\b${e}=(?:"([^"]*)"|'([^']*)')`,"i").exec(t);if(!r)return;let o=r[1]??r[2]??"";return py(o)}function py(t){return t.replace(/&quot;/g,'"').replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&")}function uy(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 zd(t,e,n){let o={text:ye(n,"text")??"",children:[]},a=ye(n,"type");a!==void 0&&(o.type=a);let i=ye(n,"id");i!==void 0&&(o.id=i);let s=ye(n,"due");s!==void 0&&(o.due=s);let c=ye(n,"defer");c!==void 0&&(o.defer=c),ye(n,"flagged")==="true"&&(o.flagged=true);let d=e;for(;d<t.length;){let m=t[d];if(!m)break;if(m.kind==="close"&&m.tag==="outline")return [o,d+1];if(m.kind==="open"&&m.tag==="outline")if(m.selfClose){let f=Vd(m.attrs);o.children.push(f),d++;}else {let[f,g]=zd(t,d+1,m.attrs);o.children.push(f),d=g;}else d++;}return [o,d]}function Vd(t){let n={text:ye(t,"text")??"",children:[]},r=ye(t,"type");r!==void 0&&(n.type=r);let o=ye(t,"id");o!==void 0&&(n.id=o);let a=ye(t,"due");a!==void 0&&(n.due=a);let i=ye(t,"defer");return i!==void 0&&(n.defer=i),ye(t,"flagged")==="true"&&(n.flagged=true),n}function qd(t){let e=t.trim();if(!/<opml\b/i.test(e))throw new I("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 I("Not valid OPML: missing <body> element.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});let n=uy(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 I("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(Vd(i.attrs)),a++;else {let[s,c]=zd(n,a+1,i.attrs);o.push(s),a=c;}else a++;}return {body:o}}function Bt(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 d of c.split(`
132
+ `))d.trim()&&r.push(`${a} ${d}`);t.noteHtml&&!t.note&&o.push(`Task "${t.name}": HTML note downgraded to plain text`);}let l=e.get(String(t.id))??[];for(let d of l)Bt(d,e,n+1,r,o);}function Xd(t,e,n){let r=t,o,a,i=false,s=false,c=[];r=r.replace(/@due\(([^)]+)\)/g,(f,g)=>(o=Kd(g.trim(),e,n,"due"),"")),r=r.replace(/@defer\(([^)]+)\)/g,(f,g)=>(a=Kd(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 l=r.split("//"),d=(l[0]??"").trim(),m=l[1]?l[1].trim():void 0;return d||n.push(`Line ${e}: empty task name after parsing tags \u2014 skipped`),{name:d||"(unnamed)",dueDate:o,deferDate:a,flagged:i,done:s,tagNames:c,note:m}}function Kd(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 Yd(t){let e=0;for(let n of t)if(n===" ")e++;else break;return e}var pt=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})=>Gd(s,c," ")).join(`
133
+ `),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(`
134
+ `),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 O(`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=>wn(this.adapter,c.id).then(l=>({project:c,tasks:l}))))){let{rootTasks:c,byParent:l}=lt(s);if(o.push(`${i.name}:`),i.note)for(let d of i.note.split(`
135
+ `))o.push(` ${d}`);for(let d of c)Bt(d,l,1,o,r);a+=s.length,o.push("");}return {taskpaper:o.join(`
136
+ `),projectCount:n.length,taskCount:a,warnings:r}}async importTaskPaper(e,n){if(!e.trim())throw new I("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 k=g.toLowerCase(),v=o.get(k);if(v)return v;let S=await this.adapter.createTag({name:g});return o.set(k,S),S},i=await this.adapter.listProjects(),s=new Map(i.map(g=>[g.name.toLowerCase(),g.id])),c=[],l=[],d=[],m=n,f=e.split(`
137
+ `);for(let g=0;g<f.length;g++){let k=f[g];if(!k)continue;let v=Yd(k),S=k.trimStart();if(!S.startsWith("- ")&&!S.startsWith("- ")&&S.endsWith(":")&&v===0){if(!n){let F=S.slice(0,-1).trim(),z=s.get(F.toLowerCase());z?m=z:(l.push(`Project "${F}" not found in OmniFocus \u2014 tasks will land in inbox`),m=void 0);}d.length=0;continue}if(!S.startsWith("- ")&&!S.startsWith("- "))continue;for(;d.length>0&&(d[d.length-1]?.depth??0)>=v;)d.pop();let $=S.slice(2).trim(),T=Xd($,g+1,l),D=[];for(let F of T.tagNames)try{D.push(await a(F));}catch{l.push(`Line ${g+1}: could not create tag "${F}" \u2014 skipped`);}let P=d[d.length-1],U={name:T.name,...P?{parentId:P.id}:{},...m&&!P?{projectId:m}:{},...T.dueDate?{dueDate:T.dueDate}:{},...T.deferDate?{deferDate:T.deferDate}:{},...T.flagged?{flagged:true}:{},...T.note?{note:T.note}:{},...D.length>0?{tagIds:D}:{}},b=await this.adapter.createTask(U);c.push(b),T.done&&await this.adapter.completeTask(b),d.push({depth:v,id:b});}return {created:c,warnings:l}}async importOpml(e,n={}){if(!e.trim())throw new I("opml is empty",{suggestion:"Provide a non-empty OPML XML string."});let r=qd(e),o=await this.adapter.listProjects(),a=new Map(o.map(l=>[String(l.id),l.id])),i=new Map(o.map(l=>[l.name.toLowerCase(),l.id])),s=[],c=async(l,d,m)=>{for(let f of l){let g={name:f.text||"(untitled)",...m!==void 0?{parentId:m}:d!==void 0?{projectId:d}:{},...f.due!==void 0?{dueDate:f.due}:{},...f.defer!==void 0?{deferDate:f.defer}:{},...f.flagged===true?{flagged:true}:{}},k=await this.adapter.createTask(g);s.push(k),f.children.length>0&&await c(f.children,d,k);}};for(let l of r.body){if(n.destinationProjectId!==void 0){await c([l],n.destinationProjectId,void 0);continue}if(l.type==="omnifocus:project"){let d=(l.id!==void 0?a.get(l.id):void 0)??i.get(l.text.toLowerCase());await c(l.children,d,void 0);}else await c([l],void 0,void 0);}return {imported:s.length,taskIds:s}}};var Jo='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" }.',my=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:J.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.")}),$t=class extends Error{code="TEMPLATE_NOT_FOUND";constructor(e){super(`No template named "${e}" was found in the Templates folder.`),this.name="TemplateNotFoundError";}},Lo=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 fy(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(k=>k.name.toLowerCase()===r);if(o===void 0)throw new $t(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),i=t.templateName.toLowerCase(),s=a.find(k=>k.name.toLowerCase()===i);if(s===void 0)throw new $t(t.templateName);let c=vn(s.note);if(c===void 0)throw new $t(t.templateName);let l=c.parameterNames.filter(k=>!Object.hasOwn(t.parameters,k));if(l.length>0)throw new Lo(l);let d=Jd(s.note);if(d=Bd(d,t.parameters),t.dueDate!==void 0){let k=$d(d);k!==void 0&&(d=Wd(d,k,t.dueDate));}let m=await e.adapter.createProject({name:t.templateName,...t.targetFolderId!==void 0&&{folderId:t.targetFolderId}}),g=await new pt({adapter:e.adapter}).importTaskPaper(d,m);return e.cache!==void 0&&B(e.cache,{projectId:m}),p({projectId:m,taskCount:g.created.length,...g.warnings.length>0&&{importWarnings:g.warnings}},e.makeMeta({syncPending:true}))}function Qd(t,e){return t.registerTool("project_template_instantiate",{description:Jo,inputSchema:my.shape},async n=>{let r=await fy(n,e);return u(r)})}var Bo="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.",hy=z.object({});async function yy(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=vn(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 Zd(t,e){return t.registerTool("project_template_list",{description:Bo,inputSchema:hy.shape},async n=>{let r=await yy(n,e);return u(r)})}var Wo='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"] }.',Iy=z.object({projectId:y.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.")}),$o=class extends Error{code="TEMPLATE_EXISTS";constructor(e){super(`A template named "${e}" already exists in the Templates folder.`),this.name="TemplateExistsError";}};async function ky(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 Ty(t,e){await e.adapter.getProject(t.projectId);let n=await ky(e.adapter,e.templatesFolderName),r=await e.adapter.listProjects({folderId:n}),o=t.templateName.toLowerCase();if(r.some(v=>v.name.toLowerCase()===o))throw new $o(t.templateName);let a=await e.adapter.getProject(t.projectId),i=await wn(e.adapter,t.projectId),{rootTasks:s,byParent:c}=lt(i),l=[],d=[`${a.name}:`];if(a.note)for(let v of a.note.split(`
138
+ `))d.push(` ${v}`);for(let v of s)Bt(v,c,1,d,l);let m=d.join(`
139
+ `),f={name:t.templateName,parameterNames:t.parameterNames??[],capturedAt:new Date().toISOString()},g=Ld(f,m),k=await e.adapter.createProject({name:t.templateName,folderId:n,note:g});return e.cache!==void 0&&B(e.cache,{projectId:k}),p({templateId:k,templateName:t.templateName,capturedAt:f.capturedAt,...l.length>0&&{exportWarnings:l}},e.makeMeta({syncPending:true}))}function el(t,e){return t.registerTool("project_template_save",{description:Wo,inputSchema:Iy.shape},async n=>{let r=await Ty(n,e);return u(r)})}var Ho='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 })',Go=z.object({id:y.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:ne(["active","on-hold"],{paused:"on-hold"},"Project status. Use project_complete or project_drop to close a project.").optional(),completionCriterion:ne(["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(w.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 vy(t,e){let{id:n,...r}=t,o=e.idempotencyStore??ge;return fe(o,t.idempotency_key,async()=>{let a=await e.adapter.getProject(n);Me(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})),l=async()=>(await e.adapter.updateProject(n,i),e.cache!==void 0&&B(e.cache,{projectId:n}),p({updated:true,id:n,name:s},e.makeMeta({syncPending:true,humanReadableSummary:yc(a.name)})));return Fe(t.dry_run,c,l)})}function tl(t,e){return t.registerTool("project_update",{description:Ho,inputSchema:Go.shape},async n=>{let r=await vy(n,e);return u(r)})}var zo="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 wy(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 ${me(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 ${me(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=>ce(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 nl(t,e){return t.registerTool("project_update_describe",{description:zo,inputSchema:Go.shape},async n=>{let r=await wy(n,e);return u(r)})}var qo="\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()); }\" })",Sy=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 jy(t,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??C).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 rl(t,e,n){return n.allowRawScript?t.registerTool("run_jxa_script",{description:qo,inputSchema:Sy.shape},async r=>{let o=await jy(r,e);return u(o)}):null}var Xo='\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" })',by=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 _y(t,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??C).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 ol(t,e,n){return n.allowRawScript?t.registerTool("run_omnijs_script",{description:Xo,inputSchema:by.shape},async r=>{let o=await _y(r,e);return u(o)}):null}var Py=[{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"}],jn=new Map(Py.flatMap(({aliases:t,weekday:e})=>t.map(n=>[n,e]))),Oy=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]]),xy=new Map([["first",1],["1st",1],["second",2],["2nd",2],["third",3],["3rd",3],["fourth",4],["4th",4],["last","last"],["final","last"]]);function Dy(t){return t.toLowerCase().replace(/\s+/g," ").trim()}function Qo(t){if(!t)return null;let e=Number.parseInt(t,10);if(Number.isFinite(e)&&e>=1)return e;let n=Oy.get(t);return n!==void 0?n:null}var al=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];function sl(t){return [...t].sort((e,n)=>al.indexOf(e)-al.indexOf(n))}function Ay(t){if(t.length===0)return "";if(t.length===7)return "every day";let e=sl(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 Cy(t){return t==="last"?"last":["first","second","third","fourth"][t-1]??String(t)}function Ry(t){return t==="start-again"?"after I complete it":t==="due-again"?"from the due date":""}function Yo(t,e){let n;if(t.weekdays&&t.weekdays.length>0)n=Ay(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 ${Cy(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=Ry(t.method);return [n,e,r].filter(a=>a.length>0).join(", ")}function My(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 Fy(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 Ey(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=Qo(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=Qo(r[1]);if(o!==null)return `${o} times`}return ""}function Ny(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=xy.get(e[1]??""),s=jn.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=Qo(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&&jn.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(l=>l.trim()).filter(l=>l.length>0),c=[];for(let l of s){let d=jn.get(l);d&&c.push(d);}if(c.length>0)return {rule:{unit:"weeks",steps:1,weekdays:Array.from(new Set(c))}}}return null}function il(t){if(!t||!t.trim())return {kind:"error",reason:"no-repetition-detected"};let e=Dy(t),n=My(e),r=n.consumed?e.replace(n.consumed," ").replace(/\s+/g," ").trim():e,o=Fy(r),a=Ey(r),i=Ny(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:sl(i.rule.weekdays)}:{},...i.rule.monthlyAnchor?{monthlyAnchor:i.rule.monthlyAnchor}:{}},c=r.match(/\bevery other\s+([a-z]+)\b/);if(c){let d=jn.get(c[1]??"");if(d){let m=[o,a].filter(Boolean).join(", ");return {kind:"ambiguous",interpretations:[{rule:s,description:Yo(s,m)},{rule:{method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:d,position:1}},description:Yo({method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:d,position:1}},m)}]}}}let l=[o,a].filter(Boolean).join(", ");return {kind:"ok",rule:s,normalizedDescription:Yo(s,l)}}var Zo=`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" })`,Uy=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 Ly(t,e){let n=il(t.prose),r=e.makeMeta();return p(n,r)}function cl(t,e){return t.registerTool("repetition_from_prose",{description:Zo,inputSchema:Uy.shape},async n=>{let r=await Ly(n,e);return u(r)})}var ea="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()",By=z.object({});async function $y(t,e){let n=await e.reviewService.listDue(),r=e.makeMeta({cacheHit:n.cacheHit});return p({projects:n.projects},r)}function dl(t,e){return t.registerTool("review_list_due",{description:ea,inputSchema:By.shape},async n=>{let r=await $y(n,e);return u(r)})}var ta=`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. Example: review_mark_reviewed({ id: "prj123" })`,Wy=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function Hy(t,e){return await e.reviewService.markReviewed(y.of(t.id)),p({id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:fn()}))}function pl(t,e){return t.registerTool("review_mark_reviewed",{description:ta,inputSchema:Wy.shape},async n=>{let r=await Hy(n,e);return u(r)})}var na='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. Example: project_mark_reviewed({ id: "prj123" })',Gy=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function zy(t,e){return await e.reviewService.markReviewed(y.of(t.id)),p({id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:fn()}))}function ml(t,e){return t.registerTool("project_mark_reviewed",{description:na,inputSchema:Gy.shape},async n=>{let r=await zy(n,e);return u(r)})}var oa=`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. Example: review_set_interval({ id: "prj123", days: 7 }) Example: review_set_interval({ id: "prj123", days: null })`,Vy=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 qy(t,e){return await e.reviewService.setInterval(y.of(t.id),t.days),p({id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Cc(t.days)}))}function fl(t,e){return t.registerTool("review_set_interval",{description:oa,inputSchema:Vy.shape},async n=>{let r=await qy(n,e);return u(r)})}var aa=`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 }. 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 })`,Ky=z.object({projectId:z.string().min(1).describe("Persistent ID of the project whose next review date should change."),nextReviewDate:z.union([ae(),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 Xy(t,e){return await e.reviewService.setNextReviewDate(y.of(t.projectId),t.nextReviewDate),p({id:t.projectId},e.makeMeta({syncPending:true,humanReadableSummary:Rc(t.nextReviewDate)}))}function gl(t,e){return t.registerTool("project_set_next_review_date",{description:aa,inputSchema:Ky.shape},async n=>{let r=await Xy(n,e);return u(r)})}var sa=`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" })`,Yy=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:y.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list."),tagIds:z.array(w.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 Qy(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 hl(t,e){return t.registerTool("search_query",{description:sa,inputSchema:Yy.shape},async n=>{let r=await Qy(n,e);return u(r)})}var ia="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()",eI=z.object({});async function tI(t,e){let n=await e.adapter.getLastSync();return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},e.makeMeta())}function yl(t,e){return t.registerTool("sync_status",{description:ia,inputSchema:eI.shape},async n=>{let r=await tI(n,e);return u(r)})}var ca="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()",rI=z.object({});async function oI(t,e){let n=await e.adapter.syncTrigger();e.cache!==void 0&&Wi(e.cache);let r=e.makeMeta({syncPending:false});return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},r)}function Il(t,e){return t.registerTool("sync_trigger",{description:ca,inputSchema:rI.shape},async n=>{let r=await oI(n,e);return u(r)})}var la=`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" })`,pa=z.object({name:z.string().min(1).describe("Tag name. Must be non-empty."),parentId:w.schema.optional().describe("Parent tag ID to nest under. Omit for a root tag. Get from tag_list."),status:ne(["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 aI(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:vc(t.name)});return p({tag:r},o)}function kl(t,e){return t.registerTool("tag_create",{description:la,inputSchema:pa.shape},async n=>{let r=await aI(n,e);return u(r)})}var ua="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 sI(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await ce(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 Tl(t,e){return t.registerTool("tag_create_describe",{description:ua,inputSchema:pa.shape},async n=>{let r=await sI(n,e);return u(r)})}var ma=`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" })`,fa=z.object({id:w.schema.describe("Persistent tag ID to delete. Get from tag_list.")});async function cI(t,e){return await e.tagService.delete(t.id),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Dc()}))}function vl(t,e){return t.registerTool("tag_delete",{description:ma,inputSchema:fa.shape},async n=>{let r=await cI(n,e);return u(r)})}var ga="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 dI(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 wl(t,e){return t.registerTool("tag_delete_describe",{description:ga,inputSchema:fa.shape},async n=>{let r=await dI(n,e);return u(r)})}var ha='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" })',pI=z.object({id:w.schema.describe("Persistent tag ID. Get from tag_list. IDs are stable across renames.")});async function uI(t,e){let n=await e.tagService.get(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({tag:n.tag},r)}function Sl(t,e){return t.registerTool("tag_get",{description:ha,inputSchema:pI.shape},async n=>{let r=await uI(n,e);return u(r)})}var ya='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" })',fI=z.object({id:w.schema.describe("Persistent tag ID. Get from tag_list.")});async function gI(t,e){let n=await e.tagService.getLocation(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({location:n.location},r)}function jl(t,e){return t.registerTool("tag_get_location",{description:ya,inputSchema:fI.shape},async n=>{let r=await gI(n,e);return u(r)})}var Ia='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"] })',_n=100,hI=z.object({ids:z.array(w.schema).min(0).max(_n).describe(`Array of tag IDs to fetch (0..${_n}). Get IDs from tag_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function yI(t,e){if(t.ids.length===0)return p({tags:[]},e.makeMeta());if(t.ids.length>_n)throw new I(`ids array exceeds the maximum batch size of ${_n} (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?[rt(o)]:void 0,i=e.makeMeta({...a!==void 0?{warnings:a}:{}});return p({tags:r},i)}function _l(t,e){return t.registerTool("tag_get_many",{description:Ia,inputSchema:hI.shape},async n=>{let r=await yI(n,e);return u(r)})}var ka='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" })',kI=z.object({parentId:w.schema.optional().describe("Return only direct children of this tag. Get the ID from a previous tag_list call. Omit for root tags."),status:ne(["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 TI(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 Pl(t,e){return t.registerTool("tag_list",{description:ka,inputSchema:kI.shape},async n=>{let r=await TI(n,e);return u(r)})}var Ta=`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 })`,va=z.object({id:w.schema.describe("Persistent ID of the tag to move. Get from tag_list."),parentId:w.schema.nullable().describe("New parent tag ID, or null to promote the tag to root level.")});async function wI(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:wc(n.name,"root")}))}function Ol(t,e){return t.registerTool("tag_move",{description:Ta,inputSchema:va.shape},async n=>{let r=await wI(n,e);return u(r)})}var wa="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 SI(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 ce(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 xl(t,e){return t.registerTool("tag_move_describe",{description:wa,inputSchema:va.shape},async n=>{let r=await SI(n,e);return u(r)})}var Sa='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 })',jI=z.object({id:w.schema.describe("Persistent tag ID. Get from tag_list."),allowsNextAction:z.boolean().describe("true to enable next-action selection; false to disable.")});async function bI(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:Ce(n.name)}))}function Al(t,e){return t.registerTool("tag_set_allows_next_action",{description:Sa,inputSchema:jI.shape},async n=>{let r=await bI(n,e);return u(r)})}var ja='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" })',_I=z.object({id:w.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 PI(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:Ce(n.name)}))}function Cl(t,e){return t.registerTool("tag_set_location",{description:ja,inputSchema:_I.shape},async n=>{let r=await PI(n,e);return u(r)})}var ba='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" })',xI=z.object({id:w.schema.describe("Persistent tag ID. Get from tag_list."),status:ne(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status for the tag.")});async function DI(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:Ce(n.name)}))}function Rl(t,e){return t.registerTool("tag_set_status",{description:ba,inputSchema:xI.shape},async n=>{let r=await DI(n,e);return u(r)})}var Pa='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" })',Oa=z.object({id:w.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:w.schema.nullable().optional().describe("New parent tag ID. Pass null to promote to root. Get from tag_list."),status:ne(["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 AI(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:Ce(o.name)}))}function Ml(t,e){return t.registerTool("tag_update",{description:Pa,inputSchema:Oa.shape},async n=>{let r=await AI(n,e);return u(r)})}var xa="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 CI(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 Fl(t,e){return t.registerTool("tag_update_describe",{description:xa,inputSchema:Oa.shape},async n=>{let r=await CI(n,e);return u(r)})}var Da=`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"] }] })`,RI=z.object({taskId:h.schema.describe("Persistent task ID."),projectId:y.schema.optional().describe("If set, move the task to this project before applying other changes."),addTagIds:z.array(w.schema).optional().describe("Tag IDs to add. Combined with removeTagIds via current-tagIds pre-read."),removeTagIds:z.array(w.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(),dueDate:z.string().datetime({offset:true}).nullable().optional(),flagged:z.boolean().optional()}).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"}),MI=z.object({assignments:z.array(RI).min(1).describe("Triage assignments \u2014 one per task. Must contain at least one item.")});function FI(t){return t.map((e,n)=>e.projectId!==void 0?n:-1).filter(e=>e>=0)}function EI(t){return t.map((e,n)=>e.addTagIds!==void 0||e.removeTagIds!==void 0?n:-1).filter(e=>e>=0)}function Aa(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=>w.of(o))}function NI(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=Aa(e??[],t.addTagIds,t.removeTagIds)),Object.keys(n).length>0?n:null}async function Ca(t,e){let n=t.assignments,r=n.map(T=>T.taskId),o=await e.adapter.getTasksMany(r),a=new Map;for(let T=0;T<r.length;T++){let D=o[T];D!=null&&a.set(r[T],D.name);}let i=EI(n),s=new Map;if(i.length>0){let T=i.map(P=>n[P].taskId),D=await e.adapter.getTasksMany(T);for(let P=0;P<i.length;P++){let U=D[P];U&&s.set(i[P],U.tagIds);}}let c=FI(n),l=c.length>0?await e.adapter.batchMoveTasks(c.map(T=>({id:n[T].taskId,destination:{projectId:n[T].projectId}}))):{succeeded:[],failed:[]},d=new Map;for(let T of l.succeeded){let D=c[T.index];d.set(D,"ok");}for(let T of l.failed){let D=c[T.index];d.set(D,{errorCode:T.errorCode,message:T.message});}let m=[];for(let T=0;T<n.length;T++){let D=n[T],P=d.get(T);if(P!==void 0&&P!=="ok")continue;let U=NI(D,s.get(T));U!==null&&m.push({origIdx:T,id:D.taskId,patch:U});}let f=m.length>0?await e.adapter.batchUpdateTasks(m.map(T=>({id:T.id,patch:T.patch}))):{succeeded:[],failed:[]},g=new Set(f.succeeded.map(T=>T.index)),k=new Map;for(let T of f.failed)k.set(T.index,{errorCode:T.errorCode,message:T.message});let v=[],S=[];for(let T=0;T<n.length;T++){let D=n[T],P=d.get(T);if(P!==void 0&&P!=="ok"){S.push({index:T,errorCode:`move:${P.errorCode}`,message:P.message});continue}let U=m.findIndex(b=>b.origIdx===T);if(U>=0){if(g.has(U))v.push({index:T,value:D.taskId});else if(k.has(U)){let b=k.get(U);S.push({index:T,errorCode:`update:${b.errorCode}`,message:b.message});}}else v.push({index:T,value:D.taskId});}if(e.cache!==void 0&&v.length>0)for(let T of v){let D=n[T.index];j(e.cache,{taskId:D.taskId,...D.projectId!==void 0&&{projectId:D.projectId}});}let $=v.map(T=>({index:T.index,value:{id:T.value,name:a.get(T.value)??""}}));return p({assigned:$,failed:S},e.makeMeta({syncPending:v.length>0}))}function El(t,e){return t.registerTool("task_batch_assign",{description:Da,inputSchema:MI.shape},async n=>{let r=await Ca(n,e);return u(r)})}var Ra='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" }] })',UI=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.")}),LI=z.object({items:z.array(UI).min(1).describe("Array of { id, at? } items. Must contain at least one item.")});async function JI(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 l=r[c];l!=null&&o.set(n[c],l.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 l=t.items[c.index];l!==void 0&&j(e.cache,{taskId:l.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:lc(i.succeeded.length)}))}function Nl(t,e){return t.registerTool("task_batch_complete",{description:Ra,inputSchema:LI.shape},async n=>{let r=await JI(n,e);return u(r)})}var Ma='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" }] })',BI=z.object({name:z.string().min(1).describe("Task name. Required, non-empty."),projectId:y.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(),flagged:z.boolean().optional(),dueDate:z.string().datetime({offset:true}).optional(),dueDateFloating:z.boolean().optional(),deferDate:z.string().datetime({offset:true}).optional(),deferDateFloating:z.boolean().optional(),estimatedMinutes:z.number().int().positive().optional(),tagIds:z.array(w.schema).optional(),sequential:z.boolean().optional(),completedByChildren:z.boolean().optional()}).refine(t=>!(t.projectId!==void 0&&t.parentTaskId!==void 0),{message:"Supply at most one of projectId or parentTaskId",path:["projectId"]}),Fa=z.object({items:z.array(BI).min(1).describe("Array of task inputs. Must contain at least one item.")});async function $I(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),j(e.cache,{projectId:c}));}a.size===0&&j(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:ot(r.succeeded.length)}))}function Ul(t,e){return t.registerTool("task_batch_create",{description:Ma,inputSchema:Fa.shape},async n=>{let r=await $I(n,e);return u(r)})}var Ea="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 WI(t,e){let n=[],r=[];for(let a of t.items){let i;a.projectId!==void 0?i=`project '${await st(e.adapter,a.projectId)}'`:a.parentTaskId!==void 0?i=`subtask of '${await it(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 Ll(t,e){return t.registerTool("task_batch_create_describe",{description:Ea,inputSchema:Fa.shape},async n=>{let r=await WI(n,e);return u(r)})}var Na='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" }] })',HI=z.object({id:h.schema.describe("Persistent task ID.")}),GI=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(HI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function zI(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&&j(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:uc(a.succeeded.length)}))}function Jl(t,e){return t.registerTool("task_batch_delete",{description:Na,inputSchema:GI.shape},async n=>{let r=await zI(n,e);return u(r)})}var La='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" }] })',VI=z.object({id:h.schema.describe("Persistent task ID.")}),qI=z.object({items:z.array(VI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function KI(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&&j(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:mc(a.succeeded.length)}))}function Bl(t,e){return t.registerTool("task_batch_drop",{description:La,inputSchema:qI.shape},async n=>{let r=await KI(n,e);return u(r)})}var Ja='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" }] })',XI=z.object({projectId:y.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"}),YI=z.object({id:h.schema.describe("Persistent task ID."),destination:XI.describe("Where to move the task. Provide projectId, parentId, or neither (inbox).")}),QI=z.object({items:z.array(YI).min(1).describe("Array of { id, destination } items. Must contain at least one item.")});async function ZI(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&&j(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:gc(a.succeeded.length,"destination")}))}function $l(t,e){return t.registerTool("task_batch_move",{description:Ja,inputSchema:QI.shape},async n=>{let r=await ZI(n,e);return u(r)})}var $a='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" }] })',ek=z.object({id:h.schema.describe("Persistent task ID.")}),tk=z.object({items:z.array(ek).min(1).describe("Array of { id } items. Must contain at least one item.")});async function nk(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&&j(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:pc(a.succeeded.length)}))}function Wl(t,e){return t.registerTool("task_batch_uncomplete",{description:$a,inputSchema:tk.shape},async n=>{let r=await nk(n,e);return u(r)})}var Ha='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" }] })',rk=z.object({id:h.schema.describe("Persistent task ID.")}),ok=z.object({items:z.array(rk).min(1).describe("Array of { id } items. Must contain at least one item.")});async function ak(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&&j(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:fc(a.succeeded.length)}))}function Hl(t,e){return t.registerTool("task_batch_undrop",{description:Ha,inputSchema:ok.shape},async n=>{let r=await ak(n,e);return u(r)})}var Ga=`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" } }] })`,sk=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(),dueDateFloating:z.boolean().optional(),deferDate:z.string().datetime({offset:true}).nullable().optional(),deferDateFloating:z.boolean().optional(),estimatedMinutes:z.number().int().positive().nullable().optional(),tagIds:z.array(w.schema).optional(),sequential:z.boolean().optional(),completedByChildren:z.boolean().optional()}).refine(t=>Object.keys(t).length>0,{message:"Patch must contain at least one field"}),ik=z.object({id:h.schema.describe("Persistent task ID."),patch:sk.describe("Fields to change. At least one field required.")}),za=z.object({items:z.array(ik).min(1).describe("Array of { id, patch } pairs. Must contain at least one item.")});async function ck(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 l=r[c];l!=null&&o.set(n[c],l.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 l=t.items[c.index];l!==void 0&&j(e.cache,{taskId:l.id});}let s=i.succeeded.map(c=>{let d=t.items[c.index]?.patch?.name??o.get(c.value)??"";return {index:c.index,value:{id:c.value,name:d}}});return p({updated:s,failed:i.failed},e.makeMeta({syncPending:i.succeeded.length>0,humanReadableSummary:dc(i.succeeded.length)}))}function Gl(t,e){return t.registerTool("task_batch_update",{description:Ga,inputSchema:za.shape},async n=>{let r=await ck(n,e);return u(r)})}var Va="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 dk(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,l]of Object.entries(a.patch))n.push({field:`${a.id}.${c}`,newValue:l===null?null:String(l)});}let o=`Would update ${t.items.length} task${t.items.length===1?"":"s"}: ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function zl(t,e){return t.registerTool("task_batch_update_describe",{description:Va,inputSchema:za.shape},async n=>{let r=await dk(n,e);return u(r)})}var qa='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" })',pk=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function uk(t,e){await e.adapter.clearTaskAlarms(t.id);let n=await e.adapter.getTask(t.id);e.cache!==void 0&&j(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:cc(n.name)});return p({task:n},r)}function Vl(t,e){return t.registerTool("task_clear_alarms",{description:qa,inputSchema:pk.shape},async n=>{let r=await uk(n,e);return u(r)})}var Ka='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" })',fk=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function gk(t,e){await e.adapter.updateTask(t.id,{repetition:null});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&j(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:sc(n.name)});return p({task:n},r)}function ql(t,e){return t.registerTool("task_clear_repetition",{description:Ka,inputSchema:fk.shape},async n=>{let r=await gk(n,e);return u(r)})}var Xa='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, 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" })',Ya=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 hk(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=t.at!==void 0?new Date(t.at):void 0;await e.adapter.completeTask(t.id,r),e.cache!==void 0&&j(e.cache,{taskId:t.id,projectId:n.projectId});let o;if(n.projectId!==null)try{if((await e.adapter.listTasks({projectId:n.projectId,completed:!1})).length===0){let i=await e.adapter.getProject(n.projectId);o=dt([wd(n.projectId,i.name)]);}}catch{}return p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Xi(n.name)}),void 0,o)}function Xl(t,e){return t.registerTool("task_complete",{description:Xa,inputSchema:Ya.shape},async n=>{let r=await hk(n,e);return u(r)})}var Qa="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 yk(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 Yl(t,e){return t.registerTool("task_complete_describe",{description:Qa,inputSchema:Ya.shape},async n=>{let r=await yk(n,e);return u(r)})}var Za=`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" })`,Ik=z.object({id:h.schema.describe("Persistent ID of the task to promote."),folderId:J.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 kk(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&&(j(e.cache,{taskId:t.id}),B(e.cache,{projectId:o})),p({converted:true,projectId:o,taskId:t.id,name:r.name},e.makeMeta({syncPending:true,humanReadableSummary:oc(r.name)}))}function Zl(t,e){return t.registerTool("task_convert_to_project",{description:Za,inputSchema:Ik.shape},async n=>{let r=await kk(n,e);return u(r)})}function Se(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 ep(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 Tk(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 vk(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 tp(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 np(t,e){let n=r=>e===void 0?void 0:ep(e,r);switch(t.code){case "invalid_type":{let r=t.expected;return [{field:Se(t.path),sent:n(t.path),expected:`${r}`}]}case "invalid_value":{let r=t.values;return [{field:Se(t.path),sent:n(t.path),expected:`one of: ${Tk(r)}`,...r.length>0&&{examples:r.slice(0,3)}}]}case "invalid_format":{let r=vk(t.format);return [{field:Se(t.path),sent:n(t.path),expected:r.expected,...r.examples&&{examples:r.examples}}]}case "too_small":return [{field:Se(t.path),sent:n(t.path),expected:tp("min",Number(t.minimum),t.inclusive??true,t.origin)}];case "too_big":return [{field:Se(t.path),sent:n(t.path),expected:tp("max",Number(t.maximum),t.inclusive??true,t.origin)}];case "unrecognized_keys":{let r=Se(t.path);return t.keys.map(o=>({field:r==="<root>"?o:`${r}.${o}`,sent:ep(e,[...t.path,o]),expected:"key is not part of the schema; remove it"}))}case "not_multiple_of":return [{field:Se(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(...np(i,e));}if(r.length===0)return [{field:Se(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:Se(t.path),sent:n(t.path),expected:o}]}default:return [{field:Se(t.path),sent:n(t.path),expected:t.message||`value satisfying schema constraint "${String(t.code)}"`}]}}function rp(t,e){let n=[];for(let r of t.issues)n.push(...np(r,e));return n}function Ie(t,e,n="Cross-field validation failed"){let r=t.safeParse(e);if(r.success)return r.data;let o=rp(r.error,e);throw new I(n,{details:{failures:o}})}var es='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" })',Dn=z.object({name:z.string().min(1).describe("Task name. Required, must be non-empty."),projectId:y.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(w.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.")}),wk=Dn.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 Sk(t,e){Ie(wk,t);let n=e.idempotencyStore??ge;return fe(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&&j(e.cache,{...t.projectId!==void 0&&{projectId:t.projectId}});let a=[Id(o,t.name),kd(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(Td(s.length));}catch{}let i=dt(a.filter(s=>s!=null));return p({id:o,name:t.name},e.makeMeta({syncPending:true,humanReadableSummary:Ki(t.name)}),void 0,i)})}function op(t,e){return t.registerTool("task_create",{description:es,inputSchema:Dn.shape},async n=>{let r=await Sk(n,e);return u(r)})}var ts="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 jk(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.projectId!==void 0){let a=await st(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 it(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 ${me(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${me(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=>ce(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 ap(t,e){return t.registerTool("task_create_describe",{description:ts,inputSchema:Dn.shape},async n=>{let r=await jk(n,e);return u(r)})}var ns='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 })',rs=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 bk(t,e){let n=e.idempotencyStore??ge;return fe(n,t.idempotency_key,async()=>{let r=await e.adapter.getTask(t.id);Me(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&&j(e.cache,{taskId:t.id,projectId:r.projectId}),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Qi(r.name)})));return Fe(t.dry_run,o,a)})}function sp(t,e){return t.registerTool("task_delete",{description:ns,inputSchema:rs.shape},async n=>{let r=await bk(n,e);return u(r)})}var os="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 _k(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 ip(t,e){return t.registerTool("task_delete_describe",{description:os,inputSchema:rs.shape},async n=>{let r=await _k(n,e);return u(r)})}var as='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" })',ss=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 Pk(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&&j(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Zi(n.name)}))}function dp(t,e){return t.registerTool("task_drop",{description:as,inputSchema:ss.shape},async n=>{let r=await Pk(n,e);return u(r)})}var is="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 Ok(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 lp(t,e){return t.registerTool("task_drop_describe",{description:is,inputSchema:ss.shape},async n=>{let r=await Ok(n,e);return u(r)})}var cs=`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" } })`,xk=z.union([z.object({projectId:y.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."),Dk=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:xk.optional()}).describe("Duplicate options. `destination` overrides the default same-container placement.");async function Ak(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 I("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&&(j(e.cache,{taskId:r,projectId:n.projectId}),t.destination!==void 0&&"projectId"in t.destination&&t.destination.projectId!==n.projectId&&j(e.cache,{projectId:t.destination.projectId})),p({duplicated:true,sourceId:t.id,newId:r,descendantCount:o,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:nc(n.name)}))}function pp(t,e){return t.registerTool("task_duplicate",{description:cs,inputSchema:Dk.shape},async n=>{let r=await Ak(n,e);return u(r)})}var mp=[".png",".jpg",".jpeg",".heic",".heif",".gif",".webp",".pdf"];function fp(t){return mp.includes(extname(t).toLowerCase())}function Rk(t){return t.mimeType!==null?t.mimeType.startsWith("image/")||t.mimeType==="application/pdf":fp(t.name)}var ds=`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 })`,up=z.object({name:z.string().min(1),note:z.string().optional(),deferDate:z.string().datetime({offset:true}).optional(),dueDate:z.string().datetime({offset:true}).optional()}),Mk=z.discriminatedUnion("kind",[z.object({kind:z.literal("path"),imagePath:z.string().min(1).describe("Absolute path; within attachment-path-scope + size cap.")}),z.object({kind:z.literal("attachment"),attachmentId:ve.schema,ownerTaskId:h.schema.optional(),ownerProjectId:y.schema.optional()})]).describe("Image source. attachment requires exactly one owner."),Fk=z.enum(["parent-task","each-task","none"]).default("parent-task").describe("Re-attachment mode after task creation."),gp=z.object({source:Mk,targetProjectId:y.schema,proposed:z.array(up).min(1).describe("Agent-supplied extraction."),attachSourceTo:Fk,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(up).optional().describe("Required when dryRun=false. (Possibly-edited) confirmed tasks.")}),Ek=gp.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"]});async function Nk(t,e){if(t.kind==="path"){if(!fp(t.imagePath))throw new I(`Unsupported image extension: ${t.imagePath}. Allowed: ${mp.join(",")}`,{details:{field:"source.imagePath"}});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 I(`Attachment not found: ${t.attachmentId}`,{details:{field:"source.attachmentId"}});if(!Rk(r))throw new I(`Attachment is not an image: ${r.name}`,{details:{field:"source.attachmentId"}});return {kind:"attachment",attachment:r}}function Uk(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 Lk(t,e){Ie(Ek,t);let n=await Nk(t.source,e);if(t.dryRun||!t.confirmation)return p({phase:"dryRun",proposed:t.proposed,sourceKind:n.kind},e.makeMeta());if(n.kind==="attachment"&&t.attachSourceTo!=="none")throw new I("attachment-mode source requires attachSourceTo='none' only in v1.",{details:{field:"attachSourceTo",attachSourceTo:t.attachSourceTo}});let r=t.confirmation,o=n.kind==="path"?n.imagePath:void 0,a,i={projectId:t.targetProjectId};if(t.attachSourceTo==="parent-task"){let d=t.parentTaskName??"Captured from image",m=await e.adapter.createTask({name:d,projectId:t.targetProjectId});o!==void 0&&await e.attachmentService.add({taskId:m,filePath:o}),a={taskId:m,name:d,...o!==void 0&&{attachedSourcePath:o}},i={parentId:m};}let s=await e.adapter.batchCreateTasks(r.map(d=>Uk(d,i))),c=[];for(let d of s.succeeded){let m=r[d.index],f;t.attachSourceTo==="each-task"&&o!==void 0&&(await e.attachmentService.add({taskId:d.value,filePath:o}),f=o),c.push({taskId:d.value,name:m?.name??"(unknown)",...f!==void 0&&{attachedSourcePath:f}});}e.cache!==void 0&&s.succeeded.length>0&&j(e.cache,{projectId:t.targetProjectId});let l=e.makeMeta({syncPending:s.succeeded.length>0});return p({phase:"created",parent:a,created:c,outcome:s},l)}function hp(t,e){return t.registerTool("task_extract_from_image",{description:ds,inputSchema:gp.shape},async n=>{let r=await Lk(n,e);return u(r)})}var Jk=["add","build","call","check","create","draft","email","file","finish","fix","follow up","plan","prepare","research","review","schedule","send","set up","start","write"],Bk=/^\s*(?:\(\d+\)|\d+[.)])\s+(.+)$/,$k=/^\s*(?:[-*•–—])\s+(.+)$/,Wk=/^\s*(?:✅|⏰|📝|📌|✏️|📞|📧|📤|📩|🔔|🚨|⚡️|➡️|→)\s+(.+)$/u;function Hk(t){return t.replace(/[\s.;,:]+$/,"").trim()}function Gk(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 yp(t,e={}){let n=e.verbs??Jk,r=Gk(n),o=[],a=[],i=t.replace(/\r\n?/g,`
140
+ `).split(`
141
+ `);for(let s=0;s<i.length;s++){let c=s+1,l=i[s]??"";if(!l.trim())continue;let d=Bk.exec(l),m=d?null:$k.exec(l),f=!d&&!m?Wk.exec(l):null,g=!d&&!m&&!f?r.exec(l):null,k=null;if(d)k=d[1]??null;else if(m)k=m[1]??null;else if(f)k=f[1]??null;else if(g){let S=g[1]??"",$=g[2]??"";k=S&&$?`${S} ${$}`:null;}if(k===null){a.push(`L${c}: ${l.trim()}`);continue}let v=Hk(k);if(!v){a.push(`L${c}: ${l.trim()}`);continue}o.push({name:v,sourceLines:[c]});}return {proposed:o,unmappedLines:a}}var ls=`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 })`,zk=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.")}),Vk=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:y.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."),Ip=z.object({source:Vk,targetProjectId:y.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(zk).optional().describe("Required when dryRun is false. The (possibly-edited) ProposedTask[] the agent has confirmed with the user.")}),qk=Ip.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation[] is required when dryRun is false",path:["confirmation"]});async function Kk(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 Xk(t,e){Ie(qk,t);let n=await Kk(t.source,e.adapter),r=yp(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&&j(e.cache,{projectId:t.targetProjectId});let i=e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:ot(a.succeeded.length)});return p({phase:"created",outcome:a},i)}function kp(t,e){return t.registerTool("task_extract_from_note",{description:ls,inputSchema:Ip.shape},async n=>{let r=await Xk(n,e);return u(r)})}var ps='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" })',Yk=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 Qk(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}}),l=c.slice(0,o),d=e.makeMeta();return p({tasks:l,matchCount:c.length},d)}function Tp(t,e){return t.registerTool("task_find_by_name",{description:ps,inputSchema:Yk.shape},async n=>{let r=await Qk(n,e);return u(r)})}var us=`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, projectId, tags }] } sorted by score descending; 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 })`,vp=5,wp=50,Zk=z.object({projectId:y.schema.optional(),tagId:w.schema.optional()}).refine(t=>!(t.projectId!==void 0&&t.tagId!==void 0),{message:"Supply at most one of projectId or tagId"}),eT=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:Zk.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(wp).default(vp).describe(`Top-K candidates to return. Default ${vp}, max ${wp}.`),includeCompleted:z.boolean().default(false).describe("When true, include completed and dropped tasks. Default false (open tasks only).")});async function tT(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=>{let s=wi(o,{name:i.name,note:i.note});return s===0?null:{taskId:String(i.id),name:i.name,score:s,projectId:i.projectId===null?null:String(i.projectId),tags:i.tagIds.map(String)}}).filter(i=>i!==null).sort((i,s)=>s.score-i.score).slice(0,t.limit);return p({candidates:a},e.makeMeta())}function Sp(t,e){return t.registerTool("task_find_similar",{description:us,inputSchema:eT.shape},async n=>{let r=await tT(n,e);return u(r)})}var ms='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" })',nT=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 rT(t,e){let n=await e.taskService.get(t),r=nt(n.task.note);return p({task:n.task,...n.subtasks!==void 0&&{subtasks:n.subtasks},...r!==void 0&&{waitingOn:r}},e.makeMeta({cacheHit:n.cacheHit}))}function bp(t,e){return t.registerTool("task_get",{description:ms,inputSchema:nT.shape},async n=>{let r=await rT(n,e);return u(r)})}var fs='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"] })',An=100,oT=z.object({ids:z.array(h.schema).min(0).max(An).describe(`Array of task IDs to fetch (0..${An}). 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 aT(t,e){if(t.ids.length===0)return p({tasks:[]},e.makeMeta());if(t.ids.length>An)throw new I(`ids array exceeds the maximum batch size of ${An} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTasksMany(t.ids),r=n.filter(l=>l!==null),o=t.ids.filter((l,d)=>n[d]===null),a={};for(let l of r){let d=nt(l.note);d!==void 0&&(a[l.id]=d);}let i=Object.keys(a).length>0,s=o.length>0?[rt(o)]:void 0,c=e.makeMeta({...s!==void 0?{warnings:s}:{}});return p({tasks:r,...i&&{waitingOn:a}},c)}function Pp(t,e){return t.registerTool("task_get_many",{description:fs,inputSchema:oT.shape},async n=>{let r=await aT(n,e);return u(r)})}var Op=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(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 gs(t){return {self:`omnifocus://project/${t.id}`,folder:t.folderId!==null?`omnifocus://folder/${t.folderId}`:null}}function gt(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 ht(t){let e=JSON.stringify(t);return Buffer.from(e,"utf8").toString("base64url")}function yt(t,e){let n;try{n=Buffer.from(t,"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(n);}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 It(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 Dp=z.enum(["dueDate","createdAt","modifiedAt","name"]),xp=200,hs=1e3,Cn=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=gt(r),a=e.cursor!==void 0?yt(e.cursor,o):void 0,i=this.cacheKeyFor(o,e.cursor),s=this.cache.has(i),{tasks:c,nextCursor:l}=await this.cache.wrap(i,async()=>this.fetchPage(e,r,a,n,o));return {tasks:c,nextCursor:l,hasMore:l!==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:Ue(i)};if(!n)return {task:s};let l=(await this.adapter.listTasks({parentId:e.id})).map(d=>({...d,_links:Ue(d)}));return {task:s,subtasks:l}}),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(P=>n.tagIds.every(U=>P.tagIds.includes(U))):s,{updatedSince:l}=n,d=l!==void 0?c.filter(P=>P.modifiedAt>l):c,{sortBy:m,sortDirection:f}=n,g=P=>{switch(m){case "dueDate":return P.dueDate??null;case "modifiedAt":return P.modifiedAt;case "name":return P.name;default:return P.createdAt}},k=[...d].sort((P,U)=>{let b=g(P),F=g(U);if(b===null&&F===null)return P.id<U.id?-1:1;if(b===null)return 1;if(F===null)return -1;if(b!==F){let z=b<F?-1:1;return f==="asc"?z:-z}return P.id<U.id?-1:1}),v=r!==void 0?k.filter(P=>It({id:P.id,sortValue:g(P)},r,f)):k,S=v.slice(0,o),T=v.length>o?this.encodeNextCursor(S,a,g):null;return {tasks:S.map(P=>({...P,_links:Ue(P)})),nextCursor:T}}resolveLimit(e){if(e.limit===void 0)return xp;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>hs)throw new I(`limit must be an integer between 1 and ${hs}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${hs}, or omit to use the default of ${xp}.`,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 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 n=e.tagIds!==void 0?[...new Set(e.tagIds)].sort((o,a)=>o.localeCompare(a)):[],r;if(e.updatedSince!==void 0)if(Dt(e.updatedSince))r=e.updatedSince;else if(Ot(e.updatedSince))r=xt(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: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 I("Internal: cannot encode cursor for empty page.");return ht({lastId:o.id,lastSortValue:r(o),filterHash:n})}};var ys='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" })',cT=z.object({projectId:y.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list. Omit for all projects."),tagIds:z.array(w.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:Dp.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:Pe().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 dT(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 Ap(t,e){return t.registerTool("task_list",{description:ys,inputSchema:cT.shape},async n=>{let r=await dT(n,e);return u(r)})}var ks='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" })',Ts=z.object({id:h.schema.describe("Persistent ID of the task to move."),projectId:y.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 lT(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 I("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:Is(r.projectId,r.parentId)},e.makeMeta());let s=Is(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&&(j(e.cache,{taskId:t.id,projectId:r.projectId}),t.projectId!==void 0&&t.projectId!==r.projectId&&j(e.cache,{projectId:t.projectId}));let l=t.toInbox===true?{inbox:true}:Is(t.projectId??null,t.parentId??null);return p({moved:true,id:t.id,from:s,to:l},e.makeMeta({syncPending:true,humanReadableSummary:tc(r.name,t.toInbox===true?"inbox":t.projectId!=null?"project":"parent task")}))}function Is(t,e){return e!==null?{parentId:e}:t!==null?{projectId:t}:{inbox:true}}function Rp(t,e){return t.registerTool("task_move",{description:ks,inputSchema:Ts.shape},async n=>{let r=await lT(n,e);return u(r)})}var vs="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 pT(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 st(e.adapter,t.projectId);n.push({field:"projectId",newValue:t.projectId}),o=`project '${i}'`;}else if(t.parentId!==void 0){let i=await it(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 Mp(t,e){return t.registerTool("task_move_describe",{description:vs,inputSchema:Ts.shape},async n=>{let r=await pT(n,e);return u(r)})}function ws(t){let e=(g,k=2)=>String(g).padStart(k,"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(),l=c>=0?"+":"-",d=Math.abs(c),m=e(Math.floor(d/60)),f=e(d%60);return `${n}-${r}-${o}T${a}:${i}:${s}${l}${m}:${f}`}function Fp(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:ws(i)}}if(r==="tomorrow"){let i=new Date(o.getFullYear(),o.getMonth(),o.getDate()+1,0,0,0);return {value:ws(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]),l=Number(s[1]),d=Number(s[2]),m=new Date(c,l-1,d,0,0,0);return {value:ws(m)}}return {value:t}}}return {value:t,warning:`Line ${e}: ${n} date '${t}' is not a recognized date format; passing through as-is`}}function Ep(t){let e=t.split(`
142
+ `),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,l=[],d,m,f=false,g,k=c.indexOf("//");k!==-1&&(g=c.slice(k+2).trim(),c=c.slice(0,k).trim());let v=c.split(/\s+/).filter(D=>D.length>0),S=[];for(let D of v)if(D==="!!")f=true;else if(D.startsWith("::")){let P=D.slice(2);if(P.length>0){let{value:U,warning:b}=Fp(P,i,"Defer");m=U,b&&r.push(b);}}else if(D.startsWith("@")){let P=D.slice(1);P.length>0&&l.push(P);}else if(D.startsWith("#")){let P=D.slice(1);if(P.length>0){let{value:U,warning:b}=Fp(P,i,"Due");d=U,b&&r.push(b);}}else S.push(D);let $=S.join(" ").trim();if($===""){r.push(`Line ${i}: task line has no name after removing tokens; skipping`);continue}let T={name:$};g!==void 0&&g.length>0&&(T.note=g),f&&(T.flagged=true),d!==void 0&&(T.dueDate=d),m!==void 0&&(T.deferDate=m),l.length>0&&(T.tagNames=l),o!==void 0&&(T.projectName=o),n.push(T);}return {tasks:n,warnings:r}}var Ss=`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" })`,uT=z.object({text:z.string().min(1).describe("Transport text to parse. One task per line; 'Project: Name' prefix sets project context.")});async function mT(t,e){let n=Ep(t.text),r=e.makeMeta();return p({tasks:n.tasks,count:n.tasks.length,...n.warnings.length>0&&{warnings:n.warnings}},r)}function Up(t,e){return t.registerTool("task_parse_transport_text",{description:Ss,inputSchema:uT.shape},async n=>{let r=await mT(n,e);return u(r)})}function zt(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=>zt(n,e));case "or":return t.predicates.some(n=>zt(n,e));case "not":return !zt(t.predicate,e)}}var bs=`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" })`,js=200,Rn=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:w.schema.describe("Match tasks carrying this tag.")}),z.object({kind:z.literal("project"),projectId:y.schema.describe("Match tasks in this project.")}),z.object({kind:z.literal("and"),predicates:z.array(Rn).describe("All children must match.")}),z.object({kind:z.literal("or"),predicates:z.array(Rn).describe("Any child match suffices.")}),z.object({kind:z.literal("not"),predicate:Rn.describe("Inverts the inner predicate's result.")})])),fT=z.object({addTags:z.array(w.schema).optional().describe("Tag IDs to add to every match."),removeTags:z.array(w.schema).optional().describe("Tag IDs to remove from every match."),setProject:y.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"}),Lp=z.object({predicate:Rn.describe("AST for selecting tasks. Composable via and/or/not. Always evaluated against open (non-completed, non-dropped) tasks."),changes:fT.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.')}),gT=Lp.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation is required when dryRun is false",path:["confirmation"]});function hT(t,e){let n=e.setProject!==void 0?String(e.setProject):t.projectId===null?null:String(t.projectId),r=Aa(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 yT(t){return {projectId:t.projectId===null?null:String(t.projectId),tagIds:t.tagIds.map(String),flagged:t.flagged}}async function IT(t,e){Ie(gT,t);let r=(await e.adapter.listTasks({completed:false})).filter(s=>zt(t.predicate,s));if(t.dryRun){let s=r.map(c=>({taskId:String(c.id),name:c.name,before:yT(c),after:hT(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>js)return p({phase:"over-cap",matched:r.length,cap:js,message:`${r.length} matches exceeds the ${js}-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 Ca({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 Jp(t,e){return t.registerTool("task_reclassify",{description:bs,inputSchema:Lp.shape},async n=>{let r=await IT(n,e);return u(r)})}var _s='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" } })',kT=z.union([z.object({projectId:y.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."),TT=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:kT.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 vT(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 I("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 I("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 I("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 I("task_reorder: no positioning form matched",{details:{field:"before|after|at"}});return await e.adapter.reorderTask(t.id,a),e.cache!==void 0&&(j(e.cache,{taskId:t.id,projectId:o}),t.at!==void 0&&t.in!==void 0&&"projectId"in t.in&&t.in.projectId!==o&&j(e.cache,{projectId:t.in.projectId})),p({reordered:true,id:t.id,position:a},e.makeMeta({syncPending:true,humanReadableSummary:rc(r.name)}))}function Bp(t,e){return t.registerTool("task_reorder",{description:_s,inputSchema:TT.shape},async n=>{let r=await vT(n,e);return u(r)})}var Ps='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" })',$p={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:y.schema.optional().describe("Restrict search to tasks within this project."),tagIds:z.array(w.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:Pe().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset or relative shortcut."),dueAfter:Pe().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.")},wT=z.object($p).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 ST(t,e){Ie(wT,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 Wp(t,e){return t.registerTool("task_search",{description:Ps,inputSchema:$p},async n=>{let r=await ST(n,e);return u(r)})}var Hp=z.enum(["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]),jT=z.union([z.object({day:z.number().int().min(1).max(31)}),z.object({weekday:Hp,position:z.union([z.literal(1),z.literal(2),z.literal(3),z.literal(4),z.literal("last")])})]),Os=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:ae()})]),xs=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(Hp).optional(),monthlyAnchor:jT.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:y.schema.nullable(),parentId:h.schema.nullable(),tagIds:z.array(w.schema),deferDate:ae().nullable(),deferDateFloating:z.boolean().optional(),dueDate:ae().nullable(),dueDateFloating:z.boolean().optional(),estimatedMinutes:z.number().int().min(1).nullable(),flagged:z.boolean(),completed:z.boolean(),completedAt:ae().nullable(),dropped:z.boolean(),droppedAt:ae().nullable(),available:z.boolean(),blocked:z.boolean(),sequential:z.boolean(),completedByChildren:z.boolean(),repetition:xs.nullable(),notifications:z.array(Os).optional(),createdAt:ae(),modifiedAt:ae(),_links:Op.optional()});var Ds=`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" }] })`,bT=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),alarms:z.array(Os).describe("Full replacement set of alarms. Empty array is permitted and equivalent to task_clear_alarms.")});async function _T(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 I("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 I("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&&j(e.cache,{taskId:t.id,projectId:r.projectId});let o=e.makeMeta({syncPending:true,humanReadableSummary:ic(n.name,t.alarms.length)});return p({task:r},o)}function zp(t,e){return t.registerTool("task_set_alarms",{description:Ds,inputSchema:bT.shape},async n=>{let r=await _T(n,e);return u(r)})}var As='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 } })',OT=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),rule:xs.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 xT(t,e){await e.adapter.updateTask(t.id,{repetition:t.rule});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&j(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:ac(n.name)});return p({task:n},r)}function Vp(t,e){return t.registerTool("task_set_repetition",{description:As,inputSchema:OT.shape},async n=>{let r=await xT(n,e);return u(r)})}var Cs='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" })',AT=z.object({id:h.schema.describe("Persistent task ID.")});async function CT(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&&j(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Yi(n.name)})))}function qp(t,e){return t.registerTool("task_uncomplete",{description:Cs,inputSchema:AT.shape},async n=>{let r=await CT(n,e);return u(r)})}var Rs='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" })',MT=z.object({id:h.schema.describe("Persistent task ID.")});async function FT(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&&j(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:ec(n.name)})))}function Kp(t,e){return t.registerTool("task_undrop",{description:Rs,inputSchema:MT.shape},async n=>{let r=await FT(n,e);return u(r)})}var Ms='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"] })',Mn=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(w.schema).optional().describe("Full-replacement tag list. Replaces all existing tags. Mutually exclusive with addTags/removeTags."),addTags:z.array(w.schema).optional().describe("Tags to add. No-op for tags the task already has. Mutually exclusive with tagIds."),removeTags:z.array(w.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.")}),ET=Mn.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 NT(t,e){Ie(ET,t);let{id:n,addTags:r,removeTags:o,setFlagged:a,tagIds:i,...s}=t,c=e.idempotencyStore??ge;return fe(c,t.idempotency_key,async()=>{let l=await e.adapter.getTask(n);Me(t.expectedModifiedAt,l.modifiedAt,`task:${n}`);let d;if(r!==void 0||o!==void 0){let v=new Set(l.tagIds);for(let S of r??[])v.add(S);for(let S of o??[])v.delete(S);d=[...v];}else i!==void 0&&(d=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}:{},...d!==void 0?{tagIds:d}:{}},g=()=>{let v={...l,...f};return p({task:v},e.makeMeta({syncPending:false}))},k=async()=>{await e.adapter.updateTask(n,f);let v=await e.adapter.getTask(n);return e.cache!==void 0&&j(e.cache,{taskId:n,projectId:v.projectId}),p({task:v},e.makeMeta({syncPending:true}))};return Fe(t.dry_run,g,k)})}function Xp(t,e){return t.registerTool("task_update",{description:Ms,inputSchema:Mn.shape},async n=>{let r=await NT(n,e);return u(r)})}var Fs="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 UT(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 ${me(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 ${me(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(l=>ce(e.adapter,l)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`set tags to [${c.map(l=>`'${l}'`).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(l=>ce(e.adapter,l)));n.push({field:"addTags",newValue:t.addTags.join(",")}),r.push(`add tags [${c.map(l=>`'${l}'`).join(", ")}]`);}if(t.removeTags!==void 0&&t.removeTags.length>0){let c=await Promise.all(t.removeTags.map(l=>ce(e.adapter,l)));n.push({field:"removeTags",newValue:t.removeTags.join(",")}),r.push(`remove tags [${c.map(l=>`'${l}'`).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 Yp(t,e){return t.registerTool("task_update_describe",{description:Fs,inputSchema:Mn.shape},async n=>{let r=await UT(n,e);return u(r)})}var Es='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" }',Ns='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" }',LT=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.")}),JT=z.object({taskId:h.schema.describe("Persistent task ID.")});async function BT(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 $T(t,e){let n=await e.adapter.getTask(t.taskId),r=fr.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=Ri(n.note,r),a=await BT(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&&j(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,waitingOn:r},e.makeMeta({syncPending:true}))}function Qp(t,e){return t.registerTool("task_set_waiting_on",{description:Es,inputSchema:LT.shape},async n=>{let r=await $T(n,e);return u(r)})}async function WT(t,e){let n=await e.adapter.getTask(t.taskId),r=Mi(n.note),o=await e.adapter.listTags(),a=e.waitingTagName.toLowerCase(),i=o.find(d=>d.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 l={};return c&&(l.note=r),s&&i!==void 0&&(l.tagIds=n.tagIds.filter(d=>d!==i)),await e.adapter.updateTask(t.taskId,l),e.cache!==void 0&&j(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,cleared:true},e.makeMeta({syncPending:true}))}function Zp(t,e){return t.registerTool("task_clear_waiting_on",{description:Ns,inputSchema:JT.shape},async n=>{let r=await WT(n,e);return u(r)})}var Us="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()",HT=z.object({});async function GT(t,e){let n=await e.adapter.getWindowState();return p(n,e.makeMeta())}function eu(t,e){return t.registerTool("window_get_state",{description:Us,inputSchema:HT.shape},async n=>u(await GT(n,e)))}var Ls=`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" })`,zT=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 VT(t,e){let n=await e.adapter.setWindowPerspective(t.perspectiveName);return p(n,e.makeMeta())}function tu(t,e){return t.registerTool("window_set_perspective",{description:Ls,inputSchema:zT.shape},async n=>u(await VT(n,e)))}var Js="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 })",qT=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 KT(t,e){let n=await e.adapter.setWindowFocus(t.containerId);return p(n,e.makeMeta())}function nu(t,e){return t.registerTool("window_set_focus",{description:Js,inputSchema:qT.shape},async n=>u(await KT(n,e)))}var Bs="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()",XT=z.object({});async function YT(t,e){let n=await e.adapter.appWindowNew();return p(n,e.makeMeta())}function ru(t,e){return t.registerTool("app_window_new",{description:Bs,inputSchema:XT.shape},async n=>u(await YT(n,e)))}var $s="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()",QT=z.object({});async function ZT(t,e){let n=await e.adapter.appWindowNewTab();return p(n,e.makeMeta())}function ou(t,e){return t.registerTool("app_window_new_tab",{description:$s,inputSchema:QT.shape},async n=>u(await ZT(n,e)))}var au={app_launch:Tr,attachment_add:Sr,attachment_list:wr,attachment_remove:jr,attachment_save_to_path:br,database_redo:_r,database_undo:Pr,export_opml:xr,export_taskpaper:Cr,forecast_get:qr,forecast_get_tag:Kr,forecast_pack:Xr,forecast_set_tag:Qr,folder_create:Mr,folder_create_describe:Er,folder_delete:Nr,folder_delete_describe:Lr,folder_get:Jr,folder_list:Br,folder_move:$r,folder_move_describe:Hr,folder_update:Gr,folder_update_describe:Vr,import_opml:Ar,import_taskpaper:Rr,internal_status:so,note_append:Zr,note_get:to,note_get_html:ro,note_set:oo,note_set_html:ao,perspective_delete:io,perspective_evaluate:co,perspective_get:lo,perspective_list:po,plugin_invoke:mo,project_batch_complete:go,project_batch_drop:yo,project_complete:Io,project_complete_describe:To,project_create:So,project_create_describe:bo,project_delete:_o,project_delete_describe:Oo,project_drop:xo,project_drop_describe:Ao,project_get:Co,project_get_many:Ro,project_list:Mo,project_mark_reviewed:na,project_move:Fo,project_move_describe:No,project_template_instantiate:Jo,project_template_list:Bo,project_template_save:Wo,project_update:Ho,repetition_from_prose:Zo,project_update_describe:zo,review_list_due:ea,review_mark_reviewed:ta,project_set_next_review_date:aa,review_set_interval:oa,run_jxa_script:qo,run_omnijs_script:Xo,search_query:sa,sync_status:ia,sync_trigger:ca,tag_create:la,tag_create_describe:ua,tag_delete:ma,tag_delete_describe:ga,tag_get:ha,tag_get_many:Ia,tag_get_location:ya,tag_list:ka,tag_move:Ta,tag_move_describe:wa,tag_set_allows_next_action:Sa,tag_set_location:ja,tag_set_status:ba,tag_update:Pa,task_batch_assign:Da,tag_update_describe:xa,task_batch_complete:Ra,task_batch_create:Ma,task_batch_create_describe:Ea,task_batch_delete:Na,task_batch_drop:La,task_batch_move:Ja,task_batch_uncomplete:$a,task_batch_undrop:Ha,task_batch_update:Ga,task_batch_update_describe:Va,task_complete:Xa,task_complete_describe:Qa,task_create:es,task_create_describe:ts,task_drop:as,task_drop_describe:is,task_clear_alarms:qa,task_clear_repetition:Ka,task_delete:ns,task_delete_describe:os,task_duplicate:cs,task_extract_from_image:ds,task_extract_from_note:ls,task_find_by_name:ps,task_find_similar:us,task_get:ms,task_get_many:fs,task_list:ys,task_convert_to_project:Za,task_move:ks,task_move_describe:vs,task_search:Ps,task_parse_transport_text:Ss,task_reclassify:bs,task_reorder:_s,task_clear_waiting_on:Ns,task_set_alarms:Ds,task_set_repetition:As,task_set_waiting_on:Es,task_uncomplete:Cs,task_undrop:Rs,task_update:Ms,app_window_new:Bs,app_window_new_tab:$s,window_get_state:Us,window_set_focus:Js,window_set_perspective:Ls,task_update_describe:Fs};var ov=Ws.join(tv.homedir(),"Library","Application Support","OmniFocus","OmniFocus.ofocus");function av(){let t=fileURLToPath(import.meta.url);return Ws.resolve(Ws.dirname(t),"../../bin/omnifocus-watcher")}var Fn=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??ov,this.binaryPath=n.binaryPath!==void 0?n.binaryPath:av();}start(){if(this.started)return;if(!Vt.existsSync(this.dbPath)){C.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,C.debug({event:"database.watcher.stopped"});}tryStartSwift(){if(this.binaryPath===null)return false;try{Vt.accessSync(this.binaryPath,Vt.constants.X_OK);}catch{return C.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=>{C.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&&(C.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=>{C.warn({event:"database.watcher.swift_error",err:n}),this.swiftProcess=null,this.startNodeWatcher();}),C.debug({event:"database.watcher.swift_started",dbPath:this.dbPath}),!0}catch(e){return C.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){C.debug({event:"database.watcher.swift_parse_error",line:e,err:n});}}startNodeWatcher(){if(Vt.existsSync(this.dbPath))try{this.nodeWatcher=Vt.watch(this.dbPath,{persistent:!1},(e,n)=>{this.scheduleNotify({source:"node",ts:new Date().toISOString()});}),this.nodeWatcher.on("error",e=>{C.warn({event:"database.watcher.node_error",err:e}),this.stop();}),C.debug({event:"database.watcher.node_started",dbPath:this.dbPath});}catch(e){C.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=[],C.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 Hs=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 wt(`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 wt(`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,C.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,C.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,C.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)}},Gs=class{_breakers=new Map;_defaults;constructor(e={}){this._defaults=e;}get(e){let n=this._breakers.get(e);return n||(n=new Hs(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}))}},zs=new Gs;var En=["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 su=z.enum(["all","any","none"]),sv=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()}),Vs=z.lazy(()=>z.union([z.object({aggregateType:su,aggregateRules:z.array(Vs)}),z.object({disabledRule:Vs}),sv])),iv=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:su,rules:z.array(Vs),iconColor:iv.nullable()});function cv(t){return t instanceof Error?{errorCode:t.code??"OF_UNKNOWN",message:t.message}:{errorCode:"OF_UNKNOWN",message:String(t)}}async function je(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,...cv(i)});}}return {succeeded:n,failed:r}}function V(t){return t.toISOString()}var Nn=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 O(`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 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 O(`Project not found: ${e.projectId}`,{details:{resource:"project",id:e.projectId}});if(e.parentId!==void 0&&!this.tasks.has(e.parentId))throw new O(`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 O(`Tag not found: ${a}`,{details:{resource:"tag",id:a}});let n=this.nextId("task",h),r=V(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 I("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 O(`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:V(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?V(r??this.now()):null;this.tasks.set(e,{...o,completed:n,completedAt:a,modifiedAt:a??V(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?V(r??this.now()):null;this.tasks.set(e,{...o,dropped:n,droppedAt:a,modifiedAt:a??V(this.now())});}async batchCreateTasks(e){return je(e,n=>this.createTask(n))}async batchUpdateTasks(e){return je(e,async({id:n,patch:r})=>(await this.updateTask(n,r),n))}async batchCompleteTasks(e){return je(e,async({id:n,at:r})=>(await this.completeTask(n,r),n))}async batchUncompleteTasks(e){return je(e,async({id:n})=>(await this.uncompleteTask(n),n))}async batchDeleteTasks(e){return je(e,async({id:n})=>(await this.deleteTask(n),n))}async batchDropTasks(e){return je(e,async({id:n})=>(await this.dropTask(n),n))}async batchUndropTasks(e){return je(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 I("moveTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(n.projectId!==void 0&&!this.projects.has(n.projectId))throw new O(`Project not found: ${n.projectId}`,{details:{resource:"project",id:n.projectId}});if(n.parentId!==void 0&&!this.tasks.has(n.parentId))throw new O(`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:V(this.now())}),this.adjustProjectCountsForTask(o,r,1);}async convertTaskToProject(e,n){let r=await this.getTask(e),o=y.of(e),a=V(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 je(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 d=n.destination;if(("projectId"in d?1:0)+("parentId"in d?1:0)+("toInbox"in d&&d.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 d){if(!this.projects.has(d.projectId))throw new O(`Project not found: ${d.projectId}`,{details:{resource:"project",id:d.projectId}});o=d.projectId,a=null;}else if("parentId"in d){let f=this.tasks.get(d.parentId);if(f===void 0)throw new O(`Parent task not found: ${d.parentId}`,{details:{resource:"task",id:d.parentId}});o=f.projectId,a=d.parentId;}else o=null,a=null;}let i=d=>Array.from(this.tasks.values()).filter(m=>m.parentId===d),s=(d,m,f)=>{let g=this.nextId("task",h),k=V(this.now()),v={id:g,name:d.name,note:d.note,noteHtml:d.noteHtml,projectId:m,parentId:f,tagIds:[...d.tagIds],deferDate:d.deferDate,dueDate:d.dueDate,estimatedMinutes:d.estimatedMinutes,flagged:d.flagged,completed:false,completedAt:null,dropped:false,droppedAt:null,available:true,blocked:false,sequential:d.sequential,completedByChildren:d.completedByChildren,repetition:d.repetition,createdAt:k,modifiedAt:k};return this.tasks.set(g,v),this.bumpProjectTaskCount(m,1),g},c=s(r,o,a),l=0;if(n.recursive){let d=(m,f,g)=>{for(let k of i(m)){let v=s(k,g,f);l+=1,d(k.id,v,g);}};d(r.id,c,o);}return {newId:c,descendantCount:l}}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 l={...r,projectId:o,parentId:a,modifiedAt:V(this.now())},d=[];for(let[g,k]of this.tasks)g!==e&&d.push([g,k]);let m=g=>g.projectId===o&&g.parentId===a,f;if(i==="start")f=d.findIndex(([,g])=>m(g)),f===-1&&(f=d.length);else if(i==="end"){let g=-1;d.forEach(([,k],v)=>{m(k)&&(g=v);}),f=g===-1?d.length:g+1;}else {let g=d.findIndex(([k])=>k===s);f=i==="before"?g:g+1;}this.tasks.clear(),d.forEach(([g,k],v)=>{v===f&&this.tasks.set(e,l),this.tasks.set(g,k);}),f>=d.length&&this.tasks.set(e,l);}resolveReorderDestination(e,n,r){if("before"in r||"after"in r){let c="before"in r?r.before:r.after,l=this.tasks.get(c);if(l===void 0)throw new O(`Reference task not found: ${c}`,{details:{resource:"task",id:c}});if(c===e)throw new I("reorderTask: reference must differ from the task id",{details:{field:"position"}});if(l.projectId!==n.projectId||l.parentId!==n.parentId)throw new I("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 O(`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 O(`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"}});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 O(`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 I("Project name must be non-empty",{details:{field:"name"}});if(e.folderId!==void 0&&!this.folders.has(e.folderId))throw new O(`Folder not found: ${e.folderId}`,{details:{resource:"folder",id:e.folderId}});let n=this.nextId("proj",y),r=V(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 I("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:V(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=V(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=V(n??this.now());this.projects.set(e,{...r,status:"dropped",dropped:true,droppedAt:o,modifiedAt:o});}async batchCompleteProjects(e){return je(e,async({id:n})=>(await this.completeProject(n),n))}async batchDropProjects(e){return je(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 O(`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:V(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=V(r),a=null;if(n.reviewIntervalDays!==null){let i=new Date(r);i.setUTCDate(i.getUTCDate()+n.reviewIntervalDays),a=V(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 O(`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 I("Tag name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.tags.has(e.parentId))throw new O(`Parent tag not found: ${e.parentId}`,{details:{resource:"tag",id:e.parentId}});let n=this.nextId("tag",w),r=V(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 I("Tag name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.tags.has(n.parentId))throw new O(`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:V(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 O(`Folder not found: ${e}`,{details:{resource:"folder",id:e}});return n}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 O(`Parent folder not found: ${e.parentId}`,{details:{resource:"folder",id:e.parentId}});let n=this.nextId("fold",J),r=V(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 I("Folder name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.folders.has(n.parentId))throw new O(`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:V(this.now())});}async deleteFolder(e){let n=await this.getFolder(e);if(n.projectCount>0||n.subfolderCount>0)throw new I(`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=V(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 O(`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 O(`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 En.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 O(`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}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 O(`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 O(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}})}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):[],l=n.filter(f=>f.dueDate!==null&&f.dueDate>=r&&f.dueDate<=o),d=i?n.filter(f=>f.deferDate!==null&&f.deferDate>=r&&f.deferDate<=o):[],m=s?n.filter(f=>f.flagged):[];return {overdue:c,dueToday:l,deferredToday:d,flagged:m}}async getForecastTag(){return {tagId:this.forecastTagId}}async setForecastTag(e){if(e!==null&&!this.tags.has(e))throw new O(`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 O(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new O(`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 O(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new O(`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",ve),o=e.filePath.split("/").pop()??"attachment",a={id:r,name:o,mimeType:null,sizeBytes:null,addedAt:V(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 O(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new O(`Project not found: ${e.projectId}`);let n=this.ownerKey(e),r=this.attachments.get(n);if(!r?.has(e.attachmentId))throw new O(`Attachment not found: ${e.attachmentId}`);r.delete(e.attachmentId);}async saveAttachmentToPath(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new O(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new O(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);if(!this.attachments.get(n)?.has(e.attachmentId))throw new O(`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 O(`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 O("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 re(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"&&t.error!==null}function Te(t,e){return {succeeded:t.succeeded.map(n=>({index:n.index,value:e(n.value)})),failed:t.failed}}var iu=`/**
71
143
  * app_launch.js \u2014 launch OmniFocus explicitly (never automatic).
72
144
  *
73
145
  * Per SPEC resolved-decisions: automatic launch is out of scope \u2014 agents must
@@ -95,7 +167,7 @@ function run(_argv) {
95
167
 
96
168
  return JSON.stringify({ launched: !alreadyRunning, alreadyRunning });
97
169
  }
98
- `;var _s=`/**
170
+ `;var cu=`/**
99
171
  * JXA: add an attachment to a task or project from a local file path.
100
172
  *
101
173
  * Args (argv[0] JSON): { taskId?: string, projectId?: string, filePath: string }
@@ -143,7 +215,7 @@ function run(argv) {
143
215
  const newAtt = atts[atts.length - 1];
144
216
  return JSON.stringify({ id: newAtt.id() });
145
217
  }
146
- `;var xs=`/**
218
+ `;var du=`/**
147
219
  * JXA: list all attachments on a task or project.
148
220
  *
149
221
  * Args (argv[0] JSON): { taskId?: string, projectId?: string }
@@ -229,7 +301,7 @@ function run(argv) {
229
301
 
230
302
  return JSON.stringify({ attachments: result });
231
303
  }
232
- `;var As=`/**
304
+ `;var lu=`/**
233
305
  * JXA: remove an attachment by ID from a task or project.
234
306
  *
235
307
  * Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string }
@@ -281,7 +353,7 @@ function run(argv) {
281
353
 
282
354
  return JSON.stringify({});
283
355
  }
284
- `;var Ds=`/**
356
+ `;var pu=`/**
285
357
  * JXA: copy an attachment's content to a local file path.
286
358
  *
287
359
  * Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string, destPath: string }
@@ -380,7 +452,7 @@ function run(argv) {
380
452
 
381
453
  return JSON.stringify({ saved: true, path: destPath, sizeBytes: sizeBytes });
382
454
  }
383
- `;var Cs=`/**
455
+ `;var uu=`/**
384
456
  * JXA: return task and project IDs modified since a given timestamp.
385
457
  *
386
458
  * This script powers the richer change-semantics layer: when the
@@ -454,7 +526,7 @@ function run(argv) {
454
526
 
455
527
  return JSON.stringify({ tasks, projects });
456
528
  }
457
- `;var Rs=`/**
529
+ `;var mu=`/**
458
530
  * JXA: create a folder, optionally under a parent folder.
459
531
  *
460
532
  * Args (argv[0] JSON): { name: string, parentId?: string }
@@ -519,7 +591,7 @@ function run(argv) {
519
591
 
520
592
  return JSON.stringify({ folder: buildFolder(newFolder) });
521
593
  }
522
- `;var Ms=`/**
594
+ `;var fu=`/**
523
595
  * JXA: delete a folder by ID. Refuses if the folder has projects or subfolders.
524
596
  *
525
597
  * Args (argv[0] JSON): { id: string }
@@ -557,7 +629,7 @@ function run(argv) {
557
629
 
558
630
  return JSON.stringify({ id: args.id });
559
631
  }
560
- `;var Fs=`/**
632
+ `;var gu=`/**
561
633
  * JXA: fetch one folder by ID.
562
634
  *
563
635
  * Args (argv[0] JSON): { id: string }
@@ -604,7 +676,7 @@ function run(argv) {
604
676
 
605
677
  throw new Error(\`Folder not found: \${args.id}\`);
606
678
  }
607
- `;var Es=`/**
679
+ `;var hu=`/**
608
680
  * JXA: list all folders, optionally filtered by parentId.
609
681
  *
610
682
  * Args (argv[0] JSON): { parentId?: string }
@@ -645,27 +717,51 @@ function run(argv) {
645
717
  id: id,
646
718
  name: folder.name(),
647
719
  parentId: parentId,
648
- projectCount: folder.projects ? folder.projects().length : 0,
649
- subfolderCount: folder.folders ? folder.folders().length : 0,
650
- createdAt: folder.creationDate
651
- ? folder.creationDate().toISOString()
652
- : new Date().toISOString(),
653
- modifiedAt: folder.modificationDate
654
- ? folder.modificationDate().toISOString()
655
- : new Date().toISOString(),
720
+ projectCount: (() => {
721
+ try {
722
+ return folder.projects().length;
723
+ } catch (_e) {
724
+ return 0;
725
+ }
726
+ })(),
727
+ subfolderCount: (() => {
728
+ try {
729
+ return folder.folders().length;
730
+ } catch (_e) {
731
+ return 0;
732
+ }
733
+ })(),
734
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
735
+ createdAt: (() => {
736
+ try {
737
+ return folder.creationDate().toISOString();
738
+ } catch (_e) {
739
+ return new Date().toISOString();
740
+ }
741
+ })(),
742
+ modifiedAt: (() => {
743
+ try {
744
+ return folder.modificationDate().toISOString();
745
+ } catch (_e) {
746
+ return new Date().toISOString();
747
+ }
748
+ })(),
656
749
  };
657
750
  }
658
751
 
659
752
  const result = [];
660
753
  for (let i = 0; i < allFolders.length; i++) {
661
754
  const built = buildFolder(allFolders[i]);
662
- if (args.parentId !== undefined && built.parentId !== args.parentId) continue;
755
+ // JxaTransport may send \`parentId: null\` for "no filter" \u2014 treat null and
756
+ // undefined identically so those calls don't filter every folder out.
757
+ // See #515 (tag_list had the same bug).
758
+ if (args.parentId != null && built.parentId !== args.parentId) continue;
663
759
  result.push(built);
664
760
  }
665
761
 
666
762
  return JSON.stringify({ folders: result });
667
763
  }
668
- `;var Ns=`/**
764
+ `;var yu=`/**
669
765
  * JXA: rename a folder.
670
766
  *
671
767
  * Args (argv[0] JSON): { id: string, name: string }
@@ -717,7 +813,7 @@ function run(argv) {
717
813
 
718
814
  return JSON.stringify({ folder: buildFolder(target) });
719
815
  }
720
- `;var Us=`/**
816
+ `;var Iu=`/**
721
817
  * JXA: get forecast-view tasks grouped by category (overdue, dueToday,
722
818
  * deferredToday, flagged).
723
819
  *
@@ -867,51 +963,111 @@ function run(argv) {
867
963
  completedByChildren,
868
964
  estimatedMinutes: null,
869
965
  repetition: buildRepetition(task),
870
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
871
- modifiedAt: task.modificationDate
872
- ? task.modificationDate().toISOString()
873
- : new Date().toISOString(),
966
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
967
+
968
+ createdAt: (() => {
969
+ try {
970
+ return task.creationDate().toISOString();
971
+ } catch (_e) {
972
+ return new Date().toISOString();
973
+ }
974
+ })(),
975
+
976
+ modifiedAt: (() => {
977
+ try {
978
+ return task.modificationDate().toISOString();
979
+ } catch (_e) {
980
+ return new Date().toISOString();
981
+ }
982
+ })(),
874
983
  };
875
984
  }
876
985
 
877
986
  // ---------------------------------------------------------------------------
878
- // Collect all active (not completed, not dropped) tasks
987
+ // Collect candidates and bucket them \u2014 see #500.
988
+ //
989
+ // Push every filter into OF's runtime via \`whose()\`, one query per bucket.
990
+ // Each query returns only the tasks that actually match \u2014 typically a
991
+ // handful \u2014 so we never pay the per-task JXA accessor cost on the long
992
+ // tail of unrelated tasks.
993
+ //
994
+ // Empirical comparison (~240 active tasks, OF 4.8.x):
995
+ //
996
+ // Original (build every task) ~12 s
997
+ // whose() + per-task probe of 3 accessors ~12 s (probe wins
998
+ // nothing \u2014
999
+ // JXA per-call
1000
+ // overhead is
1001
+ // the limit)
1002
+ // Four whose() queries (this implementation) <0.5 s
1003
+ //
1004
+ // OF's whose() supports equality and date range comparisons (\`_lessThan\`,
1005
+ // \`_greaterThan\`, \`_greaterThanEquals\`, \`_lessThanEquals\`) on individual
1006
+ // properties, but rejects \`_or\` / \`_isnt: null\`. Hence one query per bucket
1007
+ // rather than a single \`_or\`-combined query.
1008
+ //
1009
+ // Tasks that match multiple buckets are built once and pushed into each
1010
+ // matching bucket. Dedup is keyed on persistent ID.
879
1011
  // ---------------------------------------------------------------------------
880
1012
 
881
- const allTasks = ofApp.defaultDocument.flattenedTasks();
882
- const active = [];
883
- for (let i = 0; i < allTasks.length; i++) {
884
- const t = allTasks[i];
885
- const built = buildTask(t);
886
- if (!built.completed && !built.dropped) {
887
- active.push(built);
888
- }
1013
+ const fromDate = new Date(from);
1014
+ const toDate = new Date(to);
1015
+
1016
+ // ID \u2192 built task, populated lazily so each task is constructed once.
1017
+ const builtById = {};
1018
+ function builtFor(task) {
1019
+ const id = task.id();
1020
+ if (builtById[id] !== undefined) return builtById[id];
1021
+ const b = buildTask(task);
1022
+ builtById[id] = b;
1023
+ return b;
889
1024
  }
890
1025
 
891
- // ---------------------------------------------------------------------------
892
- // Bucket into forecast categories
893
- // ---------------------------------------------------------------------------
1026
+ function runQuery(predicate) {
1027
+ try {
1028
+ return ofApp.defaultDocument.flattenedTasks.whose(predicate)();
1029
+ } catch (_e) {
1030
+ return [];
1031
+ }
1032
+ }
894
1033
 
895
1034
  const overdue = [];
896
1035
  const dueToday = [];
897
1036
  const deferredToday = [];
898
1037
  const flaggedTasks = [];
899
1038
 
900
- for (let i = 0; i < active.length; i++) {
901
- const t = active[i];
1039
+ if (includeOverdue) {
1040
+ const matches = runQuery({
1041
+ completed: false,
1042
+ dropped: false,
1043
+ dueDate: { _lessThan: fromDate },
1044
+ });
1045
+ for (let i = 0; i < matches.length; i++) overdue.push(builtFor(matches[i]));
1046
+ }
902
1047
 
903
- if (includeOverdue && t.dueDate !== null && t.dueDate < from) {
904
- overdue.push(t);
905
- }
906
- if (t.dueDate !== null && t.dueDate >= from && t.dueDate <= to) {
907
- dueToday.push(t);
908
- }
909
- if (includeDeferred && t.deferDate !== null && t.deferDate >= from && t.deferDate <= to) {
910
- deferredToday.push(t);
911
- }
912
- if (includeFlagged && t.flagged) {
913
- flaggedTasks.push(t);
914
- }
1048
+ // dueToday is always populated regardless of include flags (mirrors the
1049
+ // pre-#500 behaviour).
1050
+ {
1051
+ const matches = runQuery({
1052
+ completed: false,
1053
+ dropped: false,
1054
+ dueDate: { _greaterThanEquals: fromDate, _lessThanEquals: toDate },
1055
+ });
1056
+ for (let i = 0; i < matches.length; i++) dueToday.push(builtFor(matches[i]));
1057
+ }
1058
+
1059
+ if (includeDeferred) {
1060
+ const matches = runQuery({
1061
+ completed: false,
1062
+ dropped: false,
1063
+ deferDate: { _greaterThanEquals: fromDate, _lessThanEquals: toDate },
1064
+ });
1065
+ for (let i = 0; i < matches.length; i++) deferredToday.push(builtFor(matches[i]));
1066
+ }
1067
+
1068
+ if (includeFlagged) {
1069
+ const matches = runQuery({ completed: false, dropped: false, flagged: true });
1070
+ for (let i = 0; i < matches.length; i++) flaggedTasks.push(builtFor(matches[i]));
915
1071
  }
916
1072
 
917
1073
  return JSON.stringify({
@@ -921,7 +1077,7 @@ function run(argv) {
921
1077
  flagged: flaggedTasks,
922
1078
  });
923
1079
  }
924
- `;var Ls=`/**
1080
+ `;var ku=`/**
925
1081
  * JXA: evaluate a built-in OmniFocus perspective and return its task list.
926
1082
  *
927
1083
  * Args (argv[0] JSON): { "perspectiveId": "inbox" | "projects" | "tags" | "forecast" | "flagged" | "nearby" | "review" }
@@ -1070,10 +1226,23 @@ function run(argv) {
1070
1226
  sequential: sequential,
1071
1227
  completedByChildren: completedByChildren,
1072
1228
  repetition: buildRepetition(task),
1073
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
1074
- modifiedAt: task.modificationDate
1075
- ? task.modificationDate().toISOString()
1076
- : new Date().toISOString(),
1229
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
1230
+
1231
+ createdAt: (() => {
1232
+ try {
1233
+ return task.creationDate().toISOString();
1234
+ } catch (_e) {
1235
+ return new Date().toISOString();
1236
+ }
1237
+ })(),
1238
+
1239
+ modifiedAt: (() => {
1240
+ try {
1241
+ return task.modificationDate().toISOString();
1242
+ } catch (_e) {
1243
+ return new Date().toISOString();
1244
+ }
1245
+ })(),
1077
1246
  };
1078
1247
  }
1079
1248
 
@@ -1137,7 +1306,7 @@ function run(argv) {
1137
1306
  return JSON.stringify({ error: String(e) });
1138
1307
  }
1139
1308
  }
1140
- `;var Js=`/**
1309
+ `;var Tu=`/**
1141
1310
  * JXA: list all perspectives (built-in + custom).
1142
1311
  *
1143
1312
  * Args (argv[0] JSON): {} (no arguments)
@@ -1224,7 +1393,7 @@ function run(_argv) {
1224
1393
 
1225
1394
  return JSON.stringify({ perspectives });
1226
1395
  }
1227
- `;var Bs=`/**
1396
+ `;var vu=`/**
1228
1397
  * JXA: batch-complete projects in a single round-trip.
1229
1398
  *
1230
1399
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -1284,7 +1453,7 @@ function run(argv) {
1284
1453
 
1285
1454
  return JSON.stringify({ succeeded: succeeded, failed: failed });
1286
1455
  }
1287
- `;var Gs=`/**
1456
+ `;var wu=`/**
1288
1457
  * JXA: batch-drop projects in a single round-trip.
1289
1458
  *
1290
1459
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -1345,7 +1514,7 @@ function run(argv) {
1345
1514
 
1346
1515
  return JSON.stringify({ succeeded: succeeded, failed: failed });
1347
1516
  }
1348
- `;var Hs=`/**
1517
+ `;var Su=`/**
1349
1518
  * JXA: mark a project complete.
1350
1519
  *
1351
1520
  * Args (argv[0] JSON): { id: string, completionDate?: string|null }
@@ -1381,7 +1550,7 @@ function run(argv) {
1381
1550
 
1382
1551
  return JSON.stringify({ id: args.id });
1383
1552
  }
1384
- `;var $s=`/**
1553
+ `;var ju=`/**
1385
1554
  * JXA: create a new project.
1386
1555
  *
1387
1556
  * Args (argv[0] JSON): { name: string, folderId?: string|null, note?: string|null,
@@ -1524,10 +1693,23 @@ function run(argv) {
1524
1693
  droppedAt: status === "dropped" ? completedAt : null,
1525
1694
  taskCount: taskCount,
1526
1695
  completedTaskCount: completedTaskCount,
1527
- createdAt: proj.creationDate ? proj.creationDate().toISOString() : new Date().toISOString(),
1528
- modifiedAt: proj.modificationDate
1529
- ? proj.modificationDate().toISOString()
1530
- : new Date().toISOString(),
1696
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
1697
+
1698
+ createdAt: (() => {
1699
+ try {
1700
+ return proj.creationDate().toISOString();
1701
+ } catch (_e) {
1702
+ return new Date().toISOString();
1703
+ }
1704
+ })(),
1705
+
1706
+ modifiedAt: (() => {
1707
+ try {
1708
+ return proj.modificationDate().toISOString();
1709
+ } catch (_e) {
1710
+ return new Date().toISOString();
1711
+ }
1712
+ })(),
1531
1713
  };
1532
1714
  }
1533
1715
 
@@ -1561,7 +1743,7 @@ function run(argv) {
1561
1743
 
1562
1744
  return JSON.stringify({ project: buildProject(newProj) });
1563
1745
  }
1564
- `;var zs=`/**
1746
+ `;var bu=`/**
1565
1747
  * JXA: delete a project by ID.
1566
1748
  *
1567
1749
  * Args (argv[0] JSON): { id: string }
@@ -1591,7 +1773,7 @@ function run(argv) {
1591
1773
 
1592
1774
  return JSON.stringify({ id: args.id });
1593
1775
  }
1594
- `;var qs=`/**
1776
+ `;var _u=`/**
1595
1777
  * JXA: mark a project dropped.
1596
1778
  *
1597
1779
  * Args (argv[0] JSON): { id: string }
@@ -1621,7 +1803,7 @@ function run(argv) {
1621
1803
 
1622
1804
  return JSON.stringify({ id: args.id });
1623
1805
  }
1624
- `;var Vs=`/**
1806
+ `;var Pu=`/**
1625
1807
  * JXA: fetch one project by ID.
1626
1808
  *
1627
1809
  * Args (argv[0] JSON): { id: string }
@@ -1762,10 +1944,23 @@ function run(argv) {
1762
1944
  droppedAt: status === "dropped" ? completedAt : null,
1763
1945
  taskCount: taskCount,
1764
1946
  completedTaskCount: completedTaskCount,
1765
- createdAt: proj.creationDate ? proj.creationDate().toISOString() : new Date().toISOString(),
1766
- modifiedAt: proj.modificationDate
1767
- ? proj.modificationDate().toISOString()
1768
- : new Date().toISOString(),
1947
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
1948
+
1949
+ createdAt: (() => {
1950
+ try {
1951
+ return proj.creationDate().toISOString();
1952
+ } catch (_e) {
1953
+ return new Date().toISOString();
1954
+ }
1955
+ })(),
1956
+
1957
+ modifiedAt: (() => {
1958
+ try {
1959
+ return proj.modificationDate().toISOString();
1960
+ } catch (_e) {
1961
+ return new Date().toISOString();
1962
+ }
1963
+ })(),
1769
1964
  };
1770
1965
  }
1771
1966
 
@@ -1778,7 +1973,7 @@ function run(argv) {
1778
1973
 
1779
1974
  throw new Error(\`Project not found: \${args.id}\`);
1780
1975
  }
1781
- `;var Ws=`/**
1976
+ `;var Ou=`/**
1782
1977
  * JXA: fetch multiple projects by IDs.
1783
1978
  *
1784
1979
  * Args (argv[0] JSON): { ids: string[] }
@@ -1917,10 +2112,23 @@ function run(argv) {
1917
2112
  droppedAt: status === "dropped" ? completedAt : null,
1918
2113
  taskCount: taskCount,
1919
2114
  completedTaskCount: completedTaskCount,
1920
- createdAt: proj.creationDate ? proj.creationDate().toISOString() : new Date().toISOString(),
1921
- modifiedAt: proj.modificationDate
1922
- ? proj.modificationDate().toISOString()
1923
- : new Date().toISOString(),
2115
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
2116
+
2117
+ createdAt: (() => {
2118
+ try {
2119
+ return proj.creationDate().toISOString();
2120
+ } catch (_e) {
2121
+ return new Date().toISOString();
2122
+ }
2123
+ })(),
2124
+
2125
+ modifiedAt: (() => {
2126
+ try {
2127
+ return proj.modificationDate().toISOString();
2128
+ } catch (_e) {
2129
+ return new Date().toISOString();
2130
+ }
2131
+ })(),
1924
2132
  };
1925
2133
  }
1926
2134
 
@@ -1944,7 +2152,7 @@ function run(argv) {
1944
2152
 
1945
2153
  return JSON.stringify({ projects: results });
1946
2154
  }
1947
- `;var Xs=`/**
2155
+ `;var xu=`/**
1948
2156
  * JXA: list projects, optionally filtered by folderId or status.
1949
2157
  *
1950
2158
  * Args (argv[0] JSON): { folderId?: string|null, status?: string|null }
@@ -2085,10 +2293,23 @@ function run(argv) {
2085
2293
  droppedAt: status === "dropped" ? completedAt : null,
2086
2294
  taskCount: taskCount,
2087
2295
  completedTaskCount: completedTaskCount,
2088
- createdAt: proj.creationDate ? proj.creationDate().toISOString() : new Date().toISOString(),
2089
- modifiedAt: proj.modificationDate
2090
- ? proj.modificationDate().toISOString()
2091
- : new Date().toISOString(),
2296
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
2297
+
2298
+ createdAt: (() => {
2299
+ try {
2300
+ return proj.creationDate().toISOString();
2301
+ } catch (_e) {
2302
+ return new Date().toISOString();
2303
+ }
2304
+ })(),
2305
+
2306
+ modifiedAt: (() => {
2307
+ try {
2308
+ return proj.modificationDate().toISOString();
2309
+ } catch (_e) {
2310
+ return new Date().toISOString();
2311
+ }
2312
+ })(),
2092
2313
  };
2093
2314
  }
2094
2315
 
@@ -2113,7 +2334,7 @@ function run(argv) {
2113
2334
 
2114
2335
  return JSON.stringify({ projects: result });
2115
2336
  }
2116
- `;var Ks=`/**
2337
+ `;var Du=`/**
2117
2338
  * JXA: mark a project as reviewed (sets lastReviewDate to now).
2118
2339
  *
2119
2340
  * Args (argv[0] JSON): { id: string }
@@ -2144,7 +2365,7 @@ function run(argv) {
2144
2365
 
2145
2366
  return JSON.stringify({ id: args.id });
2146
2367
  }
2147
- `;var Ys=`/**
2368
+ `;var Au=`/**
2148
2369
  * JXA: move a project to a folder (or to the root if folderId is null).
2149
2370
  *
2150
2371
  * Args (argv[0] JSON): { id: string, folderId?: string|null }
@@ -2180,7 +2401,49 @@ function run(argv) {
2180
2401
 
2181
2402
  return JSON.stringify({ id: args.id });
2182
2403
  }
2183
- `;var Qs=`/**
2404
+ `;var Cu=`/**
2405
+ * JXA: set a project's next review date.
2406
+ *
2407
+ * The third axis of OmniFocus's review schedule (the others being
2408
+ * reviewIntervalDays and lastReviewDate). Setting nextReviewDate directly
2409
+ * lets agents reschedule a review independent of the recurring interval \u2014
2410
+ * "push the Q3 review to next Monday" without mutating the cadence.
2411
+ *
2412
+ * Args (argv[0] JSON):
2413
+ * { id: string, nextReviewDate: string | null }
2414
+ * - \`nextReviewDate\` ISO-8601 \u2192 set to that date
2415
+ * - \`nextReviewDate\` null \u2192 clear (Project shows up as not scheduled)
2416
+ *
2417
+ * Returns JSON: { id: string }
2418
+ *
2419
+ * Past-dated values are allowed by OmniFocus and surface the project as
2420
+ * overdue for review immediately \u2014 matches the app UX, no special handling.
2421
+ *
2422
+ * @see #467
2423
+ * @see src/adapter/jxa/JxaTransport.ts \u2014 caller
2424
+ */
2425
+
2426
+ // biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
2427
+ function run(argv) {
2428
+ const args = JSON.parse(argv[0]);
2429
+ const ofApp = Application("OmniFocus");
2430
+ ofApp.includeStandardAdditions = false;
2431
+
2432
+ const allProjects = ofApp.defaultDocument.flattenedProjects();
2433
+ let target = null;
2434
+ for (let i = 0; i < allProjects.length; i++) {
2435
+ if (allProjects[i].id() === args.id) {
2436
+ target = allProjects[i];
2437
+ break;
2438
+ }
2439
+ }
2440
+ if (!target) throw new Error(\`Project not found: \${args.id}\`);
2441
+
2442
+ target.nextReviewDate = args.nextReviewDate === null ? null : new Date(args.nextReviewDate);
2443
+
2444
+ return JSON.stringify({ id: args.id });
2445
+ }
2446
+ `;var Ru=`/**
2184
2447
  * JXA: set a project's review interval.
2185
2448
  *
2186
2449
  * Args (argv[0] JSON): { id: string, days: number | null }
@@ -2210,7 +2473,7 @@ function run(argv) {
2210
2473
 
2211
2474
  return JSON.stringify({ id: args.id });
2212
2475
  }
2213
- `;var Zs=`/**
2476
+ `;var Mu=`/**
2214
2477
  * JXA: update mutable fields on an existing project.
2215
2478
  *
2216
2479
  * Args (argv[0] JSON): { id: string, name?: string, note?: string|null,
@@ -2354,10 +2617,23 @@ function run(argv) {
2354
2617
  droppedAt: status === "dropped" ? completedAt : null,
2355
2618
  taskCount: taskCount,
2356
2619
  completedTaskCount: completedTaskCount,
2357
- createdAt: proj.creationDate ? proj.creationDate().toISOString() : new Date().toISOString(),
2358
- modifiedAt: proj.modificationDate
2359
- ? proj.modificationDate().toISOString()
2360
- : new Date().toISOString(),
2620
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
2621
+
2622
+ createdAt: (() => {
2623
+ try {
2624
+ return proj.creationDate().toISOString();
2625
+ } catch (_e) {
2626
+ return new Date().toISOString();
2627
+ }
2628
+ })(),
2629
+
2630
+ modifiedAt: (() => {
2631
+ try {
2632
+ return proj.modificationDate().toISOString();
2633
+ } catch (_e) {
2634
+ return new Date().toISOString();
2635
+ }
2636
+ })(),
2361
2637
  };
2362
2638
  }
2363
2639
 
@@ -2393,7 +2669,7 @@ function run(argv) {
2393
2669
 
2394
2670
  return JSON.stringify({ project: buildProject(target) });
2395
2671
  }
2396
- `;var ei=`/**
2672
+ `;var Fu=`/**
2397
2673
  * JXA: list projects due for review (nextReviewDate <= today, or null).
2398
2674
  *
2399
2675
  * Args (argv[0] JSON): {}
@@ -2460,7 +2736,7 @@ function run(argv) {
2460
2736
 
2461
2737
  return JSON.stringify({ projects: due });
2462
2738
  }
2463
- `;var ti=`/**
2739
+ `;var Eu=`/**
2464
2740
  * JXA: trigger Omni Sync.
2465
2741
  *
2466
2742
  * \`Application("OmniFocus").defaultDocument.synchronize()\` kicks off a sync
@@ -2483,7 +2759,7 @@ function run(_argv) {
2483
2759
  const lastSyncAt = new Date().toISOString();
2484
2760
  return JSON.stringify({ lastSyncAt: lastSyncAt, inFlight: false });
2485
2761
  }
2486
- `;var ni=`/**
2762
+ `;var Nu=`/**
2487
2763
  * JXA: create a tag, optionally under a parent tag.
2488
2764
  *
2489
2765
  * Args (argv[0] JSON): { name: string, parentId?: string }
@@ -2534,10 +2810,23 @@ function run(argv) {
2534
2810
  location: location,
2535
2811
  allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
2536
2812
  taskCount: tag.tasks ? tag.tasks().length : 0,
2537
- createdAt: tag.creationDate ? tag.creationDate().toISOString() : new Date().toISOString(),
2538
- modifiedAt: tag.modificationDate
2539
- ? tag.modificationDate().toISOString()
2540
- : new Date().toISOString(),
2813
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
2814
+
2815
+ createdAt: (() => {
2816
+ try {
2817
+ return tag.creationDate().toISOString();
2818
+ } catch (_e) {
2819
+ return new Date().toISOString();
2820
+ }
2821
+ })(),
2822
+
2823
+ modifiedAt: (() => {
2824
+ try {
2825
+ return tag.modificationDate().toISOString();
2826
+ } catch (_e) {
2827
+ return new Date().toISOString();
2828
+ }
2829
+ })(),
2541
2830
  };
2542
2831
  }
2543
2832
 
@@ -2568,7 +2857,7 @@ function run(argv) {
2568
2857
  const fetchedTag = doc.flattenedTags.byId(tagId);
2569
2858
  return JSON.stringify({ tag: buildTag(fetchedTag) });
2570
2859
  }
2571
- `;var ri=`/**
2860
+ `;var Uu=`/**
2572
2861
  * JXA: delete a tag by ID.
2573
2862
  *
2574
2863
  * Args (argv[0] JSON): { id: string }
@@ -2598,7 +2887,7 @@ function run(argv) {
2598
2887
 
2599
2888
  return JSON.stringify({ id: args.id });
2600
2889
  }
2601
- `;var oi=`/**
2890
+ `;var Lu=`/**
2602
2891
  * JXA: fetch one tag by ID.
2603
2892
  *
2604
2893
  * Args (argv[0] JSON): { id: string }
@@ -2649,10 +2938,23 @@ function run(argv) {
2649
2938
  location: location,
2650
2939
  allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
2651
2940
  taskCount: tag.tasks ? tag.tasks().length : 0,
2652
- createdAt: tag.creationDate ? tag.creationDate().toISOString() : new Date().toISOString(),
2653
- modifiedAt: tag.modificationDate
2654
- ? tag.modificationDate().toISOString()
2655
- : new Date().toISOString(),
2941
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
2942
+
2943
+ createdAt: (() => {
2944
+ try {
2945
+ return tag.creationDate().toISOString();
2946
+ } catch (_e) {
2947
+ return new Date().toISOString();
2948
+ }
2949
+ })(),
2950
+
2951
+ modifiedAt: (() => {
2952
+ try {
2953
+ return tag.modificationDate().toISOString();
2954
+ } catch (_e) {
2955
+ return new Date().toISOString();
2956
+ }
2957
+ })(),
2656
2958
  };
2657
2959
  }
2658
2960
 
@@ -2665,7 +2967,7 @@ function run(argv) {
2665
2967
 
2666
2968
  throw new Error(\`Tag not found: \${args.id}\`);
2667
2969
  }
2668
- `;var ai=`/**
2970
+ `;var Ju=`/**
2669
2971
  * JXA: fetch multiple tags by IDs.
2670
2972
  *
2671
2973
  * Args (argv[0] JSON): { ids: string[] }
@@ -2716,10 +3018,23 @@ function run(argv) {
2716
3018
  location: location,
2717
3019
  allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
2718
3020
  taskCount: tag.tasks ? tag.tasks().length : 0,
2719
- createdAt: tag.creationDate ? tag.creationDate().toISOString() : new Date().toISOString(),
2720
- modifiedAt: tag.modificationDate
2721
- ? tag.modificationDate().toISOString()
2722
- : new Date().toISOString(),
3021
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
3022
+
3023
+ createdAt: (() => {
3024
+ try {
3025
+ return tag.creationDate().toISOString();
3026
+ } catch (_e) {
3027
+ return new Date().toISOString();
3028
+ }
3029
+ })(),
3030
+
3031
+ modifiedAt: (() => {
3032
+ try {
3033
+ return tag.modificationDate().toISOString();
3034
+ } catch (_e) {
3035
+ return new Date().toISOString();
3036
+ }
3037
+ })(),
2723
3038
  };
2724
3039
  }
2725
3040
 
@@ -2743,7 +3058,7 @@ function run(argv) {
2743
3058
 
2744
3059
  return JSON.stringify({ tags: results });
2745
3060
  }
2746
- `;var si=`/**
3061
+ `;var Bu=`/**
2747
3062
  * JXA: list all tags, optionally filtered by parentId or status.
2748
3063
  *
2749
3064
  * Args (argv[0] JSON): { parentId?: string, status?: string }
@@ -2786,18 +3101,43 @@ function run(argv) {
2786
3101
  } catch (_e) {}
2787
3102
  const status = rawStatus === "on hold" ? "on-hold" : rawStatus;
2788
3103
 
3104
+ // creationDate/modificationDate are present as functions on every Tag, but
3105
+ // invoking them throws "Can't get object." for tags that lack the
3106
+ // timestamp in the document \u2014 see #498. Truthiness on the property
3107
+ // reference is not enough; we have to guard the call.
3108
+ let createdAt;
3109
+ try {
3110
+ createdAt = tag.creationDate().toISOString();
3111
+ } catch (_e) {
3112
+ createdAt = new Date().toISOString();
3113
+ }
3114
+ let modifiedAt;
3115
+ try {
3116
+ modifiedAt = tag.modificationDate().toISOString();
3117
+ } catch (_e) {
3118
+ modifiedAt = new Date().toISOString();
3119
+ }
3120
+
3121
+ let allowsNextAction = false;
3122
+ try {
3123
+ allowsNextAction = tag.allowsNextAction();
3124
+ } catch (_e) {}
3125
+
3126
+ let taskCount = 0;
3127
+ try {
3128
+ taskCount = tag.tasks().length;
3129
+ } catch (_e) {}
3130
+
2789
3131
  return {
2790
3132
  id: tag.id(),
2791
3133
  name: tag.name(),
2792
3134
  parentId: parentId,
2793
3135
  status: status,
2794
3136
  location: location,
2795
- allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
2796
- taskCount: tag.tasks ? tag.tasks().length : 0,
2797
- createdAt: tag.creationDate ? tag.creationDate().toISOString() : new Date().toISOString(),
2798
- modifiedAt: tag.modificationDate
2799
- ? tag.modificationDate().toISOString()
2800
- : new Date().toISOString(),
3137
+ allowsNextAction: allowsNextAction,
3138
+ taskCount: taskCount,
3139
+ createdAt: createdAt,
3140
+ modifiedAt: modifiedAt,
2801
3141
  };
2802
3142
  }
2803
3143
 
@@ -2808,15 +3148,18 @@ function run(argv) {
2808
3148
  const tag = allTags[i];
2809
3149
  const built = buildTag(tag);
2810
3150
 
2811
- if (args.parentId !== undefined && built.parentId !== args.parentId) continue;
2812
- if (args.status !== undefined && built.status !== args.status) continue;
3151
+ // JxaTransport sends \`parentId: null\` / \`status: null\` for "no filter"
3152
+ // (rather than omitting). Treat null and undefined identically here so
3153
+ // those calls don't filter every tag out \u2014 see #515.
3154
+ if (args.parentId != null && built.parentId !== args.parentId) continue;
3155
+ if (args.status != null && built.status !== args.status) continue;
2813
3156
 
2814
3157
  result.push(built);
2815
3158
  }
2816
3159
 
2817
3160
  return JSON.stringify({ tags: result });
2818
3161
  }
2819
- `;var ii=`/**
3162
+ `;var $u=`/**
2820
3163
  * JXA: update mutable fields on an existing tag.
2821
3164
  *
2822
3165
  * Args (argv[0] JSON): { id: string, name?: string, status?: string, allowsNextAction?: boolean }
@@ -2867,10 +3210,23 @@ function run(argv) {
2867
3210
  location: location,
2868
3211
  allowsNextAction: tag.allowsNextAction ? tag.allowsNextAction() : false,
2869
3212
  taskCount: tag.tasks ? tag.tasks().length : 0,
2870
- createdAt: tag.creationDate ? tag.creationDate().toISOString() : new Date().toISOString(),
2871
- modifiedAt: tag.modificationDate
2872
- ? tag.modificationDate().toISOString()
2873
- : new Date().toISOString(),
3213
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
3214
+
3215
+ createdAt: (() => {
3216
+ try {
3217
+ return tag.creationDate().toISOString();
3218
+ } catch (_e) {
3219
+ return new Date().toISOString();
3220
+ }
3221
+ })(),
3222
+
3223
+ modifiedAt: (() => {
3224
+ try {
3225
+ return tag.modificationDate().toISOString();
3226
+ } catch (_e) {
3227
+ return new Date().toISOString();
3228
+ }
3229
+ })(),
2874
3230
  };
2875
3231
  }
2876
3232
 
@@ -2894,7 +3250,7 @@ function run(argv) {
2894
3250
 
2895
3251
  return JSON.stringify({ tag: buildTag(target) });
2896
3252
  }
2897
- `;var ci=`/**
3253
+ `;var Wu=`/**
2898
3254
  * JXA: batch-complete tasks in a single round-trip.
2899
3255
  *
2900
3256
  * Args (argv[0] JSON): { items: Array<{ id, at? }> }
@@ -2941,7 +3297,7 @@ function run(argv) {
2941
3297
 
2942
3298
  return JSON.stringify({ succeeded: succeeded, failed: failed });
2943
3299
  }
2944
- `;var di=`/**
3300
+ `;var Hu=`/**
2945
3301
  * JXA: batch-create tasks in a single round-trip.
2946
3302
  *
2947
3303
  * Args (argv[0] JSON): { inputs: CreateTaskInput[] }
@@ -3017,7 +3373,7 @@ function run(argv) {
3017
3373
 
3018
3374
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3019
3375
  }
3020
- `;var li=`/**
3376
+ `;var Gu=`/**
3021
3377
  * JXA: batch-delete tasks permanently in a single round-trip.
3022
3378
  *
3023
3379
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3057,7 +3413,7 @@ function run(argv) {
3057
3413
 
3058
3414
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3059
3415
  }
3060
- `;var pi=`/**
3416
+ `;var zu=`/**
3061
3417
  * JXA: batch-drop (cancel) tasks in a single round-trip.
3062
3418
  *
3063
3419
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3100,7 +3456,7 @@ function run(argv) {
3100
3456
 
3101
3457
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3102
3458
  }
3103
- `;var ui=`/**
3459
+ `;var Vu=`/**
3104
3460
  * JXA: batch-uncomplete (mark incomplete) tasks in a single round-trip.
3105
3461
  *
3106
3462
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3162,7 +3518,7 @@ function run(argv) {
3162
3518
 
3163
3519
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3164
3520
  }
3165
- `;var mi=`/**
3521
+ `;var qu=`/**
3166
3522
  * JXA: batch-undrop (restore) tasks in a single round-trip.
3167
3523
  *
3168
3524
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3206,7 +3562,7 @@ function run(argv) {
3206
3562
 
3207
3563
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3208
3564
  }
3209
- `;var fi=`/**
3565
+ `;var Ku=`/**
3210
3566
  * JXA: batch-update tasks in a single round-trip.
3211
3567
  *
3212
3568
  * Args (argv[0] JSON): { updates: Array<{ id, patch }> }
@@ -3279,7 +3635,7 @@ function run(argv) {
3279
3635
 
3280
3636
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3281
3637
  }
3282
- `;var gi=`/**
3638
+ `;var Xu=`/**
3283
3639
  * JXA: mark a task complete.
3284
3640
  *
3285
3641
  * Args (argv[0] JSON): { id: string, completionDate?: string|null }
@@ -3314,7 +3670,7 @@ function run(argv) {
3314
3670
 
3315
3671
  return JSON.stringify({ id: args.id });
3316
3672
  }
3317
- `;var hi=`/**
3673
+ `;var Yu=`/**
3318
3674
  * JXA: create a new task.
3319
3675
  *
3320
3676
  * Args (argv[0] JSON): {
@@ -3460,10 +3816,23 @@ function run(argv) {
3460
3816
  sequential: sequential,
3461
3817
  completedByChildren: completedByChildren,
3462
3818
  repetition: buildRepetition(task),
3463
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
3464
- modifiedAt: task.modificationDate
3465
- ? task.modificationDate().toISOString()
3466
- : new Date().toISOString(),
3819
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
3820
+
3821
+ createdAt: (() => {
3822
+ try {
3823
+ return task.creationDate().toISOString();
3824
+ } catch (_e) {
3825
+ return new Date().toISOString();
3826
+ }
3827
+ })(),
3828
+
3829
+ modifiedAt: (() => {
3830
+ try {
3831
+ return task.modificationDate().toISOString();
3832
+ } catch (_e) {
3833
+ return new Date().toISOString();
3834
+ }
3835
+ })(),
3467
3836
  };
3468
3837
  }
3469
3838
 
@@ -3533,7 +3902,7 @@ function run(argv) {
3533
3902
  }
3534
3903
  return JSON.stringify({ task: buildTask(newTask) });
3535
3904
  }
3536
- `;var Ii=`/**
3905
+ `;var Qu=`/**
3537
3906
  * JXA: delete a task permanently.
3538
3907
  *
3539
3908
  * Args (argv[0] JSON): { id: string }
@@ -3562,7 +3931,7 @@ function run(argv) {
3562
3931
 
3563
3932
  return JSON.stringify({ id: args.id });
3564
3933
  }
3565
- `;var ki=`/**
3934
+ `;var Zu=`/**
3566
3935
  * JXA: mark a task as dropped.
3567
3936
  *
3568
3937
  * Args (argv[0] JSON): { id: string, droppedAt?: string|null }
@@ -3586,7 +3955,7 @@ function run(argv) {
3586
3955
 
3587
3956
  return JSON.stringify({ id: args.id });
3588
3957
  }
3589
- `;var Ti=`/**
3958
+ `;var em=`/**
3590
3959
  * JXA: duplicate a task. Editable fields copy; completed/dropped state reset.
3591
3960
  * When recursive=true, subtask tree is cloned depth-first, preserving order.
3592
3961
  *
@@ -3703,7 +4072,7 @@ function run(argv) {
3703
4072
 
3704
4073
  return JSON.stringify({ newId: rootClone.id(), descendantCount: descendantCount });
3705
4074
  }
3706
- `;var yi=`/**
4075
+ `;var tm=`/**
3707
4076
  * JXA: fetch one task by ID.
3708
4077
  *
3709
4078
  * Args (argv[0] JSON): { id: string }
@@ -3844,10 +4213,23 @@ function run(argv) {
3844
4213
  sequential: sequential,
3845
4214
  completedByChildren: completedByChildren,
3846
4215
  repetition: buildRepetition(task),
3847
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
3848
- modifiedAt: task.modificationDate
3849
- ? task.modificationDate().toISOString()
3850
- : new Date().toISOString(),
4216
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
4217
+
4218
+ createdAt: (() => {
4219
+ try {
4220
+ return task.creationDate().toISOString();
4221
+ } catch (_e) {
4222
+ return new Date().toISOString();
4223
+ }
4224
+ })(),
4225
+
4226
+ modifiedAt: (() => {
4227
+ try {
4228
+ return task.modificationDate().toISOString();
4229
+ } catch (_e) {
4230
+ return new Date().toISOString();
4231
+ }
4232
+ })(),
3851
4233
  };
3852
4234
  }
3853
4235
 
@@ -3860,7 +4242,7 @@ function run(argv) {
3860
4242
 
3861
4243
  throw new Error(\`Task not found: \${args.id}\`);
3862
4244
  }
3863
- `;var vi=`/**
4245
+ `;var nm=`/**
3864
4246
  * JXA: fetch multiple tasks by IDs.
3865
4247
  *
3866
4248
  * Args (argv[0] JSON): { ids: string[] }
@@ -4001,10 +4383,23 @@ function run(argv) {
4001
4383
  sequential: sequential,
4002
4384
  completedByChildren: completedByChildren,
4003
4385
  repetition: buildRepetition(task),
4004
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
4005
- modifiedAt: task.modificationDate
4006
- ? task.modificationDate().toISOString()
4007
- : new Date().toISOString(),
4386
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
4387
+
4388
+ createdAt: (() => {
4389
+ try {
4390
+ return task.creationDate().toISOString();
4391
+ } catch (_e) {
4392
+ return new Date().toISOString();
4393
+ }
4394
+ })(),
4395
+
4396
+ modifiedAt: (() => {
4397
+ try {
4398
+ return task.modificationDate().toISOString();
4399
+ } catch (_e) {
4400
+ return new Date().toISOString();
4401
+ }
4402
+ })(),
4008
4403
  };
4009
4404
  }
4010
4405
 
@@ -4028,7 +4423,7 @@ function run(argv) {
4028
4423
 
4029
4424
  return JSON.stringify({ tasks: results });
4030
4425
  }
4031
- `;var Si=`/**
4426
+ `;var rm=`/**
4032
4427
  * JXA: list tasks, optionally filtered.
4033
4428
  *
4034
4429
  * Args (argv[0] JSON): {
@@ -4155,6 +4550,22 @@ function run(argv) {
4155
4550
  blocked = task.blocked();
4156
4551
  } catch (_e) {}
4157
4552
 
4553
+ // See #498 \u2014 JXA reports creationDate/modificationDate as truthy
4554
+ // functions even on tasks where invocation throws "Can't get object."
4555
+ // The call must be guarded, not just the property reference.
4556
+ let createdAt;
4557
+ try {
4558
+ createdAt = task.creationDate().toISOString();
4559
+ } catch (_e) {
4560
+ createdAt = new Date().toISOString();
4561
+ }
4562
+ let modifiedAt;
4563
+ try {
4564
+ modifiedAt = task.modificationDate().toISOString();
4565
+ } catch (_e) {
4566
+ modifiedAt = new Date().toISOString();
4567
+ }
4568
+
4158
4569
  return {
4159
4570
  id: task.id(),
4160
4571
  name: task.name(),
@@ -4176,10 +4587,8 @@ function run(argv) {
4176
4587
  sequential: sequential,
4177
4588
  completedByChildren: completedByChildren,
4178
4589
  repetition: buildRepetition(task),
4179
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
4180
- modifiedAt: task.modificationDate
4181
- ? task.modificationDate().toISOString()
4182
- : new Date().toISOString(),
4590
+ createdAt: createdAt,
4591
+ modifiedAt: modifiedAt,
4183
4592
  };
4184
4593
  }
4185
4594
 
@@ -4254,7 +4663,7 @@ function run(argv) {
4254
4663
 
4255
4664
  return JSON.stringify({ tasks: result });
4256
4665
  }
4257
- `;var wi=`/**
4666
+ `;var om=`/**
4258
4667
  * JXA: move a task to a different project or parent task.
4259
4668
  *
4260
4669
  * Args (argv[0] JSON): { id: string, projectId?: string|null, parentId?: string|null }
@@ -4293,7 +4702,7 @@ function run(argv) {
4293
4702
 
4294
4703
  return JSON.stringify({ id: args.id });
4295
4704
  }
4296
- `;var ji=`/**
4705
+ `;var am=`/**
4297
4706
  * JXA: search tasks by keyword and/or structured filters.
4298
4707
  *
4299
4708
  * Args (argv[0] JSON): {
@@ -4440,10 +4849,23 @@ function run(argv) {
4440
4849
  completedByChildren,
4441
4850
  estimatedMinutes: null,
4442
4851
  repetition: buildRepetition(task),
4443
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
4444
- modifiedAt: task.modificationDate
4445
- ? task.modificationDate().toISOString()
4446
- : new Date().toISOString(),
4852
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
4853
+
4854
+ createdAt: (() => {
4855
+ try {
4856
+ return task.creationDate().toISOString();
4857
+ } catch (_e) {
4858
+ return new Date().toISOString();
4859
+ }
4860
+ })(),
4861
+
4862
+ modifiedAt: (() => {
4863
+ try {
4864
+ return task.modificationDate().toISOString();
4865
+ } catch (_e) {
4866
+ return new Date().toISOString();
4867
+ }
4868
+ })(),
4447
4869
  };
4448
4870
  }
4449
4871
 
@@ -4512,7 +4934,7 @@ function run(argv) {
4512
4934
 
4513
4935
  return JSON.stringify({ tasks: result });
4514
4936
  }
4515
- `;var bi=`/**
4937
+ `;var sm=`/**
4516
4938
  * JXA: mark a task incomplete.
4517
4939
  *
4518
4940
  * Args (argv[0] JSON): { id: string }
@@ -4541,7 +4963,7 @@ function run(argv) {
4541
4963
 
4542
4964
  return JSON.stringify({ id: args.id });
4543
4965
  }
4544
- `;var Pi=`/**
4966
+ `;var im=`/**
4545
4967
  * JXA: remove dropped status from a task.
4546
4968
  *
4547
4969
  * Args (argv[0] JSON): { id: string }
@@ -4565,7 +4987,7 @@ function run(argv) {
4565
4987
 
4566
4988
  return JSON.stringify({ id: args.id });
4567
4989
  }
4568
- `;var Oi=`/**
4990
+ `;var cm=`/**
4569
4991
  * JXA: update task fields.
4570
4992
  *
4571
4993
  * Args (argv[0] JSON): {
@@ -4712,10 +5134,23 @@ function run(argv) {
4712
5134
  sequential: sequential,
4713
5135
  completedByChildren: completedByChildren,
4714
5136
  repetition: buildRepetition(task),
4715
- createdAt: task.creationDate ? task.creationDate().toISOString() : new Date().toISOString(),
4716
- modifiedAt: task.modificationDate
4717
- ? task.modificationDate().toISOString()
4718
- : new Date().toISOString(),
5137
+ // Guard against "Can't get object." thrown when invoking these \u2014 see #498.
5138
+
5139
+ createdAt: (() => {
5140
+ try {
5141
+ return task.creationDate().toISOString();
5142
+ } catch (_e) {
5143
+ return new Date().toISOString();
5144
+ }
5145
+ })(),
5146
+
5147
+ modifiedAt: (() => {
5148
+ try {
5149
+ return task.modificationDate().toISOString();
5150
+ } catch (_e) {
5151
+ return new Date().toISOString();
5152
+ }
5153
+ })(),
4719
5154
  };
4720
5155
  }
4721
5156
 
@@ -4767,29 +5202,1120 @@ function run(argv) {
4767
5202
 
4768
5203
  return JSON.stringify({ task: buildTask(found) });
4769
5204
  }
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
- ${n}`;return ["function run(_argv) {",' const ofApp = Application("OmniFocus");'," ofApp.includeStandardAdditions = false;",` const __omnijs = ${JSON.stringify(t)};`," const __result = ofApp.evaluateJavascript(__omnijs);"," return __result;","}"].join(`
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,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}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
- `);return `${t}<outline ${r.join(" ")}>
4774
- ${s}
4775
- ${t}</outline>`}function Ei(n,e,t){let{rootTasks:r,byParent:o}=Bt(e),a=[`text="${te(n.name)}"`,'type="omnifocus:project"',`id="${te(String(n.id))}"`,`status="${te(n.status)}"`];n.dueDate&&a.push(`due="${te(n.dueDate)}"`),n.deferDate&&a.push(`defer="${te(n.deferDate)}"`),n.flagged&&a.push('flagged="true"'),n.note&&a.push(`note="${te(n.note)}"`);let s=`${t} `;if(r.length===0)return `${t}<outline ${a.join(" ")} />`;let i=r.map(l=>Fi(l,o,s)).join(`
4776
- `);return `${t}<outline ${a.join(" ")}>
4777
- ${i}
4778
- ${t}</outline>`}function Z(n,e){let r=new RegExp(`\\b${e}=(?:"([^"]*)"|'([^']*)')`,"i").exec(n);if(!r)return;let o=r[1]??r[2]??"";return Nu(o)}function Nu(n){return n.replace(/&quot;/g,'"').replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&")}function Uu(n){let e=[],t=/<(!--[\s\S]*?--|[?!][^>]*|\/(\w[\w:-]*)\s*|(\w[\w:-]*)([^>]*?)(\/?))\s*>/g;for(let r=t.exec(n);r!==null;r=t.exec(n)){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 s=r[3];if(s){let i=(r[4]??"").trim(),l=(r[5]??"")==="/";e.push({kind:"open",tag:s.toLowerCase(),attrs:i,selfClose:l});}}return e}function Ni(n,e,t){let o={text:Z(t,"text")??"",children:[]},a=Z(t,"type");a!==void 0&&(o.type=a);let s=Z(t,"id");s!==void 0&&(o.id=s);let i=Z(t,"due");i!==void 0&&(o.due=i);let l=Z(t,"defer");l!==void 0&&(o.defer=l),Z(t,"flagged")==="true"&&(o.flagged=true);let c=e;for(;c<n.length;){let h=n[c];if(!h)break;if(h.kind==="close"&&h.tag==="outline")return [o,c+1];if(h.kind==="open"&&h.tag==="outline")if(h.selfClose){let m=Ui(h.attrs);o.children.push(m),c++;}else {let[m,g]=Ni(n,c+1,h.attrs);o.children.push(m),c=g;}else c++;}return [o,c]}function Ui(n){let t={text:Z(n,"text")??"",children:[]},r=Z(n,"type");r!==void 0&&(t.type=r);let o=Z(n,"id");o!==void 0&&(t.id=o);let a=Z(n,"due");a!==void 0&&(t.due=a);let s=Z(n,"defer");return s!==void 0&&(t.defer=s),Z(n,"flagged")==="true"&&(t.flagged=true),t}function Li(n){let e=n.trim();if(!/<opml\b/i.test(e))throw new I("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 I("Not valid OPML: missing <body> element.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});let t=Uu(e),r=-1;for(let s=0;s<t.length;s++){let i=t[s];if(i&&i.kind==="open"&&i.tag==="body"){r=s+1;break}}if(r===-1)throw new I("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<t.length;){let s=t[a];if(!s||s.kind==="close"&&s.tag==="body")break;if(s.kind==="open"&&s.tag==="outline")if(s.selfClose)o.push(Ui(s.attrs)),a++;else {let[i,l]=Ni(t,a+1,s.attrs);o.push(i),a=l;}else a++;}return {body:o}}function so(n,e,t,r,o){let a=" ".repeat(t),s=[];n.dueDate&&s.push(`@due(${n.dueDate.slice(0,10)})`),n.deferDate&&s.push(`@defer(${n.deferDate.slice(0,10)})`),n.flagged&&s.push("@flagged"),n.completed&&s.push("@done"),n.dropped&&s.push("@dropped");let i=s.length>0?` ${s.join(" ")}`:"";r.push(`${a}- ${n.name}${i}`);let l=n.note??(n.noteHtml?n.noteHtml.replace(/<[^>]*>/g,""):null);if(l){for(let c of l.split(`
4779
- `))c.trim()&&r.push(`${a} ${c}`);n.noteHtml&&!n.note&&o.push(`Task "${n.name}": HTML note downgraded to plain text`);}let u=e.get(String(n.id))??[];for(let c of u)so(c,e,t+1,r,o);}function Bi(n,e,t){let r=n,o,a,s=false,i=false,l=[];r=r.replace(/@due\(([^)]+)\)/g,(m,g)=>(o=Ji(g.trim(),e,t,"due"),"")),r=r.replace(/@defer\(([^)]+)\)/g,(m,g)=>(a=Ji(g.trim(),e,t,"defer"),"")),r=r.replace(/@flagged/g,()=>(s=true,"")),r=r.replace(/@done/g,()=>(i=true,"")),r=r.replace(/@dropped/g,()=>(i=true,"")),r=r.replace(/@([\w-]+)/g,(m,g)=>(g!=="due"&&g!=="defer"&&g!=="flagged"&&g!=="done"&&g!=="dropped"&&l.push(g),""));let u=r.split("//"),c=(u[0]??"").trim(),h=u[1]?u[1].trim():void 0;return c||t.push(`Line ${e}: empty task name after parsing tags \u2014 skipped`),{name:c||"(unnamed)",dueDate:o,deferDate:a,flagged:s,done:i,tagNames:l,note:h}}function Ji(n,e,t,r){if(/^\d{4}-\d{2}-\d{2}$/.test(n))return `${n}T00:00:00Z`;if(/^\d{4}-\d{2}-\d{2}T/.test(n))return n;t.push(`Line ${e}: unrecognised ${r} date format "${n}" \u2014 skipped`);}function Gi(n){let e=0;for(let t of n)if(t===" ")e++;else break;return e}var Gt=class{adapter;constructor(e){this.adapter=e.adapter;}async exportOpml(e){let t=await this.resolveProjects(e),r=await Promise.all(t.map(i=>this.adapter.listTasks({projectId:i.id}).then(l=>({project:i,tasks:l})))),o=r.map(({project:i,tasks:l})=>Ei(i,l," ")).join(`
4780
- `),a=r.reduce((i,{tasks:l})=>i+l.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(`
4781
- `),projectCount:t.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 t=await this.resolveProjects(e),r=[],o=[],a=0;for(let{project:s,tasks:i}of await Promise.all(t.map(l=>Mi(this.adapter,l.id).then(u=>({project:l,tasks:u}))))){let{rootTasks:l,byParent:u}=Bt(i);if(o.push(`${s.name}:`),s.note)for(let c of s.note.split(`
4782
- `))o.push(` ${c}`);for(let c of l)so(c,u,1,o,r);a+=i.length,o.push("");}return {taskpaper:o.join(`
4783
- `),projectCount:t.length,taskCount:a,warnings:r}}async importTaskPaper(e,t){if(!e.trim())throw new I("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 b=g.toLowerCase(),w=o.get(b);if(w)return w;let O=await this.adapter.createTag({name:g});return o.set(b,O),O},s=await this.adapter.listProjects(),i=new Map(s.map(g=>[g.name.toLowerCase(),g.id])),l=[],u=[],c=[],h=t,m=e.split(`
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
- `),process.exit(0));(Yt.includes("--help")||Yt.includes("-h"))&&(process.stdout.write(`${re.name} v${re.version}
4786
- ${re.description}
5205
+ `;var dm=`/**
5206
+ * JXA: read the current state of OmniFocus's front window.
5207
+ *
5208
+ * Returns JSON: { perspectiveName, focusContainerIds }
5209
+ * - \`perspectiveName\` \u2014 name of the active perspective (e.g. "Forecast")
5210
+ * - \`focusContainerIds[]\` \u2014 IDs of projects/folders the window is focused on
5211
+ * (empty array when nothing is focused)
5212
+ * or { error: { code: "NO_FRONT_WINDOW", message } } when OF has no front window
5213
+ *
5214
+ * @see #466
5215
+ * @see src/adapter/jxa/JxaTransport.ts \u2014 getWindowState() caller
5216
+ */
5217
+
5218
+ // biome-ignore lint/correctness/noUnusedVariables: osascript invokes run() by convention.
5219
+ function run(_argv) {
5220
+ const ofApp = Application("OmniFocus");
5221
+ ofApp.includeStandardAdditions = false;
5222
+
5223
+ const wins = ofApp.windows();
5224
+ if (!wins || wins.length === 0) {
5225
+ return JSON.stringify({
5226
+ error: { code: "NO_FRONT_WINDOW", message: "OmniFocus has no front window" },
5227
+ });
5228
+ }
5229
+
5230
+ const w = wins[0];
5231
+ let perspectiveName = null;
5232
+ try {
5233
+ perspectiveName = String(w.perspectiveName());
5234
+ } catch (_e) {
5235
+ perspectiveName = null;
5236
+ }
5237
+
5238
+ // Focus is a (possibly empty) array of containers (sections / projects /
5239
+ // folders). We surface the IDs in input order; \`[]\` means nothing is focused.
5240
+ const focusContainerIds = [];
5241
+ try {
5242
+ const focus = w.focus();
5243
+ if (focus && focus.length) {
5244
+ for (let i = 0; i < focus.length; i++) {
5245
+ try {
5246
+ focusContainerIds.push(String(focus[i].id()));
5247
+ } catch (_e) {
5248
+ // Skip containers we can't introspect (defensive \u2014 shouldn't happen).
5249
+ }
5250
+ }
5251
+ }
5252
+ } catch (_e) {
5253
+ // No focus set \u2014 leave focusContainerIds as []
5254
+ }
5255
+
5256
+ return JSON.stringify({ perspectiveName, focusContainerIds });
5257
+ }
5258
+ `;var lm=`/**
5259
+ * JXA: set or clear the front window's focus container.
5260
+ *
5261
+ * Args (argv[0] JSON): { containerId: string | null }
5262
+ * - \`containerId\` non-null \u2192 set focus to that project or folder
5263
+ * - \`containerId\` null \u2192 clear focus (window shows the perspective's
5264
+ * default scope)
5265
+ *
5266
+ * Returns JSON: { focusContainerIds }
5267
+ * - When set: array containing the supplied containerId (so callers can
5268
+ * verify in one round-trip)
5269
+ * - When cleared: empty array
5270
+ * or { error: { code: "NO_FRONT_WINDOW" | "NOT_FOUND", message } }
5271
+ *
5272
+ * Container resolution: tries projects first, then folders. If the same ID
5273
+ * matches both (shouldn't happen but defensive), the project wins.
5274
+ *
5275
+ * @see #466
5276
+ * @see src/adapter/jxa/JxaTransport.ts \u2014 setWindowFocus() caller
5277
+ */
5278
+
5279
+ // biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
5280
+ function run(argv) {
5281
+ const args = JSON.parse(argv[0]);
5282
+ const ofApp = Application("OmniFocus");
5283
+ ofApp.includeStandardAdditions = false;
5284
+
5285
+ const wins = ofApp.windows();
5286
+ if (!wins || wins.length === 0) {
5287
+ return JSON.stringify({
5288
+ error: { code: "NO_FRONT_WINDOW", message: "OmniFocus has no front window" },
5289
+ });
5290
+ }
5291
+
5292
+ const w = wins[0];
5293
+
5294
+ if (args.containerId === null || args.containerId === undefined) {
5295
+ // Clear focus \u2014 assigning an empty array unfocuses the window.
5296
+ w.focus = [];
5297
+ return JSON.stringify({ focusContainerIds: [] });
5298
+ }
5299
+
5300
+ // Resolve to a project or folder.
5301
+ const doc = ofApp.defaultDocument;
5302
+ let target = null;
5303
+
5304
+ const projects = doc.flattenedProjects();
5305
+ for (let i = 0; i < projects.length; i++) {
5306
+ if (projects[i].id() === args.containerId) {
5307
+ target = projects[i];
5308
+ break;
5309
+ }
5310
+ }
5311
+
5312
+ if (!target) {
5313
+ const folders = doc.flattenedFolders();
5314
+ for (let i = 0; i < folders.length; i++) {
5315
+ if (folders[i].id() === args.containerId) {
5316
+ target = folders[i];
5317
+ break;
5318
+ }
5319
+ }
5320
+ }
5321
+
5322
+ if (!target) {
5323
+ return JSON.stringify({
5324
+ error: {
5325
+ code: "NOT_FOUND",
5326
+ message: \`Container not found (project or folder): \${args.containerId}\`,
5327
+ },
5328
+ });
5329
+ }
5330
+
5331
+ w.focus = [target];
5332
+ return JSON.stringify({ focusContainerIds: [args.containerId] });
5333
+ }
5334
+ `;var pm=`/**
5335
+ * JXA: switch the front window to a named perspective.
5336
+ *
5337
+ * Args (argv[0] JSON): { perspectiveName: string }
5338
+ * Returns JSON: { perspectiveName }
5339
+ * or { error: { code: "NO_FRONT_WINDOW" | "NOT_FOUND", message } }
5340
+ *
5341
+ * Built-in perspectives (Inbox, Projects, Tags, Forecast, Flagged, Review, etc.)
5342
+ * and custom perspectives both work \u2014 JXA \`Window.perspective\` accepts either.
5343
+ *
5344
+ * @see #466
5345
+ * @see src/adapter/jxa/JxaTransport.ts \u2014 setWindowPerspective() caller
5346
+ */
5347
+
5348
+ // biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
5349
+ function run(argv) {
5350
+ const args = JSON.parse(argv[0]);
5351
+ const ofApp = Application("OmniFocus");
5352
+ ofApp.includeStandardAdditions = false;
5353
+
5354
+ const wins = ofApp.windows();
5355
+ if (!wins || wins.length === 0) {
5356
+ return JSON.stringify({
5357
+ error: { code: "NO_FRONT_WINDOW", message: "OmniFocus has no front window" },
5358
+ });
5359
+ }
5360
+
5361
+ const w = wins[0];
5362
+
5363
+ // Look up the perspective by name. Both built-in and custom perspectives
5364
+ // appear in \`perspectives()\`. We match exact name \u2014 case-sensitive, matches
5365
+ // OF's own UX.
5366
+ const all = ofApp.perspectives();
5367
+ let target = null;
5368
+ for (let i = 0; i < all.length; i++) {
5369
+ if (all[i].name() === args.perspectiveName) {
5370
+ target = all[i];
5371
+ break;
5372
+ }
5373
+ }
5374
+ if (!target) {
5375
+ return JSON.stringify({
5376
+ error: {
5377
+ code: "NOT_FOUND",
5378
+ message: \`Perspective not found: \${args.perspectiveName}\`,
5379
+ },
5380
+ });
5381
+ }
5382
+
5383
+ w.perspective = target;
5384
+ return JSON.stringify({ perspectiveName: args.perspectiveName });
5385
+ }
5386
+ `;var mm=new AsyncLocalStorage;function fm(t,e){let n=ulid();return mm.run(n,t)}function Ae(){return mm.getStore()}function gm(){return ulid()}function pv(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 Un(t,e,n,r,o){C.debug({event:"transport.call",transport:t,scriptName:e,argsHash:pv(n),durationMs:r,outcome:o,correlationId:Ae()},"transport call");}var mv=16*1024*1024,fv=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-",e],{timeout:n,maxBuffer:mv,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,i,s)=>{let c=i,l=s,d=a!==null&&a.killed===true,m=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:l,exitCode:a===null?0:a.code??1,timedOut:d,...m!==void 0?{spawnError:m}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");});async function _(t,e={},n={}){let r=n.spawner??fv,o=n.timeoutMs??3e4,a=JSON.stringify(e??{}),i=n.scriptName,s=performance.now(),c=await r(t,a,o),l=Math.round(performance.now()-s),d=c.spawnError!==void 0||c.timedOut||c.exitCode!==0||c.stdout.trim()===""?"error":"ok";if(Un("jxa",i,e,l,d),c.spawnError!==void 0)throw new et("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 Ze(`JXA script exceeded ${o}ms timeout${f}`,{details:{transport:"jxa",timeoutMs:o,...i!==void 0?{scriptName:i}:{}}})}if(c.exitCode!==0){let f=gv(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:ze(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:ze(m,200),...i!==void 0?{scriptName:i}:{}}})}}function gv(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 Ke({details:{transport:"jxa",stderr:ze(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 Xe({details:{transport:"jxa",stderr:ze(t,512),...e!==void 0?{scriptName:e}:{}}}):/\bnot found\b/i.test(t)||/^OF_NOT_FOUND\b/m.test(t)?new O(t,{details:{transport:"jxa",stderr:ze(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_VALIDATION\b/m.test(t)||/\bValidationError:/m.test(t)||/\bis required\b/i.test(t)?new I(t,{details:{transport:"jxa",stderr:ze(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_CONFLICT\b/m.test(t)?new Qe(t,{details:{transport:"jxa",stderr:ze(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function ze(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function qs(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"}var Ln=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 _(rm,{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 _(tm,{id:e},{...this.runOpts,scriptName:"task_get"});return {...n.task,id:h.of(n.task.id)}}async getTasksMany(e){return (await _(nm,{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 _(Yu,{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 _(cm,{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 _(Xu,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"task_complete"});}async uncompleteTask(e){await _(sm,{id:e},{...this.runOpts,scriptName:"task_uncomplete"});}async dropTask(e,n){await _(Zu,{id:e,droppedAt:n?.toISOString()??null},{...this.runOpts,scriptName:"task_drop"});}async undropTask(e){await _(im,{id:e},{...this.runOpts,scriptName:"task_undrop"});}async deleteTask(e){await _(Qu,{id:e},{...this.runOpts,scriptName:"task_delete"});}async moveTask(e,n){await _(om,{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 _(em,{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 _(Hu,{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 Te(n,h.of)}async batchUpdateTasks(e){let n=await _(Ku,{updates:e},{...this.runOpts,scriptName:"task_batch_update"});return Te(n,h.of)}async batchCompleteTasks(e){let n=await _(Wu,{items:e.map(r=>({id:r.id,at:r.at?.toISOString()??null}))},{...this.runOpts,scriptName:"task_batch_complete"});return Te(n,h.of)}async batchUncompleteTasks(e){let n=await _(Vu,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_uncomplete"});return Te(n,h.of)}async batchDeleteTasks(e){let n=await _(Gu,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_delete"});return Te(n,h.of)}async batchDropTasks(e){let n=await _(zu,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_drop"});return Te(n,h.of)}async batchUndropTasks(e){let n=await _(qu,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_undrop"});return Te(n,h.of)}async listProjects(e){return (await _(xu,{folderId:e?.folderId??null,status:e?.status??null},{...this.runOpts,scriptName:"project_list"})).projects.map(r=>({...r,id:y.of(r.id)}))}async getProject(e){let n=await _(Pu,{id:e},{...this.runOpts,scriptName:"project_get"});return {...n.project,id:y.of(n.project.id)}}async getProjectsMany(e){return (await _(Ou,{ids:e},{...this.runOpts,scriptName:"project_get_many"})).projects.map(r=>r?{...r,id:y.of(r.id)}:null)}async createProject(e){let n=await _(ju,{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 y.of(n.project.id)}async updateProject(e,n){await _(Mu,{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 _(Su,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"project_complete"});}async dropProject(e){await _(_u,{id:e},{...this.runOpts,scriptName:"project_drop"});}async batchCompleteProjects(e){let n=await _(vu,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_complete"});return Te(n,y.of)}async batchDropProjects(e){let n=await _(wu,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_drop"});return Te(n,y.of)}async moveProject(e,n){await _(Au,{id:e,folderId:n.folderId??null},{...this.runOpts,scriptName:"project_move"});}async deleteProject(e){await _(bu,{id:e},{...this.runOpts,scriptName:"project_delete"});}async markProjectReviewed(e){await _(Du,{id:e},{...this.runOpts,scriptName:"project_mark_reviewed"});}async listProjectsDueForReview(){return (await _(Fu,{},{...this.runOpts,scriptName:"review_list_due"})).projects}async setProjectReviewInterval(e,n){await _(Ru,{id:e,days:n},{...this.runOpts,scriptName:"project_set_review_interval"});}async setProjectNextReviewDate(e,n){await _(Cu,{id:e,nextReviewDate:n},{...this.runOpts,scriptName:"project_set_next_review_date"});}async listTags(e){return (await _(Bu,{parentId:e?.parentId??null,status:e?.status??null},{...this.runOpts,scriptName:"tag_list"})).tags.map(r=>({...r,id:w.of(r.id)}))}async getTag(e){let n=await _(Lu,{id:e},{...this.runOpts,scriptName:"tag_get"});return {...n.tag,id:w.of(n.tag.id)}}async getTagsMany(e){return (await _(Ju,{ids:e},{...this.runOpts,scriptName:"tag_get_many"})).tags.map(r=>r?{...r,id:w.of(r.id)}:null)}async createTag(e){let n=await _(Nu,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"tag_create"});return w.of(n.tag.id)}async updateTag(e,n){await _($u,{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 _(Uu,{id:e},{...this.runOpts,scriptName:"tag_delete"});}async listFolders(e){return (await _(hu,{parentId:e?.parentId??null},{...this.runOpts,scriptName:"folder_list"})).folders.map(r=>({...r,id:J.of(r.id)}))}async getFolder(e){let n=await _(gu,{id:e},{...this.runOpts,scriptName:"folder_get"});return {...n.folder,id:J.of(n.folder.id)}}async createFolder(e){let n=await _(mu,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"folder_create"});return J.of(n.folder.id)}async updateFolder(e,n){await _(yu,{id:e,...n.name!==void 0?{name:n.name}:{}},{...this.runOpts,scriptName:"folder_update"});}async deleteFolder(e){await _(fu,{id:e},{...this.runOpts,scriptName:"folder_delete"});}async listPerspectives(){return (await _(Tu,{},{...this.runOpts,scriptName:"perspective_list"})).perspectives}async evaluatePerspective(e){return (await _(ku,{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 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 searchTasks(e){return (await _(am,{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 _(Iu,{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 _(du,e,{...this.runOpts,scriptName:"attachment_list"})).attachments}async addAttachment(e){let n=await _(cu,e,{...this.runOpts,scriptName:"attachment_add"});return ve.of(n.id)}async removeAttachment(e){await _(lu,e,{...this.runOpts,scriptName:"attachment_remove"});}async saveAttachmentToPath(e){return _(pu,e,{...this.runOpts,scriptName:"attachment_save_to_path"})}async appLaunch(){return _(iu,{},{...this.runOpts,scriptName:"app_launch"})}async getWindowState(){let e=await _(dm,{},{...this.runOpts,scriptName:"window_get_state"});if(qs(e))throw new tt(e.error.message,{details:{transport:"jxa",scriptName:"window_get_state"}});return e}async setWindowPerspective(e){let n=await _(pm,{perspectiveName:e},{...this.runOpts,scriptName:"window_set_perspective"});if(qs(n))throw n.error.code==="NO_FRONT_WINDOW"?new tt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}}):new O(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}});return n}async setWindowFocus(e){let n=await _(lm,{containerId:e},{...this.runOpts,scriptName:"window_set_focus"});if(qs(n))throw n.error.code==="NO_FRONT_WINDOW"?new tt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}}):new O(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 _(Eu,{},{...this.runOpts,scriptName:"sync_trigger"})}async getLastSync(){return {lastSyncAt:null,inFlight:false}}async getChangesSince(e){let n=await _(uu,{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 _(e,n??{},{...this.runOpts,scriptName:"raw"})}};var hm=`/**
5387
+ * OmniJS: open a new OmniFocus window.
5388
+ *
5389
+ * Wraps \`document.newWindow()\`. The new window opens in the foreground with
5390
+ * the app's default perspective. No args.
5391
+ *
5392
+ * Returns JSON: { perspectiveName: string | null, focusContainerIds: [] }
5393
+ *
5394
+ * @see #527
5395
+ */
5396
+ (() => {
5397
+ try {
5398
+ const w = document.newWindow();
5399
+ const perspectiveName = w.perspective ? w.perspective.name : null;
5400
+ return JSON.stringify({ perspectiveName, focusContainerIds: [] });
5401
+ } catch (e) {
5402
+ const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
5403
+ return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
5404
+ }
5405
+ })();
5406
+ `;var ym=`/**
5407
+ * OmniJS: add a new tab to the front OmniFocus window.
5408
+ *
5409
+ * Wraps \`document.newTabOnWindow(window)\` using the front window (index 0).
5410
+ * The new tab opens with the app's default perspective.
5411
+ *
5412
+ * Args injected as \`globalThis.__args\`: {} (no input for now)
5413
+ *
5414
+ * Returns JSON: { perspectiveName: string | null, focusContainerIds: [] }
5415
+ *
5416
+ * @see #527
5417
+ */
5418
+ (() => {
5419
+ try {
5420
+ const windows = document.windows;
5421
+ if (!windows || windows.length === 0) {
5422
+ return JSON.stringify({
5423
+ error: { code: "WINDOW_UNAVAILABLE", message: "No open OmniFocus window" },
5424
+ });
5425
+ }
5426
+ const frontWindow = windows[0];
5427
+ const tab = document.newTabOnWindow(frontWindow);
5428
+ const perspectiveName = tab.perspective ? tab.perspective.name : null;
5429
+ return JSON.stringify({ perspectiveName, focusContainerIds: [] });
5430
+ } catch (e) {
5431
+ const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
5432
+ return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
5433
+ }
5434
+ })();
5435
+ `;var Im=`/**
5436
+ * OmniJS: redo the most recently undone mutation.
5437
+ *
5438
+ * Wraps \`Database.redo()\`. Behaves identically to \u2318\u21E7Z in the OmniFocus UI:
5439
+ * advances one entry on the document's redo stack. Any mutation between
5440
+ * undo and redo invalidates the redo stack (matching UI semantics).
5441
+ *
5442
+ * Args injected as \`globalThis.__args\`: {} (no input)
5443
+ *
5444
+ * Returns JSON: { redid: boolean }
5445
+ * - \`true\` \u2014 an entry was redone
5446
+ * - \`false\` \u2014 redo stack was empty
5447
+ *
5448
+ * @see #526
5449
+ */
5450
+ (() => {
5451
+ try {
5452
+ Database.redo();
5453
+ return JSON.stringify({ redid: true });
5454
+ } catch (e) {
5455
+ const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
5456
+ if (/nothing to redo|empty/i.test(msg)) {
5457
+ return JSON.stringify({ redid: false });
5458
+ }
5459
+ return JSON.stringify({ error: { code: "REDO_FAILED", message: msg } });
5460
+ }
5461
+ })();
5462
+ `;var km=`/**
5463
+ * OmniJS: undo the most recent document mutation.
5464
+ *
5465
+ * Wraps \`Database.undo()\`. Behaves identically to \u2318Z in the OmniFocus UI:
5466
+ * walks back one entry on the document's undo stack, regardless of whether
5467
+ * the mutation was issued by the MCP or another caller (manual edit, sync
5468
+ * replay, etc.). The agent contract is documented at the tool layer.
5469
+ *
5470
+ * Args injected as \`globalThis.__args\`: {} (no input)
5471
+ *
5472
+ * Returns JSON: { undid: boolean }
5473
+ * - \`true\` \u2014 an entry was undone
5474
+ * - \`false\` \u2014 undo stack was empty (nothing to undo)
5475
+ *
5476
+ * @see #526
5477
+ */
5478
+ (() => {
5479
+ // Database.undo() returns void in the OmniJS docs but throws when the
5480
+ // stack is empty in some versions. Belt-and-suspenders: catch and report
5481
+ // the empty-stack case as \`undid: false\` rather than propagating.
5482
+ try {
5483
+ Database.undo();
5484
+ return JSON.stringify({ undid: true });
5485
+ } catch (e) {
5486
+ const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
5487
+ if (/nothing to undo|empty/i.test(msg)) {
5488
+ return JSON.stringify({ undid: false });
5489
+ }
5490
+ return JSON.stringify({ error: { code: "UNDO_FAILED", message: msg } });
5491
+ }
5492
+ })();
5493
+ `;var Tm=`/**
5494
+ * OmniJS: read the forecast-tag preference (the tag whose tasks always
5495
+ * appear on the Forecast view alongside dated items).
5496
+ *
5497
+ * Args injected as \`globalThis.__args\`: {} (no input)
5498
+ *
5499
+ * Returns JSON: { tagId: string | null }
5500
+ * - \`tagId\` is the configured forecast tag's primary-key string when set
5501
+ * - \`null\` when no forecast tag is configured (fresh OF install or cleared)
5502
+ *
5503
+ * @see #465
5504
+ */
5505
+ (() => {
5506
+ // OmniJS exposes the preference as \`Database.forecastTag\`. Older docs
5507
+ // reference \`Database.forecastTagID\`; the property here is the actual Tag
5508
+ // object (or null), so we read \`.id.primaryKey\` from it.
5509
+ const tag = Database.forecastTag;
5510
+ return JSON.stringify({ tagId: tag === null ? null : tag.id.primaryKey });
5511
+ })();
5512
+ `;var vm=`/**
5513
+ * OmniJS: set or clear the forecast-tag preference.
5514
+ *
5515
+ * Args injected as \`globalThis.__args\`:
5516
+ * { tagId: string | null }
5517
+ * - \`tagId\` non-null \u2192 set Database.forecastTag = <that tag>
5518
+ * - \`tagId\` null \u2192 clear (Database.forecastTag = null)
5519
+ *
5520
+ * Returns JSON: { tagId: string | null } \u2014 echoes what was applied
5521
+ * or { error: { code, message } } on failure
5522
+ *
5523
+ * @see #465
5524
+ */
5525
+ (() => {
5526
+ const { tagId } = globalThis.__args;
5527
+
5528
+ if (tagId === null || tagId === undefined) {
5529
+ Database.forecastTag = null;
5530
+ return JSON.stringify({ tagId: null });
5531
+ }
5532
+
5533
+ if (typeof tagId !== "string") {
5534
+ return JSON.stringify({
5535
+ error: { code: "VALIDATION", message: "tagId must be a string or null" },
5536
+ });
5537
+ }
5538
+
5539
+ const tag = flattenedTags.filter((t) => t.id.primaryKey === tagId)[0];
5540
+ if (!tag) {
5541
+ return JSON.stringify({
5542
+ error: { code: "NOT_FOUND", message: \`Tag not found: \${tagId}\` },
5543
+ });
5544
+ }
5545
+
5546
+ Database.forecastTag = tag;
5547
+ return JSON.stringify({ tagId });
5548
+ })();
5549
+ `;var wm=`/**
5550
+ * perspective_delete.js \u2014 delete a custom OmniFocus perspective by identifier.
5551
+ *
5552
+ * Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
5553
+ * { identifier: string }
5554
+ *
5555
+ * Returns JSON: \`{ id: string }\` on success (echoes the deleted identifier),
5556
+ * or \`{ error: { code, message } }\` for typed failures.
5557
+ *
5558
+ * Built-in perspectives cannot be deleted \u2014 \`Perspective.Custom.byIdentifier\`
5559
+ * returns null for them, which the script reports as NOT_FOUND. JXA cannot
5560
+ * delete custom perspectives; only OmniJS \`deleteObject\` works.
5561
+ *
5562
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 deleteCustomPerspective()
5563
+ */
5564
+ (() => {
5565
+ const { identifier } = globalThis.__args;
5566
+
5567
+ if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
5568
+ return JSON.stringify({
5569
+ error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
5570
+ });
5571
+ }
5572
+
5573
+ const persp = Perspective.Custom.byIdentifier(identifier);
5574
+ if (persp === null || persp === undefined) {
5575
+ return JSON.stringify({
5576
+ error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
5577
+ });
5578
+ }
5579
+
5580
+ try {
5581
+ deleteObject(persp);
5582
+ } catch (e) {
5583
+ return JSON.stringify({
5584
+ error: { code: "SCRIPT_ERROR", message: String(e && e.message ? e.message : e) },
5585
+ });
5586
+ }
5587
+
5588
+ return JSON.stringify({ id: identifier });
5589
+ })();
5590
+ `;var Sm=`/**
5591
+ * perspective_evaluate.js \u2014 evaluate a custom OmniFocus perspective and
5592
+ * return its task list as domain \`Task\` objects.
5593
+ *
5594
+ * Called via the OmniJS transport (evaluateJavascript bridge).
5595
+ * Args injected as \`globalThis.__args\`:
5596
+ * { identifier: string }
5597
+ *
5598
+ * Returns a JSON string: { tasks: Task[] } on success, or a JSON error
5599
+ * envelope \`{ error: { code, message } }\` for typed failures (Pro-gating,
5600
+ * unknown perspective identifier) that the caller maps to typed errors.
5601
+ *
5602
+ * Evaluation strategy (per Omni Automation): the perspective is set on the
5603
+ * front document window, and the resulting \`content.rootNode.descendants\`
5604
+ * are walked to collect every \`Task\` object. This mirrors what the user
5605
+ * would see if they switched to the perspective in the UI \u2014 the only API
5606
+ * surface OmniFocus exposes for perspective evaluation.
5607
+ *
5608
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 evaluateCustomPerspective()
5609
+ * @see src/domain/task.ts \u2014 Task domain type
5610
+ * @see docs/adr/0005-scripts-as-first-class-files.md
5611
+ */
5612
+ (() => {
5613
+ const { identifier } = globalThis.__args;
5614
+
5615
+ if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
5616
+ return JSON.stringify({
5617
+ error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
5618
+ });
5619
+ }
5620
+
5621
+ const persp = Perspective.Custom.byIdentifier(identifier);
5622
+ if (persp === null || persp === undefined) {
5623
+ return JSON.stringify({
5624
+ error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
5625
+ });
5626
+ }
5627
+
5628
+ const win = document.windows[0];
5629
+ win.perspective = persp;
5630
+
5631
+ function isoOrNull(d) {
5632
+ return d ? d.toISOString() : null;
5633
+ }
5634
+
5635
+ function buildRepetition(task) {
5636
+ try {
5637
+ const rr = task.repetitionRule;
5638
+ if (!rr) return null;
5639
+ return { method: String(rr.method), unit: String(rr.unit), steps: rr.steps };
5640
+ } catch (_e) {
5641
+ return null;
5642
+ }
5643
+ }
5644
+
5645
+ function buildTask(task) {
5646
+ const tagIds = [];
5647
+ try {
5648
+ const tags = task.tags;
5649
+ for (let i = 0; i < tags.length; i++) tagIds.push(tags[i].id.primaryKey);
5650
+ } catch (_e) {}
5651
+
5652
+ let projectId = null;
5653
+ try {
5654
+ if (task.containingProject) projectId = task.containingProject.id.primaryKey;
5655
+ } catch (_e) {}
5656
+
5657
+ let parentId = null;
5658
+ try {
5659
+ if (task.parent && task.parent instanceof Task) parentId = task.parent.id.primaryKey;
5660
+ } catch (_e) {}
5661
+
5662
+ return {
5663
+ id: task.id.primaryKey,
5664
+ name: task.name,
5665
+ note: task.note || null,
5666
+ noteHtml: null,
5667
+ projectId,
5668
+ parentId,
5669
+ tagIds,
5670
+ deferDate: isoOrNull(task.deferDate),
5671
+ dueDate: isoOrNull(task.dueDate),
5672
+ estimatedMinutes: task.estimatedMinutes ?? null,
5673
+ flagged: !!task.flagged,
5674
+ completed: !!task.completed,
5675
+ completedAt: isoOrNull(task.completionDate),
5676
+ dropped: !!task.dropped,
5677
+ droppedAt: isoOrNull(task.dropDate),
5678
+ available: !task.blocked && !task.completed && !task.dropped,
5679
+ blocked: !!task.blocked,
5680
+ sequential: !!task.sequential,
5681
+ completedByChildren: !!task.completedByChildren,
5682
+ repetition: buildRepetition(task),
5683
+ createdAt: isoOrNull(task.added) ?? new Date().toISOString(),
5684
+ modifiedAt: isoOrNull(task.modified) ?? new Date().toISOString(),
5685
+ };
5686
+ }
5687
+
5688
+ const tasks = [];
5689
+ const seen = new Set();
5690
+ const walk = (node) => {
5691
+ if (!node) return;
5692
+ try {
5693
+ const obj = node.object;
5694
+ if (obj instanceof Task && !seen.has(obj.id.primaryKey)) {
5695
+ seen.add(obj.id.primaryKey);
5696
+ tasks.push(buildTask(obj));
5697
+ }
5698
+ } catch (_e) {}
5699
+ const children = node.children || [];
5700
+ for (let i = 0; i < children.length; i++) walk(children[i]);
5701
+ };
5702
+ walk(win.content.rootNode);
5703
+
5704
+ return JSON.stringify({ tasks });
5705
+ })();
5706
+ `;var jm=`/**
5707
+ * perspective_get.js \u2014 read a custom OmniFocus perspective's full configuration.
5708
+ *
5709
+ * Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
5710
+ * { identifier: string }
5711
+ *
5712
+ * Returns JSON on success:
5713
+ * { perspective: { id, name, aggregation, rules, iconColor } }
5714
+ *
5715
+ * Or a typed error envelope:
5716
+ * { error: { code, message } }
5717
+ *
5718
+ * Built-in perspectives are not supported here \u2014 this surface only exposes
5719
+ * the rich rule-tree for custom perspectives. The caller (router) must
5720
+ * gate on \`kind: "custom"\` from \`perspective_list\` before calling.
5721
+ *
5722
+ * Rule serialization: \`archivedFilterRules\` is already a plain-JS array of
5723
+ * rule atoms. Walk it recursively, copying only known own-property keys so
5724
+ * the output is stable across OmniFocus versions and JSON-safe.
5725
+ *
5726
+ * iconColor: OmniJS exposes \`Color\` objects with \`red\`/\`green\`/\`blue\`/\`alpha\`
5727
+ * accessors; serialize as \`{ r, g, b, a }\` in [0, 1] floats, or null when
5728
+ * the perspective has no custom color.
5729
+ *
5730
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 getCustomPerspective()
5731
+ * @see src/domain/perspective.ts \u2014 PerspectiveDetail
5732
+ */
5733
+ (() => {
5734
+ const { identifier } = globalThis.__args;
5735
+
5736
+ if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
5737
+ return JSON.stringify({
5738
+ error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
5739
+ });
5740
+ }
5741
+
5742
+ const persp = Perspective.Custom.byIdentifier(identifier);
5743
+ if (persp === null || persp === undefined) {
5744
+ return JSON.stringify({
5745
+ error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
5746
+ });
5747
+ }
5748
+
5749
+ // Rule atom keys observed on real OmniFocus perspectives. Unknown keys are
5750
+ // preserved verbatim under \`unknown\` so a future OF release exposing a new
5751
+ // rule type doesn't silently drop data.
5752
+ const KNOWN_ATOM_KEYS = [
5753
+ "actionAvailability",
5754
+ "actionStatus",
5755
+ "actionHasAllOfTags",
5756
+ "actionHasAnyOfTags",
5757
+ "actionHasNoProject",
5758
+ "actionHasDueDate",
5759
+ "actionHasDeferDate",
5760
+ "actionIsLeaf",
5761
+ "actionIsProject",
5762
+ "actionMatchingSearch",
5763
+ "actionWithinFocus",
5764
+ ];
5765
+
5766
+ function serializeRule(rule) {
5767
+ if (rule === null || rule === undefined || typeof rule !== "object") {
5768
+ return null;
5769
+ }
5770
+ // Disabled-rule wrapper.
5771
+ if (Object.hasOwn(rule, "disabledRule")) {
5772
+ const inner = serializeRule(rule.disabledRule);
5773
+ return inner === null ? null : { disabledRule: inner };
5774
+ }
5775
+ // Aggregate (compound) rule.
5776
+ if (Object.hasOwn(rule, "aggregateType")) {
5777
+ const children = Array.isArray(rule.aggregateRules) ? rule.aggregateRules : [];
5778
+ return {
5779
+ aggregateType: String(rule.aggregateType),
5780
+ aggregateRules: children.map(serializeRule).filter((r) => r !== null),
5781
+ };
5782
+ }
5783
+ // Atom rule \u2014 copy known keys; preserve unknown keys under \`unknown\`.
5784
+ const out = {};
5785
+ const unknown = {};
5786
+ let hasUnknown = false;
5787
+ for (const key of Object.keys(rule)) {
5788
+ const value = rule[key];
5789
+ if (KNOWN_ATOM_KEYS.indexOf(key) >= 0) {
5790
+ out[key] = value;
5791
+ } else {
5792
+ unknown[key] = value;
5793
+ hasUnknown = true;
5794
+ }
5795
+ }
5796
+ if (hasUnknown) out.unknown = unknown;
5797
+ return out;
5798
+ }
5799
+
5800
+ function serializeColor(c) {
5801
+ if (c === null || c === undefined) return null;
5802
+ try {
5803
+ const r = typeof c.red === "number" ? c.red : null;
5804
+ const g = typeof c.green === "number" ? c.green : null;
5805
+ const b = typeof c.blue === "number" ? c.blue : null;
5806
+ const a = typeof c.alpha === "number" ? c.alpha : 1;
5807
+ if (r === null || g === null || b === null) return null;
5808
+ return { r, g, b, a };
5809
+ } catch (_e) {
5810
+ return null;
5811
+ }
5812
+ }
5813
+
5814
+ let rawRules;
5815
+ try {
5816
+ rawRules = persp.archivedFilterRules;
5817
+ } catch (_e) {
5818
+ rawRules = null;
5819
+ }
5820
+ const rules = Array.isArray(rawRules)
5821
+ ? rawRules.map(serializeRule).filter((r) => r !== null)
5822
+ : [];
5823
+
5824
+ let aggregation = null;
5825
+ try {
5826
+ const a = persp.archivedTopLevelFilterAggregation;
5827
+ aggregation = a === null || a === undefined ? "all" : String(a);
5828
+ } catch (_e) {
5829
+ aggregation = "all";
5830
+ }
5831
+
5832
+ let iconColor = null;
5833
+ try {
5834
+ iconColor = serializeColor(persp.iconColor);
5835
+ } catch (_e) {
5836
+ iconColor = null;
5837
+ }
5838
+
5839
+ let id;
5840
+ try {
5841
+ id = persp.identifier;
5842
+ } catch (_e) {
5843
+ id = identifier;
5844
+ }
5845
+
5846
+ return JSON.stringify({
5847
+ perspective: {
5848
+ id: String(id),
5849
+ name: String(persp.name),
5850
+ aggregation,
5851
+ rules,
5852
+ iconColor,
5853
+ },
5854
+ });
5855
+ })();
5856
+ `;var bm=`/**
5857
+ * plugin_invoke.js \u2014 invoke a named Omni Automation plug-in action.
5858
+ *
5859
+ * Called via the OmniJS transport (evaluateJavascript bridge).
5860
+ * Args injected as \`globalThis.__args\`:
5861
+ * { identifier: string, arg?: unknown }
5862
+ *
5863
+ * Returns a JSON string: { result: <plug-in return value> }
5864
+ *
5865
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 pluginInvoke()
5866
+ * @see docs/adr/0005-scripts-as-first-class-files.md
5867
+ */
5868
+ (() => {
5869
+ const { identifier, arg = null } = globalThis.__args;
5870
+
5871
+ const plugin = PlugIn.find(identifier);
5872
+ if (plugin === null) {
5873
+ throw new Error(\`PlugIn not found: \${identifier}\`);
5874
+ }
5875
+
5876
+ // The PlugIn.Action runtime is available from OmniFocus Standard+.
5877
+ // \`plugin.action(identifier)\` returns the default action when no name is
5878
+ // supplied; per the Omni Automation spec the default action is the one
5879
+ // whose \`name\` matches the plug-in's bundle identifier.
5880
+ const action = plugin.action(identifier);
5881
+ if (action === null) {
5882
+ throw new Error(\`No default action found in PlugIn: \${identifier}\`);
5883
+ }
5884
+
5885
+ const rawResult = action.perform([arg]);
5886
+ return JSON.stringify({ result: rawResult ?? null });
5887
+ })();
5888
+ `;var _m=`/**
5889
+ * OmniJS: batch-move tasks to different destinations in a single round-trip.
5890
+ *
5891
+ * JXA's \`task.move()\` is unimplemented in OmniFocus 4.x (error 9 "Replacement
5892
+ * not supported currently"). The OmniJS \`moveTasks(tasks, location)\` API performs
5893
+ * genuine reparenting while preserving persistent IDs \u2014 hence this script routes
5894
+ * through OmniJS.
5895
+ *
5896
+ * Args injected as \`globalThis.__args\`:
5897
+ * { items: Array<{ id: string, projectId?: string|null, parentId?: string|null }> }
5898
+ * For each item: pass projectId to move into a project, parentId to move under a
5899
+ * parent task, or neither to move to the inbox.
5900
+ *
5901
+ * Returns JSON: { succeeded: [{index, value}], failed: [{index, errorCode, message}] }
5902
+ *
5903
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 batchMoveTasks() caller
5904
+ * @see src/scripts/omnijs/task_move.js \u2014 singular counterpart
5905
+ * @see docs/adr/0002-omnifocus-transport-dual.md \u2014 JXA/OmniJS split rationale
5906
+ */
5907
+ (() => {
5908
+ const { items } = globalThis.__args;
5909
+
5910
+ if (!items || !Array.isArray(items)) {
5911
+ return JSON.stringify({
5912
+ error: { code: "VALIDATION", message: "items array is required" },
5913
+ });
5914
+ }
5915
+
5916
+ const succeeded = [];
5917
+ const failed = [];
5918
+
5919
+ for (let i = 0; i < items.length; i++) {
5920
+ const it = items[i];
5921
+ try {
5922
+ const task = flattenedTasks.filter((t) => t.id.primaryKey === it.id)[0];
5923
+ if (!task) {
5924
+ failed.push({ index: i, errorCode: "OF_NOT_FOUND", message: \`Task not found: \${it.id}\` });
5925
+ continue;
5926
+ }
5927
+
5928
+ if (it.parentId != null) {
5929
+ const parent = flattenedTasks.filter((t) => t.id.primaryKey === it.parentId)[0];
5930
+ if (!parent) {
5931
+ failed.push({
5932
+ index: i,
5933
+ errorCode: "OF_NOT_FOUND",
5934
+ message: \`Parent task not found: \${it.parentId}\`,
5935
+ });
5936
+ continue;
5937
+ }
5938
+ moveTasks([task], parent);
5939
+ } else if (it.projectId != null) {
5940
+ const proj = flattenedProjects.filter((p) => p.id.primaryKey === it.projectId)[0];
5941
+ if (!proj) {
5942
+ failed.push({
5943
+ index: i,
5944
+ errorCode: "OF_NOT_FOUND",
5945
+ message: \`Project not found: \${it.projectId}\`,
5946
+ });
5947
+ continue;
5948
+ }
5949
+ moveTasks([task], proj);
5950
+ } else {
5951
+ // No destination \u2014 move to inbox.
5952
+ moveTasks([task], inbox.beginning);
5953
+ }
5954
+
5955
+ succeeded.push({ index: i, value: it.id });
5956
+ } catch (e) {
5957
+ const msg = String(e?.message ?? e);
5958
+ failed.push({ index: i, errorCode: "OF_UNKNOWN", message: msg });
5959
+ }
5960
+ }
5961
+
5962
+ return JSON.stringify({ succeeded, failed });
5963
+ })();
5964
+ `;var Pm=`/**
5965
+ * OmniJS: clear all alarms/notifications from a task.
5966
+ *
5967
+ * Args injected as \`globalThis.__args\`: { taskId: string }
5968
+ *
5969
+ * Returns JSON: { ok: true } | { error: { code, message } }
5970
+ *
5971
+ * Equivalent to \`task_set_alarms\` with an empty \`alarms\` array, but
5972
+ * exposed as a dedicated verb so the tool surface is consistent with
5973
+ * \`task_clear_repetition\`.
5974
+ *
5975
+ * @see #461
5976
+ */
5977
+ (() => {
5978
+ const args = globalThis.__args || {};
5979
+ const taskId = args.taskId;
5980
+
5981
+ if (typeof taskId !== "string" || !taskId) {
5982
+ return JSON.stringify({
5983
+ error: { code: "VALIDATION", message: "taskId must be a non-empty string" },
5984
+ });
5985
+ }
5986
+
5987
+ const task = Task.byIdentifier(taskId);
5988
+ if (!task) {
5989
+ return JSON.stringify({
5990
+ error: { code: "NOT_FOUND", message: \`Task \${taskId} not found\` },
5991
+ });
5992
+ }
5993
+
5994
+ const existing = Array.from(task.notifications);
5995
+ for (const n of existing) {
5996
+ try {
5997
+ n.removeFromContainer();
5998
+ } catch (_e) {
5999
+ // Best-effort; continue
6000
+ }
6001
+ }
6002
+
6003
+ return JSON.stringify({ ok: true });
6004
+ })();
6005
+ `;var Om=`/**
6006
+ * OmniJS: convert a task to a project via Database.convertTasksToProjects().
6007
+ *
6008
+ * This operation is only available via OmniJS \u2014 JXA has no equivalent.
6009
+ * The task's persistent identifier is preserved on the resulting project
6010
+ * (i.e. \`Project.byIdentifier(task.id.primaryKey)\` resolves after conversion).
6011
+ *
6012
+ * Args injected as \`globalThis.__args\`:
6013
+ * {
6014
+ * id: string, // task primary key
6015
+ * folderId?: string, // if set, place in this folder
6016
+ * position?: "beginning" | "ending" // default "ending"
6017
+ * }
6018
+ *
6019
+ * Returns JSON: { projectId: string }
6020
+ *
6021
+ * @see src/tools/task/convertToProject.ts \u2014 handler
6022
+ * @see https://github.com/torsday/omnifocus-mcp/issues/525
6023
+ */
6024
+ (() => {
6025
+ const { id, folderId, position } = globalThis.__args;
6026
+
6027
+ if (!id) {
6028
+ return JSON.stringify({ error: { code: "VALIDATION", message: "id is required" } });
6029
+ }
6030
+
6031
+ const task = flattenedTasks.filter((t) => t.id.primaryKey === id)[0];
6032
+ if (!task) {
6033
+ return JSON.stringify({ error: { code: "NOT_FOUND", message: \`Task not found: \${id}\` } });
6034
+ }
6035
+
6036
+ // Resolve the position argument for convertTasksToProjects.
6037
+ // Valid positions: library.beginning, library.ending,
6038
+ // folder.children.beginning, folder.children.ending
6039
+ let pos;
6040
+ const atBeginning = position === "beginning";
6041
+
6042
+ if (folderId != null) {
6043
+ const folder =
6044
+ library.folders.filter((f) => f.id.primaryKey === folderId)[0] ??
6045
+ flattenedFolders.filter((f) => f.id.primaryKey === folderId)[0];
6046
+ if (!folder) {
6047
+ return JSON.stringify({
6048
+ error: { code: "NOT_FOUND", message: \`Folder not found: \${folderId}\` },
6049
+ });
6050
+ }
6051
+ pos = atBeginning ? folder.children.beginning : folder.children.ending;
6052
+ } else {
6053
+ pos = atBeginning ? library.beginning : library.ending;
6054
+ }
6055
+
6056
+ const results = convertTasksToProjects([task], pos);
6057
+
6058
+ // convertTasksToProjects returns an array of Projects parallel to the input.
6059
+ const project = results[0];
6060
+ if (!project) {
6061
+ return JSON.stringify({
6062
+ error: { code: "CONVERSION_FAILED", message: "convertTasksToProjects returned no project" },
6063
+ });
6064
+ }
6065
+
6066
+ return JSON.stringify({ projectId: project.id.primaryKey });
6067
+ })();
6068
+ `;var xm=`/**
6069
+ * OmniJS: move a task to a different project, parent task, or the inbox.
6070
+ *
6071
+ * JXA's \`task.move({ to: container })\` is unimplemented in OmniFocus 4.x
6072
+ * (error 9 "Replacement not supported currently"). The OmniJS
6073
+ * \`Database.moveTasks(tasks, location)\` API performs genuine reparenting
6074
+ * while preserving the task's persistent ID \u2014 hence this script routes
6075
+ * through the OmniJS transport instead.
6076
+ *
6077
+ * Args injected as \`globalThis.__args\`:
6078
+ * { id: string, projectId?: string|null, parentId?: string|null }
6079
+ * Pass neither projectId nor parentId to move the task to the inbox.
6080
+ *
6081
+ * Returns JSON: { id: string }
6082
+ *
6083
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 moveTask() caller
6084
+ * @see docs/adr/0002-omnifocus-transport-dual.md \u2014 JXA/OmniJS split rationale
6085
+ */
6086
+ (() => {
6087
+ const { id, projectId, parentId } = globalThis.__args;
6088
+
6089
+ if (!id) {
6090
+ return JSON.stringify({ error: { code: "VALIDATION", message: "id is required" } });
6091
+ }
6092
+
6093
+ const task = flattenedTasks.filter((t) => t.id.primaryKey === id)[0];
6094
+ if (!task) {
6095
+ return JSON.stringify({ error: { code: "NOT_FOUND", message: \`Task not found: \${id}\` } });
6096
+ }
6097
+
6098
+ if (parentId != null) {
6099
+ // Move under a parent task.
6100
+ const parent = flattenedTasks.filter((t) => t.id.primaryKey === parentId)[0];
6101
+ if (!parent) {
6102
+ return JSON.stringify({
6103
+ error: { code: "NOT_FOUND", message: \`Parent task not found: \${parentId}\` },
6104
+ });
6105
+ }
6106
+ moveTasks([task], parent);
6107
+ } else if (projectId != null) {
6108
+ // Move into a project (as a top-level action of that project).
6109
+ const proj = flattenedProjects.filter((p) => p.id.primaryKey === projectId)[0];
6110
+ if (!proj) {
6111
+ return JSON.stringify({
6112
+ error: { code: "NOT_FOUND", message: \`Project not found: \${projectId}\` },
6113
+ });
6114
+ }
6115
+ moveTasks([task], proj);
6116
+ } else {
6117
+ // No destination specified \u2014 move to inbox.
6118
+ // \`inbox\` alone is not a valid moveTasks position; use inbox.beginning.
6119
+ moveTasks([task], inbox.beginning);
6120
+ }
6121
+
6122
+ return JSON.stringify({ id });
6123
+ })();
6124
+ `;var Dm=`/**
6125
+ * OmniJS: reorder a task among its siblings.
6126
+ *
6127
+ * JXA's \`task.move({ to: ref, positioned: ... })\` shares the same broken
6128
+ * code path as \`task.move({ to: container })\` \u2014 both throw error 9
6129
+ * ("Replacement not supported currently") in OmniFocus 4.x. The OmniJS
6130
+ * \`Database.moveTasks(tasks, ChildInsertionLocation)\` API supports precise
6131
+ * sibling positioning via \`.before\` / \`.after\` / \`.beginning\` / \`.ending\`
6132
+ * and is the Omni-recommended automation route \u2014 hence this script.
6133
+ *
6134
+ * Args injected as \`globalThis.__args\`:
6135
+ * {
6136
+ * id: string, // task to reorder
6137
+ * mode: "before" | "after" | "start" | "end",
6138
+ * refId?: string, // required for before/after
6139
+ * container?: { // required for start/end
6140
+ * projectId?: string,
6141
+ * parentId?: string,
6142
+ * inbox?: true,
6143
+ * }
6144
+ * }
6145
+ *
6146
+ * Returns JSON: { id: string }
6147
+ *
6148
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 reorderTask() caller
6149
+ * @see docs/spikes/2026-04-task-reorder.md \u2014 Route A vs Route B rationale
6150
+ * @see docs/adr/0002-omnifocus-transport-dual.md \u2014 JXA/OmniJS split
6151
+ */
6152
+ (() => {
6153
+ const { id, mode, refId, container } = globalThis.__args;
6154
+
6155
+ if (!id) {
6156
+ return JSON.stringify({ error: { code: "VALIDATION", message: "id is required" } });
6157
+ }
6158
+ if (!mode) {
6159
+ return JSON.stringify({ error: { code: "VALIDATION", message: "mode is required" } });
6160
+ }
6161
+
6162
+ const task = flattenedTasks.filter((t) => t.id.primaryKey === id)[0];
6163
+ if (!task) {
6164
+ return JSON.stringify({ error: { code: "NOT_FOUND", message: \`Task not found: \${id}\` } });
6165
+ }
6166
+
6167
+ let location;
6168
+
6169
+ if (mode === "before" || mode === "after") {
6170
+ if (!refId) {
6171
+ return JSON.stringify({
6172
+ error: { code: "VALIDATION", message: \`refId is required for mode "\${mode}"\` },
6173
+ });
6174
+ }
6175
+ const ref = flattenedTasks.filter((t) => t.id.primaryKey === refId)[0];
6176
+ if (!ref) {
6177
+ return JSON.stringify({
6178
+ error: { code: "NOT_FOUND", message: \`Reference task not found: \${refId}\` },
6179
+ });
6180
+ }
6181
+ location = mode === "before" ? ref.before : ref.after;
6182
+ } else if (mode === "start" || mode === "end") {
6183
+ let c;
6184
+ if (container?.projectId) {
6185
+ c = flattenedProjects.filter((p) => p.id.primaryKey === container.projectId)[0];
6186
+ if (!c) {
6187
+ return JSON.stringify({
6188
+ error: { code: "NOT_FOUND", message: \`Project not found: \${container.projectId}\` },
6189
+ });
6190
+ }
6191
+ } else if (container?.parentId) {
6192
+ c = flattenedTasks.filter((t) => t.id.primaryKey === container.parentId)[0];
6193
+ if (!c) {
6194
+ return JSON.stringify({
6195
+ error: { code: "NOT_FOUND", message: \`Parent task not found: \${container.parentId}\` },
6196
+ });
6197
+ }
6198
+ } else {
6199
+ // inbox.beginning / inbox.ending
6200
+ c = inbox;
6201
+ }
6202
+ location = mode === "start" ? c.beginning : c.ending;
6203
+ } else {
6204
+ return JSON.stringify({
6205
+ error: { code: "VALIDATION", message: \`Unknown mode: \${mode}\` },
6206
+ });
6207
+ }
6208
+
6209
+ moveTasks([task], location);
6210
+ return JSON.stringify({ id });
6211
+ })();
6212
+ `;var Am=`/**
6213
+ * OmniJS: replace a task's alarm/notification set atomically.
6214
+ *
6215
+ * Args injected as \`globalThis.__args\`: { taskId: string, alarms: TaskAlarm[] }
6216
+ *
6217
+ * TaskAlarm =
6218
+ * | { kind: "due-relative", offsetSeconds: number }
6219
+ * | { kind: "defer-relative", offsetSeconds: number }
6220
+ * | { kind: "absolute", fireAt: ISO-8601 string }
6221
+ *
6222
+ * Algorithm:
6223
+ * 1. Resolve the task by primary-key.
6224
+ * 2. Drop every existing notification on the task (full-replace; the
6225
+ * MCP tool layer already enforced this is the intended semantics).
6226
+ * 3. Add each requested alarm via \`Task.addNotification\`. OmniJS's
6227
+ * addNotification accepts either a Date (for absolute) or a Number
6228
+ * of seconds (for relative; positive = before, negative = after).
6229
+ * 4. Return JSON: { ok: true } on success, or
6230
+ * { error: { code, message } } on transport-level failures.
6231
+ *
6232
+ * The tool-layer pre-validates that relative alarms have a corresponding
6233
+ * date anchor; the script trusts its inputs.
6234
+ *
6235
+ * @see #461
6236
+ */
6237
+ (() => {
6238
+ const args = globalThis.__args || {};
6239
+ const taskId = args.taskId;
6240
+ const alarms = Array.isArray(args.alarms) ? args.alarms : [];
6241
+
6242
+ if (typeof taskId !== "string" || !taskId) {
6243
+ return JSON.stringify({
6244
+ error: { code: "VALIDATION", message: "taskId must be a non-empty string" },
6245
+ });
6246
+ }
6247
+
6248
+ const task = Task.byIdentifier(taskId);
6249
+ if (!task) {
6250
+ return JSON.stringify({
6251
+ error: { code: "NOT_FOUND", message: \`Task \${taskId} not found\` },
6252
+ });
6253
+ }
6254
+
6255
+ // Phase 1: clear all existing notifications.
6256
+ // Iterate over a snapshot \u2014 modifying the live collection during the loop
6257
+ // would skip entries.
6258
+ const existing = Array.from(task.notifications);
6259
+ for (const n of existing) {
6260
+ try {
6261
+ n.removeFromContainer();
6262
+ } catch (_e) {
6263
+ // Continue best-effort; some notifications resist removal but the
6264
+ // subsequent addNotification calls re-derive state from scratch.
6265
+ }
6266
+ }
6267
+
6268
+ // Phase 2: add each new alarm.
6269
+ try {
6270
+ for (const alarm of alarms) {
6271
+ if (alarm.kind === "absolute") {
6272
+ const fireAt = new Date(alarm.fireAt);
6273
+ if (Number.isNaN(fireAt.getTime())) {
6274
+ return JSON.stringify({
6275
+ error: { code: "VALIDATION", message: \`invalid fireAt: \${alarm.fireAt}\` },
6276
+ });
6277
+ }
6278
+ task.addNotification(fireAt);
6279
+ } else if (alarm.kind === "due-relative" || alarm.kind === "defer-relative") {
6280
+ const seconds = Number(alarm.offsetSeconds);
6281
+ if (!Number.isFinite(seconds)) {
6282
+ return JSON.stringify({
6283
+ error: { code: "VALIDATION", message: \`invalid offsetSeconds: \${alarm.offsetSeconds}\` },
6284
+ });
6285
+ }
6286
+ // OmniJS's addNotification(Number) treats the value as seconds-from-now
6287
+ // for absolute; relative-to-due uses Task.dueDate / deferDate offsets.
6288
+ // The OmniJS API accepts a fireOffset relative to the task's anchor
6289
+ // when called with a Number. The kind distinguishes which anchor
6290
+ // OmniFocus uses internally \u2014 \`addNotification\` reads the task's
6291
+ // dueDate / deferDate when constructing the relative offset.
6292
+ task.addNotification(seconds);
6293
+ } else {
6294
+ return JSON.stringify({
6295
+ error: { code: "VALIDATION", message: \`unknown alarm kind: \${alarm.kind}\` },
6296
+ });
6297
+ }
6298
+ }
6299
+ } catch (e) {
6300
+ const msg = e && typeof e === "object" && "message" in e ? String(e.message) : String(e);
6301
+ return JSON.stringify({
6302
+ error: { code: "ADD_FAILED", message: msg },
6303
+ });
6304
+ }
6305
+
6306
+ return JSON.stringify({ ok: true });
6307
+ })();
6308
+ `;var yv=16*1024*1024,Iv=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-"],{timeout:n,maxBuffer:yv,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,i,s)=>{let c=i,l=s,d=a!==null&&a.killed===true,m=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:l,exitCode:a===null?0:a.code??1,timedOut:d,...m!==void 0?{spawnError:m}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");});async function Q(t,e={},n={}){let r=n.spawner??Iv,o=n.timeoutMs??45e3,a=JSON.stringify(e??{}),i=n.scriptName,s=kv(t,a),c=performance.now(),l=await r(s,a,o),d=Math.round(performance.now()-c),m=l.spawnError!==void 0||l.timedOut||l.exitCode!==0||l.stdout.trim()===""?"error":"ok";if(Un("omnijs",i,e,d,m),l.spawnError!==void 0)throw new et("Failed to spawn osascript",{cause:l.spawnError,details:{transport:"omnijs",reason:l.spawnError.code??"spawn-failed",...i!==void 0?{scriptName:i}:{}}});if(l.timedOut){let g=i!==void 0?` (script: ${i})`:"";throw new Ze(`OmniJS script exceeded ${o}ms timeout${g}`,{details:{transport:"omnijs",timeoutMs:o,...i!==void 0?{scriptName:i}:{}}})}if(l.exitCode!==0){let g=Tv(l.stderr,i);if(g!==null)throw g;let k=i!==void 0?` [${i}]`:"";throw new N(`OmniJS script failed (exit ${l.exitCode})${k}`,{details:{transport:"omnijs",exitCode:l.exitCode,stderr:Jn(l.stderr,1024),...i!==void 0?{scriptName:i}:{}}})}let f=l.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:Jn(f,200),...i!==void 0?{scriptName:i}:{}}})}}function kv(t,e){let n=`globalThis.__args = ${e};
6309
+ ${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(`
6310
+ `)}function Tv(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 Ke({details:{transport:"omnijs",stderr:Jn(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 Xe({details:{transport:"omnijs",stderr:Jn(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function Jn(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 Bn=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(xm,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});if(re(o))throw o.error.code==="NOT_FOUND"?new O(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}}):new I(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}})}async convertTaskToProject(e,n){let r=await Q(Om,{id:e,folderId:n.folderId??null,position:n.position??"ending"},{...this.runOpts,scriptName:"task_convert_to_project"});if(re(r))throw r.error.code==="NOT_FOUND"?new O(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}}):new I(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}});return y.of(r.projectId)}async batchMoveTasks(e){let r=await Q(_m,{items:e.map(o=>({id:o.id,projectId:o.destination.projectId??null,parentId:o.destination.parentId??null}))},{...this.runOpts,scriptName:"task_batch_move"});if(re(r))throw new I(r.error.message,{details:{transport:"omnijs",scriptName:"task_batch_move"}});return Te(r,h.of)}async reorderTask(e,n){let r=Dm,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(re(a))throw a.error.code==="NOT_FOUND"?new O(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}}):new I(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(Tm,{},{...this.runOpts,scriptName:"forecast_get_tag"});if(re(n))throw new N(n.error.message,{details:{transport:"omnijs",scriptName:"forecast_get_tag"}});return {tagId:n.tagId===null?null:w.of(n.tagId)}}async setForecastTag(e){let r=await Q(vm,{tagId:e},{...this.runOpts,scriptName:"forecast_set_tag"});if(re(r))throw r.error.code==="NOT_FOUND"?new O(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}}):new I(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}});return {tagId:r.tagId===null?null:w.of(r.tagId)}}async undoLastMutation(){let e=await Q(km,{},{...this.runOpts,scriptName:"database_undo"});if(re(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(Im,{},{...this.runOpts,scriptName:"database_redo"});if(re(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(Am,{taskId:String(e),alarms:n},{...this.runOpts,scriptName:"task_set_alarms"});if(re(r))throw r.error.code==="NOT_FOUND"?new O(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms"}}):r.error.code==="VALIDATION"?new I(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(Pm,{taskId:String(e)},{...this.runOpts,scriptName:"task_clear_alarms"});if(re(n))throw n.error.code==="NOT_FOUND"?new O(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(hm,{},{...this.runOpts,scriptName:"app_window_new"});if(re(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(ym,{},{...this.runOpts,scriptName:"app_window_new_tab"});if(re(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(bm,{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(Sm,{identifier:e},{...this.runOpts,scriptName:"perspective_evaluate"});if(re(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Ye(r.error.message,{details:{feature:"custom-perspectives"}}):new O(r.error.message,{details:{resource:"perspective",id:e}});return r.tasks.map(o=>({...o,id:h.of(o.id)}))}async getCustomPerspective(e){let n=await Q(jm,{identifier:e},{...this.runOpts,scriptName:"perspective_get"});if(re(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ye(n.error.message,{details:{feature:"custom-perspectives"}}):new O(n.error.message,{details:{resource:"perspective",id:e}});return n.perspective}async deleteCustomPerspective(e){let n=await Q(wm,{identifier:e},{...this.runOpts,scriptName:"perspective_delete"});if(re(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ye(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="NOT_FOUND"?new O(n.error.message,{details:{resource:"perspective",id:e}}):new N(n.error.message,{details:{transport:"omnijs",script:"perspective_delete",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 $n=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=Ae(),i={event:"cache.invalidated",scopes:[e],evicted:o,...a!==void 0?{correlationId:a}:{}};o>0&&C.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 jv=["/System/","/private/System/","/Library/","/private/Library/"];async function Ks(t,e){let n;try{n=await realpath(t);}catch{throw new I(`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 jv)if(r.startsWith(a))throw new I(`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 I(`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 Cm=1024*1024;async function Rm(t,e){if(e<=0)return;let n;try{n=await stat(t);}catch{throw new I(`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/Cm;if(r>e)throw new I(`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*Cm}})}function Mm(t){return "taskId"in t&&t.taskId!==void 0?{taskId:t.taskId}:{projectId:t.projectId}}function Fm(t){return "taskId"in t?"task":"project"}var Hn=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 Ks(e.filePath,this.allowedPaths),await Rm(e.filePath,this.maxMb);let n=await this.adapter.addAttachment(e),r=Mm(e);return {id:n,ownerKind:Fm(r),ownerName:await this.lookupOwnerName(r)}}async remove(e){let n=Mm(e),r=await this.lookupOwnerName(n);return await this.adapter.removeAttachment(e),{ownerKind:Fm(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 Ks(n,this.allowedPaths),this.adapter.saveAttachmentToPath(e)}};function Gn(t,e){t!==void 0&&$i(t,{folderId:e});}var zn=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 Gn(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateFolder(e,n),Gn(this.cache,e);}async delete(e,n=false){n&&await this._cascadeEmpty(e),await this.adapter.deleteFolder(e),Gn(this.cache,e);}async move(e,n){await this.adapter.updateFolder(e,{parentId:n}),Gn(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 Vn=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 Xs(t){return En.includes(t)}var qn=class{adapter;constructor(e){this.adapter=e.adapter;}async list(){return {perspectives:await this.adapter.listPerspectives(),cacheHit:false}}async evaluate(e){return {tasks:Xs(e)?await this.adapter.evaluatePerspective(e):await this.adapter.evaluateCustomPerspective(e),cacheHit:false}}async get(e){if(Xs(e))throw new I(`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(Xs(e))throw new I(`perspective_delete cannot delete built-in perspectives; got "${e}"`,{details:{field:"perspectiveId",value:e,kind:"builtin"}});await this.adapter.deleteCustomPerspective(e);}};var Em=200,Ys=1e3,Kn=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=gt(r),a=e.cursor!==void 0?yt(e.cursor,o):void 0,i=this.listCacheKey(o,e.cursor),s=this.cache.has(i),{projects:c,nextCursor:l}=await this.cache.wrap(i,async()=>this.fetchPage(r,a,n,o));return {projects:c,nextCursor:l,hasMore:l!==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:gs(i)};if(!n)return {project:s};let l=(await this.adapter.listTasks({projectId:e.id})).map(d=>({...d,_links:Ue(d)}));return {project:s,tasks:l}}),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,d=[...i.filter(S=>!(s!==void 0&&S.flagged!==s||c!==void 0&&(S.nextReviewDate===null||S.nextReviewDate>=c)))].sort((S,$)=>S.createdAt!==$.createdAt?S.createdAt<$.createdAt?-1:1:S.id<$.id?-1:1),m=n!==void 0?d.filter(S=>It({id:S.id,sortValue:S.createdAt},n,"asc")):d,f=m.slice(0,r),k=m.length>r?this.encodeNextCursor(f,o):null;return {projects:f.map(S=>({...S,_links:gs(S)})),nextCursor:k}}resolveLimit(e){if(e.limit===void 0)return Em;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>Ys)throw new I(`limit must be an integer between 1 and ${Ys}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${Ys}, or omit to use the default of ${Em}.`,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,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 I("Internal: cannot encode cursor for empty page.");return ht({lastId:r.id,lastSortValue:r.createdAt,filterHash:n})}};var Xn=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&&B(this.cache,{projectId:e});}async setInterval(e,n){await this.adapter.setProjectReviewInterval(e,n),this.cache!==void 0&&B(this.cache,{projectId:e});}async setNextReviewDate(e,n){await this.adapter.setProjectNextReviewDate(e,n),this.cache!==void 0&&B(this.cache,{projectId:e});}};var _v=100,Pv=500,Yn=class{adapter;constructor({adapter:e}){this.adapter=e;}async search(e){let n=Math.min(e.limit??_v,Pv),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=gt(r),a=null;e.cursor&&(a=yt(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((v,S)=>{let $=v.createdAt.localeCompare(S.createdAt);return $!==0?$:v.id.localeCompare(S.id)}),d=(a?c.filter(v=>It({id:v.id,sortValue:v.createdAt},a,"asc")):c).slice(0,n+1),m=d.length>n,f=d.slice(0,n).map(v=>({...v,_links:Ue(v)})),g=f.at(-1),k=m&&g?ht({lastId:g.id,lastSortValue:g.createdAt,filterHash:o}):null;return {tasks:f,nextCursor:k,hasMore:m,cacheHit:false}}};function Le(t,e){t!==void 0&&Bi(t,{tagId:e});}var Qn=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 Le(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateTag(e,n),Le(this.cache,e);}async delete(e){await this.adapter.deleteTag(e),Le(this.cache,e);}async move(e,n){await this.adapter.updateTag(e,{parentId:n}),Le(this.cache,e);}async setStatus(e,n){await this.adapter.updateTag(e,{status:n}),Le(this.cache,e);}async setAllowsNextAction(e,n){await this.adapter.updateTag(e,{allowsNextAction:n}),Le(this.cache,e);}async setLocation(e,n){await this.adapter.updateTag(e,{location:n}),Le(this.cache,e);}async clearLocation(e){await this.adapter.updateTag(e,{location:null}),Le(this.cache,e);}async getLocation(e){return {location:(await this.adapter.getTag(e)).location,cacheHit:false}}};function Nm(t){if(t.OMNIFOCUS_E2E_USE_MEMORY){let r=new Nn;return new vt({jxa:r,omnijs:r})}let e=new Ln({timeoutMs:t.OMNIFOCUS_JXA_TIMEOUT_MS}),n=new Bn({timeoutMs:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS});return vt.fromTransports(e,n)}function Um(t,e){let n=new $n({capacity:e.OMNIFOCUS_CACHE_CAPACITY,ttlMs:e.OMNIFOCUS_CACHE_TTL_MS});return {cache:n,taskService:new Cn({adapter:t,cache:n}),projectService:new Kn({adapter:t,cache:n}),tagService:new Qn({adapter:t,cache:n}),folderService:new zn({adapter:t,cache:n}),attachmentService:new Hn({adapter:t,allowedPaths:e.OMNIFOCUS_ATTACHMENT_PATHS,maxAttachmentMb:e.OMNIFOCUS_MAX_ATTACHMENT_MB}),exportService:new pt({adapter:t}),forecastService:new Vn({adapter:t}),perspectiveService:new qn({adapter:t}),pluginService:new ct({adapter:t}),reviewService:new Xn({adapter:t,cache:n}),searchService:new Yn({adapter:t})}}var Ov="jxa",xv="unknown";function M(t={}){return {correlationId:Ae()??gm(),durationMs:0,cacheHit:false,transport:Ov,ofVersion:xv,...t}}function Lm(t){let{adapter:e,cache:n,server:r,aggregateUris:o}=t;return async a=>{let i=new Date(a.detectedAt).getTime()-200,s=new Date(i).toISOString(),c={taskIds:[],projectIds:[]},l=false;try{c=await e.getChangesSince(s),l=!0;}catch(d){C.debug({event:"database.changed.query_failed",err:d});}if(l&&(c.taskIds.length>0||c.projectIds.length>0)){for(let d of c.taskIds)n.invalidate(`task:${d}`);for(let d of c.projectIds)n.invalidate(`project:${d}`);}else n.clear();for(let d of c.taskIds)r.server.sendResourceUpdated({uri:`omnifocus://task/${d}`}).catch(()=>{});for(let d of c.projectIds)r.server.sendResourceUpdated({uri:`omnifocus://project/${d}`}).catch(()=>{});for(let d of o)r.server.sendResourceUpdated({uri:d}).catch(()=>{});C.debug({event:"database.changed",source:a.source,detectedAt:a.detectedAt,changedTasks:c.taskIds.length,changedProjects:c.projectIds.length,cacheStrategy:l?"targeted":"full-clear"});}}async function Jm(t,e){let n=performance.now();try{let r=await e(),o=Math.round(performance.now()-n);return C.info({event:"tool.invoked",tool:t,correlationId:Ae(),durationMs:o,transport:r.meta.transport,cacheHit:r.meta.cacheHit},"tool invoked"),r}catch(r){let o=Math.round(performance.now()-n),a=ei(r)?r.code:"UNKNOWN";throw C.warn({event:"tool.error",tool:t,correlationId:Ae(),durationMs:o,code:a,err:r},"tool error"),r}}function Bm(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(C.warn({event:"loop.detected",tool:t,callCount:s,windowMs:c,level:a}),a==="error")return Promise.reject(new Qt(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 $m(t,e,n){return e.check(t),n().then(r=>{let o=e.remaining(t);return {...r,meta:{...r.meta,rateLimit:o}}})}function Dv(t,e,n){return (r,o)=>fm(async()=>(n.shutdown.assertNotShuttingDown(),n.circuitRegistry.get(t).call(async()=>{let i=async()=>(await e(r,o)).structuredContent,s=await $m(t,n.rateLimiter,()=>Bm(t,r,n.loopDetector,()=>Jm(t,i)));return u(s)})))}function Wm(t,e){let n=t;if(n.__omnifocusMiddlewareInstalled===true)return;let r=t.registerTool.bind(t),o=(...a)=>{let[i,s,c]=a,l=Dv(i,c,e);return r(i,s,l)};t.registerTool=o,n.__omnifocusMiddlewareInstalled=true;}var Av=5e3,Cv=1e4,Rv=50,Qs=class{_shuttingDown=false;_queues=[];_readGraceMs;_writeGraceMs;constructor(e={}){this._readGraceMs=e.readGraceMs??Number(process.env.OMNIFOCUS_READ_GRACE_MS??Av),this._writeGraceMs=e.writeGraceMs??Number(process.env.OMNIFOCUS_WRITE_GRACE_MS??Cv);}get isShuttingDown(){return this._shuttingDown}assertNotShuttingDown(){if(this._shuttingDown)throw new Yt}registerQueue(e){this._queues.includes(e)||this._queues.push(e);}async initiate(e,n=process.exit){if(this._shuttingDown)return;this._shuttingDown=true,C.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),C.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,Rv));}let r=this._queues.filter(o=>o.pendingCount()>0);for(let o of r)C.warn({event:"server.shutdown.drain_timeout",queue:o.name,pending:o.pendingCount()},"queue did not drain within grace window; forcing shutdown");}},Ve=new Qs;var Mv=["@modelcontextprotocol/sdk","node_modules/@modelcontextprotocol"];function Fv(t){return Mv.some(e=>t.includes(e))}var Hm=null,Gm=false;function zm(){if(Gm)return;Hm=process.stdout.write,Gm=true;let t=Hm;process.stdout.write=function(n,r,o){let a=new Error().stack??"";if(Fv(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 q("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 Vm=_e.version,Uv=_e.name.split("/").pop()??"omnifocus-mcp",Lv=new Set(["run_jxa_script","run_omnijs_script"]),Jv=Date.now();function Bv(){return new McpServer({name:Uv,version:Vm})}async function qm(){zm();let t=ti();C.level=t.OMNIFOCUS_LOG_LEVEL;let e=Bv(),n=new en(t.OMNIFOCUS_TOOL_RATE_LIMIT),r=new Zt;Wm(e,{rateLimiter:n,loopDetector:r,circuitRegistry:zs,shutdown:Ve});let o=new StdioServerTransport,a=Nm(t),i=new qt({size:t.OMNIFOCUS_READ_POOL_SIZE,name:"jxa-read"}),s=new St({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"jxa-write"}),c=new St({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"omnijs"});Ve.registerQueue(i),Ve.registerQueue(s),Ve.registerQueue(c);let l=Zs(a,{readPool:i,jxaWriteQueue:s,omniJsQueue:c}),d=Um(l,t);od(e,{startedAt:Jv,adapter:l,circuitRegistry:zs,makeMeta:M}),oi(e),si(e,()=>ai(t)),Ui(e,{adapter:l,projectService:d.projectService,reviewService:d.reviewService,forecastService:d.forecastService,perspectiveService:d.perspectiveService});let m={folderService:d.folderService,makeMeta:M};Nc(e,m),Jc(e,m),$c(e,m),Wc(e,m),Hc(e,m),Vc(e,m);let f={adapter:l,makeMeta:M};Uc(e,f),Bc(e,f),Gc(e,f),qc(e,f);let g={tagService:d.tagService,makeMeta:M};kl(e,g),vl(e,g),_l(e,{adapter:l,makeMeta:M}),Sl(e,g),jl(e,g),Pl(e,g),Ol(e,g),Al(e,g),Cl(e,g),Rl(e,g),Ml(e,g);let k={adapter:l,makeMeta:M};Tl(e,k),wl(e,k),xl(e,k),Fl(e,k);let v={adapter:l,makeMeta:M,cache:d.cache};Zc(e,v),ed(e,v),td(e,v),nd(e,v),rd(e,v),hl(e,{searchService:d.searchService,makeMeta:M}),Kc(e,{forecastService:d.forecastService,makeMeta:M}),Yc(e,{forecastService:d.forecastService,makeMeta:M}),Xc(e,{forecastService:d.forecastService,makeMeta:M}),Qc(e,{forecastService:d.forecastService,cache:d.cache,makeMeta:M});let S={perspectiveService:d.perspectiveService,makeMeta:M};pd(e,S),cd(e,S),ld(e,S),sd(e,{perspectiveService:d.perspectiveService,cache:d.cache,makeMeta:M}),ud(e,{adapter:l,makeMeta:M}),yl(e,{adapter:l,makeMeta:M}),Il(e,{adapter:l,makeMeta:M,cache:d.cache}),Vi(e,{adapter:l,makeMeta:M,cache:d.cache}),Gi(e,{adapter:l,makeMeta:M,cache:d.cache});let $={reviewService:d.reviewService,makeMeta:M};dl(e,$),pl(e,$),ml(e,$),fl(e,$),gl(e,$);let T={exportService:d.exportService,makeMeta:M};qi(e,T),Mc(e,T),Fc(e,T),Li(e,{adapter:l,makeMeta:M});let D={adapter:l,makeMeta:M};eu(e,D),tu(e,D),nu(e,D),ru(e,D),ou(e,D);let P={projectService:d.projectService,makeMeta:M,cache:d.cache},U={adapter:l,makeMeta:M,cache:d.cache};md(e,U),fd(e,U),gd(e,P),_d(e,U),Od(e,U),Dd(e,P),Fd(e,{adapter:l,makeMeta:M}),Rd(e,{projectService:d.projectService,makeMeta:M}),Ed(e,{projectService:d.projectService,makeMeta:M}),Nd(e,P),tl(e,U);let b={adapter:l,makeMeta:M,cache:d.cache,templatesFolderName:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME};el(e,b),Zd(e,b),Qd(e,b);let F={adapter:l,makeMeta:M};hd(e,F),Pd(e,F),xd(e,F),Ad(e,F),Ud(e,F),nl(e,F);let z={taskService:d.taskService,makeMeta:M},X={adapter:l,makeMeta:M},A={adapter:l,makeMeta:M,cache:d.cache};bp(e,z),Ap(e,z),Tp(e,X),Sp(e,X),Wp(e,{searchService:d.searchService,makeMeta:M}),Pp(e,X),Up(e,{makeMeta:M}),cl(e,{makeMeta:M}),Jp(e,A),El(e,A),Nl(e,A),Ul(e,A),Jl(e,A),Bl(e,A),$l(e,A),Wl(e,A),Hl(e,A),Gl(e,A),Vl(e,A),ql(e,A),Xl(e,A),dp(e,A),pp(e,A),hp(e,{...A,attachmentService:d.attachmentService}),kp(e,A),Zl(e,A);let oe={adapter:l,makeMeta:M,cache:d.cache,waitingTagName:t.OMNIFOCUS_WAITING_TAG_NAME};Qp(e,oe),Zp(e,oe),Rp(e,A),Bp(e,A),zp(e,A),Vp(e,A),qp(e,A),Kp(e,A),op(e,A),sp(e,A),Xp(e,A);let le={adapter:l,makeMeta:M};Ll(e,le),zl(e,le),Yl(e,le),ap(e,le),ip(e,le),lp(e,le),Mp(e,le),Yp(e,le),Ji(e,{attachmentService:d.attachmentService,makeMeta:M});let Z={adapter:l,makeMeta:M},L={allowRawScript:t.OMNIFOCUS_ALLOW_RAW_SCRIPT};rl(e,Z,L),ol(e,Z,L),process.on("SIGINT",()=>{Ve.initiate("SIGINT");}),process.on("SIGTERM",()=>{Ve.initiate("SIGTERM");}),process.on("unhandledRejection",Be=>{C.fatal({event:"server.unhandled_rejection",reason:Be},"unhandled rejection"),C.flush(),process.exit(1);}),process.on("uncaughtException",Be=>{C.fatal({event:"server.uncaught_exception",err:Be},"uncaught exception"),C.flush(),process.exit(1);}),await e.connect(o);let Je=Lm({adapter:l,cache:d.cache,server:e,aggregateUris:[Ct,Rt,Mt,Ft,Et,Nt]}),be=new Fn(Be=>{Je(Be).catch(Km=>{C.error({event:"database.changed.handler_error",err:Km});});});be.start(),process.on("exit",()=>be.stop());let qe=Object.keys(au).filter(Be=>t.OMNIFOCUS_ALLOW_RAW_SCRIPT||!Lv.has(Be)).sort();C.info({event:"server.started",version:Vm,config:ni(t),tools:qe,prompts:[er,tr,nr,rr],resources:[tn,Ct,Rt,Mt,Ft,Et,Nt,yr,Ir,kr]},"server started");}var Zn=process.argv.slice(2);(Zn.includes("--version")||Zn.includes("-v"))&&(process.stdout.write(`${_e.version}
6311
+ `),process.exit(0));(Zn.includes("--help")||Zn.includes("-h"))&&(process.stdout.write(`${_e.name} v${_e.version}
6312
+ ${_e.description}
4787
6313
 
4788
6314
  Usage: omnifocus-mcp
4789
6315
 
4790
6316
  Speaks Model Context Protocol over stdio. Add to your MCP client's
4791
6317
  configuration (Claude Desktop, Claude Code, etc.) \u2014 see the README
4792
- for client-specific setup: ${re.homepage}
6318
+ for client-specific setup: ${_e.homepage}
4793
6319
 
4794
6320
  Options:
4795
6321
  -v, --version Print version and exit
@@ -4800,5 +6326,5 @@ Environment:
4800
6326
  OMNIFOCUS_ALLOW_RAW_SCRIPT Enable raw-script tools (off by default)
4801
6327
  OMNIFOCUS_JXA_TIMEOUT_MS Per-call JXA timeout (default: 30000)
4802
6328
  OMNIFOCUS_OMNIJS_TIMEOUT_MS Per-call OmniJS timeout (default: 45000)
4803
- `),process.exit(0));tc().catch(n=>{process.stderr.write(`[omnifocus-mcp] Fatal startup error: ${String(n)}
6329
+ `),process.exit(0));qm().catch(t=>{process.stderr.write(`[omnifocus-mcp] Fatal startup error: ${String(t)}
4804
6330
  `),process.exit(1);});