@torsday/omnifocus-mcp 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 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:
2
+ import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {createHmac,createHash,randomBytes}from'crypto';import xb,{homedir}from'os';import {z}from'zod';import uc from'pino';import {spawn,execFile}from'child_process';import dt from'fs';import Zi,{extname,sep}from'path';import {fileURLToPath}from'url';import {createInterface}from'readline';import Mb from'https';import {AsyncLocalStorage}from'async_hooks';import {ulid}from'ulid';import {EventEmitter}from'events';import {LRUCache}from'lru-cache';import {realpath,stat}from'fs/promises';var Fe={name:"@torsday/omnifocus-mcp",version:"1.2.0",description:"MCP server exposing the full OmniFocus surface to LLM agents \u2014 80 typed tools spanning tasks, projects, tags, folders, perspectives, forecast, review, notes, attachments, and sync, with a strict typed-error taxonomy and per-tool circuit breakers, rate limiter, and idempotency-key support. macOS-only; talks to OmniFocus 4 via JXA + OmniJS.",homepage:"https://github.com/torsday/omnifocus-mcp#readme"};var $t=Object.freeze({listTasks:"jxa",getTask:"jxa",getTasksMany:"jxa",createTask:"jxa",updateTask:"jxa",completeTask:"jxa",uncompleteTask:"jxa",dropTask:"jxa",undropTask:"jxa",deleteTask:"jxa",moveTask:"omnijs",convertTaskToProject:"omnijs",batchMoveTasks:"omnijs",reorderTask:"omnijs",duplicateTask:"jxa",batchCreateTasks:"jxa",batchUpdateTasks:"jxa",batchCompleteTasks:"jxa",batchUncompleteTasks:"jxa",batchDeleteTasks:"jxa",batchDropTasks:"jxa",batchUndropTasks:"jxa",listProjects:"jxa",getProject:"jxa",getProjectsMany:"jxa",createProject:"jxa",updateProject:"jxa",completeProject:"jxa",batchCompleteProjects:"jxa",dropProject:"jxa",batchDropProjects:"jxa",moveProject:"jxa",deleteProject:"jxa",markProjectReviewed:"jxa",listProjectsDueForReview:"jxa",setProjectReviewInterval:"jxa",setProjectNextReviewDate:"jxa",listTags:"jxa",getTag:"jxa",getTagsMany:"jxa",createTag:"jxa",updateTag:"jxa",deleteTag:"jxa",listFolders:"jxa",getFolder:"jxa",createFolder:"jxa",updateFolder:"jxa",deleteFolder:"jxa",searchTasks:"jxa",getForecast:"jxa",getForecastTag:"omnijs",setForecastTag:"omnijs",listPerspectives:"jxa",evaluatePerspective:"jxa",evaluateCustomPerspective:"omnijs",evaluatePerspectiveRules:"omnijs",getCustomPerspective:"omnijs",deleteCustomPerspective:"omnijs",createCustomPerspective:"omnijs",updateCustomPerspective:"omnijs",syncTrigger:"jxa",getLastSync:"jxa",undoLastMutation:"omnijs",redoLastMutation:"omnijs",setTaskAlarms:"omnijs",clearTaskAlarms:"omnijs",listAttachments:"jxa",addAttachment:"jxa",removeAttachment:"jxa",saveAttachmentToPath:"jxa",appLaunch:"jxa",getWindowState:"jxa",setWindowPerspective:"jxa",setWindowFocus:"jxa",appWindowNew:"omnijs",appWindowNewTab:"omnijs",pluginInvoke:"omnijs",getChangesSince:"jxa",runJxaScript:"jxa",runOmniJsScript:"omnijs"});var Wt=class t{jxa;omnijs;constructor(e){this.jxa=e.jxa,this.omnijs=e.omnijs;}get routingTable(){return $t}static fromTransports(e,n){return new t({jxa:e,omnijs:n})}pick(e){return $t[e]==="jxa"?this.jxa:this.omnijs}listTasks(e){return this.pick("listTasks").listTasks(e)}getTask(e){return this.pick("getTask").getTask(e)}getTasksMany(e){return this.pick("getTasksMany").getTasksMany(e)}createTask(e){return this.pick("createTask").createTask(e)}updateTask(e,n){return this.pick("updateTask").updateTask(e,n)}completeTask(e,n){return this.pick("completeTask").completeTask(e,n)}uncompleteTask(e){return this.pick("uncompleteTask").uncompleteTask(e)}dropTask(e,n){return this.pick("dropTask").dropTask(e,n)}undropTask(e){return this.pick("undropTask").undropTask(e)}deleteTask(e){return this.pick("deleteTask").deleteTask(e)}moveTask(e,n){return this.pick("moveTask").moveTask(e,n)}convertTaskToProject(e,n){return this.pick("convertTaskToProject").convertTaskToProject(e,n)}batchMoveTasks(e){return this.pick("batchMoveTasks").batchMoveTasks(e)}reorderTask(e,n){return this.pick("reorderTask").reorderTask(e,n)}duplicateTask(e,n){return this.pick("duplicateTask").duplicateTask(e,n)}batchCreateTasks(e){return this.pick("batchCreateTasks").batchCreateTasks(e)}batchUpdateTasks(e){return this.pick("batchUpdateTasks").batchUpdateTasks(e)}batchCompleteTasks(e){return this.pick("batchCompleteTasks").batchCompleteTasks(e)}batchUncompleteTasks(e){return this.pick("batchUncompleteTasks").batchUncompleteTasks(e)}batchDeleteTasks(e){return this.pick("batchDeleteTasks").batchDeleteTasks(e)}batchDropTasks(e){return this.pick("batchDropTasks").batchDropTasks(e)}batchUndropTasks(e){return this.pick("batchUndropTasks").batchUndropTasks(e)}listProjects(e){return this.pick("listProjects").listProjects(e)}getProject(e){return this.pick("getProject").getProject(e)}getProjectsMany(e){return this.pick("getProjectsMany").getProjectsMany(e)}createProject(e){return this.pick("createProject").createProject(e)}updateProject(e,n){return this.pick("updateProject").updateProject(e,n)}completeProject(e,n){return this.pick("completeProject").completeProject(e,n)}dropProject(e,n){return this.pick("dropProject").dropProject(e,n)}batchCompleteProjects(e){return this.pick("batchCompleteProjects").batchCompleteProjects(e)}batchDropProjects(e){return this.pick("batchDropProjects").batchDropProjects(e)}moveProject(e,n){return this.pick("moveProject").moveProject(e,n)}deleteProject(e){return this.pick("deleteProject").deleteProject(e)}markProjectReviewed(e){return this.pick("markProjectReviewed").markProjectReviewed(e)}listProjectsDueForReview(){return this.pick("listProjectsDueForReview").listProjectsDueForReview()}setProjectReviewInterval(e,n){return this.pick("setProjectReviewInterval").setProjectReviewInterval(e,n)}setProjectNextReviewDate(e,n){return this.pick("setProjectNextReviewDate").setProjectNextReviewDate(e,n)}listTags(e){return this.pick("listTags").listTags(e)}getTag(e){return this.pick("getTag").getTag(e)}getTagsMany(e){return this.pick("getTagsMany").getTagsMany(e)}createTag(e){return this.pick("createTag").createTag(e)}updateTag(e,n){return this.pick("updateTag").updateTag(e,n)}deleteTag(e){return this.pick("deleteTag").deleteTag(e)}listFolders(e){return this.pick("listFolders").listFolders(e)}getFolder(e){return this.pick("getFolder").getFolder(e)}createFolder(e){return this.pick("createFolder").createFolder(e)}updateFolder(e,n){return this.pick("updateFolder").updateFolder(e,n)}deleteFolder(e){return this.pick("deleteFolder").deleteFolder(e)}searchTasks(e){return this.pick("searchTasks").searchTasks(e)}getForecast(e){return this.pick("getForecast").getForecast(e)}getForecastTag(){return this.pick("getForecastTag").getForecastTag()}setForecastTag(e){return this.pick("setForecastTag").setForecastTag(e)}listPerspectives(){return this.pick("listPerspectives").listPerspectives()}evaluatePerspective(e){return this.pick("evaluatePerspective").evaluatePerspective(e)}evaluateCustomPerspective(e){return this.pick("evaluateCustomPerspective").evaluateCustomPerspective(e)}evaluatePerspectiveRules(e,n){return this.pick("evaluatePerspectiveRules").evaluatePerspectiveRules(e,n)}getCustomPerspective(e){return this.pick("getCustomPerspective").getCustomPerspective(e)}deleteCustomPerspective(e){return this.pick("deleteCustomPerspective").deleteCustomPerspective(e)}createCustomPerspective(e){return this.pick("createCustomPerspective").createCustomPerspective(e)}updateCustomPerspective(e,n){return this.pick("updateCustomPerspective").updateCustomPerspective(e,n)}syncTrigger(){return this.pick("syncTrigger").syncTrigger()}getLastSync(){return this.pick("getLastSync").getLastSync()}undoLastMutation(){return this.pick("undoLastMutation").undoLastMutation()}redoLastMutation(){return this.pick("redoLastMutation").redoLastMutation()}setTaskAlarms(e,n){return this.pick("setTaskAlarms").setTaskAlarms(e,n)}clearTaskAlarms(e){return this.pick("clearTaskAlarms").clearTaskAlarms(e)}listAttachments(e){return this.pick("listAttachments").listAttachments(e)}addAttachment(e){return this.pick("addAttachment").addAttachment(e)}removeAttachment(e){return this.pick("removeAttachment").removeAttachment(e)}saveAttachmentToPath(e){return this.pick("saveAttachmentToPath").saveAttachmentToPath(e)}appLaunch(){return this.pick("appLaunch").appLaunch()}getWindowState(){return this.pick("getWindowState").getWindowState()}setWindowPerspective(e){return this.pick("setWindowPerspective").setWindowPerspective(e)}setWindowFocus(e){return this.pick("setWindowFocus").setWindowFocus(e)}appWindowNew(){return this.pick("appWindowNew").appWindowNew()}appWindowNewTab(){return this.pick("appWindowNewTab").appWindowNewTab()}pluginInvoke(e){return this.pick("pluginInvoke").pluginInvoke(e)}runJxaScript(e,n){let r=this.pick("runJxaScript");return typeof r.runJxaScript!="function"?Promise.reject(new TypeError("Router dispatched runJxaScript to a transport that does not implement it")):r.runJxaScript(e,n)}runOmniJsScript(e,n){let r=this.pick("runOmniJsScript");return typeof r.runOmniJsScript!="function"?Promise.reject(new TypeError("Router dispatched runOmniJsScript to a transport that does not implement it")):r.runOmniJsScript(e,n)}getChangesSince(e){return this.pick("getChangesSince").getChangesSince(e)}};var eh=new Set(["createTask","updateTask","completeTask","uncompleteTask","dropTask","undropTask","deleteTask","moveTask","reorderTask","duplicateTask","batchCreateTasks","batchUpdateTasks","batchCompleteTasks","createProject","updateProject","completeProject","dropProject","moveProject","deleteProject","markProjectReviewed","setProjectReviewInterval","createTag","updateTag","deleteTag","createFolder","updateFolder","deleteFolder","syncTrigger","addAttachment","removeAttachment","appLaunch","pluginInvoke","runJxaScript","runOmniJsScript"]);function th(t,e){return $t[t]==="omnijs"?e.omniJsQueue:eh.has(t)?e.jxaWriteQueue:e.readPool}function cc(t,e){return new Proxy(t,{get(n,r,o){if(typeof r!="string"||!(r in $t))return Reflect.get(n,r,o);let a=r,i=th(a,e),c=Reflect.get(n,a,o).bind(n);return (...d)=>i.run(()=>c(...d))}})}var In=class{name;size;inFlight=0;waiters=[];constructor(e){if(!Number.isInteger(e.size)||e.size<1)throw new RangeError(`ReadPool.size must be a positive integer (got ${String(e.size)})`);this.size=e.size,this.name=e.name??"read-pool";}async run(e){await this.acquire();try{return await e()}finally{this.release();}}inFlightCount(){return this.inFlight}waitingCount(){return this.waiters.length}pendingCount(){return this.inFlight+this.waiters.length}acquire(){return this.inFlight<this.size?(this.inFlight++,Promise.resolve()):new Promise(e=>{this.waiters.push(()=>{this.inFlight++,e();});})}release(){this.inFlight--;let e=this.waiters.shift();e!==void 0&&e();}};var Y=class extends Error{code;remediationClass;suggestion;details;constructor(e,n,r={}){super(n,r.cause!==void 0?{cause:r.cause}:void 0),this.name=new.target.name,this.code=e,r.remediationClass!==void 0&&(this.remediationClass=r.remediationClass),r.suggestion!==void 0&&(this.suggestion=r.suggestion),r.details!==void 0&&(this.details=r.details);}toJSON(){let e={name:this.name,code:this.code,message:this.message};return this.remediationClass!==void 0&&(e.remediationClass=this.remediationClass),this.suggestion!==void 0&&(e.suggestion=this.suggestion),this.details!==void 0&&(e.details=this.details),e}};function dc(t){return t instanceof Y}var ut=class extends Y{constructor(e={}){super("OF_NOT_RUNNING","OmniFocus is not running.",{remediationClass:"environment",suggestion:"Launch OmniFocus and retry.",...e});}},mt=class extends Y{constructor(e={}){super("OF_PERMISSION_DENIED","Automation permission for OmniFocus is denied.",{remediationClass:"environment",suggestion:"Open System Settings \u2192 Privacy & Security \u2192 Automation; grant this terminal or client access to OmniFocus. See docs/troubleshooting.md for step-by-step recovery.",...e});}},vn=class extends Y{constructor(e={}){super("OF_CALENDAR_PERMISSION_DENIED","Calendar access has not been granted.",{remediationClass:"environment",suggestion:"Open System Settings \u2192 Privacy & Security \u2192 Calendars; grant this terminal or client access. Or invoke the calendar-bridge `request-access` flow to trigger the macOS prompt.",...e});}},ge=class extends Y{constructor(e,n={}){super("OF_CALENDAR_BRIDGE_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"Run `pnpm build:calendar-bridge` to compile the Swift binary. The published npm tarball includes a prebuilt binary; if you're running from source, the binary is built on demand.",...n});}},Ue=class extends Y{constructor(e,n={}){super("OF_FEATURE_REQUIRES_PRO",e,{remediationClass:"environment",suggestion:"This feature requires OmniFocus Pro. Upgrade or use a different tool.",...n});}};var y=class extends Y{constructor(e,n={}){super("OF_VALIDATION",e,{remediationClass:"input",suggestion:"Fix the input and retry. See `details` for field-level reasons.",...n});}},P=class extends Y{constructor(e,n={}){super("OF_NOT_FOUND",e,{remediationClass:"input",suggestion:"Confirm the ID with the corresponding `*_list` tool. Use OmniFocus persistent IDs, not names.",...n});}},ft=class extends Y{constructor(e,n={}){super("OF_CONFLICT",e,{remediationClass:"input",suggestion:"The resource was modified since you read it. Re-read with the corresponding `*_get` tool, merge your changes, and retry with the fresh `modifiedAt` value.",...n});}},gt=class extends Y{constructor(e,n={}){super("OF_TIMEOUT",e,{remediationClass:"transient",suggestion:"Retry once. If repeated, OmniFocus may be wedged \u2014 relaunch it.",...n});}},Tn=class extends Y{constructor(e,n={}){super("OF_RATE_LIMITED",e,{remediationClass:"transient",suggestion:"Wait details.retryAfterMs milliseconds then retry.",...n,details:{retryAfterMs:6e4,...n.details}});}},wn=class extends Y{constructor(e,n={}){super("OF_QUEUE_FULL",e,{remediationClass:"transient",suggestion:"The write queue is saturated. Wait for in-flight writes to drain, then retry.",...n});}},Ht=class extends Y{constructor(e,n={}){super("OF_CIRCUIT_OPEN",e,{remediationClass:"transient",suggestion:"This tool failed repeatedly and is rejecting calls fast. Wait details.retryAfterMs milliseconds for the circuit to half-open.",...n,details:{retryAfterMs:6e4,...n.details}});}},ht=class extends Y{constructor(e,n={}){super("OF_TRANSPORT_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"The required transport is unreachable. Verify OmniFocus is running and responsive.",...n});}},yt=class extends Y{constructor(e,n={}){super("OF_WINDOW_UNAVAILABLE",e,{remediationClass:"environment",suggestion:"OmniFocus has no front window. Ask the user to open an OmniFocus window (Cmd-N or click the Dock icon) and retry.",...n});}},N=class extends Y{constructor(e,n={}){super("OF_SCRIPT_ERROR",e,{remediationClass:"infrastructure",suggestion:"The OmniFocus script failed. Inspect `details.transport` and `details.reason` for context.",...n});}},Sn=class extends Y{constructor(e={}){super("OF_SHUTTING_DOWN","Server is shutting down; not accepting new requests.",{remediationClass:"lifecycle",suggestion:"Reconnect to a fresh server instance.",...e});}},bn=class extends Y{constructor(e,n,r,o={}){super("OF_LOOP_DETECTED",`Tool "${e}" has been called ${n} time(s) with identical arguments within ${r}s. The agent appears to be stuck.`,{remediationClass:"input",suggestion:"Act on the result of the previous call before repeating this tool. If you need the same data again, verify the previous response was consumed.",details:{tool:e,count:n,windowSeconds:r},...o});}};var zt=class{name;cap;inFlight=0;queue=[];constructor(e){if(!Number.isInteger(e.cap)||e.cap<1)throw new RangeError(`WriteQueue.cap must be a positive integer (got ${String(e.cap)})`);this.cap=e.cap,this.name=e.name??"write-queue";}run(e){if(this.pendingCount()>=this.cap)throw new wn(`${this.name} is full (cap ${this.cap}); ${this.pendingCount()} pending`,{details:{queue:this.name,cap:this.cap,pending:this.pendingCount()}});let{promise:n,resolve:r,reject:o}=Promise.withResolvers();return this.queue.push({fn:e,resolve:r,reject:o}),this.pump(),n}inFlightCount(){return this.inFlight}waitingCount(){return this.queue.length}pendingCount(){return this.inFlight+this.queue.length}pump(){if(this.inFlight>0)return;let e=this.queue.shift();e!==void 0&&(this.inFlight++,(async()=>{try{let n=await e.fn();e.resolve(n);}catch(n){e.reject(n);}finally{this.inFlight--,this.pump();}})());}};var oh=z.string().regex(/^\d+\/\d+$/,'must be "N/SECONDS" format, e.g. "120/60"').transform(t=>{let[e,n]=t.split("/").map(Number);return {limit:e,windowSeconds:n}}),ah=z.object({OMNIFOCUS_LOG_LEVEL:z.enum(["trace","debug","info","warn","error"]).default("info"),OMNIFOCUS_INTEGRATION:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_E2E:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_E2E_USE_MEMORY:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_ALLOW_RAW_SCRIPT:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_WEBHOOKS_ENABLED:z.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_CACHE_TTL_MS:z.coerce.number().int().positive().default(3e4),OMNIFOCUS_CACHE_CAPACITY:z.coerce.number().int().positive().default(256),OMNIFOCUS_READ_POOL_SIZE:z.coerce.number().int().min(1).max(8).default(2),OMNIFOCUS_WRITE_QUEUE_CAP:z.coerce.number().int().positive().default(50),OMNIFOCUS_JXA_TIMEOUT_MS:z.coerce.number().int().positive().default(3e4),OMNIFOCUS_OMNIJS_TIMEOUT_MS:z.coerce.number().int().positive().default(45e3),OMNIFOCUS_ATTACHMENT_PATHS:z.string().prefault(homedir()).transform(t=>t.split(":").filter(Boolean)),OMNIFOCUS_MAX_ATTACHMENT_MB:z.coerce.number().int().positive().default(100),OMNIFOCUS_TOOL_RATE_LIMIT:oh.prefault("120/60"),OMNIFOCUS_WAITING_TAG_NAME:z.string().min(1).default("waiting"),OMNIFOCUS_TEMPLATES_FOLDER_NAME:z.string().min(1).default("Templates")});function lc(t=process.env,e=n=>{process.stderr.write(`[omnifocus-mcp] Config error: ${n}
3
+ `),process.exit(1);}){let n=ah.safeParse({OMNIFOCUS_LOG_LEVEL:t.OMNIFOCUS_LOG_LEVEL,OMNIFOCUS_INTEGRATION:t.OMNIFOCUS_INTEGRATION,OMNIFOCUS_E2E:t.OMNIFOCUS_E2E,OMNIFOCUS_E2E_USE_MEMORY:t.OMNIFOCUS_E2E_USE_MEMORY,OMNIFOCUS_ALLOW_RAW_SCRIPT:t.OMNIFOCUS_ALLOW_RAW_SCRIPT,OMNIFOCUS_WEBHOOKS_ENABLED:t.OMNIFOCUS_WEBHOOKS_ENABLED,OMNIFOCUS_CACHE_TTL_MS:t.OMNIFOCUS_CACHE_TTL_MS,OMNIFOCUS_CACHE_CAPACITY:t.OMNIFOCUS_CACHE_CAPACITY,OMNIFOCUS_READ_POOL_SIZE:t.OMNIFOCUS_READ_POOL_SIZE,OMNIFOCUS_WRITE_QUEUE_CAP:t.OMNIFOCUS_WRITE_QUEUE_CAP,OMNIFOCUS_JXA_TIMEOUT_MS:t.OMNIFOCUS_JXA_TIMEOUT_MS,OMNIFOCUS_OMNIJS_TIMEOUT_MS:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS,OMNIFOCUS_ATTACHMENT_PATHS:t.OMNIFOCUS_ATTACHMENT_PATHS,OMNIFOCUS_MAX_ATTACHMENT_MB:t.OMNIFOCUS_MAX_ATTACHMENT_MB,OMNIFOCUS_TOOL_RATE_LIMIT:t.OMNIFOCUS_TOOL_RATE_LIMIT,OMNIFOCUS_WAITING_TAG_NAME:t.OMNIFOCUS_WAITING_TAG_NAME,OMNIFOCUS_TEMPLATES_FOLDER_NAME:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME});if(!n.success){let r=n.error.issues.map(o=>` ${o.path.join(".")}: ${o.message}`);return e(`Invalid environment configuration:
4
4
  ${r.join(`
5
5
  `)}
6
- See DESIGN \xA722 for allowed values.`)}return 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:
6
+ See DESIGN \xA722 for allowed values.`)}return n.data}function sh(t){return createHash("sha256").update(t).digest("hex").slice(0,12)}function pc(t){return {OMNIFOCUS_LOG_LEVEL:t.OMNIFOCUS_LOG_LEVEL,OMNIFOCUS_INTEGRATION:t.OMNIFOCUS_INTEGRATION,OMNIFOCUS_E2E:t.OMNIFOCUS_E2E,OMNIFOCUS_E2E_USE_MEMORY:t.OMNIFOCUS_E2E_USE_MEMORY,OMNIFOCUS_ALLOW_RAW_SCRIPT:t.OMNIFOCUS_ALLOW_RAW_SCRIPT,OMNIFOCUS_WEBHOOKS_ENABLED:t.OMNIFOCUS_WEBHOOKS_ENABLED,OMNIFOCUS_CACHE_TTL_MS:t.OMNIFOCUS_CACHE_TTL_MS,OMNIFOCUS_CACHE_CAPACITY:t.OMNIFOCUS_CACHE_CAPACITY,OMNIFOCUS_READ_POOL_SIZE:t.OMNIFOCUS_READ_POOL_SIZE,OMNIFOCUS_WRITE_QUEUE_CAP:t.OMNIFOCUS_WRITE_QUEUE_CAP,OMNIFOCUS_JXA_TIMEOUT_MS:t.OMNIFOCUS_JXA_TIMEOUT_MS,OMNIFOCUS_OMNIJS_TIMEOUT_MS:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS,OMNIFOCUS_ATTACHMENT_PATHS:t.OMNIFOCUS_ATTACHMENT_PATHS.map(sh),OMNIFOCUS_MAX_ATTACHMENT_MB:t.OMNIFOCUS_MAX_ATTACHMENT_MB,OMNIFOCUS_TOOL_RATE_LIMIT:t.OMNIFOCUS_TOOL_RATE_LIMIT,OMNIFOCUS_WAITING_TAG_NAME:t.OMNIFOCUS_WAITING_TAG_NAME,OMNIFOCUS_TEMPLATES_FOLDER_NAME:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME}}var ih=["name","note","noteHtml","tagNames","tagNames[*]","data.name","data.note","data.noteHtml","data.tagNames","data.tagNames[*]","*.name","*.note","*.noteHtml","*.tagNames"];function ch(t="info"){return uc({level:t,redact:{paths:ih,censor:"[redacted]"},formatters:{level(e){return {level:e}}},timestamp:uc.stdTimeFunctions.epochTime},process.stderr)}var R=ch(process.env.OMNIFOCUS_LOG_LEVEL??"info");var lh={threshold:5,errorThreshold:10,windowSeconds:60};function ph(t,e){let n=JSON.stringify(e,Object.keys(e).sort()),r=createHash("sha1").update(n).digest("hex").slice(0,16);return `${t}:${r}`}var jn=class{config;windows=new Map;constructor(e={}){this.config={...lh,...e};}record(e,n){let r=ph(e,n),o=Date.now(),a=o-this.config.windowSeconds*1e3,i=this.getAndPrune(r,a);i.push(o);let s=i.length;if(s<this.config.threshold)return;let c={code:"WARN_LOOP_DETECTED",message:`Tool "${e}" has been called ${s} time(s) with identical arguments within ${this.config.windowSeconds}s.`,suggestion:"The agent may be stuck in a loop. Verify that the previous response was acted on before repeating this call.",details:{tool:e,count:s,windowSeconds:this.config.windowSeconds}};return s>=this.config.errorThreshold?{level:"error",warning:c}:{level:"warn",warning:c}}reset(e){e===void 0?this.windows.clear():this.windows.delete(e);}getAndPrune(e,n){this.windows.has(e)||this.windows.set(e,[]);let r=this.windows.get(e),o=0;for(;o<r.length&&r[o]<=n;)o++;return o>0&&r.splice(0,o),r}};var Ar="daily-review",Cr="weekly-review",Er="capture-meeting",Mr="project-planning",uh="inbox-triage",mh="perspective-author";function fh(){return "You are running a daily OmniFocus review. Follow these steps in order:\n\n1. **Load context** \u2014 read these three resources:\n - `omnifocus://snapshot` (5-count orientation: inbox, overdue, due-today, flagged, review-due)\n - `omnifocus://overdue` (tasks whose due date has passed, sorted oldest-first)\n - `omnifocus://forecast/today` (tasks due or deferred to today, plus flagged)\n\n2. **Clear overdue** \u2014 for every task in `overdue`:\n - If it should be done today, leave it (it will appear in forecast/today too).\n - If it can be rescheduled, call `task_update` with a new `dueDate`.\n - If it should be dropped, call `task_drop` with a reason note.\n\n3. **Plan due-today** \u2014 for every task in `forecast/today.dueToday`:\n - Confirm it is still relevant. If the due date should change, call `task_update`.\n - If it is already done, call `task_complete`.\n\n4. **Decide on flagged** \u2014 for every task in `forecast/today.flagged`:\n - If it is genuinely today's priority, leave the flag.\n - If it should not be today, call `task_update` to remove the flag (`flagged: false`).\n\n5. **Inbox zero** \u2014 if `snapshot.inboxCount > 0`, load `omnifocus://inbox` and process each\n task: assign to a project (`task_update` with `projectId`), add tags, or complete it.\n\n6. **Report** \u2014 summarise what you did: counts of rescheduled, dropped, completed, and\n inbox-cleared tasks. Mention any items you left for the user to decide."}function gh(){return `You are running a weekly OmniFocus review. Follow these steps in order:
7
7
 
8
8
  1. **Load review queue** \u2014 read \`omnifocus://review-due\` (projects whose review date has
9
9
  arrived, sorted by nextReviewDate ascending). If the list is empty, report "No projects
@@ -22,7 +22,7 @@ See DESIGN \xA722 for allowed values.`)}return n.data}function rf(t){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 mf(t,e){return `You are capturing action items from meeting notes. Follow these steps:
25
+ how many tasks rescheduled or cleaned up.`}function hh(t,e){return `You are capturing action items from meeting notes. Follow these steps:
26
26
 
27
27
  1. **Read the notes** below carefully.
28
28
 
@@ -42,7 +42,7 @@ See DESIGN \xA722 for allowed values.`)}return n.data}function rf(t){return crea
42
42
  ---
43
43
  ### Meeting notes
44
44
 
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:
45
+ ${t}`}function yh(t,e,n){let r=n?` - \`folderId\`: \`${n}\``:" - No folder (top-level project)";return `You are setting up a new OmniFocus project. Follow these steps:
46
46
 
47
47
  1. **Create the project** \u2014 call \`project_create\` with:
48
48
  - \`name\`: \`${t}\`
@@ -65,7 +65,7 @@ ${r}
65
65
  ---
66
66
  ### Project brief
67
67
 
68
- ${e}`}function gf(){return `You are running an inbox-triage pass on OmniFocus. The goal is to clear the
68
+ ${e}`}function kh(){return `You are running an inbox-triage pass on OmniFocus. The goal is to clear the
69
69
  inbox in one user confirmation, not ten clicks. Follow these steps in order:
70
70
 
71
71
  1. **Load the inbox** \u2014 read \`omnifocus://inbox\`. If it is empty, report
@@ -111,35 +111,127 @@ inbox in one user confirmation, not ten clicks. Follow these steps in order:
111
111
  7. **Report** \u2014 summarise: how many tasks landed in which projects, how
112
112
  many failed and why (the \`failed[].errorCode\` is prefixed \`move:\` or
113
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}
114
+ whether to retry, skip, or hand off.`}function Ih(t,e){return `You are translating a natural-language request into a saved OmniFocus custom perspective.
115
+
116
+ User's description: "${t}"${e?`
117
+
118
+ Proposed name: "${e}"`:""}
119
+
120
+ Follow these steps in order. Do NOT skip the preview step \u2014 saving without a dry-run is the failure mode this flow exists to prevent.
121
+
122
+ ## 1. Propose a rule tree
123
+
124
+ Translate the description into a \`PerspectiveRule[]\` tree. The shape is the same one \`perspective_create\` accepts. **If the description is genuinely ambiguous, ask exactly ONE disambiguating question and stop \u2014 do not guess.** Examples of ambiguity worth asking about:
125
+ - "nearby tasks" \u2192 nearby what? Location-based filtering is unsupported; clarify whether the user means *available* (\`actionAvailability: "available"\`) or something else.
126
+ - "due this week" \u2192 today only, or any time before Sunday?
127
+ - "high priority" \u2192 flagged, or due-soon, or both?
128
+
129
+ When the description is clear, propose the tree directly without asking.
130
+
131
+ ### Rule-tree atom reference
132
+
133
+ Each atom is a single-key object. Combine atoms inside a top-level array \u2014 \`aggregation\` (\`"all"\` | \`"any"\` | \`"none"\`) controls how they combine. Use a nested \`{ aggregateType, aggregateRules }\` for compound logic.
134
+
135
+ | Atom key | Meaning |
136
+ |---|---|
137
+ | \`actionAvailability\` | \`"available"\` (not blocked, not deferred-future), \`"remaining"\` (not done), \`"completed"\`, \`"dropped"\`, \`"firstAvailable"\` |
138
+ | \`actionStatus\` | \`"flagged"\` or \`"due"\` |
139
+ | \`actionHasAllOfTags\` | \`string[]\` of tag IDs \u2014 task must carry every tag |
140
+ | \`actionHasAnyOfTags\` | \`string[]\` of tag IDs \u2014 task must carry at least one |
141
+ | \`actionHasNoProject\` | \`true\` for inbox-style filter |
142
+ | \`actionHasDueDate\` | \`true\` / \`false\` |
143
+ | \`actionHasDeferDate\` | \`true\` / \`false\` |
144
+ | \`actionIsLeaf\` | \`true\` for tasks with no children |
145
+ | \`actionIsProject\` | \`true\` to restrict to project-equivalent items |
146
+ | \`actionMatchingSearch\` | \`string[]\` substrings \u2014 searches name + note |
147
+ | \`actionWithinFocus\` | \`string[]\` of project / folder IDs \u2014 restrict to descendants |
148
+
149
+ Wrap a single atom in \`{ disabledRule: <atom> }\` to keep it in the tree but skip it during evaluation (useful when the user asks to toggle a constraint on/off).
150
+
151
+ ### Common patterns
152
+
153
+ - **Available + flagged** (high-priority "actionable now" view):
154
+ \`\`\`json
155
+ { "aggregation": "all", "rules": [{ "actionAvailability": "available" }, { "actionStatus": "flagged" }] }
156
+ \`\`\`
157
+ - **Inbox-only**:
158
+ \`\`\`json
159
+ { "aggregation": "all", "rules": [{ "actionHasNoProject": true }] }
160
+ \`\`\`
161
+ - **At-home-or-phone, no due date, available** (the ticket's example shape):
162
+ \`\`\`json
163
+ {
164
+ "aggregation": "all",
165
+ "rules": [
166
+ { "actionAvailability": "available" },
167
+ { "actionHasAnyOfTags": ["<home-tag-id>", "<phone-tag-id>"] }
168
+ ]
169
+ }
170
+ \`\`\`
171
+ Resolve the tag IDs via \`tag_list\` first if you don't have them.
172
+
173
+ ## 2. Preview via dry-run
174
+
175
+ Call \`perspective_evaluate_dry_run({ aggregation, rules })\` with the proposed tree. Report the count of matched tasks and 3\u20135 sample names. **Do not advance to step 3 if:**
176
+ - The count is 0 (the rules don't match anything \u2014 re-propose, don't save an empty view).
177
+ - The count is suspiciously broad (e.g. > 100 when the user asked for "today's actionable tasks") \u2014 re-propose with tighter constraints.
178
+
179
+ Show the agent-friendly summary inline; the user can read the count and decide.
180
+
181
+ ## 3. Save on user confirm
182
+
183
+ After the user confirms the preview matches their intent, call:
184
+
185
+ \`\`\`
186
+ perspective_create({ name: "<name>", aggregation: "<all|any|none>", rules: [...] })
187
+ \`\`\`
188
+
189
+ The \`rules\` value is the same tree you passed to \`perspective_evaluate_dry_run\` \u2014 pass it verbatim, no re-validation needed. \`perspective_create\` returns \`{ id }\`; surface that id to the user so they can reference it later (\`perspective_evaluate({ perspectiveId: "<id>" })\`).
190
+
191
+ After save, call \`perspective_list\` to confirm the new perspective is visible. Report the resulting count + name + id.
192
+
193
+ ## Failure modes
194
+
195
+ - **OF Pro required.** Custom perspectives need OmniFocus Pro. \`perspective_evaluate_dry_run\` and \`perspective_create\` both return \`OF_FEATURE_REQUIRES_PRO\` on Standard. Surface that to the user \u2014 they cannot save a perspective without upgrading.
196
+ - **Duplicate name.** \`perspective_create\` rejects duplicate names with a typed validation error. Either ask the user for a different name or append a disambiguator (e.g. \` - 2\`).
197
+ - **Don't save without a name.** If the user did not supply a name, ask for one before step 3 \u2014 don't invent one.
198
+
199
+ Read-only verification first; mutation only on explicit user approval.`}function mc(t){t.registerPrompt(Ar,{description:"Run a daily OmniFocus triage: load snapshot + overdue + forecast/today, reschedule or drop overdue tasks, confirm due-today tasks, unflag low-priority flagged tasks, and process the inbox. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:fh()}}]})),t.registerPrompt(Cr,{description:"Run a weekly OmniFocus review: walk every project due for review, check its tasks, mark it reviewed or complete/drop it, and clean up stale tasks. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:gh()}}]})),t.registerPrompt(Er,{description:"Extract action items from meeting notes and create OmniFocus tasks. Pass raw notes as `notes`; optionally target a project with `projectId`. Tasks land in the inbox when projectId is omitted.",argsSchema:{notes:z.string().min(1).describe("Raw meeting notes to extract action items from."),projectId:z.string().optional().describe("Persistent OmniFocus project ID to assign tasks to. Omit to use the inbox.")}},async({notes:e,projectId:n})=>({messages:[{role:"user",content:{type:"text",text:hh(e,n)}}]})),t.registerPrompt(Mr,{description:"Create a new OmniFocus project and populate it with tasks derived from a brief. Pass `name` and `brief`; optionally place it in a folder with `folderId`.",argsSchema:{name:z.string().min(1).describe("Name of the new project."),brief:z.string().min(1).describe("One-paragraph description of the project goal. Used to derive subtasks."),folderId:z.string().optional().describe("Persistent OmniFocus folder ID to place the project in. Omit for top-level.")}},async({name:e,brief:n,folderId:r})=>({messages:[{role:"user",content:{type:"text",text:yh(e,n,r)}}]})),t.registerPrompt(uh,{description:"Triage the OmniFocus inbox in one user confirmation. The agent reads the inbox, proposes a structured assignment per task (project, tags, defer/due, flagged), presents the proposals as a table, and on user approval fires task_batch_assign. Does NOT auto-confirm \u2014 the user's approval is the gating step. No parameters required.",argsSchema:{}},async()=>({messages:[{role:"user",content:{type:"text",text:kh()}}]})),t.registerPrompt(mh,{description:"Translate a free-text description into a saved OmniFocus custom perspective. Walks the agent through three steps: (1) propose a PerspectiveRule[] tree from the prose (asking ONE disambiguation question if genuinely ambiguous, else proposing directly), (2) preview the matched tasks via perspective_evaluate_dry_run, (3) save via perspective_create only after user confirmation. Embeds a reference card of common rule-tree atoms so the agent has the vocabulary without web access. Custom perspectives require OmniFocus Pro.",argsSchema:{description:z.string().min(1).describe("Free-text description of the perspective the user wants. Examples: 'everything I could do at home, on a phone, with under 15 minutes', 'flagged tasks that are available right now', 'inbox items with no defer date'."),name:z.string().min(1).optional().describe("Optional display name for the new perspective. When omitted, the agent asks the user for one before the save step.")}},async({description:e,name:n})=>({messages:[{role:"user",content:{type:"text",text:Ih(e,n)}}]}));}var _n=class{config;windows=new Map;constructor(e){this.config=e;}check(e){let n=Date.now(),r=n-this.config.windowSeconds*1e3,o=this.getAndPrune(e,r);if(o.length>=this.config.limit){let i=o[0]+this.config.windowSeconds*1e3-n+1;throw new Tn(`Rate limit exceeded for tool "${e}". Limit: ${this.config.limit} calls per ${this.config.windowSeconds}s.`,{details:{retryAfterMs:i}})}o.push(n);}remaining(e){let n=Date.now(),r=n-this.config.windowSeconds*1e3,o=this.getAndPrune(e,r),a=this.config.limit-o.length,i=o.length>0?new Date(o[0]+this.config.windowSeconds*1e3).toISOString():new Date(n+this.config.windowSeconds*1e3).toISOString();return {remaining:a,resetAt:i}}reset(e){this.windows.delete(e);}getAndPrune(e,n){this.windows.has(e)||this.windows.set(e,[]);let r=this.windows.get(e),o=0;for(;o<r.length&&r[o]<=n;)o++;return o>0&&r.splice(0,o),r}};function Sh(){let t=fileURLToPath(import.meta.url);return Zi.resolve(Zi.dirname(t),"../../bin/calendar-bridge")}var qe=class{binaryPath;spawn;existsSync;constructor(e={}){this.binaryPath=e.binaryPath??Sh(),this.spawn=e.spawn??spawn,this.existsSync=e.existsSync??dt.existsSync;}async ping(){return this.invoke(["ping"])}async getPermission(){return this.invoke(["permission"])}async requestAccess(){return this.invoke(["request-access"])}async readEvents(e,n,r){let o=r?{...process.env,OMNIFOCUS_CALENDAR_SOURCES:r}:process.env,a=await this.invoke(["calendar",e,n],o);if("error"in a)throw a.error==="permission-denied"?new vn({details:{permission:a.permission}}):new ge(`calendar-bridge returned error: ${a.error}`,{details:{error:a.error}});return a.events}async invoke(e,n){if(!this.existsSync(this.binaryPath))throw new ge(`calendar-bridge binary not found at ${this.binaryPath}`,{details:{binaryPath:this.binaryPath}});let r;try{r=this.spawn(this.binaryPath,e,n?{env:n}:{});}catch(o){throw new ge(`failed to spawn calendar-bridge: ${o.message}`,{cause:o,details:{binaryPath:this.binaryPath}})}return new Promise((o,a)=>{let i="",s="";r.stdout?.on("data",c=>{i+=typeof c=="string"?c:c.toString("utf8");}),r.stderr?.on("data",c=>{s+=typeof c=="string"?c:c.toString("utf8");}),r.on("error",c=>{a(new ge(`calendar-bridge spawn error: ${c.message}`,{cause:c}));}),r.on("close",c=>{if(c!==0){a(new ge(`calendar-bridge exited with code ${c}: ${s.trim()}`,{details:{exitCode:c,stderr:s.trim()}}));return}let d=i.trim().split(`
200
+ `)[0]??"";if(!d){a(new ge("calendar-bridge produced no output",{details:{stderr:s.trim()}}));return}try{o(JSON.parse(d));}catch(l){a(new ge(`calendar-bridge produced unparsable JSON: ${d.slice(0,200)}`,{cause:l,details:{line:d}}));}});})}};var bh=864e5;function gc(t,e={}){let{limit:n,windowSeconds:r}=t.OMNIFOCUS_TOOL_RATE_LIMIT,o=Math.round(n*60/r),a=e.ofEdition??"standard",i=a==="pro";return {ofVersion:e.ofVersion??"unknown",ofEdition:a,transports:{jxa:{available:true,timeoutMs:t.OMNIFOCUS_JXA_TIMEOUT_MS},omnijs:{available:true,timeoutMs:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS}},features:{customPerspectives:i,forecastTag:i,repetitionRules:i,pluginInvocation:i,rawScriptTools:t.OMNIFOCUS_ALLOW_RAW_SCRIPT},rateLimits:{defaultPerToolPerMinute:o},idempotencyTtlMs:bh,calendarAccess:e.calendarAccess??{available:false,permission:"unknown"},webhooks:e.webhooks??{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:0,names:[]}}}async function On(t=new qe){try{let{permission:e}=await t.getPermission();return {available:!0,permission:e}}catch(e){if(e instanceof ge)return R.debug({event:"capabilities.calendar_bridge_unavailable",message:e.message}),{available:false,permission:"unknown"};throw e}}var Pn="omnifocus://capabilities";function hc(t,e){t.registerResource("omnifocus-capabilities",Pn,{description:"Structured capabilities object for this omnifocus-mcp server instance. Read once at session start to discover available features (Pro vs Standard), transport timeouts, rate limits, calendar-bridge availability, and whether raw-script tools are enabled. ofVersion and ofEdition are 'unknown'/'standard' until the lazy OF probe runs.",mimeType:"application/json"},async n=>({contents:[{uri:Pn,mimeType:"application/json",text:JSON.stringify(await e(),null,2)}]}));}var yc=/^[A-Za-z0-9._-]{3,64}$/;function _h(t){return typeof t=="string"&&yc.test(t)}function Gt(t){let e=z.string().regex(yc,`Invalid ${t}: expected 3-64 alphanumeric / _ / - characters`).transform(n=>n);return {kind:t,of(n){return e.parse(n)},is(n){return _h(n)},schema:e}}var h=Gt("TaskId"),k=Gt("ProjectId"),S=Gt("TagId"),W=Gt("FolderId"),Oe=Gt("AttachmentId");var Tc="omnifocus://agenda{?date}",Oh=6e4;function kc(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0)}function xh(t){let e=new Date(t);if(Number.isNaN(e.getTime()))throw new y(`agenda: could not parse date as ISO-8601: ${t}`,{details:{raw:t}});return e}function Ic(t){return t.dueDate!==null}function vc(t,e){return {kind:"of-task",id:t.id,name:t.name,startsAt:e,dueDate:t.dueDate,deferDate:t.deferDate,flagged:t.flagged,projectId:t.projectId,parentId:t.parentId}}var Nr=class{entry=null;get(e,n){return !this.entry||this.entry.key!==e||this.entry.value.expiresAt<=n?null:this.entry.value.payload}set(e,n,r){this.entry={key:e,value:{payload:n,expiresAt:r}};}},Dh=new Nr;async function Rh(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??Oh,a=t.sources,i=e.date?kc(xh(e.date)):kc(r),s=new Date(i.getTime()+1440*60*1e3),c=i.toISOString(),d=s.toISOString(),l=t.cache??Dh,m=`${c}|${d}|${a??""}`,f=l.get(m,r.getTime());if(f)return f;let[g,v]=await Promise.all([t.bridge.readEvents(c,d,a),t.forecastService.get({from:c,to:d})]),I=new Set,T=[];for(let M of [...v.dueToday,...v.deferredToday,...v.flagged,...v.overdue]){let H=String(M.id);I.has(H)||(I.add(H),T.push(M));}let E=g.map(M=>({kind:"calendar-event",...M})),w=T.filter(Ic),D=T.filter(M=>!Ic(M)),j=w.map(M=>vc(M,M.dueDate)),B=[...E,...j].sort((M,H)=>M.startsAt!==H.startsAt?M.startsAt<H.startsAt?-1:1:xn(M).localeCompare(xn(H))),_=D.map(M=>vc(M,"")).sort((M,H)=>xn(M).localeCompare(xn(H))),U={items:B,floating:_};return l.set(m,U,r.getTime()+o),U}function xn(t){return t.kind==="calendar-event"?t.title:t.name}function wc(t,e,n=new qe){t.registerResource("omnifocus-agenda",new ResourceTemplate(Tc,{list:void 0}),{description:"Merged daily agenda: macOS calendar events from EventKit interleaved with the OmniFocus forecast for the same day. Returns { items, floating } where items[] is the sorted timeline (calendar-event and of-task entries by startsAt ASC) and floating[] is OF tasks with no dueDate. Each AgendaItem is tagged kind: 'calendar-event' | 'of-task'. date query param is ISO-8601; defaults to today (local zone). Cached 60s. Throws CalendarPermissionDenied when Calendar access has not been granted; throws CalendarBridgeUnavailable when the Swift binary is missing.",mimeType:"application/json"},async(r,o)=>{let a=o,i=await Rh({bridge:n,forecastService:e,sources:process.env.OMNIFOCUS_CALENDAR_SOURCES},{date:a.date});return {contents:[{uri:r.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Sc="omnifocus://burndown/{projectId}";function Fr(t){return Math.round(t*100)/100}async function Ch(t,e,n=new Date){let r;try{r=k.of(e);}catch{return {error:{code:"ProjectNotFound",message:`Project not found: ${e}`}}}let a=(await t.listProjects()).find(z=>String(z.id)===String(r));if(!a)return {error:{code:"ProjectNotFound",message:`Project not found: ${e}`}};if(!a.dueDate)return {error:{code:"NoDueDate",message:`Project ${String(a.id)} has no due date \u2014 burndown requires a deadline.`}};let[i,s]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true})]),c=String(a.id),d=i.filter(z=>!z.dropped&&z.projectId!==null&&String(z.projectId)===c),l=s.filter(z=>z.projectId!==null&&String(z.projectId)===c),m=d.length+l.length,f=l.length,g=d.length,v=[...d,...l],I=v[0],T=v.length>0&&I!==void 0?v.reduce((z,le)=>le.createdAt<z?le.createdAt:z,I.createdAt):a.createdAt,E=new Date(T).getTime(),w=new Date(a.dueDate).getTime(),D=n.getTime(),j=Math.max(1,(w-E)/864e5),B=Math.max(0,Math.min(j,(D-E)/864e5)),_=Fr(B/j*100),U=m===0?100:Fr(f/m*100),M=Fr((U-_)/100*j),H;return m===0?H="No tasks in project \u2014 burndown is trivially complete.":M>0?H=`Ahead of pace by ${M.toFixed(1)} day(s). Ideal: ${_.toFixed(1)}% done; actual: ${U.toFixed(1)}%.`:M<0?H=`Behind pace by ${Math.abs(M).toFixed(1)} day(s). Ideal: ${_.toFixed(1)}% done; actual: ${U.toFixed(1)}%.`:H=`On pace. Ideal: ${_.toFixed(1)}%; actual: ${U.toFixed(1)}%.`,H+=" (Ideal line is naive linear from earliest-task creation to project due date. Task weights and blockers are not modelled.)",{projectId:c,name:a.name,dueDate:a.dueDate,startDate:T,totalTasks:m,completedTasks:f,remainingTasks:g,idealLinePercent:_,actualPercent:U,deltaDays:M,note:H}}function bc(t,e){t.registerResource("omnifocus-burndown",new ResourceTemplate(Sc,{list:void 0}),{description:"Per-project burndown chart data \u2014 remaining vs completed tasks against a naive linear ideal line. Returns totalTasks, completedTasks, remainingTasks, idealLinePercent, actualPercent, and deltaDays (positive = ahead of pace; negative = behind). Requires the project to have a due date; returns a typed NoDueDate error otherwise. Returns ProjectNotFound when the projectId is unknown. Ideal line is a linear model from earliest-task creation to due date \u2014 task weights and blockers are not modelled. Pairs with omnifocus://velocity for macro-level and omnifocus://retrospective for qualitative review. Read-only.",mimeType:"application/json"},async(n,r)=>{let a=r.projectId??"",i=await Ch(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var jc="omnifocus://calendar{?from,to}",Mh=6e4;function Nh(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function Fh(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate()+1,0,0,0,0).toISOString()}var Ur=class{entry=null;get(e,n){return !this.entry||this.entry.key!==e||this.entry.value.expiresAt<=n?null:this.entry.value.payload}set(e,n,r){this.entry={key:e,value:{payload:n,expiresAt:r}};}clear(){this.entry=null;}};async function Uh(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??Mh,a=t.sources,i=e.from??Nh(r),s=e.to??Fh(r),c=t.cache??Lh,d=`${i}|${s}|${a??""}`,l=c.get(d,r.getTime());if(l)return l;let f={events:await t.bridge.readEvents(i,s,a)};return c.set(d,f,r.getTime()+o),f}var Lh=new Ur;function _c(t,e=new qe){t.registerResource("omnifocus-calendar",new ResourceTemplate(jc,{list:void 0}),{description:"macOS Calendar events from EventKit in the half-open interval [from, to). Both query params are ISO-8601; when omitted, defaults span the current local-zone day. Returns { events: CalendarEvent[] } where each event carries id, title, startsAt, endsAt, allDay, calendarName, calendarSource, optional location, status (confirmed|tentative|cancelled), and optional isAttendee. Read-only \u2014 does not write to EventKit. Filter calendar sources via the OMNIFOCUS_CALENDAR_SOURCES env var (comma-separated, substring match against calendar.title, case-insensitive). Cached 60s. First call may trigger the macOS Calendar TCC prompt; subsequent calls return the cached state. Throws CalendarPermissionDenied when access has not been granted.",mimeType:"application/json"},async(n,r)=>{let o=r,a=await Uh({bridge:e,sources:process.env.OMNIFOCUS_CALENDAR_SOURCES},{from:o.from,to:o.to});return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(a,null,2)}]}});}var Lr=[{phrase:"add a task",aliases:["create a task","new task","remind me to","todo"],description:"When I describe something I need to do, capture it as a task.",sequence:[{kind:"tool",name:"task_create"}],category:"capture"},{phrase:"save this meeting",aliases:["capture meeting notes","log this meeting","record this discussion"],description:"When I want to record a meeting and its action items, walk me through capture.",sequence:[{kind:"prompt",name:"capture-meeting"}],category:"capture"},{phrase:"plan a project",aliases:["scope a project","set up a project","plan out"],description:"When I want to scope a new project, walk me through structured planning.",sequence:[{kind:"prompt",name:"project-planning"}],category:"capture"},{phrase:"what's on my plate today",aliases:["what do I have today","today's tasks","what's due today","today's forecast"],description:"When I ask what's on my plate today, give me the forecast organized by category.",sequence:[{kind:"resource",uri:"omnifocus://snapshot"},{kind:"resource",uri:"omnifocus://forecast/today"}],category:"plan"},{phrase:"pack today's deep work",aliases:["plan a focused day","fit work into the day","schedule my day"],description:"When I want to fit my available work into a time budget, run forecast_pack.",sequence:[{kind:"tool",name:"forecast_pack"}],category:"plan"},{phrase:"what's coming up",aliases:["upcoming work","next few days","what's due soon"],description:"When I want the near-term outlook, pull the forecast for the next several days.",sequence:[{kind:"tool",name:"forecast_get",args:{days:7}}],category:"plan"},{phrase:"weekly review",aliases:["GTD weekly review","do my weekly","weekly catch-up"],description:"When I want to do my weekly review, walk me through it section by section.",sequence:[{kind:"prompt",name:"weekly-review"}],category:"review"},{phrase:"daily review",aliases:["start my day","daily catch-up","morning review"],description:"When I want to orient at the start of the day, walk me through the daily check-in.",sequence:[{kind:"prompt",name:"daily-review"}],category:"review"},{phrase:"what changed today",aliases:["recent activity","what's new","what happened today"],description:"When I want a chronological view of recent task activity, pull the recent-activity resource.",sequence:[{kind:"resource",uri:"omnifocus://recent-activity"}],category:"review"},{phrase:"what's overdue",aliases:["am I behind","show overdue","missed deadlines"],description:"When I want to see what's slipped, pull the overdue list.",sequence:[{kind:"resource",uri:"omnifocus://overdue"}],category:"review"},{phrase:"what needs review",aliases:["projects due for review","what to review","review queue"],description:"When I want to catch projects that haven't been reviewed in a while, pull the review-due list.",sequence:[{kind:"resource",uri:"omnifocus://review-due"}],category:"review"},{phrase:"process my inbox",aliases:["clear my inbox","triage inbox","inbox zero"],description:"When I want to triage the inbox, pull the inbox tasks so I can route each one.",sequence:[{kind:"resource",uri:"omnifocus://inbox"}],category:"triage"},{phrase:"what's flagged",aliases:["my flagged work","starred tasks","priority queue"],description:"When I want my self-curated priority queue, pull the flagged tasks.",sequence:[{kind:"resource",uri:"omnifocus://flagged"}],category:"triage"},{phrase:"what did I close last week",aliases:["what did I finish","completed work","closed last week"],description:"When I want to look back at finished work, pull the retrospective for the date range.",sequence:[{kind:"resource",uri:"omnifocus://retrospective"}],category:"retrospect"},{phrase:"how am I tracking",aliases:["my velocity","completion rate","throughput"],description:"When I want a macro-level signal on completion pace, pull the velocity resource.",sequence:[{kind:"resource",uri:"omnifocus://velocity"}],category:"retrospect"},{phrase:"is this project on pace",aliases:["project burndown","project progress","am I behind on"],description:"When I want to see whether one project is on pace toward its due date, pull burndown for that project.",sequence:[{kind:"resource",uri:"omnifocus://burndown/{projectId}"}],category:"retrospect"},{phrase:"export this project as text",aliases:["copy project to clipboard","project as taskpaper","share this project"],description:"When I want a portable text view of a project, export it as TaskPaper.",sequence:[{kind:"tool",name:"export_taskpaper"}],category:"share"},{phrase:"export as outline",aliases:["project as opml","outline export"],description:"When I want an outline-shaped export I can paste into another tool, use OPML.",sequence:[{kind:"tool",name:"export_opml"}],category:"share"},{phrase:"find duplicates",aliases:["taxonomy collisions","duplicate tags","duplicate projects"],description:"When I suspect taxonomy drift (same tag spelled two ways, etc.), pull the audit resource.",sequence:[{kind:"resource",uri:"omnifocus://taxonomy-audit"}],category:"audit"},{phrase:"what's stalled",aliases:["stalled projects","projects without next actions","blocked projects"],description:"When I want to find projects that look stuck, pull the project-health resource.",sequence:[{kind:"tool",name:"project_list",args:{status:"active"}}],category:"audit"},{phrase:"run an OmniFocus plug-in",aliases:["invoke automation","trigger plugin"],description:"When I name an installed Omni Automation plug-in, invoke it by identifier.",sequence:[{kind:"tool",name:"plugin_invoke"}],category:"automate"}];var Br="omnifocus://intents";function Bh(t=new Date){return {intents:Lr,count:Lr.length,generatedAt:t.toISOString()}}function Pc(t){t.registerResource("omnifocus-intents",Br,{description:"Curated routing table mapping human-style user phrases to canonical tool / prompt / resource sequences. Eighty registered tools, eight verbs (capture, plan, review, triage, retrospect, share, audit, automate). Read at session start \u2014 or when uncertain which tool fits the user's intent \u2014 to discover obvious paths. The agent is NOT constrained by this resource; it's a fallback for ambiguity, not a gatekeeper. Each intent has a phrase, aliases, a one-sentence description in the user's voice, and an ordered sequence of steps (tool calls, prompts, or resource reads). Steps may carry template `args` placeholders the agent fills. Read-only.",mimeType:"application/json"},async e=>({contents:[{uri:Br,mimeType:"application/json",text:JSON.stringify(Bh(),null,2)}]}));}var Jh=new Set(["today","tomorrow","yesterday","this-week","next-week","end-of-week","end-of-month"]);function qt(t){return typeof t=="string"&&Jh.has(t)}function ot(t){let e=-t.getTimezoneOffset(),n=e>=0?"+":"-",r=Math.abs(e),o=String(Math.floor(r/60)).padStart(2,"0"),a=String(r%60).padStart(2,"0"),i=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),c=String(t.getDate()).padStart(2,"0");return `${i}-${s}-${c}T00:00:00${n}${o}:${a}`}function Vt(t,e=new Date){let n=new Date(e);switch(n.setHours(0,0,0,0),t){case "today":return ot(n);case "tomorrow":{let r=new Date(n);return r.setDate(r.getDate()+1),ot(r)}case "yesterday":{let r=new Date(n);return r.setDate(r.getDate()-1),ot(r)}case "this-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?-6:1-o)),ot(r)}case "next-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?1:8-o)),ot(r)}case "end-of-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?0:7-o)),ot(r)}case "end-of-month":{let r=new Date(n);return r.setMonth(r.getMonth()+1,0),ot(r)}}}var Oc=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;function Kt(t){return typeof t=="string"&&Oc.test(t)&&!Number.isNaN(Date.parse(t))}function oe(){return z.string().regex(Oc,"Expected ISO-8601 with offset (e.g. 2026-04-19T12:00:00-05:00 or 2026-04-19T17:00:00Z). Bare local time is rejected.").refine(t=>!Number.isNaN(Date.parse(t)),{message:"Well-formed ISO-8601 but not a valid date (out-of-range month, hour, or minute)."})}function Le(){return z.string().transform((t,e)=>Kt(t)?t:qt(t)?Vt(t):(e.addIssue({code:z.ZodIssueCode.custom,message:`Expected ISO-8601 with offset or a relative shortcut (today, tomorrow, yesterday, this-week, next-week, end-of-week, end-of-month). Got: "${t}".`}),z.NEVER))}function Be(t,e){if(t===null||t.length===0)return;let n=`\`\`\`${e}`,o=new RegExp(`(^|\\n)${$h(n)}[ \\t]*\\n`).exec(t);if(o===null)return;let a=o.index+o[0].length,i=/\n```[ \t]*(?=\n|$)/;i.lastIndex=a;let s=t.slice(a),c=i.exec(s);return c===null?void 0:{body:s.slice(0,c.index),start:o.index+(o[1]===""?0:1),end:a+c.index+c[0].length}}function kt(t){let e={},n=t.split(`
201
+ `);for(let r of n){let o=r.trim();if(o==="")continue;let a=o.indexOf(":");if(a===-1)continue;let i=o.slice(0,a).trim();if(i==="")continue;let s=o.slice(a+1).trim();(s.startsWith('"')&&s.endsWith('"')&&s.length>=2||s.startsWith("'")&&s.endsWith("'")&&s.length>=2)&&(s=s.slice(1,-1)),e[i]=s;}return e}function It(t){let e=[];for(let[n,r]of Object.entries(t))r!==void 0&&e.push(`${n}: ${r}`);return e.join(`
202
+ `)}function vt(t,e,n){let r=`\`\`\`${e}
117
203
  ${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}
204
+ \`\`\``,o=Be(t,e);return o!==void 0&&t!==null?t.slice(0,o.start)+r+t.slice(o.end):t===null||t.length===0?r:`${r}
119
205
 
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}
206
+ ${t}`}function Rn(t,e){if(t===null)return null;let n=Be(t,e);if(n===void 0)return t.length===0?null:t;let r=t.slice(0,n.start).replace(/[ \t]*\n+$/,""),o=t.slice(n.end).replace(/^\n+[ \t]*/,""),a;return r===""||o===""?a=r+o:a=`${r}
121
207
 
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}
208
+ ${o}`,a===""?null:a}function $h(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var $r="decision-journal",Wr=["stall-is-intentional","deferred-by-choice","blocked-on-external","awaiting-decision","acknowledged-zombie"],Wh=z.object({kind:z.enum(Wr).describe("The kind of judgment recorded."),reason:z.string().min(1).describe("Human-readable reason for the decision."),recordedAt:oe().describe("When the decision was recorded (ISO-8601 with offset)."),until:oe().optional().describe("Optional auto-expiry. When set and in the past, the decision is treated as expired and downstream consumers re-surface the target.")});function Ae(t){let e=Be(t,$r);if(e===void 0)return;let n=kt(e.body),r={};typeof n.kind=="string"&&n.kind.length>0&&(r.kind=n.kind),typeof n.reason=="string"&&n.reason.length>0&&(r.reason=n.reason),typeof n.recordedAt=="string"&&n.recordedAt.length>0&&(r.recordedAt=n.recordedAt),typeof n.until=="string"&&n.until.length>0&&(r.until=n.until);let o=Wh.safeParse(r);return o.success?o.data:void 0}function xc(t,e=new Date){return t.until===void 0?true:new Date(t.until).getTime()>e.getTime()}function Hr(t,e){let n=It({kind:e.kind,reason:e.reason,recordedAt:e.recordedAt,until:e.until});return vt(t,$r,n)}function zr(t){return Rn(t,$r)}function Xt(t,e,n,r=14){if(t.status!=="active"||t.completed||t.dropped||t.deferDate&&new Date(t.deferDate).getTime()>n.getTime())return false;let o=e??t.modifiedAt,a=new Date(o).getTime();return (n.getTime()-a)/(1440*60*1e3)>=r}var Rc="omnifocus://project-health{?staleDays}";function zh(t){let e=new Map;for(let n of t){if(!n.projectId)continue;let r=String(n.projectId),o=e.get(r);o?o.push(n):e.set(r,[n]);}return e}function Gh(t){if(t.length===0)return null;let e=t[0]??null;for(let n=1;n<t.length;n++){let r=t[n];r&&(e===null||r>e)&&(e=r);}return e}function Dc(t,e){let n=e.getTime()-new Date(t).getTime();return Math.max(0,Math.floor(n/(1440*60*1e3)))}function qh(t,e,n){let r=e.filter(T=>!T.completed&&!T.dropped),o=e.map(T=>T.modifiedAt),a=Gh(o),i=a??t.modifiedAt,s=Dc(i,n),c=r.filter(T=>T.available).length,d=r.filter(T=>T.blocked).length,l=r.length===0,m=n.toISOString(),f=r.length>0&&r.every(T=>T.deferDate!==null&&T.deferDate>m),g=t.lastReviewDate,v=g?Dc(g,n):null,I;return t.nextReviewDate===null?I=true:I=t.nextReviewDate<=m,{lastTaskActivityAt:a,daysSinceActivity:s,availableTaskCount:c,blockedTaskCount:d,hasNoActions:l,deferredFutureTasks:f,lastReviewedAt:g,daysSinceReview:v,overdueForReview:I}}function Vh(t,e,n){return n?!!(e||t.availableTaskCount===0||t.overdueForReview||t.deferredFutureTasks):false}function Kh(t){let e=0;if(t.overdueForReview){let n=t.daysSinceReview??9999;e+=1e6+n;}return e+=1e3+t.daysSinceActivity,t.availableTaskCount===0&&(e+=50),t.hasNoActions&&(e+=25),e}async function Xh(t,e=14,n=new Date){let[r,o]=await Promise.all([t.listProjects(),t.listTasks({})]),a=zh(o),i=[],s=[];for(let d of r){let l=d.status==="active"&&!d.completed&&!d.dropped;if(!l)continue;let m=a.get(String(d.id))??[],f=qh(d,m,n),g=Xt(d,f.lastTaskActivityAt,n,e);if(!Vh(f,g,l))continue;let v=Ae(d.note),I=v!==void 0&&xc(v,n),T={projectId:String(d.id),name:d.name,status:d.status,signals:f,...v!==void 0&&{decision:v},_severity:Kh(f)};I?s.push(T):i.push(T);}let c=(d,l)=>d._severity!==l._severity?l._severity-d._severity:d.name.localeCompare(l.name);return i.sort(c),s.sort(c),{projects:i.map(({_severity:d,...l})=>l),acknowledged:s.map(({_severity:d,...l})=>l),staleDays:e,generatedAt:n.toISOString()}}function Yh(t){if(!t)return 14;let e=Number.parseInt(t,10);return !Number.isFinite(e)||e<1?14:e}function Ac(t,e){t.registerResource("omnifocus-project-health",new ResourceTemplate(Rc,{list:void 0}),{description:"Triage list of active projects flagged by health-warning conditions \u2014 the weekly-review answer to 'which active projects are stalled?' Returns per-project signals (lastTaskActivityAt, daysSinceActivity, availableTaskCount, blockedTaskCount, hasNoActions, deferredFutureTasks, lastReviewedAt, daysSinceReview, overdueForReview), filtered to projects matching \u22651 of: \u2265 staleDays since last activity (default 14, override with ?staleDays=N), zero available tasks, overdue for review, all tasks deferred into the future. Projects with an active decision-journal entry (#485) are partitioned into a separate `acknowledged` array rather than `projects` \u2014 auditable, not invisible. When a decision's `until` passes, the project re-emerges in `projects` automatically. Sorted by severity (review-overdue first, then longest no-activity, then no-available-tasks). Granular signals \u2014 leaves the judgment (blocked vs abandoned) to the agent. Read-only.",mimeType:"application/json"},async(n,r)=>{let a=Yh(r.staleDays),i=await Xh(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Cc="omnifocus://recent-activity{?hours}",Gr=24,Qh=168;function ey(t){if(t==null||t==="")return Gr;let e=Number(t);return !Number.isFinite(e)||e<0?Gr:Math.min(Math.max(1,Math.round(e)),Qh)}async function ty(t,e){let n=new Date(Date.now()-e*36e5).toISOString(),[r,o,a]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true,completedSince:n}),t.listProjects()]),i=r.filter(m=>!m.dropped&&m.createdAt>=n).map(m=>({taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,createdAt:m.createdAt})).sort((m,f)=>f.createdAt>m.createdAt?1:f.createdAt<m.createdAt?-1:0),s=o.filter(m=>m.completedAt!==null).map(m=>{let f=m.completedAt,g=new Date(f).getTime()-new Date(m.createdAt).getTime();return {taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,completedAt:f,age_days_at_completion:Math.max(0,Math.round(g/864e5))}}).sort((m,f)=>f.completedAt>m.completedAt?1:f.completedAt<m.completedAt?-1:0),c=r.filter(m=>m.dropped&&m.droppedAt!==null&&m.droppedAt>=n).map(m=>({taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,droppedAt:m.droppedAt})).sort((m,f)=>f.droppedAt>m.droppedAt?1:f.droppedAt<m.droppedAt?-1:0),d=r.filter(m=>!m.dropped&&m.deferDate!==null&&m.modifiedAt>=n).map(m=>({taskId:String(m.id),name:m.name,projectId:m.projectId!==null?String(m.projectId):null,deferDate:m.deferDate})).sort((m,f)=>f.deferDate>m.deferDate?1:f.deferDate<m.deferDate?-1:0),l=a.filter(m=>m.modifiedAt>=n).map(m=>({projectId:String(m.id),name:m.name,status:m.status,modifiedAt:m.modifiedAt})).sort((m,f)=>f.modifiedAt>m.modifiedAt?1:f.modifiedAt<m.modifiedAt?-1:0);return {window:{hours:e,since:n},tasksCreated:i,tasksCompleted:s,tasksDropped:c,tasksDeferred:d,projectsModified:l,summary:{taskCreatedCount:i.length,taskCompletedCount:s.length,taskDroppedCount:c.length,taskDeferredCount:d.length,projectsAffected:l.length}}}function Ec(t,e){t.registerResource("omnifocus-recent-activity",new ResourceTemplate(Cc,{list:void 0}),{description:"Session-priming activity feed for the last N hours (default 24, max 168). Returns tasks created, completed, dropped, and deferred, plus projects modified. Safe to read at every session start \u2014 designed for agent context priming. All sections sorted by timestamp descending. Empty sections return [], never omitted. Fidelity notes: tasksCreated includes active tasks only (completed-within-window appear in tasksCompleted); tasksDeferred uses modifiedAt as a proxy (tasks modified in window with a deferDate set); projectsModified includes any project modification, not status-change-only.",mimeType:"application/json"},async(n,r)=>{let o=ey(r.hours),a=o===Gr?"omnifocus://recent-activity":`omnifocus://recent-activity?hours=${o}`,i=await ty(e,o);return {contents:[{uri:a,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Nc="omnifocus://retrospective{?from,to}",ry=7;function oy(t,e,n=()=>new Date){let r=n(),o=r.toISOString(),a=new Date(r.getTime()-ry*864e5).toISOString(),i=Mc(t)?t:a,s=Mc(e)?e:o;return i>s?{from:s,to:i}:{from:i,to:s}}function Mc(t){if(t==null||t==="")return false;let e=new Date(t);return Number.isFinite(e.getTime())}async function ay(t,e){let{from:n,to:r}=e,[o,a]=await Promise.all([t.listTasks({completed:true,completedSince:n}),t.listTasks({completed:false})]),i=o.filter(l=>l.completedAt!==null&&l.completedAt<=r).map(l=>{let m=l.completedAt,f=new Date(m).getTime()-new Date(l.createdAt).getTime();return {taskId:String(l.id),name:l.name,projectId:l.projectId!==null?String(l.projectId):null,completedAt:m,age_days_at_completion:Math.max(0,Math.round(f/864e5))}}).sort((l,m)=>m.completedAt>l.completedAt?1:m.completedAt<l.completedAt?-1:0),s=a.filter(l=>l.dropped&&l.droppedAt!==null&&l.droppedAt>=n&&l.droppedAt<=r).map(l=>({taskId:String(l.id),name:l.name,projectId:l.projectId!==null?String(l.projectId):null,droppedAt:l.droppedAt})).sort((l,m)=>m.droppedAt>l.droppedAt?1:m.droppedAt<l.droppedAt?-1:0),c=a.filter(l=>!l.dropped&&l.deferDate!==null&&l.modifiedAt>=n&&l.modifiedAt<=r).map(l=>({taskId:String(l.id),name:l.name,projectId:l.projectId!==null?String(l.projectId):null,deferDate:l.deferDate})).sort((l,m)=>m.deferDate>l.deferDate?1:m.deferDate<l.deferDate?-1:0),d=new Set;for(let l of i)l.projectId!==null&&d.add(l.projectId);for(let l of s)l.projectId!==null&&d.add(l.projectId);for(let l of c)l.projectId!==null&&d.add(l.projectId);return {window:{from:n,to:r},completed:i,dropped:s,rolled:c,summary:{completedCount:i.length,droppedCount:s.length,rolledCount:c.length,projectsActive:d.size}}}function Fc(t,e){t.registerResource("omnifocus-retrospective",new ResourceTemplate(Nc,{list:void 0}),{description:"Retrospective for a date range \u2014 closes the weekly-review reflection loop. Returns tasks completed (with age-at-completion in days), tasks dropped, and tasks 'rolled' (deferred-forward heuristic) within the window, plus a summary count and the distinct-projects-active count. Defaults to the trailing 7 days when from/to are omitted; partial windows fill the missing side with the conventional default. Fidelity notes: 'rolled' is a heuristic \u2014 OF does not expose deferDate change history, so we use modifiedAt-in-window as a proxy for 'recently re-deferred'. Treat as a signal, not an exact count of defer hops. Cached \u2014 historical data doesn't change quickly. Read-only.",mimeType:"application/json"},async(n,r)=>{let o=r,a=oy(o.from,o.to),i=await ay(e,a);return {contents:[{uri:`omnifocus://retrospective?from=${encodeURIComponent(a.from)}&to=${encodeURIComponent(a.to)}`,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var qr="omnifocus://stats";function sy(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function iy(t){let e=new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0),n=e.getDay(),r=n===0?-6:1-n;return e.setDate(e.getDate()+r),e.toISOString()}function cy(t){let e=new Map;for(let n of t){if(!n.projectId)continue;let r=String(n.projectId),o=e.get(r);o?o.push(n):e.set(r,[n]);}return e}function dy(t){if(t.length===0)return null;let e=t[0]??null;for(let n=1;n<t.length;n++){let r=t[n];r&&(e===null||r>e)&&(e=r);}return e}async function ly(t,e=new Date){let[n,r,o,a,i,s]=await Promise.all([t.listTasks({}),t.listTasks({inbox:true,completed:false}),t.listProjects(),t.listProjectsDueForReview(),t.listTags(),t.getLastSync()]),c=sy(e),d=iy(e),l=e.toISOString(),m=0,f=0,g=0,v=0,I=0,T=0,E=0,w=0,D=0;for(let $ of n){if(m+=1,$.completed){$.completedAt&&($.completedAt>=d&&(T+=1),$.completedAt>=c&&(I+=1));continue}if($.dropped){$.droppedAt&&$.droppedAt>=c&&(D+=1);continue}$.available&&(f+=1),$.blocked&&(g+=1),$.flagged&&(w+=1),$.deferDate&&$.deferDate>l&&(v+=1),$.dueDate&&$.dueDate<l&&(E+=1);}let j=0,B=0,_=0,U=0,M=cy(n),H=0;for(let $ of o){$.status==="active"&&!$.completed&&!$.dropped&&(j+=1),$.status==="on-hold"&&(B+=1),($.completed||$.status==="done")&&(_+=1),($.dropped||$.status==="dropped")&&(U+=1);let fe=M.get(String($.id))??[],Ne=dy(fe.map(nt=>nt.modifiedAt));Xt($,Ne,e)&&(H+=1);}let z=null;if(r.length>0){let $=r.map(Ne=>Ne.createdAt).reduce((Ne,nt)=>nt<Ne?nt:Ne),fe=e.getTime()-new Date($).getTime();z=Math.max(0,Math.floor(fe/(1440*60*1e3)));}let le=new Set;for(let $ of n)for(let fe of $.tagIds)le.add(String(fe));let Ge=le.size,A=null;if(s.lastSyncAt){let $=e.getTime()-new Date(s.lastSyncAt).getTime();A=Math.max(0,Math.floor($/1e3));}return {tasks:{total:m,available:f,blocked:g,deferred:v,completed_today:I,completed_this_week:T,overdue_count:E,flagged_count:w,dropped_today:D},projects:{total:o.length,active:j,on_hold:B,completed:_,dropped:U,stalled_count:H,due_for_review_count:a.length},inbox:{count:r.length,oldest_age_days:z},tags:{total:i.length,with_tasks_count:Ge},database:{sync_age_seconds:A,last_sync_at:s.lastSyncAt}}}function Uc(t,e){t.registerResource("omnifocus-stats",qr,{description:"Server-side aggregate counts for the OmniFocus database \u2014 tasks, projects, inbox, tags, sync. Use for 'how is my system doing?' queries (weekly review, daily standup, capacity planning) instead of listing every task and tallying client-side. Returns tasks { total, available, blocked, deferred, completed_today, completed_this_week, overdue_count, flagged_count, dropped_today }, projects { total, active, on_hold, completed, dropped, stalled_count, due_for_review_count }, inbox { count, oldest_age_days }, tags { total, with_tasks_count }, database { sync_age_seconds, last_sync_at }. Stalled = active project with \u2265 14 days since last task activity and no future defer date. Read-only.",mimeType:"application/json"},async n=>{let r=await ly(e);return {contents:[{uri:qr,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function py(t,e){if(t===e)return 0;if(t.length===0)return e.length;if(e.length===0)return t.length;let n=Array.from({length:e.length+1},(o,a)=>a),r=new Array(e.length+1);for(let o=1;o<=t.length;o++){r[0]=o;for(let a=1;a<=e.length;a++){let i=t[o-1]===e[a-1]?0:1;r[a]=Math.min(r[a-1]+1,n[a]+1,n[a-1]+i);}[n,r]=[r,n];}return n[e.length]}function Yt(t){return t.toLowerCase().trim().replace(/\s+/g," ").replace(/^@/,"")}function Lc(t){return Yt(t).split(/[\s\-_]+/).filter(Boolean).sort()}function uy(t,e){let n=Lc(t),r=Lc(e);return n.length!==r.length?false:n.every((o,a)=>o===r[a])}function Vr(t,e){if(t===e)return "exact-duplicate";let n=Yt(t),r=Yt(e);return n===r?"case-difference":n===`${r}s`||r===`${n}s`||n===`${r}es`||r===`${n}es`?"plural-singular":py(n,r)<=2||uy(t,e)?"near-duplicate":null}var my=2,fy=new Set(["a","an","and","as","at","by","for","from","in","is","it","of","on","or","the","to","with"]),gy=.7,hy=.2,yy=.05,ky=.05;function Cn(t){return t?t.toLowerCase().split(/[^a-z0-9']+/i).filter(e=>e.length>=my&&!fy.has(e)):[]}function Bc(t,e){if(t.size===0&&e.size===0)return 0;let n=0;for(let o of t)e.has(o)&&(n+=1);let r=t.size+e.size-n;return r===0?0:n/r}function Jc(t,e){let n=new Set(Cn(t.name)),r=new Set(Cn(e.name)),o=Bc(n,r),a=0;if(t.note&&e.note){let m=new Set(Cn(t.note)),f=new Set(Cn(e.note));m.size>0&&f.size>0&&(a=Bc(m,f));}let i=[...n][0],s=[...r][0],c=i!==void 0&&i===s?1:0,d=Yt(t.name)===Yt(e.name)?1:0,l=o*gy+a*hy+c*yy+d*ky;return Math.max(0,Math.min(1,l))}var Kr="omnifocus://taxonomy-audit";async function Iy(t){let[e,n]=await Promise.all([t.listTags(),t.listProjects()]),r=vy(e),o=Ty(n);return {tagCollisions:r,projectCollisions:o}}function vy(t){let e=t.map(n=>({tagId:String(n.id),name:n.name,taskCount:n.taskCount}));return Hc(e,(n,r)=>Vr(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function Ty(t){let e=t.map(n=>({projectId:String(n.id),name:n.name,folderId:n.folderId!==null?String(n.folderId):null,taskCount:n.taskCount}));return Hc(e,(n,r)=>Vr(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function Hc(t,e,n){if(t.length<2)return [];let r=t.map((d,l)=>l),o=new Map;function a(d){for(;r[d]!==d;)r[d]=r[r[d]],d=r[d];return d}function i(d,l,m){let f=a(d),g=a(l);if(f===g){let I=o.get(f)??"near-duplicate";o.set(f,Wc(I,m));return}r[g]=f;let v=o.get(f)??o.get(g)??m;o.set(f,Wc(v,m));}for(let d=0;d<t.length;d++)for(let l=d+1;l<t.length;l++){let m=e(t[d],t[l]);m!==null&&i(d,l,m);}let s=new Map;for(let d=0;d<t.length;d++){let l=a(d),m=s.get(l);m?m.push(t[d]):s.set(l,[t[d]]);}let c=[];for(let[d,l]of s)if(l.length>=2){let m=o.get(d)??"near-duplicate";c.push(n(l,m));}return c}var $c={"exact-duplicate":0,"case-difference":1,"plural-singular":2,"near-duplicate":3};function Wc(t,e){return $c[t]<=$c[e]?t:e}function zc(t,e){t.registerResource("omnifocus-taxonomy-audit",Kr,{description:"Taxonomy audit: detects tag and project name collisions (exact duplicates, case differences, plural/singular variants, near-duplicates with Levenshtein \u2264 2 or token-set equality). Returns { tagCollisions: TagCollision[], projectCollisions: ProjectCollision[] }. Each collision lists the candidate names and a reason. Use to identify naming drift and plan merge operations. Empty sections return [], never omitted.",mimeType:"application/json"},async n=>{let r=await Iy(e);return {contents:[{uri:Kr,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function Xr(t){let e=new Date(t);e.setHours(0,0,0,0);let n=e.getDay(),r=n===0?6:n-1;return e.setDate(e.getDate()-r),e}function En(t){let e=new Date(t);return e.setDate(e.getDate()+7),e}function Gc(t,e=new Date){let n=Xr(e),r=[];for(let o=t-1;o>=0;o--){let a=new Date(n);a.setDate(a.getDate()-o*7),r.push(a);}return r}var Vc="omnifocus://velocity{?weeks}",qc=8,Sy=52;function by(t){if(t==null||t==="")return qc;let e=Number(t);return !Number.isFinite(e)||e<0?qc:Math.min(Sy,Math.max(1,Math.round(e)))}async function jy(t,e,n=new Date){let r=Gc(e,n),o=r[0],a=r[r.length-1],i=En(a),s=o.toISOString(),c=i.toISOString(),[d,l,m]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true,completedSince:s}),t.listProjects()]),f=new Map;for(let _ of m)f.set(String(_.id),_.name);let g=r.map(_=>{let U=En(_),M=_.toISOString(),H=U.toISOString(),z=d.filter(A=>A.createdAt>=M&&A.createdAt<H).length,le=l.filter(A=>A.completedAt!==null&&A.completedAt>=M&&A.completedAt<H).length,Ge=d.filter(A=>A.dropped&&A.droppedAt!==null&&A.droppedAt>=M&&A.droppedAt<H).length;return {weekStart:M,created:z,completed:le,dropped:Ge,netDelta:z-le-Ge}}),v=[];for(let _ of [4,8]){if(e<_)continue;let U=g.slice(-_),M=U.reduce((z,le)=>z+le.completed,0),H=U.reduce((z,le)=>z+le.created,0);v.push({window:_,completedPerWeek:Math.round(M/_*100)/100,createdPerWeek:Math.round(H/_*100)/100});}let I=Xr(a).toISOString(),T=En(a).toISOString(),E=r[Math.max(0,r.length-4)].toISOString(),w=new Map,D=new Map;for(let _ of l){if(_.completedAt===null||_.projectId===null)continue;let U=String(_.projectId);_.completedAt>=I&&_.completedAt<T&&w.set(U,(w.get(U)??0)+1),_.completedAt>=E&&_.completedAt<c&&D.set(U,(D.get(U)??0)+1);}let j=new Set([...w.keys(),...D.keys()]),B=Array.from(j).map(_=>({projectId:_,name:f.get(_)??_,closedThisWeek:w.get(_)??0,closedTrailing4:D.get(_)??0})).sort((_,U)=>U.closedThisWeek!==_.closedThisWeek?U.closedThisWeek-_.closedThisWeek:U.closedTrailing4-_.closedTrailing4).slice(0,5);return {window:{from:s,to:c},weeklyTotals:g,rollingAverages:v,topClosingProjects:B}}function Kc(t,e){t.registerResource("omnifocus-velocity",new ResourceTemplate(Vc,{list:void 0}),{description:"Rolling task velocity over trailing weeks \u2014 created, completed, dropped, and net-delta per week, plus rolling 4-week and 8-week completion/creation averages and the top-5 highest-closing projects. Default window: 8 weeks; max: 52 weeks. Use to answer 'am I getting better or worse?' alongside omnifocus://retrospective (per-range) and omnifocus://burndown/{projectId} (per-project). Read-only.",mimeType:"application/json"},async(n,r)=>{let a=by(r.weeks),i=await jy(e,a);return {contents:[{uri:`omnifocus://velocity?weeks=${a}`,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Zr="waiting-on",Qr=z.object({whom:z.string().min(1).describe("Person, team, or system being waited on."),what:z.string().min(1).optional().describe("Short description of what is being waited on."),since:oe().describe("When the wait started (ISO-8601 with offset)."),followUpAfter:oe().optional().describe("When the agent should nudge if still unresolved (ISO-8601 with offset).")});function Tt(t){let e=Be(t,Zr);if(e===void 0)return;let n=kt(e.body),r={};typeof n.whom=="string"&&n.whom.length>0&&(r.whom=n.whom),typeof n.what=="string"&&n.what.length>0&&(r.what=n.what),typeof n.since=="string"&&n.since.length>0&&(r.since=n.since),typeof n.followUpAfter=="string"&&n.followUpAfter.length>0&&(r.followUpAfter=n.followUpAfter);let o=Qr.safeParse(r);return o.success?o.data:void 0}function Xc(t,e){let n=It({whom:e.whom,what:e.what,since:e.since,followUpAfter:e.followUpAfter});return vt(t,Zr,n)}function Yc(t){return Rn(t,Zr)}function Zc(t,e=new Date){if(t.followUpAfter===void 0)return null;let n=new Date(t.followUpAfter).getTime();if(Number.isNaN(n))return null;let r=e.getTime()-n;return r<0?null:Math.floor(r/864e5)}var eo="omnifocus://waiting-on";async function _y(t,e=new Date){let n=await t.listTasks({completed:false}),r=[];for(let o of n){let a=Tt(o.note);a!==void 0&&r.push({taskId:String(o.id),name:o.name,whom:a.whom,...a.what!==void 0&&{what:a.what},since:a.since,...a.followUpAfter!==void 0&&{followUpAfter:a.followUpAfter},daysOverdue:Zc(a,e)});}return r.sort((o,a)=>{let i=o.daysOverdue,s=a.daysOverdue;return i===null&&s===null?o.since<a.since?-1:o.since>a.since?1:0:i===null?1:s===null?-1:i!==s?s-i:o.since<a.since?-1:o.since>a.since?1:0}),{items:r}}function Qc(t,e){t.registerResource("omnifocus-waiting-on",eo,{description:"All tasks with structured waiting-on metadata, sorted by daysOverdue descending. Each item: { taskId, name, whom, what?, since, followUpAfter?, daysOverdue }. daysOverdue is the integer number of whole days past followUpAfter (0 same-day, null when followUpAfter is unset or still in the future). Use to surface stalled follow-ups without scanning every task. Read-only; safe to retry. Set waiting-on with task_set_waiting_on; clear with task_clear_waiting_on.",mimeType:"application/json"},async n=>{let r=await _y(e);return {contents:[{uri:eo,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}var Qt="omnifocus://snapshot",en="omnifocus://inbox",tn="omnifocus://forecast/today",nn="omnifocus://overdue",rn="omnifocus://flagged",on="omnifocus://review-due",no="omnifocus://project/{id}",ro="omnifocus://tag/{id}",oo="omnifocus://perspective/{id}",ed="omnifocus://tasks/inbox",Py="omnifocus://tasks/project/{projectId}",Oy="omnifocus://tasks/tag/{tagId}";function to(){let t=new Date,e=new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0),n=new Date(t.getFullYear(),t.getMonth(),t.getDate(),23,59,59,999);return {from:e.toISOString(),to:n.toISOString()}}function Ie(t,e){return {contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(e,null,2)}]}}function td(t,e){let{adapter:n,projectService:r,reviewService:o,forecastService:a,perspectiveService:i}=e;t.registerResource("omnifocus-snapshot",Qt,{description:"Orientation snapshot of the current OmniFocus state: inboxCount, overdueCount, dueTodayCount, flaggedCount, reviewDueCount, and syncStatus { lastSyncAt, inFlight }. Read at session start to orient before calling task_list or forecast_get. Use syncStatus.lastSyncAt to detect stale data before making decisions.",mimeType:"application/json"},async s=>{let{from:c,to:d}=to(),[l,m,f,g]=await Promise.all([n.listTasks({completed:false}),n.getForecast({from:c,to:d,includeOverdue:true,includeFlagged:true}),n.listProjectsDueForReview(),n.getLastSync()]),v=l.filter(I=>I.projectId===null&&I.parentId===null).length;return Ie(Qt,{inboxCount:v,overdueCount:m.overdue.length,dueTodayCount:m.dueToday.length,flaggedCount:m.flagged.length,reviewDueCount:f.length,syncStatus:{lastSyncAt:g.lastSyncAt,inFlight:g.inFlight}})}),t.registerResource("omnifocus-inbox",en,{description:"Inbox tasks as Task[]. Incomplete tasks not assigned to any project or parent task. Use to triage the inbox without calling task_list.",mimeType:"application/json"},async s=>{let d=(await n.listTasks({completed:false})).filter(l=>l.projectId===null&&l.parentId===null);return Ie(en,d)}),t.registerResource("omnifocus-forecast-today",tn,{description:"Today's forecast tasks grouped by category: overdue[], dueToday[], deferredToday[], flagged[]. Equivalent to forecast_get with from/to=today. Use for 'what's on my plate today' without a tool call.",mimeType:"application/json"},async s=>{let{from:c,to:d}=to(),l=await a.get({from:c,to:d});return Ie(tn,{overdue:l.overdue,dueToday:l.dueToday,deferredToday:l.deferredToday,flagged:l.flagged})}),t.registerResource("omnifocus-overdue",nn,{description:"All overdue tasks as Task[], sorted by dueDate ascending. Tasks whose dueDate is in the past and are not completed/dropped.",mimeType:"application/json"},async s=>{let{from:c}=to(),l=[...(await n.getForecast({from:c,to:c,includeOverdue:true,includeFlagged:false,includeDeferred:false})).overdue].sort((m,f)=>m.dueDate?f.dueDate?m.dueDate<f.dueDate?-1:m.dueDate>f.dueDate?1:0:-1:1);return Ie(nn,l)}),t.registerResource("omnifocus-flagged",rn,{description:"All flagged available tasks as Task[]. Equivalent to task_list with flagged=true. Use to review the flagged list without a tool call.",mimeType:"application/json"},async s=>{let c=await n.listTasks({flagged:true,completed:false});return Ie(rn,c)}),t.registerResource("omnifocus-review-due",on,{description:"Projects due for review as Project[], sorted by nextReviewDate ascending. Equivalent to review_list_due. Use to start a review session without a tool call.",mimeType:"application/json"},async s=>{let d=[...(await o.listDue()).projects].sort((l,m)=>l.nextReviewDate?m.nextReviewDate?l.nextReviewDate<m.nextReviewDate?-1:l.nextReviewDate>m.nextReviewDate?1:0:-1:1);return Ie(on,d)}),t.registerResource("omnifocus-project",new ResourceTemplate(no,{list:void 0}),{description:"Single project with its full task tree as { project: Project, tasks: Task[] }. Get the project ID from project_list. Tasks are a flat array; rebuild the tree via task.parentId.",mimeType:"application/json"},async(s,c)=>{let d=k.of(c.id),l=await r.get({id:d,includeTaskTree:true});return Ie(`omnifocus://project/${d}`,{project:l.project,tasks:l.tasks??[]})}),t.registerResource("omnifocus-tag",new ResourceTemplate(ro,{list:void 0}),{description:"Single tag with its tasks as { tag: Tag, tasks: Task[] }. Get the tag ID from tag_list.",mimeType:"application/json"},async(s,c)=>{let d=S.of(c.id),[l,m]=await Promise.all([n.getTag(d),n.listTasks({tagId:d,completed:false})]);return Ie(`omnifocus://tag/${d}`,{tag:l,tasks:m})}),t.registerResource("omnifocus-perspective",new ResourceTemplate(oo,{list:void 0}),{description:"Perspective evaluation result as { perspectiveId: string, tasks: Task[] }. Get perspective IDs from perspective_list. Built-in IDs: inbox, projects, tags, forecast, flagged, nearby, review.",mimeType:"application/json"},async(s,c)=>{let d=c.id,l=await i.evaluate(d);return Ie(`omnifocus://perspective/${d}`,{perspectiveId:d,tasks:l.tasks})}),t.registerResource("omnifocus-tasks-inbox",ed,{description:"Inbox tasks as Task[]. Alias for omnifocus://inbox using the unified tasks namespace. Returns incomplete tasks not assigned to any project or parent task.",mimeType:"application/json"},async()=>{let c=(await n.listTasks({completed:false})).filter(d=>d.projectId===null&&d.parentId===null);return Ie(ed,c)}),t.registerResource("omnifocus-tasks-by-project",new ResourceTemplate(Py,{list:void 0}),{description:"Active tasks in a project as Task[]. Get the project ID from project_list or project_get. Returns incomplete tasks whose projectId matches the given ID.",mimeType:"application/json"},async(s,c)=>{let d=k.of(c.projectId),l=await n.listTasks({projectId:d,completed:false});return Ie(`omnifocus://tasks/project/${d}`,l)}),t.registerResource("omnifocus-tasks-by-tag",new ResourceTemplate(Oy,{list:void 0}),{description:"Active tasks with a specific tag as Task[]. Get the tag ID from tag_list or tag_get. Returns incomplete tasks that carry the given tag.",mimeType:"application/json"},async(s,c)=>{let d=S.of(c.tagId),l=await n.listTasks({tagId:d,completed:false});return Ie(`omnifocus://tasks/tag/${d}`,l)}),Ec(t,n),Qc(t,n),Fc(t,n),zc(t,n),Kc(t,n),bc(t,n),_c(t),wc(t,a),Pc(t),Uc(t,n),Ac(t,n);}var Dy=300*1e3,Ry=200,ao=class{_store=new Map;_ttlMs;constructor(e=Dy){this._ttlMs=e;}register(e,n){this._store.size>=Ry&&this._evictExpired();let r=randomBytes(16).toString("hex");return this._store.set(r,{options:e,callback:n,expiresAt:Date.now()+this._ttlMs}),r}get(e){let n=this._store.get(e);if(n){if(Date.now()>n.expiresAt){this._store.delete(e);return}return n}}consume(e){let n=this.get(e);return n&&this._store.delete(e),n}get size(){return this._store.size}_evictExpired(){let e=Date.now();for(let[n,r]of this._store)e>r.expiresAt&&this._store.delete(n);}},ve=new ao;function wt(t){return {code:"WARN_IDS_NOT_FOUND",message:`${t.length} requested ID(s) were not found and have been omitted.`,suggestion:"Verify the IDs are correct and that the items have not been deleted.",details:{missing:t}}}function p(t,e,n,r){let o={data:t,meta:e};return n!==void 0&&(o.pagination=n),r!==void 0&&r.length>0&&(o.hints=r),o}function so(t,e){return {error:t.toJSON(),meta:e}}function St(t,e,n,r,o){let a={kind:"clarification-needed",question:t,replayToken:e,meta:n};return r!==void 0&&r.length>0&&(a.options=r),o!==void 0&&Object.keys(o).length>0&&(a.partial=o),a}function u(t){return {content:[{type:"text",text:JSON.stringify(t)}],structuredContent:t}}var io="Explicitly launch OmniFocus. Do NOT call this automatically \u2014 only invoke when the user explicitly asks to open OmniFocus; prefer other tools when OF is already running. Safe to call when OmniFocus is already running (idempotent). Returns { launched, alreadyRunning } \u2014 launched=true means OmniFocus was not running and was started; alreadyRunning=true means it was already open. Side effects: may open OmniFocus and bring it to the foreground. Example: app_launch()",Cy=z.object({});async function Ey(t,e){let n=await e.adapter.appLaunch(),r=e.makeMeta();return p({launched:n.launched,alreadyRunning:n.alreadyRunning},r)}function nd(t,e){return t.registerTool("app_launch",{description:io,inputSchema:Cy.shape},async n=>{let r=await Ey(n,e);return u(r)})}var Mn=z.object({taskId:h.schema.optional().describe("Persistent ID of the task that owns the attachment. Provide exactly one of taskId or projectId."),projectId:k.schema.optional().describe("Persistent ID of the project that owns the attachment. Provide exactly one of taskId or projectId.")});function Nn(t){if(t.taskId)return {taskId:h.of(t.taskId)};if(t.projectId)return {projectId:k.of(t.projectId)};throw new y("Provide exactly one of taskId or projectId.",{})}var lo='List all file attachments on a task or project. Do not use to retrieve attachment content \u2014 use attachment_save_to_path instead. Returns { attachments } \u2014 array of objects with id, name, mimeType, sizeBytes, addedAt, and kind (embedded|alias). Provide exactly one of taskId or projectId. Read-only; safe to retry. Example: attachment_list({ taskId: "abc123" })',My=Mn;async function Ny(t,e){let n=Nn(t),r=await e.attachmentService.list(n);return p({attachments:r},e.makeMeta())}var po=`Add a file attachment to a task or project from a local file path. The file is embedded into the OmniFocus database. Path must be within the allowed scope (default: $HOME; override via OMNIFOCUS_ATTACHMENT_PATHS). File must not exceed the size cap (default 100 MB; override via OMNIFOCUS_MAX_ATTACHMENT_MB). Returns { id, ownerKind, ownerName } \u2014 ownerKind is 'task' or 'project' and ownerName is the parent's display name (null only if the parent was deleted between the add and the lookup) so the agent can describe the new attachment without a follow-up read. Mutations do not propagate until sync_trigger is called. Example: attachment_add({ taskId: "abc123", filePath: "/Users/me/report.pdf" })`,Fy=Mn.extend({filePath:z.string().min(1).describe("Absolute path to the source file to attach. Must be within the allowed attachment path scope.")});async function Uy(t,e){let n=Nn(t),{id:r,ownerKind:o,ownerName:a}=await e.attachmentService.add({...n,filePath:t.filePath});return p({id:r,ownerKind:o,ownerName:a},e.makeMeta())}var uo=`Remove an attachment from a task or project by attachment ID. Do not use to retrieve or export attachment content \u2014 use attachment_save_to_path instead. Returns { removed: true, attachmentId, ownerKind, ownerName } \u2014 ownerKind is 'task' or 'project' and ownerName is captured BEFORE the JXA call so it survives even if the lookup were to fail post-mutation; null only when the parent itself has been deleted. The agent can describe the removal without a follow-up read. Throws NotFound if the attachment or owner does not exist. Permanent \u2014 cannot be undone. Mutations do not propagate until sync_trigger is called. Example: attachment_remove({ taskId: "abc123", attachmentId: "att456" })`,Ly=Mn.extend({attachmentId:Oe.schema.describe("Persistent ID of the attachment to remove. Get from attachment_list.")});async function By(t,e){let n=Nn(t),r=Oe.of(t.attachmentId),{ownerKind:o,ownerName:a}=await e.attachmentService.remove({...n,attachmentId:r});return p({removed:true,attachmentId:r,ownerKind:o,ownerName:a},e.makeMeta())}var mo=`Copy an attachment's content to a local file path. Do not use to list or remove attachments \u2014 use attachment_list or attachment_remove instead. Returns { saved: true, path, sizeBytes } on success. Destination path must be within the allowed scope (default: $HOME). Writes the file to destPath (creates or overwrites); no side effects on OmniFocus data. Example: attachment_save_to_path({ taskId: "abc123", attachmentId: "att456", destPath: "/Users/me/report.pdf" })`,Jy=Mn.extend({attachmentId:Oe.schema.describe("Persistent ID of the attachment to save. Get from attachment_list."),destPath:z.string().min(1).describe("Absolute destination path where the attachment will be written. Must be within the allowed attachment path scope. Existing files are overwritten.")});async function $y(t,e){let n=Nn(t),r=await e.attachmentService.saveTo({...n,attachmentId:Oe.of(t.attachmentId),destPath:t.destPath});return p(r,e.makeMeta())}function rd(t,e){t.registerTool("attachment_list",{description:lo,inputSchema:My.shape},async n=>{let r=await Ny(n,e);return u(r)}),t.registerTool("attachment_add",{description:po,inputSchema:Fy.shape},async n=>{let r=await Uy(n,e);return u(r)}),t.registerTool("attachment_remove",{description:uo,inputSchema:Ly.shape},async n=>{let r=await By(n,e);return u(r)}),t.registerTool("attachment_save_to_path",{description:mo,inputSchema:Jy.shape},async n=>{let r=await $y(n,e);return u(r)});}function b(t,e={}){e.taskId!==void 0&&t.invalidate(`task:${e.taskId}`),e.projectId!==void 0&&e.projectId!==null&&t.invalidate(`project:${e.projectId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function J(t,e){t.invalidate(`project:${e.projectId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function od(t,e){t.invalidate(`tag:${e.tagId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function ad(t,e){t.invalidate(`folder:${e.folderId}`),t.invalidate("perspective:*"),t.invalidate("search:*");}function sd(t){t.clear();}function Fn(t){t.clear();}var fo="Re-apply the most recently undone mutation, identical to \u2318\u21E7Z in OmniFocus. Advances one entry on the document's redo stack. Any mutation between an undo and a redo invalidates the redo stack (matching UI semantics). Mandatory `confirm: true` mirrors database_undo's destructive-write pattern. Returns { redid: boolean } \u2014 true when an entry was redone, false when the stack was empty. Do NOT use this tool to re-apply a specific operation \u2014 the redo stack is opaque. Prefer database_redo only as a direct counterpart to database_undo when an undo was issued in error. Side effects: re-applies whatever entry is at the top of the document's redo stack; fully invalidates the read cache; does NOT trigger sync. Call sync_trigger when you need the change to appear on other devices. Example: database_redo({ confirm: true })",Wy=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that redo can re-apply a mutation that may now conflict with intervening edits. Must be exactly true. The call is rejected if this field is absent or false.")});async function Hy(t,e){let n=await e.adapter.redoLastMutation();return n.redid&&e.cache!==void 0&&Fn(e.cache),p(n,e.makeMeta({syncPending:n.redid}))}function cd(t,e){return t.registerTool("database_redo",{description:fo,inputSchema:Wy.shape},async n=>{let r=await Hy(n,e);return u(r)})}var go="Reverse the most recent document mutation, identical to \u2318Z in OmniFocus. Walks back one entry on the document's undo stack regardless of mutation source \u2014 an MCP undo can revert a manual UI edit if that was the most recent change. Mandatory `confirm: true` mirrors task_batch_delete's destructive-write pattern, since undo can silently revert changes the agent or another caller just made. Returns { undid: boolean } \u2014 true when an entry was undone, false when the stack was empty. Do NOT use this tool to roll back specific operations \u2014 the undo stack is opaque and you cannot inspect what would be reverted before calling. Prefer database_undo for: post-batch error recovery, retry-after-partial-failure cleanup, and integration-test teardown. Side effects: reverts whatever entry is at the top of the document's undo stack; fully invalidates the read cache (we don't know what was reverted); does NOT trigger sync. Call sync_trigger when you need the change to appear on other devices. Example: database_undo({ confirm: true })",zy=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that undo can revert mutations from any source \u2014 MCP, manual UI edit, or sync replay. Must be exactly true. The call is rejected if this field is absent or false.")});async function Gy(t,e){let n=await e.adapter.undoLastMutation();return n.undid&&e.cache!==void 0&&Fn(e.cache),p(n,e.makeMeta({syncPending:n.undid}))}function ld(t,e){return t.registerTool("database_undo",{description:go,inputSchema:zy.shape},async n=>{let r=await Gy(n,e);return u(r)})}var yo='Clear the decision-journal entry from a task or project\'s note. Strips only the `decision-journal` fenced block; any other user prose and sibling fences (e.g. waiting-on) are preserved. Idempotent: returns noChange:true when the target has no decision recorded. Do NOT use this to delete the target \u2014 prefer task_delete / project_delete. Returns { targetKind, targetId, cleared:true } or { targetKind, targetId, noChange:true }. Side effects: writes the target\'s note via task_update / project_update; sets meta.syncPending = true. Example: { "targetKind": "project", "targetId": "abc" }',qy=z.object({targetKind:z.enum(["task","project"]).describe("Whether the target is a task or a project."),targetId:z.string().min(1).describe("ID of the task or project.")});async function Vy(t,e){if(t.targetKind==="task"){let a=h.of(t.targetId),i=await e.adapter.getTask(a),s=zr(i.note);return s===i.note?p({targetKind:"task",targetId:a,noChange:true},e.makeMeta()):(await e.adapter.updateTask(a,{note:s}),e.cache!==void 0&&b(e.cache,{taskId:a,projectId:i.projectId}),p({targetKind:"task",targetId:a,cleared:true},e.makeMeta({syncPending:true})))}let n=k.of(t.targetId),r=await e.adapter.getProject(n),o=zr(r.note);return o===r.note?p({targetKind:"project",targetId:n,noChange:true},e.makeMeta()):(await e.adapter.updateProject(n,{note:o}),e.cache!==void 0&&J(e.cache,{projectId:n}),p({targetKind:"project",targetId:n,cleared:true},e.makeMeta({syncPending:true})))}function pd(t,e){return t.registerTool("decision_clear",{description:yo,inputSchema:qy.shape},async n=>{let r=await Vy(n,e);return u(r)})}var ko=`Record agent memory of user judgment on a task or project \u2014 kind, reason, and an optional auto-expiry. Writes a \`decision-journal\` fenced block to the target's note (preserving any existing user prose), so future scans (e.g. project_health) can honor the decision instead of re-litigating it. Discriminates on \`targetKind\`: 'task' or 'project'. Do NOT use this for short-lived state \u2014 prefer waiting-on for follow-ups, or task_update for routine field changes. Returns { targetKind, targetId, decision } with the persisted entry. Side effects: writes the target's note via task_update / project_update; sets meta.syncPending = true. Example: { "targetKind": "project", "targetId": "abc", "decision": { "kind": "stall-is-intentional", "reason": "Strategic pause until Q3" } }`,Ky=z.object({kind:z.enum(Wr).describe("The kind of judgment recorded."),reason:z.string().min(1).describe("Human-readable reason for the decision."),until:z.string().datetime({offset:true}).optional().describe("Optional ISO-8601 auto-expiry. When set and in the past, the decision is treated as expired and downstream consumers re-surface the target.")}),Xy=z.object({targetKind:z.enum(["task","project"]).describe("Whether the decision attaches to a task or a project."),targetId:z.string().min(1).describe("ID of the task or project. Must match `targetKind` \u2014 agent-side validation, but the adapter call surfaces NotFound if the ID is wrong."),decision:Ky.describe("The decision payload. `recordedAt` is set automatically on write.")});async function Yy(t,e){let n=e.now?e.now():new Date,r={kind:t.decision.kind,reason:t.decision.reason,recordedAt:n.toISOString(),...t.decision.until!==void 0&&{until:t.decision.until}};if(t.targetKind==="task"){let s=h.of(t.targetId),c=await e.adapter.getTask(s),d=Hr(c.note,r);return await e.adapter.updateTask(s,{note:d}),e.cache!==void 0&&b(e.cache,{taskId:s,projectId:c.projectId}),p({targetKind:"task",targetId:s,decision:r},e.makeMeta({syncPending:true}))}let o=k.of(t.targetId),a=await e.adapter.getProject(o),i=Hr(a.note,r);return await e.adapter.updateProject(o,{note:i}),e.cache!==void 0&&J(e.cache,{projectId:o}),p({targetKind:"project",targetId:o,decision:r},e.makeMeta({syncPending:true}))}function ud(t,e){return t.registerTool("decision_record",{description:ko,inputSchema:Xy.shape},async n=>{let r=await Yy(n,e);return u(r)})}var vo=`Export OmniFocus data as OPML XML \u2014 a structured outline format OmniFocus can import. Do NOT use to export a single task; OPML scope is project-level or broader. Three scopes: 'project' (one project + its tasks), 'folder' (all projects in a folder), or 'all' (all active projects). Returns { opml, projectCount, taskCount } where opml is a complete XML string. Safe to call repeatedly; no side effects. Example: export_opml({ scope: "project", id: "abc123" }) Example: export_opml({ scope: "all" })`,Zy=z.object({scope:z.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z.string().optional().describe("Required when scope='project' (project ID from project_list) or scope='folder' (folder ID from folder_list). Omit for scope='all'.")});function Qy(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new y(`scope='${t.scope}' requires an id`,{details:{field:"id",scope:t.scope}});return t.scope==="project"?{kind:"project",id:k.of(t.id)}:{kind:"folder",id:W.of(t.id)}}async function ek(t,e){let n=Qy(t),r=await e.exportService.exportOpml(n),o=e.makeMeta();return p({opml:r.opml,projectCount:r.projectCount,taskCount:r.taskCount},o)}function md(t,e){return t.registerTool("export_opml",{description:vo,inputSchema:Zy.shape},async n=>{let r=await ek(n,e);return u(r)})}function q(t){return `'${t}'`}function G(t){return t.length>140?`${t.slice(0,137)}\u2026`:t}function fd(t){return G(`Created task ${q(t)}.`)}function gd(t){return G(`Completed task ${q(t)}.`)}function hd(t){return G(`Uncompleted task ${q(t)}.`)}function yd(t){return G(`Deleted task ${q(t)}.`)}function kd(t){return G(`Dropped task ${q(t)}.`)}function Id(t){return G(`Restored task ${q(t)} from dropped.`)}function vd(t,e){return G(`Moved task ${q(t)} to ${e}.`)}function Td(t){return G(`Duplicated task ${q(t)}.`)}function wd(t){return G(`Reordered task ${q(t)}.`)}function Sd(t){return G(`Converted task ${q(t)} to a project.`)}function bd(t){return G(`Set repetition rule on task ${q(t)}.`)}function jd(t){return G(`Cleared repetition rule on task ${q(t)}.`)}function _d(t,e){return G(`Set ${e} ${e===1?"alarm":"alarms"} on task ${q(t)}.`)}function Pd(t){return G(`Cleared alarms on task ${q(t)}.`)}function bt(t){return `Created ${t} task${t===1?"":"s"}.`}function Od(t){return `Updated ${t} task${t===1?"":"s"}.`}function xd(t){return `Completed ${t} task${t===1?"":"s"}.`}function Dd(t){return `Uncompleted ${t} task${t===1?"":"s"}.`}function Rd(t){return `Deleted ${t} task${t===1?"":"s"}.`}function Ad(t){return `Dropped ${t} task${t===1?"":"s"}.`}function Cd(t){return `Restored ${t} dropped task${t===1?"":"s"}.`}function Ed(t,e){return G(`Moved ${t} task${t===1?"":"s"} to ${e}.`)}function Md(t){return G(`Created project ${q(t)}.`)}function Nd(t){return G(`Updated project ${q(t)}.`)}function Fd(t){return G(`Deleted project ${q(t)}.`)}function Ud(t){return `Completed ${t} project${t===1?"":"s"}.`}function Ld(t){return `Dropped ${t} project${t===1?"":"s"}.`}function Bd(t){return G(`Created tag ${q(t)}.`)}function Ve(t){return G(`Updated tag ${q(t)}.`)}function Jd(t,e){return G(`Moved tag ${q(t)} to ${e}.`)}function $d(t){return G(`Created folder ${q(t)}.`)}function Wd(t){return G(`Updated folder ${q(t)}.`)}function Hd(t,e){return G(`Moved folder ${q(t)} to ${e}.`)}function Un(t,e){return G(`Set note on ${t} ${q(e)}.`)}function zd(t,e){return G(`Appended to note on ${t} ${q(e)}.`)}function Gd(){return "Completed project."}function qd(){return "Dropped project."}function Vd(t){return G(`Moved project to ${t}.`)}function Kd(){return "Deleted tag."}function Xd(){return "Deleted folder."}function Ln(){return "Marked project as reviewed."}function Yd(t){return t===null?"Cleared project review interval.":`Set project review interval to ${t} day${t===1?"":"s"}.`}function Zd(t){return t===null?"Cleared project next review date.":G(`Set project next review date to ${t}.`)}var wo='Import tasks from an OPML XML string into OmniFocus. Parses the OPML produced by export_opml and recreates the task hierarchy. Top-level <outline type="omnifocus:project"> elements are matched to existing projects by OmniFocus ID (for round-trip) then by name; unmatched project outlines land in the Inbox. LOSSY: due dates, defer dates, and flagged state are preserved; tags, notes, attachments, and repetition rules are silently dropped (not encoded in OPML). Do NOT use to export data; prefer export_opml for that. Returns { imported, tasks: [{ id, name }] } \u2014 imported is the count of tasks created and tasks pairs each new id with its display name (resolved via a single getTasksMany batch, no N+1) so the agent can confirm what landed without a follow-up read. Orphan ids (rare; the task was deleted between import and lookup) are dropped from the array. Writes to OmniFocus; call sync_trigger after import to propagate changes to other devices. Example: import_opml({ opml: "<opml>...</opml>" })',tk=z.object({opml:z.string().min(1).describe("Well-formed OPML XML string to import. Use the output of export_opml for a round-trip."),destinationProjectId:z.string().optional().describe("When set, all tasks are created in this project regardless of project headings in the OPML. Get the ID from project_list. Omit to match projects by ID/name from the OPML structure.")});async function nk(t,e){let n=await e.exportService.importOpml(t.opml,{...t.destinationProjectId!==void 0?{destinationProjectId:k.of(t.destinationProjectId)}:{}}),r=n.taskIds.length>0?await e.adapter.getTasksMany(n.taskIds):[],o=n.taskIds.map((i,s)=>{let c=r[s];return c==null?null:{id:String(i),name:c.name}}).filter(i=>i!==null),a=e.makeMeta({syncPending:true,humanReadableSummary:bt(n.imported)});return p({imported:n.imported,tasks:o},a)}function Qd(t,e){return t.registerTool("import_opml",{description:wo,inputSchema:tk.shape},async n=>{let r=await nk(n,e);return u(r)})}var So=`Export OmniFocus data as TaskPaper plain text. Three scopes: 'project' (one project + its tasks), 'folder' (all projects in a folder), or 'all' (all active projects). Export is lossy \u2014 HTML notes are downgraded to plain text; tag locations, attachments, and complex repetition rules are omitted. Lossiness warnings are returned in meta.warnings. Do NOT use to import data; prefer import_taskpaper for that. Returns { taskpaper, projectCount, taskCount }. Safe to call repeatedly; no side effects. Example: export_taskpaper({ scope: "project", id: "abc123" }) Example: export_taskpaper({ scope: "all" })`,bo=`Import tasks from TaskPaper text into OmniFocus. Parses '- Task name @tag @due(2026-01-15) @defer(2026-01-10) @flagged' lines. Indented subtasks become children of the nearest parent task. Project headings ('Project name:') map to existing OF projects by name \u2014 unrecognised headings fall back to inbox (warning emitted). Unknown @tags are created automatically. Do NOT use to export data; prefer export_taskpaper for that. Returns { tasks: [{ id, name }], warnings: string[] } \u2014 tasks pairs each new id with its display name (resolved via a single getTasksMany batch, no N+1) so the agent can confirm what landed without a follow-up read. Orphan ids (rare; deleted between import and lookup) are dropped from the array. Writes to OmniFocus; call sync_trigger to propagate changes to other devices. Example: import_taskpaper({ text: "- Buy milk @errands\\n- Call dentist @due(2026-05-01)" })`,rk=z.object({scope:z.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z.string().optional().describe("Required when scope='project' (project ID from project_list) or scope='folder' (folder ID from folder_list). Omit for scope='all'.")}),ok=z.object({text:z.string().min(1).describe("TaskPaper-formatted text to import. Each '- Task name' line becomes a task."),targetProjectId:z.string().optional().describe("When set, all top-level tasks are created in this project regardless of project headings in the text. Get the ID from project_list.")});function ak(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new y(`scope="${t.scope}" requires an id \u2014 provide the ${t.scope==="project"?"project":"folder"} ID`,{details:{field:"id",scope:t.scope}});return t.scope==="project"?{kind:"project",id:k.of(t.id)}:{kind:"folder",id:W.of(t.id)}}function el(t,e){t.registerTool("export_taskpaper",{description:So,inputSchema:rk.shape},async n=>{let r=ak(n),o=await e.exportService.exportTaskPaper(r);return u(p({taskpaper:o.taskpaper,projectCount:o.projectCount,taskCount:o.taskCount,warnings:o.warnings},e.makeMeta()))}),t.registerTool("import_taskpaper",{description:bo,inputSchema:ok.shape},async n=>u(await sk(n,e)));}async function sk(t,e){let n=await e.exportService.importTaskPaper(t.text,t.targetProjectId===void 0?void 0:k.of(t.targetProjectId)),r=n.created.length>0?await e.adapter.getTasksMany(n.created):[],o=n.created.map((a,i)=>{let s=r[i];return s==null?null:{id:String(a),name:s.name}}).filter(a=>a!==null);return p({tasks:o,warnings:n.warnings},e.makeMeta())}var jo=`Create a new folder in OmniFocus. Optionally nest it inside an existing parent folder (get IDs from folder_list). Do not use to move an existing folder; prefer folder_move instead. Returns the new folder's persistent ID. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_create({ name: "Work" }) Example: folder_create({ name: "Archive", parentId: "fld123" })`,_o=z.object({name:z.string().min(1).describe("Folder name. Must be non-empty."),parentId:W.schema.optional().describe("Parent folder ID. Omit for a root-level folder. Get from folder_list.")});async function ik(t,e){let n=await e.folderService.create({name:t.name,...t.parentId!==void 0?{parentId:t.parentId}:{}}),{folder:r}=await e.folderService.get(n.id);return p({folder:r},e.makeMeta({syncPending:true,humanReadableSummary:$d(t.name)}))}function nl(t,e){return t.registerTool("folder_create",{description:jo,inputSchema:_o.shape},async n=>{let r=await ik(n,e);return u(r)})}function Te(t){let e=new Date(t),n=e.getUTCFullYear(),r=String(e.getUTCMonth()+1).padStart(2,"0"),o=String(e.getUTCDate()).padStart(2,"0"),a=e.getUTCHours(),i=e.getUTCMinutes();return a===0&&i===0?`${n}-${r}-${o}`:`${n}-${r}-${o} ${String(a).padStart(2,"0")}:${String(i).padStart(2,"0")}`}async function _t(t,e){try{return (await t.getProject(e)).name}catch{return e}}async function Pt(t,e){try{return (await t.getTask(e)).name}catch{return e}}async function he(t,e){try{return (await t.getTag(e)).name}catch{return e}}async function Ke(t,e){try{return (await t.getFolder(e)).name}catch{return e}}var Po="Preview what folder_create would do without making any changes. Do NOT use to actually create a folder \u2014 use folder_create instead. Returns { description, plannedChanges } describing the folder that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function ck(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await Ke(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),r.push(`inside '${a}'`);}else r.push("at root");let o=`Would create folder ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function rl(t,e){return t.registerTool("folder_create_describe",{description:Po,inputSchema:_o.shape},async n=>{let r=await ck(n,e);return u(r)})}var Oo='Delete a folder from OmniFocus. By default returns ValidationError when the folder contains projects or subfolders. Pass cascade=true to orphan all direct projects (move to no folder) and recursively delete subfolders before deleting. IRREVERSIBLE \u2014 do not use to archive; prefer folder_update to rename instead. Get the folder ID from folder_list. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_delete({ id: "fld123" }) Example: folder_delete({ id: "fld123", cascade: true })',xo=z.object({id:W.schema.describe("Persistent folder ID to delete. Get from folder_list."),cascade:z.boolean().optional().describe("When true, orphan all direct projects and recursively delete subfolders before deleting. Default false \u2014 returns an error if the folder is non-empty.")});async function dk(t,e){return await e.folderService.delete(t.id,t.cascade??false),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Xd()}))}function al(t,e){return t.registerTool("folder_delete",{description:Oo,inputSchema:xo.shape},async n=>{let r=await dk(n,e);return u(r)})}var Do="Preview what folder_delete would do without making any changes. Do NOT use to actually delete a folder \u2014 use folder_delete instead. Returns { description, plannedChanges } describing the deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function lk(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getFolder(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=t.cascade===true?" (cascade: orphan projects, delete subfolders)":"",a=`Would delete folder '${r}' (id: ${t.id})${o}. IRREVERSIBLE.`;return p({description:a,plannedChanges:n},e.makeMeta())}function sl(t,e){return t.registerTool("folder_delete_describe",{description:Do,inputSchema:xo.shape},async n=>{let r=await lk(n,e);return u(r)})}var Ro='Fetch a single folder by its persistent ID, including project and subfolder counts. Do not use to list multiple folders; prefer folder_list instead. Returns folder details including name, parentId, projectCount, and subfolderCount. Safe to call repeatedly; no side effects. Example: folder_get({ id: "fld123" })',uk=z.object({id:W.schema.describe("Persistent folder ID. Get from folder_list. IDs are stable across renames.")});async function mk(t,e){let n=await e.folderService.get(t.id);return p({folder:n.folder},e.makeMeta({cacheHit:n.cacheHit}))}function il(t,e){return t.registerTool("folder_get",{description:Ro,inputSchema:uk.shape},async n=>{let r=await mk(n,e);return u(r)})}var Ao='List folders in OmniFocus, optionally filtered by parent folder. Do not use to fetch a single folder by ID; prefer folder_get instead. Returns a flat array with projectCount and subfolderCount per folder. Use parentId to walk the hierarchy one level at a time. Safe to call repeatedly; no side effects. Example: folder_list({}) Example: folder_list({ parentId: "fld123" })',gk=z.object({parentId:W.schema.optional().describe("Return only direct children of this folder. Get the ID from a previous folder_list call. Omit for root folders.")});async function hk(t,e){let n={...t.parentId!==void 0?{parentId:t.parentId}:{}},r=await e.folderService.list(n);return p({folders:r.folders},e.makeMeta({cacheHit:r.cacheHit}))}function cl(t,e){return t.registerTool("folder_list",{description:Ao,inputSchema:gk.shape},async n=>{let r=await hk(n,e);return u(r)})}var Co=`Move a folder to a new parent, or promote it to a root folder by passing parentId=null. Do not use to rename a folder; prefer folder_update instead. Get folder IDs from folder_list. Returns the updated folder's ID and new parentId on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_move({ id: "fld123", parentId: "fld456" }) Example: folder_move({ id: "fld123", parentId: null })`,Eo=z.object({id:W.schema.describe("Persistent ID of the folder to move. Get from folder_list."),parentId:W.schema.nullable().describe("New parent folder ID, or null to promote the folder to root level.")});async function kk(t,e){await e.folderService.move(t.id,t.parentId);let{folder:n}=await e.folderService.get(t.id);return p({folder:n},e.makeMeta({syncPending:true,humanReadableSummary:Hd(n.name,t.parentId!=null?"folder":"library root")}))}function dl(t,e){return t.registerTool("folder_move",{description:Co,inputSchema:Eo.shape},async n=>{let r=await kk(n,e);return u(r)})}var Mo="Preview what folder_move would do without making any changes. Do NOT use to actually move a folder \u2014 use folder_move instead. Returns { description, plannedChanges } describing the reparenting that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Ik(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getFolder(t.id)).name;}catch{}let o;if(t.parentId!==null){let i=await Ke(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`inside '${i}'`;}else n.push({field:"parentId",newValue:null}),o="root level";let a=`Would move folder '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function ll(t,e){return t.registerTool("folder_move_describe",{description:Mo,inputSchema:Eo.shape},async n=>{let r=await Ik(n,e);return u(r)})}var No='Rename a folder (partial patch \u2014 only supplied fields are changed). To move a folder use folder_move instead. Get the folder ID from folder_list. Returns the updated folder on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: folder_update({ id: "fld123", name: "Personal" })',Fo=z.object({id:W.schema.describe("Persistent folder ID. Get from folder_list."),name:z.string().min(1).optional().describe("New folder name. Must be non-empty if supplied.")});async function vk(t,e){let{id:n,...r}=t;await e.folderService.update(n,{...r.name!==void 0?{name:r.name}:{}});let{folder:o}=await e.folderService.get(n);return p({folder:o},e.makeMeta({syncPending:true,humanReadableSummary:Wd(o.name)}))}function ul(t,e){return t.registerTool("folder_update",{description:No,inputSchema:Fo.shape},async n=>{let r=await vk(n,e);return u(r)})}var Uo="Preview what folder_update would do without making any changes. Do NOT use to actually update a folder \u2014 use folder_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Tk(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getFolder(t.id);o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update folder '${o}': ${r.join(", ")}.`:`Would update folder '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function ml(t,e){return t.registerTool("folder_update_describe",{description:Uo,inputSchema:Fo.shape},async n=>{let r=await Tk(n,e);return u(r)})}var Lo=`Get forecast-view tasks from OmniFocus grouped by category: overdue, dueToday, deferredToday, flagged. Use this for 'what's on my plate today' or multi-day planning queries. Do NOT use to list all tasks across all projects; prefer task_list instead. Supply date (ISO-8601 or shortcut like 'today', 'tomorrow') and days (1\u20137) for the ergonomic interface, or use from/to for exact ISO-8601 ranges. All include flags default to true; set to false to omit a category. When days > 1, response also includes byDate[] grouping tasks per calendar day. Returns { overdue[], dueToday[], deferredToday[], flagged[], byDate? }; safe to call repeatedly; no side effects. Example: forecast_get({ date: "today" }) Example: forecast_get({ date: "today", days: 3, includeFlagged: false })`,wk=z.object({date:Le().optional().describe("Anchor date for the forecast (ISO-8601 or relative shortcut: today, tomorrow, yesterday, this-week, next-week). Mutually exclusive with from/to. Defaults to today."),days:z.number().int().min(1).max(7).optional().default(1).describe("Number of days to cover (1\u20137). Default 1. When > 1, byDate[] is included in the response."),from:Le().optional().describe("Start of date range (ISO-8601 or relative shortcut like 'today'). Use date/days for the ergonomic interface instead. Defaults to start of today."),to:Le().optional().describe("End of date range (ISO-8601 or relative shortcut like 'today'). Use date/days for the ergonomic interface instead. Defaults to end of today."),includeOverdue:z.boolean().optional().default(true).describe("Include tasks overdue before the start of the range. Default true."),includeDeferred:z.boolean().optional().default(true).describe("Include tasks whose defer date falls within the range. Default true."),includeFlagged:z.boolean().optional().default(true).describe("Include all flagged incomplete tasks. Default true.")});function Sk(t){let e;if(qt(t))e=Vt(t);else if(Kt(t))e=t;else throw new y(`Invalid date: "${t}". Expected ISO-8601 or a relative shortcut.`,{details:{field:"date",value:t}});let n=new Date(e);return n.setHours(0,0,0,0),n}function bk(t){let e=t.date!==void 0,n=t.from!==void 0||t.to!==void 0;if(e&&n)throw new y("date and from/to are mutually exclusive \u2014 use one or the other.",{suggestion:"Remove from/to when using date, or remove date when using from/to.",details:{field:"date|from|to"}});let r=t.days??1;if(e){let s=Sk(t.date),c=new Date(s);return c.setDate(c.getDate()+r-1),c.setHours(23,59,59,999),{from:s.toISOString(),to:c.toISOString(),days:r}}if(n){let s=new Date,c=new Date(s);c.setHours(0,0,0,0);let d=new Date(s);return d.setHours(23,59,59,999),{from:t.from??c.toISOString(),to:t.to??d.toISOString(),days:r}}let o=new Date,a=new Date(o);a.setHours(0,0,0,0);let i=new Date(a);return i.setDate(i.getDate()+r-1),i.setHours(23,59,59,999),{from:a.toISOString(),to:i.toISOString(),days:r}}function jk(t){let e=new Map;for(let n of t){if(!n.dueDate)continue;let r=n.dueDate.slice(0,10),o=e.get(r)??[];o.push(n),e.set(r,o);}return [...e.entries()].sort(([n],[r])=>n.localeCompare(r)).map(([n,r])=>({date:n,tasks:r}))}async function _k(t,e){let{from:n,to:r,days:o}=bk(t),a=await e.forecastService.get({from:n,to:r,includeOverdue:t.includeOverdue,includeDeferred:t.includeDeferred,includeFlagged:t.includeFlagged}),i=e.makeMeta({cacheHit:a.cacheHit}),s={overdue:a.overdue,dueToday:a.dueToday,deferredToday:a.deferredToday,flagged:a.flagged};return o>1&&(s.byDate=jk(a.dueToday)),p(s,i)}function fl(t,e){return t.registerTool("forecast_get",{description:Lo,inputSchema:wk.shape},async n=>{let r=await _k(n,e);return u(r)})}var Bo="Read the OmniFocus forecast-tag preference: the single tag whose tasks always appear on the Forecast view alongside dated items. Use when the agent needs to answer 'what tag is the user using as their daily agenda?' or to confirm a tag before composing follow-up queries against it. Do NOT use to list tags in general \u2014 prefer tag_list. Takes no arguments. Returns { tagId: string | null, name: string | null } \u2014 name is the tag's display name (or null when tagId is null or the tag has been deleted) so the agent can describe the forecast tag without a follow-up tag_get. Read-only; no side effects; safe to retry. Backed by OmniJS Database.forecastTag.Example: forecast_get_tag()",Ok=z.object({});async function xk(t,e){let n=await e.forecastService.getForecastTag();return p(n,e.makeMeta())}function gl(t,e){return t.registerTool("forecast_get_tag",{description:Bo,inputSchema:Ok.shape},async n=>{let r=await xk(n,e);return u(r)})}var Jo=`Pack today's forecast tasks into a time budget. Use when the user asks 'I have N hours; what should I do?' or wants a focused subset of forecast tasks that fit a limited window. Do NOT use for the full forecast \u2014 prefer forecast_get for that. Do NOT use to schedule work across multiple days \u2014 pass scope='next7' as a hint, but the pack is still budget-bounded; for true multi-day planning use forecast_get with days>1 and let the agent compose. Pass budgetMinutes (1\u20131440) and optional filter { tagIds?, scope? }; scope is 'today' (default) or 'next7'. Returns { selected[], totalMinutes, skipped[] }. selected[] are the picks in execution order (flagged first, then dueDate ascending, then stable by ID). skipped[] surfaces tasks the agent should ask the user about: { reason: 'no-estimate' } means the task has no estimatedMinutes so couldn't be packed; { reason: 'exceeds-budget' } means it would have fit individually but was bumped by earlier higher-priority picks. Read-only; no side effects; safe to retry. Pack algorithm is greedy \u2014 predictable and explainable beats optimal-by-1-minute. Example: forecast_pack({ budgetMinutes: 120 }) Example: forecast_pack({ budgetMinutes: 240, filter: { tagIds: ["tag123"], scope: "today" } })`,Dk=z.object({budgetMinutes:z.number().int().min(1).max(1440).describe("Time budget in minutes (1\u20131440 \u2014 i.e. up to 24 hours). Selected tasks' estimatedMinutes will sum to \u2264 this value."),filter:z.object({tagIds:z.array(S.schema).optional().describe("Restrict to tasks bearing at least one of these tag IDs. Empty array or omitted means no tag filter."),scope:z.enum(["today","next7"]).optional().default("today").describe("Forecast horizon to pack from: 'today' (overdue + dueToday + flagged) or 'next7' (everything in the next 7 days). Default 'today'.")}).optional().describe("Optional filter narrowing the candidate set before packing.")});function Rk(t,e){if(t.flagged!==e.flagged)return t.flagged?-1:1;let n=t.dueDate,r=e.dueDate;if(n!==r){if(n===null)return 1;if(r===null||n<r)return -1;if(n>r)return 1}return t.id<e.id?-1:t.id>e.id?1:0}function Ak(t,e,n){let r=n?.tagIds,a=[...r&&r.length>0?t.filter(d=>d.tagIds.some(l=>r.includes(l))):t].sort(Rk),i=[],s=[],c=0;for(let d of a){if(d.estimatedMinutes===null){s.push({taskId:d.id,name:d.name,estimatedMinutes:null,reason:"no-estimate"});continue}c+d.estimatedMinutes<=e?(i.push({taskId:d.id,name:d.name,estimatedMinutes:d.estimatedMinutes,flagged:d.flagged,dueDate:d.dueDate}),c+=d.estimatedMinutes):s.push({taskId:d.id,name:d.name,estimatedMinutes:d.estimatedMinutes,reason:"exceeds-budget"});}return {selected:i,totalMinutes:c,skipped:s}}function Ck(t){let e=new Date,n=new Date(e);n.setHours(0,0,0,0);let r=new Date(n);return r.setDate(r.getDate()+(t==="next7"?6:0)),r.setHours(23,59,59,999),{from:n.toISOString(),to:r.toISOString()}}function Ek(t){let e=new Set,n=[];for(let r of [t.overdue,t.dueToday,t.deferredToday,t.flagged])for(let o of r)e.has(o.id)||(e.add(o.id),n.push(o));return n}async function Mk(t,e){let n=t.filter?.scope??"today",{from:r,to:o}=Ck(n),a=await e.forecastService.get({from:r,to:o,includeOverdue:true,includeDeferred:n==="next7",includeFlagged:true}),i=Ek(a),s=Ak(i,t.budgetMinutes,t.filter),c=e.makeMeta({cacheHit:a.cacheHit});return p(s,c)}function hl(t,e){return t.registerTool("forecast_pack",{description:Jo,inputSchema:Dk.shape},async n=>{let r=await Mk(n,e);return u(r)})}var Wo=`Set or clear the OmniFocus forecast-tag preference. Use when the user wants to designate (or change) the tag whose tasks should always appear on Forecast \u2014 common during onboarding flows or context switches ('use @today as my agenda'). Do NOT use to add tags to a task \u2014 prefer task_update. Pass tagId as a TagId string to set, or null to clear. Returns { tagId: string | null, name: string | null } echoing what was applied \u2014 name is paired with the tag id so the agent can describe the change without a follow-up tag_get. Errors: NOT_FOUND when the supplied tagId does not exist. Side effects: mutation; invalidates the forecast read cache. Backed by OmniJS Database.forecastTag. Example: forecast_set_tag({ tagId: "tag123" }) Example: forecast_set_tag({ tagId: null })`,Nk=z.object({tagId:z.union([S.schema,z.null()]).describe("The TagId to designate as the forecast tag, or null to clear the preference. Use null to remove the forecast-tag binding entirely.")});async function Fk(t,e){let n=await e.forecastService.setForecastTag(t.tagId);return e.cache.invalidate("forecast:*"),p(n,e.makeMeta())}function yl(t,e){return t.registerTool("forecast_set_tag",{description:Wo,inputSchema:Nk.shape},async n=>{let r=await Fk(n,e);return u(r)})}var Ho=`Append text to the plain-text note on a task or project. Adds a newline between existing content and the new text unless the note is empty. Do not use to replace the note entirely; prefer note_set instead. Returns { updated: true, id, targetKind, name, note } \u2014 name is the parent task/project's display name (captured from the same read that fetched the existing note) so the agent can describe the change without a follow-up read; note is the full content after appending. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices. Example: note_append({ targetKind: "task", id: "abc123", text: "Follow up next week" })`,Uk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to append to."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),text:z.string().min(1).describe("Text to append. A newline separator is inserted before the text if a note exists.")});async function Lk(t,e){let n,r;if(t.targetKind==="task"){let a=await e.adapter.getTask(h.of(t.id));n=a.note,r=a.name;}else {let a=await e.adapter.getProject(k.of(t.id));n=a.note,r=a.name;}let o=n?`${n}
209
+ ${t.text}`:t.text;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{note:o}),e.cache!==void 0&&b(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(k.of(t.id),{note:o}),e.cache!==void 0&&J(e.cache,{projectId:k.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:r,note:o},e.makeMeta({syncPending:true,humanReadableSummary:zd(t.targetKind,r)}))}function kl(t,e){return t.registerTool("note_append",{description:Ho,inputSchema:Uk.shape},async n=>{let r=await Lk(n,e);return u(r)})}var Go=`Read the plain-text note from a task or project. Do not use when formatting fidelity matters; prefer note_get_html instead. Returns { note } \u2014 a string (may be empty) or null when no note exists. Set targetKind to 'task' and provide a task ID, or 'project' and a project ID. Safe to call repeatedly; no side effects. Example: note_get({ targetKind: "task", id: "abc123" })`,Bk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to read."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function Jk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).note:(await e.adapter.getProject(k.of(t.id))).note;return p({note:n??null},e.makeMeta())}function Il(t,e){return t.registerTool("note_get",{description:Go,inputSchema:Bk.shape},async n=>{let r=await Jk(n,e);return u(r)})}var Vo=`Read the HTML fragment from a task or project note. Returns { noteHtml } \u2014 an HTML string (may be empty) or null when no note exists. Set targetKind to 'task' and provide a task ID, or 'project' and a project ID. For plain-text access without formatting, use note_get instead. Safe to call repeatedly; no side effects. Example: note_get_html({ targetKind: "task", id: "abc123" })`,$k=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to read."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function Wk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).noteHtml:(await e.adapter.getProject(k.of(t.id))).noteHtml;return p({noteHtml:n??null},e.makeMeta())}function vl(t,e){return t.registerTool("note_get_html",{description:Vo,inputSchema:$k.shape},async n=>{let r=await Wk(n,e);return u(r)})}var Ko=`Replace the plain-text note on a task or project. Overwrites the existing note entirely. Pass note: null to clear the note. To add text without overwriting use note_append instead. Returns { updated: true, id, targetKind, name, note } \u2014 name is the parent task/project's display name (pre-fetched so the response describes the change without a follow-up read); note echoes back the final content after writing (or null if cleared). Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices. Example: note_set({ targetKind: "task", id: "abc123", note: "Check with Alice first" })`,Hk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose note to set."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),note:z.string().nullable().describe("New note text. Pass null to clear the note entirely.")});async function zk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(k.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{note:t.note}),e.cache!==void 0&&b(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(k.of(t.id),{note:t.note}),e.cache!==void 0&&J(e.cache,{projectId:k.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,note:t.note},e.makeMeta({syncPending:true,humanReadableSummary:Un(t.targetKind,n)}))}function Tl(t,e){return t.registerTool("note_set",{description:Ko,inputSchema:Hk.shape},async n=>{let r=await zk(n,e);return u(r)})}var Xo=`Replace the HTML fragment note on a task or project. Overwrites the existing note entirely with the provided HTML. OmniFocus preserves its supported HTML subset (bold, italic, links, lists, inline images); unsupported elements may be stripped. Pass noteHtml: null to clear the note. For plain-text writes use note_set instead. Returns { updated: true, id, targetKind, name, noteHtml } \u2014 name is the parent task/project's display name (pre-fetched so the response describes the change without a follow-up read); noteHtml echoes back the requested HTML (or null if cleared). Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the change to appear on other devices. Example: note_set_html({ targetKind: "task", id: "abc123", noteHtml: "<b>Priority:</b> high" })`,Gk=z.object({targetKind:z.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to set."),id:z.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),noteHtml:z.string().nullable().describe("HTML fragment to set as the note. Pass null to clear the note entirely.")});async function qk(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(k.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&b(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(k.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&J(e.cache,{projectId:k.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,noteHtml:t.noteHtml},e.makeMeta({syncPending:true,humanReadableSummary:Un(t.targetKind,n)}))}function wl(t,e){return t.registerTool("note_set_html",{description:Xo,inputSchema:Gk.shape},async n=>{let r=await qk(n,e);return u(r)})}function Kk(){let t=fileURLToPath(import.meta.url);return Zi.resolve(Zi.dirname(t),"../../../reports/mutation/mutation.json")}function Xk(t){let e={Killed:0,Survived:0,Timeout:0,NoCoverage:0},n=t.files;if(!n)return e;for(let r of Object.values(n))for(let o of r.mutants??[]){let a=o.status;a==="Killed"?e.Killed++:a==="Survived"?e.Survived++:a==="Timeout"?e.Timeout++:a==="NoCoverage"&&e.NoCoverage++;}return e}function bl(t={}){let e=t.reportPath??Kk();if(!(t.existsSync??dt.existsSync)(e))return null;let r=t.readFile??(g=>dt.readFileSync(g,"utf8")),o=t.stat??dt.statSync,a;try{a=JSON.parse(r(e));}catch{return null}let{Killed:i,Survived:s,Timeout:c,NoCoverage:d}=Xk(a),l=i+s+c+d;if(l===0)return null;let m=(i+c)/l*100,f=o(e).mtime.toISOString();return {score:m,lastRunAt:f}}var Zo="Return a health snapshot of the running omnifocus-mcp server. Do NOT use this to read OmniFocus data \u2014 prefer task_list, project_list, sync_status, etc. Returns { uptimeMs, ofRunning, lastSync, calendarAccess, mutation, cache, circuits, queueDepth }. uptimeMs is the milliseconds since the server process started. circuits lists each circuit-breaker name and state (closed/open/half_open). lastSync mirrors sync_status data; null if getLastSync throws. calendarAccess reports the macOS Calendar bridge state \u2014 { available, permission } where available is true when the Swift binary is callable and permission is the live EventKit authorization status (granted | denied | restricted | not-determined), or 'unknown' when available is false. Read-only \u2014 does NOT trigger the macOS Calendar TCC prompt. mutation surfaces Stryker calibration freshness \u2014 { score, lastRunAt } where score is the latest mutation-testing score (0\u2013100) per ADR-0017 and lastRunAt is the report's mtime. Returns null when no report file is present (the published npm tarball ships without one). Read-only; no side effects. Example: internal_status()",Zk=z.object({});async function Qk(t,e){let n=Date.now()-e.startedAt,r=null;try{let c=await e.adapter.getLastSync();r={lastSyncAt:c.lastSyncAt,inFlight:c.inFlight};}catch{r=null;}let o=e.circuitRegistry.snapshot(),a=null;try{a=await(e.probeCalendarAccess??On)();}catch{a=null;}let i=null;try{i=(e.probeMutationScore??bl)();}catch{i=null;}return p({uptimeMs:n,ofRunning:true,lastSync:r,calendarAccess:a,mutation:i,cache:null,circuits:o,queueDepth:null},e.makeMeta())}function jl(t,e){return t.registerTool("internal_status",{description:Zo,inputSchema:Zk.shape},async n=>{let r=await Qk(n,e);return u(r)})}var _l=["actionAvailability","actionStatus","actionHasAllOfTags","actionHasAnyOfTags","actionHasNoProject","actionHasDueDate","actionHasDeferDate","actionIsLeaf","actionIsProject","actionMatchingSearch","actionWithinFocus"];function eI(t){let e=0;for(let n of _l)t[n]!==void 0&&(e+=1);return e}var Qo=z.array(z.string().min(1,"id must be non-empty")).min(1,"list must contain at least one id"),tI=z.enum(["all","any","none"]),nI=z.object({actionAvailability:z.enum(["available","remaining","completed","dropped","firstAvailable"]).optional(),actionStatus:z.enum(["flagged","due"]).optional(),actionHasAllOfTags:Qo.optional(),actionHasAnyOfTags:Qo.optional(),actionHasNoProject:z.boolean().optional(),actionHasDueDate:z.boolean().optional(),actionHasDeferDate:z.boolean().optional(),actionIsLeaf:z.boolean().optional(),actionIsProject:z.boolean().optional(),actionMatchingSearch:z.array(z.string().min(1)).min(1).optional(),actionWithinFocus:Qo.optional()}).strict(),rI=nI.superRefine((t,e)=>{let n=eI(t);if(n>1){let r=_l.filter(o=>t[o]!==void 0);e.addIssue({code:z.ZodIssueCode.custom,message:`Rule atom must set at most one action predicate; got ${n} (${r.join(", ")}). Combine predicates by wrapping atoms in a RuleAggregate with aggregateType: "all" / "any" / "none" instead.`,path:[]});}}),Xe=z.lazy(()=>z.union([z.object({aggregateType:tI,aggregateRules:z.array(Xe)}).strict(),z.object({disabledRule:Xe}).strict(),rI]));var ea='Create a new custom OmniFocus perspective with the given name, optional rule tree, optional aggregation, and optional icon color. The shell is created via JXA `make` (the only supported create path) and rules + aggregation + iconColor are written via OmniJS in the same transport hop \u2014 if rule writing throws, the shell is rolled back so the database is never left with a half-configured perspective. Use BEFORE composing complex authoring flows: pair with `perspective_get` to clone an existing perspective, or with `perspective_delete` to replace one. Do NOT use to update an existing perspective \u2014 prefer `perspective_update` (slice C) when it lands. rules is the same shape `perspective_get` returns (atom | aggregate | disabled wrapper) \u2014 round-trips are lossless. Each rule atom may set at most one action* predicate; combine predicates by wrapping atoms in a RuleAggregate with aggregateType all/any/none. Tag-id and focus-id arrays must be non-empty with non-empty entries. Returns { id } \u2014 the persistent identifier of the new custom perspective. Side effects: creates a perspective in OmniFocus; invalidates the perspective cache; sets meta.syncPending = true. Custom perspectives require OmniFocus Pro \u2014 without it, the adapter throws FeatureRequiresPro. Example: perspective_create({ name: "Today\'s plate", aggregation: "all", rules: [{ actionStatus: "flagged" }] })',oI=z.enum(["all","any","none"]),aI=z.object({r:z.number().min(0).max(1).describe("Red channel as a [0, 1] float."),g:z.number().min(0).max(1).describe("Green channel as a [0, 1] float."),b:z.number().min(0).max(1).describe("Blue channel as a [0, 1] float."),a:z.number().min(0).max(1).describe("Alpha channel as a [0, 1] float; 1 is opaque.")}).strict(),sI=z.object({name:z.string().min(1).describe("Display name for the new perspective. Must be non-empty and unique within the OmniFocus database \u2014 duplicate names are rejected with VALIDATION_ERROR."),aggregation:oI.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z.array(Xe).optional().describe("Top-level rule list. Empty array means 'show everything' (the default for fresh perspectives). Each rule is an atom (single action* predicate), an aggregate (compound rule with aggregateType + aggregateRules), or a disabled wrapper around either."),iconColor:aI.optional().describe("Custom icon color in [0, 1] floats { r, g, b, a }. Omit for the OmniFocus-assigned default.")});async function iI(t,e){let n=await e.adapter.createCustomPerspective({name:t.name,...t.aggregation!==void 0&&{aggregation:t.aggregation},...t.rules!==void 0&&{rules:t.rules},...t.iconColor!==void 0&&{iconColor:t.iconColor}});return e.cache!==void 0&&e.cache.invalidate("perspective:*"),p({id:n,name:t.name},e.makeMeta({syncPending:true,humanReadableSummary:`Created custom perspective "${t.name}".`}))}function Pl(t,e){return t.registerTool("perspective_create",{description:ea,inputSchema:sI.shape},async n=>{let r=await iI(n,e);return u(r)})}var ta='Delete a custom OmniFocus perspective by id. Use when a perspective is no longer needed \u2014 e.g. cleaning up after a templated workflow, or rotating out a stale view. Do not use on built-in perspectives (inbox, projects, tags, forecast, flagged, nearby, review) \u2014 they cannot be deleted; the call returns a validation error. Custom perspectives require OmniFocus Pro; without it the call returns OF_FEATURE_REQUIRES_PRO. Deletion is permanent \u2014 there is no undo for perspective removal in OmniFocus, so confirm with the user before invoking on a perspective they may want to keep. Recommend a sync_trigger after deletion so other devices observe the change. Returns { id } echoing the deleted identifier. Side effects: writes to OmniFocus (removes the perspective from the document), sets meta.syncPending = true. Example: { "perspectiveId": "fOpKrtZBLaZ" } \u2192 { id: "fOpKrtZBLaZ" }.',cI=z.object({perspectiveId:z.string().min(1).describe('Identifier of the custom perspective to delete. Obtain from perspective_list (look for kind: "custom"). Built-in ids are rejected with a validation error \u2014 built-ins are immutable.')});async function dI(t,e){await e.perspectiveService.delete(t.perspectiveId),e.cache.invalidate("perspective:*");let n=e.makeMeta({cacheHit:false});return p({id:t.perspectiveId},n)}function xl(t,e){return t.registerTool("perspective_delete",{description:ta,inputSchema:cI.shape},async n=>{let r=await dI(n,e);return u(r)})}var na=`Evaluate an OmniFocus perspective and return its task list. Accepts both built-in ids (inbox, projects, tags, forecast, flagged, nearby, review) and custom-perspective ids obtained from perspective_list \u2014 the tool selects the correct transport internally (JXA for built-in, OmniJS for custom). Custom perspectives require OmniFocus Pro; otherwise returns an error with code OF_FEATURE_REQUIRES_PRO. Returns { tasks: Task[] }. For 'review', returns [] \u2014 use review_list_due instead. For 'nearby', returns [] (location unavailable). No side effects; read-only. Example: perspective_evaluate({ perspectiveId: "flagged" }) Example: perspective_evaluate({ perspectiveId: "cust-abc123" })`,lI=z.object({perspectiveId:z.string().min(1).describe("OmniFocus perspective id. Accepts a built-in id (inbox, projects, tags, forecast, flagged, nearby, review) or a custom-perspective id from perspective_list (kind: custom).")});async function pI(t,e){let n=await e.perspectiveService.evaluate(t.perspectiveId),r=e.makeMeta({cacheHit:n.cacheHit});return p({tasks:n.tasks},r)}function Rl(t,e){return t.registerTool("perspective_evaluate",{description:na,inputSchema:lI.shape},async n=>{let r=await pI(n,e);return u(r)})}var oa="Preview a *proposed* OmniFocus perspective rule tree without persisting it. Creates a temporary perspective with the supplied rules, evaluates it, and always deletes the temp perspective inside one OmniJS execution. Pairs with perspective_create for the propose-then-save flow used by the perspective-author prompt: propose rules \u2192 preview matched tasks via this tool \u2192 commit via perspective_create. Custom perspectives require OmniFocus Pro; otherwise returns OF_FEATURE_REQUIRES_PRO. Do NOT use to evaluate a *saved* perspective \u2014 use perspective_evaluate. Returns { tasks: Task[] }. Side effects: creates and immediately deletes a sentinel-named temp perspective inside one OmniJS execution; the database state is unchanged after the call returns. Example: perspective_evaluate_dry_run({ aggregation: 'all', rules: [{ actionStatus: 'flagged' }] })",uI=z.enum(["all","any","none"]),mI=z.object({aggregation:uI.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z.array(Xe).describe("Top-level rule list to evaluate. Empty array means 'show everything' (matches every available task). Each rule is an atom (single action* predicate), an aggregate (compound rule with aggregateType + aggregateRules), or a disabled wrapper around either.")});async function fI(t,e){let n=await e.perspectiveService.evaluateRules(t.rules,t.aggregation);return p({tasks:n.tasks},e.makeMeta({cacheHit:false}))}function Al(t,e){return t.registerTool("perspective_evaluate_dry_run",{description:oa,inputSchema:mI.shape},async n=>{let r=await fI(n,e);return u(r)})}var aa='Read the full configuration of a custom OmniFocus perspective \u2014 name, top-level rule aggregation (all/any/none), the structured rule tree, and icon color (when set). Use to introspect what a perspective filters on before evaluating it, or as a building block for cloning / duplicating perspectives. Do not use on built-in perspectives (inbox, projects, tags, forecast, flagged, nearby, review) \u2014 they have no rule tree and the call returns a validation error. Use perspective_list instead to enumerate available perspectives. Custom perspectives require OmniFocus Pro; without it the call returns OF_FEATURE_REQUIRES_PRO. Returns { perspective: { id, name, aggregation, rules, iconColor } }. Safe to call repeatedly; no side effects, no writes. Example: { "perspectiveId": "fOpKrtZBLaZ" } \u2192 { perspective: { id, name: "Daily Triage", aggregation: "any", rules: [...], iconColor: { r: 0.2, g: 0.5, b: 0.9, a: 1 } } }.',gI=z.object({perspectiveId:z.string().min(1).describe('Identifier of the custom perspective to read. Obtain from perspective_list (look for kind: "custom"). Built-in ids (inbox, projects, tags, forecast, flagged, nearby, review) are rejected with a validation error \u2014 built-ins have no rule tree.')});async function hI(t,e){let n=await e.perspectiveService.get(t.perspectiveId),r=e.makeMeta({cacheHit:false});return p({perspective:n},r)}function El(t,e){return t.registerTool("perspective_get",{description:aa,inputSchema:gI.shape},async n=>{let r=await hI(n,e);return u(r)})}var sa="List all perspectives in OmniFocus \u2014 both built-in (Inbox, Projects, Tags, Forecast, Flagged, Nearby, Review) and custom (OmniFocus Pro). Do not use to evaluate a perspective; prefer perspective_evaluate for that. Returns each perspective's id, name, kind (builtin|custom), and requiresPro flag. Safe to call repeatedly; no side effects, no writes. Example: perspective_list()",kI=z.object({});async function II(t,e){let n=await e.perspectiveService.list(),r=e.makeMeta({cacheHit:n.cacheHit});return p({perspectives:n.perspectives},r)}function Ml(t,e){return t.registerTool("perspective_list",{description:sa,inputSchema:kI.shape},async n=>{let r=await II(n,e);return u(r)})}var st=["inbox","projects","tags","forecast","flagged","nearby","review"];z.object({id:z.string().min(1),name:z.string().min(1),kind:z.enum(["builtin","custom"]),requiresPro:z.boolean(),icon:z.string().nullable()});var Nl=z.enum(["all","any","none"]),vI=z.object({actionAvailability:z.enum(["available","remaining","completed","dropped","firstAvailable"]).optional(),actionStatus:z.enum(["flagged","due"]).optional(),actionHasAllOfTags:z.array(z.string()).optional(),actionHasAnyOfTags:z.array(z.string()).optional(),actionHasNoProject:z.boolean().optional(),actionHasDueDate:z.boolean().optional(),actionHasDeferDate:z.boolean().optional(),actionIsLeaf:z.boolean().optional(),actionIsProject:z.boolean().optional(),actionMatchingSearch:z.array(z.string()).optional(),actionWithinFocus:z.array(z.string()).optional(),unknown:z.record(z.string(),z.unknown()).optional()}),ia=z.lazy(()=>z.union([z.object({aggregateType:Nl,aggregateRules:z.array(ia)}),z.object({disabledRule:ia}),vI])),TI=z.object({r:z.number().min(0).max(1),g:z.number().min(0).max(1),b:z.number().min(0).max(1),a:z.number().min(0).max(1)});z.object({id:z.string().min(1),name:z.string().min(1),aggregation:Nl,rules:z.array(ia),iconColor:TI.nullable()});var ca='Partial-patch update of a custom OmniFocus perspective. Only fields present in the input are written \u2014 omitting a field leaves the existing value unchanged. Passing iconColor: null clears the custom color back to the OmniFocus default; passing rules: [] clears the rule tree to \'show everything\'. Use to rename a perspective, retune its rule tree, swap the aggregation, or recolor the icon. Do NOT use to create a new perspective (prefer `perspective_create`) or to alter built-in perspectives \u2014 built-in ids (Inbox, Forecast, Flagged, Projects, Tags, Nearby, Review) are rejected with VALIDATION_ERROR. rules is the same shape `perspective_get` returns (atom | aggregate | disabled wrapper) \u2014 round-trips are lossless. Each rule atom may set at most one action* predicate; combine predicates by wrapping atoms in a RuleAggregate with aggregateType all/any/none. Returns { id } \u2014 the persistent identifier of the patched perspective. Side effects: writes to OmniFocus; invalidates the perspective cache; sets meta.syncPending = true. Custom perspectives require OmniFocus Pro \u2014 without it, the adapter throws FeatureRequiresPro. Example: perspective_update({ perspectiveId: "abc123", name: "Today\'s plate", aggregation: "all" }) Example: perspective_update({ perspectiveId: "abc123", iconColor: null })',wI=z.enum(["all","any","none"]),SI=z.object({r:z.number().min(0).max(1).describe("Red channel as a [0, 1] float."),g:z.number().min(0).max(1).describe("Green channel as a [0, 1] float."),b:z.number().min(0).max(1).describe("Blue channel as a [0, 1] float."),a:z.number().min(0).max(1).describe("Alpha channel as a [0, 1] float; 1 is opaque.")}).strict(),bI=z.object({perspectiveId:z.string().min(1).describe("Persistent identifier of the custom perspective to patch. Get from `perspective_list`. Built-in perspective ids are rejected with VALIDATION_ERROR."),name:z.string().min(1).optional().describe("New display name. Must be non-empty when provided. OmniFocus rejects duplicate names."),aggregation:wI.optional().describe('New top-level rule aggregation. One of "all", "any", "none".'),rules:z.array(Xe).optional().describe("New top-level rule list. Empty array clears the rule tree to 'show everything'. Each rule is an atom (single action* predicate), an aggregate (compound rule with aggregateType + aggregateRules), or a disabled wrapper around either."),iconColor:z.union([SI,z.null()]).optional().describe("New custom icon color, or null to clear back to the OmniFocus default. Omit to leave the existing color unchanged.")});async function jI(t,e){if(st.includes(t.perspectiveId))throw new y(`Built-in perspectives cannot be updated: ${t.perspectiveId}. Built-in ids: ${st.join(", ")}.`,{details:{field:"perspectiveId",id:t.perspectiveId,kind:"builtin"},suggestion:"Use perspective_list to find a custom perspective id, or call perspective_create to create a new one."});return await e.adapter.updateCustomPerspective(t.perspectiveId,{...t.name!==void 0&&{name:t.name},...t.aggregation!==void 0&&{aggregation:t.aggregation},...t.rules!==void 0&&{rules:t.rules},...t.iconColor!==void 0&&{iconColor:t.iconColor}}),e.cache!==void 0&&e.cache.invalidate("perspective:*"),p({id:t.perspectiveId},e.makeMeta({syncPending:true,humanReadableSummary:`Updated custom perspective ${t.perspectiveId}.`}))}function Fl(t,e){return t.registerTool("perspective_update",{description:ca,inputSchema:bI.shape},async n=>{let r=await jI(n,e);return u(r)})}var Ot=class{adapter;constructor({adapter:e}){this.adapter=e;}async invoke(e){let n={identifier:e.identifier,...e.arg!==void 0?{arg:e.arg}:{}};return {result:(await this.adapter.pluginInvoke(n)).result}}};var la='Invoke a named Omni Automation plug-in action in OmniFocus. Use this when you need to run a specific installed plug-in \u2014 not for built-in OmniFocus operations. Do NOT use to run arbitrary JavaScript; for raw scripting use run_omnijs_script (requires opt-in env var). `identifier` is the plug-in\'s bundle ID (e.g. `"com.example.my-plugin"`). `arg` is an optional JSON-serialisable value passed to the plug-in action as Action.args[0]. Returns { result } where result is the plug-in\'s return value (arbitrary JSON). Throws NotFound if the plug-in is not installed. Side effects: plug-in may mutate OmniFocus data; call sync_trigger if you need changes on other devices. Example: plugin_invoke({ identifier: "com.example.my-plugin" }) Example: plugin_invoke({ identifier: "com.example.my-plugin", arg: { mode: "export" } })',_I=z.object({identifier:z.string().min(1).describe('Bundle identifier of the Omni Automation plug-in to invoke (e.g. "com.example.my-plugin").'),arg:z.unknown().optional().describe("Optional JSON-serialisable argument forwarded to the plug-in action as Action.args[0]. Defaults to null.")});async function PI(t,e){let n=new Ot({adapter:e.adapter}),{result:r}=await n.invoke({identifier:t.identifier,arg:t.arg}),o=e.makeMeta();return p({result:r},o)}function Ul(t,e){return t.registerTool("plugin_invoke",{description:la,inputSchema:_I.shape},async n=>{let r=await PI(n,e);return u(r)})}var ua='Mark many OmniFocus projects as completed in a single JXA round trip. Completed projects are hidden from active views and closed to new task entry. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each completion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated project_complete calls whenever completing more than one project. Each item is { id }. Returns { completed: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the project name so the agent can describe each completion without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: project_batch_complete({ items: [{ id: "prj123" }, { id: "prj456" }] })',OI=z.object({id:k.schema.describe("Persistent project ID.")}),xI=z.object({items:z.array(OI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function DI(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getProjectsMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchCompleteProjects(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&J(e.cache,{projectId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({completed:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ud(a.succeeded.length)}))}function Ll(t,e){return t.registerTool("project_batch_complete",{description:ua,inputSchema:xI.shape},async n=>{let r=await DI(n,e);return u(r)})}var fa='Cancel (drop) many OmniFocus projects in a single JXA round trip. Dropped projects remain in OmniFocus but are treated as cancelled/inactive \u2014 they do not appear in active project lists. Use project_delete for permanent removal. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each drop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated project_drop calls whenever dropping more than one project. Each item is { id }. Returns { dropped: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the project name so the agent can describe each drop without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: project_batch_drop({ items: [{ id: "prj123" }, { id: "prj456" }] })',RI=z.object({id:k.schema.describe("Persistent project ID.")}),AI=z.object({items:z.array(RI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function CI(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getProjectsMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchDropProjects(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&J(e.cache,{projectId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({dropped:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ld(a.succeeded.length)}))}function Bl(t,e){return t.registerTool("project_batch_drop",{description:fa,inputSchema:AI.shape},async n=>{let r=await CI(n,e);return u(r)})}var ga=`Complete an OmniFocus project \u2014 marks it done with today's date and moves it out of the active view. Use when a project is finished. Do not use to archive or hide a project without completing it; prefer project_drop for that. Returns { completed: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: sets completionDate, removes from active projects, sets meta.syncPending = true. Example: project_complete({ id: "prj123" })`,ha=z.object({id:k.schema.describe("Persistent ID of the project to complete.")});async function MI(t,e){let{project:n}=await e.projectService.get({id:t.id,includeTaskTree:false});return await e.projectService.completeProject(t.id),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({completed:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Gd()}))}function Jl(t,e){return t.registerTool("project_complete",{description:ga,inputSchema:ha.shape},async n=>{let r=await MI(n,e);return u(r)})}var ya="Preview what project_complete would do without making any changes. Do NOT use to actually complete a project \u2014 use project_complete instead. Returns { description, plannedChanges } describing the completion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function NI(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}n.push({field:"status",newValue:"done",oldValue:"active"});let o=`Would mark project '${r}' as completed.`;return p({description:o,plannedChanges:n},e.makeMeta())}function $l(t,e){return t.registerTool("project_complete_describe",{description:ya,inputSchema:ha.shape},async n=>{let r=await NI(n,e);return u(r)})}function ie(t,e,n){let r={};for(let[i,s]of Object.entries(e)){if(!t.includes(s))throw new TypeError(`aliasedEnum: alias '${i}' points at '${s}' which is not in canonical set ${JSON.stringify(t)}`);r[i.toLowerCase()]=s;}let o=Object.entries(e).map(([i,s])=>`'${i}' \u2192 ${s}`).join(", "),a=o?`${n} Accepts: ${o}.`:n;return z.preprocess(i=>typeof i!="string"?i:r[i.toLowerCase()]??i,z.enum(t)).describe(a)}function FI(t,e={}){return {kind:"missing-detail",reason:t,...e}}function ka(t,e={}){return {kind:"next-natural-step",reason:t,...e}}function UI(t,e={}){return {kind:"consider-alternative",reason:t,...e}}function LI(t,e=3){return t.length<=e?t:[...t].sort((r,o)=>{let a=(r.severity??"info")==="warn"?0:1,i=(o.severity??"info")==="warn"?0:1;return a-i}).slice(0,e)}function BI(t){return process.env.OMNIFOCUS_HINT_LEVEL==="warn"?t.filter(e=>(e.severity??"info")==="warn"):t}function xt(t,e=3){let n=BI(t),r=LI(n,e);return r.length>0?r:void 0}var JI=/\b(daily|weekly|monthly|every\s+(day|week|month|monday|tuesday|wednesday|thursday|friday|saturday|sunday|weekday|weekend))\b/i;function Hl(t,e){if(JI.test(e))return ka("Task name contains a recurrence cue \u2014 setting a repetition rule keeps it rescheduled automatically.",{suggestedTool:"task_set_repetition",suggestedArgs:{id:t},severity:"info"})}function zl(t,e,n){if(!(e===void 0||n!==void 0))return FI("Task has a due date but no time estimate \u2014 an estimate helps schedule the task accurately.",{suggestedTool:"task_update",suggestedArgs:{id:t},severity:"info"})}function Gl(t,e=5){if(!(t<e))return UI(`Inbox now has ${t} unrouted tasks \u2014 consider triaging to keep your inbox clear.`,{suggestedTool:"task_list",suggestedArgs:{inbox:true},severity:"info"})}function ql(t,e){if(e===void 0)return ka("Project has no review interval \u2014 setting one ensures it surfaces in regular review.",{suggestedTool:"project_update",suggestedArgs:{id:t,reviewIntervalDays:7},severity:"info"})}function Vl(t,e){return ka(`Project '${e}' now has no remaining tasks \u2014 consider completing or reviewing the project.`,{suggestedTool:"project_complete",suggestedArgs:{id:t},severity:"info"})}var Ia=class{_ttlMs;_maxEntries;_now;_entries=new Map;constructor(e={}){this._ttlMs=e.ttlMs??6e5,this._maxEntries=e.maxEntries??1024,this._now=e.now??(()=>Date.now());}get(e){let n=this._entries.get(e);if(n){if(n.expiresAt<=this._now()){this._entries.delete(e);return}return this._entries.delete(e),this._entries.set(e,n),n.envelope}}set(e,n){for(this._entries.delete(e),this._entries.set(e,{envelope:n,expiresAt:this._now()+this._ttlMs});this._entries.size>this._maxEntries;){let r=this._entries.keys().next().value;if(r===void 0)break;this._entries.delete(r);}}get size(){return this._entries.size}clear(){this._entries.clear();}},Kl=new WeakMap;function $I(t){let e=Kl.get(t);return e||(e=new Map,Kl.set(t,e)),e}async function ce(t,e,n){if(e===void 0)return n();let r=t.get(e);if(r)return Xl(r);let o=$I(t),a=o.get(e);if(a){let s=await a;return Xl(s)}let i=(async()=>{let s=await n();return t.set(e,s),s})();o.set(e,i);try{return await i}finally{o.delete(e);}}function Xl(t){return {...t,meta:{...t.meta,idempotentReplay:true}}}function Yl(t,e){let n=process.env[t];if(n===void 0||n==="")return e;let r=Number.parseInt(n,10);return !Number.isFinite(r)||r<=0?e:r}var de=new Ia({ttlMs:Yl("OMNIFOCUS_IDEMPOTENCY_TTL_MS",6e5),maxEntries:Yl("OMNIFOCUS_IDEMPOTENCY_MAX_ENTRIES",1024)});var Ta='Create a new OmniFocus project. Optionally place it in a folder, assign tags, set completion criterion, status, defer/due dates, estimated minutes, flagged state, and review interval. Safety control: pass idempotency_key to make transport retries safe \u2014 identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate project. Returns { created: true, id }. Side effects: creates a project in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the project to appear on other devices. Example: project_create({ name: "Website Redesign" }) Example: project_create({ name: "Q3 Planning", folderId: "fld123", flagged: true })',wa=z.object({name:z.string().min(1).describe("Project name. Required, must be non-empty."),folderId:W.schema.optional().describe("Folder ID to place the project in. Omit for root."),note:z.string().optional().describe("Plain-text note for the project."),status:ie(["active","on-hold"],{paused:"on-hold"},"Initial project status. Default: active.").optional(),completionCriterion:ie(["parallel","sequential","singleActions"],{"in-order":"sequential","in order":"sequential","any-order":"parallel","any order":"parallel"},"How the project's tasks are completed: parallel (any order), sequential (in order), or singleActions.").optional(),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with UTC offset."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with UTC offset."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().min(1).optional().describe("Estimated total duration in minutes."),flagged:z.boolean().optional().describe("Flag the project."),tagIds:z.array(S.schema).optional().describe("Tag IDs to apply to the project."),reviewIntervalDays:z.number().int().min(1).optional().describe("Review interval in days. Omit to use OmniFocus default."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe creates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate project.")});async function WI(t,e){let n=e.idempotencyStore??de,r=e.replayStore??ve;if(t.idempotency_key!==void 0&&n.get(t.idempotency_key)!==void 0)return va(t,e,n);let o=[];try{o=(await e.adapter.listProjects()).filter(i=>i.name.toLowerCase()===t.name.toLowerCase()&&i.status!=="dropped");}catch{}if(o.length>0){let a=e.makeMeta(),i=o[0],s=[`Use existing project "${i.name}" (id: ${i.id})`,"Create a new project with the same name"],c=r.register(s,async d=>d===0?p({created:false,id:i.id,existing:true},e.makeMeta()):va(t,e,n));return St(`A project named "${t.name}" already exists. What should happen?`,c,a,s.map((d,l)=>({index:l,label:d})),{name:t.name})}return va(t,e,n)}async function va(t,e,n){return ce(n,t.idempotency_key,async()=>{let r={name:t.name,...t.folderId!==void 0&&{folderId:t.folderId},...t.note!==void 0&&{note:t.note},...t.status!==void 0&&{status:t.status},...t.completionCriterion!==void 0&&{completionCriterion:t.completionCriterion},...t.deferDate!==void 0&&{deferDate:t.deferDate},...t.deferDateFloating!==void 0&&{deferDateFloating:t.deferDateFloating},...t.dueDate!==void 0&&{dueDate:t.dueDate},...t.dueDateFloating!==void 0&&{dueDateFloating:t.dueDateFloating},...t.estimatedMinutes!==void 0&&{estimatedMinutes:t.estimatedMinutes},...t.flagged!==void 0&&{flagged:t.flagged},...t.tagIds!==void 0&&{tagIds:t.tagIds},...t.reviewIntervalDays!==void 0&&{reviewIntervalDays:t.reviewIntervalDays}},o=await e.adapter.createProject(r);e.cache!==void 0&&J(e.cache,{projectId:o});let a=xt([ql(o,t.reviewIntervalDays)].filter(i=>i!=null));return p({created:true,id:o},e.makeMeta({syncPending:true,humanReadableSummary:Md(t.name)}),void 0,a)})}function Zl(t,e){return t.registerTool("project_create",{description:Ta,inputSchema:wa.shape},async n=>{let r=await WI(n,e);return u(r)})}var Sa="Preview what project_create would do without making any changes. Do NOT use to actually create a project \u2014 use project_create instead. Returns { description, plannedChanges } describing the project that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function HI(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.folderId!==void 0){let a=await Ke(e.adapter,t.folderId);n.push({field:"folderId",newValue:t.folderId}),r.push(`in folder '${a}'`);}else r.push("at root");if(t.status!==void 0&&(n.push({field:"status",newValue:t.status}),r.push(`status '${t.status}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate}),r.push(`due ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${Te(t.deferDate)}`)),t.flagged===true&&(n.push({field:"flagged",newValue:"true"}),r.push("flagged")),t.tagIds!==void 0&&t.tagIds.length>0){let a=await Promise.all(t.tagIds.map(i=>he(e.adapter,i)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`tagged ${a.map(i=>`'${i}'`).join(", ")}`);}t.note!==void 0&&n.push({field:"note",newValue:t.note.slice(0,50)});let o=`Would create project ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Ql(t,e){return t.registerTool("project_create_describe",{description:Sa,inputSchema:wa.shape},async n=>{let r=await HI(n,e);return u(r)})}function Ye(t,e,n){if(t===void 0)return;let r=Date.parse(t);if(Number.isNaN(r))throw new y(`expectedModifiedAt for ${n} is not a valid ISO-8601 timestamp.`,{details:{resource:n,expected:t}});let o=Date.parse(e);if(Number.isNaN(o))throw new y(`observed modifiedAt for ${n} is not a valid ISO-8601 timestamp.`,{details:{resource:n,observed:e}});if(r!==o)throw new ft(`${n} was modified since expectedModifiedAt.`,{details:{resource:n,expected:t,observed:e}})}async function Ce(t,e,n){if(t!==true)return n();let r=await e();return zI(r)}function zI(t){return {...t,meta:{...t.meta,dryRun:true,syncPending:false}}}var ba='Permanently delete an OmniFocus project and ALL its contained tasks. IRREVERSIBLE \u2014 uses OmniFocus deleteObject; there is no undo. All tasks inside the project are also permanently deleted (cascade). Prefer project_drop when you want a recoverable status change. Only use project_delete when the agent has explicit user intent to permanently remove the project and its tasks. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent project_get) to reject the call if the project changed since you read it; pass idempotency_key to coalesce retries so the same delete is only performed once. Returns { deleted: true, id } on success. Side effects: removes the project and its tasks from OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the deletion to appear on other devices. Example: project_delete({ id: "prj123", dry_run: true }) Example: project_delete({ id: "prj123", expectedModifiedAt: "2026-04-01T10:00:00Z" })',ja=z.object({id:k.schema.describe("Persistent ID of the project to delete. Get from project_list. Verify you have the correct ID before calling \u2014 this action is irreversible and deletes all contained tasks."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent project_get. If the project's current modifiedAt differs, the call fails with OF_CONFLICT and no delete is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe deletes. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-deleting (or re-raising NotFound on the second attempt).")});async function GI(t,e){let n=e.idempotencyStore??de;return ce(n,t.idempotency_key,async()=>{let r=await e.adapter.getProject(t.id);Ye(t.expectedModifiedAt,r.modifiedAt,`project:${t.id}`);let o=()=>p({deleted:true,id:t.id},e.makeMeta({syncPending:false})),a=async()=>(await e.adapter.deleteProject(t.id),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Fd(r.name)})));return Ce(t.dry_run,o,a)})}function ep(t,e){return t.registerTool("project_delete",{description:ba,inputSchema:ja.shape},async n=>{let r=await GI(n,e);return u(r)})}var _a="Preview what project_delete would do without making any changes. Do NOT use to actually delete a project \u2014 use project_delete instead. Returns { description, plannedChanges } describing the permanent deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function qI(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=`Would permanently delete project '${r}' (id: ${t.id}) and ALL its contained tasks. IRREVERSIBLE.`;return p({description:o,plannedChanges:n},e.makeMeta())}function tp(t,e){return t.registerTool("project_delete_describe",{description:_a,inputSchema:ja.shape},async n=>{let r=await qI(n,e);return u(r)})}var Pa='Drop an OmniFocus project \u2014 marks it as on-hold/dropped and removes it from the active view without completing it. Use to defer or abandon a project while keeping it recoverable. Do not use if the project is actually done; prefer project_complete for that. Returns { dropped: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: changes project status, sets meta.syncPending = true.Example: project_drop({ id: "prj123" })',Oa=z.object({id:k.schema.describe("Persistent ID of the project to drop.")});async function KI(t,e){let{project:n}=await e.projectService.get({id:t.id,includeTaskTree:false});return await e.projectService.dropProject(t.id),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({dropped:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:qd()}))}function np(t,e){return t.registerTool("project_drop",{description:Pa,inputSchema:Oa.shape},async n=>{let r=await KI(n,e);return u(r)})}var xa="Preview what project_drop would do without making any changes. Do NOT use to actually drop a project \u2014 use project_drop instead. Returns { description, plannedChanges } describing the status change that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function XI(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}n.push({field:"status",newValue:"dropped",oldValue:"active"});let o=`Would drop project '${r}' (mark as on-hold/abandoned).`;return p({description:o,plannedChanges:n},e.makeMeta())}function rp(t,e){return t.registerTool("project_drop_describe",{description:xa,inputSchema:Oa.shape},async n=>{let r=await XI(n,e);return u(r)})}var Da=`Fetch a single OmniFocus project by persistent ID. Do NOT use for queries across projects \u2014 use project_list. When includeTaskTree=true (default), the project's flat task list is attached. Returns { project, tasks? }; safe to call repeatedly; no side effects. Example: project_get({ id: "prj123" }) Example: project_get({ id: "prj123", includeTaskTree: false })`,YI=z.object({id:k.schema.describe("Persistent project ID. Get from project_list or search_query."),includeTaskTree:z.boolean().optional().describe("Whether to attach the project's tasks (flat array; clients rebuild the tree via parentId). Default true. Set to false for a fast project-only read.")});async function ZI(t,e){let n=await e.projectService.get({id:t.id,...t.includeTaskTree!==void 0?{includeTaskTree:t.includeTaskTree}:{}}),r=e.makeMeta({cacheHit:n.cacheHit}),o=Ae(n.project.note),a={project:n.project};return n.tasks!==void 0&&(a.tasks=n.tasks),o!==void 0&&(a.decision=o),p(a,r)}function ap(t,e){return t.registerTool("project_get",{description:Da,inputSchema:YI.shape},async n=>{let r=await ZI(n,e);return u(r)})}var Ra='Fetch up to 100 projects by persistent ID in a single OmniFocus round-trip. Use when you have a set of project IDs and need full project objects for all of them. Do NOT use for a single ID \u2014 use project_get instead. Returns Project[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry. Example: project_get_many({ ids: ["prj123", "prj456"] })',Hn=100,QI=z.object({ids:z.array(k.schema).min(0).max(Hn).describe(`Array of project IDs to fetch (0..${Hn}). Get IDs from project_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function ev(t,e){if(t.ids.length===0)return p({projects:[]},e.makeMeta());if(t.ids.length>Hn)throw new y(`ids array exceeds the maximum batch size of ${Hn} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getProjectsMany(t.ids),r=n.filter(d=>d!==null),o=t.ids.filter((d,l)=>n[l]===null),a={};for(let d of r){let l=Ae(d.note);l!==void 0&&(a[d.id]=l);}let i=Object.keys(a).length>0,s=o.length>0?[wt(o)]:void 0,c=e.makeMeta({...s!==void 0?{warnings:s}:{}});return p({projects:r,...i&&{decisions:a}},c)}function ip(t,e){return t.registerTool("project_get_many",{description:Ra,inputSchema:QI.shape},async n=>{let r=await ev(n,e);return u(r)})}var Aa='List projects in OmniFocus with optional filters. Use for queries across projects. Do NOT use for a known single project (use project_get). Filters: folderId, status, flagged, reviewDueBefore. Returns projects[] with pagination; safe to call repeatedly; no side effects. Example: project_list({}) Example: project_list({ status: "active", folderId: "fld123" })',tv=z.object({folderId:W.schema.optional().describe("Restrict to projects inside this folder. Get the ID from folder_list. Omit for all folders."),status:ie(["active","on-hold","done","dropped"],{paused:"on-hold",completed:"done",cancelled:"dropped"},"Restrict to projects with this status. 'active' = available; 'on-hold' = paused; 'done' = completed; 'dropped' = abandoned. Omit for any status.").optional(),flagged:z.boolean().optional().describe("true = flagged only; false = unflagged only; omit = both."),reviewDueBefore:z.string().optional().describe("Restrict to projects whose next review date is strictly before this moment. ISO-8601 with offset (e.g. '2026-05-01T00:00:00-07:00'). Projects without a review interval are excluded."),limit:z.number().int().min(1).max(1e3).optional().describe("Max projects per page (1..1000). Default 200. Use `cursor` to fetch subsequent pages."),cursor:z.string().optional().describe("Opaque cursor from a previous project_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")});async function nv(t,e){let n=t,r=await e.projectService.list(n),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return p({projects:r.projects},a,o)}function cp(t,e){return t.registerTool("project_list",{description:Aa,inputSchema:tv.shape},async n=>{let r=await nv(n,e);return u(r)})}var Ca=`Move an OmniFocus project to a different folder. Pass folderId to move into a folder, or null to move to the root (no folder). Use when reorganizing projects. Do not use to complete or drop a project. Returns { moved: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: changes the project's folder, sets meta.syncPending = true.Example: project_move({ id: "prj123", folderId: "fld456" }) Example: project_move({ id: "prj123", folderId: null })`,Ea=z.object({id:k.schema.describe("Persistent ID of the project to move."),folderId:W.schema.nullable().describe("Target folder ID, or null to move to root.")});async function ov(t,e){let{project:n}=await e.projectService.get({id:t.id,includeTaskTree:false});return await e.projectService.moveProject(t.id,{folderId:t.folderId}),e.cache!==void 0&&J(e.cache,{projectId:t.id}),p({moved:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Vd(t.folderId!=null?"folder":"library root")}))}function dp(t,e){return t.registerTool("project_move",{description:Ca,inputSchema:Ea.shape},async n=>{let r=await ov(n,e);return u(r)})}var Ma="Preview what project_move would do without making any changes. Do NOT use to actually move a project \u2014 use project_move instead. Returns { description, plannedChanges } describing the folder change that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function av(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getProject(t.id)).name;}catch{}let o;if(t.folderId!==null){let i=await Ke(e.adapter,t.folderId);n.push({field:"folderId",newValue:t.folderId}),o=`folder '${i}'`;}else n.push({field:"folderId",newValue:null}),o="root (no folder)";let a=`Would move project '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function lp(t,e){return t.registerTool("project_move_describe",{description:Ma,inputSchema:Ea.shape},async n=>{let r=await av(n,e);return u(r)})}var Na="project-template",sv=z.object({name:z.string().min(1),parameterNames:z.array(z.string().min(1)),capturedAt:oe()});function Dt(t){let e=Be(t,Na);if(e===void 0)return;let n=kt(e.body),r={};typeof n.name=="string"&&n.name.length>0&&(r.name=n.name),typeof n.parameters=="string"?r.parameterNames=n.parameters.split(",").map(a=>a.trim()).filter(a=>a.length>0):r.parameterNames=[],typeof n.capturedAt=="string"&&n.capturedAt.length>0&&(r.capturedAt=n.capturedAt);let o=sv.safeParse(r);return o.success?o.data:void 0}function pp(t,e){let n=It({name:t.name,parameters:t.parameterNames.length>0?t.parameterNames.join(","):void 0,capturedAt:t.capturedAt}),r=vt(null,Na,n);return e.length===0?r:`${r}
124
210
 
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(`
211
+ ${e}`}function up(t){let e=Be(t,Na);return e===void 0||t===null?"":t.slice(e.end).replace(/^\n+/,"")}function mp(t,e){return t.replace(/\{\{\s*([A-Za-z0-9_-]+)\s*\}\}/g,(n,r)=>Object.hasOwn(e,r)?e[r]:n)}function fp(t){let e=[];for(let n of t.matchAll(/@due\((\d{4}-\d{2}-\d{2})\)/g))e.push(n[1]);if(e.length!==0)return e.sort()[0]}function gp(t,e,n){let r=Date.UTC(Number(e.slice(0,4)),Number(e.slice(5,7))-1,Number(e.slice(8,10))),a=Date.UTC(Number(n.slice(0,4)),Number(n.slice(5,7))-1,Number(n.slice(8,10)))-r;if(a===0)return t;let i=s=>{let c=Date.UTC(Number(s.slice(0,4)),Number(s.slice(5,7))-1,Number(s.slice(8,10)))+a,d=new Date(c),l=d.getUTCFullYear().toString().padStart(4,"0"),m=(d.getUTCMonth()+1).toString().padStart(2,"0"),f=d.getUTCDate().toString().padStart(2,"0");return `${l}-${m}-${f}`};return t.replace(/@(due|defer)\((\d{4}-\d{2}-\d{2})\)/g,(s,c,d)=>`@${c}(${i(d)})`)}async function Gn(t,e){let n=await t.listTasks({projectId:e}),r=[...n],o=[...n];for(;;){let a=o.shift();if(a===void 0)break;let i=await t.listTasks({parentId:a.id});for(let s of i)r.push(s),o.push(s);}return r}function Rt(t){let e=new Set(t.map(o=>String(o.id))),n=[],r=new Map;for(let o of t)if(o.parentId===null||!e.has(String(o.parentId)))n.push(o);else {let i=String(o.parentId),s=r.get(i);s?s.push(o):r.set(i,[o]);}return {rootTasks:n,byParent:r}}function xe(t){return t.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function hp(t,e,n){let r=[`text="${xe(t.name)}"`,'type="omnifocus:task"',`id="${xe(String(t.id))}"`];t.dueDate&&r.push(`due="${xe(t.dueDate)}"`),t.deferDate&&r.push(`defer="${xe(t.deferDate)}"`),t.flagged&&r.push('flagged="true"'),t.completed&&r.push('completed="true"'),t.dropped&&r.push('dropped="true"'),t.note&&r.push(`note="${xe(t.note)}"`);let o=e.get(String(t.id))??[];if(o.length===0)return `${n}<outline ${r.join(" ")} />`;let a=`${n} `,i=o.map(s=>hp(s,e,a)).join(`
126
212
  `);return `${n}<outline ${r.join(" ")}>
127
213
  ${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(`
214
+ ${n}</outline>`}function yp(t,e,n){let{rootTasks:r,byParent:o}=Rt(e),a=[`text="${xe(t.name)}"`,'type="omnifocus:project"',`id="${xe(String(t.id))}"`,`status="${xe(t.status)}"`];t.dueDate&&a.push(`due="${xe(t.dueDate)}"`),t.deferDate&&a.push(`defer="${xe(t.deferDate)}"`),t.flagged&&a.push('flagged="true"'),t.note&&a.push(`note="${xe(t.note)}"`);let i=`${n} `;if(r.length===0)return `${n}<outline ${a.join(" ")} />`;let s=r.map(c=>hp(c,o,i)).join(`
129
215
  `);return `${n}<outline ${a.join(" ")}>
130
216
  ${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(`
217
+ ${n}</outline>`}function be(t,e){let r=new RegExp(`\\b${e}=(?:"([^"]*)"|'([^']*)')`,"i").exec(t);if(!r)return;let o=r[1]??r[2]??"";return iv(o)}function iv(t){return t.replace(/&quot;/g,'"').replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/g,"&")}function cv(t){let e=[],n=/<(!--[\s\S]*?--|[?!][^>]*|\/(\w[\w:-]*)\s*|(\w[\w:-]*)([^>]*?)(\/?))\s*>/g;for(let r=n.exec(t);r!==null;r=n.exec(t)){let o=r[0];if(o.startsWith("<!--")||o.startsWith("<?")||o.startsWith("<!"))continue;let a=r[2];if(a){e.push({kind:"close",tag:a.toLowerCase()});continue}let i=r[3];if(i){let s=(r[4]??"").trim(),c=(r[5]??"")==="/";e.push({kind:"open",tag:i.toLowerCase(),attrs:s,selfClose:c});}}return e}function kp(t,e,n){let o={text:be(n,"text")??"",children:[]},a=be(n,"type");a!==void 0&&(o.type=a);let i=be(n,"id");i!==void 0&&(o.id=i);let s=be(n,"due");s!==void 0&&(o.due=s);let c=be(n,"defer");c!==void 0&&(o.defer=c),be(n,"flagged")==="true"&&(o.flagged=true);let l=e;for(;l<t.length;){let m=t[l];if(!m)break;if(m.kind==="close"&&m.tag==="outline")return [o,l+1];if(m.kind==="open"&&m.tag==="outline")if(m.selfClose){let f=Ip(m.attrs);o.children.push(f),l++;}else {let[f,g]=kp(t,l+1,m.attrs);o.children.push(f),l=g;}else l++;}return [o,l]}function Ip(t){let n={text:be(t,"text")??"",children:[]},r=be(t,"type");r!==void 0&&(n.type=r);let o=be(t,"id");o!==void 0&&(n.id=o);let a=be(t,"due");a!==void 0&&(n.due=a);let i=be(t,"defer");return i!==void 0&&(n.defer=i),be(t,"flagged")==="true"&&(n.flagged=true),n}function vp(t){let e=t.trim();if(!/<opml\b/i.test(e))throw new y("Not valid OPML: missing <opml> root element.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});if(!/<body\b/i.test(e))throw new y("Not valid OPML: missing <body> element.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});let n=cv(e),r=-1;for(let i=0;i<n.length;i++){let s=n[i];if(s&&s.kind==="open"&&s.tag==="body"){r=i+1;break}}if(r===-1)throw new y("Not valid OPML: could not find <body> open tag in token stream.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});let o=[],a=r;for(;a<n.length;){let i=n[a];if(!i||i.kind==="close"&&i.tag==="body")break;if(i.kind==="open"&&i.tag==="outline")if(i.selfClose)o.push(Ip(i.attrs)),a++;else {let[s,c]=kp(n,a+1,i.attrs);o.push(s),a=c;}else a++;}return {body:o}}function dn(t,e,n,r,o){let a=" ".repeat(n),i=[];t.dueDate&&i.push(`@due(${t.dueDate.slice(0,10)})`),t.deferDate&&i.push(`@defer(${t.deferDate.slice(0,10)})`),t.flagged&&i.push("@flagged"),t.completed&&i.push("@done"),t.dropped&&i.push("@dropped");let s=i.length>0?` ${i.join(" ")}`:"";r.push(`${a}- ${t.name}${s}`);let c=t.note??(t.noteHtml?t.noteHtml.replace(/<[^>]*>/g,""):null);if(c){for(let l of c.split(`
218
+ `))l.trim()&&r.push(`${a} ${l}`);t.noteHtml&&!t.note&&o.push(`Task "${t.name}": HTML note downgraded to plain text`);}let d=e.get(String(t.id))??[];for(let l of d)dn(l,e,n+1,r,o);}function wp(t,e,n){let r=t,o,a,i=false,s=false,c=[];r=r.replace(/@due\(([^)]+)\)/g,(f,g)=>(o=Tp(g.trim(),e,n,"due"),"")),r=r.replace(/@defer\(([^)]+)\)/g,(f,g)=>(a=Tp(g.trim(),e,n,"defer"),"")),r=r.replace(/@flagged/g,()=>(i=true,"")),r=r.replace(/@done/g,()=>(s=true,"")),r=r.replace(/@dropped/g,()=>(s=true,"")),r=r.replace(/@([\w-]+)/g,(f,g)=>(g!=="due"&&g!=="defer"&&g!=="flagged"&&g!=="done"&&g!=="dropped"&&c.push(g),""));let d=r.split("//"),l=(d[0]??"").trim(),m=d[1]?d[1].trim():void 0;return l||n.push(`Line ${e}: empty task name after parsing tags \u2014 skipped`),{name:l||"(unnamed)",dueDate:o,deferDate:a,flagged:i,done:s,tagNames:c,note:m}}function Tp(t,e,n,r){if(/^\d{4}-\d{2}-\d{2}$/.test(t))return `${t}T00:00:00Z`;if(/^\d{4}-\d{2}-\d{2}T/.test(t))return t;n.push(`Line ${e}: unrecognised ${r} date format "${t}" \u2014 skipped`);}function Sp(t){let e=0;for(let n of t)if(n===" ")e++;else break;return e}var At=class{adapter;constructor(e){this.adapter=e.adapter;}async exportOpml(e){let n=await this.resolveProjects(e),r=await Promise.all(n.map(s=>this.adapter.listTasks({projectId:s.id}).then(c=>({project:s,tasks:c})))),o=r.map(({project:s,tasks:c})=>yp(s,c," ")).join(`
133
219
  `),a=r.reduce((s,{tasks:c})=>s+c.length,0);return {opml:['<?xml version="1.0" encoding="UTF-8"?>','<opml version="2.0">'," <head>"," <title>OmniFocus Export</title>"," </head>"," <body>",o," </body>","</opml>"].join(`
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,`
220
+ `),projectCount:n.length,taskCount:a}}async resolveProjects(e){if(e.kind==="all")return this.adapter.listProjects({status:"active"});if(e.kind==="folder"){let r=await this.adapter.listProjects({folderId:e.id});if(r.length===0)try{await this.adapter.getFolder(e.id);}catch{throw new P(`Folder not found: ${String(e.id)}`,{details:{resource:"folder",id:String(e.id)}})}return r}return [await this.adapter.getProject(e.id)]}async exportTaskPaper(e){let n=await this.resolveProjects(e),r=[],o=[],a=0;for(let{project:i,tasks:s}of await Promise.all(n.map(c=>Gn(this.adapter,c.id).then(d=>({project:c,tasks:d}))))){let{rootTasks:c,byParent:d}=Rt(s);if(o.push(`${i.name}:`),i.note)for(let l of i.note.split(`
221
+ `))o.push(` ${l}`);for(let l of c)dn(l,d,1,o,r);a+=s.length,o.push("");}return {taskpaper:o.join(`
222
+ `),projectCount:n.length,taskCount:a,warnings:r}}async importTaskPaper(e,n){if(!e.trim())throw new y("text is empty",{suggestion:"Provide non-empty TaskPaper text."});let r=await this.adapter.listTags(),o=new Map(r.map(g=>[g.name.toLowerCase(),g.id])),a=async g=>{let v=g.toLowerCase(),I=o.get(v);if(I)return I;let T=await this.adapter.createTag({name:g});return o.set(v,T),T},i=await this.adapter.listProjects(),s=new Map(i.map(g=>[g.name.toLowerCase(),g.id])),c=[],d=[],l=[],m=n,f=e.split(`
223
+ `);for(let g=0;g<f.length;g++){let v=f[g];if(!v)continue;let I=Sp(v),T=v.trimStart();if(!T.startsWith("- ")&&!T.startsWith("- ")&&T.endsWith(":")&&I===0){if(!n){let U=T.slice(0,-1).trim(),M=s.get(U.toLowerCase());M?m=M:(d.push(`Project "${U}" not found in OmniFocus \u2014 tasks will land in inbox`),m=void 0);}l.length=0;continue}if(!T.startsWith("- ")&&!T.startsWith("- "))continue;for(;l.length>0&&(l[l.length-1]?.depth??0)>=I;)l.pop();let E=T.slice(2).trim(),w=wp(E,g+1,d),D=[];for(let U of w.tagNames)try{D.push(await a(U));}catch{d.push(`Line ${g+1}: could not create tag "${U}" \u2014 skipped`);}let j=l[l.length-1],B={name:w.name,...j?{parentId:j.id}:{},...m&&!j?{projectId:m}:{},...w.dueDate?{dueDate:w.dueDate}:{},...w.deferDate?{deferDate:w.deferDate}:{},...w.flagged?{flagged:true}:{},...w.note?{note:w.note}:{},...D.length>0?{tagIds:D}:{}},_=await this.adapter.createTask(B);c.push(_),w.done&&await this.adapter.completeTask(_),l.push({depth:I,id:_});}return {created:c,warnings:d}}async importOpml(e,n={}){if(!e.trim())throw new y("opml is empty",{suggestion:"Provide a non-empty OPML XML string."});let r=vp(e),o=await this.adapter.listProjects(),a=new Map(o.map(d=>[String(d.id),d.id])),i=new Map(o.map(d=>[d.name.toLowerCase(),d.id])),s=[],c=async(d,l,m)=>{for(let f of d){let g={name:f.text||"(untitled)",...m!==void 0?{parentId:m}:l!==void 0?{projectId:l}:{},...f.due!==void 0?{dueDate:f.due}:{},...f.defer!==void 0?{deferDate:f.defer}:{},...f.flagged===true?{flagged:true}:{}},v=await this.adapter.createTask(g);s.push(v),f.children.length>0&&await c(f.children,l,v);}};for(let d of r.body){if(n.destinationProjectId!==void 0){await c([d],n.destinationProjectId,void 0);continue}if(d.type==="omnifocus:project"){let l=(d.id!==void 0?a.get(d.id):void 0)??i.get(d.text.toLowerCase());await c(d.children,l,void 0);}else await c([d],void 0,void 0);}return {imported:s.length,taskIds:s}}};var Ua='Spawn a new project from a saved template under the Templates folder. Substitutes {{name}} placeholders with the supplied parameters and shifts @due / @defer dates relative to the optional dueDate anchor (the earliest @due in the template). Do NOT use to copy a one-off project \u2014 prefer task_duplicate. Returns { projectId, taskCount, importWarnings }. Side effects: writes a new project + tasks; sets meta.syncPending = true. Example: { templateName: "Client onboarding", parameters: { client: "Acme" }, dueDate: "2026-06-04" }.',dv=z.object({templateName:z.string().min(1).describe("Saved template to instantiate."),parameters:z.record(z.string(),z.string()).default({}).describe("Map of placeholder name \u2192 substitution value."),targetFolderId:W.schema.optional().describe("Folder to create the new project in. Defaults to the library root."),dueDate:z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"must be YYYY-MM-DD").optional().describe("Anchor for relative-date shifting. The earliest @due in the template becomes this date; every other @due/@defer shifts by the same delta.")}),ln=class extends Error{code="TEMPLATE_NOT_FOUND";constructor(e){super(`No template named "${e}" was found in the Templates folder.`),this.name="TemplateNotFoundError";}},Fa=class extends Error{code="MISSING_TEMPLATE_PARAMETER";missing;constructor(e){super(`Template requires parameters not supplied: ${e.map(n=>`"${n}"`).join(", ")}.`),this.name="MissingTemplateParameterError",this.missing=e;}};async function lv(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(v=>v.name.toLowerCase()===r);if(o===void 0)throw new ln(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),i=t.templateName.toLowerCase(),s=a.find(v=>v.name.toLowerCase()===i);if(s===void 0)throw new ln(t.templateName);let c=Dt(s.note);if(c===void 0)throw new ln(t.templateName);let d=c.parameterNames.filter(v=>!Object.hasOwn(t.parameters,v));if(d.length>0)throw new Fa(d);let l=up(s.note);if(l=mp(l,t.parameters),t.dueDate!==void 0){let v=fp(l);v!==void 0&&(l=gp(l,v,t.dueDate));}let m=await e.adapter.createProject({name:t.templateName,...t.targetFolderId!==void 0&&{folderId:t.targetFolderId}}),g=await new At({adapter:e.adapter}).importTaskPaper(l,m);return e.cache!==void 0&&J(e.cache,{projectId:m}),p({projectId:m,taskCount:g.created.length,...g.warnings.length>0&&{importWarnings:g.warnings}},e.makeMeta({syncPending:true}))}function bp(t,e){return t.registerTool("project_template_instantiate",{description:Ua,inputSchema:dv.shape},async n=>{let r=await lv(n,e);return u(r)})}var La="List saved project templates under the Templates folder. Projects without a parseable template fence are skipped. Do NOT use to enumerate ordinary projects \u2014 call project_list. Returns { templates: [{ templateId, templateName, parameterNames, capturedAt }] }, sorted by capturedAt desc. Read-only; safe to retry. Example: call with no args; receives [] when no Templates folder exists yet.",uv=z.object({});async function mv(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(s=>s.name.toLowerCase()===r);if(o===void 0)return p({templates:[]},e.makeMeta());let a=await e.adapter.listProjects({folderId:o.id}),i=[];for(let s of a){let c=Dt(s.note);c!==void 0&&i.push({templateId:s.id,templateName:c.name,parameterNames:c.parameterNames,capturedAt:c.capturedAt});}return i.sort((s,c)=>s.capturedAt!==c.capturedAt?s.capturedAt<c.capturedAt?1:-1:s.templateName<c.templateName?-1:s.templateName>c.templateName?1:0),p({templates:i},e.makeMeta())}function jp(t,e){return t.registerTool("project_template_list",{description:La,inputSchema:uv.shape},async n=>{let r=await mv(n,e);return u(r)})}var Ja='Capture a project as a reusable template under the Templates folder (env OMNIFOCUS_TEMPLATES_FOLDER_NAME). Metadata is stored in a fenced YAML block at the top of the template-project note; TaskPaper body sits below. Do NOT use to duplicate a one-off project \u2014 prefer task_duplicate. Returns { templateId, templateName, capturedAt }. Side effects: writes folder + project; sets meta.syncPending = true. Example: { projectId: "p_001", templateName: "Client onboarding", parameterNames: ["client"] }.',fv=z.object({projectId:k.schema.describe("Source project to capture."),templateName:z.string().min(1).describe("Display name; must be unique within the Templates folder."),parameterNames:z.array(z.string().min(1)).optional().describe("Optional placeholder names for future _instantiate substitution.")}),Ba=class extends Error{code="TEMPLATE_EXISTS";constructor(e){super(`A template named "${e}" already exists in the Templates folder.`),this.name="TemplateExistsError";}};async function gv(t,e){let n=await t.listFolders(),r=e.toLowerCase(),o=n.find(a=>a.name.toLowerCase()===r);return o!==void 0?o.id:t.createFolder({name:e})}async function hv(t,e){await e.adapter.getProject(t.projectId);let n=await gv(e.adapter,e.templatesFolderName),r=await e.adapter.listProjects({folderId:n}),o=t.templateName.toLowerCase();if(r.some(I=>I.name.toLowerCase()===o))throw new Ba(t.templateName);let a=await e.adapter.getProject(t.projectId),i=await Gn(e.adapter,t.projectId),{rootTasks:s,byParent:c}=Rt(i),d=[],l=[`${a.name}:`];if(a.note)for(let I of a.note.split(`
224
+ `))l.push(` ${I}`);for(let I of s)dn(I,c,1,l,d);let m=l.join(`
225
+ `),f={name:t.templateName,parameterNames:t.parameterNames??[],capturedAt:new Date().toISOString()},g=pp(f,m),v=await e.adapter.createProject({name:t.templateName,folderId:n,note:g});return e.cache!==void 0&&J(e.cache,{projectId:v}),p({templateId:v,templateName:t.templateName,capturedAt:f.capturedAt,...d.length>0&&{exportWarnings:d}},e.makeMeta({syncPending:true}))}function _p(t,e){return t.registerTool("project_template_save",{description:Ja,inputSchema:fv.shape},async n=>{let r=await hv(n,e);return u(r)})}var $a='Partially update mutable fields on an OmniFocus project. Only supplied fields are changed; omit a field to leave it unchanged. Pass null for note, deferDate, dueDate, estimatedMinutes, or reviewIntervalDays to clear those fields. Do NOT use to create or delete projects; prefer project_create or project_delete instead. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent project_get) to reject the call if the project changed since you read it; pass idempotency_key to coalesce retries so the same update is only performed once. Returns { updated: true, id, name } \u2014 name reflects the post-patch name. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: project_update({ id: "prj123", name: "New Name", flagged: true }) Example: project_update({ id: "prj123", status: "on-hold", dry_run: true })',Wa=z.object({id:k.schema.describe("Persistent project ID. Get from project_list or project_get."),name:z.string().min(1).optional().describe("New project name. Must be non-empty if supplied."),note:z.string().nullable().optional().describe("Plain-text note. Pass null to clear."),noteHtml:z.string().nullable().optional().describe("HTML note. Pass null to clear. Prefer note for plain-text edits."),status:ie(["active","on-hold"],{paused:"on-hold"},"Project status. Use project_complete or project_drop to close a project.").optional(),completionCriterion:ie(["parallel","sequential","singleActions"],{"in-order":"sequential","in order":"sequential","any-order":"parallel","any order":"parallel"},"How the project's tasks are completed.").optional(),deferDate:z.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated total duration in minutes. Pass null to clear."),flagged:z.boolean().optional().describe("Flag or unflag the project."),tagIds:z.array(S.schema).optional().describe("Full-replacement tag list. Replaces all existing tags."),reviewIntervalDays:z.number().int().positive().nullable().optional().describe("Review interval in days. Pass null to clear."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent project_get. If the project's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe updates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying the patch.")});async function yv(t,e){let{id:n,...r}=t,o=e.idempotencyStore??de;return ce(o,t.idempotency_key,async()=>{let a=await e.adapter.getProject(n);Ye(t.expectedModifiedAt,a.modifiedAt,`project:${n}`);let i={...r.name!==void 0&&{name:r.name},...r.note!==void 0&&{note:r.note},...r.noteHtml!==void 0&&{noteHtml:r.noteHtml},...r.status!==void 0&&{status:r.status},...r.completionCriterion!==void 0&&{completionCriterion:r.completionCriterion},...r.deferDate!==void 0&&{deferDate:r.deferDate},...r.deferDateFloating!==void 0&&{deferDateFloating:r.deferDateFloating},...r.dueDate!==void 0&&{dueDate:r.dueDate},...r.dueDateFloating!==void 0&&{dueDateFloating:r.dueDateFloating},...r.estimatedMinutes!==void 0&&{estimatedMinutes:r.estimatedMinutes},...r.flagged!==void 0&&{flagged:r.flagged},...r.tagIds!==void 0&&{tagIds:r.tagIds},...r.reviewIntervalDays!==void 0&&{reviewIntervalDays:r.reviewIntervalDays}},s=r.name??a.name,c=()=>p({updated:true,id:n,name:s},e.makeMeta({syncPending:false})),d=async()=>(await e.adapter.updateProject(n,i),e.cache!==void 0&&J(e.cache,{projectId:n}),p({updated:true,id:n,name:s},e.makeMeta({syncPending:true,humanReadableSummary:Nd(a.name)})));return Ce(t.dry_run,c,d)})}function Pp(t,e){return t.registerTool("project_update",{description:$a,inputSchema:Wa.shape},async n=>{let r=await yv(n,e);return u(r)})}var Ha="Preview what project_update would do without making any changes. Do NOT use to actually update a project \u2014 use project_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function kv(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getProject(t.id);if(o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`)),t.status!==void 0&&(n.push({field:"status",newValue:t.status,oldValue:i.status}),r.push(`set status to '${t.status}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate,oldValue:i.dueDate??null}),r.push(t.dueDate===null?"clear due date":`set due date to ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate,oldValue:i.deferDate??null}),r.push(t.deferDate===null?"clear defer date":`set defer date to ${Te(t.deferDate)}`)),t.flagged!==void 0&&(n.push({field:"flagged",newValue:String(t.flagged),oldValue:String(i.flagged)}),r.push(t.flagged?"flag":"unflag")),t.tagIds!==void 0){let s=await Promise.all(t.tagIds.map(c=>he(e.adapter,c)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`set tags to [${s.map(c=>`'${c}'`).join(", ")}]`);}t.note!==void 0&&(n.push({field:"note",newValue:t.note===null?null:t.note.slice(0,50)}),r.push(t.note===null?"clear note":"update note"));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update project '${o}': ${r.join(", ")}.`:`Would update project '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function Op(t,e){return t.registerTool("project_update_describe",{description:Ha,inputSchema:Wa.shape},async n=>{let r=await kv(n,e);return u(r)})}var Ga="\u26A0 DANGEROUS \u2014 raw JXA escape hatch. Executes an arbitrary JavaScript-for-Automation script against OmniFocus with FULL Automation privileges (read, write, delete, move, sync). Only available when the server was started with OMNIFOCUS_ALLOW_RAW_SCRIPT=1. Every call is audit-logged with the full script body. Do NOT use this for operations covered by the typed tools (task_*, project_*, tag_*, folder_*, etc.) \u2014 typed tools are safer, idempotent, and return structured results. Use ONLY when you need a feature no typed tool exposes AND you control the environment. `script` must be a JXA program that defines `function run(argv)` and returns a JSON-encoded string. `arg` is an optional JSON-serialisable value passed as argv[0] (defaults to `{}`). Returns { result } where result is the parsed JSON output of the script (arbitrary shape). Side effects: may mutate, delete, or exfiltrate any OmniFocus data the user has access to. Use sync_trigger separately if the script mutated data and you need it to propagate. Example: run_jxa_script({ script: \"function run(argv) { return JSON.stringify(Application('OmniFocus').version()); }\" })",Iv=z.object({script:z.string().min(1).describe("Raw JXA script body. Must define `function run(argv)` and return a JSON-encoded string."),arg:z.unknown().optional().describe("Optional JSON-serialisable argument passed to `run()` as argv[0]. Defaults to `{}`.")});async function vv(t,e){if(typeof e.adapter.runJxaScript!="function")throw new y("run_jxa_script is not available in this adapter configuration",{details:{reason:"raw-script-unavailable"},suggestion:"Start the server with OMNIFOCUS_ALLOW_RAW_SCRIPT=1 to enable raw-script tools."});(e.logger??R).info({event:"raw_script.invoked",tool:"run_jxa_script",scriptLength:t.script.length,script:t.script},"raw JXA script invoked");let r=await e.adapter.runJxaScript(t.script,t.arg);return p({result:r},e.makeMeta({syncPending:true,humanReadableSummary:"Ran JXA script."}))}function xp(t,e,n){return n.allowRawScript?t.registerTool("run_jxa_script",{description:Ga,inputSchema:Iv.shape},async r=>{let o=await vv(r,e);return u(o)}):null}var Va='\u26A0 DANGEROUS \u2014 raw OmniJS escape hatch. Executes an arbitrary Omni Automation (OmniJS) script against OmniFocus with FULL Automation privileges (read, write, delete, move, sync, plug-in APIs). Only available when the server was started with OMNIFOCUS_ALLOW_RAW_SCRIPT=1. Every call is audit-logged with the full script body. Do NOT use this for operations covered by the typed tools (task_*, project_*, plugin_invoke, etc.) \u2014 typed tools are safer, idempotent, and return structured results. Use ONLY when you need a feature no typed tool exposes AND you control the environment. `script` is a raw OmniJS program; the serialised result must be JSON-encodable. `arg` is an optional JSON-serialisable value forwarded through the callback-file bridge (defaults to `{}`). Returns { result } where result is the parsed JSON output of the script (arbitrary shape). Side effects: may mutate, delete, or exfiltrate any OmniFocus data the user has access to. Use sync_trigger separately if the script mutated data and you need it to propagate. Example: run_omnijs_script({ script: "flattenedProjects.length" })',Tv=z.object({script:z.string().min(1).describe("Raw OmniJS script body. Must produce a JSON-encodable result."),arg:z.unknown().optional().describe("Optional JSON-serialisable argument forwarded through the callback-file bridge. Defaults to `{}`.")});async function wv(t,e){if(typeof e.adapter.runOmniJsScript!="function")throw new y("run_omnijs_script is not available in this adapter configuration",{details:{reason:"raw-script-unavailable"},suggestion:"Start the server with OMNIFOCUS_ALLOW_RAW_SCRIPT=1 to enable raw-script tools."});(e.logger??R).info({event:"raw_script.invoked",tool:"run_omnijs_script",scriptLength:t.script.length,script:t.script},"raw OmniJS script invoked");let r=await e.adapter.runOmniJsScript(t.script,t.arg);return p({result:r},e.makeMeta({syncPending:true,humanReadableSummary:"Ran OmniJS script."}))}function Dp(t,e,n){return n.allowRawScript?t.registerTool("run_omnijs_script",{description:Va,inputSchema:Tv.shape},async r=>{let o=await wv(r,e);return u(o)}):null}var Sv=[{aliases:["sunday","sundays","sun"],weekday:"sunday"},{aliases:["monday","mondays","mon"],weekday:"monday"},{aliases:["tuesday","tuesdays","tue","tues"],weekday:"tuesday"},{aliases:["wednesday","wednesdays","wed"],weekday:"wednesday"},{aliases:["thursday","thursdays","thu","thurs"],weekday:"thursday"},{aliases:["friday","fridays","fri"],weekday:"friday"},{aliases:["saturday","saturdays","sat"],weekday:"saturday"}],Vn=new Map(Sv.flatMap(({aliases:t,weekday:e})=>t.map(n=>[n,e]))),bv=new Map([["a",1],["an",1],["one",1],["two",2],["three",3],["four",4],["five",5],["six",6],["seven",7],["eight",8],["nine",9],["ten",10],["eleven",11],["twelve",12]]),jv=new Map([["first",1],["1st",1],["second",2],["2nd",2],["third",3],["3rd",3],["fourth",4],["4th",4],["last","last"],["final","last"]]);function _v(t){return t.toLowerCase().replace(/\s+/g," ").trim()}function Xa(t){if(!t)return null;let e=Number.parseInt(t,10);if(Number.isFinite(e)&&e>=1)return e;let n=bv.get(t);return n!==void 0?n:null}var Rp=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];function Ap(t){return [...t].sort((e,n)=>Rp.indexOf(e)-Rp.indexOf(n))}function Pv(t){if(t.length===0)return "";if(t.length===7)return "every day";let e=Ap(t),n=new Set(e),r=new Set(["monday","tuesday","wednesday","thursday","friday"]);if(n.size===5&&[...r].every(i=>n.has(i)))return "every weekday";if(n.size===2&&n.has("saturday")&&n.has("sunday"))return "every weekend";let o=i=>i.charAt(0).toUpperCase()+i.slice(1),a=e.map(o);return a.length===1?`every ${a[0]}`:a.length===2?`every ${a[0]} and ${a[1]}`:`every ${a.slice(0,-1).join(", ")}, and ${a[a.length-1]}`}function Ov(t){return t==="last"?"last":["first","second","third","fourth"][t-1]??String(t)}function xv(t){return t==="start-again"?"after I complete it":t==="due-again"?"from the due date":""}function Ka(t,e){let n;if(t.weekdays&&t.weekdays.length>0)n=Pv(t.weekdays),t.steps!==1&&(n=`${n} every ${t.steps} weeks`);else if(t.monthlyAnchor){if("day"in t.monthlyAnchor)n=`the ${t.monthlyAnchor.day} of every month`;else {let a=t.monthlyAnchor,i=s=>s.charAt(0).toUpperCase()+s.slice(1);n=`the ${Ov(a.position)} ${i(a.weekday)} of every month`;}t.steps!==1&&(n=`${n.replace("every month",`every ${t.steps} months`)}`);}else {let a=t.steps===1?t.unit.replace(/s$/,""):t.unit;n=t.steps===1?`every ${a}`:`every ${t.steps} ${a}`;}let r=xv(t.method);return [n,e,r].filter(a=>a.length>0).join(", ")}function Dv(t){let e=[{re:/\bafter i complete (it|the task)?\b/,method:"start-again"},{re:/\bafter completion\b/,method:"start-again"},{re:/\bfrom completion\b/,method:"start-again"},{re:/\bonce completed\b/,method:"start-again"},{re:/\bfrom the due date\b/,method:"due-again"},{re:/\bfrom (its|the) due date\b/,method:"due-again"}];for(let{re:n,method:r}of e){let o=t.match(n);if(o)return {method:r,consumed:o[0]}}return {method:"fixed",consumed:""}}function Rv(t){let e=t.match(/\bat (\d{1,2})(?::(\d{2}))?\s*(am|pm)?\b/);if(!e)return "";let n=e[1],r=e[2]??"00",o=e[3]??"";return `at ${n}:${r}${o}`.replace(":00am","am").replace(":00pm","pm").trim()}function Av(t){let e=t.match(/\bfor (\d+|a|an|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+(day|week|month|year)s?\b/);if(e){let o=Xa(e[1]),a=e[2];if(o!==null)return `for ${o} ${a}${o===1?"":"s"}`}let n=t.match(/\buntil (\d{4}-\d{2}-\d{2}|\w+ \d{1,2}(?:,? \d{4})?)\b/);if(n)return `until ${n[1]}`;let r=t.match(/\b(\d+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+times\b/);if(r){let o=Xa(r[1]);if(o!==null)return `${o} times`}return ""}function Cv(t){if(/\bdaily\b/.test(t))return {rule:{unit:"days",steps:1}};if(/\bweekly\b/.test(t))return {rule:{unit:"weeks",steps:1}};if(/\bmonthly\b/.test(t))return {rule:{unit:"months",steps:1}};if(/\b(yearly|annually)\b/.test(t))return {rule:{unit:"years",steps:1}};if(/\b(biweekly|fortnightly)\b/.test(t))return {rule:{unit:"weeks",steps:2}};if(/\bbimonthly\b/.test(t))return {rule:{unit:"months",steps:2}};if(/\bevery weekday\b/.test(t))return {rule:{unit:"weeks",steps:1,weekdays:["monday","tuesday","wednesday","thursday","friday"]}};if(/\bevery weekend\b/.test(t))return {rule:{unit:"weeks",steps:1,weekdays:["saturday","sunday"]}};let e=t.match(/\bthe (first|second|third|fourth|last|final|1st|2nd|3rd|4th)\s+([a-z]+)\s+of (every|each)?\s*month\b/);if(e){let i=jv.get(e[1]??""),s=Vn.get(e[2]??"");if(i!==void 0&&s!==void 0)return {rule:{unit:"months",steps:1,monthlyAnchor:{weekday:s,position:i}}}}let n=t.match(/\bthe (\d{1,2})(?:st|nd|rd|th)?\s+of (every|each)?\s*month\b/);if(n){let i=Number.parseInt(n[1]??"",10);if(Number.isFinite(i)&&i>=1&&i<=31)return {rule:{unit:"months",steps:1,monthlyAnchor:{day:i}}}}let r=t.match(/\bevery (\d+|a|an|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+(minute|hour|day|week|month|year)s?\b/);if(r){let i=Xa(r[1]),s=`${r[2]}s`;if(i!==null&&i>=1)return {rule:{unit:s,steps:i}}}let o=t.match(/\bevery other\s+(minute|hour|day|week|month|year)\b/);if(o)return {rule:{unit:`${o[1]}s`,steps:2}};if(/\bevery other\s+([a-z]+)\b/.test(t)){let i=t.match(/\bevery other\s+([a-z]+)\b/),s=i&&Vn.get(i[1]??"");if(s)return {rule:{unit:"weeks",steps:2,weekdays:[s]}}}let a=t.match(/\bevery\s+([a-z, ]+?)(?=$|\s+(at|after|from|once|until|for))/);if(a){let s=(a[1]??"").replace(/\band\b/g,",").split(/[, ]+/).map(d=>d.trim()).filter(d=>d.length>0),c=[];for(let d of s){let l=Vn.get(d);l&&c.push(l);}if(c.length>0)return {rule:{unit:"weeks",steps:1,weekdays:Array.from(new Set(c))}}}return null}function Cp(t){if(!t?.trim())return {kind:"error",reason:"no-repetition-detected"};let e=_v(t),n=Dv(e),r=n.consumed?e.replace(n.consumed," ").replace(/\s+/g," ").trim():e,o=Rv(r),a=Av(r),i=Cv(r);if(!i)return {kind:"error",reason:"no-repetition-detected",suggestion:"Try a phrase like 'every Monday', 'weekly', 'every 3 days', or 'monthly'."};let s={method:n.method,unit:i.rule.unit,steps:i.rule.steps,...i.rule.weekdays?{weekdays:Ap(i.rule.weekdays)}:{},...i.rule.monthlyAnchor?{monthlyAnchor:i.rule.monthlyAnchor}:{}},c=r.match(/\bevery other\s+([a-z]+)\b/);if(c){let l=Vn.get(c[1]??"");if(l){let m=[o,a].filter(Boolean).join(", ");return {kind:"ambiguous",interpretations:[{rule:s,description:Ka(s,m)},{rule:{method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:l,position:1}},description:Ka({method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:l,position:1}},m)}]}}}let d=[o,a].filter(Boolean).join(", ");return {kind:"ok",rule:s,normalizedDescription:Ka(s,d)}}var Ya=`Deterministic prose-to-RepetitionRule helper. Pass a natural-language phrase ('every Monday', 'every 3 days', 'first Tuesday of every month') and receive a structured RepetitionRule plus a normalized description to confirm with the user. Returns one of three shapes: { kind: 'ok', rule, normalizedDescription } when the prose maps to one rule; { kind: 'ambiguous', interpretations[] } when prose admits multiple valid readings (typically 2-3) \u2014 agent picks one with the user; { kind: 'error', reason, suggestion? } for no-repetition-detected or unsupported-pattern. Supported patterns: daily/weekly/monthly/yearly, every-N-days/weeks/months/years, every weekday/weekend, every {Mon|Tue|...}, nth-weekday-of-month, nth-day-of-month, completion-relative phrasing ('after I complete it'). Time-of-day and end-conditions surface in normalizedDescription only \u2014 the canonical RepetitionRule schema doesn't carry those fields. Do NOT use this tool when the agent already has a structured RepetitionRule from another source \u2014 call task_set_repetition directly instead. Prefer this helper over ad-hoc LLM translation whenever the user's repetition phrasing is the only signal. No model calls; no side effects. Use with task_set_repetition or task_create. Example: repetition_from_prose({ prose: "every Monday" }) Example: repetition_from_prose({ prose: "every 3 days after I complete it" })`,Ev=z.object({prose:z.string().min(1).describe("Natural-language phrase describing a repetition cadence. Examples: 'every Monday', 'every other Tuesday at 10am', 'first Thursday of every month after I complete it'."),anchor:z.object({dueDate:z.string().optional().describe("Optional ISO-8601 due-date anchor."),deferDate:z.string().optional().describe("Optional ISO-8601 defer-date anchor.")}).optional().describe("Optional date anchor \u2014 currently informational. The grammar reads time-of-day from prose into normalizedDescription; embedding it into a date is the agent's responsibility once it has anchor context.")});async function Mv(t,e){let n=Cp(t.prose),r=e.makeMeta();if(n.kind!=="ambiguous")return p(n,r);let o=e.replayStore??ve,a=n.interpretations.map(s=>s.description),i=o.register(a,async s=>{let c=n.interpretations[s];return p({kind:"ok",rule:c.rule,normalizedDescription:c.description},e.makeMeta())});return St(`"${t.prose}" matches multiple repetition patterns. Which did you mean?`,i,r,a.map((s,c)=>({index:c,label:s})),t.anchor!==void 0?{prose:t.prose,anchor:t.anchor}:{prose:t.prose})}function Ep(t,e){return t.registerTool("repetition_from_prose",{description:Ya,inputSchema:Ev.shape},async n=>{let r=await Mv(n,e);return u(r)})}var Za="List projects due for review in OmniFocus \u2014 those whose next review date is today or earlier, or has never been set. Sorted by next review date ascending (overdue first, never-reviewed first). Do not use to get all projects; prefer project_list for that. Returns each project's id, name, nextReviewDate, lastReviewDate, and reviewIntervalDays. Safe to call repeatedly; no side effects, no writes. Example: review_list_due()",Fv=z.object({});async function Uv(t,e){let n=await e.reviewService.listDue(),r=e.makeMeta({cacheHit:n.cacheHit});return p({projects:n.projects},r)}function Mp(t,e){return t.registerTool("review_list_due",{description:Za,inputSchema:Fv.shape},async n=>{let r=await Uv(n,e);return u(r)})}var Qa=`Mark a project as reviewed in OmniFocus \u2014 sets lastReviewDate to now and advances nextReviewDate by the project's review interval. Use this after completing a weekly review of a project. Do not use to change the review interval; prefer review_set_interval for that. Returns { id, name, lastReviewDate, nextReviewDate } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted between write and read), and the dates echo back the new schedule so the agent can describe the result without a follow-up read. Side effects: writes to OmniFocus; sets syncPending = true. Example: review_mark_reviewed({ id: "prj123" })`,Lv=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function Bv(t,e){let n=await e.reviewService.markReviewed(k.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:Ln()}))}function Fp(t,e){return t.registerTool("review_mark_reviewed",{description:Qa,inputSchema:Lv.shape},async n=>{let r=await Bv(n,e);return u(r)})}var es=`Convenience alias for review_mark_reviewed \u2014 mark a single project as reviewed, setting lastReviewDate to now and advancing nextReviewDate. Use when you have a project id and want a single-call review operation. Do not use to list projects due for review; prefer review_list_due for that. Returns { id, name, lastReviewDate, nextReviewDate } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted between write and read), and the dates echo back the new schedule so the agent can describe the result without a follow-up read. Side effects: writes to OmniFocus; sets syncPending = true. Example: project_mark_reviewed({ id: "prj123" })`,Jv=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function $v(t,e){let n=await e.reviewService.markReviewed(k.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:Ln()}))}function Lp(t,e){return t.registerTool("project_mark_reviewed",{description:es,inputSchema:Jv.shape},async n=>{let r=await $v(n,e);return u(r)})}var ns=`Set a project's review interval in OmniFocus \u2014 updates how many days between reviews. Use null to remove the recurring schedule. Do not use to mark a project as reviewed; prefer review_mark_reviewed for that. Returns { id, name, reviewIntervalDays } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted), and reviewIntervalDays echoes back the new value (or null when cleared) so the agent can describe the change without a follow-up read. Side effects: writes to OmniFocus; sets syncPending = true. Example: review_set_interval({ id: "prj123", days: 7 }) Example: review_set_interval({ id: "prj123", days: null })`,Wv=z.object({id:z.string().min(1).describe("Persistent ID of the project to update."),days:z.number().int().min(1).nullable().describe("Review interval in days. Pass null to remove the recurring review schedule.")});async function Hv(t,e){let n=await e.reviewService.setInterval(k.of(t.id),t.days);return p({id:t.id,name:n.name,reviewIntervalDays:n.reviewIntervalDays},e.makeMeta({syncPending:true,humanReadableSummary:Yd(t.days)}))}function Bp(t,e){return t.registerTool("review_set_interval",{description:ns,inputSchema:Wv.shape},async n=>{let r=await Hv(n,e);return u(r)})}var rs=`Set or clear a project's next review date directly. Use when the user wants to reschedule a review independent of the recurring interval \u2014 'push the Q3 review to next Monday' without changing the cadence. Do NOT use to mark a project as reviewed (prefer review_mark_reviewed) or to change the recurring interval (prefer review_set_interval). Pass projectId and nextReviewDate (ISO-8601 date string), or pass null for nextReviewDate to clear (project becomes 'not scheduled'). Past-dated values are accepted and surface the project as overdue immediately \u2014 matches OmniFocus's own UX. Returns { id, name, nextReviewDate } \u2014 name is the project's display name (post-mutation lookup; null if the project has been deleted), and nextReviewDate echoes back the new value (or null when cleared) so the agent can describe the change without a follow-up read. Errors: NOT_FOUND when projectId does not exist. Side effects: writes to OmniFocus; invalidates project + review caches; sets syncPending = true. Example: project_set_next_review_date({ projectId: "prj123", nextReviewDate: "2026-05-05" }) Example: project_set_next_review_date({ projectId: "prj123", nextReviewDate: null })`,zv=z.object({projectId:z.string().min(1).describe("Persistent ID of the project whose next review date should change."),nextReviewDate:z.union([oe(),z.null()]).describe("Next review date as ISO-8601 (with offset). Pass null to clear the schedule. Past-dated values are accepted and mark the project as overdue immediately.")});async function Gv(t,e){let n=await e.reviewService.setNextReviewDate(k.of(t.projectId),t.nextReviewDate);return p({id:t.projectId,name:n.name,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:Zd(t.nextReviewDate)}))}function Jp(t,e){return t.registerTool("project_set_next_review_date",{description:rs,inputSchema:zv.shape},async n=>{let r=await Gv(n,e);return u(r)})}var os=`Full-text search across OmniFocus task names and/or notes. Use for finding tasks by content when you don't know the ID. Supports optional filters (project, tags, flagged, completion status) and cursor pagination. Do NOT use when a known task ID is available (use task_get instead). Returns tasks[] with pagination; safe to call repeatedly; no side effects. Example: search_query({ q: "dentist" }) Example: search_query({ q: "report", projectId: "prj123", completed: "exclude" })`,qv=z.object({q:z.string().describe("Search query. Case-insensitive substring match. Empty string matches all tasks (useful with filters)."),scope:z.enum(["name","note","all"]).optional().describe("'name' = search task names only; 'note' = search notes only; 'all' = both. Default 'all'."),projectId:k.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list."),tagIds:z.array(S.schema).optional().describe("Restrict to tasks carrying ALL of these tags. Get IDs from tag_list."),flagged:z.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed only; 'any' = both. Default 'any'."),limit:z.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 100."),cursor:z.string().optional().describe("Opaque cursor from a previous search_query response. Must use identical filters \u2014 changing filters returns a ValidationError.")});async function Vv(t,e){let n={q:t.q,...t.scope!==void 0?{scope:t.scope}:{},...t.projectId!==void 0?{projectId:t.projectId}:{},...t.tagIds!==void 0?{tagIds:t.tagIds}:{},...t.flagged!==void 0?{flagged:t.flagged}:{},...t.completed!==void 0?{completed:t.completed}:{},...t.limit!==void 0?{limit:t.limit}:{},...t.cursor!==void 0?{cursor:t.cursor}:{}},r=await e.searchService.search(n),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return p({tasks:r.tasks},a,o)}function $p(t,e){return t.registerTool("search_query",{description:os,inputSchema:qv.shape},async n=>{let r=await Vv(n,e);return u(r)})}var as="Return the last OmniFocus sync state without triggering a new sync. Do NOT call this to initiate a sync \u2014 use sync_trigger instead. Use to check whether a previous sync completed before querying cross-device data. Returns { lastSyncAt, inFlight }. lastSyncAt is null if OmniFocus has never synced in this session. Read-only; no side effects. Example: sync_status()",Xv=z.object({});async function Yv(t,e){let n=await e.adapter.getLastSync();return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},e.makeMeta())}function Wp(t,e){return t.registerTool("sync_status",{description:as,inputSchema:Xv.shape},async n=>{let r=await Yv(n,e);return u(r)})}var ss="Kick off an OmniFocus sync with Omni Sync Server. Do not call when no mutations have been made; prefer checking meta.syncPending first. Call this after any sequence of mutations (task_create, task_update, folder_create, etc.) when you need changes to appear on other devices. The sync starts immediately but completes asynchronously \u2014 this tool does not block until done. Returns meta.syncPending = false to confirm the sync was initiated. Side effects: triggers a sync request to Omni Sync Server. Example: sync_trigger()",Qv=z.object({});async function eT(t,e){let n=await e.adapter.syncTrigger();e.cache!==void 0&&sd(e.cache);let r=e.makeMeta({syncPending:false});return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},r)}function Hp(t,e){return t.registerTool("sync_trigger",{description:ss,inputSchema:Qv.shape},async n=>{let r=await eT(n,e);return u(r)})}var cs=`Create a new tag in OmniFocus. Optionally nest it under an existing parent tag (get IDs from tag_list). Do not use to move an existing tag; prefer tag_move instead. Returns the new tag's persistent ID. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_create({ name: "errands" }) Example: tag_create({ name: "home", parentId: "tag123" })`,ds=z.object({name:z.string().min(1).describe("Tag name. Must be non-empty."),parentId:S.schema.optional().describe("Parent tag ID to nest under. Omit for a root tag. Get from tag_list."),status:ie(["active","on-hold"],{paused:"on-hold"},"Initial status. Defaults to 'active'. Cannot create a tag in 'dropped' state.").optional(),allowsNextAction:z.boolean().optional().describe("Whether the tag allows next-action selection. Defaults to true.")});async function tT(t,e){let n=await e.tagService.create({name:t.name,...t.parentId!==void 0?{parentId:t.parentId}:{},...t.status!==void 0?{status:t.status}:{},...t.allowsNextAction!==void 0?{allowsNextAction:t.allowsNextAction}:{}}),{tag:r}=await e.tagService.get(n.id),o=e.makeMeta({syncPending:true,humanReadableSummary:Bd(t.name)});return p({tag:r},o)}function zp(t,e){return t.registerTool("tag_create",{description:cs,inputSchema:ds.shape},async n=>{let r=await tT(n,e);return u(r)})}var ls="Preview what tag_create would do without making any changes. Do NOT use to actually create a tag \u2014 use tag_create instead. Returns { description, plannedChanges } describing the tag that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function nT(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await he(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),r.push(`under '${a}'`);}else r.push("at root");t.status!==void 0&&(n.push({field:"status",newValue:t.status}),r.push(`status '${t.status}'`));let o=`Would create tag ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Gp(t,e){return t.registerTool("tag_create_describe",{description:ls,inputSchema:ds.shape},async n=>{let r=await nT(n,e);return u(r)})}var ps=`Hard-delete a tag from OmniFocus. IRREVERSIBLE \u2014 the tag and all its children are removed. Tasks that carried this tag lose it. Get the tag ID from tag_list. Prefer tag_set_status with status='dropped' to preserve history. Returns the deleted tag's ID on success. Side effects: writes to OmniFocus, sets meta.syncPending = true. Example: tag_delete({ id: "tag123" })`,us=z.object({id:S.schema.describe("Persistent tag ID to delete. Get from tag_list.")});async function oT(t,e){return await e.tagService.delete(t.id),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Kd()}))}function qp(t,e){return t.registerTool("tag_delete",{description:ps,inputSchema:us.shape},async n=>{let r=await oT(n,e);return u(r)})}var ms="Preview what tag_delete would do without making any changes. Do NOT use to actually delete a tag \u2014 use tag_delete instead. Returns { description, plannedChanges } describing the permanent deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function aT(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTag(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=`Would permanently delete tag '${r}' (id: ${t.id}) and all its children. IRREVERSIBLE.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Vp(t,e){return t.registerTool("tag_delete_describe",{description:ms,inputSchema:us.shape},async n=>{let r=await aT(n,e);return u(r)})}var fs='Fetch a single tag by its persistent ID, including task count. Do not use to list multiple tags; prefer tag_list instead. Returns tag details; no side effects. Example: tag_get({ id: "tag123" })',iT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list. IDs are stable across renames.")});async function cT(t,e){let n=await e.tagService.get(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({tag:n.tag},r)}function Kp(t,e){return t.registerTool("tag_get",{description:fs,inputSchema:iT.shape},async n=>{let r=await cT(n,e);return u(r)})}var gs='Get the geographic location trigger currently set on a tag, or null if none. Do not use to set or clear a location; prefer tag_set_location instead. Location-based tags are an OmniFocus Pro feature. Get the tag ID from tag_list. Returns { location } with name, radius, and trigger direction, or null if unset. Safe to call repeatedly; no side effects. Example: tag_get_location({ id: "tag123" })',lT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list.")});async function pT(t,e){let n=await e.tagService.getLocation(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({location:n.location},r)}function Xp(t,e){return t.registerTool("tag_get_location",{description:gs,inputSchema:lT.shape},async n=>{let r=await pT(n,e);return u(r)})}var hs='Fetch up to 100 tags by persistent ID in a single OmniFocus round-trip. Use when you have a set of tag IDs and need full tag objects for all of them. Do NOT use for a single ID \u2014 use tag_get instead. Returns Tag[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry. Example: tag_get_many({ ids: ["tag123", "tag456"] })',Xn=100,uT=z.object({ids:z.array(S.schema).min(0).max(Xn).describe(`Array of tag IDs to fetch (0..${Xn}). Get IDs from tag_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function mT(t,e){if(t.ids.length===0)return p({tags:[]},e.makeMeta());if(t.ids.length>Xn)throw new y(`ids array exceeds the maximum batch size of ${Xn} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTagsMany(t.ids),r=n.filter(s=>s!==null),o=t.ids.filter((s,c)=>n[c]===null),a=o.length>0?[wt(o)]:void 0,i=e.makeMeta({...a!==void 0?{warnings:a}:{}});return p({tags:r},i)}function Zp(t,e){return t.registerTool("tag_get_many",{description:hs,inputSchema:uT.shape},async n=>{let r=await mT(n,e);return u(r)})}var ys='List all tags in OmniFocus, optionally filtered by parent tag or status. Do not use to fetch a single tag by ID; prefer tag_get instead. Returns a flat array \u2014 use parentId to walk the hierarchy one level at a time. Safe to call repeatedly; no side effects. Example: tag_list({}) Example: tag_list({ status: "active", parentId: "tag123" })',gT=z.object({parentId:S.schema.optional().describe("Return only direct children of this tag. Get the ID from a previous tag_list call. Omit for root tags."),status:ie(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"Filter by tag status. Omit to return tags of all statuses.").optional()});async function hT(t,e){let n={...t.parentId!==void 0?{parentId:t.parentId}:{},...t.status!==void 0?{status:t.status}:{}},r=await e.tagService.list(n),o=e.makeMeta({cacheHit:r.cacheHit});return p({tags:r.tags},o)}function Qp(t,e){return t.registerTool("tag_list",{description:ys,inputSchema:gT.shape},async n=>{let r=await hT(n,e);return u(r)})}var ks=`Move a tag to a new parent, or promote it to a root tag by passing parentId=null. Do not use to rename a tag; prefer tag_update instead. Get tag IDs from tag_list. Returns the updated tag's ID and new parentId on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_move({ id: "tag123", parentId: "tag456" }) Example: tag_move({ id: "tag123", parentId: null })`,Is=z.object({id:S.schema.describe("Persistent ID of the tag to move. Get from tag_list."),parentId:S.schema.nullable().describe("New parent tag ID, or null to promote the tag to root level.")});async function kT(t,e){await e.tagService.move(t.id,t.parentId);let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Jd(n.name,"root")}))}function eu(t,e){return t.registerTool("tag_move",{description:ks,inputSchema:Is.shape},async n=>{let r=await kT(n,e);return u(r)})}var vs="Preview what tag_move would do without making any changes. Do NOT use to actually move a tag \u2014 use tag_move instead. Returns { description, plannedChanges } describing the reparenting that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function IT(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTag(t.id)).name;}catch{}let o;if(t.parentId!==null){let i=await he(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`under '${i}'`;}else n.push({field:"parentId",newValue:null}),o="root level";let a=`Would move tag '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function tu(t,e){return t.registerTool("tag_move_describe",{description:vs,inputSchema:Is.shape},async n=>{let r=await IT(n,e);return u(r)})}var Ts='Enable or disable next-action selection for a tag in OmniFocus. When true, tasks with this tag are eligible for next-action promotion. Do not use to change other tag properties; prefer tag_update instead. Get the tag ID from tag_list. Returns the updated tag with allowsNextAction confirmed. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_set_allows_next_action({ id: "tag123", allowsNextAction: true })',vT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),allowsNextAction:z.boolean().describe("true to enable next-action selection; false to disable.")});async function TT(t,e){await e.tagService.setAllowsNextAction(t.id,t.allowsNextAction);let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Ve(n.name)}))}function ru(t,e){return t.registerTool("tag_set_allows_next_action",{description:Ts,inputSchema:vT.shape},async n=>{let r=await TT(n,e);return u(r)})}var ws='Set a geographic location trigger on a tag (OmniFocus Pro only). The trigger fires when arriving at, leaving, or both for the specified radius. Do not use to read the current location; prefer tag_get_location instead. Get the tag ID from tag_list. Returns FeatureRequiresPro on OmniFocus Standard installs. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_set_location({ id: "tag123", latitude: 37.785, longitude: -122.407, radiusMeters: 200, trigger: "arriving", name: "Office" })',wT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),latitude:z.number().min(-90).max(90).describe("Latitude in decimal degrees (\u221290 to 90)."),longitude:z.number().min(-180).max(180).describe("Longitude in decimal degrees (\u2212180 to 180)."),radiusMeters:z.number().min(0).describe("Trigger radius in metres. Must be \u2265 0."),trigger:z.enum(["entering","leaving","both"]).describe("When to trigger: 'entering', 'leaving', or 'both'."),name:z.string().nullable().optional().describe("Optional human-readable name for the location (e.g. 'Home', 'Office').")});async function ST(t,e){await e.tagService.setLocation(t.id,{name:t.name??null,latitude:t.latitude,longitude:t.longitude,radiusMeters:t.radiusMeters,trigger:t.trigger});let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Ve(n.name)}))}function ou(t,e){return t.registerTool("tag_set_location",{description:ws,inputSchema:wT.shape},async n=>{let r=await ST(n,e);return u(r)})}var Ss='Set the lifecycle status of a tag to active, on-hold, or dropped. Dropped tags are hidden in OmniFocus but not deleted. Do not use to permanently remove a tag; prefer tag_delete instead. Get the tag ID from tag_list. Returns the updated tag with the confirmed status. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_set_status({ id: "tag123", status: "on-hold" }) Example: tag_set_status({ id: "tag123", status: "active" })',jT=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),status:ie(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status for the tag.")});async function _T(t,e){await e.tagService.setStatus(t.id,t.status);let{tag:n}=await e.tagService.get(t.id);return p({tag:n},e.makeMeta({syncPending:true,humanReadableSummary:Ve(n.name)}))}function au(t,e){return t.registerTool("tag_set_status",{description:Ss,inputSchema:jT.shape},async n=>{let r=await _T(n,e);return u(r)})}var js='Update mutable fields on an existing tag (partial patch). Only supplied fields are changed; omit a field to leave it unchanged. Do not use to move a tag to a different parent; prefer tag_move instead. Get the tag ID from tag_list. Returns the updated tag on success. Triggers a sync; call sync_trigger after to propagate to other devices. Example: tag_update({ id: "tag123", name: "shopping" }) Example: tag_update({ id: "tag123", status: "dropped" })',_s=z.object({id:S.schema.describe("Persistent tag ID. Get from tag_list."),name:z.string().min(1).optional().describe("New tag name. Must be non-empty if supplied."),parentId:S.schema.nullable().optional().describe("New parent tag ID. Pass null to promote to root. Get from tag_list."),status:ie(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status.").optional(),allowsNextAction:z.boolean().optional().describe("Whether the tag allows next-action selection in OmniFocus.")});async function PT(t,e){let{id:n,...r}=t;await e.tagService.update(n,{...r.name!==void 0?{name:r.name}:{},...r.parentId!==void 0?{parentId:r.parentId}:{},...r.status!==void 0?{status:r.status}:{},...r.allowsNextAction!==void 0?{allowsNextAction:r.allowsNextAction}:{}});let{tag:o}=await e.tagService.get(n);return p({tag:o},e.makeMeta({syncPending:true,humanReadableSummary:Ve(o.name)}))}function su(t,e){return t.registerTool("tag_update",{description:js,inputSchema:_s.shape},async n=>{let r=await PT(n,e);return u(r)})}var Ps="Preview what tag_update would do without making any changes. Do NOT use to actually update a tag \u2014 use tag_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function OT(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getTag(t.id);o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`)),t.status!==void 0&&(n.push({field:"status",newValue:t.status,oldValue:i.status}),r.push(`set status to '${t.status}'`)),t.allowsNextAction!==void 0&&(n.push({field:"allowsNextAction",newValue:String(t.allowsNextAction),oldValue:String(i.allowsNextAction)}),r.push(`set allowsNextAction to ${t.allowsNextAction}`));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update tag '${o}': ${r.join(", ")}.`:`Would update tag '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function iu(t,e){return t.registerTool("tag_update_describe",{description:Ps,inputSchema:_s.shape},async n=>{let r=await OT(n,e);return u(r)})}var Os=`Apply inbox-triage style assignments to many tasks in one batch \u2014 move to a project, diff tags additively, set defer/due/flagged. Tighter schema than task_batch_update; designed for the inbox-triage prompt's confirm step. Each assignment is { taskId, projectId?, addTagIds?, removeTagIds?, deferDate?, dueDate?, flagged? }. Tag diffs are resolved via a pre-read of current tagIds; specifying both addTagIds and removeTagIds for the same tag is a no-op (remove wins). Atomicity: best-effort, per-item \u2014 OF has no transactional batch. An item succeeds only if both its move (if requested) AND its non-move update succeed. Failures are reported with errorCode prefixed 'move:' or 'update:'. Returns { assigned: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each assignment without a follow-up read. Do NOT use this tool for full task replacement \u2014 use task_update or task_batch_update for those. Prefer task_batch_assign over a sequence of single task_update calls when you have a confirmed triage plan for multiple tasks. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_assign({ assignments: [{ taskId: "abc123", projectId: "prj456", flagged: true }, { taskId: "abc789", addTagIds: ["tag1"] }] })`,xT=z.object({taskId:h.schema.describe("Persistent task ID."),projectId:k.schema.optional().describe("If set, move the task to this project before applying other changes."),addTagIds:z.array(S.schema).optional().describe("Tag IDs to add. Combined with removeTagIds via current-tagIds pre-read."),removeTagIds:z.array(S.schema).optional().describe("Tag IDs to remove. Wins over addTagIds when the same ID appears in both."),deferDate:z.string().datetime({offset:true}).nullable().optional().describe("Defer date as ISO-8601 with offset. Null clears the date."),dueDate:z.string().datetime({offset:true}).nullable().optional().describe("Due date as ISO-8601 with offset. Null clears the date."),flagged:z.boolean().optional().describe("Flag or unflag the task.")}).refine(t=>t.projectId!==void 0||t.addTagIds!==void 0||t.removeTagIds!==void 0||t.deferDate!==void 0||t.dueDate!==void 0||t.flagged!==void 0,{message:"Each assignment must set at least one of projectId, addTagIds, removeTagIds, deferDate, dueDate, or flagged"}),DT=z.object({assignments:z.array(xT).min(1).describe("Triage assignments \u2014 one per task. Must contain at least one item.")});function RT(t){return t.map((e,n)=>e.projectId!==void 0?n:-1).filter(e=>e>=0)}function AT(t){return t.map((e,n)=>e.addTagIds!==void 0||e.removeTagIds!==void 0?n:-1).filter(e=>e>=0)}function xs(t,e,n){let r=new Set(t.map(String));for(let o of e??[])r.add(String(o));for(let o of n??[])r.delete(String(o));return Array.from(r).map(o=>S.of(o))}function CT(t,e){let n={};return t.deferDate!==void 0&&(n.deferDate=t.deferDate),t.dueDate!==void 0&&(n.dueDate=t.dueDate),t.flagged!==void 0&&(n.flagged=t.flagged),(t.addTagIds!==void 0||t.removeTagIds!==void 0)&&(n.tagIds=xs(e??[],t.addTagIds,t.removeTagIds)),Object.keys(n).length>0?n:null}async function Ds(t,e){let n=t.assignments,r=n.map(w=>w.taskId),o=await e.adapter.getTasksMany(r),a=new Map;for(let w=0;w<r.length;w++){let D=o[w];D!=null&&a.set(r[w],D.name);}let i=AT(n),s=new Map;if(i.length>0){let w=i.map(j=>n[j].taskId),D=await e.adapter.getTasksMany(w);for(let j=0;j<i.length;j++){let B=D[j];B&&s.set(i[j],B.tagIds);}}let c=RT(n),d=c.length>0?await e.adapter.batchMoveTasks(c.map(w=>({id:n[w].taskId,destination:{projectId:n[w].projectId}}))):{succeeded:[],failed:[]},l=new Map;for(let w of d.succeeded){let D=c[w.index];l.set(D,"ok");}for(let w of d.failed){let D=c[w.index];l.set(D,{errorCode:w.errorCode,message:w.message});}let m=[];for(let w=0;w<n.length;w++){let D=n[w],j=l.get(w);if(j!==void 0&&j!=="ok")continue;let B=CT(D,s.get(w));B!==null&&m.push({origIdx:w,id:D.taskId,patch:B});}let f=m.length>0?await e.adapter.batchUpdateTasks(m.map(w=>({id:w.id,patch:w.patch}))):{succeeded:[],failed:[]},g=new Set(f.succeeded.map(w=>w.index)),v=new Map;for(let w of f.failed)v.set(w.index,{errorCode:w.errorCode,message:w.message});let I=[],T=[];for(let w=0;w<n.length;w++){let D=n[w],j=l.get(w);if(j!==void 0&&j!=="ok"){T.push({index:w,errorCode:`move:${j.errorCode}`,message:j.message});continue}let B=m.findIndex(_=>_.origIdx===w);if(B>=0){if(g.has(B))I.push({index:w,value:D.taskId});else if(v.has(B)){let _=v.get(B);T.push({index:w,errorCode:`update:${_.errorCode}`,message:_.message});}}else I.push({index:w,value:D.taskId});}if(e.cache!==void 0&&I.length>0)for(let w of I){let D=n[w.index];b(e.cache,{taskId:D.taskId,...D.projectId!==void 0&&{projectId:D.projectId}});}let E=I.map(w=>({index:w.index,value:{id:w.value,name:a.get(w.value)??""}}));return p({assigned:E,failed:T},e.makeMeta({syncPending:I.length>0}))}function cu(t,e){return t.registerTool("task_batch_assign",{description:Os,inputSchema:DT.shape},async n=>{let r=await Ds(n,e);return u(r)})}var Rs='Mark many OmniFocus tasks complete in a single JXA round trip. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each completion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_complete calls whenever you are completing more than one task. Each item is { id, at? } where `at` is an optional ISO-8601 completion timestamp (defaults to now). Already-completed tasks are not treated specially here \u2014 use task_complete\'s idempotent noChange path if you need that per-item semantics. Returns { completed: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each completion without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_complete({ items: [{ id: "abc123" }, { id: "abc456" }] })',ET=z.object({id:h.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("Optional ISO-8601 completion time; defaults to now.")}),MT=z.object({items:z.array(ET).min(1).describe("Array of { id, at? } items. Must contain at least one item.")});async function NT(t,e){let n=t.items.map(c=>c.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let c=0;c<n.length;c++){let d=r[c];d!=null&&o.set(n[c],d.name);}let a=t.items.map(c=>({id:c.id,...c.at!==void 0&&{at:new Date(c.at)}})),i=await e.adapter.batchCompleteTasks(a);if(e.cache!==void 0)for(let c of i.succeeded){let d=t.items[c.index];d!==void 0&&b(e.cache,{taskId:d.id});}let s=i.succeeded.map(c=>({index:c.index,value:{id:c.value,name:o.get(c.value)??""}}));return p({completed:s,failed:i.failed},e.makeMeta({syncPending:i.succeeded.length>0,humanReadableSummary:xd(i.succeeded.length)}))}function du(t,e){return t.registerTool("task_batch_complete",{description:Rs,inputSchema:MT.shape},async n=>{let r=await NT(n,e);return u(r)})}var As='Create many OmniFocus tasks in a single JXA round trip. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: once the batch reaches OmniFocus, each task succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_create calls whenever you are creating more than one task. Each item accepts the same shape as task_create (name, optional projectId or parentTaskId, note, flagged, dueDate, deferDate, estimatedMinutes, tagIds, sequential, completedByChildren). Returns { created: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name (echoed from the input) so the agent can describe each new task without a follow-up read. Side effects: creates tasks in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the tasks to appear on other devices. Example: task_batch_create({ items: [{ name: "Buy milk" }, { name: "Call dentist", projectId: "prj123" }] })',FT=z.object({name:z.string().min(1).describe("Task name. Required, non-empty."),projectId:k.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:h.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z.string().optional().describe("Plain-text note."),flagged:z.boolean().optional().describe("Flag the task."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().optional().describe("Estimated duration in minutes."),tagIds:z.array(S.schema).optional().describe("Tag IDs to apply."),sequential:z.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Complete when all subtasks complete.")}).refine(t=>!(t.projectId!==void 0&&t.parentTaskId!==void 0),{message:"Supply at most one of projectId or parentTaskId",path:["projectId"]}),Cs=z.object({items:z.array(FT).min(1).describe("Array of task inputs. Must contain at least one item.")});async function UT(t,e){let n=t.items.map(a=>({name:a.name,...a.projectId!==void 0&&{projectId:a.projectId},...a.parentTaskId!==void 0&&{parentId:a.parentTaskId},...a.note!==void 0&&{note:a.note},...a.flagged!==void 0&&{flagged:a.flagged},...a.dueDate!==void 0&&{dueDate:a.dueDate},...a.dueDateFloating!==void 0&&{dueDateFloating:a.dueDateFloating},...a.deferDate!==void 0&&{deferDate:a.deferDate},...a.deferDateFloating!==void 0&&{deferDateFloating:a.deferDateFloating},...a.estimatedMinutes!==void 0&&{estimatedMinutes:a.estimatedMinutes},...a.tagIds!==void 0&&{tagIds:a.tagIds},...a.sequential!==void 0&&{sequential:a.sequential},...a.completedByChildren!==void 0&&{completedByChildren:a.completedByChildren}})),r=await e.adapter.batchCreateTasks(n);if(e.cache!==void 0&&r.succeeded.length>0){let a=new Set;for(let i of r.succeeded){let c=t.items[i.index]?.projectId;c!==void 0&&!a.has(c)&&(a.add(c),b(e.cache,{projectId:c}));}a.size===0&&b(e.cache,{});}let o=r.succeeded.map(a=>({index:a.index,value:{id:a.value,name:t.items[a.index]?.name??""}}));return p({created:o,failed:r.failed},e.makeMeta({syncPending:r.succeeded.length>0,humanReadableSummary:bt(r.succeeded.length)}))}function lu(t,e){return t.registerTool("task_batch_create",{description:As,inputSchema:Cs.shape},async n=>{let r=await UT(n,e);return u(r)})}var Es="Preview what task_batch_create would do without making any changes. Do NOT use to actually create tasks \u2014 use task_batch_create instead. Returns { description, plannedChanges } summarising all tasks that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function LT(t,e){let n=[],r=[];for(let a of t.items){let i;a.projectId!==void 0?i=`project '${await _t(e.adapter,a.projectId)}'`:a.parentTaskId!==void 0?i=`subtask of '${await Pt(e.adapter,a.parentTaskId)}'`:i="Inbox",r.push(`'${a.name}' (${i})`),n.push({field:"name",newValue:a.name}),a.projectId!==void 0?n.push({field:"projectId",newValue:a.projectId}):a.parentTaskId!==void 0&&n.push({field:"parentTaskId",newValue:a.parentTaskId});}let o=`Would create ${t.items.length} task${t.items.length===1?"":"s"}: ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function pu(t,e){return t.registerTool("task_batch_create_describe",{description:Es,inputSchema:Cs.shape},async n=>{let r=await LT(n,e);return u(r)})}var Zn=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function Qn(t,e){switch(t.kind){case "next-work-day":return BT(t.at??"morning",e);case "next-weekday":return JT(t.weekday,t.at??"morning",e);case "in-business-days":return $T(t.days,e);case "after-event":throw new ge("task_defer_smart `after-event` variant is not yet implemented \u2014 file a follow-up if you need calendar-event-anchored defers.");case "next-month-start":return WT(e);case "explicit-with-skip-weekends":return HT(t.date)}}function BT(t,e){let n=new Date(e.now);for(n.setDate(n.getDate()+1);Ms(n);)n.setDate(n.getDate()+1);return Ns(n,mu(t,e)),{resolvedDeferDate:un(n),reason:`next work ${t} (${Zn[n.getDay()]} ${fu(n)})`}}function JT(t,e,n){if(!Number.isInteger(t)||t<0||t>6)throw new y(`weekday must be an integer 0\u20136 (Sun\u2013Sat); got ${String(t)}`);let r=new Date(n.now),o=(t-r.getDay()+7)%7;return o===0&&(o=7),r.setDate(r.getDate()+o),Ns(r,mu(e,n)),{resolvedDeferDate:un(r),reason:`next ${Zn[t]} ${e} (${er(r)} ${fu(r)})`}}function $T(t,e){if(!Number.isInteger(t)||t<1)throw new y(`days must be a positive integer; got ${String(t)}`);let n=new Date(e.now),r=t;for(;r>0;)n.setDate(n.getDate()+1),Ms(n)||r--;return Ns(n,e.morningHour),{resolvedDeferDate:un(n),reason:`${t} business day${t===1?"":"s"} from now (${Zn[n.getDay()]} ${er(n)})`}}function WT(t){let e=new Date(t.now.getFullYear(),t.now.getMonth()+1,1,0,0,0,0);return {resolvedDeferDate:un(e),reason:`start of next month (${er(e)})`}}function HT(t,e){let n=new Date(t);if(Number.isNaN(n.getTime()))throw new y(`date must be a parseable ISO-8601 timestamp; got ${t}`);let r=new Date(n),o=false;for(;Ms(r);)r.setDate(r.getDate()+1),o=true;return {resolvedDeferDate:un(r),reason:o?`${t} \u2192 snapped to ${Zn[r.getDay()]} ${er(r)} (skipped weekend)`:`${t} (no weekend skip needed)`}}function Ms(t){let e=t.getDay();return e===0||e===6}function mu(t,e){return t==="morning"?e.morningHour:e.afternoonHour}function Ns(t,e){t.setHours(e,0,0,0);}function De(t){return t<10?`0${t}`:String(t)}function er(t){return `${t.getFullYear()}-${De(t.getMonth()+1)}-${De(t.getDate())}`}function fu(t){return `${De(t.getHours())}:${De(t.getMinutes())}`}function un(t){let e=-t.getTimezoneOffset(),n=e>=0?"+":"-",r=Math.abs(e),o=`${n}${De(Math.floor(r/60))}:${De(r%60)}`;return `${t.getFullYear()}-${De(t.getMonth()+1)}-${De(t.getDate())}T${De(t.getHours())}:${De(t.getMinutes())}:${De(t.getSeconds())}${o}`}var zT=9,GT=14;function tr(t=process.env){return {morningHour:uu(t.OMNIFOCUS_MORNING_HOUR,zT),afternoonHour:uu(t.OMNIFOCUS_AFTERNOON_HOUR,GT)}}function uu(t,e){if(t===void 0||t==="")return e;let n=Number(t);return !Number.isInteger(n)||n<0||n>23?e:n}var Fs="Batch variant of task_defer_smart: accepts an array of { taskId, intent } and resolves each intent independently. Same intent grammar as task_defer_smart. Do NOT use this for a single task \u2014 prefer task_defer_smart for one entry. Returns { results: [{ taskId, ok: true, resolvedDeferDate, reason } | { taskId, ok: false, error }] } \u2014 per-entry failures surface inline so one malformed entry does not abort the others. Side effects: writes the resolved deferDate to each successful task; dry_run skips writes. Triggers a sync when any entry succeeds. Example: task_batch_defer_smart({ entries: [{ taskId: '...', intent: { kind: 'next-work-day' } }] })",gu=z.enum(["morning","afternoon"]),qT=z.discriminatedUnion("kind",[z.object({kind:z.literal("next-work-day"),at:gu.optional()}),z.object({kind:z.literal("next-weekday"),weekday:z.number().int().min(0).max(6),at:gu.optional()}),z.object({kind:z.literal("in-business-days"),days:z.number().int().positive()}),z.object({kind:z.literal("after-event"),eventId:z.string().min(1)}),z.object({kind:z.literal("next-month-start")}),z.object({kind:z.literal("explicit-with-skip-weekends"),date:z.string().min(1)})]),VT=z.object({entries:z.array(z.object({taskId:h.schema,intent:qT})).min(1).describe("Array of { taskId, intent } pairs. Per-entry failures surface in the results array; one bad entry does not abort siblings."),dry_run:z.boolean().optional().describe("When true, resolves every intent but does NOT write to OmniFocus. Useful for previewing the batch's resolved dates before committing."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key. Identical subsequent calls within the TTL window replay the original results envelope with meta.idempotentReplay = true.")});async function KT(t,e){let n=e.idempotencyStore??de,r=e.now?e.now():new Date,o=e.hours??tr();return ce(n,t.idempotency_key,async()=>{let a=[];for(let s of t.entries)try{let c=Qn(s.intent,{now:r,morningHour:o.morningHour,afternoonHour:o.afternoonHour});if(!t.dry_run&&(await e.adapter.updateTask(s.taskId,{deferDate:c.resolvedDeferDate}),e.cache!==void 0)){let d=await e.adapter.getTask(s.taskId);b(e.cache,{taskId:s.taskId,projectId:d.projectId});}a.push({taskId:s.taskId,ok:!0,resolvedDeferDate:c.resolvedDeferDate,reason:c.reason});}catch(c){a.push({taskId:s.taskId,ok:false,error:c instanceof Error?c.message:String(c)});}let i={syncPending:!t.dry_run};return t.dry_run&&(i.dryRun=true),p({results:a},e.makeMeta(i))})}function hu(t,e){return t.registerTool("task_batch_defer_smart",{description:Fs,inputSchema:VT.shape},async n=>{let r=await KT(n,e);return u(r)})}var Us='Permanently delete many OmniFocus tasks in a single JXA round trip. IRREVERSIBLE \u2014 deleted tasks cannot be recovered. REQUIRED: pass confirm=true at the top level to acknowledge this action is irreversible; the entire batch is rejected without it. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each deletion succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_delete calls whenever deleting more than one task. Each item is { id }. Returns { deleted: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name (captured pre-delete) so the agent can describe each removal without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_delete({ confirm: true, items: [{ id: "abc123" }, { id: "abc456" }] })',XT=z.object({id:h.schema.describe("Persistent task ID.")}),YT=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that all deletions are permanent and irreversible. Must be exactly true. The entire batch is rejected if this field is absent or false."),items:z.array(XT).min(1).describe("Array of { id } items. Must contain at least one item.")});async function ZT(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchDeleteTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({deleted:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Rd(a.succeeded.length)}))}function yu(t,e){return t.registerTool("task_batch_delete",{description:Us,inputSchema:YT.shape},async n=>{let r=await ZT(n,e);return u(r)})}var Bs='Cancel (drop) many OmniFocus tasks in a single JXA round trip. Dropped tasks remain in OmniFocus but are treated as cancelled/inactive \u2014 they do not appear in active task lists. Use task_batch_delete for permanent removal. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each drop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_drop calls whenever dropping more than one task. Each item is { id }. Returns { dropped: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each drop without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_drop({ items: [{ id: "abc123" }, { id: "abc456" }] })',QT=z.object({id:h.schema.describe("Persistent task ID.")}),ew=z.object({items:z.array(QT).min(1).describe("Array of { id } items. Must contain at least one item.")});async function tw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchDropTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({dropped:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ad(a.succeeded.length)}))}function ku(t,e){return t.registerTool("task_batch_drop",{description:Bs,inputSchema:ew.shape},async n=>{let r=await tw(n,e);return u(r)})}var Js='Move many OmniFocus tasks to new destinations in a single OmniJS round trip. Routes through OmniJS \u2014 not JXA \u2014 because JXA task.move() is unimplemented in OmniFocus 4.x. Each item specifies a task ID and exactly one destination: projectId (move into a project) or parentId (move under a parent task). Omit both to move to the inbox. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each move succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_move calls whenever moving more than one task. Returns { moved: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each move without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_move({ items: [{ id: "abc123", projectId: "prj456" }, { id: "abc789", parentId: "tsk111" }] })',nw=z.object({projectId:k.schema.optional().describe("Move into a project as a top-level action. Mutually exclusive with parentId."),parentId:h.schema.optional().describe("Move under a parent task. Mutually exclusive with projectId.")}).refine(t=>!(t.projectId!==void 0&&t.parentId!==void 0),{message:"Provide projectId OR parentId, not both"}),rw=z.object({id:h.schema.describe("Persistent task ID."),destination:nw.describe("Where to move the task. Provide projectId, parentId, or neither (inbox).")}),ow=z.object({items:z.array(rw).min(1).describe("Array of { id, destination } items. Must contain at least one item.")});async function aw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchMoveTasks(t.items.map(s=>({id:s.id,destination:{...s.destination.projectId!==void 0&&{projectId:s.destination.projectId},...s.destination.parentId!==void 0&&{parentId:s.destination.parentId}}})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({moved:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ed(a.succeeded.length,"destination")}))}function Iu(t,e){return t.registerTool("task_batch_move",{description:Js,inputSchema:ow.shape},async n=>{let r=await aw(n,e);return u(r)})}var Ws='Mark many OmniFocus tasks as incomplete in a single JXA round trip. Reverses a previous completion \u2014 useful when a task was completed by mistake or needs to be re-done. Uncompleted tasks return to active status. Use task_batch_complete to mark tasks as completed. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each uncomplete succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_uncomplete calls whenever uncompleting more than one task. Each item is { id }. Returns { uncompleted: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each restoration without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_uncomplete({ items: [{ id: "abc123" }, { id: "abc456" }] })',sw=z.object({id:h.schema.describe("Persistent task ID.")}),iw=z.object({items:z.array(sw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function cw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchUncompleteTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({uncompleted:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Dd(a.succeeded.length)}))}function vu(t,e){return t.registerTool("task_batch_uncomplete",{description:Ws,inputSchema:iw.shape},async n=>{let r=await cw(n,e);return u(r)})}var zs='Restore (undrop) many cancelled OmniFocus tasks in a single JXA round trip. Undropped tasks are returned to active status and will reappear in active task lists. Use task_batch_drop to cancel tasks. Validation is atomic: if any input fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each undrop succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_undrop calls whenever undropping more than one task. Each item is { id }. Returns { undropped: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 value carries the task name so the agent can describe each restoration without a follow-up read. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_undrop({ items: [{ id: "abc123" }, { id: "abc456" }] })',dw=z.object({id:h.schema.describe("Persistent task ID.")}),lw=z.object({items:z.array(dw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function pw(t,e){let n=t.items.map(s=>s.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let s=0;s<n.length;s++){let c=r[s];c!=null&&o.set(n[s],c.name);}let a=await e.adapter.batchUndropTasks(t.items.map(s=>({id:s.id})));if(e.cache!==void 0)for(let s of a.succeeded){let c=t.items[s.index];c!==void 0&&b(e.cache,{taskId:c.id});}let i=a.succeeded.map(s=>({index:s.index,value:{id:s.value,name:o.get(s.value)??""}}));return p({undropped:i,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Cd(a.succeeded.length)}))}function Tu(t,e){return t.registerTool("task_batch_undrop",{description:zs,inputSchema:lw.shape},async n=>{let r=await pw(n,e);return u(r)})}var Gs=`Partially update many OmniFocus tasks in a single JXA round trip. Validation is atomic: if any patch fails schema, the whole batch is rejected before any mutation. Execution is best-effort: each update succeeds or fails independently, and the response reports per-index outcomes. Prefer this tool over repeated task_update calls whenever you are updating more than one task. Each item is { id, patch } where patch accepts a subset of task_update's editable fields (name, note, flagged, dueDate, deferDate, estimatedMinutes, tagIds, sequential, completedByChildren). Additive tag diffs (addTags/removeTags) and safety primitives (dry_run, expectedModifiedAt, idempotency_key) are not supported in batch form; fall back to task_update for those. Returns { updated: [{index, value: { id, name }}], failed: [{index, errorCode, message}] } \u2014 name reflects the post-patch name (uses patch.name when supplied, otherwise the existing name). Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_batch_update({ items: [{ id: "abc123", patch: { flagged: true } }, { id: "abc456", patch: { dueDate: "2026-05-01T00:00:00Z" } }] })`,uw=z.object({name:z.string().min(1).optional().describe("New task name."),note:z.string().nullable().optional().describe("Plain-text note. Null clears the note."),flagged:z.boolean().optional().describe("Flag or unflag the task."),dueDate:z.string().datetime({offset:true}).nullable().optional().describe("Due date as ISO-8601 with offset. Null clears the date."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),deferDate:z.string().datetime({offset:true}).nullable().optional().describe("Defer date as ISO-8601 with offset. Null clears the date."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated duration in minutes. Null clears the estimate."),tagIds:z.array(S.schema).optional().describe("Full replacement tag ID list."),sequential:z.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Complete when all subtasks complete.")}).refine(t=>Object.keys(t).length>0,{message:"Patch must contain at least one field"}),mw=z.object({id:h.schema.describe("Persistent task ID."),patch:uw.describe("Fields to change. At least one field required.")}),qs=z.object({items:z.array(mw).min(1).describe("Array of { id, patch } pairs. Must contain at least one item.")});async function fw(t,e){let n=t.items.map(c=>c.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let c=0;c<n.length;c++){let d=r[c];d!=null&&o.set(n[c],d.name);}let a=t.items.map(c=>({id:c.id,patch:c.patch})),i=await e.adapter.batchUpdateTasks(a);if(e.cache!==void 0)for(let c of i.succeeded){let d=t.items[c.index];d!==void 0&&b(e.cache,{taskId:d.id});}let s=i.succeeded.map(c=>{let l=t.items[c.index]?.patch?.name??o.get(c.value)??"";return {index:c.index,value:{id:c.value,name:l}}});return p({updated:s,failed:i.failed},e.makeMeta({syncPending:i.succeeded.length>0,humanReadableSummary:Od(i.succeeded.length)}))}function wu(t,e){return t.registerTool("task_batch_update",{description:Gs,inputSchema:qs.shape},async n=>{let r=await fw(n,e);return u(r)})}var Vs="Preview what task_batch_update would do without making any changes. Do NOT use to actually update tasks \u2014 use task_batch_update instead. Returns { description, plannedChanges } summarising all patches that would be applied. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function gw(t,e){let n=[],r=[];for(let a of t.items){let i=String(a.id);try{i=(await e.adapter.getTask(a.id)).name;}catch{}let s=Object.keys(a.patch).join(", ");r.push(`'${i}' [${s}]`);for(let[c,d]of Object.entries(a.patch))n.push({field:`${a.id}.${c}`,newValue:d===null?null:String(d)});}let o=`Would update ${t.items.length} task${t.items.length===1?"":"s"}: ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Su(t,e){return t.registerTool("task_batch_update_describe",{description:Vs,inputSchema:qs.shape},async n=>{let r=await gw(n,e);return u(r)})}var Ks='Remove all alarms/notifications from an OmniFocus task. After clearing, the task has no scheduled notifications. Use task_set_alarms to install a new alarm set. Returns the updated task. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_clear_alarms({ id: "abc123" })',yw=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function kw(t,e){await e.adapter.clearTaskAlarms(t.id);let n=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:Pd(n.name)});return p({task:n},r)}function bu(t,e){return t.registerTool("task_clear_alarms",{description:Ks,inputSchema:yw.shape},async n=>{let r=await kw(n,e);return u(r)})}var Xs='Remove the repetition rule from an OmniFocus task. After clearing, the task becomes a one-time item. Use task_set_repetition to set or change a rule. Returns the updated task with repetitionRule confirmed as null. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_clear_repetition({ id: "abc123" })',vw=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function Tw(t,e){await e.adapter.updateTask(t.id,{repetition:null});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:jd(n.name)});return p({task:n},r)}function ju(t,e){return t.registerTool("task_clear_repetition",{description:Xs,inputSchema:vw.shape},async n=>{let r=await Tw(n,e);return u(r)})}var Ys='Complete an OmniFocus task \u2014 marks it done with a completion timestamp. Accepts an optional ISO-8601 date for the completion time; defaults to now. Idempotent: returns noChange: true if the task is already completed. When the task has incomplete children, returns clarification-needed asking whether to complete children too \u2014 call the `clarify` tool with the user\'s choice. Do not use to drop or delete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: sets completedAt, sets meta.syncPending = true. Example: task_complete({ id: "abc123" }) Example: task_complete({ id: "abc123", at: "2026-05-01T09:00:00Z" })',Zs=z.object({id:h.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("ISO-8601 completion time. Defaults to now.")});async function ww(t,e){let n=await e.adapter.getTask(t.id);if(n.completed)return p({noChange:true,id:t.id,name:n.name},e.makeMeta());let r=e.replayStore??ve,o=[];try{o=await e.adapter.listTasks({parentId:t.id,completed:!1});}catch{}if(o.length>0){let a=e.makeMeta(),i=["Complete this task and all incomplete children","Complete this task only \u2014 leave children incomplete"],s=o.map(d=>d.id),c=r.register(i,async d=>Pu(t,n,e,d===0?s:[]));return St(`"${n.name}" has ${o.length} incomplete child task(s). How should they be handled?`,c,a,i.map((d,l)=>({index:l,label:d})),{id:t.id,...t.at!==void 0?{at:t.at}:{}})}return Pu(t,n,e,[])}async function Pu(t,e,n,r){for(let i of r)try{await n.adapter.completeTask(i);}catch{}let o=t.at!==void 0?new Date(t.at):void 0;await n.adapter.completeTask(t.id,o),n.cache!==void 0&&b(n.cache,{taskId:t.id,projectId:e.projectId});let a;if(e.projectId!==null)try{if((await n.adapter.listTasks({projectId:e.projectId,completed:!1})).length===0){let s=await n.adapter.getProject(e.projectId);a=xt([Vl(e.projectId,s.name)]);}}catch{}return p({done:true,id:t.id,name:e.name},n.makeMeta({syncPending:true,humanReadableSummary:gd(e.name)}),void 0,a)}function Ou(t,e){return t.registerTool("task_complete",{description:Ys,inputSchema:Zs.shape},async n=>{let r=await ww(n,e);return u(r)})}var Qs="Preview what task_complete would do without making any changes. Do NOT use to actually complete a task \u2014 use task_complete instead. Returns { description, plannedChanges } describing the completion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Sw(t,e){let n=[],r=String(t.id),o=false;try{let s=await e.adapter.getTask(t.id);r=s.name,o=s.completed;}catch{}if(o){let s=`Task '${r}' is already completed \u2014 would be a no-op.`;return p({description:s,plannedChanges:n},e.makeMeta())}n.push({field:"completed",newValue:"true",oldValue:"false"}),t.at!==void 0&&n.push({field:"completedAt",newValue:t.at});let a=t.at!==void 0?` at ${t.at}`:"",i=`Would mark task '${r}' as done${a}.`;return p({description:i,plannedChanges:n},e.makeMeta())}function xu(t,e){return t.registerTool("task_complete_describe",{description:Qs,inputSchema:Zs.shape},async n=>{let r=await Sw(n,e);return u(r)})}var ei=`Promote an OmniFocus task to a first-class project via OmniJS Database.convertTasksToProjects(). The task's persistent identifier is preserved on the resulting project \u2014 agents can continue using the same ID as a project ID after conversion. Subtasks, notes, tags, and dates are carried over by OmniFocus automatically. Use this when a task has grown in scope and needs its own review interval, subtask hierarchy, or project-level metadata. Do NOT use on tasks already in a project \u2014 use task_move instead for reparenting; use project_create when starting from scratch. Returns { converted: true, projectId, taskId, name } \u2014 name is the task name (carried over to the new project) so the agent can describe the conversion without a follow-up read. Side effects: removes the task from the task list and adds a project; sets meta.syncPending = true. Example: task_convert_to_project({ id: "abc123" }) Example: task_convert_to_project({ id: "abc123", folderId: "fld456" })`,bw=z.object({id:h.schema.describe("Persistent ID of the task to promote."),folderId:W.schema.optional().describe("Place the new project inside this folder. Omit to place at the top of the library."),position:z.enum(["beginning","ending"]).optional().describe('Where within the folder or library to insert the new project. Defaults to "ending".')});async function jw(t,e){let n={};t.folderId!==void 0&&(n.folderId=t.folderId),t.position!==void 0&&(n.position=t.position);let r=await e.adapter.getTask(t.id),o=await e.adapter.convertTaskToProject(t.id,n);return e.cache!==void 0&&(b(e.cache,{taskId:t.id}),J(e.cache,{projectId:o})),p({converted:true,projectId:o,taskId:t.id,name:r.name},e.makeMeta({syncPending:true,humanReadableSummary:Sd(r.name)}))}function Ru(t,e){return t.registerTool("task_convert_to_project",{description:ei,inputSchema:bw.shape},async n=>{let r=await jw(n,e);return u(r)})}function Ee(t){if(t.length===0)return "<root>";let e="";for(let n of t)typeof n=="number"?e+=`[${n}]`:e+=e===""?String(n):`.${String(n)}`;return e}function Au(t,e){let n=t;for(let r of e){if(n==null)return;if(typeof n=="object")n=n[r];else return}return n}function _w(t,e=6){let r=t.slice(0,e).map(o=>typeof o=="string"?`"${o}"`:String(o));return t.length>e&&r.push(`\u2026 (${t.length-e} more)`),r.join(" | ")}function Pw(t){switch(t){case "datetime":return {expected:"ISO-8601 datetime, UTC (Z) or with offset",examples:["2025-03-01T09:00:00Z","2025-03-01T09:00:00-05:00"]};case "date":return {expected:"ISO-8601 date (YYYY-MM-DD)",examples:["2025-03-01"]};case "time":return {expected:"ISO-8601 time (HH:MM:SS)",examples:["09:00:00"]};case "email":return {expected:"email address",examples:["user@example.com"]};case "url":return {expected:"absolute URL",examples:["https://example.com/path"]};case "uuid":return {expected:"UUID v4",examples:["3fa85f64-5717-4562-b3fc-2c963f66afa6"]};case "ipv4":return {expected:"IPv4 address",examples:["192.0.2.42"]};case "ipv6":return {expected:"IPv6 address",examples:["2001:db8::1"]};case "regex":return {expected:"string matching the schema's regex"};default:return {expected:`string in "${t}" format`}}}function Cu(t,e,n,r){let o=t==="min"?n?"\u2265":">":n?"\u2264":"<";switch(r){case "string":return `string with ${o} ${e} character${e===1?"":"s"}`;case "array":return `array with ${o} ${e} item${e===1?"":"s"}`;case "set":return `set with ${o} ${e} member${e===1?"":"s"}`;default:return `value ${o} ${e}`}}function Eu(t,e){let n=r=>e===void 0?void 0:Au(e,r);switch(t.code){case "invalid_type":{let r=t.expected;return [{field:Ee(t.path),sent:n(t.path),expected:`${r}`}]}case "invalid_value":{let r=t.values;return [{field:Ee(t.path),sent:n(t.path),expected:`one of: ${_w(r)}`,...r.length>0&&{examples:r.slice(0,3)}}]}case "invalid_format":{let r=Pw(t.format);return [{field:Ee(t.path),sent:n(t.path),expected:r.expected,...r.examples&&{examples:r.examples}}]}case "too_small":return [{field:Ee(t.path),sent:n(t.path),expected:Cu("min",Number(t.minimum),t.inclusive??true,t.origin)}];case "too_big":return [{field:Ee(t.path),sent:n(t.path),expected:Cu("max",Number(t.maximum),t.inclusive??true,t.origin)}];case "unrecognized_keys":{let r=Ee(t.path);return t.keys.map(o=>({field:r==="<root>"?o:`${r}.${o}`,sent:Au(e,[...t.path,o]),expected:"key is not part of the schema; remove it"}))}case "not_multiple_of":return [{field:Ee(t.path),sent:n(t.path),expected:`multiple of ${String(t.divisor)}`}];case "invalid_union":{let r=[];for(let a of t.errors){let i=a.slice().sort((s,c)=>c.path.length-s.path.length)[0];i!==void 0&&r.push(...Eu(i,e));}if(r.length===0)return [{field:Ee(t.path),sent:n(t.path),expected:"value matching one of the schema's accepted shapes"}];let o=`one of: ${r.map(a=>a.expected).join("; or ")}`;return [{field:Ee(t.path),sent:n(t.path),expected:o}]}default:return [{field:Ee(t.path),sent:n(t.path),expected:t.message||`value satisfying schema constraint "${String(t.code)}"`}]}}function Mu(t,e){let n=[];for(let r of t.issues)n.push(...Eu(r,e));return n}function je(t,e,n="Cross-field validation failed"){let r=t.safeParse(e);if(r.success)return r.data;let o=Mu(r.error,e);throw new y(n,{details:{failures:o}})}var ti='Create a new task in OmniFocus \u2014 in the inbox, inside a project, or as a subtask of another task. Supply exactly one of: projectId (project task), parentTaskId (subtask), or neither (inbox). Do not use for bulk creation; prefer task_batch_create for that. Safety control: pass idempotency_key to make transport retries safe \u2014 identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate task. Returns { id, name } \u2014 name echoes the supplied name so the agent can describe the new task without a follow-up read. Side effects: creates a task in OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the task to appear on other devices. Example: task_create({ name: "Buy milk" }) Example: task_create({ name: "Write report", projectId: "prj123", dueDate: "2026-05-01T00:00:00Z" })',or=z.object({name:z.string().min(1).describe("Task name. Required, must be non-empty."),projectId:k.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:h.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z.string().optional().describe("Plain-text note."),flagged:z.boolean().optional().describe("Flag the task."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset."),dueDateFloating:z.boolean().optional().describe("When true, the due time follows the user across time zones (floating) rather than being pinned to a fixed UTC instant. Use for recurring daily tasks where '9 AM' should mean 9 AM wherever the user is. Default: false (fixed-offset)."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().min(1).optional().describe("Estimated duration in minutes."),tagIds:z.array(S.schema).optional().describe("Tag IDs to apply."),sequential:z.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Complete when all subtasks complete."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe creates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of creating a duplicate task.")}),Ow=or.refine(t=>!(t.projectId!==void 0&&t.parentTaskId!==void 0),{message:"Supply at most one of projectId or parentTaskId",path:["projectId"]}).refine(t=>!(t.dueDate!==void 0&&t.deferDate!==void 0&&new Date(t.dueDate)<new Date(t.deferDate)),{message:"dueDate must not be earlier than deferDate",path:["dueDate"]});async function xw(t,e){je(Ow,t);let n=e.idempotencyStore??de;return ce(n,t.idempotency_key,async()=>{let r={name:t.name,...t.projectId!==void 0&&{projectId:t.projectId},...t.parentTaskId!==void 0&&{parentId:t.parentTaskId},...t.note!==void 0&&{note:t.note},...t.flagged!==void 0&&{flagged:t.flagged},...t.dueDate!==void 0&&{dueDate:t.dueDate},...t.dueDateFloating!==void 0&&{dueDateFloating:t.dueDateFloating},...t.deferDate!==void 0&&{deferDate:t.deferDate},...t.deferDateFloating!==void 0&&{deferDateFloating:t.deferDateFloating},...t.estimatedMinutes!==void 0&&{estimatedMinutes:t.estimatedMinutes},...t.tagIds!==void 0&&{tagIds:t.tagIds},...t.sequential!==void 0&&{sequential:t.sequential},...t.completedByChildren!==void 0&&{completedByChildren:t.completedByChildren}},o=await e.adapter.createTask(r);e.cache!==void 0&&b(e.cache,{...t.projectId!==void 0&&{projectId:t.projectId}});let a=[Hl(o,t.name),zl(o,t.dueDate,t.estimatedMinutes)];if(t.projectId===void 0&&t.parentTaskId===void 0)try{let s=await e.adapter.listTasks({inbox:!0,completed:!1});a.push(Gl(s.length));}catch{}let i=xt(a.filter(s=>s!=null));return p({id:o,name:t.name},e.makeMeta({syncPending:true,humanReadableSummary:fd(t.name)}),void 0,i)})}function Nu(t,e){return t.registerTool("task_create",{description:ti,inputSchema:or.shape},async n=>{let r=await xw(n,e);return u(r)})}var ni="Preview what task_create would do without making any changes. Do NOT use to actually create a task \u2014 use task_create instead. Returns { description, plannedChanges } describing the task that would be created. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Dw(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.projectId!==void 0){let a=await _t(e.adapter,t.projectId);n.push({field:"projectId",newValue:t.projectId}),r.push(`in project '${a}'`);}else if(t.parentTaskId!==void 0){let a=await Pt(e.adapter,t.parentTaskId);n.push({field:"parentTaskId",newValue:t.parentTaskId}),r.push(`as subtask of '${a}'`);}else r.push("in Inbox");if(t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate}),r.push(`due ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${Te(t.deferDate)}`)),t.flagged===true&&(n.push({field:"flagged",newValue:"true"}),r.push("flagged")),t.estimatedMinutes!==void 0&&(n.push({field:"estimatedMinutes",newValue:String(t.estimatedMinutes)}),r.push(`estimated ${t.estimatedMinutes} min`)),t.tagIds!==void 0&&t.tagIds.length>0){let a=await Promise.all(t.tagIds.map(i=>he(e.adapter,i)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`tagged ${a.map(i=>`'${i}'`).join(", ")}`);}t.note!==void 0&&n.push({field:"note",newValue:t.note.slice(0,50)});let o=`Would create task ${r.join(", ")}.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Fu(t,e){return t.registerTool("task_create_describe",{description:ni,inputSchema:or.shape},async n=>{let r=await Dw(n,e);return u(r)})}var ri="Defer a task to a date computed from a high-level intent (e.g. 'next work morning', 'skip weekends', 'in 3 business days'), instead of guessing an ISO date that may land on a weekend or off-hours. Variants: next-work-day, next-weekday, in-business-days, after-event (gated on calendar bridge), next-month-start, explicit-with-skip-weekends. Morning/afternoon defaults are configurable via OMNIFOCUS_MORNING_HOUR / OMNIFOCUS_AFTERNOON_HOUR env (default 09:00 / 14:00). Do NOT use this for unconditional ISO-date defers \u2014 prefer task_update with deferDate. Returns { taskId, resolvedDeferDate, reason } so the agent can echo the resolved date verbatim. Side effects: writes the resolved deferDate via task_update; supports dry_run, idempotency_key, and expectedModifiedAt for safety. Triggers a sync. Example: task_defer_smart({ taskId: '...', intent: { kind: 'next-work-day' } })",Uu=z.enum(["morning","afternoon"]),Rw=z.discriminatedUnion("kind",[z.object({kind:z.literal("next-work-day"),at:Uu.optional()}),z.object({kind:z.literal("next-weekday"),weekday:z.number().int().min(0).max(6),at:Uu.optional()}),z.object({kind:z.literal("in-business-days"),days:z.number().int().positive()}),z.object({kind:z.literal("after-event"),eventId:z.string().min(1)}),z.object({kind:z.literal("next-month-start")}),z.object({kind:z.literal("explicit-with-skip-weekends"),date:z.string().min(1)})]),Aw=z.object({taskId:h.schema.describe("ID of the task to defer."),intent:Rw.describe("High-level defer intent. Discriminated union on `kind` \u2014 see tool description for variants."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and resolves the intent but does NOT write to OmniFocus. Returns the resolved date + reason in the response with meta.dryRun = true."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe defers. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying.")});async function Cw(t,e){let n=e.idempotencyStore??de,r=e.now?e.now():new Date,o=e.hours??tr();return ce(n,t.idempotency_key,async()=>{let a=Qn(t.intent,{now:r,morningHour:o.morningHour,afternoonHour:o.afternoonHour}),i=()=>p({taskId:t.taskId,resolvedDeferDate:a.resolvedDeferDate,reason:a.reason},e.makeMeta({syncPending:false})),s=async()=>{await e.adapter.updateTask(t.taskId,{deferDate:a.resolvedDeferDate});let c=await e.adapter.getTask(t.taskId);return e.cache!==void 0&&b(e.cache,{taskId:t.taskId,projectId:c.projectId}),p({taskId:t.taskId,resolvedDeferDate:a.resolvedDeferDate,reason:a.reason},e.makeMeta({syncPending:true}))};return Ce(t.dry_run,i,s)})}function Lu(t,e){return t.registerTool("task_defer_smart",{description:ri,inputSchema:Aw.shape},async n=>{let r=await Cw(n,e);return u(r)})}var oi='Permanently delete an OmniFocus task. IRREVERSIBLE \u2014 uses OmniFocus deleteObject; there is no undo. Prefer task_drop when you want a recoverable status change. Only use task_delete when the agent has explicit user intent to permanently remove the task. REQUIRED: pass confirm=true to acknowledge this action is irreversible; the call is rejected without it. Safety controls: set dry_run=true to preview without mutating; pass expectedModifiedAt (from a recent task_get) to reject the call if the task changed since you read it; pass idempotency_key to coalesce retries so the same delete is only performed once. Returns { deleted: true, id } on success. Side effects: removes the task from OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need the deletion to appear on other devices. Example: task_delete({ id: "abc123", confirm: true }) Example: task_delete({ id: "abc123", confirm: true, dry_run: true })',ai=z.object({confirm:z.literal(true).describe("Explicit acknowledgement that this deletion is permanent and irreversible. Must be exactly true. The call is rejected if this field is absent or false."),id:h.schema.describe("Persistent ID of the task to delete. Get from task_list or search_query. Verify you have the correct ID before calling \u2014 this action is irreversible."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no delete is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe deletes. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-deleting (or re-raising NotFound on the second attempt).")});async function Ew(t,e){let n=e.idempotencyStore??de;return ce(n,t.idempotency_key,async()=>{let r=await e.adapter.getTask(t.id);Ye(t.expectedModifiedAt,r.modifiedAt,`task:${t.id}`);let o=()=>p({deleted:true,id:t.id},e.makeMeta({syncPending:false})),a=async()=>(await e.adapter.deleteTask(t.id),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:r.projectId}),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:yd(r.name)})));return Ce(t.dry_run,o,a)})}function Bu(t,e){return t.registerTool("task_delete",{description:oi,inputSchema:ai.shape},async n=>{let r=await Ew(n,e);return u(r)})}var si="Preview what task_delete would do without making any changes. Do NOT use to actually delete a task \u2014 use task_delete instead. Returns { description, plannedChanges } describing the permanent deletion that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Mw(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTask(t.id)).name;}catch{}n.push({field:"deleted",newValue:"true"});let o=`Would permanently delete task '${r}' (id: ${t.id}). IRREVERSIBLE.`;return p({description:o,plannedChanges:n},e.makeMeta())}function Ju(t,e){return t.registerTool("task_delete_describe",{description:si,inputSchema:ai.shape},async n=>{let r=await Mw(n,e);return u(r)})}var ii='Drop an OmniFocus task \u2014 marks it as dropped/deferred and removes it from active view. Reversible via task_undrop. Accepts an optional ISO-8601 date. Idempotent: returns noChange: true if already dropped. Do not use to complete or delete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: sets droppedAt, sets meta.syncPending = true.Example: task_drop({ id: "abc123" })',ci=z.object({id:h.schema.describe("Persistent task ID."),at:z.string().datetime({offset:true}).optional().describe("ISO-8601 drop time. Defaults to now.")});async function Nw(t,e){let n=await e.adapter.getTask(t.id);if(n.dropped)return p({noChange:true,id:t.id,name:n.name},e.makeMeta());let r=t.at!==void 0?new Date(t.at):void 0;return await e.adapter.dropTask(t.id,r),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:kd(n.name)}))}function Wu(t,e){return t.registerTool("task_drop",{description:ii,inputSchema:ci.shape},async n=>{let r=await Nw(n,e);return u(r)})}var di="Preview what task_drop would do without making any changes. Do NOT use to actually drop a task \u2014 use task_drop instead. Returns { description, plannedChanges } describing the drop that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function Fw(t,e){let n=[],r=String(t.id),o=false;try{let s=await e.adapter.getTask(t.id);r=s.name,o=s.dropped;}catch{}if(o){let s=`Task '${r}' is already dropped \u2014 would be a no-op.`;return p({description:s,plannedChanges:n},e.makeMeta())}n.push({field:"dropped",newValue:"true",oldValue:"false"}),t.at!==void 0&&n.push({field:"droppedAt",newValue:t.at});let a=t.at!==void 0?` at ${t.at}`:"",i=`Would drop task '${r}' (mark as dropped/on-hold)${a}.`;return p({description:i,plannedChanges:n},e.makeMeta())}function Hu(t,e){return t.registerTool("task_drop_describe",{description:di,inputSchema:ci.shape},async n=>{let r=await Fw(n,e);return u(r)})}var li=`Duplicate an OmniFocus task, optionally including its entire subtask subtree when recursive: true. Editable fields copy over (name, note, defer/due dates, flagged, tags, estimate, repetition); system fields (id, timestamps) regenerate; completed/dropped state is NOT carried \u2014 the duplicate is a fresh, active task. Do NOT use task_duplicate as a substitute for task_move (which reparents the existing task) or task_create (when the new task's fields differ from the source). By default the clone lands alongside the source. Provide destination with exactly one of projectId, parentId, or toInbox: true to place it elsewhere. Returns { duplicated: true, sourceId, newId, descendantCount, name } \u2014 name is the source task's name (the duplicate carries the same name) so the agent can describe the new task without a follow-up read. Side effects: creates one new task (plus descendants if recursive) in OmniFocus, sets meta.syncPending = true. Example: task_duplicate({ id: "abc123" }) Example: task_duplicate({ id: "abc123", recursive: true, destination: { projectId: "prj456" } })`,Uw=z.union([z.object({projectId:k.schema}),z.object({parentId:h.schema}),z.object({toInbox:z.literal(true)})]).describe("Where to place the duplicate. Exactly one of projectId, parentId, or toInbox: true. Omit to clone alongside the source."),Lw=z.object({id:h.schema.describe("Persistent ID of the task to duplicate."),recursive:z.boolean().optional().default(false).describe("When true, clone the full subtask subtree depth-first. Default: false (clone only the task itself)."),destination:Uw.optional()}).describe("Duplicate options. `destination` overrides the default same-container placement.");async function Bw(t,e){if(t.destination!==void 0){let a=t.destination,i=("projectId"in a?1:0)+("parentId"in a?1:0)+("toInbox"in a&&a.toInbox===true?1:0);if(i!==1)throw new y("task_duplicate: destination must set exactly one of projectId, parentId, or toInbox",{details:{field:"destination",provided:i},suggestion:"Set exactly one destination field or omit destination entirely."})}let n=await e.adapter.getTask(t.id),{newId:r,descendantCount:o}=await e.adapter.duplicateTask(t.id,{recursive:t.recursive,...t.destination!==void 0?{destination:t.destination}:{}});return e.cache!==void 0&&(b(e.cache,{taskId:r,projectId:n.projectId}),t.destination!==void 0&&"projectId"in t.destination&&t.destination.projectId!==n.projectId&&b(e.cache,{projectId:t.destination.projectId})),p({duplicated:true,sourceId:t.id,newId:r,descendantCount:o,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Td(n.name)}))}function zu(t,e){return t.registerTool("task_duplicate",{description:li,inputSchema:Lw.shape},async n=>{let r=await Bw(n,e);return u(r)})}var pi=[".png",".jpg",".jpeg",".heic",".heif",".gif",".webp",".pdf"];function qu(t){return pi.includes(extname(t).toLowerCase())}function $w(t){return t.mimeType!==null?t.mimeType.startsWith("image/")||t.mimeType==="application/pdf":qu(t.name)}var ui=`Capture tasks from an image \u2014 agent does vision, tool does plumbing. Source is a path or existing OF attachment; agent supplies proposed: ProposedTask[]. Two-phase: dryRun=true validates+echoes; dryRun=false with confirmation[] writes. attachSourceTo: 'parent-task' (default), 'each-task' (path-mode only), or 'none'. Path-mode: PNG/JPEG/HEIC/HEIF/GIF/WEBP/PDF; respects attachment-path-scope + size cap. Do NOT use when you already have structured tasks \u2014 call task_batch_create. Returns { phase, proposed?, parent?, created?, outcome? }. Side effects: dryRun=false creates tasks; call sync_trigger for cross-device. Example: task_extract_from_image({ source: { kind: "path", path: "/tmp/whiteboard.png" }, proposed: [{ name: "Follow up with Alice" }], dryRun: true })`,Gu=z.object({name:z.string().min(1).describe("Task name extracted from the image."),note:z.string().optional().describe("Additional context or plain-text note for the task."),deferDate:z.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset, if detected in the image."),dueDate:z.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset, if detected in the image.")}),Ww=z.discriminatedUnion("kind",[z.object({kind:z.literal("path"),imagePath:z.string().min(1).describe(`Absolute path within attachment-path-scope, with one of the supported image extensions (${pi.join(",")}). Subject to the configured size cap.`).refine(qu,{message:`imagePath must use one of ${pi.join(",")}`})}).describe("Image read from a filesystem path."),z.object({kind:z.literal("attachment"),attachmentId:Oe.schema.describe("Persistent ID of the OF attachment carrying the image."),ownerTaskId:h.schema.optional().describe("Owner task that holds the attachment. Mutually exclusive with ownerProjectId."),ownerProjectId:k.schema.optional().describe("Owner project that holds the attachment. Mutually exclusive with ownerTaskId.")}).describe("Image referenced via an existing OF attachment ID.")]).describe("Image source. attachment requires exactly one owner."),Hw=z.enum(["parent-task","each-task","none"]).default("parent-task").describe("Re-attachment mode after task creation."),Vu=z.object({source:Ww,targetProjectId:k.schema.describe("Project that receives the captured tasks (and the wrapper, if `parent-task` mode)."),proposed:z.array(Gu).min(1).describe("Agent-supplied extraction."),attachSourceTo:Hw,parentTaskName:z.string().min(1).optional().describe("Wrapper parent task name; default 'Captured from image'."),dryRun:z.boolean().default(true).describe("true (default) = preview; false requires confirmation[]."),confirmation:z.array(Gu).optional().describe("Required when dryRun=false. (Possibly-edited) confirmed tasks.")}),zw=Vu.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation[] is required when dryRun is false",path:["confirmation"]}).refine(t=>t.source.kind!=="attachment"||t.source.ownerTaskId!==void 0!=(t.source.ownerProjectId!==void 0),{message:"attachment source requires exactly one of source.ownerTaskId or source.ownerProjectId",path:["source"]}).refine(t=>t.source.kind!=="attachment"||t.attachSourceTo==="none",{message:"attachment-mode source requires attachSourceTo='none' (v1 limitation)",path:["attachSourceTo"]});async function Gw(t,e){if(t.kind==="path")return {kind:"path",imagePath:t.imagePath};let n=t.ownerTaskId?{taskId:t.ownerTaskId}:{projectId:t.ownerProjectId},r=(await e.attachmentService.list(n)).find(o=>o.id===t.attachmentId);if(!r)throw new y(`Attachment not found: ${t.attachmentId}`,{details:{field:"source.attachmentId"}});if(!$w(r))throw new y(`Attachment is not an image: ${r.name}`,{details:{field:"source.attachmentId"}});return {kind:"attachment",attachment:r}}function qw(t,e){return {name:t.name,...e,...t.note!==void 0&&{note:t.note},...t.deferDate!==void 0&&{deferDate:t.deferDate},...t.dueDate!==void 0&&{dueDate:t.dueDate}}}async function Vw(t,e){je(zw,t);let n=await Gw(t.source,e);if(t.dryRun||!t.confirmation)return p({phase:"dryRun",proposed:t.proposed,sourceKind:n.kind},e.makeMeta());let r=t.confirmation,o=n.kind==="path"?n.imagePath:void 0,a,i={projectId:t.targetProjectId};if(t.attachSourceTo==="parent-task"){let l=t.parentTaskName??"Captured from image",m=await e.adapter.createTask({name:l,projectId:t.targetProjectId});o!==void 0&&await e.attachmentService.add({taskId:m,filePath:o}),a={taskId:m,name:l,...o!==void 0&&{attachedSourcePath:o}},i={parentId:m};}let s=await e.adapter.batchCreateTasks(r.map(l=>qw(l,i))),c=[];for(let l of s.succeeded){let m=r[l.index],f;t.attachSourceTo==="each-task"&&o!==void 0&&(await e.attachmentService.add({taskId:l.value,filePath:o}),f=o),c.push({taskId:l.value,name:m?.name??"(unknown)",...f!==void 0&&{attachedSourcePath:f}});}e.cache!==void 0&&s.succeeded.length>0&&b(e.cache,{projectId:t.targetProjectId});let d=e.makeMeta({syncPending:s.succeeded.length>0});return p({phase:"created",parent:a,created:c,outcome:s},d)}function Ku(t,e){return t.registerTool("task_extract_from_image",{description:ui,inputSchema:Vu.shape},async n=>{let r=await Vw(n,e);return u(r)})}var Kw=["add","build","call","check","create","draft","email","file","finish","fix","follow up","plan","prepare","research","review","schedule","send","set up","start","write"],Xw=/^\s*(?:\(\d+\)|\d+[.)])\s+(.+)$/,Yw=/^\s*(?:[-*•–—])\s+(.+)$/,Zw=/^\s*(?:✅|⏰|📝|📌|✏️|📞|📧|📤|📩|🔔|🚨|⚡️|➡️|→)\s+(.+)$/u;function Qw(t){return t.replace(/[\s.;,:]+$/,"").trim()}function eS(t){let e=t.map(n=>n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(String.raw`^\s*(?:(?:i (?:should|will|need to|must)|please|we (?:need to|should))\s+)?(${e.join("|")})\b\s+(.+)$`,"i")}function Xu(t,e={}){let n=e.verbs??Kw,r=eS(n),o=[],a=[],i=t.replace(/\r\n?/g,`
140
226
  `).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=`/**
227
+ `);for(let s=0;s<i.length;s++){let c=s+1,d=i[s]??"";if(!d.trim())continue;let l=Xw.exec(d),m=l?null:Yw.exec(d),f=!l&&!m?Zw.exec(d):null,g=!l&&!m&&!f?r.exec(d):null,v=null;if(l)v=l[1]??null;else if(m)v=m[1]??null;else if(f)v=f[1]??null;else if(g){let T=g[1]??"",E=g[2]??"";v=T&&E?`${T} ${E}`:null;}if(v===null){a.push(`L${c}: ${d.trim()}`);continue}let I=Qw(v);if(!I){a.push(`L${c}: ${d.trim()}`);continue}o.push({name:I,sourceLines:[c]});}return {proposed:o,unmappedLines:a}}var mi=`Mechanically split prose into a candidate-task list with source-line provenance. Source can be a task's note (kind: 'task'), a project's note (kind: 'project'), or inline text (kind: 'inline') \u2014 useful for piping a transcript through capture-meeting. Two-phase contract: dryRun=true returns { proposed, unmappedLines }; dryRun=false with confirmation: ProposedTask[] creates the (possibly-edited) tasks in targetProjectId via batchCreateTasks semantics. Returns { phase: 'dryRun', proposed, unmappedLines } or { phase: 'created', outcome: BatchOutcome<TaskId> } accordingly. Do NOT use this tool when you already have structured tasks \u2014 call task_batch_create directly instead. Prefer this helper when the input is a wall-of-text note that needs splitting. Side effects: dryRun=true is read-only; dryRun=false creates tasks in the target project. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_extract_from_note({ source: { kind: "task", id: "abc123" }, dryRun: true })`,tS=z.object({name:z.string().min(1).describe("Task name."),note:z.string().optional().describe("Optional note body for the created task."),deferDate:z.string().datetime({offset:true}).optional(),dueDate:z.string().datetime({offset:true}).optional(),tags:z.array(z.string()).optional().describe("Tag NAMES \u2014 resolved by the agent before passing here, since this tool does not look up tag IDs."),sourceLines:z.array(z.number().int().nonnegative()).optional().describe("1-based source line numbers from the original prose; preserved when surfacing proposals to the user.")}),nS=z.discriminatedUnion("kind",[z.object({kind:z.literal("task"),taskId:h.schema.describe("Task whose note will be parsed.")}),z.object({kind:z.literal("project"),projectId:k.schema.describe("Project whose note will be parsed.")}),z.object({kind:z.literal("inline"),text:z.string().min(1).describe("Raw prose to parse \u2014 agent supplies directly.")})]).describe("Where to read prose from."),Yu=z.object({source:nS,targetProjectId:k.schema.describe("Project that will receive created tasks on dryRun=false. Read-only on dryRun=true."),dryRun:z.boolean().default(true).describe("Default true \u2014 return proposals without creating. false requires confirmation[]."),confirmation:z.array(tS).optional().describe("Required when dryRun is false. The (possibly-edited) ProposedTask[] the agent has confirmed with the user.")}),rS=Yu.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation[] is required when dryRun is false",path:["confirmation"]});async function oS(t,e){return t.kind==="inline"?t.text:t.kind==="task"?(await e.getTask(t.taskId)).note??"":(await e.getProject(t.projectId)).note??""}async function aS(t,e){je(rS,t);let n=await oS(t.source,e.adapter),r=Xu(n);if(t.dryRun||!t.confirmation){let s=e.makeMeta();return p({phase:"dryRun",proposed:r.proposed,unmappedLines:r.unmappedLines},s)}let o=t.confirmation.map(s=>({name:s.name,projectId:t.targetProjectId,...s.note!==void 0&&{note:s.note},...s.deferDate!==void 0&&{deferDate:s.deferDate},...s.dueDate!==void 0&&{dueDate:s.dueDate}})),a=await e.adapter.batchCreateTasks(o);e.cache!==void 0&&a.succeeded.length>0&&b(e.cache,{projectId:t.targetProjectId});let i=e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:bt(a.succeeded.length)});return p({phase:"created",outcome:a},i)}function Zu(t,e){return t.registerTool("task_extract_from_note",{description:mi,inputSchema:Yu.shape},async n=>{let r=await aS(n,e);return u(r)})}var fi='Find tasks in OmniFocus by name. Returns ALL matching tasks (names are not unique in OmniFocus). Names collide in OmniFocus; prefer task_get with an ID when you have one. Use search_query instead when you need to search task notes as well, or want full-text content search. Zero matches returns an empty array \u2014 not an error. Returns tasks[]; safe to call repeatedly; no side effects. Example: task_find_by_name({ name: "Buy milk" }) Example: task_find_by_name({ name: "report", matchMode: "contains" })',sS=z.object({query:z.string().min(1).describe("Name to search for. Behaviour depends on mode: exact = full name match; prefix = name starts with this string; contains = substring match anywhere in name."),mode:z.enum(["exact","prefix","contains"]).optional().describe("'exact' = full task name must match (default); 'prefix' = name must start with query; 'contains' = query appears anywhere in name."),caseSensitive:z.boolean().optional().describe("true = match is case-sensitive; false = case-insensitive (default false)."),limit:z.number().int().min(1).max(500).optional().describe("Maximum number of results to return (1..500). Default 50.")});async function iS(t,e){let n=t.mode??"exact",r=t.caseSensitive??false,o=t.limit??50,a=await e.adapter.listTasks({}),i=m=>r?m:m.toLowerCase(),s=i(t.query),c=a.filter(m=>{let f=i(m.name);switch(n){case "exact":return f===s;case "prefix":return f.startsWith(s);case "contains":return f.includes(s);default:return false}}),d=c.slice(0,o),l=e.makeMeta();return p({tasks:d,matchCount:c.length},l)}function Qu(t,e){return t.registerTool("task_find_by_name",{description:fi,inputSchema:sS.shape},async n=>{let r=await iS(n,e);return u(r)})}var gi=`Lexical nearest-neighbour search for de-duplicating tasks. Pass a candidate name (and optional note) and receive the top-K most-similar existing tasks ranked by a deterministic [0, 1] lexical-signal score (Jaccard token-overlap + prefix bonus + exact-name boost). Title-dominant: a perfect title match outranks a perfect note match. Use BEFORE task_create when you suspect a duplicate; the agent inspects the candidates and decides whether to create new, link to existing, or merge. Excludes completed and dropped tasks by default; opt-in via includeCompleted: true. Optional scope { projectId } or { tagId } narrows the candidate set. Returns { candidates: [{ taskId, name, score, project, tags }] } sorted by score descending \u2014 project is { id, name } | null and tags is [{ id, name }, ...]. Names are paired alongside ids via a single getProjectsMany + single getTagsMany batch (no N+1) so the agent can describe each candidate without a follow-up read. An empty result is { candidates: [] }, not an error. Do NOT use this tool for general full-text search \u2014 call task_search for that. Prefer this helper when the question is 'is this task already in the system?'. No model calls; no side effects. Read-only. Example: task_find_similar({ name: "Call dentist" }) Example: task_find_similar({ name: "Write report", scope: { projectId: "prj123" }, topK: 5 })`,em=5,tm=50,cS=z.object({projectId:k.schema.optional(),tagId:S.schema.optional()}).refine(t=>!(t.projectId!==void 0&&t.tagId!==void 0),{message:"Supply at most one of projectId or tagId"}),dS=z.object({name:z.string().min(1).describe("The candidate task name to compare against existing tasks."),note:z.string().optional().describe("Optional note text. When both the candidate and an existing task have a note, note overlap contributes to the score as a tiebreaker."),scope:cS.optional().describe("Narrow the candidate set to one project or one tag. Mutually exclusive \u2014 supply at most one. Omit to search all open tasks."),limit:z.number().int().min(1).max(tm).default(em).describe(`Top-K candidates to return. Default ${em}, max ${tm}.`),includeCompleted:z.boolean().default(false).describe("When true, include completed and dropped tasks. Default false (open tasks only).")});async function lS(t,e){let n=t.includeCompleted?{}:{completed:false};t.scope?.projectId!==void 0&&(n.projectId=t.scope.projectId),t.scope?.tagId!==void 0&&(n.tagId=t.scope.tagId);let r=await e.adapter.listTasks(n),o={name:t.name,...t.note!==void 0&&{note:t.note}},a=r.map(I=>({task:I,s:Jc(o,{name:I.name,note:I.note})})).filter(I=>I.s>0).sort((I,T)=>T.s-I.s).slice(0,t.limit),i=new Set,s=new Set;for(let{task:I}of a){I.projectId!==null&&i.add(I.projectId);for(let T of I.tagIds)s.add(T);}let c=[...i],d=[...s],[l,m]=await Promise.all([c.length>0?e.adapter.getProjectsMany(c):Promise.resolve([]),d.length>0?e.adapter.getTagsMany(d):Promise.resolve([])]),f=new Map;c.forEach((I,T)=>{let E=l[T];E!=null&&f.set(String(I),E.name);});let g=new Map;d.forEach((I,T)=>{let E=m[T];E!=null&&g.set(String(I),E.name);});let v=a.map(({task:I,s:T})=>{let E=I.projectId===null?null:String(I.projectId),w=E===null?null:f.get(E)??null;return {taskId:String(I.id),name:I.name,score:T,project:E===null||w===null?null:{id:E,name:w},tags:I.tagIds.map(D=>{let j=String(D),B=g.get(j);return B===void 0?null:{id:j,name:B}}).filter(D=>D!==null)}});return p({candidates:v},e.makeMeta())}function nm(t,e){return t.registerTool("task_find_similar",{description:gi,inputSchema:dS.shape},async n=>{let r=await lS(n,e);return u(r)})}var hi='Fetch a single OmniFocus task by persistent ID. Use when you have a known task ID and need its full detail. Do NOT use for multiple IDs \u2014 use task_get_many instead. Returns the Task object plus its direct subtasks (when includeSubtasks=true, the default). Read-only; safe to retry. Example: task_get({ id: "abc123" })',pS=z.object({id:h.schema.describe("Persistent ID of the task to fetch. Get from task_list or task_get_many."),includeSubtasks:z.boolean().optional().describe("Include direct subtasks in the response. Default true.")});async function uS(t,e){let n=await e.taskService.get(t),r=Tt(n.task.note),o=Ae(n.task.note);return p({task:n.task,...n.subtasks!==void 0&&{subtasks:n.subtasks},...r!==void 0&&{waitingOn:r},...o!==void 0&&{decision:o}},e.makeMeta({cacheHit:n.cacheHit}))}function om(t,e){return t.registerTool("task_get",{description:hi,inputSchema:pS.shape},async n=>{let r=await uS(n,e);return u(r)})}var yi='Fetch up to 100 tasks by persistent ID in a single OmniFocus round-trip. Use when you have a set of task IDs from multiple sources and need full task objects for all of them. Do NOT use for a single ID \u2014 use task_get instead. Do NOT use when you only have names \u2014 use task_find_by_name. Returns Task[] in input order. Missing IDs are omitted and appear in meta.warnings. Read-only; safe to retry. Example: task_get_many({ ids: ["abc123", "abc456"] })',ar=100,mS=z.object({ids:z.array(h.schema).min(0).max(ar).describe(`Array of task IDs to fetch (0..${ar}). Get IDs from task_list, search_query, or task_find_by_name. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function fS(t,e){if(t.ids.length===0)return p({tasks:[]},e.makeMeta());if(t.ids.length>ar)throw new y(`ids array exceeds the maximum batch size of ${ar} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTasksMany(t.ids),r=n.filter(m=>m!==null),o=t.ids.filter((m,f)=>n[f]===null),a={},i={};for(let m of r){let f=Tt(m.note);f!==void 0&&(a[m.id]=f);let g=Ae(m.note);g!==void 0&&(i[m.id]=g);}let s=Object.keys(a).length>0,c=Object.keys(i).length>0,d=o.length>0?[wt(o)]:void 0,l=e.makeMeta({...d!==void 0?{warnings:d}:{}});return p({tasks:r,...s&&{waitingOn:a},...c&&{decisions:i}},l)}function sm(t,e){return t.registerTool("task_get_many",{description:yi,inputSchema:mS.shape},async n=>{let r=await fS(n,e);return u(r)})}var im=z.object({self:z.string(),project:z.string().nullable(),parent:z.string().nullable(),tags:z.array(z.string())});z.object({self:z.string(),folder:z.string().nullable()});function et(t){return {self:`omnifocus://task/${t.id}`,project:t.projectId!==null?`omnifocus://project/${t.projectId}`:null,parent:t.parentId!==null?`omnifocus://task/${t.parentId}`:null,tags:t.tagIds.map(e=>`omnifocus://tag/${e}`)}}function ki(t){return {self:`omnifocus://project/${t.id}`,folder:t.folderId!==null?`omnifocus://folder/${t.folderId}`:null}}function Nt(t){let e=JSON.stringify(Object.fromEntries(Object.entries(t).filter(([,n])=>n!==void 0).sort(([n],[r])=>n.localeCompare(r))));return createHash("sha256").update(e).digest("hex")}function Ft(t){let e=JSON.stringify(t);return Buffer.from(e,"utf8").toString("base64url")}function Ut(t,e){let n;try{n=Buffer.from(t,"base64url").toString("utf8");}catch{throw new y("Cursor is not valid base64url.",{suggestion:"Pass the cursor value exactly as returned by the previous response."})}let r;try{r=JSON.parse(n);}catch{throw new y("Cursor payload is not valid JSON.",{suggestion:"Pass the cursor value exactly as returned by the previous response."})}let o=r;if(typeof r!="object"||r===null||typeof o.lastId!="string"||o.lastSortValue!==null&&typeof o.lastSortValue!="string"||typeof o.filterHash!="string")throw new y("Cursor payload is missing required fields.",{suggestion:"Pass the cursor value exactly as returned by the previous response."});let a=r;if(a.filterHash!==e)throw new y("Cursor filter hash does not match the current query filters. Start a fresh query.",{suggestion:"Call the list tool without a cursor to begin a new page sequence with the updated filters.",details:{cursorFilterHash:a.filterHash,currentFilterHash:e}});return a}function Lt(t,e,n="asc"){let r=t.sortValue,o=e.lastSortValue;return r===null&&o===null?t.id>e.lastId:r===null?n==="asc":o===null?n==="desc":r!==o?n==="asc"?r>o:r<o:t.id>e.lastId}var dm=z.enum(["dueDate","createdAt","modifiedAt","name"]),cm=200,Ii=1e3,sr=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async list(e){let n=this.resolveLimit(e);this.assertBounded(e,n);let r=this.normalize(e),o=Nt(r),a=e.cursor!==void 0?Ut(e.cursor,o):void 0,i=this.cacheKeyFor(o,e.cursor),s=this.cache.has(i),{tasks:c,nextCursor:d}=await this.cache.wrap(i,async()=>this.fetchPage(e,r,a,n,o));return {tasks:c,nextCursor:d,hasMore:d!==null,cacheHit:s}}async get(e){let n=e.includeSubtasks??true,r=`task:${e.id}:${n?"with-subtasks":"solo"}`,o=this.cache.has(r);return {...await this.cache.wrap(r,async()=>{let i=await this.adapter.getTask(e.id),s={...i,_links:et(i)};if(!n)return {task:s};let d=(await this.adapter.listTasks({parentId:e.id})).map(l=>({...l,_links:et(l)}));return {task:s,subtasks:d}}),cacheHit:o}}async fetchPage(e,n,r,o,a){let i=this.toAdapterFilter(e,n),s=await this.adapter.listTasks(i),c=n.tagIds.length>1?s.filter(j=>n.tagIds.every(B=>j.tagIds.includes(B))):s,{updatedSince:d}=n,l=d!==void 0?c.filter(j=>j.modifiedAt>d):c,{sortBy:m,sortDirection:f}=n,g=j=>{switch(m){case "dueDate":return j.dueDate??null;case "modifiedAt":return j.modifiedAt;case "name":return j.name;default:return j.createdAt}},v=[...l].sort((j,B)=>{let _=g(j),U=g(B);if(_===null&&U===null)return j.id<B.id?-1:1;if(_===null)return 1;if(U===null)return -1;if(_!==U){let M=_<U?-1:1;return f==="asc"?M:-M}return j.id<B.id?-1:1}),I=r!==void 0?v.filter(j=>Lt({id:j.id,sortValue:g(j)},r,f)):v,T=I.slice(0,o),w=I.length>o?this.encodeNextCursor(T,a,g):null;return {tasks:T.map(j=>({...j,_links:et(j)})),nextCursor:w}}resolveLimit(e){if(e.limit===void 0)return cm;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>Ii)throw new y(`limit must be an integer between 1 and ${Ii}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${Ii}, or omit to use the default of ${cm}.`,details:{field:"limit",value:e.limit}});return e.limit}assertBounded(e,n){if(!(e.limit!==void 0||e.cursor!==void 0)&&!this.hasAnyFilter(e))throw new y("task_list requires at least one filter, limit, or cursor. Unbounded queries are rejected.",{suggestion:"Provide a filter or a limit.",details:{field:"filter|limit|cursor"}})}hasAnyFilter(e){return e.projectId!==void 0||e.tagIds!==void 0&&e.tagIds.length>0||e.flagged!==void 0||e.available!==void 0||e.completed!==void 0||e.dueBefore!==void 0||e.dueAfter!==void 0||e.deferredBefore!==void 0||e.parentId!==void 0||e.updatedSince!==void 0||e.inbox===true}normalize(e){let n=e.tagIds!==void 0?[...new Set(e.tagIds)].sort((o,a)=>o.localeCompare(a)):[],r;if(e.updatedSince!==void 0)if(Kt(e.updatedSince))r=e.updatedSince;else if(qt(e.updatedSince))r=Vt(e.updatedSince);else throw new y(`updatedSince must be an ISO-8601 timestamp with offset or a relative shortcut (today, yesterday, this-week, next-week, end-of-week, end-of-month). Got: "${e.updatedSince}".`,{details:{field:"updatedSince",value:e.updatedSince},suggestion:"Pass an ISO-8601 string with offset (e.g. '2026-04-21T10:00:00-07:00') or a shortcut like 'today'."});if(e.inbox&&(e.projectId!==void 0||e.parentId!==void 0))throw new y("inbox filter cannot be combined with projectId or parentId \u2014 inbox tasks have no project assignment.",{suggestion:"Remove projectId/parentId when filtering by inbox.",details:{field:"inbox"}});return {projectId:e.projectId,tagIds:n,flagged:e.flagged,available:e.available,completed:e.completed,dueBefore:e.dueBefore,dueAfter:e.dueAfter,deferredBefore:e.deferredBefore,parentId:e.parentId,sortBy:e.sortBy??"createdAt",sortDirection:e.sortDirection??"asc",updatedSince:r,inbox:e.inbox}}toAdapterFilter(e,n){let r={};if(n.projectId!==void 0&&(r.projectId=n.projectId),n.parentId!==void 0&&(r.parentId=n.parentId),n.tagIds.length===1){let o=n.tagIds[0];o!==void 0&&(r.tagId=o);}return n.flagged!==void 0&&(r.flagged=n.flagged),n.available!==void 0&&(r.available=n.available),n.dueBefore!==void 0&&(r.dueBefore=n.dueBefore),n.dueAfter!==void 0&&(r.dueAfter=n.dueAfter),n.deferredBefore!==void 0&&(r.deferredBefore=n.deferredBefore),n.completed==="only"?r.completed=true:n.completed==="exclude"&&(r.completed=false),n.inbox===true&&(r.inbox=true),r}cacheKeyFor(e,n){return `search:tasks:${e}:${n??"first"}`}encodeNextCursor(e,n,r){let o=e[e.length-1];if(o===void 0)throw new y("Internal: cannot encode cursor for empty page.");return Ft({lastId:o.id,lastSortValue:r(o),filterHash:n})}};var vi='List tasks in OmniFocus with optional filters (project, tag, inbox, flagged, completion, due dates). Use inbox=true to fetch unprocessed Inbox tasks. Use this for filter-based queries across tasks. Do NOT use for a known single task (use task_get). For name-based lookup, prefer task_find_by_name. For full-text content search across names and notes, prefer search_query. Returns tasks[] with pagination; safe to call repeatedly; no side effects. Example: task_list({ inbox: true }) Example: task_list({ projectId: "prj123", flagged: true }) Example: task_list({ dueBefore: "2026-05-01T00:00:00Z", completed: "exclude" })',yS=z.object({projectId:k.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list. Omit for all projects."),tagIds:z.array(S.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs. Get IDs from tag_list."),flagged:z.boolean().optional().describe("true = flagged only; false = unflagged only; omit = all."),available:z.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred). Omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed tasks only; 'any' = both. Omit for adapter default."),dueBefore:z.string().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset (e.g. '2026-04-21T17:00:00-04:00')."),dueAfter:z.string().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset."),deferredBefore:z.string().optional().describe("Tasks deferred until before this moment (already unlocked or soon). ISO-8601 with offset."),parentId:h.schema.optional().describe("Restrict to direct children of this task (subtasks). Get the ID from task_get or task_list."),limit:z.number().int().min(1).max(1e3).optional().describe("Max tasks per page (1..1000). Default 200. Use `cursor` to fetch subsequent pages."),sortBy:dm.optional().describe("Field to sort tasks by: 'createdAt' (default), 'dueDate', 'modifiedAt', or 'name'. Tasks with no value for the chosen field (e.g. no dueDate) sort last."),sortDirection:z.enum(["asc","desc"]).optional().describe("Sort direction: 'asc' (default, oldest/lowest first) or 'desc' (newest/highest first)."),updatedSince:Le().optional().describe("Return only tasks modified strictly after this timestamp. Accepts ISO-8601 with offset (e.g. '2026-04-21T10:00:00-07:00') or a relative shortcut: today, yesterday, this-week, next-week, end-of-week, end-of-month. Use this for incremental sync: call without updatedSince on session start, then pass the previous response timestamp on subsequent calls. Note: deleted tasks cannot be detected \u2014 use a snapshot resource for deletion detection."),inbox:z.boolean().optional().describe("true = Inbox tasks only (no project assignment). Cannot be combined with projectId or parentId. Use this to surface unprocessed captures without knowing their IDs."),cursor:z.string().optional().describe("Opaque cursor from a previous task_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")});async function kS(t,e){let n=t,r=await e.taskService.list(n),o={cursor:r.nextCursor,hasMore:r.hasMore},a=e.makeMeta({cacheHit:r.cacheHit});return p({tasks:r.tasks},a,o)}function lm(t,e){return t.registerTool("task_list",{description:vi,inputSchema:yS.shape},async n=>{let r=await kS(n,e);return u(r)})}var wi='Move an OmniFocus task to a new location \u2014 a different project, another task (as a subtask), or the inbox. Exactly one destination must be specified: projectId, parentId, or toInbox: true. Do NOT use task_move to reorder siblings within the same parent (task_reorder handles that); prefer task_update when you only need to change editable fields, not reparent. Idempotent: returns noChange: true when the task is already at the destination. Returns { moved: true, id, from, to } or { noChange: true, id, at }. Side effects: reparents the task in OmniFocus, sets meta.syncPending = true. Example: task_move({ id: "abc123", projectId: "prj456" }) Example: task_move({ id: "abc123", parentId: "tsk789" })',Si=z.object({id:h.schema.describe("Persistent ID of the task to move."),projectId:k.schema.optional().describe("Move into this project. Mutually exclusive with parentId and toInbox."),parentId:h.schema.optional().describe("Move under this parent task (as a subtask). Mutually exclusive with projectId and toInbox."),toInbox:z.literal(true).optional().describe("Set to true to move the task to the inbox (clear any project or parent). Mutually exclusive with projectId and parentId.")}).describe("Exactly one of projectId, parentId, or toInbox must be set. Any other combination returns a ValidationError.");async function IS(t,e){let n=(t.projectId!==void 0?1:0)+(t.parentId!==void 0?1:0)+(t.toInbox===true?1:0);if(n!==1)throw new y("task_move requires exactly one of projectId, parentId, or toInbox",{details:{field:"projectId|parentId|toInbox",provided:n},suggestion:"Set exactly one destination field \u2014 see tool schema."});let r=await e.adapter.getTask(t.id),o=t.projectId!==void 0&&r.projectId===t.projectId&&r.parentId===null,a=t.parentId!==void 0&&r.parentId===t.parentId&&r.projectId===null,i=t.toInbox===true&&r.projectId===null&&r.parentId===null;if(o||a||i)return p({noChange:true,id:t.id,at:Ti(r.projectId,r.parentId)},e.makeMeta());let s=Ti(r.projectId,r.parentId),c=t.projectId!==void 0?{projectId:t.projectId}:t.parentId!==void 0?{parentId:t.parentId}:{};await e.adapter.moveTask(t.id,c),e.cache!==void 0&&(b(e.cache,{taskId:t.id,projectId:r.projectId}),t.projectId!==void 0&&t.projectId!==r.projectId&&b(e.cache,{projectId:t.projectId}));let d=t.toInbox===true?{inbox:true}:Ti(t.projectId??null,t.parentId??null);return p({moved:true,id:t.id,from:s,to:d},e.makeMeta({syncPending:true,humanReadableSummary:vd(r.name,t.toInbox===true?"inbox":t.projectId!=null?"project":"parent task")}))}function Ti(t,e){return e!==null?{parentId:e}:t!==null?{projectId:t}:{inbox:true}}function um(t,e){return t.registerTool("task_move",{description:wi,inputSchema:Si.shape},async n=>{let r=await IS(n,e);return u(r)})}var bi="Preview what task_move would do without making any changes. Do NOT use to actually move a task \u2014 use task_move instead. Returns { description, plannedChanges } describing the reparenting that would occur. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function vS(t,e){let n=[],r=String(t.id);try{r=(await e.adapter.getTask(t.id)).name;}catch{}let o;if(t.projectId!==void 0){let i=await _t(e.adapter,t.projectId);n.push({field:"projectId",newValue:t.projectId}),o=`project '${i}'`;}else if(t.parentId!==void 0){let i=await Pt(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`subtask of '${i}'`;}else n.push({field:"location",newValue:"inbox"}),o="Inbox";let a=`Would move task '${r}' to ${o}.`;return p({description:a,plannedChanges:n},e.makeMeta())}function mm(t,e){return t.registerTool("task_move_describe",{description:bi,inputSchema:Si.shape},async n=>{let r=await vS(n,e);return u(r)})}function ji(t){let e=(g,v=2)=>String(g).padStart(v,"0"),n=t.getFullYear(),r=e(t.getMonth()+1),o=e(t.getDate()),a=e(t.getHours()),i=e(t.getMinutes()),s=e(t.getSeconds()),c=-t.getTimezoneOffset(),d=c>=0?"+":"-",l=Math.abs(c),m=e(Math.floor(l/60)),f=e(l%60);return `${n}-${r}-${o}T${a}:${i}:${s}${d}${m}:${f}`}function fm(t,e,n){let r=t.toLowerCase(),o=new Date;if(r==="today"){let i=new Date(o.getFullYear(),o.getMonth(),o.getDate(),0,0,0);return {value:ji(i)}}if(r==="tomorrow"){let i=new Date(o.getFullYear(),o.getMonth(),o.getDate()+1,0,0,0);return {value:ji(i)}}if(/^\d{4}-\d{2}-\d{2}(T.*)?$/.test(t)){let i=new Date(t);if(!Number.isNaN(i.getTime())){if(/^\d{4}-\d{2}-\d{2}$/.test(t)){let s=t.split("-"),c=Number(s[0]),d=Number(s[1]),l=Number(s[2]),m=new Date(c,d-1,l,0,0,0);return {value:ji(m)}}return {value:t}}}return {value:t,warning:`Line ${e}: ${n} date '${t}' is not a recognized date format; passing through as-is`}}function gm(t){let e=t.split(`
228
+ `),n=[],r=[],o;for(let a=0;a<e.length;a++){let i=a+1,s=(e[a]??"").trim();if(s==="")continue;if(/^Project:\s*/i.test(s)){o=s.replace(/^Project:\s*/i,"").trim()||void 0;continue}let c=s,d=[],l,m,f=false,g,v=c.indexOf("//");v!==-1&&(g=c.slice(v+2).trim(),c=c.slice(0,v).trim());let I=c.split(/\s+/).filter(D=>D.length>0),T=[];for(let D of I)if(D==="!!")f=true;else if(D.startsWith("::")){let j=D.slice(2);if(j.length>0){let{value:B,warning:_}=fm(j,i,"Defer");m=B,_&&r.push(_);}}else if(D.startsWith("@")){let j=D.slice(1);j.length>0&&d.push(j);}else if(D.startsWith("#")){let j=D.slice(1);if(j.length>0){let{value:B,warning:_}=fm(j,i,"Due");l=B,_&&r.push(_);}}else T.push(D);let E=T.join(" ").trim();if(E===""){r.push(`Line ${i}: task line has no name after removing tokens; skipping`);continue}let w={name:E};g!==void 0&&g.length>0&&(w.note=g),f&&(w.flagged=true),l!==void 0&&(w.dueDate=l),m!==void 0&&(w.deferDate=m),d.length>0&&(w.tagNames=d),o!==void 0&&(w.projectName=o),n.push(w);}return {tasks:n,warnings:r}}var _i=`Parse OmniFocus transport text DSL into structured task objects \u2014 no tasks are created. Supports @tag, #due-date, ::defer-date, !!, and //note tokens; a leading 'Project: Name' line sets the project context for subsequent tasks. Do not use this tool to create tasks; pass the returned tasks[] to task_create separately. Returns tasks[] with name, tagNames, dueDate, deferDate, flagged, note, and projectName fields, plus count and an optional warnings[] for unparseable dates. Tag names and project names are raw strings \u2014 resolve to IDs with tag_list before passing to task_create. Read-only; no side effects. Example: task_parse_transport_text({ text: "Buy milk @errands !!\\nWrite report #2026-05-01" })`,TS=z.object({text:z.string().min(1).describe("Transport text to parse. One task per line; 'Project: Name' prefix sets project context.")});async function wS(t,e){let n=gm(t.text),r=e.makeMeta();return p({tasks:n.tasks,count:n.tasks.length,...n.warnings.length>0&&{warnings:n.warnings}},r)}function ym(t,e){return t.registerTool("task_parse_transport_text",{description:_i,inputSchema:TS.shape},async n=>{let r=await wS(n,e);return u(r)})}function gn(t,e){switch(t.kind){case "title-contains":{let n=t.caseSensitive?e.name:e.name.toLowerCase(),r=t.caseSensitive?t.value:t.value.toLowerCase();return n.includes(r)}case "tag":return e.tagIds.some(n=>String(n)===String(t.tagId));case "project":return e.projectId!==null&&String(e.projectId)===String(t.projectId);case "and":return t.predicates.every(n=>gn(n,e));case "or":return t.predicates.some(n=>gn(n,e));case "not":return !gn(t.predicate,e)}}var Oi=`Predicate-driven bulk task reclassification with a mandatory two-phase contract. Phase 1 (dryRun: true): match tasks by predicate, return { matched, proposed: [{taskId, before, after}] } with no mutations. Phase 2 (dryRun: false): require \`confirmation\` echoing the matched count from the prior dry-run; mismatch fails fast. Hard cap: dryRun: false rejects > 200 matches (use task_batch_update with explicit IDs for larger sets). Predicate is a discriminated-union AST: { kind: 'title-contains', value, caseSensitive? } | { kind: 'tag', tagId } | { kind: 'project', projectId } | { kind: 'and', predicates: [] } | { kind: 'or', predicates: [] } | { kind: 'not', predicate }. Changes apply uniformly to every match: addTags, removeTags, setProject, setFlagged. Do NOT use this tool when you have explicit task IDs \u2014 call task_batch_update directly. Prefer task_reclassify whenever the targets are described by a rule rather than a list, so the dry-run diff surfaces to the user before any write. Side effects (apply phase only): writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_reclassify({ predicate: { kind: "tag", tagId: "tag123" }, changes: { setFlagged: true }, dryRun: true }) Example: task_reclassify({ predicate: { kind: "tag", tagId: "tag123" }, changes: { setFlagged: true }, dryRun: false, confirmation: "3" })`,Pi=200,ir=z.lazy(()=>z.discriminatedUnion("kind",[z.object({kind:z.literal("title-contains"),value:z.string().describe("Substring to search for in task names."),caseSensitive:z.boolean().optional().describe("When true, exact-case match. Default false (case-insensitive).")}),z.object({kind:z.literal("tag"),tagId:S.schema.describe("Match tasks carrying this tag.")}),z.object({kind:z.literal("project"),projectId:k.schema.describe("Match tasks in this project.")}),z.object({kind:z.literal("and"),predicates:z.array(ir).describe("All children must match.")}),z.object({kind:z.literal("or"),predicates:z.array(ir).describe("Any child match suffices.")}),z.object({kind:z.literal("not"),predicate:ir.describe("Inverts the inner predicate's result.")})])),SS=z.object({addTags:z.array(S.schema).optional().describe("Tag IDs to add to every match."),removeTags:z.array(S.schema).optional().describe("Tag IDs to remove from every match."),setProject:k.schema.optional().describe("Move every match to this project."),setFlagged:z.boolean().optional().describe("Set the flagged state on every match.")}).refine(t=>t.addTags!==void 0||t.removeTags!==void 0||t.setProject!==void 0||t.setFlagged!==void 0,{message:"changes must set at least one of addTags, removeTags, setProject, or setFlagged"}),km=z.object({predicate:ir.describe("AST for selecting tasks. Composable via and/or/not. Always evaluated against open (non-completed, non-dropped) tasks."),changes:SS.describe("Changes applied uniformly to every matched task."),dryRun:z.boolean().default(true).describe("Default true \u2014 return the diff without mutating. false requires `confirmation` echoing the matched count from a prior dry-run."),confirmation:z.string().optional().describe('When dryRun is false, the matched count from the most recent dry-run, as a string (e.g. "42"). Mismatch with the actual current match count fails the call fast.')}),bS=km.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation is required when dryRun is false",path:["confirmation"]});function jS(t,e){let n=e.setProject!==void 0?String(e.setProject):t.projectId===null?null:String(t.projectId),r=xs(t.tagIds,e.addTags,e.removeTags).map(String),o=e.setFlagged!==void 0?e.setFlagged:t.flagged;return {projectId:n,tagIds:r,flagged:o}}function _S(t){return {projectId:t.projectId===null?null:String(t.projectId),tagIds:t.tagIds.map(String),flagged:t.flagged}}async function PS(t,e){je(bS,t);let r=(await e.adapter.listTasks({completed:false})).filter(s=>gn(t.predicate,s));if(t.dryRun){let s=r.map(c=>({taskId:String(c.id),name:c.name,before:_S(c),after:jS(c,t.changes)}));return p({phase:"dryRun",matched:r.length,proposed:s},e.makeMeta())}let o=String(r.length);if(t.confirmation!==o)return p({phase:"stale-confirmation",matched:r.length,confirmation:t.confirmation,message:`confirmation must equal the current match count (${o}); the underlying task set may have changed since the dry-run`},e.makeMeta());if(r.length>Pi)return p({phase:"over-cap",matched:r.length,cap:Pi,message:`${r.length} matches exceeds the ${Pi}-task hard cap; use task_batch_update with explicit IDs for larger sets`},e.makeMeta());let a=r.map(s=>({taskId:h.of(String(s.id)),...t.changes.setProject!==void 0&&{projectId:t.changes.setProject},...t.changes.addTags!==void 0&&{addTagIds:t.changes.addTags},...t.changes.removeTags!==void 0&&{removeTagIds:t.changes.removeTags},...t.changes.setFlagged!==void 0&&{flagged:t.changes.setFlagged}}));if(a.length===0)return p({phase:"applied",matched:0,assigned:[],failed:[]},e.makeMeta());let i=await Ds({assignments:a},e);return "data"in i?p({phase:"applied",matched:r.length,assigned:i.data.assigned,failed:i.data.failed},e.makeMeta({syncPending:i.data.assigned.length>0})):i}function Im(t,e){return t.registerTool("task_reclassify",{description:Oi,inputSchema:km.shape},async n=>{let r=await PS(n,e);return u(r)})}var xi='Reorder an OmniFocus task among its siblings. OmniFocus has no numeric sibling index \u2014 position is always expressed relative to another task (before / after) or as the absolute start / end of a container. Do NOT use task_reorder to reparent a task to a different project or parent (task_move handles reparenting); prefer task_move when the task needs to change containers without caring about sibling order. Exactly one positioning form must be set: { before }, { after }, or { at, in }. Returns { reordered: true, id, position }. Side effects: changes sibling order in OmniFocus, sets meta.syncPending = true. Example: task_reorder({ id: "abc123", before: "abc456" }) Example: task_reorder({ id: "abc123", at: "start", in: { projectId: "prj456" } })',OS=z.union([z.object({projectId:k.schema}),z.object({parentId:h.schema}),z.object({inbox:z.literal(true)})]).describe("Container for start/end positioning. Exactly one of projectId, parentId, or inbox: true."),xS=z.object({id:h.schema.describe("Persistent ID of the task to reorder."),before:h.schema.optional().describe("Position the task immediately before this sibling. Reference must share the same parent."),after:h.schema.optional().describe("Position the task immediately after this sibling. Reference must share the same parent."),at:z.enum(["start","end"]).optional().describe("Absolute position within a container. Requires `in` to identify the container."),in:OS.optional().describe("Required when `at` is set; ignored otherwise.")}).describe("Exactly one positioning form: { before }, { after }, or { at, in }. Any other combination returns a ValidationError.");async function DS(t,e){let n=(t.before!==void 0?1:0)+(t.after!==void 0?1:0)+(t.at!==void 0?1:0);if(n!==1)throw new y("task_reorder requires exactly one positioning form: { before }, { after }, or { at, in }",{details:{field:"before|after|at",provided:n},suggestion:"Set exactly one positioning field."});if(t.at!==void 0&&t.in===void 0)throw new y("task_reorder: `in` is required when `at` is set",{details:{field:"in"},suggestion:"Provide `in: { projectId } | { parentId } | { inbox: true }`."});if(t.at===void 0&&t.in!==void 0)throw new y("task_reorder: `in` is only valid alongside `at`",{details:{field:"in"},suggestion:"Either add `at: 'start' | 'end'` or remove `in`."});let r=await e.adapter.getTask(t.id),o=r.projectId,a;if(t.before!==void 0)a={before:t.before};else if(t.after!==void 0)a={after:t.after};else if(t.at!==void 0&&t.in!==void 0)a={at:t.at,in:t.in};else throw new y("task_reorder: no positioning form matched",{details:{field:"before|after|at"}});return await e.adapter.reorderTask(t.id,a),e.cache!==void 0&&(b(e.cache,{taskId:t.id,projectId:o}),t.at!==void 0&&t.in!==void 0&&"projectId"in t.in&&t.in.projectId!==o&&b(e.cache,{projectId:t.in.projectId})),p({reordered:true,id:t.id,position:a},e.makeMeta({syncPending:true,humanReadableSummary:wd(r.name)}))}function vm(t,e){return t.registerTool("task_reorder",{description:xi,inputSchema:xS.shape},async n=>{let r=await DS(n,e);return u(r)})}var Di='Search OmniFocus tasks by keyword and/or structured filters, with cursor pagination. q is optional \u2014 omit it to filter by tag, project, date range, or availability alone. When q is supplied, scans task names and/or notes (controlled by scope) for a case-insensitive substring match. Narrow results with: projectId, tagIds (task must carry ALL listed tags), available, dueBefore, dueAfter, flagged, and completed. At least one of q, projectId, tagIds, available, dueBefore, or dueAfter must be provided. Do NOT use when you already have an ID \u2014 prefer task_get instead. Returns tasks[] with pagination (limit defaults to 100, max 500); safe to call repeatedly; no side effects. Example: task_search({ q: "dentist" }) Example: task_search({ tagIds: ["tag123"], available: true, dueBefore: "2026-05-01T00:00:00Z" })',Tm={q:z.string().min(1).optional().describe("Search query. Case-insensitive substring match applied to the fields in scope. Optional \u2014 omit to filter by tags, project, date range, or availability alone."),scope:z.enum(["name","note","all"]).optional().describe("'name' = search task name only; 'note' = search note only; 'all' = both (default). Ignored when q is omitted."),projectId:k.schema.optional().describe("Restrict search to tasks within this project."),tagIds:z.array(S.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs."),available:z.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred, not completed). Omit = all."),dueBefore:Le().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset or relative shortcut."),dueAfter:Le().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset or relative shortcut."),flagged:z.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only (default); 'only' = completed tasks only; 'any' = both."),limit:z.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 100. Use cursor to fetch subsequent pages."),cursor:z.string().optional().describe("Opaque cursor from a previous task_search response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")},RS=z.object(Tm).refine(t=>t.q!==void 0||t.projectId!==void 0||t.tagIds!==void 0||t.available!==void 0||t.dueBefore!==void 0||t.dueAfter!==void 0,{message:"At least one of q, projectId, tagIds, available, dueBefore, or dueAfter must be provided."});async function AS(t,e){je(RS,t);let n=await e.searchService.search({...t.q!==void 0&&{q:t.q},...t.scope!==void 0&&{scope:t.scope},...t.projectId!==void 0&&{projectId:t.projectId},...t.tagIds!==void 0&&{tagIds:t.tagIds},...t.available!==void 0&&{available:t.available},...t.dueBefore!==void 0&&{dueBefore:t.dueBefore},...t.dueAfter!==void 0&&{dueAfter:t.dueAfter},...t.flagged!==void 0&&{flagged:t.flagged},...t.completed!==void 0&&{completed:t.completed},...t.limit!==void 0&&{limit:t.limit},...t.cursor!==void 0&&{cursor:t.cursor}}),r={cursor:n.nextCursor,hasMore:n.hasMore};return p({tasks:n.tasks},e.makeMeta({cacheHit:n.cacheHit}),r)}function wm(t,e){return t.registerTool("task_search",{description:Di,inputSchema:Tm},async n=>{let r=await AS(n,e);return u(r)})}var Sm=z.enum(["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]),CS=z.union([z.object({day:z.number().int().min(1).max(31)}),z.object({weekday:Sm,position:z.union([z.literal(1),z.literal(2),z.literal(3),z.literal(4),z.literal("last")])})]),Ri=z.discriminatedUnion("kind",[z.object({kind:z.literal("due-relative"),offsetSeconds:z.number().int()}),z.object({kind:z.literal("defer-relative"),offsetSeconds:z.number().int()}),z.object({kind:z.literal("absolute"),fireAt:oe()})]),Ai=z.object({method:z.enum(["fixed","start-again","due-again"]),unit:z.enum(["minutes","hours","days","weeks","months","years"]),steps:z.number().int().min(1),weekdays:z.array(Sm).optional(),monthlyAnchor:CS.optional()}).refine(t=>t.weekdays===void 0||t.unit==="weeks",{message:"weekdays is only valid when unit is 'weeks'",path:["weekdays"]}).refine(t=>t.monthlyAnchor===void 0||t.unit==="months",{message:"monthlyAnchor is only valid when unit is 'months'",path:["monthlyAnchor"]}).refine(t=>!(t.weekdays!==void 0&&t.monthlyAnchor!==void 0),{message:"Only one of weekdays or monthlyAnchor may be set"});z.object({id:h.schema,name:z.string(),note:z.string().nullable(),noteHtml:z.string().nullable(),projectId:k.schema.nullable(),parentId:h.schema.nullable(),tagIds:z.array(S.schema),deferDate:oe().nullable(),deferDateFloating:z.boolean().optional(),dueDate:oe().nullable(),dueDateFloating:z.boolean().optional(),estimatedMinutes:z.number().int().min(1).nullable(),flagged:z.boolean(),completed:z.boolean(),completedAt:oe().nullable(),dropped:z.boolean(),droppedAt:oe().nullable(),available:z.boolean(),blocked:z.boolean(),sequential:z.boolean(),completedByChildren:z.boolean(),repetition:Ai.nullable(),notifications:z.array(Ri).optional(),createdAt:oe(),modifiedAt:oe(),_links:im.optional()});var Ci=`Replace the alarm/notification set on an OmniFocus task atomically. Pass an array of alarms; this overwrites any existing alarms in full. Each alarm is one of: {kind:'due-relative', offsetSeconds:N} (positive = before due date, negative = after), {kind:'defer-relative', offsetSeconds:N} (relative to defer date), or {kind:'absolute', fireAt:ISO-8601 string}. Relative kinds require the task to already have the corresponding date set, or the call returns a VALIDATION error. Use task_clear_alarms to remove all alarms with no payload. Returns the updated task. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_set_alarms({ id: "abc123", alarms: [{ kind: "due-relative", offsetSeconds: 3600 }] }) Example: task_set_alarms({ id: "abc123", alarms: [{ kind: "absolute", fireAt: "2026-05-01T09:00:00Z" }] })`,ES=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),alarms:z.array(Ri).describe("Full replacement set of alarms. Empty array is permitted and equivalent to task_clear_alarms.")});async function MS(t,e){let n=await e.adapter.getTask(t.id);for(let a of t.alarms){if(a.kind==="due-relative"&&n.dueDate===null)throw new y("Cannot set a due-relative alarm on a task with no dueDate. Set the task's due date first, or use an absolute alarm.",{details:{field:"alarms",taskId:t.id,alarmKind:a.kind}});if(a.kind==="defer-relative"&&n.deferDate===null)throw new y("Cannot set a defer-relative alarm on a task with no deferDate. Set the task's defer date first, or use an absolute alarm.",{details:{field:"alarms",taskId:t.id,alarmKind:a.kind}})}await e.adapter.setTaskAlarms(t.id,t.alarms);let r=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:r.projectId});let o=e.makeMeta({syncPending:true,humanReadableSummary:_d(n.name,t.alarms.length)});return p({task:r},o)}function jm(t,e){return t.registerTool("task_set_alarms",{description:Ci,inputSchema:ES.shape},async n=>{let r=await MS(n,e);return u(r)})}var Ei='Set the repetition rule on an OmniFocus task. Overwrites any existing rule. Use task_clear_repetition to remove a rule entirely. Returns the updated task ID; call task_get for the full object. Mutations do not sync automatically \u2014 call sync_trigger if cross-device visibility matters. Example: task_set_repetition({ id: "abc123", rule: { method: "fixed", unit: "days", steps: 7 } }) Example: task_set_repetition({ id: "abc123", rule: { method: "start-again", unit: "weeks", steps: 1 } })',FS=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),rule:Ai.describe("Repetition rule to apply. 'method': 'fixed' repeats from the due date, 'start-again' from completion, 'due-again' from due date (alias). 'unit': time unit for the interval. 'steps': how many units between occurrences (minimum 1). 'weekdays': optional array of day names \u2014 only valid when unit is 'weeks'. 'monthlyAnchor': optional day-of-month or weekday-position \u2014 only valid when unit is 'months'.")});async function US(t,e){await e.adapter.updateTask(t.id,{repetition:t.rule});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:bd(n.name)});return p({task:n},r)}function _m(t,e){return t.registerTool("task_set_repetition",{description:Ei,inputSchema:FS.shape},async n=>{let r=await US(n,e);return u(r)})}var Mi='Mark an OmniFocus task as incomplete \u2014 removes its completion timestamp. Idempotent: returns noChange: true if the task is already incomplete. Do not use to drop or delete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: clears completedAt, sets meta.syncPending = true.Example: task_uncomplete({ id: "abc123" })',BS=z.object({id:h.schema.describe("Persistent task ID.")});async function JS(t,e){let n=await e.adapter.getTask(t.id);return n.completed===false?p({noChange:true,id:t.id,name:n.name},e.makeMeta()):(await e.adapter.uncompleteTask(t.id),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:hd(n.name)})))}function Pm(t,e){return t.registerTool("task_uncomplete",{description:Mi,inputSchema:BS.shape},async n=>{let r=await JS(n,e);return u(r)})}var Ni='Restore a dropped OmniFocus task \u2014 clears its dropped status and returns it to the active view. Idempotent: returns noChange: true if the task is not dropped. Do not use to complete a task. Returns { done: true, id, name } or { noChange: true, id, name } \u2014 name lets the agent describe the change without a follow-up read. Side effects: clears droppedAt, sets meta.syncPending = true.Example: task_undrop({ id: "abc123" })',WS=z.object({id:h.schema.describe("Persistent task ID.")});async function HS(t,e){let n=await e.adapter.getTask(t.id);return n.dropped===false?p({noChange:true,id:t.id,name:n.name},e.makeMeta()):(await e.adapter.undropTask(t.id),e.cache!==void 0&&b(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Id(n.name)})))}function Om(t,e){return t.registerTool("task_undrop",{description:Ni,inputSchema:WS.shape},async n=>{let r=await HS(n,e);return u(r)})}var Fi='Partially update mutable fields on an OmniFocus task. Only supplied fields are changed; omit a field to leave it unchanged. Do not use to complete or delete a task; prefer task_complete or task_delete instead. Two tag-update modes: (1) supply tagIds to replace the full tag set; (2) supply addTags and/or removeTags to apply a diff without reading first. Supplying tagIds together with addTags/removeTags is a ValidationError. setFlagged is a convenience alias for flagged. Safety controls: set dry_run=true to preview the patched task without mutating; pass expectedModifiedAt (from a recent task_get) to reject the call if the task changed since you read it; pass idempotency_key to coalesce retries so the same update is only performed once. Returns the updated task. Side effects: writes to OmniFocus, sets meta.syncPending = true. Call sync_trigger when you need changes to appear on other devices. Example: task_update({ id: "abc123", flagged: true }) Example: task_update({ id: "abc123", dueDate: "2026-05-01T00:00:00Z", addTags: ["tag456"] })',cr=z.object({id:h.schema.describe("Persistent task ID. Get from task_list or search_query."),name:z.string().min(1).optional().describe("New task name. Must be non-empty if supplied."),note:z.string().nullable().optional().describe("Plain-text note. Pass null to clear. HTML round-trip available in M3."),flagged:z.boolean().optional().describe("Flag or unflag the task. Alias: setFlagged."),setFlagged:z.boolean().optional().describe("Convenience alias for flagged. Use when your intent is specifically to set or clear the flag without touching other fields."),deferDate:z.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),deferDateFloating:z.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),dueDateFloating:z.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z.number().int().positive().nullable().optional().describe("Estimated duration in minutes. Pass null to clear."),sequential:z.boolean().optional().describe("Whether subtasks must be completed in order."),completedByChildren:z.boolean().optional().describe("Whether the task completes when all children are complete."),tagIds:z.array(S.schema).optional().describe("Full-replacement tag list. Replaces all existing tags. Mutually exclusive with addTags/removeTags."),addTags:z.array(S.schema).optional().describe("Tags to add. No-op for tags the task already has. Mutually exclusive with tagIds."),removeTags:z.array(S.schema).optional().describe("Tags to remove. No-op for tags the task doesn't have. Mutually exclusive with tagIds."),expectedModifiedAt:z.string().optional().describe("Optimistic-concurrency guard: ISO-8601 timestamp from a recent task_get. If the task's current modifiedAt differs, the call fails with OF_CONFLICT and no update is performed. Omit to skip the check."),dry_run:z.boolean().optional().describe("When true, validates input, computes the patched task (pre-fetch merged with the supplied fields), and returns a preview envelope with meta.dryRun = true; no adapter call is made and no mutation occurs."),idempotency_key:z.string().min(1).max(128).optional().describe("Idempotency key for retry-safe updates. Identical subsequent calls within the TTL window replay the original envelope with meta.idempotentReplay = true instead of re-applying the patch.")}),zS=cr.refine(t=>!(t.tagIds!==void 0&&(t.addTags!==void 0||t.removeTags!==void 0)),{message:"tagIds cannot be combined with addTags/removeTags. Use tagIds for full replacement, or addTags/removeTags for additive diff.",path:["tagIds"]}).refine(t=>!(t.dueDate!=null&&t.deferDate!=null&&new Date(t.dueDate)<new Date(t.deferDate)),{message:"dueDate must not be earlier than deferDate",path:["dueDate"]});async function GS(t,e){je(zS,t);let{id:n,addTags:r,removeTags:o,setFlagged:a,tagIds:i,...s}=t,c=e.idempotencyStore??de;return ce(c,t.idempotency_key,async()=>{let d=await e.adapter.getTask(n);Ye(t.expectedModifiedAt,d.modifiedAt,`task:${n}`);let l;if(r!==void 0||o!==void 0){let I=new Set(d.tagIds);for(let T of r??[])I.add(T);for(let T of o??[])I.delete(T);l=[...I];}else i!==void 0&&(l=i);let m=a!==void 0?a:s.flagged,f={...s.name!==void 0?{name:s.name}:{},...s.note!==void 0?{note:s.note}:{},...m!==void 0?{flagged:m}:{},...s.deferDate!==void 0?{deferDate:s.deferDate}:{},...s.deferDateFloating!==void 0?{deferDateFloating:s.deferDateFloating}:{},...s.dueDate!==void 0?{dueDate:s.dueDate}:{},...s.dueDateFloating!==void 0?{dueDateFloating:s.dueDateFloating}:{},...s.estimatedMinutes!==void 0?{estimatedMinutes:s.estimatedMinutes}:{},...s.sequential!==void 0?{sequential:s.sequential}:{},...s.completedByChildren!==void 0?{completedByChildren:s.completedByChildren}:{},...l!==void 0?{tagIds:l}:{}},g=()=>{let I={...d,...f};return p({task:I},e.makeMeta({syncPending:false}))},v=async()=>{await e.adapter.updateTask(n,f);let I=await e.adapter.getTask(n);return e.cache!==void 0&&b(e.cache,{taskId:n,projectId:I.projectId}),p({task:I},e.makeMeta({syncPending:true}))};return Ce(t.dry_run,g,v)})}function xm(t,e){return t.registerTool("task_update",{description:Fi,inputSchema:cr.shape},async n=>{let r=await GS(n,e);return u(r)})}var Ui="Preview what task_update would do without making any changes. Do NOT use to actually update a task \u2014 use task_update instead. Returns { description, plannedChanges } showing the fields that would be patched. No side effects: read-only by contract \u2014 never mutates OmniFocus. Example: dry-run companion \u2014 pass the same args you would to the write tool, inspect plannedChanges, then call the write tool once approved.";async function qS(t,e){let n=[],r=[],o=String(t.id);try{let i=await e.adapter.getTask(t.id);o=i.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:i.name}),r.push(`rename to '${t.name}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate,oldValue:i.dueDate??null}),r.push(t.dueDate===null?"clear due date":`set due date to ${Te(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate,oldValue:i.deferDate??null}),r.push(t.deferDate===null?"clear defer date":`set defer date to ${Te(t.deferDate)}`));let s=t.setFlagged!==void 0?t.setFlagged:t.flagged;if(s!==void 0&&(n.push({field:"flagged",newValue:String(s),oldValue:String(i.flagged)}),r.push(s?"flag":"unflag")),t.estimatedMinutes!==void 0&&(n.push({field:"estimatedMinutes",newValue:t.estimatedMinutes===null?null:String(t.estimatedMinutes),oldValue:i.estimatedMinutes!=null?String(i.estimatedMinutes):null}),r.push(t.estimatedMinutes===null?"clear estimated minutes":`set estimated minutes to ${t.estimatedMinutes}`)),t.tagIds!==void 0){let c=await Promise.all(t.tagIds.map(d=>he(e.adapter,d)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`set tags to [${c.map(d=>`'${d}'`).join(", ")}]`);}else if(t.addTags!==void 0||t.removeTags!==void 0){if(t.addTags!==void 0&&t.addTags.length>0){let c=await Promise.all(t.addTags.map(d=>he(e.adapter,d)));n.push({field:"addTags",newValue:t.addTags.join(",")}),r.push(`add tags [${c.map(d=>`'${d}'`).join(", ")}]`);}if(t.removeTags!==void 0&&t.removeTags.length>0){let c=await Promise.all(t.removeTags.map(d=>he(e.adapter,d)));n.push({field:"removeTags",newValue:t.removeTags.join(",")}),r.push(`remove tags [${c.map(d=>`'${d}'`).join(", ")}]`);}}t.note!==void 0&&(n.push({field:"note",newValue:t.note===null?null:t.note.slice(0,50)}),r.push(t.note===null?"clear note":"update note"));}catch{t.name!==void 0&&(n.push({field:"name",newValue:t.name}),r.push(`rename to '${t.name}'`));}let a=r.length>0?`Would update task '${o}': ${r.join(", ")}.`:`Would update task '${o}' (no fields changed).`;return p({description:a,plannedChanges:n},e.makeMeta())}function Dm(t,e){return t.registerTool("task_update_describe",{description:Ui,inputSchema:cr.shape},async n=>{let r=await qS(n,e);return u(r)})}var Li='Record that an OmniFocus task is waiting on someone or something. Tags the task with the configured @waiting tag (creating the tag if absent) and writes a structured `waiting-on` fenced block to the top of the task note. The fence preserves any existing user prose in the note. Round-trips through task_get / task_get_many as a structured `waitingOn` field. Surfaces in the omnifocus://waiting-on resource sorted by days overdue. Use to systematize follow-ups; do NOT use for task completion or scheduling. Returns { id, waitingOn } with the persisted entry. Side effects: writes tag + note; sets meta.syncPending = true. Example: { "taskId": "abc123", "whom": "Alex", "what": "design review", "followUpAfter": "2026-05-05T17:00:00Z" }',Bi='Clear waiting-on tracking from an OmniFocus task. Strips the `waiting-on` fenced block from the task note (preserving any other user prose) and removes the configured @waiting tag from the task. Idempotent: returns noChange:true when the task has no waiting-on data. Do NOT use to delete the task or remove unrelated tags \u2014 prefer task_delete or task_update instead. Returns { id, cleared:true } or { id, noChange:true }. Side effects: writes tag + note; sets meta.syncPending = true. Example: { "taskId": "abc123" }',VS=z.object({taskId:h.schema.describe("Persistent task ID."),whom:z.string().min(1).describe("Person, team, or system being waited on. Required."),what:z.string().min(1).optional().describe("Optional short description of what is being waited on."),since:z.string().datetime({offset:true}).optional().describe("ISO-8601 date the wait began. Defaults to now. Use to backfill historical waits."),followUpAfter:z.string().datetime({offset:true}).optional().describe("ISO-8601 date past which the agent should nudge if still unresolved. Drives daysOverdue in the omnifocus://waiting-on resource.")}),KS=z.object({taskId:h.schema.describe("Persistent task ID.")});async function XS(t,e){let n=await t.listTags(),r=e.toLowerCase(),o=n.find(a=>a.name.toLowerCase()===r);return o!==void 0?o.id:t.createTag({name:e})}async function YS(t,e){let n=await e.adapter.getTask(t.taskId),r=Qr.parse({whom:t.whom,...t.what!==void 0&&{what:t.what},since:t.since??new Date().toISOString(),...t.followUpAfter!==void 0&&{followUpAfter:t.followUpAfter}}),o=Xc(n.note,r),a=await XS(e.adapter,e.waitingTagName),i=n.tagIds.includes(a)?n.tagIds:[...n.tagIds,a];return await e.adapter.updateTask(t.taskId,{note:o,tagIds:i}),e.cache!==void 0&&b(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,waitingOn:r},e.makeMeta({syncPending:true}))}function Rm(t,e){return t.registerTool("task_set_waiting_on",{description:Li,inputSchema:VS.shape},async n=>{let r=await YS(n,e);return u(r)})}async function ZS(t,e){let n=await e.adapter.getTask(t.taskId),r=Yc(n.note),o=await e.adapter.listTags(),a=e.waitingTagName.toLowerCase(),i=o.find(l=>l.name.toLowerCase()===a)?.id,s=i!==void 0&&n.tagIds.includes(i),c=r!==n.note;if(!s&&!c)return p({id:t.taskId,noChange:true},e.makeMeta());let d={};return c&&(d.note=r),s&&i!==void 0&&(d.tagIds=n.tagIds.filter(l=>l!==i)),await e.adapter.updateTask(t.taskId,d),e.cache!==void 0&&b(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,cleared:true},e.makeMeta({syncPending:true}))}function Am(t,e){return t.registerTool("task_clear_waiting_on",{description:Bi,inputSchema:KS.shape},async n=>{let r=await ZS(n,e);return u(r)})}var Cm=z.object({tagId:z.string().min(1).optional().describe("Restrict to tasks carrying this tag."),projectId:z.string().min(1).optional().describe("Restrict to tasks in this project.")}).optional().describe("Optional filter narrowing which task events fire this webhook."),QS=z.object({projectId:z.string().min(1).optional().describe("Restrict to status changes on this project.")}).optional().describe("Optional filter narrowing which project events fire this webhook."),Em=z.discriminatedUnion("on",[z.object({on:z.literal("task-completed"),filter:Cm}),z.object({on:z.literal("task-created"),filter:Cm}),z.object({on:z.literal("project-status-changed"),filter:QS})]);function Ji(t){return {name:t.name,trigger:t.trigger,secretSet:t.secret!==void 0&&t.secret.length>0,createdAt:t.createdAt}}var Jt=z.string().min(1).max(64).regex(/^\S+$/,"must not contain whitespace").describe("Stable name for the webhook. Unique within the registry; used as the lookup key."),Mm=z.string().min(1).refine(t=>t.startsWith("https://"),{message:"url must use https:// (http:// rejected per ADR-0016 \xA74b)"}).describe("Outbound HTTPS URL. http:// is rejected."),Nm=z.string().min(8,"secret must be at least 8 characters when supplied").max(256).optional().describe("Optional HMAC seed for signature header. Stored on disk only; never echoed back. Receivers verify via X-OmniFocus-Signature: sha256=<hex> using the same secret.");var $i='Register an outbound webhook that fires when an OmniFocus state change matches the supplied trigger. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1 in the environment, mirroring the raw-script gating. URLs must use https:// (http:// is rejected at registration). An optional secret enables HMAC-SHA256 signature verification by the receiver via X-OmniFocus-Signature: sha256=<hex>; the secret is stored on disk only and is never echoed back through any tool response. Do NOT use this to call this MCP server itself \u2014 webhooks are outbound only. Returns { webhook: WebhookSummary } where the summary omits both URL and secret. Side effects: writes to the registry config file at ~/Library/Application Support/omnifocus-mcp/webhooks.json (mode 0600). Example: webhook_register({ name: "slack-billing", url: "https://hooks.slack.com/services/...", trigger: { on: "task-completed", filter: { tagId: "tag_xyz" } } })',tb=z.object({name:Jt.describe("Stable name for the webhook. Unique within the registry; used as the lookup key. \u226464 chars, no whitespace."),url:Mm.describe("Outbound HTTPS URL. http:// is rejected at registration (per ADR-0016 \xA74b)."),trigger:Em.describe("What triggers a webhook fire \u2014 one of task-completed, task-created, or project-status-changed. Each variant accepts an optional filter narrowing which entities count."),secret:Nm.describe("Optional HMAC seed (8\u2013256 chars). When set, every delivery includes an X-OmniFocus-Signature: sha256=<hex> header so the receiver can verify authenticity. Stored on disk only; never echoed.")});function hn(t){if(!t.enabled)throw new y("Webhook subsystem is disabled. Set OMNIFOCUS_WEBHOOKS_ENABLED=1 in the environment to enable.",{details:{field:"OMNIFOCUS_WEBHOOKS_ENABLED"}})}async function nb(t,e){hn(e);let n=e.registry.register({name:t.name,url:t.url,trigger:t.trigger,...t.secret!==void 0&&{secret:t.secret}});return p({webhook:n},e.makeMeta())}function Fm(t,e){return t.registerTool("webhook_register",{description:$i,inputSchema:tb.shape},async n=>{let r=await nb(n,e);return u(r)})}var Wi='Delete a registered outbound webhook by name. Idempotent \u2014 returns noChange:true when the named webhook does not exist. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1. Do NOT use this for bulk-clear operations; this tool removes exactly one entry. Returns { name, deleted:true } or { name, noChange:true }. Side effects: rewrites the registry config file at ~/Library/Application Support/omnifocus-mcp/webhooks.json. Example: webhook_delete({ name: "slack-billing" })',ob=z.object({name:Jt.describe("Name of the registered webhook to delete. Idempotent \u2014 unknown names return noChange:true.")});async function ab(t,e){return hn(e),e.registry.delete(t.name)?p({name:t.name,deleted:true},e.makeMeta()):p({name:t.name,noChange:true},e.makeMeta())}function Um(t,e){return t.registerTool("webhook_delete",{description:Wi,inputSchema:ob.shape},async n=>{let r=await ab(n,e);return u(r)})}var Hi="List every registered outbound webhook by name, trigger, and createdAt timestamp. URLs and secrets are NEVER surfaced \u2014 only metadata safe to display. Use this to confirm what's wired up; delete unwanted entries via webhook_delete. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1. Do NOT use this to retrieve URLs or secrets \u2014 by design they remain on-disk only. Returns { webhooks: WebhookSummary[] } in registration order. Read-only; safe to call repeatedly. Example: webhook_list()",ib=z.object({});async function cb(t,e){return hn(e),p({webhooks:e.registry.list()},e.makeMeta())}function Lm(t,e){return t.registerTool("webhook_list",{description:Hi,inputSchema:ib.shape},async n=>{let r=await cb(n,e);return u(r)})}var zi=`Fire a synthetic event through a registered webhook to verify it's wired correctly. Goes through the same HTTPS POST + HMAC + retry + circuit-breaker path as a real delivery \u2014 if the receiver doesn't see this event, it won't see real ones either. Off by default \u2014 requires OMNIFOCUS_WEBHOOKS_ENABLED=1. Do NOT use this for load testing \u2014 circuit-breaker counters apply to synthetic events too. Returns { name, delivered: true } on dispatch success, { name, error } when the webhook is not registered. Note: 'delivered' means the dispatcher attempted delivery; the receiver's actual response is not surfaced (per ADR-0016 \xA74e: failures log to stderr, never throw upward). Side effects: makes one outbound HTTPS POST to the registered URL with a synthetic event payload. Example: webhook_test({ name: "slack-billing" })`,lb=z.object({name:Jt.describe("Name of the registered webhook to fire a synthetic event through.")});async function pb(t,e){if(!e.enabled)throw new y("Webhook subsystem is disabled. Set OMNIFOCUS_WEBHOOKS_ENABLED=1 in the environment to enable.",{details:{field:"OMNIFOCUS_WEBHOOKS_ENABLED"}});let n=await e.orchestrator.fireSynthetic(t.name);return "delivered"in n?p({name:t.name,delivered:true},e.makeMeta()):p({name:t.name,error:n.error},e.makeMeta())}function Bm(t,e){return t.registerTool("webhook_test",{description:zi,inputSchema:lb.shape},async n=>{let r=await pb(n,e);return u(r)})}var Gi="Read the active perspective and focus container of the front OmniFocus window. **UI-affecting tool family** \u2014 only meaningful in pair-assistant flows where the user is looking at OmniFocus. Headless agents should ignore. Use when the agent needs to know what view the user currently sees, or to confirm that a prior `window_set_*` took effect. Do NOT use to evaluate a perspective's data \u2014 prefer `perspective_evaluate`, which doesn't depend on UI state. Takes no arguments. Returns { perspectiveName: string | null, focusContainerIds: string[] } \u2014 perspectiveName is null when no perspective is bound; focusContainerIds is [] when the window isn't focused on a project or folder. Errors: OF_WINDOW_UNAVAILABLE when OmniFocus has no front window. Read-only; safe to retry. Example: window_get_state()",ub=z.object({});async function mb(t,e){let n=await e.adapter.getWindowState();return p(n,e.makeMeta())}function Jm(t,e){return t.registerTool("window_get_state",{description:Gi,inputSchema:ub.shape},async n=>u(await mb(n,e)))}var qi=`Switch the front OmniFocus window to a named perspective (built-in or custom). **UI-affecting tool** \u2014 only meaningful when the user can see OmniFocus. Headless agents should not fire this. Use when the user asks 'show me my flagged tasks' or a guided weekly-review prompt wants to navigate the user's UI. Do NOT use to evaluate a perspective's results \u2014 prefer perspective_evaluate, which doesn't touch the user's UI. Pass perspectiveName (case-sensitive, matches OF's UX). Built-in names: Inbox, Projects, Tags, Forecast, Flagged, Review, Nearby, Completed, Changed. Returns { perspectiveName }. Errors: OF_WINDOW_UNAVAILABLE (no front window), OF_NOT_FOUND (no perspective with this name). Side effects: changes the user's visible window state; no data caches invalidated. Example: window_set_perspective({ perspectiveName: "Flagged" })`,fb=z.object({perspectiveName:z.string().min(1).describe("Name of the perspective to activate. Case-sensitive. Built-in or custom perspectives both work.")});async function gb(t,e){let n=await e.adapter.setWindowPerspective(t.perspectiveName);return p(n,e.makeMeta())}function $m(t,e){return t.registerTool("window_set_perspective",{description:qi,inputSchema:fb.shape},async n=>u(await gb(n,e)))}var Vi="Set or clear the front OmniFocus window's focus container (a project or folder). **UI-affecting tool** \u2014 only meaningful when the user can see OmniFocus. Headless agents should not fire this. Use when the user asks 'focus on this project' or a guided flow wants to scope the visible view. Do NOT use to filter task data \u2014 prefer `task_list { projectId }` or `perspective_evaluate` instead, both of which work without touching the user's UI. Pass containerId (a ProjectId or FolderId) to focus, or null to clear focus. Returns { focusContainerIds: string[] } \u2014 single-element array when focused, [] when cleared. Errors: OF_WINDOW_UNAVAILABLE (no front window), OF_NOT_FOUND (containerId is neither a project nor a folder). Side effects: changes the user's visible window state; no data caches invalidated. Example: window_set_focus({ containerId: \"prj123\" }) Example: window_set_focus({ containerId: null })",hb=z.object({containerId:z.union([z.string().min(1),z.null()]).describe("ProjectId or FolderId to focus the front window on, or null to clear focus.")});async function yb(t,e){let n=await e.adapter.setWindowFocus(t.containerId);return p(n,e.makeMeta())}function Wm(t,e){return t.registerTool("window_set_focus",{description:Vi,inputSchema:hb.shape},async n=>u(await yb(n,e)))}var Ki="Open a new OmniFocus window via OmniJS document.newWindow(). **UI-affecting tool** \u2014 only meaningful when OmniFocus is running. Headless agents should not fire this. Use when the user asks 'open a new window' or a flow needs a fresh, unfocused OmniFocus window. Do NOT use to read task or project data \u2014 prefer task_list or project_list instead. Takes no arguments. Returns { perspectiveName: string | null, focusContainerIds: string[] } describing the new window's initial state. Errors: WINDOW_OPEN_FAILED when the window could not be created. Side effects: opens a new OmniFocus window; no data caches invalidated. Example: app_window_new()",kb=z.object({});async function Ib(t,e){let n=await e.adapter.appWindowNew();return p(n,e.makeMeta())}function Hm(t,e){return t.registerTool("app_window_new",{description:Ki,inputSchema:kb.shape},async n=>u(await Ib(n,e)))}var Xi="Open a new tab on the front OmniFocus window via OmniJS document.newTabOnWindow(). **UI-affecting tool** \u2014 only meaningful when OmniFocus has an open window. Headless agents should not fire this. Use when the user asks 'open a new tab' or a flow needs an additional view in the existing window. Do NOT use to open a standalone window \u2014 prefer app_window_new instead. Takes no arguments. Returns { perspectiveName: string | null, focusContainerIds: string[] } describing the new tab's initial state. Errors: WINDOW_UNAVAILABLE when there is no open OmniFocus window; WINDOW_OPEN_FAILED when the tab could not be created. Side effects: opens a new tab in the front OmniFocus window; no data caches invalidated. Example: app_window_new_tab()",vb=z.object({});async function Tb(t,e){let n=await e.adapter.appWindowNewTab();return p(n,e.makeMeta())}function zm(t,e){return t.registerTool("app_window_new_tab",{description:Xi,inputSchema:vb.shape},async n=>u(await Tb(n,e)))}var Gm={app_launch:io,attachment_add:po,attachment_list:lo,attachment_remove:uo,attachment_save_to_path:mo,database_redo:fo,database_undo:go,decision_clear:yo,decision_record:ko,export_opml:vo,export_taskpaper:So,forecast_get:Lo,forecast_get_tag:Bo,forecast_pack:Jo,forecast_set_tag:Wo,folder_create:jo,folder_create_describe:Po,folder_delete:Oo,folder_delete_describe:Do,folder_get:Ro,folder_list:Ao,folder_move:Co,folder_move_describe:Mo,folder_update:No,folder_update_describe:Uo,import_opml:wo,import_taskpaper:bo,internal_status:Zo,note_append:Ho,note_get:Go,note_get_html:Vo,note_set:Ko,note_set_html:Xo,perspective_create:ea,perspective_delete:ta,perspective_evaluate:na,perspective_evaluate_dry_run:oa,perspective_get:aa,perspective_list:sa,perspective_update:ca,plugin_invoke:la,project_batch_complete:ua,project_batch_drop:fa,project_complete:ga,project_complete_describe:ya,project_create:Ta,project_create_describe:Sa,project_delete:ba,project_delete_describe:_a,project_drop:Pa,project_drop_describe:xa,project_get:Da,project_get_many:Ra,project_list:Aa,project_mark_reviewed:es,project_move:Ca,project_move_describe:Ma,project_template_instantiate:Ua,project_template_list:La,project_template_save:Ja,project_update:$a,repetition_from_prose:Ya,project_update_describe:Ha,review_list_due:Za,review_mark_reviewed:Qa,project_set_next_review_date:rs,review_set_interval:ns,run_jxa_script:Ga,run_omnijs_script:Va,search_query:os,sync_status:as,sync_trigger:ss,tag_create:cs,tag_create_describe:ls,tag_delete:ps,tag_delete_describe:ms,tag_get:fs,tag_get_many:hs,tag_get_location:gs,tag_list:ys,tag_move:ks,tag_move_describe:vs,tag_set_allows_next_action:Ts,tag_set_location:ws,tag_set_status:Ss,tag_update:js,task_batch_assign:Os,tag_update_describe:Ps,task_batch_complete:Rs,task_batch_create:As,task_batch_create_describe:Es,task_batch_defer_smart:Fs,task_batch_delete:Us,task_batch_drop:Bs,task_batch_move:Js,task_batch_uncomplete:Ws,task_batch_undrop:zs,task_batch_update:Gs,task_batch_update_describe:Vs,task_complete:Ys,task_complete_describe:Qs,task_create:ti,task_create_describe:ni,task_defer_smart:ri,task_drop:ii,task_drop_describe:di,task_clear_alarms:Ks,task_clear_repetition:Xs,task_delete:oi,task_delete_describe:si,task_duplicate:li,task_extract_from_image:ui,task_extract_from_note:mi,task_find_by_name:fi,task_find_similar:gi,task_get:hi,task_get_many:yi,task_list:vi,task_convert_to_project:ei,task_move:wi,task_move_describe:bi,task_search:Di,task_parse_transport_text:_i,task_reclassify:Oi,task_reorder:xi,task_clear_waiting_on:Bi,task_set_alarms:Ci,task_set_repetition:Ei,task_set_waiting_on:Li,task_uncomplete:Mi,task_undrop:Ni,task_update:Fi,app_window_new:Ki,app_window_new_tab:Xi,window_get_state:Gi,window_set_focus:Vi,window_set_perspective:qi,task_update_describe:Ui,webhook_delete:Wi,webhook_list:Hi,webhook_register:$i,webhook_test:zi};var wb=`Replay dispatcher for clarification-needed responses. When a tool returns { kind: 'clarification-needed' }, present the question and options to the user, then call this tool with the replayToken from that response and the zero-based index of the option the user selected. The server resumes the original tool call with the disambiguation applied and returns the final result envelope. Tokens are single-use and expire after 5 minutes \u2014 call this tool promptly after the user responds. Passing an expired or unknown token returns a NotFound error. Passing a choice index outside the valid range returns an InvalidInput error. Example: clarify({ replayToken: "tok_abc", choice: 0 })`,Sb=z.object({replayToken:z.string().min(1).describe("Opaque token from the clarification-needed envelope's replayToken field."),choice:z.number().int().min(0).describe("Zero-based index of the option the user selected (matches ClarificationOption.index).")});async function bb(t,e){let n=e.replayStore??ve,r=e.makeMeta(),o=n.consume(t.replayToken);return o===void 0?so(new P(`Replay token not found or expired: ${t.replayToken.slice(0,8)}\u2026`,{suggestion:"The token may have expired (5 min TTL) or already been used. Re-invoke the original tool to get a fresh token."}),r):t.choice<0||t.choice>=o.options.length?so(new y(`choice ${t.choice} is out of range; valid indices are 0\u2013${o.options.length-1}.`,{suggestion:`Valid options: ${o.options.map((i,s)=>`${s}: ${i}`).join(", ")}.`}),r):await o.callback(t.choice)}function qm(t,e){return t.registerTool("clarify",{description:wb,inputSchema:Sb.shape},async n=>{let r=await bb(n,e);return u(r)})}var jb=`Delete a saved project template by name from the Templates folder. Returns { deleted: true, templateName } on success. Returns TemplateNotFoundError when no matching template exists \u2014 callers can distinguish 'deleted' from 'never existed'. Side effects: removes the template project; sets meta.syncPending = true. Do NOT use to delete ordinary projects \u2014 call project_delete. Example: { templateName: "Client onboarding" }.`,_b=z.object({templateName:z.string().min(1).describe("Name of the template to delete. Matched case-insensitively within the Templates folder.")}),dr=class extends P{constructor(e){super(`No template named "${e}" was found in the Templates folder.`,{details:{templateName:e}});}};async function Pb(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(c=>c.name.toLowerCase()===r);if(o===void 0)throw new dr(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),i=t.templateName.toLowerCase(),s=a.find(c=>c.name.toLowerCase()!==i?false:Dt(c.note)!==void 0);if(s===void 0)throw new dr(t.templateName);return await e.adapter.deleteProject(s.id),e.cache!==void 0&&J(e.cache,{projectId:s.id}),p({deleted:true,templateName:t.templateName},e.makeMeta({syncPending:true}))}function Km(t,e){return t.registerTool("project_template_delete",{description:jb,inputSchema:_b.shape},async n=>{let r=await Pb(n,e);return u(r)})}var Ab=Zi.join(xb.homedir(),"Library","Application Support","OmniFocus","OmniFocus.ofocus");function Cb(){let t=fileURLToPath(import.meta.url);return Zi.resolve(Zi.dirname(t),"../../bin/omnifocus-watcher")}var lr=class{dbPath;debounceMs;onChange;binaryPath;started=false;debounceTimer=null;windowStartTs=null;windowPaths=[];swiftProcess=null;nodeWatcher=null;constructor(e,n={}){this.onChange=e,this.debounceMs=n.debounceMs??500,this.dbPath=n.dbPath??Ab,this.binaryPath=n.binaryPath!==void 0?n.binaryPath:Cb();}start(){if(this.started)return;if(!dt.existsSync(this.dbPath)){R.warn({event:"database.watcher.path_not_found",dbPath:this.dbPath,message:"OmniFocus database path not found; change notifications will not fire."});return}this.tryStartSwift()||this.startNodeWatcher(),this.started=true;}stop(){this.clearDebounce(),this.swiftProcess!==null&&(this.swiftProcess.kill("SIGTERM"),this.swiftProcess=null),this.nodeWatcher!==null&&(this.nodeWatcher.close(),this.nodeWatcher=null),this.started=false,R.debug({event:"database.watcher.stopped"});}tryStartSwift(){if(this.binaryPath===null)return false;try{dt.accessSync(this.binaryPath,dt.constants.X_OK);}catch{return R.debug({event:"database.watcher.swift_unavailable",binaryPath:this.binaryPath,message:"Swift watcher binary not found or not executable; falling back to fs.watch."}),false}try{return this.swiftProcess=spawn(this.binaryPath,[this.dbPath],{stdio:["ignore","pipe","pipe"]}),this.swiftProcess.stderr?.on("data",n=>{R.debug({event:"database.watcher.swift_stderr",msg:n.toString().trim()});}),createInterface({input:this.swiftProcess.stdout}).on("line",n=>{this.handleSwiftLine(n);}),this.swiftProcess.on("exit",(n,r)=>{this.started&&(R.warn({event:"database.watcher.swift_exited",code:n,signal:r,message:"Swift watcher exited unexpectedly; falling back to fs.watch."}),this.swiftProcess=null,this.startNodeWatcher());}),this.swiftProcess.on("error",n=>{R.warn({event:"database.watcher.swift_error",err:n}),this.swiftProcess=null,this.startNodeWatcher();}),R.debug({event:"database.watcher.swift_started",dbPath:this.dbPath}),!0}catch(e){return R.warn({event:"database.watcher.swift_spawn_failed",err:e}),false}}handleSwiftLine(e){if(e.trim())try{let n=JSON.parse(e);if(n.event!=="change")return;this.scheduleNotify({source:"swift",ts:n.ts,paths:n.paths});}catch(n){R.debug({event:"database.watcher.swift_parse_error",line:e,err:n});}}startNodeWatcher(){if(dt.existsSync(this.dbPath))try{this.nodeWatcher=dt.watch(this.dbPath,{persistent:!1},(e,n)=>{this.scheduleNotify({source:"node",ts:new Date().toISOString()});}),this.nodeWatcher.on("error",e=>{R.warn({event:"database.watcher.node_error",err:e}),this.stop();}),R.debug({event:"database.watcher.node_started",dbPath:this.dbPath});}catch(e){R.warn({event:"database.watcher.node_start_failed",dbPath:this.dbPath,err:e});}}scheduleNotify(e){if(this.windowStartTs===null&&(this.windowStartTs=e.ts),e.paths)for(let n of e.paths)this.windowPaths.includes(n)||this.windowPaths.push(n);this.debounceTimer!==null&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null;let n={detectedAt:this.windowStartTs??new Date().toISOString(),source:e.source,...this.windowPaths.length>0?{changedPaths:[...this.windowPaths]}:{}};this.windowStartTs=null,this.windowPaths=[],R.debug({event:"database.watcher.change_detected",source:n.source,detectedAt:n.detectedAt,pathCount:n.changedPaths?.length??0}),this.onChange(n);},this.debounceMs);}clearDebounce(){this.debounceTimer!==null&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.windowStartTs=null,this.windowPaths=[];}};var Nb=5e3,kn=[1e3,5e3,3e4],Xm=10,Ym=3600*1e3,Fb="X-OmniFocus-Signature",Ub=({url:t,body:e,headers:n,timeoutMs:r})=>new Promise((o,a)=>{let i=new URL(t),s=Mb.request({method:"POST",host:i.host,path:`${i.pathname}${i.search}`,headers:{"Content-Length":Buffer.byteLength(e),...n},timeout:r},c=>{c.on("data",()=>{}),c.on("end",()=>{o({statusCode:c.statusCode??0});});});s.on("error",a),s.on("timeout",()=>{s.destroy(new Error(`request timeout after ${r}ms`));}),s.write(e),s.end();}),pr=class{request;now;sleep;write;circuits=new Map;constructor(e={}){this.request=e.request??Ub,this.now=e.now??Date.now,this.sleep=e.sleep??(n=>new Promise(r=>setTimeout(r,n))),this.write=e.write??(n=>process.stderr.write(n));}async deliver(e,n){let r;try{r=n(e.webhookName);}catch(l){this.write(`[webhook] ${e.kind} for "${e.webhookName}" \u2014 registry lookup threw: ${Zm(l)}; dropping.
229
+ `);return}if(r===void 0){this.write(`[webhook] ${e.kind} for "${e.webhookName}" \u2014 webhook not found in registry; dropping.
230
+ `);return}let o=this.getCircuit(r.name);if(o.openUntil!==null&&this.now()<o.openUntil){this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 circuit open (auto-resume in ${Math.ceil((o.openUntil-this.now())/1e3)}s); dropping.
231
+ `);return}o.openUntil!==null&&(o.openUntil=null,this.write(`[webhook] circuit auto-resumed for "${r.name}".
232
+ `));let a=JSON.stringify(e),i={"Content-Type":"application/json","User-Agent":"omnifocus-mcp-webhook/1"};if(r.secret!==void 0&&r.secret.length>0){let l=createHmac("sha256",r.secret).update(a).digest("hex");i[Fb]=`sha256=${l}`;}let s="",c=0;for(let l=0;l<=kn.length;l++){try{let{statusCode:f}=await this.request({url:r.url,body:a,headers:i,timeoutMs:Nb});if(f>=200&&f<300){o.consecutiveFailures=0;return}c=f,s=`non-2xx status ${f}`;}catch(f){s=Zm(f);}if(l===kn.length)break;let m=kn[l];await this.sleep(m);}o.consecutiveFailures++,o.consecutiveFailures>=Xm?(o.openUntil=this.now()+Ym,this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 failed after ${kn.length+1} attempts (last: ${s}). Circuit OPEN for ${Ym/1e3}s.
233
+ `),o.consecutiveFailures=0):this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 failed after ${kn.length+1} attempts (last: ${s}). ${o.consecutiveFailures}/${Xm} consecutive failures.
234
+ `);}getCircuit(e){let n=this.circuits.get(e);if(n!==void 0)return n;let r={consecutiveFailures:0,openUntil:null};return this.circuits.set(e,r),r}inspectCircuit(e){let n=this.circuits.get(e);return n?{...n}:void 0}};function Zm(t){return (t instanceof Error?t.message:String(t)).slice(0,80)}function ef(t){return {id:String(t.id),name:t.name,completed:!!t.completed,projectId:t.projectId?String(t.projectId):null,tagIds:t.tagIds.map(e=>String(e))}}function tf(t){return {id:String(t.id),name:t.name,status:t.status}}function Lb(t,e){return e===void 0?true:!(e.projectId!==void 0&&t.projectId!==e.projectId||e.tagId!==void 0&&!t.tagIds.includes(e.tagId))}function Bb(t,e){return e===void 0?true:!(e.projectId!==void 0&&t.id!==e.projectId)}function nf(t,e,n,r={}){let o=(r.now??new Date).toISOString(),a=[],i=new Map(t.tasks.map(c=>[c.id,c])),s=new Map(t.projects.map(c=>[c.id,c]));for(let c of e.tasks){let d=i.get(c.id);if(d===void 0){Qm(c,"task-created",n,o,a);continue}!d.completed&&c.completed&&Qm(c,"task-completed",n,o,a);}for(let c of e.projects){let d=s.get(c.id);if(d!==void 0&&d.status!==c.status)for(let l of n)l.trigger.on==="project-status-changed"&&Bb(c,l.trigger.filter)&&a.push({kind:"project-status-changed",webhookName:l.name,projectId:c.id,projectName:c.name,previousStatus:d.status,currentStatus:c.status,occurredAt:o});}return a}function Qm(t,e,n,r,o){for(let a of n){if(a.trigger.on!==e)continue;let i=a.trigger.filter;Lb(t,i)&&o.push({kind:e,webhookName:a.name,taskId:t.id,taskName:t.name,projectId:t.projectId,tagIds:t.tagIds,occurredAt:r});}}var ur=class{registry;dispatcher;now;lastTasks=[];lastProjects=[];hasSeenInitialSnapshot=false;constructor(e){this.registry=e.registry,this.dispatcher=e.dispatcher,this.now=e.now??(()=>new Date);}shouldObserve(){return this.registry.listFull().length>0}async observeSnapshot(e,n){let r=e.map(ef),o=n.map(tf);if(!this.hasSeenInitialSnapshot){this.lastTasks=r,this.lastProjects=o,this.hasSeenInitialSnapshot=true;return}let a=this.registry.listFull();if(a.length===0){this.lastTasks=r,this.lastProjects=o;return}let i=nf({tasks:this.lastTasks,projects:this.lastProjects},{tasks:r,projects:o},a,{now:this.now()});this.lastTasks=r,this.lastProjects=o,await Promise.all(i.map(s=>this.dispatcher.deliver(s,c=>this.lookupForDispatch(c))));}async fireSynthetic(e){let n=this.registry.listFull().find(a=>a.name===e);if(n===void 0)return {error:`webhook not found: ${e}`};let r=this.now().toISOString(),o=n.trigger.on==="project-status-changed"?{kind:"project-status-changed",webhookName:n.name,projectId:"synthetic-project",projectName:"Synthetic test event",previousStatus:"active",currentStatus:"completed",occurredAt:r}:{kind:n.trigger.on,webhookName:n.name,taskId:"synthetic-task",taskName:"Synthetic test event",projectId:null,tagIds:[],occurredAt:r};return await this.dispatcher.deliver(o,a=>this.lookupForDispatch(a)),{delivered:true}}lookupForDispatch(e){return this.registry.listFull().find(n=>n.name===e)}};function $b(){return Zi.join(homedir(),"Library","Application Support","omnifocus-mcp","webhooks.json")}var rf=1,mr=class{filePath;webhooks=[];constructor(e={}){this.filePath=e.filePath??$b(),this.load();}path(){return this.filePath}listFull(){return this.webhooks}list(){return this.webhooks.map(Ji)}has(e){return this.webhooks.some(n=>n.name===e)}register(e){if(this.has(e.name))throw new y(`webhook name already registered: ${e.name}`,{details:{field:"name",value:e.name}});let n={name:e.name,url:e.url,trigger:e.trigger,...e.secret!==void 0&&{secret:e.secret},createdAt:new Date().toISOString()};return this.webhooks=[...this.webhooks,n],this.persist(),Ji(n)}delete(e){let n=this.webhooks.length;this.webhooks=this.webhooks.filter(o=>o.name!==e);let r=this.webhooks.length!==n;return r&&this.persist(),r}load(){if(!dt.existsSync(this.filePath)){this.webhooks=[];return}try{let e=dt.readFileSync(this.filePath,"utf8"),n=JSON.parse(e);if(n.version!==rf||!Array.isArray(n.webhooks)){this.webhooks=[];return}this.webhooks=n.webhooks;}catch{this.webhooks=[];}}persist(){let e=Zi.dirname(this.filePath);dt.existsSync(e)||dt.mkdirSync(e,{recursive:true,mode:448});let n={version:rf,webhooks:this.webhooks},r=JSON.stringify(n,null,2),o=`${this.filePath}.tmp`;dt.writeFileSync(o,r,{mode:384}),dt.renameSync(o,this.filePath);try{dt.chmodSync(this.filePath,384);}catch{}}};var Qi=class{_tool;_failureThreshold;_windowMs;_openDurationMs;_now;_state="closed";_failures=[];_openedAt=null;_probeInFlight=false;constructor(e,n={}){this._tool=e,this._failureThreshold=n.failureThreshold??3,this._windowMs=n.windowMs??6e4,this._openDurationMs=n.openDurationMs??6e4,this._now=n.now??(()=>Date.now());}get state(){return this._maybeTransitionToHalfOpen(),this._state}async call(e){if(this._maybeTransitionToHalfOpen(),this._state==="open"){let n=this._retryAfterMs();throw new Ht(`Circuit for tool "${this._tool}" is open after repeated failures.`,{details:{tool:this._tool,retryAfterMs:n}})}if(this._state==="half_open"){if(this._probeInFlight)throw new Ht(`Circuit for tool "${this._tool}" is half-open; probe already in flight.`,{details:{tool:this._tool,retryAfterMs:0}});this._probeInFlight=true;}try{let n=await e();return this._onSuccess(),n}catch(n){throw this._onFailure(),n}finally{this._state==="half_open"&&(this._probeInFlight=false);}}_maybeTransitionToHalfOpen(){if(this._state==="open"&&this._openedAt!==null){let e=this._now()-this._openedAt;e>=this._openDurationMs&&(this._state="half_open",this._probeInFlight=false,R.info({event:"circuit.half_open",tool:this._tool,elapsed:e},"circuit moved to half-open; probe allowed"));}}_onSuccess(){this._state==="half_open"?this._close("probe succeeded"):this._failures=[];}_onFailure(){let e=this._now();if(this._state==="half_open"){this._open(e,"probe failed");return}this._failures=this._failures.filter(n=>e-n<this._windowMs),this._failures.push(e),this._failures.length>=this._failureThreshold&&this._open(e,`${this._failures.length} failures within ${this._windowMs}ms`);}_open(e,n){this._state="open",this._openedAt=e,this._probeInFlight=false,R.warn({event:"circuit.opened",tool:this._tool,reason:n,openDurationMs:this._openDurationMs},"circuit opened \u2014 fast-failing calls");}_close(e){this._state="closed",this._openedAt=null,this._failures=[],this._probeInFlight=false,R.info({event:"circuit.closed",tool:this._tool,reason:e},"circuit closed \u2014 resuming normal operation");}_retryAfterMs(){if(this._openedAt===null)return this._openDurationMs;let e=this._now()-this._openedAt;return Math.max(0,this._openDurationMs-e)}},ec=class{_breakers=new Map;_defaults;constructor(e={}){this._defaults=e;}get(e){let n=this._breakers.get(e);return n||(n=new Qi(e,this._defaults),this._breakers.set(e,n)),n}clear(){this._breakers.clear();}get size(){return this._breakers.size}snapshot(){return Array.from(this._breakers.entries()).map(([e,n])=>({name:e,state:n.state}))}},tc=new ec;function Wb(t){return t instanceof Error?{errorCode:t.code??"OF_UNKNOWN",message:t.message}:{errorCode:"OF_UNKNOWN",message:String(t)}}async function Me(t,e){let n=[],r=[];for(let o=0;o<t.length;o++){let a=t[o];if(a!==void 0)try{let i=await e(a,o);n.push({index:o,value:i});}catch(i){r.push({index:o,...Wb(i)});}}return {succeeded:n,failed:r}}function X(t){return t.toISOString()}var fr=class{now;idCounter;tasks=new Map;projects=new Map;tags=new Map;folders=new Map;attachments=new Map;lastSyncAt=null;forecastTagId=null;constructor(e={}){this.now=e.now??(()=>new Date),this.idCounter=e.idSeed??0;}nextId(e,n){return this.idCounter+=1,n.of(`${e}_${this.idCounter.toString().padStart(6,"0")}`)}async listTasks(e){return Array.from(this.tasks.values()).filter(n=>this.matchesTask(n,e)).map(n=>this.withAlarms(n))}async getTask(e){let n=this.tasks.get(e);if(n===void 0)throw new P(`Task not found: ${e}`,{details:{resource:"task",id:e}});return this.withAlarms(n)}async getTasksMany(e){return e.map(n=>{let r=this.tasks.get(n);return r===void 0?null:this.withAlarms(r)})}withAlarms(e){let n=this.alarmsByTaskId.get(e.id);return n===void 0||n.length===0?e:{...e,notifications:n.map(r=>({...r}))}}async createTask(e){if(e.name.trim()==="")throw new y("Task name must be non-empty",{details:{field:"name"}});if(e.projectId!==void 0&&e.parentId!==void 0)throw new y("createTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(e.projectId!==void 0&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`,{details:{resource:"project",id:e.projectId}});if(e.parentId!==void 0&&!this.tasks.has(e.parentId))throw new P(`Parent task not found: ${e.parentId}`,{details:{resource:"task",id:e.parentId}});for(let a of e.tagIds??[])if(!this.tags.has(a))throw new P(`Tag not found: ${a}`,{details:{resource:"tag",id:a}});let n=this.nextId("task",h),r=X(this.now()),o={id:n,name:e.name,note:e.note??null,noteHtml:e.noteHtml??null,projectId:e.projectId??null,parentId:e.parentId??null,tagIds:[...e.tagIds??[]],deferDate:e.deferDate??null,...e.deferDateFloating?{deferDateFloating:true}:{},dueDate:e.dueDate??null,...e.dueDateFloating?{dueDateFloating:true}:{},estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,completed:false,completedAt:null,dropped:false,droppedAt:null,available:true,blocked:false,sequential:e.sequential??false,completedByChildren:e.completedByChildren??false,repetition:null,createdAt:r,modifiedAt:r};return this.tasks.set(n,o),this.bumpProjectTaskCount(o.projectId,1),n}async updateTask(e,n){let r=await this.getTask(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Task name must be non-empty",{details:{field:"name"}});if(n.tagIds!==void 0){for(let a of n.tagIds)if(!this.tags.has(a))throw new P(`Tag not found: ${a}`,{details:{resource:"tag",id:a}})}let o={...r,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.noteHtml!==void 0?{noteHtml:n.noteHtml}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.deferDateFloating===true?{deferDateFloating:true}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.dueDateFloating===true?{dueDateFloating:true}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.tagIds!==void 0?{tagIds:[...n.tagIds]}:{},...n.sequential!==void 0?{sequential:n.sequential}:{},...n.completedByChildren!==void 0?{completedByChildren:n.completedByChildren}:{},...n.repetition!==void 0?{repetition:n.repetition}:{},modifiedAt:X(this.now())};n.deferDateFloating===false&&delete o.deferDateFloating,n.dueDateFloating===false&&delete o.dueDateFloating,this.tasks.set(e,o);}async completeTask(e,n){return this.applyTaskCompletion(e,true,n)}async uncompleteTask(e){return this.applyTaskCompletion(e,false)}async dropTask(e,n){return this.applyTaskDropState(e,true,n)}async undropTask(e){return this.applyTaskDropState(e,false)}async applyTaskCompletion(e,n,r){let o=await this.getTask(e);if(!n&&!o.completed)return;let a=n?X(r??this.now()):null;this.tasks.set(e,{...o,completed:n,completedAt:a,modifiedAt:a??X(this.now())}),this.bumpProjectCompletedCount(o.projectId,n?1:-1);}async applyTaskDropState(e,n,r){let o=await this.getTask(e);if(!n&&!o.dropped)return;let a=n?X(r??this.now()):null;this.tasks.set(e,{...o,dropped:n,droppedAt:a,modifiedAt:a??X(this.now())});}async batchCreateTasks(e){return Me(e,n=>this.createTask(n))}async batchUpdateTasks(e){return Me(e,async({id:n,patch:r})=>(await this.updateTask(n,r),n))}async batchCompleteTasks(e){return Me(e,async({id:n,at:r})=>(await this.completeTask(n,r),n))}async batchUncompleteTasks(e){return Me(e,async({id:n})=>(await this.uncompleteTask(n),n))}async batchDeleteTasks(e){return Me(e,async({id:n})=>(await this.deleteTask(n),n))}async batchDropTasks(e){return Me(e,async({id:n})=>(await this.dropTask(n),n))}async batchUndropTasks(e){return Me(e,async({id:n})=>(await this.undropTask(n),n))}async deleteTask(e){let n=await this.getTask(e);this.tasks.delete(e),this.adjustProjectCountsForTask(n.projectId,n,-1);}async moveTask(e,n){let r=await this.getTask(e);if(n.projectId!==void 0&&n.parentId!==void 0)throw new y("moveTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(n.projectId!==void 0&&!this.projects.has(n.projectId))throw new P(`Project not found: ${n.projectId}`,{details:{resource:"project",id:n.projectId}});if(n.parentId!==void 0&&!this.tasks.has(n.parentId))throw new P(`Parent task not found: ${n.parentId}`,{details:{resource:"task",id:n.parentId}});this.adjustProjectCountsForTask(r.projectId,r,-1);let o=n.projectId??null;this.tasks.set(e,{...r,projectId:o,parentId:n.parentId??null,modifiedAt:X(this.now())}),this.adjustProjectCountsForTask(o,r,1);}async convertTaskToProject(e,n){let r=await this.getTask(e),o=k.of(e),a=X(this.now());return this.projects.set(o,{id:o,name:r.name,note:r.note??null,noteHtml:null,folderId:n.folderId??null,tagIds:r.tagIds??[],status:"active",completionCriterion:"parallel",flagged:r.flagged??false,deferDate:r.deferDate??null,dueDate:r.dueDate??null,estimatedMinutes:null,reviewIntervalDays:null,nextReviewDate:null,lastReviewDate:null,completed:false,completedAt:null,dropped:false,droppedAt:null,taskCount:0,completedTaskCount:0,createdAt:a,modifiedAt:a}),this.tasks.delete(e),o}async batchMoveTasks(e){return Me(e,async({id:n,destination:r})=>(await this.moveTask(n,r),n))}async duplicateTask(e,n){let r=await this.getTask(e),o,a;if(n.destination===void 0)o=r.projectId,a=r.parentId;else {let l=n.destination;if(("projectId"in l?1:0)+("parentId"in l?1:0)+("toInbox"in l&&l.toInbox===true?1:0)!==1)throw new y("duplicateTask: destination must specify exactly one of projectId, parentId, or toInbox",{details:{field:"destination"}});if("projectId"in l){if(!this.projects.has(l.projectId))throw new P(`Project not found: ${l.projectId}`,{details:{resource:"project",id:l.projectId}});o=l.projectId,a=null;}else if("parentId"in l){let f=this.tasks.get(l.parentId);if(f===void 0)throw new P(`Parent task not found: ${l.parentId}`,{details:{resource:"task",id:l.parentId}});o=f.projectId,a=l.parentId;}else o=null,a=null;}let i=l=>Array.from(this.tasks.values()).filter(m=>m.parentId===l),s=(l,m,f)=>{let g=this.nextId("task",h),v=X(this.now()),I={id:g,name:l.name,note:l.note,noteHtml:l.noteHtml,projectId:m,parentId:f,tagIds:[...l.tagIds],deferDate:l.deferDate,dueDate:l.dueDate,estimatedMinutes:l.estimatedMinutes,flagged:l.flagged,completed:false,completedAt:null,dropped:false,droppedAt:null,available:true,blocked:false,sequential:l.sequential,completedByChildren:l.completedByChildren,repetition:l.repetition,createdAt:v,modifiedAt:v};return this.tasks.set(g,I),this.bumpProjectTaskCount(m,1),g},c=s(r,o,a),d=0;if(n.recursive){let l=(m,f,g)=>{for(let v of i(m)){let I=s(v,g,f);d+=1,l(v.id,I,g);}};l(r.id,c,o);}return {newId:c,descendantCount:d}}async reorderTask(e,n){let r=await this.getTask(e),{newProjectId:o,newParentId:a,anchorMode:i,anchorId:s}=this.resolveReorderDestination(e,r,n);(o!==r.projectId||a!==r.parentId)&&(this.adjustProjectCountsForTask(r.projectId,r,-1),this.adjustProjectCountsForTask(o,r,1));let d={...r,projectId:o,parentId:a,modifiedAt:X(this.now())},l=[];for(let[g,v]of this.tasks)g!==e&&l.push([g,v]);let m=g=>g.projectId===o&&g.parentId===a,f;if(i==="start")f=l.findIndex(([,g])=>m(g)),f===-1&&(f=l.length);else if(i==="end"){let g=-1;l.forEach(([,v],I)=>{m(v)&&(g=I);}),f=g===-1?l.length:g+1;}else {let g=l.findIndex(([v])=>v===s);f=i==="before"?g:g+1;}this.tasks.clear(),l.forEach(([g,v],I)=>{I===f&&this.tasks.set(e,d),this.tasks.set(g,v);}),f>=l.length&&this.tasks.set(e,d);}resolveReorderDestination(e,n,r){if("before"in r||"after"in r){let c="before"in r?r.before:r.after,d=this.tasks.get(c);if(d===void 0)throw new P(`Reference task not found: ${c}`,{details:{resource:"task",id:c}});if(c===e)throw new y("reorderTask: reference must differ from the task id",{details:{field:"position"}});if(d.projectId!==n.projectId||d.parentId!==n.parentId)throw new y("reorderTask: reference task must share parent with the task being moved",{details:{field:"position"}});return {newProjectId:n.projectId,newParentId:n.parentId,anchorMode:"before"in r?"before":"after",anchorId:c}}let{at:o,in:a}=r,i,s;if("projectId"in a){if(!this.projects.has(a.projectId))throw new P(`Project not found: ${a.projectId}`,{details:{resource:"project",id:a.projectId}});i=a.projectId,s=null;}else if("parentId"in a){if(!this.tasks.has(a.parentId))throw new P(`Parent task not found: ${a.parentId}`,{details:{resource:"task",id:a.parentId}});if(a.parentId===e)throw new y("reorderTask: cannot reparent a task under itself",{details:{field:"position.in.parentId"}});i=this.tasks.get(a.parentId)?.projectId??null,s=a.parentId;}else i=null,s=null;return {newProjectId:i,newParentId:s,anchorMode:o,anchorId:null}}async listProjects(e={}){return Array.from(this.projects.values()).filter(n=>!(e.folderId!==void 0&&n.folderId!==e.folderId||e.status!==void 0&&n.status!==e.status))}async getProject(e){let n=this.projects.get(e);if(n===void 0)throw new P(`Project not found: ${e}`,{details:{resource:"project",id:e}});return n}async getProjectsMany(e){return e.map(n=>this.projects.get(n)??null)}async createProject(e){if(e.name.trim()==="")throw new y("Project name must be non-empty",{details:{field:"name"}});if(e.folderId!==void 0&&!this.folders.has(e.folderId))throw new P(`Folder not found: ${e.folderId}`,{details:{resource:"folder",id:e.folderId}});let n=this.nextId("proj",k),r=X(this.now()),o={id:n,name:e.name,note:e.note??null,noteHtml:e.noteHtml??null,folderId:e.folderId??null,tagIds:[...e.tagIds??[]],status:e.status??"active",completionCriterion:e.completionCriterion??"parallel",deferDate:e.deferDate??null,...e.deferDateFloating?{deferDateFloating:true}:{},dueDate:e.dueDate??null,...e.dueDateFloating?{dueDateFloating:true}:{},estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,reviewIntervalDays:e.reviewIntervalDays??null,nextReviewDate:null,lastReviewDate:null,completed:false,completedAt:null,dropped:false,droppedAt:null,taskCount:0,completedTaskCount:0,createdAt:r,modifiedAt:r};return this.projects.set(n,o),this.bumpFolderProjectCount(o.folderId,1),n}async updateProject(e,n){let r=await this.getProject(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Project name must be non-empty",{details:{field:"name"}});let o={...r,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.noteHtml!==void 0?{noteHtml:n.noteHtml}:{},...n.status!==void 0?{status:n.status}:{},...n.completionCriterion!==void 0?{completionCriterion:n.completionCriterion}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.deferDateFloating===true?{deferDateFloating:true}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.dueDateFloating===true?{dueDateFloating:true}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.tagIds!==void 0?{tagIds:[...n.tagIds]}:{},...n.reviewIntervalDays!==void 0?{reviewIntervalDays:n.reviewIntervalDays}:{},modifiedAt:X(this.now())};n.deferDateFloating===false&&delete o.deferDateFloating,n.dueDateFloating===false&&delete o.dueDateFloating,this.projects.set(e,o);}async completeProject(e,n){let r=await this.getProject(e),o=X(n??this.now());this.projects.set(e,{...r,status:"done",completed:true,completedAt:o,modifiedAt:o});}async dropProject(e,n){let r=await this.getProject(e),o=X(n??this.now());this.projects.set(e,{...r,status:"dropped",dropped:true,droppedAt:o,modifiedAt:o});}async batchCompleteProjects(e){return Me(e,async({id:n})=>(await this.completeProject(n),n))}async batchDropProjects(e){return Me(e,async({id:n})=>(await this.dropProject(n),n))}async moveProject(e,n){let r=await this.getProject(e);if(n.folderId!==null&&!this.folders.has(n.folderId))throw new P(`Folder not found: ${n.folderId}`,{details:{resource:"folder",id:n.folderId}});this.bumpFolderProjectCount(r.folderId,-1),this.projects.set(e,{...r,folderId:n.folderId,modifiedAt:X(this.now())}),this.bumpFolderProjectCount(n.folderId,1);}async deleteProject(e){let n=await this.getProject(e);for(let[r,o]of this.tasks)o.projectId===e&&this.tasks.set(r,{...o,projectId:null});this.projects.delete(e),this.bumpFolderProjectCount(n.folderId,-1);}async markProjectReviewed(e){let n=await this.getProject(e),r=this.now(),o=X(r),a=null;if(n.reviewIntervalDays!==null){let i=new Date(r);i.setUTCDate(i.getUTCDate()+n.reviewIntervalDays),a=X(i);}this.projects.set(e,{...n,lastReviewDate:o,nextReviewDate:a,modifiedAt:o});}async listProjectsDueForReview(){let e=new Date;return e.setUTCHours(23,59,59,999),[...this.projects.values()].filter(n=>n.nextReviewDate===null||new Date(n.nextReviewDate)<=e).sort((n,r)=>n.nextReviewDate===null&&r.nextReviewDate===null?0:n.nextReviewDate===null?-1:r.nextReviewDate===null?1:n.nextReviewDate.localeCompare(r.nextReviewDate))}async setProjectReviewInterval(e,n){let r=await this.getProject(e);this.projects.set(e,{...r,reviewIntervalDays:n});}async setProjectNextReviewDate(e,n){let r=await this.getProject(e);this.projects.set(e,{...r,nextReviewDate:n});}async listTags(e={}){return Array.from(this.tags.values()).filter(n=>!(e.parentId!==void 0&&n.parentId!==e.parentId||e.status!==void 0&&n.status!==e.status))}async getTag(e){let n=this.tags.get(e);if(n===void 0)throw new P(`Tag not found: ${e}`,{details:{resource:"tag",id:e}});return n}async getTagsMany(e){return e.map(n=>this.tags.get(n)??null)}async createTag(e){if(e.name.trim()==="")throw new y("Tag name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.tags.has(e.parentId))throw new P(`Parent tag not found: ${e.parentId}`,{details:{resource:"tag",id:e.parentId}});let n=this.nextId("tag",S),r=X(this.now()),o={id:n,name:e.name,parentId:e.parentId??null,status:e.status??"active",location:null,allowsNextAction:e.allowsNextAction??true,taskCount:0,createdAt:r,modifiedAt:r};return this.tags.set(n,o),n}async updateTag(e,n){let r=await this.getTag(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Tag name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.tags.has(n.parentId))throw new P(`Parent tag not found: ${n.parentId}`,{details:{resource:"tag",id:n.parentId}});this.tags.set(e,{...r,...n.name!==void 0?{name:n.name}:{},...n.parentId!==void 0?{parentId:n.parentId}:{},...n.status!==void 0?{status:n.status}:{},...n.allowsNextAction!==void 0?{allowsNextAction:n.allowsNextAction}:{},...n.location!==void 0?{location:n.location}:{},modifiedAt:X(this.now())});}async deleteTag(e){await this.getTag(e);for(let[n,r]of this.tasks)r.tagIds.includes(e)&&this.tasks.set(n,{...r,tagIds:r.tagIds.filter(o=>o!==e)});this.tags.delete(e);}async listFolders(e={}){return Array.from(this.folders.values()).filter(n=>!(e.parentId!==void 0&&n.parentId!==e.parentId))}async getFolder(e){let n=this.folders.get(e);if(n===void 0)throw new P(`Folder not found: ${e}`,{details:{resource:"folder",id:e}});return n}async createFolder(e){if(e.name.trim()==="")throw new y("Folder name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.folders.has(e.parentId))throw new P(`Parent folder not found: ${e.parentId}`,{details:{resource:"folder",id:e.parentId}});let n=this.nextId("fold",W),r=X(this.now()),o={id:n,name:e.name,parentId:e.parentId??null,projectCount:0,subfolderCount:0,createdAt:r,modifiedAt:r};return this.folders.set(n,o),o.parentId!==null&&this.bumpFolderSubfolderCount(o.parentId,1),n}async updateFolder(e,n){let r=await this.getFolder(e);if(n.name!==void 0&&n.name.trim()==="")throw new y("Folder name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.folders.has(n.parentId))throw new P(`Parent folder not found: ${n.parentId}`,{details:{resource:"folder",id:n.parentId}});n.parentId!==void 0&&n.parentId!==r.parentId&&(r.parentId!==null&&this.bumpFolderSubfolderCount(r.parentId,-1),n.parentId!==null&&this.bumpFolderSubfolderCount(n.parentId,1)),this.folders.set(e,{...r,...n.name!==void 0?{name:n.name}:{},...n.parentId!==void 0?{parentId:n.parentId}:{},modifiedAt:X(this.now())});}async deleteFolder(e){let n=await this.getFolder(e);if(n.projectCount>0||n.subfolderCount>0)throw new y(`Folder is not empty (projects=${n.projectCount}, subfolders=${n.subfolderCount})`,{details:{resource:"folder",id:e}});this.folders.delete(e),n.parentId!==null&&this.bumpFolderSubfolderCount(n.parentId,-1);}async searchTasks(e){let n=e.q!==void 0?e.q.toLowerCase():null,r=e.scope??"all";return Array.from(this.tasks.values()).filter(o=>{if(n!==null){let i=r!=="note"&&o.name.toLowerCase().includes(n),s=r!=="name"&&(o.note??"").toLowerCase().includes(n);if(!i&&!s)return false}if(e.projectId!==void 0&&o.projectId!==e.projectId)return false;if(e.tagIds!==void 0&&e.tagIds.length>0){let i=new Set(o.tagIds);if(!e.tagIds.every(s=>i.has(s)))return false}if(e.available!==void 0&&o.available!==e.available)return false;if(e.dueBefore!==void 0||e.dueAfter!==void 0){if(o.dueDate===null)return false;let i=new Date(o.dueDate);if(e.dueBefore!==void 0&&i>=new Date(e.dueBefore)||e.dueAfter!==void 0&&i<=new Date(e.dueAfter))return false}if(e.flagged!==void 0&&o.flagged!==e.flagged)return false;let a=o.completedAt!==null;return !(e.completed==="only"&&!a||e.completed==="exclude"&&a)})}async syncTrigger(){return this.lastSyncAt=X(this.now()),{lastSyncAt:this.lastSyncAt,inFlight:false}}async getLastSync(){return {lastSyncAt:this.lastSyncAt,inFlight:false}}alarmsByTaskId=new Map;async setTaskAlarms(e,n){if(!this.tasks.has(e))throw new P(`Task not found: ${e}`,{details:{resource:"task",id:e}});if(n.length===0){this.alarmsByTaskId.delete(e);return}this.alarmsByTaskId.set(e,n.map(r=>({...r})));}async clearTaskAlarms(e){if(!this.tasks.has(e))throw new P(`Task not found: ${e}`,{details:{resource:"task",id:e}});this.alarmsByTaskId.delete(e);}undoStackDepth=0;redoStackDepth=0;async undoLastMutation(){return this.undoStackDepth===0?{undid:false}:(this.undoStackDepth-=1,this.redoStackDepth+=1,{undid:true})}async redoLastMutation(){return this.redoStackDepth===0?{redid:false}:(this.redoStackDepth-=1,this.undoStackDepth+=1,{redid:true})}async listPerspectives(){let e={inbox:"Inbox",projects:"Projects",tags:"Tags",forecast:"Forecast",flagged:"Flagged",nearby:"Nearby",review:"Review"};return st.map(n=>({id:n,name:e[n]??n,kind:"builtin",requiresPro:false,icon:null}))}customPerspectives=new Map;seedCustomPerspective(e,n){this.customPerspectives.set(e,[...n]);}async evaluateCustomPerspective(e){let n=this.customPerspectives.get(e);if(n===void 0)throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});let r=[];for(let o of n){let a=this.tasks.get(o);a!==void 0&&r.push(a);}return r}dryRunRulesResults=new Map;seedPerspectiveRulesEvaluation(e,n,r){let o=JSON.stringify({aggregation:r??null,rules:e});this.dryRunRulesResults.set(o,[...n]);}async evaluatePerspectiveRules(e,n){let r=JSON.stringify({aggregation:n??null,rules:e}),o=this.dryRunRulesResults.get(r)??[],a=[];for(let i of o){let s=this.tasks.get(i);s!==void 0&&a.push(s);}return a}customPerspectiveDetails=new Map;seedCustomPerspectiveDetail(e){this.customPerspectiveDetails.set(e.id,e);}async getCustomPerspective(e){let n=this.customPerspectiveDetails.get(e);if(n===void 0)throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});return n}async deleteCustomPerspective(e){if(!(this.customPerspectives.delete(e)||this.customPerspectiveDetails.delete(e)))throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}})}async createCustomPerspective(e){if(e.name.length===0)throw new y("name is required and must be non-empty",{details:{field:"name"}});for(let o of this.customPerspectiveDetails.values())if(o.name===e.name)throw new y(`Duplicate perspective name: ${e.name}`,{details:{field:"name",existingId:o.id}});this.idCounter+=1;let n=`persp_${this.idCounter.toString().padStart(6,"0")}`,r={id:n,name:e.name,aggregation:e.aggregation??"all",rules:e.rules??[],iconColor:e.iconColor??null};return this.customPerspectiveDetails.set(n,r),this.customPerspectives.set(n,[]),n}async updateCustomPerspective(e,n){let r=this.customPerspectiveDetails.get(e);if(r===void 0)throw new P(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});if(n.name!==void 0){if(n.name.length===0)throw new y("name must be non-empty",{details:{field:"name"}});for(let a of this.customPerspectiveDetails.values())if(a.id!==e&&a.name===n.name)throw new y(`Duplicate perspective name: ${n.name}`,{details:{field:"name",existingId:a.id}})}let o={id:r.id,name:n.name??r.name,aggregation:n.aggregation??r.aggregation,rules:n.rules??r.rules,iconColor:n.iconColor===void 0?r.iconColor:n.iconColor===null?null:n.iconColor};this.customPerspectiveDetails.set(e,o);}async evaluatePerspective(e){let n=Array.from(this.tasks.values());if(e==="review"||e==="nearby")return [];if(e==="inbox")return n.filter(r=>r.projectId===null&&!r.completed&&!r.dropped);if(e==="flagged")return n.filter(r=>r.flagged&&!r.completed&&!r.dropped);if(e==="forecast"){let r=new Date;return r.setHours(23,59,59,999),n.filter(o=>o.dueDate!==null&&new Date(o.dueDate)<=r&&!o.completed&&!o.dropped)}return e==="projects"?n.filter(r=>r.projectId!==null&&!r.completed&&!r.dropped):e==="tags"?n.filter(r=>r.tagIds.length>0&&!r.completed&&!r.dropped):[]}async getForecast(e){let n=Array.from(this.tasks.values()).filter(f=>!f.completed&&!f.dropped),{from:r,to:o,includeOverdue:a=true,includeDeferred:i=true,includeFlagged:s=true}=e,c=a?n.filter(f=>f.dueDate!==null&&f.dueDate<r):[],d=n.filter(f=>f.dueDate!==null&&f.dueDate>=r&&f.dueDate<=o),l=i?n.filter(f=>f.deferDate!==null&&f.deferDate>=r&&f.deferDate<=o):[],m=s?n.filter(f=>f.flagged):[];return {overdue:c,dueToday:d,deferredToday:l,flagged:m}}async getForecastTag(){return {tagId:this.forecastTagId}}async setForecastTag(e){if(e!==null&&!this.tags.has(e))throw new P(`Tag not found: ${e}`);return this.forecastTagId=e,{tagId:e}}ownerKey(e){return e.taskId?`task:${e.taskId}`:`project:${e.projectId}`}async listAttachments(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);return Array.from(this.attachments.get(n)?.values()??[])}async addAttachment(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);this.attachments.has(n)||this.attachments.set(n,new Map);let r=this.nextId("att",Oe),o=e.filePath.split("/").pop()??"attachment",a={id:r,name:o,mimeType:null,sizeBytes:null,addedAt:X(this.now()),kind:"embedded"};return this.attachments.get(n).set(r,a),r}async removeAttachment(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e),r=this.attachments.get(n);if(!r?.has(e.attachmentId))throw new P(`Attachment not found: ${e.attachmentId}`);r.delete(e.attachmentId);}async saveAttachmentToPath(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new P(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new P(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);if(!this.attachments.get(n)?.has(e.attachmentId))throw new P(`Attachment not found: ${e.attachmentId}`);return {saved:true,path:e.destPath,sizeBytes:0}}async appLaunch(){return {launched:false,alreadyRunning:true}}windowPerspectiveName=null;windowFocusContainerIds=[];async getWindowState(){return {perspectiveName:this.windowPerspectiveName,focusContainerIds:[...this.windowFocusContainerIds]}}async setWindowPerspective(e){return this.windowPerspectiveName=e,{perspectiveName:e}}async setWindowFocus(e){if(e===null)return this.windowFocusContainerIds=[],{focusContainerIds:[]};let n=false;for(let r of this.projects.keys())if(String(r)===e){n=true;break}if(!n){for(let r of this.folders.keys())if(String(r)===e){n=true;break}}if(!n)throw new P(`Container not found (project or folder): ${e}`);return this.windowFocusContainerIds=[e],{focusContainerIds:[e]}}async appWindowNew(){return {perspectiveName:null,focusContainerIds:[]}}async appWindowNewTab(){return {perspectiveName:null,focusContainerIds:[]}}async pluginInvoke(e){throw new P("pluginInvoke is not supported by InMemoryAdapter \u2014 use a real OmniJsTransport for integration tests")}async getChangesSince(e){let n=new Date(e).getTime(),r=[...this.tasks.values()].filter(a=>new Date(a.modifiedAt).getTime()>=n).map(a=>a.id),o=[...this.projects.values()].filter(a=>new Date(a.modifiedAt).getTime()>=n).map(a=>a.id);return {taskIds:r,projectIds:o}}matchesTask(e,n){return !(n.projectId!==void 0&&e.projectId!==n.projectId||n.parentId!==void 0&&e.parentId!==n.parentId||n.tagId!==void 0&&!e.tagIds.includes(n.tagId)||n.flagged!==void 0&&e.flagged!==n.flagged||n.completed!==void 0&&e.completed!==n.completed||n.available!==void 0&&e.available!==n.available||n.blocked!==void 0&&e.blocked!==n.blocked||n.completedSince!==void 0&&(e.completedAt===null||e.completedAt<n.completedSince)||n.dueBefore!==void 0&&(e.dueDate===null||e.dueDate>=n.dueBefore)||n.dueAfter!==void 0&&(e.dueDate===null||e.dueDate<=n.dueAfter)||n.deferredBefore!==void 0&&(e.deferDate===null||e.deferDate>=n.deferredBefore)||n.deferredAfter!==void 0&&(e.deferDate===null||e.deferDate<=n.deferredAfter)||n.inbox===true&&e.projectId!==null)}bumpProjectTaskCount(e,n){if(e===null)return;let r=this.projects.get(e);r!==void 0&&this.projects.set(e,{...r,taskCount:Math.max(0,r.taskCount+n)});}adjustProjectCountsForTask(e,n,r){this.bumpProjectTaskCount(e,r),n.completed&&this.bumpProjectCompletedCount(e,r);}bumpProjectCompletedCount(e,n){if(e===null)return;let r=this.projects.get(e);r!==void 0&&this.projects.set(e,{...r,completedTaskCount:Math.max(0,r.completedTaskCount+n)});}bumpFolderProjectCount(e,n){if(e===null)return;let r=this.folders.get(e);r!==void 0&&this.folders.set(e,{...r,projectCount:Math.max(0,r.projectCount+n)});}bumpFolderSubfolderCount(e,n){let r=this.folders.get(e);r!==void 0&&this.folders.set(e,{...r,subfolderCount:Math.max(0,r.subfolderCount+n)});}};function ne(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"&&t.error!==null}function Pe(t,e){return {succeeded:t.succeeded.map(n=>({index:n.index,value:e(n.value)})),failed:t.failed}}var af=`/**
143
235
  * app_launch.js \u2014 launch OmniFocus explicitly (never automatic).
144
236
  *
145
237
  * Per SPEC resolved-decisions: automatic launch is out of scope \u2014 agents must
@@ -167,7 +259,7 @@ function run(_argv) {
167
259
 
168
260
  return JSON.stringify({ launched: !alreadyRunning, alreadyRunning });
169
261
  }
170
- `;var cu=`/**
262
+ `;var sf=`/**
171
263
  * JXA: add an attachment to a task or project from a local file path.
172
264
  *
173
265
  * Args (argv[0] JSON): { taskId?: string, projectId?: string, filePath: string }
@@ -215,7 +307,7 @@ function run(argv) {
215
307
  const newAtt = atts[atts.length - 1];
216
308
  return JSON.stringify({ id: newAtt.id() });
217
309
  }
218
- `;var du=`/**
310
+ `;var cf=`/**
219
311
  * JXA: list all attachments on a task or project.
220
312
  *
221
313
  * Args (argv[0] JSON): { taskId?: string, projectId?: string }
@@ -301,7 +393,7 @@ function run(argv) {
301
393
 
302
394
  return JSON.stringify({ attachments: result });
303
395
  }
304
- `;var lu=`/**
396
+ `;var df=`/**
305
397
  * JXA: remove an attachment by ID from a task or project.
306
398
  *
307
399
  * Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string }
@@ -353,7 +445,7 @@ function run(argv) {
353
445
 
354
446
  return JSON.stringify({});
355
447
  }
356
- `;var pu=`/**
448
+ `;var lf=`/**
357
449
  * JXA: copy an attachment's content to a local file path.
358
450
  *
359
451
  * Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string, destPath: string }
@@ -452,7 +544,7 @@ function run(argv) {
452
544
 
453
545
  return JSON.stringify({ saved: true, path: destPath, sizeBytes: sizeBytes });
454
546
  }
455
- `;var uu=`/**
547
+ `;var pf=`/**
456
548
  * JXA: return task and project IDs modified since a given timestamp.
457
549
  *
458
550
  * This script powers the richer change-semantics layer: when the
@@ -526,7 +618,7 @@ function run(argv) {
526
618
 
527
619
  return JSON.stringify({ tasks, projects });
528
620
  }
529
- `;var mu=`/**
621
+ `;var uf=`/**
530
622
  * JXA: create a folder, optionally under a parent folder.
531
623
  *
532
624
  * Args (argv[0] JSON): { name: string, parentId?: string }
@@ -591,7 +683,7 @@ function run(argv) {
591
683
 
592
684
  return JSON.stringify({ folder: buildFolder(newFolder) });
593
685
  }
594
- `;var fu=`/**
686
+ `;var mf=`/**
595
687
  * JXA: delete a folder by ID. Refuses if the folder has projects or subfolders.
596
688
  *
597
689
  * Args (argv[0] JSON): { id: string }
@@ -629,7 +721,7 @@ function run(argv) {
629
721
 
630
722
  return JSON.stringify({ id: args.id });
631
723
  }
632
- `;var gu=`/**
724
+ `;var ff=`/**
633
725
  * JXA: fetch one folder by ID.
634
726
  *
635
727
  * Args (argv[0] JSON): { id: string }
@@ -676,7 +768,7 @@ function run(argv) {
676
768
 
677
769
  throw new Error(\`Folder not found: \${args.id}\`);
678
770
  }
679
- `;var hu=`/**
771
+ `;var gf=`/**
680
772
  * JXA: list all folders, optionally filtered by parentId.
681
773
  *
682
774
  * Args (argv[0] JSON): { parentId?: string }
@@ -761,7 +853,7 @@ function run(argv) {
761
853
 
762
854
  return JSON.stringify({ folders: result });
763
855
  }
764
- `;var yu=`/**
856
+ `;var hf=`/**
765
857
  * JXA: rename a folder.
766
858
  *
767
859
  * Args (argv[0] JSON): { id: string, name: string }
@@ -813,7 +905,7 @@ function run(argv) {
813
905
 
814
906
  return JSON.stringify({ folder: buildFolder(target) });
815
907
  }
816
- `;var Iu=`/**
908
+ `;var yf=`/**
817
909
  * JXA: get forecast-view tasks grouped by category (overdue, dueToday,
818
910
  * deferredToday, flagged).
819
911
  *
@@ -1077,7 +1169,7 @@ function run(argv) {
1077
1169
  flagged: flaggedTasks,
1078
1170
  });
1079
1171
  }
1080
- `;var ku=`/**
1172
+ `;var kf=`/**
1081
1173
  * JXA: evaluate a built-in OmniFocus perspective and return its task list.
1082
1174
  *
1083
1175
  * Args (argv[0] JSON): { "perspectiveId": "inbox" | "projects" | "tags" | "forecast" | "flagged" | "nearby" | "review" }
@@ -1306,7 +1398,7 @@ function run(argv) {
1306
1398
  return JSON.stringify({ error: String(e) });
1307
1399
  }
1308
1400
  }
1309
- `;var Tu=`/**
1401
+ `;var If=`/**
1310
1402
  * JXA: list all perspectives (built-in + custom).
1311
1403
  *
1312
1404
  * Args (argv[0] JSON): {} (no arguments)
@@ -1393,7 +1485,7 @@ function run(_argv) {
1393
1485
 
1394
1486
  return JSON.stringify({ perspectives });
1395
1487
  }
1396
- `;var vu=`/**
1488
+ `;var vf=`/**
1397
1489
  * JXA: batch-complete projects in a single round-trip.
1398
1490
  *
1399
1491
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -1453,7 +1545,7 @@ function run(argv) {
1453
1545
 
1454
1546
  return JSON.stringify({ succeeded: succeeded, failed: failed });
1455
1547
  }
1456
- `;var wu=`/**
1548
+ `;var Tf=`/**
1457
1549
  * JXA: batch-drop projects in a single round-trip.
1458
1550
  *
1459
1551
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -1514,7 +1606,7 @@ function run(argv) {
1514
1606
 
1515
1607
  return JSON.stringify({ succeeded: succeeded, failed: failed });
1516
1608
  }
1517
- `;var Su=`/**
1609
+ `;var wf=`/**
1518
1610
  * JXA: mark a project complete.
1519
1611
  *
1520
1612
  * Args (argv[0] JSON): { id: string, completionDate?: string|null }
@@ -1550,7 +1642,7 @@ function run(argv) {
1550
1642
 
1551
1643
  return JSON.stringify({ id: args.id });
1552
1644
  }
1553
- `;var ju=`/**
1645
+ `;var Sf=`/**
1554
1646
  * JXA: create a new project.
1555
1647
  *
1556
1648
  * Args (argv[0] JSON): { name: string, folderId?: string|null, note?: string|null,
@@ -1743,7 +1835,7 @@ function run(argv) {
1743
1835
 
1744
1836
  return JSON.stringify({ project: buildProject(newProj) });
1745
1837
  }
1746
- `;var bu=`/**
1838
+ `;var bf=`/**
1747
1839
  * JXA: delete a project by ID.
1748
1840
  *
1749
1841
  * Args (argv[0] JSON): { id: string }
@@ -1773,7 +1865,7 @@ function run(argv) {
1773
1865
 
1774
1866
  return JSON.stringify({ id: args.id });
1775
1867
  }
1776
- `;var _u=`/**
1868
+ `;var jf=`/**
1777
1869
  * JXA: mark a project dropped.
1778
1870
  *
1779
1871
  * Args (argv[0] JSON): { id: string }
@@ -1803,7 +1895,7 @@ function run(argv) {
1803
1895
 
1804
1896
  return JSON.stringify({ id: args.id });
1805
1897
  }
1806
- `;var Pu=`/**
1898
+ `;var _f=`/**
1807
1899
  * JXA: fetch one project by ID.
1808
1900
  *
1809
1901
  * Args (argv[0] JSON): { id: string }
@@ -1973,7 +2065,7 @@ function run(argv) {
1973
2065
 
1974
2066
  throw new Error(\`Project not found: \${args.id}\`);
1975
2067
  }
1976
- `;var Ou=`/**
2068
+ `;var Pf=`/**
1977
2069
  * JXA: fetch multiple projects by IDs.
1978
2070
  *
1979
2071
  * Args (argv[0] JSON): { ids: string[] }
@@ -2152,7 +2244,7 @@ function run(argv) {
2152
2244
 
2153
2245
  return JSON.stringify({ projects: results });
2154
2246
  }
2155
- `;var xu=`/**
2247
+ `;var Of=`/**
2156
2248
  * JXA: list projects, optionally filtered by folderId or status.
2157
2249
  *
2158
2250
  * Args (argv[0] JSON): { folderId?: string|null, status?: string|null }
@@ -2334,7 +2426,7 @@ function run(argv) {
2334
2426
 
2335
2427
  return JSON.stringify({ projects: result });
2336
2428
  }
2337
- `;var Du=`/**
2429
+ `;var xf=`/**
2338
2430
  * JXA: mark a project as reviewed (sets lastReviewDate to now).
2339
2431
  *
2340
2432
  * Args (argv[0] JSON): { id: string }
@@ -2365,7 +2457,7 @@ function run(argv) {
2365
2457
 
2366
2458
  return JSON.stringify({ id: args.id });
2367
2459
  }
2368
- `;var Au=`/**
2460
+ `;var Df=`/**
2369
2461
  * JXA: move a project to a folder (or to the root if folderId is null).
2370
2462
  *
2371
2463
  * Args (argv[0] JSON): { id: string, folderId?: string|null }
@@ -2401,7 +2493,7 @@ function run(argv) {
2401
2493
 
2402
2494
  return JSON.stringify({ id: args.id });
2403
2495
  }
2404
- `;var Cu=`/**
2496
+ `;var Rf=`/**
2405
2497
  * JXA: set a project's next review date.
2406
2498
  *
2407
2499
  * The third axis of OmniFocus's review schedule (the others being
@@ -2443,7 +2535,7 @@ function run(argv) {
2443
2535
 
2444
2536
  return JSON.stringify({ id: args.id });
2445
2537
  }
2446
- `;var Ru=`/**
2538
+ `;var Af=`/**
2447
2539
  * JXA: set a project's review interval.
2448
2540
  *
2449
2541
  * Args (argv[0] JSON): { id: string, days: number | null }
@@ -2473,7 +2565,7 @@ function run(argv) {
2473
2565
 
2474
2566
  return JSON.stringify({ id: args.id });
2475
2567
  }
2476
- `;var Mu=`/**
2568
+ `;var Cf=`/**
2477
2569
  * JXA: update mutable fields on an existing project.
2478
2570
  *
2479
2571
  * Args (argv[0] JSON): { id: string, name?: string, note?: string|null,
@@ -2669,7 +2761,7 @@ function run(argv) {
2669
2761
 
2670
2762
  return JSON.stringify({ project: buildProject(target) });
2671
2763
  }
2672
- `;var Fu=`/**
2764
+ `;var Ef=`/**
2673
2765
  * JXA: list projects due for review (nextReviewDate <= today, or null).
2674
2766
  *
2675
2767
  * Args (argv[0] JSON): {}
@@ -2736,7 +2828,7 @@ function run(argv) {
2736
2828
 
2737
2829
  return JSON.stringify({ projects: due });
2738
2830
  }
2739
- `;var Eu=`/**
2831
+ `;var Mf=`/**
2740
2832
  * JXA: trigger Omni Sync.
2741
2833
  *
2742
2834
  * \`Application("OmniFocus").defaultDocument.synchronize()\` kicks off a sync
@@ -2759,7 +2851,7 @@ function run(_argv) {
2759
2851
  const lastSyncAt = new Date().toISOString();
2760
2852
  return JSON.stringify({ lastSyncAt: lastSyncAt, inFlight: false });
2761
2853
  }
2762
- `;var Nu=`/**
2854
+ `;var Nf=`/**
2763
2855
  * JXA: create a tag, optionally under a parent tag.
2764
2856
  *
2765
2857
  * Args (argv[0] JSON): { name: string, parentId?: string }
@@ -2857,7 +2949,7 @@ function run(argv) {
2857
2949
  const fetchedTag = doc.flattenedTags.byId(tagId);
2858
2950
  return JSON.stringify({ tag: buildTag(fetchedTag) });
2859
2951
  }
2860
- `;var Uu=`/**
2952
+ `;var Ff=`/**
2861
2953
  * JXA: delete a tag by ID.
2862
2954
  *
2863
2955
  * Args (argv[0] JSON): { id: string }
@@ -2887,7 +2979,7 @@ function run(argv) {
2887
2979
 
2888
2980
  return JSON.stringify({ id: args.id });
2889
2981
  }
2890
- `;var Lu=`/**
2982
+ `;var Uf=`/**
2891
2983
  * JXA: fetch one tag by ID.
2892
2984
  *
2893
2985
  * Args (argv[0] JSON): { id: string }
@@ -2967,7 +3059,7 @@ function run(argv) {
2967
3059
 
2968
3060
  throw new Error(\`Tag not found: \${args.id}\`);
2969
3061
  }
2970
- `;var Ju=`/**
3062
+ `;var Lf=`/**
2971
3063
  * JXA: fetch multiple tags by IDs.
2972
3064
  *
2973
3065
  * Args (argv[0] JSON): { ids: string[] }
@@ -3058,7 +3150,7 @@ function run(argv) {
3058
3150
 
3059
3151
  return JSON.stringify({ tags: results });
3060
3152
  }
3061
- `;var Bu=`/**
3153
+ `;var Bf=`/**
3062
3154
  * JXA: list all tags, optionally filtered by parentId or status.
3063
3155
  *
3064
3156
  * Args (argv[0] JSON): { parentId?: string, status?: string }
@@ -3159,7 +3251,7 @@ function run(argv) {
3159
3251
 
3160
3252
  return JSON.stringify({ tags: result });
3161
3253
  }
3162
- `;var $u=`/**
3254
+ `;var Jf=`/**
3163
3255
  * JXA: update mutable fields on an existing tag.
3164
3256
  *
3165
3257
  * Args (argv[0] JSON): { id: string, name?: string, status?: string, allowsNextAction?: boolean }
@@ -3250,7 +3342,7 @@ function run(argv) {
3250
3342
 
3251
3343
  return JSON.stringify({ tag: buildTag(target) });
3252
3344
  }
3253
- `;var Wu=`/**
3345
+ `;var $f=`/**
3254
3346
  * JXA: batch-complete tasks in a single round-trip.
3255
3347
  *
3256
3348
  * Args (argv[0] JSON): { items: Array<{ id, at? }> }
@@ -3297,7 +3389,7 @@ function run(argv) {
3297
3389
 
3298
3390
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3299
3391
  }
3300
- `;var Hu=`/**
3392
+ `;var Wf=`/**
3301
3393
  * JXA: batch-create tasks in a single round-trip.
3302
3394
  *
3303
3395
  * Args (argv[0] JSON): { inputs: CreateTaskInput[] }
@@ -3373,7 +3465,7 @@ function run(argv) {
3373
3465
 
3374
3466
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3375
3467
  }
3376
- `;var Gu=`/**
3468
+ `;var Hf=`/**
3377
3469
  * JXA: batch-delete tasks permanently in a single round-trip.
3378
3470
  *
3379
3471
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3413,7 +3505,7 @@ function run(argv) {
3413
3505
 
3414
3506
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3415
3507
  }
3416
- `;var zu=`/**
3508
+ `;var zf=`/**
3417
3509
  * JXA: batch-drop (cancel) tasks in a single round-trip.
3418
3510
  *
3419
3511
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3456,7 +3548,7 @@ function run(argv) {
3456
3548
 
3457
3549
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3458
3550
  }
3459
- `;var Vu=`/**
3551
+ `;var Gf=`/**
3460
3552
  * JXA: batch-uncomplete (mark incomplete) tasks in a single round-trip.
3461
3553
  *
3462
3554
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3518,7 +3610,7 @@ function run(argv) {
3518
3610
 
3519
3611
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3520
3612
  }
3521
- `;var qu=`/**
3613
+ `;var qf=`/**
3522
3614
  * JXA: batch-undrop (restore) tasks in a single round-trip.
3523
3615
  *
3524
3616
  * Args (argv[0] JSON): { items: Array<{ id: string }> }
@@ -3562,7 +3654,7 @@ function run(argv) {
3562
3654
 
3563
3655
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3564
3656
  }
3565
- `;var Ku=`/**
3657
+ `;var Vf=`/**
3566
3658
  * JXA: batch-update tasks in a single round-trip.
3567
3659
  *
3568
3660
  * Args (argv[0] JSON): { updates: Array<{ id, patch }> }
@@ -3635,7 +3727,7 @@ function run(argv) {
3635
3727
 
3636
3728
  return JSON.stringify({ succeeded: succeeded, failed: failed });
3637
3729
  }
3638
- `;var Xu=`/**
3730
+ `;var Kf=`/**
3639
3731
  * JXA: mark a task complete.
3640
3732
  *
3641
3733
  * Args (argv[0] JSON): { id: string, completionDate?: string|null }
@@ -3670,7 +3762,7 @@ function run(argv) {
3670
3762
 
3671
3763
  return JSON.stringify({ id: args.id });
3672
3764
  }
3673
- `;var Yu=`/**
3765
+ `;var Xf=`/**
3674
3766
  * JXA: create a new task.
3675
3767
  *
3676
3768
  * Args (argv[0] JSON): {
@@ -3902,7 +3994,7 @@ function run(argv) {
3902
3994
  }
3903
3995
  return JSON.stringify({ task: buildTask(newTask) });
3904
3996
  }
3905
- `;var Qu=`/**
3997
+ `;var Yf=`/**
3906
3998
  * JXA: delete a task permanently.
3907
3999
  *
3908
4000
  * Args (argv[0] JSON): { id: string }
@@ -3931,7 +4023,7 @@ function run(argv) {
3931
4023
 
3932
4024
  return JSON.stringify({ id: args.id });
3933
4025
  }
3934
- `;var Zu=`/**
4026
+ `;var Zf=`/**
3935
4027
  * JXA: mark a task as dropped.
3936
4028
  *
3937
4029
  * Args (argv[0] JSON): { id: string, droppedAt?: string|null }
@@ -3955,7 +4047,7 @@ function run(argv) {
3955
4047
 
3956
4048
  return JSON.stringify({ id: args.id });
3957
4049
  }
3958
- `;var em=`/**
4050
+ `;var Qf=`/**
3959
4051
  * JXA: duplicate a task. Editable fields copy; completed/dropped state reset.
3960
4052
  * When recursive=true, subtask tree is cloned depth-first, preserving order.
3961
4053
  *
@@ -4072,7 +4164,7 @@ function run(argv) {
4072
4164
 
4073
4165
  return JSON.stringify({ newId: rootClone.id(), descendantCount: descendantCount });
4074
4166
  }
4075
- `;var tm=`/**
4167
+ `;var eg=`/**
4076
4168
  * JXA: fetch one task by ID.
4077
4169
  *
4078
4170
  * Args (argv[0] JSON): { id: string }
@@ -4242,7 +4334,7 @@ function run(argv) {
4242
4334
 
4243
4335
  throw new Error(\`Task not found: \${args.id}\`);
4244
4336
  }
4245
- `;var nm=`/**
4337
+ `;var tg=`/**
4246
4338
  * JXA: fetch multiple tasks by IDs.
4247
4339
  *
4248
4340
  * Args (argv[0] JSON): { ids: string[] }
@@ -4423,7 +4515,7 @@ function run(argv) {
4423
4515
 
4424
4516
  return JSON.stringify({ tasks: results });
4425
4517
  }
4426
- `;var rm=`/**
4518
+ `;var ng=`/**
4427
4519
  * JXA: list tasks, optionally filtered.
4428
4520
  *
4429
4521
  * Args (argv[0] JSON): {
@@ -4663,7 +4755,7 @@ function run(argv) {
4663
4755
 
4664
4756
  return JSON.stringify({ tasks: result });
4665
4757
  }
4666
- `;var om=`/**
4758
+ `;var rg=`/**
4667
4759
  * JXA: move a task to a different project or parent task.
4668
4760
  *
4669
4761
  * Args (argv[0] JSON): { id: string, projectId?: string|null, parentId?: string|null }
@@ -4702,7 +4794,7 @@ function run(argv) {
4702
4794
 
4703
4795
  return JSON.stringify({ id: args.id });
4704
4796
  }
4705
- `;var am=`/**
4797
+ `;var og=`/**
4706
4798
  * JXA: search tasks by keyword and/or structured filters.
4707
4799
  *
4708
4800
  * Args (argv[0] JSON): {
@@ -4934,7 +5026,7 @@ function run(argv) {
4934
5026
 
4935
5027
  return JSON.stringify({ tasks: result });
4936
5028
  }
4937
- `;var sm=`/**
5029
+ `;var ag=`/**
4938
5030
  * JXA: mark a task incomplete.
4939
5031
  *
4940
5032
  * Args (argv[0] JSON): { id: string }
@@ -4963,7 +5055,7 @@ function run(argv) {
4963
5055
 
4964
5056
  return JSON.stringify({ id: args.id });
4965
5057
  }
4966
- `;var im=`/**
5058
+ `;var sg=`/**
4967
5059
  * JXA: remove dropped status from a task.
4968
5060
  *
4969
5061
  * Args (argv[0] JSON): { id: string }
@@ -4987,7 +5079,7 @@ function run(argv) {
4987
5079
 
4988
5080
  return JSON.stringify({ id: args.id });
4989
5081
  }
4990
- `;var cm=`/**
5082
+ `;var ig=`/**
4991
5083
  * JXA: update task fields.
4992
5084
  *
4993
5085
  * Args (argv[0] JSON): {
@@ -5202,7 +5294,7 @@ function run(argv) {
5202
5294
 
5203
5295
  return JSON.stringify({ task: buildTask(found) });
5204
5296
  }
5205
- `;var dm=`/**
5297
+ `;var cg=`/**
5206
5298
  * JXA: read the current state of OmniFocus's front window.
5207
5299
  *
5208
5300
  * Returns JSON: { perspectiveName, focusContainerIds }
@@ -5240,7 +5332,7 @@ function run(_argv) {
5240
5332
  const focusContainerIds = [];
5241
5333
  try {
5242
5334
  const focus = w.focus();
5243
- if (focus && focus.length) {
5335
+ if (focus?.length) {
5244
5336
  for (let i = 0; i < focus.length; i++) {
5245
5337
  try {
5246
5338
  focusContainerIds.push(String(focus[i].id()));
@@ -5255,7 +5347,7 @@ function run(_argv) {
5255
5347
 
5256
5348
  return JSON.stringify({ perspectiveName, focusContainerIds });
5257
5349
  }
5258
- `;var lm=`/**
5350
+ `;var dg=`/**
5259
5351
  * JXA: set or clear the front window's focus container.
5260
5352
  *
5261
5353
  * Args (argv[0] JSON): { containerId: string | null }
@@ -5331,7 +5423,7 @@ function run(argv) {
5331
5423
  w.focus = [target];
5332
5424
  return JSON.stringify({ focusContainerIds: [args.containerId] });
5333
5425
  }
5334
- `;var pm=`/**
5426
+ `;var lg=`/**
5335
5427
  * JXA: switch the front window to a named perspective.
5336
5428
  *
5337
5429
  * Args (argv[0] JSON): { perspectiveName: string }
@@ -5383,7 +5475,7 @@ function run(argv) {
5383
5475
  w.perspective = target;
5384
5476
  return JSON.stringify({ perspectiveName: args.perspectiveName });
5385
5477
  }
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=`/**
5478
+ `;var ug=new AsyncLocalStorage;function mg(t,e){let n=ulid();return ug.run(n,t)}function ze(){return ug.getStore()}function fg(){return ulid()}function Gb(t){let n=typeof t=="object"&&t!==null&&!Array.isArray(t)?JSON.stringify(t,Object.keys(t).sort()):JSON.stringify(t??null);return createHash("sha1").update(n).digest("hex").slice(0,16)}function gr(t,e,n,r,o){R.debug({event:"transport.call",transport:t,scriptName:e,argsHash:Gb(n),durationMs:r,outcome:o,correlationId:ze()},"transport call");}var Vb=16*1024*1024,Kb=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-",e],{timeout:n,maxBuffer:Vb,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,i,s)=>{let c=i,d=s,l=a!==null&&a.killed===true,m=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:d,exitCode:a===null?0:a.code??1,timedOut:l,...m!==void 0?{spawnError:m}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");});async function O(t,e={},n={}){let r=n.spawner??Kb,o=n.timeoutMs??3e4,a=JSON.stringify(e??{}),i=n.scriptName,s=performance.now(),c=await r(t,a,o),d=Math.round(performance.now()-s),l=c.spawnError!==void 0||c.timedOut||c.exitCode!==0||c.stdout.trim()===""?"error":"ok";if(gr("jxa",i,e,d,l),c.spawnError!==void 0)throw new ht("Failed to spawn osascript",{cause:c.spawnError,details:{transport:"jxa",reason:c.spawnError.code??"spawn-failed",...i!==void 0?{scriptName:i}:{}}});if(c.timedOut){let f=i!==void 0?` (script: ${i})`:"";throw new gt(`JXA script exceeded ${o}ms timeout${f}`,{details:{transport:"jxa",timeoutMs:o,...i!==void 0?{scriptName:i}:{}}})}if(c.exitCode!==0){let f=Xb(c.stderr,i);if(f!==null)throw f;let g=i!==void 0?` [${i}]`:"";throw new N(`JXA script failed (exit ${c.exitCode})${g}`,{details:{transport:"jxa",exitCode:c.exitCode,stderr:lt(c.stderr,1024),...i!==void 0?{scriptName:i}:{}}})}let m=c.stdout.trim();if(m==="")throw new N("JXA script returned empty stdout \u2014 `run()` must return a JSON-encoded string",{details:{transport:"jxa",...i!==void 0?{scriptName:i}:{}}});try{return JSON.parse(m)}catch(f){throw new N("JXA script returned malformed JSON",{cause:f,details:{transport:"jxa",stdoutPreview:lt(m,200),...i!==void 0?{scriptName:i}:{}}})}}function Xb(t,e){return /Application can't be found/i.test(t)||/Application isn['’]t running/i.test(t)||/OmniFocus(?:.*)not running/i.test(t)?new ut({details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/-1743\b/.test(t)||/not authori[sz]ed to send Apple events/i.test(t)||/errAEEventNotPermitted/i.test(t)||/not allowed assistive access/i.test(t)?new mt({details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/\bnot found\b/i.test(t)||/^OF_NOT_FOUND\b/m.test(t)?new P(t,{details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_VALIDATION\b/m.test(t)||/\bValidationError:/m.test(t)||/\bis required\b/i.test(t)?new y(t,{details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_CONFLICT\b/m.test(t)?new ft(t,{details:{transport:"jxa",stderr:lt(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function lt(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function nc(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"}var hr=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return (await O(ng,{projectId:e.projectId??null,tagId:e.tagId??null,parentId:e.parentId??null,flagged:e.flagged??null,available:e.available??null,blocked:e.blocked??null,completed:e.completed??null,completedSince:e.completedSince??null,dueBefore:e.dueBefore??null,dueAfter:e.dueAfter??null,deferredBefore:e.deferredBefore??null,deferredAfter:e.deferredAfter??null},{...this.runOpts,scriptName:"task_list"})).tasks.map(r=>({...r,id:h.of(r.id)}))}async getTask(e){let n=await O(eg,{id:e},{...this.runOpts,scriptName:"task_get"});return {...n.task,id:h.of(n.task.id)}}async getTasksMany(e){return (await O(tg,{ids:e},{...this.runOpts,scriptName:"task_get_many"})).tasks.map(r=>r?{...r,id:h.of(r.id)}:null)}async createTask(e){let n=await O(Xf,{name:e.name,projectId:e.projectId??null,parentId:e.parentId??null,note:e.note??null,flagged:e.flagged??false,deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,tagIds:e.tagIds??[],sequential:e.sequential??false,completedByChildren:e.completedByChildren??false},{...this.runOpts,scriptName:"task_create"});return h.of(n.task.id)}async updateTask(e,n){await O(ig,{id:e,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.tagIds!==void 0?{tagIds:n.tagIds}:{},...n.sequential!==void 0?{sequential:n.sequential}:{},...n.completedByChildren!==void 0?{completedByChildren:n.completedByChildren}:{}},{...this.runOpts,scriptName:"task_update"});}async completeTask(e,n){await O(Kf,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"task_complete"});}async uncompleteTask(e){await O(ag,{id:e},{...this.runOpts,scriptName:"task_uncomplete"});}async dropTask(e,n){await O(Zf,{id:e,droppedAt:n?.toISOString()??null},{...this.runOpts,scriptName:"task_drop"});}async undropTask(e){await O(sg,{id:e},{...this.runOpts,scriptName:"task_undrop"});}async deleteTask(e){await O(Yf,{id:e},{...this.runOpts,scriptName:"task_delete"});}async moveTask(e,n){await O(rg,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});}async convertTaskToProject(e,n){throw new N("convertTaskToProject routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"convertTaskToProject"}})}async batchMoveTasks(e){throw new N("batchMoveTasks routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"batchMoveTasks"}})}async reorderTask(e,n){throw new N("reorderTask routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"reorderTask"}})}async duplicateTask(e,n){let r=n.destination===void 0?void 0:"projectId"in n.destination?{projectId:n.destination.projectId}:"parentId"in n.destination?{parentId:n.destination.parentId}:{toInbox:true},o=await O(Qf,{id:e,recursive:n.recursive,destination:r},{...this.runOpts,scriptName:"task_duplicate"});return {newId:h.of(o.newId),descendantCount:o.descendantCount}}async batchCreateTasks(e){let n=await O(Wf,{inputs:e.map(r=>({name:r.name,projectId:r.projectId??null,parentId:r.parentId??null,note:r.note??null,flagged:r.flagged??false,deferDate:r.deferDate??null,dueDate:r.dueDate??null,estimatedMinutes:r.estimatedMinutes??null,tagIds:r.tagIds??[],sequential:r.sequential??false,completedByChildren:r.completedByChildren??false}))},{...this.runOpts,scriptName:"task_batch_create"});return Pe(n,h.of)}async batchUpdateTasks(e){let n=await O(Vf,{updates:e},{...this.runOpts,scriptName:"task_batch_update"});return Pe(n,h.of)}async batchCompleteTasks(e){let n=await O($f,{items:e.map(r=>({id:r.id,at:r.at?.toISOString()??null}))},{...this.runOpts,scriptName:"task_batch_complete"});return Pe(n,h.of)}async batchUncompleteTasks(e){let n=await O(Gf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_uncomplete"});return Pe(n,h.of)}async batchDeleteTasks(e){let n=await O(Hf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_delete"});return Pe(n,h.of)}async batchDropTasks(e){let n=await O(zf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_drop"});return Pe(n,h.of)}async batchUndropTasks(e){let n=await O(qf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_undrop"});return Pe(n,h.of)}async listProjects(e){return (await O(Of,{folderId:e?.folderId??null,status:e?.status??null},{...this.runOpts,scriptName:"project_list"})).projects.map(r=>({...r,id:k.of(r.id)}))}async getProject(e){let n=await O(_f,{id:e},{...this.runOpts,scriptName:"project_get"});return {...n.project,id:k.of(n.project.id)}}async getProjectsMany(e){return (await O(Pf,{ids:e},{...this.runOpts,scriptName:"project_get_many"})).projects.map(r=>r?{...r,id:k.of(r.id)}:null)}async createProject(e){let n=await O(Sf,{name:e.name,folderId:e.folderId??null,note:e.note??null,deferDate:e.deferDate??null,dueDate:e.dueDate??null,estimatedMinutes:e.estimatedMinutes??null,flagged:e.flagged??false,status:e.status??null},{...this.runOpts,scriptName:"project_create"});return k.of(n.project.id)}async updateProject(e,n){await O(Cf,{id:e,...n.name!==void 0?{name:n.name}:{},...n.note!==void 0?{note:n.note}:{},...n.flagged!==void 0?{flagged:n.flagged}:{},...n.estimatedMinutes!==void 0?{estimatedMinutes:n.estimatedMinutes}:{},...n.deferDate!==void 0?{deferDate:n.deferDate}:{},...n.dueDate!==void 0?{dueDate:n.dueDate}:{},...n.status!==void 0?{status:n.status}:{}},{...this.runOpts,scriptName:"project_update"});}async completeProject(e,n){await O(wf,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"project_complete"});}async dropProject(e){await O(jf,{id:e},{...this.runOpts,scriptName:"project_drop"});}async batchCompleteProjects(e){let n=await O(vf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_complete"});return Pe(n,k.of)}async batchDropProjects(e){let n=await O(Tf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_drop"});return Pe(n,k.of)}async moveProject(e,n){await O(Df,{id:e,folderId:n.folderId??null},{...this.runOpts,scriptName:"project_move"});}async deleteProject(e){await O(bf,{id:e},{...this.runOpts,scriptName:"project_delete"});}async markProjectReviewed(e){await O(xf,{id:e},{...this.runOpts,scriptName:"project_mark_reviewed"});}async listProjectsDueForReview(){return (await O(Ef,{},{...this.runOpts,scriptName:"review_list_due"})).projects}async setProjectReviewInterval(e,n){await O(Af,{id:e,days:n},{...this.runOpts,scriptName:"project_set_review_interval"});}async setProjectNextReviewDate(e,n){await O(Rf,{id:e,nextReviewDate:n},{...this.runOpts,scriptName:"project_set_next_review_date"});}async listTags(e){return (await O(Bf,{parentId:e?.parentId??null,status:e?.status??null},{...this.runOpts,scriptName:"tag_list"})).tags.map(r=>({...r,id:S.of(r.id)}))}async getTag(e){let n=await O(Uf,{id:e},{...this.runOpts,scriptName:"tag_get"});return {...n.tag,id:S.of(n.tag.id)}}async getTagsMany(e){return (await O(Lf,{ids:e},{...this.runOpts,scriptName:"tag_get_many"})).tags.map(r=>r?{...r,id:S.of(r.id)}:null)}async createTag(e){let n=await O(Nf,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"tag_create"});return S.of(n.tag.id)}async updateTag(e,n){await O(Jf,{id:e,...n.name!==void 0?{name:n.name}:{},...n.status!==void 0?{status:n.status}:{},...n.allowsNextAction!==void 0?{allowsNextAction:n.allowsNextAction}:{}},{...this.runOpts,scriptName:"tag_update"});}async deleteTag(e){await O(Ff,{id:e},{...this.runOpts,scriptName:"tag_delete"});}async listFolders(e){return (await O(gf,{parentId:e?.parentId??null},{...this.runOpts,scriptName:"folder_list"})).folders.map(r=>({...r,id:W.of(r.id)}))}async getFolder(e){let n=await O(ff,{id:e},{...this.runOpts,scriptName:"folder_get"});return {...n.folder,id:W.of(n.folder.id)}}async createFolder(e){let n=await O(uf,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"folder_create"});return W.of(n.folder.id)}async updateFolder(e,n){await O(hf,{id:e,...n.name!==void 0?{name:n.name}:{}},{...this.runOpts,scriptName:"folder_update"});}async deleteFolder(e){await O(mf,{id:e},{...this.runOpts,scriptName:"folder_delete"});}async listPerspectives(){return (await O(If,{},{...this.runOpts,scriptName:"perspective_list"})).perspectives}async evaluatePerspective(e){return (await O(kf,{perspectiveId:e},{...this.runOpts,scriptName:"perspective_evaluate"})).tasks.map(r=>({...r,id:h.of(r.id)}))}async evaluateCustomPerspective(e){throw new N("evaluateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluateCustomPerspective"}})}async evaluatePerspectiveRules(e,n){throw new N("evaluatePerspectiveRules requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluatePerspectiveRules"}})}async getCustomPerspective(e){throw new N("getCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"getCustomPerspective"}})}async deleteCustomPerspective(e){throw new N("deleteCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"deleteCustomPerspective"}})}async createCustomPerspective(e){throw new N("createCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"createCustomPerspective"}})}async updateCustomPerspective(e,n){throw new N("updateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"updateCustomPerspective"}})}async searchTasks(e){return (await O(og,{q:e.q??null,scope:e.scope??"all",projectId:e.projectId??null,tagIds:e.tagIds??null,available:e.available??null,dueBefore:e.dueBefore??null,dueAfter:e.dueAfter??null,flagged:e.flagged??null,completed:e.completed??"exclude"},{...this.runOpts,scriptName:"task_search"})).tasks.map(r=>({...r,id:h.of(r.id)}))}async getForecast(e){let n=await O(yf,{from:e.from,to:e.to,includeOverdue:e.includeOverdue??true,includeDeferred:e.includeDeferred??true,includeFlagged:e.includeFlagged??true},{...this.runOpts,scriptName:"forecast_get"});return {overdue:n.overdue.map(r=>({...r,id:h.of(r.id)})),dueToday:n.dueToday.map(r=>({...r,id:h.of(r.id)})),deferredToday:n.deferredToday.map(r=>({...r,id:h.of(r.id)})),flagged:n.flagged.map(r=>({...r,id:h.of(r.id)}))}}async getForecastTag(){throw new N("getForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"getForecastTag"}})}async setForecastTag(e){throw new N("setForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setForecastTag"}})}async undoLastMutation(){throw new N("undoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"undoLastMutation"}})}async redoLastMutation(){throw new N("redoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"redoLastMutation"}})}async setTaskAlarms(e,n){throw new N("setTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setTaskAlarms"}})}async clearTaskAlarms(e){throw new N("clearTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"clearTaskAlarms"}})}async listAttachments(e){return (await O(cf,e,{...this.runOpts,scriptName:"attachment_list"})).attachments}async addAttachment(e){let n=await O(sf,e,{...this.runOpts,scriptName:"attachment_add"});return Oe.of(n.id)}async removeAttachment(e){await O(df,e,{...this.runOpts,scriptName:"attachment_remove"});}async saveAttachmentToPath(e){return O(lf,e,{...this.runOpts,scriptName:"attachment_save_to_path"})}async appLaunch(){return O(af,{},{...this.runOpts,scriptName:"app_launch"})}async getWindowState(){let e=await O(cg,{},{...this.runOpts,scriptName:"window_get_state"});if(nc(e))throw new yt(e.error.message,{details:{transport:"jxa",scriptName:"window_get_state"}});return e}async setWindowPerspective(e){let n=await O(lg,{perspectiveName:e},{...this.runOpts,scriptName:"window_set_perspective"});if(nc(n))throw n.error.code==="NO_FRONT_WINDOW"?new yt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}}):new P(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}});return n}async setWindowFocus(e){let n=await O(dg,{containerId:e},{...this.runOpts,scriptName:"window_set_focus"});if(nc(n))throw n.error.code==="NO_FRONT_WINDOW"?new yt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}}):new P(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}});return n}async appWindowNew(){throw new N("appWindowNew routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNew"}})}async appWindowNewTab(){throw new N("appWindowNewTab routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNewTab"}})}async pluginInvoke(e){throw new N("pluginInvoke is handled by OmniJsTransport; JxaTransport is not in the routing path for this method",{details:{transport:"jxa",reason:"routed-to-omnijs"}})}async syncTrigger(){return await O(Mf,{},{...this.runOpts,scriptName:"sync_trigger"})}async getLastSync(){return {lastSyncAt:null,inFlight:false}}async getChangesSince(e){let n=await O(pf,{sinceIso:e},{...this.runOpts,scriptName:"changes_since"});return {taskIds:n.tasks.map(r=>r.id),projectIds:n.projects.map(r=>r.id)}}async runJxaScript(e,n){return O(e,n??{},{...this.runOpts,scriptName:"raw"})}};var gg=`/**
5387
5479
  * OmniJS: open a new OmniFocus window.
5388
5480
  *
5389
5481
  * Wraps \`document.newWindow()\`. The new window opens in the foreground with
@@ -5403,7 +5495,7 @@ function run(argv) {
5403
5495
  return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
5404
5496
  }
5405
5497
  })();
5406
- `;var ym=`/**
5498
+ `;var hg=`/**
5407
5499
  * OmniJS: add a new tab to the front OmniFocus window.
5408
5500
  *
5409
5501
  * Wraps \`document.newTabOnWindow(window)\` using the front window (index 0).
@@ -5432,7 +5524,7 @@ function run(argv) {
5432
5524
  return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
5433
5525
  }
5434
5526
  })();
5435
- `;var Im=`/**
5527
+ `;var yg=`/**
5436
5528
  * OmniJS: redo the most recently undone mutation.
5437
5529
  *
5438
5530
  * Wraps \`Database.redo()\`. Behaves identically to \u2318\u21E7Z in the OmniFocus UI:
@@ -5459,7 +5551,7 @@ function run(argv) {
5459
5551
  return JSON.stringify({ error: { code: "REDO_FAILED", message: msg } });
5460
5552
  }
5461
5553
  })();
5462
- `;var km=`/**
5554
+ `;var kg=`/**
5463
5555
  * OmniJS: undo the most recent document mutation.
5464
5556
  *
5465
5557
  * Wraps \`Database.undo()\`. Behaves identically to \u2318Z in the OmniFocus UI:
@@ -5490,7 +5582,7 @@ function run(argv) {
5490
5582
  return JSON.stringify({ error: { code: "UNDO_FAILED", message: msg } });
5491
5583
  }
5492
5584
  })();
5493
- `;var Tm=`/**
5585
+ `;var Ig=`/**
5494
5586
  * OmniJS: read the forecast-tag preference (the tag whose tasks always
5495
5587
  * appear on the Forecast view alongside dated items).
5496
5588
  *
@@ -5509,7 +5601,7 @@ function run(argv) {
5509
5601
  const tag = Database.forecastTag;
5510
5602
  return JSON.stringify({ tagId: tag === null ? null : tag.id.primaryKey });
5511
5603
  })();
5512
- `;var vm=`/**
5604
+ `;var vg=`/**
5513
5605
  * OmniJS: set or clear the forecast-tag preference.
5514
5606
  *
5515
5607
  * Args injected as \`globalThis.__args\`:
@@ -5546,7 +5638,159 @@ function run(argv) {
5546
5638
  Database.forecastTag = tag;
5547
5639
  return JSON.stringify({ tagId });
5548
5640
  })();
5549
- `;var wm=`/**
5641
+ `;var Tg=`/**
5642
+ * perspective_create.js \u2014 create a new custom OmniFocus perspective.
5643
+ *
5644
+ * Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
5645
+ * { name, aggregation?, rules?, iconColor? }
5646
+ *
5647
+ * Returns JSON: \`{ id: string }\` on success (the new persistent identifier),
5648
+ * or \`{ error: { code, message } }\` for typed failures.
5649
+ *
5650
+ * Two-step write:
5651
+ * 1. JXA \`make({ new: "perspective", withProperties: { name } })\` to create
5652
+ * the shell. JXA is the only API that creates a custom perspective \u2014
5653
+ * OmniJS has no factory for \`Perspective.Custom\`.
5654
+ * 2. OmniJS configures \`archivedTopLevelFilterAggregation\`,
5655
+ * \`archivedFilterRules\`, and \`iconColor\` on the resulting object.
5656
+ *
5657
+ * Atomic rollback:
5658
+ * If step 2 throws, the script calls \`deleteObject(persp)\` so the user is
5659
+ * never left with a half-configured perspective. The rollback runs inside
5660
+ * this same OmniJS execution \u2014 no TS-level try/finally that could itself
5661
+ * fail between transport hops.
5662
+ *
5663
+ * Rule serialization:
5664
+ * The input rule shape mirrors the read-side shape in \`perspective_get.js\`
5665
+ * (#523/#569) so round-trips are lossless. Atom keys, aggregate
5666
+ * \`aggregateType\` / \`aggregateRules\`, and \`disabledRule\` wrappers are
5667
+ * passed through verbatim \u2014 \`archivedFilterRules\` accepts plain JS arrays
5668
+ * of plain JS objects.
5669
+ *
5670
+ * iconColor:
5671
+ * \`{ r, g, b, a }\` floats in [0, 1] are mapped to \`Color.RGB(r, g, b, a)\`,
5672
+ * symmetric with the read serialization.
5673
+ *
5674
+ * @see #577, #617
5675
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 createCustomPerspective()
5676
+ * @see src/scripts/omnijs/perspective_get.js \u2014 serialization mirror
5677
+ */
5678
+ (() => {
5679
+ const { name, aggregation, rules, iconColor } = globalThis.__args;
5680
+
5681
+ if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
5682
+ return JSON.stringify({
5683
+ error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
5684
+ });
5685
+ }
5686
+
5687
+ if (typeof name !== "string" || name.length === 0) {
5688
+ return JSON.stringify({
5689
+ error: { code: "VALIDATION_ERROR", message: "name is required and must be non-empty" },
5690
+ });
5691
+ }
5692
+
5693
+ // ----- Step 1: JXA make ----------------------------------------------------
5694
+ // Application("OmniFocus") inside OmniJS bridges to the JXA application
5695
+ // object. \`make\` is the only documented way to create a custom perspective.
5696
+ let persp;
5697
+ let identifier;
5698
+ try {
5699
+ const app = Application("OmniFocus");
5700
+ const made = app.make({
5701
+ new: "perspective",
5702
+ withProperties: { name },
5703
+ });
5704
+ // \`make\` returns a JXA reference. The persistent identifier is exposed
5705
+ // via the OmniJS-side wrapper, not the JXA reference, so re-fetch by
5706
+ // name to get the OmniJS object. Names are unique inside a database, so
5707
+ // a fresh lookup by name is unambiguous *immediately* after creation.
5708
+ if (made === null || made === undefined) {
5709
+ return JSON.stringify({
5710
+ error: { code: "SCRIPT_ERROR", message: "JXA make returned no object" },
5711
+ });
5712
+ }
5713
+ // Locate the OmniJS-side perspective by name. \`Perspective.Custom.all\`
5714
+ // is the documented enumeration; we filter by name to find the one we
5715
+ // just created.
5716
+ const all = Perspective.Custom.all;
5717
+ for (let i = 0; i < all.length; i++) {
5718
+ if (all[i].name === name) {
5719
+ persp = all[i];
5720
+ identifier = persp.identifier;
5721
+ break;
5722
+ }
5723
+ }
5724
+ if (persp === undefined) {
5725
+ return JSON.stringify({
5726
+ error: {
5727
+ code: "SCRIPT_ERROR",
5728
+ message: \`created perspective named "\${name}" but could not locate it via Perspective.Custom.all\`,
5729
+ },
5730
+ });
5731
+ }
5732
+ } catch (e) {
5733
+ const msg = String(e?.message ? e.message : e);
5734
+ // OmniFocus rejects duplicate names with a recognisable message; surface
5735
+ // as VALIDATION_ERROR so the agent can react accordingly.
5736
+ if (msg.toLowerCase().indexOf("already") >= 0 || msg.toLowerCase().indexOf("duplicate") >= 0) {
5737
+ return JSON.stringify({
5738
+ error: { code: "VALIDATION_ERROR", message: \`Duplicate perspective name: \${name}\` },
5739
+ });
5740
+ }
5741
+ return JSON.stringify({
5742
+ error: { code: "SCRIPT_ERROR", message: \`JXA make failed: \${msg}\` },
5743
+ });
5744
+ }
5745
+
5746
+ // ----- Step 2: OmniJS configure (with rollback on failure) -----------------
5747
+ try {
5748
+ if (typeof aggregation === "string") {
5749
+ persp.archivedTopLevelFilterAggregation = aggregation;
5750
+ }
5751
+ if (Array.isArray(rules)) {
5752
+ // archivedFilterRules accepts plain JS arrays of plain JS objects \u2014
5753
+ // the same shape produced by perspective_get.js. Pass through verbatim.
5754
+ persp.archivedFilterRules = rules;
5755
+ }
5756
+ if (
5757
+ iconColor !== null &&
5758
+ iconColor !== undefined &&
5759
+ typeof iconColor === "object" &&
5760
+ typeof iconColor.r === "number" &&
5761
+ typeof iconColor.g === "number" &&
5762
+ typeof iconColor.b === "number" &&
5763
+ typeof iconColor.a === "number"
5764
+ ) {
5765
+ persp.iconColor = Color.RGB(iconColor.r, iconColor.g, iconColor.b, iconColor.a);
5766
+ }
5767
+ } catch (e) {
5768
+ // Atomic rollback \u2014 the shell exists but the configure step failed, so
5769
+ // delete the shell so the user is never left with a malformed
5770
+ // perspective. If rollback also fails, surface both errors.
5771
+ const configureMsg = String(e?.message ? e.message : e);
5772
+ try {
5773
+ deleteObject(persp);
5774
+ } catch (rollbackErr) {
5775
+ const rollbackMsg = String(rollbackErr?.message ? rollbackErr.message : rollbackErr);
5776
+ return JSON.stringify({
5777
+ error: {
5778
+ code: "SCRIPT_ERROR",
5779
+ message: \`configure failed (\${configureMsg}); rollback also failed (\${rollbackMsg}). Shell perspective "\${name}" may need manual deletion.\`,
5780
+ },
5781
+ });
5782
+ }
5783
+ return JSON.stringify({
5784
+ error: {
5785
+ code: "SCRIPT_ERROR",
5786
+ message: \`configure failed: \${configureMsg}. Shell perspective rolled back.\`,
5787
+ },
5788
+ });
5789
+ }
5790
+
5791
+ return JSON.stringify({ id: identifier });
5792
+ })();
5793
+ `;var wg=`/**
5550
5794
  * perspective_delete.js \u2014 delete a custom OmniFocus perspective by identifier.
5551
5795
  *
5552
5796
  * Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
@@ -5581,13 +5825,13 @@ function run(argv) {
5581
5825
  deleteObject(persp);
5582
5826
  } catch (e) {
5583
5827
  return JSON.stringify({
5584
- error: { code: "SCRIPT_ERROR", message: String(e && e.message ? e.message : e) },
5828
+ error: { code: "SCRIPT_ERROR", message: String(e?.message ? e.message : e) },
5585
5829
  });
5586
5830
  }
5587
5831
 
5588
5832
  return JSON.stringify({ id: identifier });
5589
5833
  })();
5590
- `;var Sm=`/**
5834
+ `;var Sg=`/**
5591
5835
  * perspective_evaluate.js \u2014 evaluate a custom OmniFocus perspective and
5592
5836
  * return its task list as domain \`Task\` objects.
5593
5837
  *
@@ -5703,7 +5947,204 @@ function run(argv) {
5703
5947
 
5704
5948
  return JSON.stringify({ tasks });
5705
5949
  })();
5706
- `;var jm=`/**
5950
+ `;var bg=`/**
5951
+ * perspective_evaluate_dry_run.js \u2014 evaluate a *proposed* perspective rule
5952
+ * tree and return its task list, without persisting the perspective.
5953
+ *
5954
+ * The OmniJS perspective evaluator (window.perspective + content.rootNode)
5955
+ * only operates on saved Perspective.Custom objects \u2014 there is no "evaluate
5956
+ * ad-hoc rules" surface. We work around this by creating a temporary
5957
+ * perspective with a sentinel name, evaluating it, and **always** deleting
5958
+ * it inside the same OmniJS execution. The rollback runs in this single
5959
+ * script so a TS-level retry that drops between transport hops cannot leave
5960
+ * an orphan perspective behind (per the existing perspective_create.js
5961
+ * rollback comment).
5962
+ *
5963
+ * Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
5964
+ * { aggregation?: string, rules?: Array<unknown> }
5965
+ *
5966
+ * Returns JSON: \`{ tasks: Task[] }\` on success, or
5967
+ * \`{ error: { code, message } }\` for typed failures (Pro-gating, JXA
5968
+ * failure, configure failure, walk failure). The caller maps codes to
5969
+ * typed errors.
5970
+ *
5971
+ * @see #659 \u2014 issue
5972
+ * @see src/scripts/omnijs/perspective_evaluate.js \u2014 saved-perspective evaluator
5973
+ * @see src/scripts/omnijs/perspective_create.js \u2014 JXA-make + configure pattern
5974
+ * @see src/scripts/omnijs/perspective_delete.js \u2014 deleteObject pattern
5975
+ */
5976
+ (() => {
5977
+ const { aggregation, rules } = globalThis.__args;
5978
+
5979
+ if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
5980
+ return JSON.stringify({
5981
+ error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
5982
+ });
5983
+ }
5984
+
5985
+ // Sentinel name \u2014 long random suffix avoids collision with user perspectives
5986
+ // even under concurrent dry-run calls. The leading underscore + "dry-run"
5987
+ // marker makes orphans (if any) trivially identifiable.
5988
+ const sentinelName = \`__omnifocus-mcp-dry-run-\${Date.now()}-\${Math.floor(Math.random() * 1e9).toString(36)}\`;
5989
+
5990
+ // ----- Step 1: JXA make ----------------------------------------------------
5991
+ let persp;
5992
+ try {
5993
+ const app = Application("OmniFocus");
5994
+ const made = app.make({ new: "perspective", withProperties: { name: sentinelName } });
5995
+ if (made === null || made === undefined) {
5996
+ return JSON.stringify({
5997
+ error: { code: "SCRIPT_ERROR", message: "JXA make returned no object" },
5998
+ });
5999
+ }
6000
+ const all = Perspective.Custom.all;
6001
+ for (let i = 0; i < all.length; i++) {
6002
+ if (all[i].name === sentinelName) {
6003
+ persp = all[i];
6004
+ break;
6005
+ }
6006
+ }
6007
+ if (persp === undefined) {
6008
+ return JSON.stringify({
6009
+ error: {
6010
+ code: "SCRIPT_ERROR",
6011
+ message: \`temp perspective "\${sentinelName}" created but not located via Perspective.Custom.all\`,
6012
+ },
6013
+ });
6014
+ }
6015
+ } catch (e) {
6016
+ return JSON.stringify({
6017
+ error: {
6018
+ code: "SCRIPT_ERROR",
6019
+ message: \`JXA make failed: \${String(e?.message ? e.message : e)}\`,
6020
+ },
6021
+ });
6022
+ }
6023
+
6024
+ // From here on, \`persp\` exists and MUST be deleted before returning.
6025
+ // Defensive \`safeDelete\` swallows secondary errors so the primary error
6026
+ // (or success) reaches the caller.
6027
+ function safeDelete() {
6028
+ try {
6029
+ deleteObject(persp);
6030
+ } catch (_e) {
6031
+ // Surface in the message if the primary path was a success \u2014 caller
6032
+ // sees "tasks but cleanup failed". On primary failure we don't double-report.
6033
+ }
6034
+ }
6035
+
6036
+ // ----- Step 2: configure rules + aggregation -------------------------------
6037
+ try {
6038
+ if (typeof aggregation === "string") {
6039
+ persp.archivedTopLevelFilterAggregation = aggregation;
6040
+ }
6041
+ if (Array.isArray(rules)) {
6042
+ persp.archivedFilterRules = rules;
6043
+ }
6044
+ } catch (e) {
6045
+ safeDelete();
6046
+ return JSON.stringify({
6047
+ error: {
6048
+ code: "SCRIPT_ERROR",
6049
+ message: \`configure failed: \${String(e?.message ? e.message : e)}. Temp perspective rolled back.\`,
6050
+ },
6051
+ });
6052
+ }
6053
+
6054
+ // ----- Step 3: switch window + walk tasks ----------------------------------
6055
+ function isoOrNull(d) {
6056
+ return d ? d.toISOString() : null;
6057
+ }
6058
+
6059
+ function buildRepetition(task) {
6060
+ try {
6061
+ const rr = task.repetitionRule;
6062
+ if (!rr) return null;
6063
+ return { method: String(rr.method), unit: String(rr.unit), steps: rr.steps };
6064
+ } catch (_e) {
6065
+ return null;
6066
+ }
6067
+ }
6068
+
6069
+ function buildTask(task) {
6070
+ const tagIds = [];
6071
+ try {
6072
+ const tags = task.tags;
6073
+ for (let i = 0; i < tags.length; i++) tagIds.push(tags[i].id.primaryKey);
6074
+ } catch (_e) {}
6075
+
6076
+ let projectId = null;
6077
+ try {
6078
+ if (task.containingProject) projectId = task.containingProject.id.primaryKey;
6079
+ } catch (_e) {}
6080
+
6081
+ let parentId = null;
6082
+ try {
6083
+ if (task.parent && task.parent instanceof Task) parentId = task.parent.id.primaryKey;
6084
+ } catch (_e) {}
6085
+
6086
+ return {
6087
+ id: task.id.primaryKey,
6088
+ name: task.name,
6089
+ note: task.note || null,
6090
+ noteHtml: null,
6091
+ projectId,
6092
+ parentId,
6093
+ tagIds,
6094
+ deferDate: isoOrNull(task.deferDate),
6095
+ dueDate: isoOrNull(task.dueDate),
6096
+ estimatedMinutes: task.estimatedMinutes ?? null,
6097
+ flagged: !!task.flagged,
6098
+ completed: !!task.completed,
6099
+ completedAt: isoOrNull(task.completionDate),
6100
+ dropped: !!task.dropped,
6101
+ droppedAt: isoOrNull(task.dropDate),
6102
+ available: !task.blocked && !task.completed && !task.dropped,
6103
+ blocked: !!task.blocked,
6104
+ sequential: !!task.sequential,
6105
+ completedByChildren: !!task.completedByChildren,
6106
+ repetition: buildRepetition(task),
6107
+ createdAt: isoOrNull(task.added) ?? new Date().toISOString(),
6108
+ modifiedAt: isoOrNull(task.modified) ?? new Date().toISOString(),
6109
+ };
6110
+ }
6111
+
6112
+ let tasks;
6113
+ try {
6114
+ const win = document.windows[0];
6115
+ win.perspective = persp;
6116
+
6117
+ tasks = [];
6118
+ const seen = new Set();
6119
+ const walk = (node) => {
6120
+ if (!node) return;
6121
+ try {
6122
+ const obj = node.object;
6123
+ if (obj instanceof Task && !seen.has(obj.id.primaryKey)) {
6124
+ seen.add(obj.id.primaryKey);
6125
+ tasks.push(buildTask(obj));
6126
+ }
6127
+ } catch (_e) {}
6128
+ const children = node.children || [];
6129
+ for (let i = 0; i < children.length; i++) walk(children[i]);
6130
+ };
6131
+ walk(win.content.rootNode);
6132
+ } catch (e) {
6133
+ safeDelete();
6134
+ return JSON.stringify({
6135
+ error: {
6136
+ code: "SCRIPT_ERROR",
6137
+ message: \`evaluate walk failed: \${String(e?.message ? e.message : e)}. Temp perspective rolled back.\`,
6138
+ },
6139
+ });
6140
+ }
6141
+
6142
+ // ----- Step 4: cleanup (always) --------------------------------------------
6143
+ safeDelete();
6144
+
6145
+ return JSON.stringify({ tasks });
6146
+ })();
6147
+ `;var jg=`/**
5707
6148
  * perspective_get.js \u2014 read a custom OmniFocus perspective's full configuration.
5708
6149
  *
5709
6150
  * Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
@@ -5853,7 +6294,105 @@ function run(argv) {
5853
6294
  },
5854
6295
  });
5855
6296
  })();
5856
- `;var bm=`/**
6297
+ `;var _g=`/**
6298
+ * perspective_update.js \u2014 partial-patch update of a custom OmniFocus
6299
+ * perspective.
6300
+ *
6301
+ * Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
6302
+ * { identifier, name?, aggregation?, rules?, iconColor? }
6303
+ *
6304
+ * Returns JSON: \`{ id: string }\` on success (echoes the identifier),
6305
+ * or \`{ error: { code, message } }\` for typed failures.
6306
+ *
6307
+ * Patch semantics:
6308
+ * - Only fields present in args are written. Omitted fields leave the
6309
+ * existing value unchanged.
6310
+ * - \`iconColor: null\` clears any custom color back to the OmniFocus default.
6311
+ * - \`iconColor: { r, g, b, a }\` writes a new color via Color.RGB(...).
6312
+ * - \`rules: []\` clears the rule tree to "show everything" (the default).
6313
+ *
6314
+ * Built-in perspectives have no rule tree to patch \u2014 \`Perspective.Custom
6315
+ * .byIdentifier\` returns null for them, which the script reports as
6316
+ * NOT_FOUND. The MCP-tool layer rejects built-in ids as VALIDATION_ERROR
6317
+ * before calling the adapter, so callers should rarely reach this branch.
6318
+ *
6319
+ * No rollback dance: unlike create, an update does not have a shell to
6320
+ * roll back \u2014 failures leave the perspective in whatever state OmniFocus
6321
+ * persisted before the throw. Tests that simulate mid-patch failure should
6322
+ * gate on real OmniFocus behavior.
6323
+ *
6324
+ * @see #577, #618
6325
+ * @see src/adapter/omnijs/OmniJsTransport.ts \u2014 updateCustomPerspective()
6326
+ * @see src/scripts/omnijs/perspective_create.js \u2014 sibling create script
6327
+ */
6328
+ (() => {
6329
+ const { identifier, name, aggregation, rules, iconColor } = globalThis.__args;
6330
+
6331
+ if (typeof Perspective === "undefined" || typeof Perspective.Custom === "undefined") {
6332
+ return JSON.stringify({
6333
+ error: { code: "FEATURE_REQUIRES_PRO", message: "Custom perspectives require OmniFocus Pro" },
6334
+ });
6335
+ }
6336
+
6337
+ if (typeof identifier !== "string" || identifier.length === 0) {
6338
+ return JSON.stringify({
6339
+ error: { code: "VALIDATION_ERROR", message: "identifier is required and must be non-empty" },
6340
+ });
6341
+ }
6342
+
6343
+ const persp = Perspective.Custom.byIdentifier(identifier);
6344
+ if (persp === null || persp === undefined) {
6345
+ return JSON.stringify({
6346
+ error: { code: "NOT_FOUND", message: \`Custom perspective not found: \${identifier}\` },
6347
+ });
6348
+ }
6349
+
6350
+ try {
6351
+ if (typeof name === "string") {
6352
+ if (name.length === 0) {
6353
+ return JSON.stringify({
6354
+ error: { code: "VALIDATION_ERROR", message: "name must be non-empty" },
6355
+ });
6356
+ }
6357
+ persp.name = name;
6358
+ }
6359
+ if (typeof aggregation === "string") {
6360
+ persp.archivedTopLevelFilterAggregation = aggregation;
6361
+ }
6362
+ if (Array.isArray(rules)) {
6363
+ // Same passthrough convention as perspective_create.js \u2014 the input
6364
+ // shape mirrors the read-side shape in perspective_get.js so
6365
+ // archivedFilterRules accepts it verbatim.
6366
+ persp.archivedFilterRules = rules;
6367
+ }
6368
+ if (iconColor === null) {
6369
+ // Explicit null clears the custom color back to the OmniFocus default.
6370
+ persp.iconColor = null;
6371
+ } else if (
6372
+ iconColor !== undefined &&
6373
+ typeof iconColor === "object" &&
6374
+ typeof iconColor.r === "number" &&
6375
+ typeof iconColor.g === "number" &&
6376
+ typeof iconColor.b === "number" &&
6377
+ typeof iconColor.a === "number"
6378
+ ) {
6379
+ persp.iconColor = Color.RGB(iconColor.r, iconColor.g, iconColor.b, iconColor.a);
6380
+ }
6381
+ } catch (e) {
6382
+ const msg = String(e?.message ? e.message : e);
6383
+ if (msg.toLowerCase().indexOf("already") >= 0 || msg.toLowerCase().indexOf("duplicate") >= 0) {
6384
+ return JSON.stringify({
6385
+ error: { code: "VALIDATION_ERROR", message: \`Duplicate perspective name: \${name}\` },
6386
+ });
6387
+ }
6388
+ return JSON.stringify({
6389
+ error: { code: "SCRIPT_ERROR", message: msg },
6390
+ });
6391
+ }
6392
+
6393
+ return JSON.stringify({ id: identifier });
6394
+ })();
6395
+ `;var Pg=`/**
5857
6396
  * plugin_invoke.js \u2014 invoke a named Omni Automation plug-in action.
5858
6397
  *
5859
6398
  * Called via the OmniJS transport (evaluateJavascript bridge).
@@ -5885,7 +6424,7 @@ function run(argv) {
5885
6424
  const rawResult = action.perform([arg]);
5886
6425
  return JSON.stringify({ result: rawResult ?? null });
5887
6426
  })();
5888
- `;var _m=`/**
6427
+ `;var Og=`/**
5889
6428
  * OmniJS: batch-move tasks to different destinations in a single round-trip.
5890
6429
  *
5891
6430
  * JXA's \`task.move()\` is unimplemented in OmniFocus 4.x (error 9 "Replacement
@@ -5961,7 +6500,7 @@ function run(argv) {
5961
6500
 
5962
6501
  return JSON.stringify({ succeeded, failed });
5963
6502
  })();
5964
- `;var Pm=`/**
6503
+ `;var xg=`/**
5965
6504
  * OmniJS: clear all alarms/notifications from a task.
5966
6505
  *
5967
6506
  * Args injected as \`globalThis.__args\`: { taskId: string }
@@ -6002,7 +6541,7 @@ function run(argv) {
6002
6541
 
6003
6542
  return JSON.stringify({ ok: true });
6004
6543
  })();
6005
- `;var Om=`/**
6544
+ `;var Dg=`/**
6006
6545
  * OmniJS: convert a task to a project via Database.convertTasksToProjects().
6007
6546
  *
6008
6547
  * This operation is only available via OmniJS \u2014 JXA has no equivalent.
@@ -6065,7 +6604,7 @@ function run(argv) {
6065
6604
 
6066
6605
  return JSON.stringify({ projectId: project.id.primaryKey });
6067
6606
  })();
6068
- `;var xm=`/**
6607
+ `;var Rg=`/**
6069
6608
  * OmniJS: move a task to a different project, parent task, or the inbox.
6070
6609
  *
6071
6610
  * JXA's \`task.move({ to: container })\` is unimplemented in OmniFocus 4.x
@@ -6121,7 +6660,7 @@ function run(argv) {
6121
6660
 
6122
6661
  return JSON.stringify({ id });
6123
6662
  })();
6124
- `;var Dm=`/**
6663
+ `;var Ag=`/**
6125
6664
  * OmniJS: reorder a task among its siblings.
6126
6665
  *
6127
6666
  * JXA's \`task.move({ to: ref, positioned: ... })\` shares the same broken
@@ -6209,7 +6748,7 @@ function run(argv) {
6209
6748
  moveTasks([task], location);
6210
6749
  return JSON.stringify({ id });
6211
6750
  })();
6212
- `;var Am=`/**
6751
+ `;var Cg=`/**
6213
6752
  * OmniJS: replace a task's alarm/notification set atomically.
6214
6753
  *
6215
6754
  * Args injected as \`globalThis.__args\`: { taskId: string, alarms: TaskAlarm[] }
@@ -6305,17 +6844,17 @@ function run(argv) {
6305
6844
 
6306
6845
  return JSON.stringify({ ok: true });
6307
6846
  })();
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};
6847
+ `;var Zb=16*1024*1024,Qb=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-"],{timeout:n,maxBuffer:Zb,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,i,s)=>{let c=i,d=s,l=a!==null&&a.killed===true,m=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:d,exitCode:a===null?0:a.code??1,timedOut:l,...m!==void 0?{spawnError:m}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");});async function Q(t,e={},n={}){let r=n.spawner??Qb,o=n.timeoutMs??45e3,a=JSON.stringify(e??{}),i=n.scriptName,s=ej(t,a),c=performance.now(),d=await r(s,a,o),l=Math.round(performance.now()-c),m=d.spawnError!==void 0||d.timedOut||d.exitCode!==0||d.stdout.trim()===""?"error":"ok";if(gr("omnijs",i,e,l,m),d.spawnError!==void 0)throw new ht("Failed to spawn osascript",{cause:d.spawnError,details:{transport:"omnijs",reason:d.spawnError.code??"spawn-failed",...i!==void 0?{scriptName:i}:{}}});if(d.timedOut){let g=i!==void 0?` (script: ${i})`:"";throw new gt(`OmniJS script exceeded ${o}ms timeout${g}`,{details:{transport:"omnijs",timeoutMs:o,...i!==void 0?{scriptName:i}:{}}})}if(d.exitCode!==0){let g=tj(d.stderr,i);if(g!==null)throw g;let v=i!==void 0?` [${i}]`:"";throw new N(`OmniJS script failed (exit ${d.exitCode})${v}`,{details:{transport:"omnijs",exitCode:d.exitCode,stderr:yr(d.stderr,1024),...i!==void 0?{scriptName:i}:{}}})}let f=d.stdout.trim();if(f==="")throw new N("OmniJS script returned empty stdout \u2014 the IIFE must return a JSON-encoded string",{details:{transport:"omnijs",...i!==void 0?{scriptName:i}:{}}});try{return JSON.parse(f)}catch(g){throw new N("OmniJS script returned malformed JSON",{cause:g,details:{transport:"omnijs",stdoutPreview:yr(f,200),...i!==void 0?{scriptName:i}:{}}})}}function ej(t,e){let n=`globalThis.__args = ${e};
6309
6848
  ${t}`;return ["function run(_argv) {",' const ofApp = Application("OmniFocus");'," ofApp.includeStandardAdditions = false;",` const __omnijs = ${JSON.stringify(n)};`," const __result = ofApp.evaluateJavascript(__omnijs);"," return __result;","}"].join(`
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}
6849
+ `)}function tj(t,e){return /Application can't be found/i.test(t)||/Application isn['’]t running/i.test(t)||/OmniFocus(?:.*)not running/i.test(t)?new ut({details:{transport:"omnijs",stderr:yr(t,512),...e!==void 0?{scriptName:e}:{}}}):/-1743\b/.test(t)||/not authori[sz]ed to send Apple events/i.test(t)||/errAEEventNotPermitted/i.test(t)||/not allowed assistive access/i.test(t)?new mt({details:{transport:"omnijs",stderr:yr(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function yr(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function x(t){throw new N(`OmniJsTransport.${t} is not wired yet`,{details:{transport:"omnijs",reason:"not-yet-wired",method:t}})}var kr=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return x("listTasks")}async getTask(e){return x("getTask")}async getTasksMany(e){return x("getTasksMany")}async createTask(e){return x("createTask")}async updateTask(e,n){return x("updateTask")}async completeTask(e,n){return x("completeTask")}async uncompleteTask(e){return x("uncompleteTask")}async dropTask(e,n){return x("dropTask")}async undropTask(e){return x("undropTask")}async deleteTask(e){return x("deleteTask")}async moveTask(e,n){let o=await Q(Rg,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});if(ne(o))throw o.error.code==="NOT_FOUND"?new P(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}}):new y(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}})}async convertTaskToProject(e,n){let r=await Q(Dg,{id:e,folderId:n.folderId??null,position:n.position??"ending"},{...this.runOpts,scriptName:"task_convert_to_project"});if(ne(r))throw r.error.code==="NOT_FOUND"?new P(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}}):new y(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}});return k.of(r.projectId)}async batchMoveTasks(e){let r=await Q(Og,{items:e.map(o=>({id:o.id,projectId:o.destination.projectId??null,parentId:o.destination.parentId??null}))},{...this.runOpts,scriptName:"task_batch_move"});if(ne(r))throw new y(r.error.message,{details:{transport:"omnijs",scriptName:"task_batch_move"}});return Pe(r,h.of)}async reorderTask(e,n){let r=Ag,o;if("before"in n)o={id:e,mode:"before",refId:n.before};else if("after"in n)o={id:e,mode:"after",refId:n.after};else {let i="projectId"in n.in?{projectId:n.in.projectId}:"parentId"in n.in?{parentId:n.in.parentId}:{inbox:true};o={id:e,mode:n.at,container:i};}let a=await Q(r,o,{...this.runOpts,scriptName:"task_reorder"});if(ne(a))throw a.error.code==="NOT_FOUND"?new P(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}}):new y(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}})}async duplicateTask(e,n){return x("duplicateTask")}async batchCreateTasks(e){return x("batchCreateTasks")}async batchUpdateTasks(e){return x("batchUpdateTasks")}async batchCompleteTasks(e){return x("batchCompleteTasks")}async batchUncompleteTasks(e){return x("batchUncompleteTasks")}async batchDeleteTasks(e){return x("batchDeleteTasks")}async batchDropTasks(e){return x("batchDropTasks")}async batchUndropTasks(e){return x("batchUndropTasks")}async listProjects(e){return x("listProjects")}async getProject(e){return x("getProject")}async getProjectsMany(e){return x("getProjectsMany")}async createProject(e){return x("createProject")}async updateProject(e,n){return x("updateProject")}async completeProject(e,n){return x("completeProject")}async dropProject(e,n){return x("dropProject")}async batchCompleteProjects(e){return x("batchCompleteProjects")}async batchDropProjects(e){return x("batchDropProjects")}async moveProject(e,n){return x("moveProject")}async deleteProject(e){return x("deleteProject")}async markProjectReviewed(e){return x("markProjectReviewed")}async listProjectsDueForReview(){return x("listProjectsDueForReview")}async setProjectReviewInterval(e,n){return x("setProjectReviewInterval")}async setProjectNextReviewDate(e,n){return x("setProjectNextReviewDate")}async listTags(e){return x("listTags")}async getTag(e){return x("getTag")}async getTagsMany(e){return x("getTagsMany")}async createTag(e){return x("createTag")}async updateTag(e,n){return x("updateTag")}async deleteTag(e){return x("deleteTag")}async listFolders(e){return x("listFolders")}async getFolder(e){return x("getFolder")}async createFolder(e){return x("createFolder")}async updateFolder(e,n){return x("updateFolder")}async deleteFolder(e){return x("deleteFolder")}async searchTasks(e){return x("searchTasks")}async getForecast(e){return x("getForecast")}async getForecastTag(){let n=await Q(Ig,{},{...this.runOpts,scriptName:"forecast_get_tag"});if(ne(n))throw new N(n.error.message,{details:{transport:"omnijs",scriptName:"forecast_get_tag"}});return {tagId:n.tagId===null?null:S.of(n.tagId)}}async setForecastTag(e){let r=await Q(vg,{tagId:e},{...this.runOpts,scriptName:"forecast_set_tag"});if(ne(r))throw r.error.code==="NOT_FOUND"?new P(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}}):new y(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}});return {tagId:r.tagId===null?null:S.of(r.tagId)}}async undoLastMutation(){let e=await Q(kg,{},{...this.runOpts,scriptName:"database_undo"});if(ne(e))throw new N(e.error.message,{details:{transport:"omnijs",scriptName:"database_undo",code:e.error.code}});return {undid:e.undid}}async redoLastMutation(){let e=await Q(yg,{},{...this.runOpts,scriptName:"database_redo"});if(ne(e))throw new N(e.error.message,{details:{transport:"omnijs",scriptName:"database_redo",code:e.error.code}});return {redid:e.redid}}async setTaskAlarms(e,n){let r=await Q(Cg,{taskId:String(e),alarms:n},{...this.runOpts,scriptName:"task_set_alarms"});if(ne(r))throw r.error.code==="NOT_FOUND"?new P(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms"}}):r.error.code==="VALIDATION"?new y(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms"}}):new N(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms",code:r.error.code}})}async clearTaskAlarms(e){let n=await Q(xg,{taskId:String(e)},{...this.runOpts,scriptName:"task_clear_alarms"});if(ne(n))throw n.error.code==="NOT_FOUND"?new P(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms"}}):new N(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms",code:n.error.code}})}async listAttachments(e){return x("listAttachments")}async addAttachment(e){return x("addAttachment")}async removeAttachment(e){return x("removeAttachment")}async saveAttachmentToPath(e){return x("saveAttachmentToPath")}async appLaunch(){return x("appLaunch")}async getWindowState(){return x("getWindowState")}async setWindowPerspective(e){return x("setWindowPerspective")}async setWindowFocus(e){return x("setWindowFocus")}async appWindowNew(){let e=await Q(gg,{},{...this.runOpts,scriptName:"app_window_new"});if(ne(e))throw new N(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new",code:e.error.code}});return e}async appWindowNewTab(){let e=await Q(hg,{},{...this.runOpts,scriptName:"app_window_new_tab"});if(ne(e))throw e.error.code==="WINDOW_UNAVAILABLE"?new N(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}}):new N(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}});return e}async pluginInvoke(e){return Q(Pg,{identifier:e.identifier,arg:e.arg??null},{...this.runOpts,scriptName:"plugin_invoke"})}async listPerspectives(){return x("listPerspectives")}async evaluatePerspective(e){return x("evaluatePerspective")}async evaluateCustomPerspective(e){let r=await Q(Sg,{identifier:e},{...this.runOpts,scriptName:"perspective_evaluate"});if(ne(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Ue(r.error.message,{details:{feature:"custom-perspectives"}}):new P(r.error.message,{details:{resource:"perspective",id:e}});return r.tasks.map(o=>({...o,id:h.of(o.id)}))}async evaluatePerspectiveRules(e,n){let r=await Q(bg,{rules:e,...n!==void 0&&{aggregation:n}},{...this.runOpts,scriptName:"perspective_evaluate_dry_run"});if(ne(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Ue(r.error.message,{details:{feature:"custom-perspectives"}}):new N(r.error.message,{details:{script:"perspective_evaluate_dry_run"}});return r.tasks.map(o=>({...o,id:h.of(o.id)}))}async getCustomPerspective(e){let n=await Q(jg,{identifier:e},{...this.runOpts,scriptName:"perspective_get"});if(ne(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):new P(n.error.message,{details:{resource:"perspective",id:e}});return n.perspective}async deleteCustomPerspective(e){let n=await Q(wg,{identifier:e},{...this.runOpts,scriptName:"perspective_delete"});if(ne(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="NOT_FOUND"?new P(n.error.message,{details:{resource:"perspective",id:e}}):new N(n.error.message,{details:{transport:"omnijs",script:"perspective_delete",id:e}})}async createCustomPerspective(e){let n=await Q(Tg,{name:e.name,...e.aggregation!==void 0&&{aggregation:e.aggregation},...e.rules!==void 0&&{rules:e.rules},...e.iconColor!==void 0&&{iconColor:e.iconColor}},{...this.runOpts,scriptName:"perspective_create"});if(ne(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="VALIDATION_ERROR"?new y(n.error.message,{details:{name:e.name}}):new N(n.error.message,{details:{transport:"omnijs",script:"perspective_create",name:e.name}});return n.id}async updateCustomPerspective(e,n){let r={identifier:e};n.name!==void 0&&(r.name=n.name),n.aggregation!==void 0&&(r.aggregation=n.aggregation),n.rules!==void 0&&(r.rules=n.rules),n.iconColor!==void 0&&(r.iconColor=n.iconColor);let o=await Q(_g,r,{...this.runOpts,scriptName:"perspective_update"});if(ne(o))throw o.error.code==="FEATURE_REQUIRES_PRO"?new Ue(o.error.message,{details:{feature:"custom-perspectives"}}):o.error.code==="NOT_FOUND"?new P(o.error.message,{details:{resource:"perspective",id:e}}):o.error.code==="VALIDATION_ERROR"?new y(o.error.message,{details:{id:e}}):new N(o.error.message,{details:{transport:"omnijs",script:"perspective_update",id:e}})}async syncTrigger(){return x("syncTrigger")}async getLastSync(){return x("getLastSync")}async getChangesSince(e){return {taskIds:[],projectIds:[]}}async runOmniJsScript(e,n){return Q(e,n??{},{...this.runOpts,scriptName:"raw"})}};var Ir=class extends EventEmitter{cache;inflight=new Map;hits=0;misses=0;coalesced=0;evictions=0;constructor({capacity:e=256,ttlMs:n=3e4}={}){super(),this.cache=new LRUCache({max:e,ttl:n,disposeAfter:()=>{this.evictions++;}});}async wrap(e,n){let r=this.cache.get(e);if(r!==void 0)return this.hits++,r.v;let o=this.inflight.get(e);if(o!==void 0)return this.coalesced++,await o.promise;this.misses++;let a=Symbol(e),i=(async()=>{try{let s=await n();return this.inflight.get(e)?.token===a&&this.cache.set(e,{v:s}),s}finally{this.inflight.get(e)?.token===a&&this.inflight.delete(e);}})();return this.inflight.set(e,{token:a,promise:i}),await i}invalidate(e){let n=e.endsWith(":*")?e.slice(0,-1):`${e}:`,r=[];for(let s of this.cache.keys())(s.startsWith(n)||s===e)&&r.push(s);for(let s of r)this.cache.delete(s);for(let s of this.inflight.keys())(s.startsWith(n)||s===e)&&this.inflight.delete(s);let o=r.length,a=ze(),i={event:"cache.invalidated",scopes:[e],evicted:o,...a!==void 0?{correlationId:a}:{}};o>0&&R.info(i,"cache.invalidated"),this.emit("cache.invalidated",i);}stats(){return {size:this.cache.size,hits:this.hits,misses:this.misses,evictions:this.evictions,coalesced:this.coalesced}}set(e,n){this.cache.set(e,{v:n});}has(e){return this.cache.has(e)}clear(){this.cache.clear(),this.inflight.clear();}};var aj=["/System/","/private/System/","/Library/","/private/Library/"];async function rc(t,e){let n;try{n=await realpath(t);}catch{throw new y(`Attachment file not found or cannot be resolved: ${t}`,{suggestion:"Verify the file path is correct and the file exists.",details:{field:"filePath",value:t}})}let r=n.endsWith(sep)?n:n+sep;for(let a of aj)if(r.startsWith(a))throw new y(`Attachment path resolves to a blocked system directory: ${n}`,{suggestion:"Attachment files must be under your home directory or an explicitly allowed path (OMNIFOCUS_ATTACHMENT_PATHS).",details:{field:"filePath",value:t,resolvedPath:n}});if(!e.some(a=>{let i=a.endsWith(sep)?a:a+sep;return r.startsWith(i)}))throw new y(`Attachment path is outside the allowed scope: ${n}`,{suggestion:"Move the file to your home directory, or add its parent directory to OMNIFOCUS_ATTACHMENT_PATHS (colon-separated list of absolute paths).",details:{field:"filePath",value:t,resolvedPath:n,allowedPaths:[...e]}})}var Eg=1024*1024;async function Mg(t,e){if(e<=0)return;let n;try{n=await stat(t);}catch{throw new y(`Attachment file not found or not accessible: ${t}`,{suggestion:"Verify the file path is correct and the file is readable.",details:{field:"filePath",value:t}})}let r=n.size/Eg;if(r>e)throw new y(`Attachment file exceeds the ${e} MB size cap (file is ${r.toFixed(2)} MB): ${t}`,{suggestion:`Reduce the file size to below ${e} MB, or increase the cap via the OMNIFOCUS_MAX_ATTACHMENT_MB environment variable.`,details:{field:"filePath",value:t,fileSizeBytes:n.size,capBytes:e*Eg}})}function Ng(t){return "taskId"in t&&t.taskId!==void 0?{taskId:t.taskId}:{projectId:t.projectId}}function Fg(t){return "taskId"in t?"task":"project"}var Tr=class{adapter;allowedPaths;maxMb;constructor(e){this.adapter=e.adapter,this.allowedPaths=e.allowedPaths,this.maxMb=e.maxAttachmentMb;}async list(e){return this.adapter.listAttachments(e)}async add(e){await rc(e.filePath,this.allowedPaths),await Mg(e.filePath,this.maxMb);let n=await this.adapter.addAttachment(e),r=Ng(e);return {id:n,ownerKind:Fg(r),ownerName:await this.lookupOwnerName(r)}}async remove(e){let n=Ng(e),r=await this.lookupOwnerName(n);return await this.adapter.removeAttachment(e),{ownerKind:Fg(n),ownerName:r}}async lookupOwnerName(e){try{return "taskId"in e?(await this.adapter.getTask(e.taskId)).name:(await this.adapter.getProject(e.projectId)).name}catch{return null}}async saveTo(e){let n=e.destPath.substring(0,e.destPath.lastIndexOf("/"))||"/";return await rc(n,this.allowedPaths),this.adapter.saveAttachmentToPath(e)}};function wr(t,e){t!==void 0&&ad(t,{folderId:e});}var Sr=class{adapter;cache;constructor({adapter:e,cache:n}){this.adapter=e,this.cache=n;}async list(e={}){return {folders:await this.adapter.listFolders(e.parentId!==void 0?{parentId:e.parentId}:{}),cacheHit:false}}async get(e){return {folder:await this.adapter.getFolder(e),cacheHit:false}}async create(e){let n=await this.adapter.createFolder(e);return wr(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateFolder(e,n),wr(this.cache,e);}async delete(e,n=false){n&&await this._cascadeEmpty(e),await this.adapter.deleteFolder(e),wr(this.cache,e);}async move(e,n){await this.adapter.updateFolder(e,{parentId:n}),wr(this.cache,e);}async _cascadeEmpty(e){let n=await this.adapter.listProjects({folderId:e});await Promise.all(n.map(o=>this.adapter.moveProject(o.id,{folderId:null})));let r=await this.adapter.listFolders({parentId:e});for(let o of r)await this._cascadeEmpty(o.id),await this.adapter.deleteFolder(o.id);}};var br=class{adapter;constructor(e){this.adapter=e.adapter;}async get(e){return {...await this.adapter.getForecast(e),cacheHit:false}}async getForecastTag(){let{tagId:e}=await this.adapter.getForecastTag();return {tagId:e,name:await this.lookupTagName(e)}}async setForecastTag(e){let{tagId:n}=await this.adapter.setForecastTag(e);return {tagId:n,name:await this.lookupTagName(n)}}async lookupTagName(e){if(e===null)return null;try{return (await this.adapter.getTag(e)).name}catch{return null}}};function oc(t){return st.includes(t)}var jr=class{adapter;constructor(e){this.adapter=e.adapter;}async list(){return {perspectives:await this.adapter.listPerspectives(),cacheHit:false}}async evaluate(e){return {tasks:oc(e)?await this.adapter.evaluatePerspective(e):await this.adapter.evaluateCustomPerspective(e),cacheHit:false}}async evaluateRules(e,n){return {tasks:await this.adapter.evaluatePerspectiveRules(e,n),cacheHit:false}}async get(e){if(oc(e))throw new y(`perspective_get only supports custom perspectives; got built-in id "${e}"`,{details:{field:"perspectiveId",value:e,kind:"builtin"}});return this.adapter.getCustomPerspective(e)}async delete(e){if(oc(e))throw new y(`perspective_delete cannot delete built-in perspectives; got "${e}"`,{details:{field:"perspectiveId",value:e,kind:"builtin"}});await this.adapter.deleteCustomPerspective(e);}};var Ug=200,ac=1e3,_r=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async list(e){let n=this.resolveLimit(e);this.assertBounded(e);let r=this.normalize(e),o=Nt(r),a=e.cursor!==void 0?Ut(e.cursor,o):void 0,i=this.listCacheKey(o,e.cursor),s=this.cache.has(i),{projects:c,nextCursor:d}=await this.cache.wrap(i,async()=>this.fetchPage(r,a,n,o));return {projects:c,nextCursor:d,hasMore:d!==null,cacheHit:s}}async completeProject(e){await this.adapter.completeProject(e);}async dropProject(e){await this.adapter.dropProject(e);}async moveProject(e,n){await this.adapter.moveProject(e,n);}async createProject(e){return this.adapter.createProject(e)}async updateProject(e,n){return this.adapter.updateProject(e,n)}async get(e){let n=e.includeTaskTree??true,r=this.getCacheKey(e.id,n),o=this.cache.has(r);return {...await this.cache.wrap(r,async()=>{let i=await this.adapter.getProject(e.id),s={...i,_links:ki(i)};if(!n)return {project:s};let d=(await this.adapter.listTasks({projectId:e.id})).map(l=>({...l,_links:et(l)}));return {project:s,tasks:d}}),cacheHit:o}}async fetchPage(e,n,r,o){let a={};e.folderId!==void 0&&(a.folderId=e.folderId),e.status!==void 0&&(a.status=e.status);let i=await this.adapter.listProjects(a),{flagged:s,reviewDueBefore:c}=e,l=[...i.filter(T=>!(s!==void 0&&T.flagged!==s||c!==void 0&&(T.nextReviewDate===null||T.nextReviewDate>=c)))].sort((T,E)=>T.createdAt!==E.createdAt?T.createdAt<E.createdAt?-1:1:T.id<E.id?-1:1),m=n!==void 0?l.filter(T=>Lt({id:T.id,sortValue:T.createdAt},n,"asc")):l,f=m.slice(0,r),v=m.length>r?this.encodeNextCursor(f,o):null;return {projects:f.map(T=>({...T,_links:ki(T)})),nextCursor:v}}resolveLimit(e){if(e.limit===void 0)return Ug;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>ac)throw new y(`limit must be an integer between 1 and ${ac}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${ac}, or omit to use the default of ${Ug}.`,details:{field:"limit",value:e.limit}});return e.limit}assertBounded(e){if(!(e.limit!==void 0||e.cursor!==void 0)&&!this.hasAnyFilter(e))throw new y("project_list requires at least one filter, limit, or cursor. Unbounded queries are rejected.",{suggestion:"Provide a filter or a limit.",details:{field:"filter|limit|cursor"}})}hasAnyFilter(e){return e.folderId!==void 0||e.status!==void 0||e.flagged!==void 0||e.reviewDueBefore!==void 0}normalize(e){return {folderId:e.folderId,status:e.status,flagged:e.flagged,reviewDueBefore:e.reviewDueBefore}}listCacheKey(e,n){return `search:projects:${e}:${n??"first"}`}getCacheKey(e,n){return `project:${e}:${n?"with-tasks":"solo"}`}encodeNextCursor(e,n){let r=e[e.length-1];if(r===void 0)throw new y("Internal: cannot encode cursor for empty page.");return Ft({lastId:r.id,lastSortValue:r.createdAt,filterHash:n})}};var Pr=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async listDue(){return {projects:await this.adapter.listProjectsDueForReview(),cacheHit:false}}async markReviewed(e){await this.adapter.markProjectReviewed(e),this.cache!==void 0&&J(this.cache,{projectId:e});let n=await this.lookupProject(e);return {name:n?.name??null,lastReviewDate:n?.lastReviewDate??null,nextReviewDate:n?.nextReviewDate??null}}async setInterval(e,n){await this.adapter.setProjectReviewInterval(e,n),this.cache!==void 0&&J(this.cache,{projectId:e});let r=await this.lookupProject(e);return {name:r?.name??null,reviewIntervalDays:r?.reviewIntervalDays??n}}async setNextReviewDate(e,n){await this.adapter.setProjectNextReviewDate(e,n),this.cache!==void 0&&J(this.cache,{projectId:e});let r=await this.lookupProject(e);return {name:r?.name??null,nextReviewDate:r?.nextReviewDate??n}}async lookupProject(e){try{return await this.adapter.getProject(e)}catch{return null}}};var ij=100,cj=500,Or=class{adapter;constructor({adapter:e}){this.adapter=e;}async search(e){let n=Math.min(e.limit??ij,cj),r={...e.q!==void 0?{q:e.q}:{},scope:e.scope??"all",...e.projectId!==void 0?{projectId:e.projectId}:{},...e.tagIds!==void 0?{tagIds:[...e.tagIds].sort()}:{},...e.available!==void 0?{available:e.available}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.dueAfter!==void 0?{dueAfter:e.dueAfter}:{},...e.flagged!==void 0?{flagged:e.flagged}:{},...e.completed!==void 0?{completed:e.completed}:{}},o=Nt(r),a=null;e.cursor&&(a=Ut(e.cursor,o));let i={...e.q!==void 0?{q:e.q}:{},...e.scope!==void 0?{scope:e.scope}:{},...e.projectId!==void 0?{projectId:e.projectId}:{},...e.tagIds!==void 0?{tagIds:e.tagIds}:{},...e.available!==void 0?{available:e.available}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.dueAfter!==void 0?{dueAfter:e.dueAfter}:{},...e.flagged!==void 0?{flagged:e.flagged}:{},...e.completed!==void 0?{completed:e.completed}:{}},c=[...await this.adapter.searchTasks(i)].sort((I,T)=>{let E=I.createdAt.localeCompare(T.createdAt);return E!==0?E:I.id.localeCompare(T.id)}),l=(a?c.filter(I=>Lt({id:I.id,sortValue:I.createdAt},a,"asc")):c).slice(0,n+1),m=l.length>n,f=l.slice(0,n).map(I=>({...I,_links:et(I)})),g=f.at(-1),v=m&&g?Ft({lastId:g.id,lastSortValue:g.createdAt,filterHash:o}):null;return {tasks:f,nextCursor:v,hasMore:m,cacheHit:false}}};function tt(t,e){t!==void 0&&od(t,{tagId:e});}var xr=class{adapter;cache;constructor({adapter:e,cache:n}){this.adapter=e,this.cache=n;}async list(e={}){return {tags:await this.adapter.listTags({...e.parentId!==void 0?{parentId:e.parentId}:{},...e.status!==void 0?{status:e.status}:{}}),cacheHit:false}}async get(e){return {tag:await this.adapter.getTag(e),cacheHit:false}}async create(e){let n=await this.adapter.createTag(e);return tt(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateTag(e,n),tt(this.cache,e);}async delete(e){await this.adapter.deleteTag(e),tt(this.cache,e);}async move(e,n){await this.adapter.updateTag(e,{parentId:n}),tt(this.cache,e);}async setStatus(e,n){await this.adapter.updateTag(e,{status:n}),tt(this.cache,e);}async setAllowsNextAction(e,n){await this.adapter.updateTag(e,{allowsNextAction:n}),tt(this.cache,e);}async setLocation(e,n){await this.adapter.updateTag(e,{location:n}),tt(this.cache,e);}async clearLocation(e){await this.adapter.updateTag(e,{location:null}),tt(this.cache,e);}async getLocation(e){return {location:(await this.adapter.getTag(e)).location,cacheHit:false}}};function Lg(t){if(t.OMNIFOCUS_E2E_USE_MEMORY){let r=new fr;return new Wt({jxa:r,omnijs:r})}let e=new hr({timeoutMs:t.OMNIFOCUS_JXA_TIMEOUT_MS}),n=new kr({timeoutMs:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS});return Wt.fromTransports(e,n)}function Bg(t,e){let n=new Ir({capacity:e.OMNIFOCUS_CACHE_CAPACITY,ttlMs:e.OMNIFOCUS_CACHE_TTL_MS});return {cache:n,taskService:new sr({adapter:t,cache:n}),projectService:new _r({adapter:t,cache:n}),tagService:new xr({adapter:t,cache:n}),folderService:new Sr({adapter:t,cache:n}),attachmentService:new Tr({adapter:t,allowedPaths:e.OMNIFOCUS_ATTACHMENT_PATHS,maxAttachmentMb:e.OMNIFOCUS_MAX_ATTACHMENT_MB}),exportService:new At({adapter:t}),forecastService:new br({adapter:t}),perspectiveService:new jr({adapter:t}),pluginService:new Ot({adapter:t}),reviewService:new Pr({adapter:t,cache:n}),searchService:new Or({adapter:t})}}var dj="jxa",lj="unknown";function C(t={}){return {correlationId:ze()??fg(),durationMs:0,cacheHit:false,transport:dj,ofVersion:lj,...t}}function Jg(t){let{adapter:e,cache:n,server:r,aggregateUris:o,orchestrator:a}=t;return async i=>{let s=new Date(i.detectedAt).getTime()-200,c=new Date(s).toISOString(),d={taskIds:[],projectIds:[]},l=false;try{d=await e.getChangesSince(c),l=!0;}catch(m){R.debug({event:"database.changed.query_failed",err:m});}if(l&&(d.taskIds.length>0||d.projectIds.length>0)){for(let m of d.taskIds)n.invalidate(`task:${m}`);for(let m of d.projectIds)n.invalidate(`project:${m}`);}else n.clear();if(a?.shouldObserve())try{let[m,f]=await Promise.all([e.listTasks({}),e.listProjects()]);await a.observeSnapshot(m,f);}catch(m){R.debug({event:"database.changed.webhook_observe_failed",err:m});}for(let m of d.taskIds)r.server.sendResourceUpdated({uri:`omnifocus://task/${m}`}).catch(()=>{});for(let m of d.projectIds)r.server.sendResourceUpdated({uri:`omnifocus://project/${m}`}).catch(()=>{});for(let m of o)r.server.sendResourceUpdated({uri:m}).catch(()=>{});R.debug({event:"database.changed",source:i.source,detectedAt:i.detectedAt,changedTasks:d.taskIds.length,changedProjects:d.projectIds.length,cacheStrategy:l?"targeted":"full-clear"});}}async function $g(t,e){let n=performance.now();try{let r=await e(),o=Math.round(performance.now()-n);return R.info({event:"tool.invoked",tool:t,correlationId:ze(),durationMs:o,transport:r.meta.transport,cacheHit:r.meta.cacheHit},"tool invoked"),r}catch(r){let o=Math.round(performance.now()-n),a=dc(r)?r.code:"UNKNOWN";throw R.warn({event:"tool.error",tool:t,correlationId:ze(),durationMs:o,code:a,err:r},"tool error"),r}}function Wg(t,e,n,r){let o=n.record(t,e);if(o!==void 0){let{level:a,warning:i}=o,s=i.details?.count??0,c=(i.details?.windowSeconds??60)*1e3;if(R.warn({event:"loop.detected",tool:t,callCount:s,windowMs:c,level:a}),a==="error")return Promise.reject(new bn(t,s,i.details?.windowSeconds??60))}return r().then(a=>{if(o===void 0)return a;let i=a.meta.warnings??[];return {...a,meta:{...a.meta,warnings:[...i,o.warning]}}})}function Hg(t,e,n){return e.check(t),n().then(r=>{let o=e.remaining(t);return {...r,meta:{...r.meta,rateLimit:o}}})}function pj(t,e,n){return (r,o)=>mg(async()=>(n.shutdown.assertNotShuttingDown(),n.circuitRegistry.get(t).call(async()=>{let i=async()=>(await e(r,o)).structuredContent,s=await Hg(t,n.rateLimiter,()=>Wg(t,r,n.loopDetector,()=>$g(t,i)));return u(s)})))}function zg(t,e){let n=t;if(n.__omnifocusMiddlewareInstalled===true)return;let r=t.registerTool.bind(t),o=(...a)=>{let[i,s,c]=a,d=pj(i,c,e);return r(i,s,d)};t.registerTool=o,n.__omnifocusMiddlewareInstalled=true;}var uj=5e3,mj=1e4,fj=50,sc=class{_shuttingDown=false;_queues=[];_readGraceMs;_writeGraceMs;constructor(e={}){this._readGraceMs=e.readGraceMs??Number(process.env.OMNIFOCUS_READ_GRACE_MS??uj),this._writeGraceMs=e.writeGraceMs??Number(process.env.OMNIFOCUS_WRITE_GRACE_MS??mj);}get isShuttingDown(){return this._shuttingDown}assertNotShuttingDown(){if(this._shuttingDown)throw new Sn}registerQueue(e){this._queues.includes(e)||this._queues.push(e);}async initiate(e,n=process.exit){if(this._shuttingDown)return;this._shuttingDown=true,R.info({event:"server.shutdown",reason:e,readGraceMs:this._readGraceMs,writeGraceMs:this._writeGraceMs},"graceful shutdown initiated");let r=this._readGraceMs+this._writeGraceMs;await this._drainAll(r),R.flush(),n(0);}async _drainAll(e){if(this._queues.length===0)return;let n=Date.now()+e;for(;Date.now()<n;){if(this._queues.filter(a=>a.pendingCount()>0).length===0)return;await new Promise(a=>setTimeout(a,fj));}let r=this._queues.filter(o=>o.pendingCount()>0);for(let o of r)R.warn({event:"server.shutdown.drain_timeout",queue:o.name,pending:o.pendingCount()},"queue did not drain within grace window; forcing shutdown");}},pt=new sc;var gj=["@modelcontextprotocol/sdk","node_modules/@modelcontextprotocol"];function hj(t){return gj.some(e=>t.includes(e))}var Gg=null,qg=false;function Vg(){if(qg)return;Gg=process.stdout.write,qg=true;let t=Gg;process.stdout.write=function(n,r,o){let a=new Error().stack??"";if(hj(a))return typeof r=="function"?t.call(process.stdout,n,r):t.call(process.stdout,n,r,o);let i=typeof n=="string"?n.slice(0,120):`<${n.byteLength} bytes>`;throw new Y("OF_STRAY_STDOUT",`Stray stdout write detected \u2014 stdout is reserved for MCP transport. Use process.stderr / logger for diagnostics. Attempted: ${JSON.stringify(i)}`,{suggestion:"Replace console.log / process.stdout.write with process.stderr.write or logger."})};}var Kg=Fe.version,Ij=Fe.name.split("/").pop()??"omnifocus-mcp",vj=new Set(["run_jxa_script","run_omnijs_script"]),Tj=Date.now();function wj(){return new McpServer({name:Ij,version:Kg})}async function Xg(){Vg();let t=lc();R.level=t.OMNIFOCUS_LOG_LEVEL;let e=wj(),n=new _n(t.OMNIFOCUS_TOOL_RATE_LIMIT),r=new jn;zg(e,{rateLimiter:n,loopDetector:r,circuitRegistry:tc,shutdown:pt});let o=new StdioServerTransport,a=Lg(t),i=new In({size:t.OMNIFOCUS_READ_POOL_SIZE,name:"jxa-read"}),s=new zt({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"jxa-write"}),c=new zt({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"omnijs"});pt.registerQueue(i),pt.registerQueue(s),pt.registerQueue(c);let d=cc(a,{readPool:i,jxaWriteQueue:s,omniJsQueue:c}),l=Bg(d,t);jl(e,{startedAt:Tj,adapter:d,circuitRegistry:tc,makeMeta:C}),mc(e);let m=new mr,f={registry:m,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:C};Fm(e,f),Lm(e,f),Um(e,f);let g=new ur({registry:m,dispatcher:new pr});Bm(e,{orchestrator:g,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:C}),hc(e,async()=>{let Re=m.list();return gc(t,{calendarAccess:await On(),webhooks:{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:Re.length,names:Re.map(Rr=>Rr.name)}})}),td(e,{adapter:d,projectService:l.projectService,reviewService:l.reviewService,forecastService:l.forecastService,perspectiveService:l.perspectiveService});let v={folderService:l.folderService,makeMeta:C};nl(e,v),al(e,v),il(e,v),cl(e,v),dl(e,v),ul(e,v);let I={adapter:d,makeMeta:C};rl(e,I),sl(e,I),ll(e,I),ml(e,I);let T={tagService:l.tagService,makeMeta:C};zp(e,T),qp(e,T),Zp(e,{adapter:d,makeMeta:C}),Kp(e,T),Xp(e,T),Qp(e,T),eu(e,T),ru(e,T),ou(e,T),au(e,T),su(e,T);let E={adapter:d,makeMeta:C};Gp(e,E),Vp(e,E),tu(e,E),iu(e,E);let w={adapter:d,makeMeta:C,cache:l.cache};kl(e,w),Il(e,w),vl(e,w),Tl(e,w),wl(e,w),$p(e,{searchService:l.searchService,makeMeta:C}),fl(e,{forecastService:l.forecastService,makeMeta:C}),hl(e,{forecastService:l.forecastService,makeMeta:C}),gl(e,{forecastService:l.forecastService,makeMeta:C}),yl(e,{forecastService:l.forecastService,cache:l.cache,makeMeta:C});let D={perspectiveService:l.perspectiveService,makeMeta:C};Ml(e,D),Rl(e,D),Al(e,D),El(e,D),xl(e,{perspectiveService:l.perspectiveService,cache:l.cache,makeMeta:C}),Pl(e,{adapter:d,cache:l.cache,makeMeta:C}),Fl(e,{adapter:d,cache:l.cache,makeMeta:C}),Ul(e,{adapter:d,makeMeta:C}),Wp(e,{adapter:d,makeMeta:C}),Hp(e,{adapter:d,makeMeta:C,cache:l.cache}),ld(e,{adapter:d,makeMeta:C,cache:l.cache}),cd(e,{adapter:d,makeMeta:C,cache:l.cache});let j={reviewService:l.reviewService,makeMeta:C};Mp(e,j),Fp(e,j),Lp(e,j),Bp(e,j),Jp(e,j);let B={exportService:l.exportService,adapter:d,makeMeta:C};md(e,B),Qd(e,B),el(e,B),nd(e,{adapter:d,makeMeta:C});let _={adapter:d,makeMeta:C};Jm(e,_),$m(e,_),Wm(e,_),Hm(e,_),zm(e,_);let U={projectService:l.projectService,makeMeta:C,cache:l.cache},M={adapter:d,makeMeta:C,cache:l.cache};Ll(e,M),Bl(e,M),Jl(e,U),Zl(e,{...M,replayStore:ve}),ep(e,M),np(e,U),ip(e,{adapter:d,makeMeta:C}),ap(e,{projectService:l.projectService,makeMeta:C}),cp(e,{projectService:l.projectService,makeMeta:C}),dp(e,U),Pp(e,M);let H={adapter:d,makeMeta:C,cache:l.cache,templatesFolderName:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME};_p(e,H),jp(e,H),Km(e,H),bp(e,H);let z={adapter:d,makeMeta:C};$l(e,z),Ql(e,z),tp(e,z),rp(e,z),lp(e,z),Op(e,z);let le={taskService:l.taskService,makeMeta:C},Ge={adapter:d,makeMeta:C},A={adapter:d,makeMeta:C,cache:l.cache};om(e,le),lm(e,le),Qu(e,Ge),nm(e,Ge),wm(e,{searchService:l.searchService,makeMeta:C}),sm(e,Ge),ym(e,{makeMeta:C}),qm(e,{makeMeta:C}),Ep(e,{makeMeta:C,replayStore:ve}),ud(e,A),pd(e,A),Lu(e,A),hu(e,A),Im(e,A),cu(e,A),du(e,A),lu(e,A),yu(e,A),ku(e,A),Iu(e,A),vu(e,A),Tu(e,A),wu(e,A),bu(e,A),ju(e,A),Ou(e,{...A,replayStore:ve}),Wu(e,A),zu(e,A),Ku(e,{...A,attachmentService:l.attachmentService}),Zu(e,A),Ru(e,A);let $={adapter:d,makeMeta:C,cache:l.cache,waitingTagName:t.OMNIFOCUS_WAITING_TAG_NAME};Rm(e,$),Am(e,$),um(e,A),vm(e,A),jm(e,A),_m(e,A),Pm(e,A),Om(e,A),Nu(e,A),Bu(e,A),xm(e,A);let fe={adapter:d,makeMeta:C};pu(e,fe),Su(e,fe),xu(e,fe),Fu(e,fe),Ju(e,fe),Hu(e,fe),mm(e,fe),Dm(e,fe),rd(e,{attachmentService:l.attachmentService,makeMeta:C});let Ne={adapter:d,makeMeta:C},nt={allowRawScript:t.OMNIFOCUS_ALLOW_RAW_SCRIPT};xp(e,Ne,nt),Dp(e,Ne,nt),process.on("SIGINT",()=>{pt.initiate("SIGINT");}),process.on("SIGTERM",()=>{pt.initiate("SIGTERM");}),process.on("unhandledRejection",Re=>{R.fatal({event:"server.unhandled_rejection",reason:Re},"unhandled rejection"),R.flush(),process.exit(1);}),process.on("uncaughtException",Re=>{R.fatal({event:"server.uncaught_exception",err:Re},"uncaught exception"),R.flush(),process.exit(1);}),await e.connect(o);let Yg=Jg({adapter:d,cache:l.cache,server:e,aggregateUris:[Qt,en,tn,nn,rn,on],orchestrator:g}),ic=new lr(Re=>{Yg(Re).catch(Rr=>{R.error({event:"database.changed.handler_error",err:Rr});});});ic.start(),process.on("exit",()=>ic.stop());let Zg=Object.keys(Gm).filter(Re=>t.OMNIFOCUS_ALLOW_RAW_SCRIPT||!vj.has(Re)).sort();R.info({event:"server.started",version:Kg,config:pc(t),tools:Zg,prompts:[Ar,Cr,Er,Mr],resources:[Pn,Qt,en,tn,nn,rn,on,no,ro,oo]},"server started");}var Dr=process.argv.slice(2);(Dr.includes("--version")||Dr.includes("-v"))&&(process.stdout.write(`${Fe.version}
6850
+ `),process.exit(0));(Dr.includes("--help")||Dr.includes("-h"))&&(process.stdout.write(`${Fe.name} v${Fe.version}
6851
+ ${Fe.description}
6313
6852
 
6314
6853
  Usage: omnifocus-mcp
6315
6854
 
6316
6855
  Speaks Model Context Protocol over stdio. Add to your MCP client's
6317
6856
  configuration (Claude Desktop, Claude Code, etc.) \u2014 see the README
6318
- for client-specific setup: ${_e.homepage}
6857
+ for client-specific setup: ${Fe.homepage}
6319
6858
 
6320
6859
  Options:
6321
6860
  -v, --version Print version and exit
@@ -6326,5 +6865,5 @@ Environment:
6326
6865
  OMNIFOCUS_ALLOW_RAW_SCRIPT Enable raw-script tools (off by default)
6327
6866
  OMNIFOCUS_JXA_TIMEOUT_MS Per-call JXA timeout (default: 30000)
6328
6867
  OMNIFOCUS_OMNIJS_TIMEOUT_MS Per-call OmniJS timeout (default: 45000)
6329
- `),process.exit(0));qm().catch(t=>{process.stderr.write(`[omnifocus-mcp] Fatal startup error: ${String(t)}
6868
+ `),process.exit(0));Xg().catch(t=>{process.stderr.write(`[omnifocus-mcp] Fatal startup error: ${String(t)}
6330
6869
  `),process.exit(1);});