@torsday/omnifocus-mcp 1.3.0 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +205 -22
- package/README.md +30 -727
- package/dist/index.js +406 -264
- package/package.json +11 -4
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {McpServer,ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {createHmac,createHash,randomBytes}from'crypto';import GS,{homedir}from'os';import {z}from'zod';import xc from'pino';import {spawn,execFile}from'child_process';import gt from'fs';import wr,{extname,sep}from'path';import {fileURLToPath}from'url';import {createInterface}from'readline';import QS 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 Ne={name:"@torsday/omnifocus-mcp",version:"1.3.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 Xt=Object.freeze({listTasks:"jxa",getTask:"jxa",getTasksMany:"jxa",createTask:"omnijs",updateTask:"jxa",completeTask:"jxa",uncompleteTask:"jxa",dropTask:"jxa",undropTask:"jxa",deleteTask:"jxa",moveTask:"omnijs",convertTaskToProject:"omnijs",batchMoveTasks:"omnijs",reorderTask:"omnijs",duplicateTask:"omnijs",batchCreateTasks:"jxa",batchUpdateTasks:"jxa",batchCompleteTasks:"jxa",batchUncompleteTasks:"jxa",batchDeleteTasks:"jxa",batchDropTasks:"jxa",batchUndropTasks:"jxa",listProjects:"jxa",getProject:"jxa",getProjectsMany:"jxa",createProject:"omnijs",updateProject:"jxa",completeProject:"jxa",batchCompleteProjects:"jxa",dropProject:"jxa",batchDropProjects:"jxa",moveProject:"omnijs",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 Yt=class t{jxa;omnijs;constructor(e){this.jxa=e.jxa,this.omnijs=e.omnijs;}get routingTable(){return Xt}static fromTransports(e,n){return new t({jxa:e,omnijs:n})}pick(e){return Xt[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 Th=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 wh(t,e){return Xt[t]==="omnijs"?e.omniJsQueue:Th.has(t)?e.jxaWriteQueue:e.readPool}function bc(t,e){return new Proxy(t,{get(n,r,o){if(typeof r!="string"||!(r in Xt))return Reflect.get(n,r,o);let a=r,i=wh(a,e),c=Reflect.get(n,a,o).bind(n);return (...d)=>i.run(()=>c(...d))}})}var _n=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 Z=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 Sc(t){return t instanceof Z}var kt=class extends Z{constructor(e={}){super("OF_NOT_RUNNING","OmniFocus is not running.",{remediationClass:"environment",suggestion:"Launch OmniFocus and retry.",...e});}},vt=class extends Z{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});}},xn=class extends Z{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 Z{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 Z{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 Z{constructor(e,n={}){super("OF_VALIDATION",e,{remediationClass:"input",suggestion:"Fix the input and retry. See `details` for field-level reasons.",...n});}},j=class extends Z{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});}},It=class extends Z{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});}},Tt=class extends Z{constructor(e,n={}){super("OF_TIMEOUT",e,{remediationClass:"transient",suggestion:"Retry once. If repeated, OmniFocus may be wedged \u2014 relaunch it.",...n});}},On=class extends Z{constructor(e,n={}){super("OF_RATE_LIMITED",e,{remediationClass:"transient",suggestion:"Wait details.retryAfterMs milliseconds then retry.",...n,details:{retryAfterMs:6e4,...n.details}});}},Pn=class extends Z{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});}},Zt=class extends Z{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}});}},wt=class extends Z{constructor(e,n={}){super("OF_TRANSPORT_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"The required transport is unreachable. Verify OmniFocus is running and responsive.",...n});}},bt=class extends Z{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});}},E=class extends Z{constructor(e,n={}){super("OF_SCRIPT_ERROR",e,{remediationClass:"infrastructure",suggestion:"The OmniFocus script failed. Inspect `details.transport` and `details.reason` for context.",...n});}},Dn=class extends Z{constructor(e={}){super("OF_SHUTTING_DOWN","Server is shutting down; not accepting new requests.",{remediationClass:"lifecycle",suggestion:"Reconnect to a fresh server instance.",...e});}},An=class extends Z{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 Qt=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 Pn(`${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 jh=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}}),_h=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:jh.prefault("120/60"),OMNIFOCUS_WAITING_TAG_NAME:z.string().min(1).default("waiting"),OMNIFOCUS_TEMPLATES_FOLDER_NAME:z.string().min(1).default("Templates"),OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE:z.coerce.number().min(0).max(1).default(0),OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES:z.coerce.number().int().positive().default(51200)});function jc(t=process.env,e=n=>{process.stderr.write(`[omnifocus-mcp] Config error: ${n}
|
|
3
|
-
`),process.exit(1);}){let n=
|
|
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 yj,{homedir}from'os';import {z as z$1}from'zod';import Gc from'pino';import {spawn,execFile}from'child_process';import Pt from'fs';import $r,{extname,sep}from'path';import {fileURLToPath}from'url';import {createInterface}from'readline';import wj from'https';import {setTimeout as setTimeout$1}from'timers/promises';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 Ke={name:"@torsday/omnifocus-mcp",version:"1.5.3",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 uo={enabled:process.env.OMNIFOCUS_TRANSIENT_RETRY_ENABLED!=="0",delayMs:Number(process.env.OMNIFOCUS_TRANSIENT_RETRY_DELAY_MS??100)};function mo(t){uo={...uo,...t};}function $n(t){return {...uo,...t??{}}}var mn=Object.freeze({listTasks:"jxa",getTask:"jxa",getNoteHtml:"jxa",getTasksMany:"jxa",createTask:"omnijs",updateTask:"jxa",completeTask:"jxa",uncompleteTask:"jxa",dropTask:"jxa",undropTask:"jxa",deleteTask:"jxa",moveTask:"omnijs",convertTaskToProject:"omnijs",batchMoveTasks:"omnijs",reorderTask:"omnijs",duplicateTask:"omnijs",batchCreateTasks:"jxa",batchUpdateTasks:"jxa",batchCompleteTasks:"jxa",batchUncompleteTasks:"jxa",batchDeleteTasks:"jxa",batchDropTasks:"jxa",batchUndropTasks:"jxa",listProjects:"jxa",getProject:"jxa",getProjectsMany:"jxa",createProject:"omnijs",updateProject:"jxa",completeProject:"jxa",batchCompleteProjects:"jxa",dropProject:"jxa",batchDropProjects:"jxa",moveProject:"omnijs",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 fn=class t{jxa;omnijs;constructor(e){this.jxa=e.jxa,this.omnijs=e.omnijs;}get routingTable(){return mn}static fromTransports(e,n){return new t({jxa:e,omnijs:n})}pick(e){return mn[e]==="jxa"?this.jxa:this.omnijs}listTasks(e){return this.pick("listTasks").listTasks(e)}getTask(e){return this.pick("getTask").getTask(e)}getNoteHtml(e,n){return this.pick("getNoteHtml").getNoteHtml(e,n)}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 Lh=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 Jh(t,e){return mn[t]==="omnijs"?e.omniJsQueue:Lh.has(t)?e.jxaWriteQueue:e.readPool}function Hc(t,e){return new Proxy(t,{get(n,r,o){if(typeof r!="string"||!(r in mn))return Reflect.get(n,r,o);let a=r,s=Jh(a,e),c=Reflect.get(n,a,o).bind(n);return (...l)=>s.run(()=>c(...l))}})}var Wn=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 oe=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 zc(t){return t instanceof oe}var Ct=class extends oe{constructor(e={}){super("OF_NOT_RUNNING","OmniFocus is not running.",{remediationClass:"environment",suggestion:"Launch OmniFocus and retry.",...e});}},Ft=class extends oe{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});}},Hn=class extends oe{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});}},be=class extends oe{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});}},Xe=class extends oe{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 k=class extends oe{constructor(e,n={}){super("OF_VALIDATION",e,{remediationClass:"input",suggestion:"Fix the input and retry. See `details` for field-level reasons.",...n});}},_=class extends oe{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});}},it=class extends oe{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});}},Et=class extends oe{constructor(e,n={}){super("OF_TIMEOUT",e,{remediationClass:"transient",suggestion:"Retry once. If repeated, OmniFocus may be wedged \u2014 relaunch it.",...n});}},zn=class extends oe{constructor(e,n={}){super("OF_RATE_LIMITED",e,{remediationClass:"transient",suggestion:"Wait details.retryAfterMs milliseconds then retry.",...n,details:{retryAfterMs:6e4,...n.details}});}},Vn=class extends oe{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});}},gn=class extends oe{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}});}},Mt=class extends oe{constructor(e,n={}){super("OF_TRANSPORT_UNAVAILABLE",e,{remediationClass:"infrastructure",suggestion:"The required transport is unreachable. Verify OmniFocus is running and responsive.",...n});}},Nt=class extends oe{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});}},E=class extends oe{constructor(e,n={}){super("OF_SCRIPT_ERROR",e,{remediationClass:"infrastructure",suggestion:"The OmniFocus script failed. Inspect `details.transport` and `details.reason` for context.",...n});}},qn=class extends oe{constructor(e={}){super("OF_SHUTTING_DOWN","Server is shutting down; not accepting new requests.",{remediationClass:"lifecycle",suggestion:"Reconnect to a fresh server instance.",...e});}},Gn=class extends oe{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 hn=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 Vn(`${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 Wh=z$1.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}}),Hh=z$1.object({OMNIFOCUS_LOG_LEVEL:z$1.enum(["trace","debug","info","warn","error"]).default("info"),OMNIFOCUS_INTEGRATION:z$1.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_E2E:z$1.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_E2E_USE_MEMORY:z$1.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_ALLOW_RAW_SCRIPT:z$1.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_WEBHOOKS_ENABLED:z$1.string().prefault("").transform(t=>t==="1"),OMNIFOCUS_CACHE_TTL_MS:z$1.coerce.number().int().positive().default(3e4),OMNIFOCUS_CACHE_CAPACITY:z$1.coerce.number().int().positive().default(256),OMNIFOCUS_READ_CACHE_MAX_BYTES:z$1.coerce.number().int().nonnegative().default(16*1024*1024),OMNIFOCUS_READ_POOL_SIZE:z$1.coerce.number().int().min(1).max(8).default(2),OMNIFOCUS_WRITE_QUEUE_CAP:z$1.coerce.number().int().positive().default(50),OMNIFOCUS_JXA_TIMEOUT_MS:z$1.coerce.number().int().positive().default(3e4),OMNIFOCUS_OMNIJS_TIMEOUT_MS:z$1.coerce.number().int().positive().default(45e3),OMNIFOCUS_TRANSIENT_RETRY_ENABLED:z$1.string().prefault("1").transform(t=>t!=="0"),OMNIFOCUS_TRANSIENT_RETRY_DELAY_MS:z$1.coerce.number().int().min(0).default(100),OMNIFOCUS_ATTACHMENT_PATHS:z$1.string().prefault(homedir()).transform(t=>t.split(":").filter(Boolean)),OMNIFOCUS_MAX_ATTACHMENT_MB:z$1.coerce.number().int().positive().default(100),OMNIFOCUS_TOOL_RATE_LIMIT:Wh.prefault("120/60"),OMNIFOCUS_WAITING_TAG_NAME:z$1.string().min(1).default("waiting"),OMNIFOCUS_TEMPLATES_FOLDER_NAME:z$1.string().min(1).default("Templates"),OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE:z$1.coerce.number().min(0).max(1).default(0),OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES:z$1.coerce.number().int().positive().default(51200),OMNIFOCUS_LOOP_DETECTOR_MAX_KEYS:z$1.coerce.number().int().positive().default(4096)});function Vc(t=process.env,e=n=>{process.stderr.write(`[omnifocus-mcp] Config error: ${n}
|
|
3
|
+
`),process.exit(1);}){let n=Hh.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_CACHE_MAX_BYTES:t.OMNIFOCUS_READ_CACHE_MAX_BYTES,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_TRANSIENT_RETRY_ENABLED:t.OMNIFOCUS_TRANSIENT_RETRY_ENABLED,OMNIFOCUS_TRANSIENT_RETRY_DELAY_MS:t.OMNIFOCUS_TRANSIENT_RETRY_DELAY_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,OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE:t.OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE,OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES:t.OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES,OMNIFOCUS_LOOP_DETECTOR_MAX_KEYS:t.OMNIFOCUS_LOOP_DETECTOR_MAX_KEYS});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
|
|
6
|
+
See DESIGN \xA722 for allowed values.`)}return n.data}function zh(t){return createHash("sha256").update(t).digest("hex").slice(0,12)}function qc(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_CACHE_MAX_BYTES:t.OMNIFOCUS_READ_CACHE_MAX_BYTES,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_TRANSIENT_RETRY_ENABLED:t.OMNIFOCUS_TRANSIENT_RETRY_ENABLED,OMNIFOCUS_TRANSIENT_RETRY_DELAY_MS:t.OMNIFOCUS_TRANSIENT_RETRY_DELAY_MS,OMNIFOCUS_ATTACHMENT_PATHS:t.OMNIFOCUS_ATTACHMENT_PATHS.map(zh),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,OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE:t.OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE,OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES:t.OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES,OMNIFOCUS_LOOP_DETECTOR_MAX_KEYS:t.OMNIFOCUS_LOOP_DETECTOR_MAX_KEYS}}var Vh=["name","note","noteHtml","tagNames","tagNames[*]","*.name","*.note","*.noteHtml","*.tagNames","data.name","data.note","data.noteHtml","data.tagNames","data.tagNames[*]","tasks[*].name","tasks[*].note","tasks[*].noteHtml","tasks[*].tagNames","tasks[*].tagNames[*]","projects[*].name","projects[*].note","projects[*].noteHtml","tags[*].name","folders[*].name","attachments[*].name","attachments[*].path","data.tasks[*].name","data.tasks[*].note","data.tasks[*].noteHtml","data.tasks[*].tagNames","data.tasks[*].tagNames[*]","data.projects[*].name","data.projects[*].note","data.projects[*].noteHtml","data.tags[*].name","data.folders[*].name","data.attachments[*].name","data.attachments[*].path","data.overdue[*].name","data.overdue[*].note","data.overdue[*].noteHtml","data.overdue[*].tagNames","data.overdue[*].tagNames[*]","data.dueToday[*].name","data.dueToday[*].note","data.dueToday[*].noteHtml","data.dueToday[*].tagNames","data.dueToday[*].tagNames[*]","data.deferredToday[*].name","data.deferredToday[*].note","data.deferredToday[*].noteHtml","data.deferredToday[*].tagNames","data.deferredToday[*].tagNames[*]","data.flagged[*].name","data.flagged[*].note","data.flagged[*].noteHtml","data.flagged[*].tagNames","data.flagged[*].tagNames[*]","data.inbox[*].name","data.inbox[*].note","data.inbox[*].noteHtml","data.inbox[*].tagNames","data.task.name","data.task.note","data.task.noteHtml","data.task.tagNames","data.task.tagNames[*]","data.project.name","data.project.note","data.project.noteHtml","data.tag.name","data.folder.name","data.attachment.name","data.attachment.path","details.input.name","details.input.note","details.input.noteHtml","details.input.tagNames","details.input.destPath","err.details.input.name","err.details.input.note","err.details.input.noteHtml","err.details.input.destPath"];function qh(t="info"){return Gc({level:t,redact:{paths:Vh,censor:"[redacted]"},formatters:{level(e){return {level:e}}},timestamp:Gc.stdTimeFunctions.epochTime},process.stderr)}var R=qh(process.env.OMNIFOCUS_LOG_LEVEL??"info");function ct(t){if(t===void 0)return "undefined";if(t===null||typeof t!="object")return JSON.stringify(t);if(Array.isArray(t))return `[${t.map(ct).join(",")}]`;let e=t;return `{${Object.keys(e).sort().filter(r=>e[r]!==void 0).map(r=>`${JSON.stringify(r)}:${ct(e[r])}`).join(",")}}`}var Kh={threshold:5,errorThreshold:10,windowSeconds:60,maxKeys:4096};function Xh(t,e){let n=ct(e),r=createHash("sha1").update(n).digest("hex").slice(0,16);return `${t}:${r}`}var Kn=class{config;windows=new Map;constructor(e={}){this.config={...Kh,...e};}get size(){return this.windows.size}record(e,n){let r=Xh(e,n),o=Date.now(),a=o-this.config.windowSeconds*1e3,s=this.getAndPrune(r,a);s.push(o);let i=s.length;if(i<this.config.threshold)return;let c={code:"WARN_LOOP_DETECTED",message:`Tool "${e}" has been called ${i} time(s) with identical arguments within ${this.config.windowSeconds}s.`,suggestion:"The agent may be stuck in a loop. Verify that the previous response was acted on before repeating this call.",details:{tool:e,count:i,windowSeconds:this.config.windowSeconds}};return i>=this.config.errorThreshold?{level:"error",warning:c}:{level:"warn",warning:c}}reset(e){e===void 0?this.windows.clear():this.windows.delete(e);}getAndPrune(e,n){let r=this.windows.get(e);if(r!==void 0){let a=0;for(;a<r.length&&r[a]<=n;)a++;if(a>0&&r.splice(0,a),r.length===0)this.windows.delete(e);else return r}if(this.windows.size>=this.config.maxKeys){let a=this.windows.keys().next().value;a!==void 0&&this.windows.delete(a);}let o=[];return this.windows.set(e,o),o}};var Xn=class{constructor(e){this.opts=e;this.startedAt=(e.now??(()=>new Date))(),this.random=e.random??Math.random,this.reservoirSize=e.reservoirSize??1024;}opts;states=new Map;startedAt;random;reservoirSize;record(e,n){if(this.opts.sampleRate<=0||this.opts.sampleRate<1&&this.random()>=this.opts.sampleRate||!Number.isFinite(n)||n<0)return;let r=this.states.get(e);r||(r={count:0,total:0,max:0,ring:[],cursor:0,overThreshold:false},this.states.set(e,r)),r.count+=1,r.total+=n,n>r.max&&(r.max=n),r.ring.length<this.reservoirSize?r.ring.push(n):(r.ring[r.cursor]=n,r.cursor=(r.cursor+1)%this.reservoirSize),this.checkThreshold(e,r);}snapshot(){let e={};for(let[n,r]of this.states)e[n]={count:r.count,total:r.total,max:r.max,p50:fo(r.ring,.5),p95:fo(r.ring,.95)};return {since:this.startedAt.toISOString(),tools:e,sampleRate:this.opts.sampleRate,thresholdBytes:this.opts.thresholdBytes}}reset(){this.states.clear();}checkThreshold(e,n){if(!Number.isFinite(this.opts.thresholdBytes)||n.ring.length<16)return;let r=fo(n.ring,.95),o=r>=this.opts.thresholdBytes;o&&!n.overThreshold?(this.opts.logger.warn({event:"response.size.exceeded",tool:e,p95Bytes:r,thresholdBytes:this.opts.thresholdBytes,count:n.count},"tool response p95 above threshold"),n.overThreshold=true):!o&&n.overThreshold&&(this.opts.logger.info({event:"response.size.recovered",tool:e,p95Bytes:r,thresholdBytes:this.opts.thresholdBytes},"tool response p95 recovered below threshold"),n.overThreshold=false);}};function fo(t,e){if(t.length===0)return 0;if(t.length===1)return t[0]??0;let n=[...t].sort((c,l)=>c-l),r=e*(n.length-1),o=Math.floor(r),a=Math.ceil(r);if(o===a)return n[o]??0;let s=n[o]??0,i=n[a]??0;return s+(i-s)*(r-o)}var go="daily-review",ho="weekly-review",yo="capture-meeting",ko="project-planning",Yh="inbox-triage",Zh="perspective-author";function Qh(){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 ey(){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 xh(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
|
|
25
|
+
how many tasks rescheduled or cleaned up.`}function ty(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 xh(t){return crea
|
|
|
42
42
|
---
|
|
43
43
|
### Meeting notes
|
|
44
44
|
|
|
45
|
-
${t}`}function
|
|
45
|
+
${t}`}function ny(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
|
|
68
|
+
${e}`}function ry(){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,7 +111,7 @@ 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
|
|
114
|
+
whether to retry, skip, or hand off.`}function oy(t,e){return `You are translating a natural-language request into a saved OmniFocus custom perspective.
|
|
115
115
|
|
|
116
116
|
User's description: "${t}"${e?`
|
|
117
117
|
|
|
@@ -196,42 +196,42 @@ After save, call \`perspective_list\` to confirm the new perspective is visible.
|
|
|
196
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
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
198
|
|
|
199
|
-
Read-only verification first; mutation only on explicit user approval.`}function
|
|
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 Vh=864e5;function Dc(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:Vh,calendarAccess:e.calendarAccess??{available:false,permission:"unknown"},webhooks:e.webhooks??{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:0,names:[]}}}async function Mn(t=new Ye){try{let{permission:e}=await t.getPermission();return {available:!0,permission:e}}catch(e){if(e instanceof ge)return C.debug({event:"capabilities.calendar_bridge_unavailable",message:e.message}),{available:false,permission:"unknown"};throw e}}var En="omnifocus://capabilities";function Ac(t,e){t.registerResource("omnifocus-capabilities",En,{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:En,mimeType:"application/json",text:JSON.stringify(await e(),null,2)}]}));}var Rc=/^[A-Za-z0-9._-]{3,64}$/;function qh(t){return typeof t=="string"&&Rc.test(t)}function en(t){let e=z.string().regex(Rc,`Invalid ${t}: expected 3-64 alphanumeric / _ / - characters`).transform(n=>n);return {kind:t,of(n){return e.parse(n)},is(n){return qh(n)},schema:e}}var h=en("TaskId"),v=en("ProjectId"),b=en("TagId"),$=en("FolderId"),Pe=en("AttachmentId");var Mc="omnifocus://agenda{?date}",Kh=6e4;function Cc(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 Fc(t){return t.dueDate!==null}function Ec(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 Xr=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}};}},Yh=new Xr;async function Zh(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??Kh,a=t.sources,i=e.date?Cc(Xh(e.date)):Cc(r),s=new Date(i.getTime()+1440*60*1e3),c=i.toISOString(),d=s.toISOString(),l=t.cache??Yh,m=`${c}|${d}|${a??""}`,f=l.get(m,r.getTime());if(f)return f;let[g,I]=await Promise.all([t.bridge.readEvents(c,d,a),t.forecastService.get({from:c,to:d})]),k=new Set,w=[];for(let N of [...I.dueToday,...I.deferredToday,...I.flagged,...I.overdue]){let W=String(N.id);k.has(W)||(k.add(W),w.push(N));}let R=g.map(N=>({kind:"calendar-event",...N})),T=w.filter(Fc),D=w.filter(N=>!Fc(N)),_=T.map(N=>Ec(N,N.dueDate)),L=[...R,..._].sort((N,W)=>N.startsAt!==W.startsAt?N.startsAt<W.startsAt?-1:1:Nn(N).localeCompare(Nn(W))),x=D.map(N=>Ec(N,"")).sort((N,W)=>Nn(N).localeCompare(Nn(W))),M={items:L,floating:x};return l.set(m,M,r.getTime()+o),M}function Nn(t){return t.kind==="calendar-event"?t.title:t.name}function Nc(t,e,n=new Ye){t.registerResource("omnifocus-agenda",new ResourceTemplate(Mc,{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 Zh({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 Uc="omnifocus://burndown/{projectId}";function Yr(t){return Math.round(t*100)/100}async function ey(t,e,n=new Date){let r;try{r=v.of(e);}catch{return {error:{code:"ProjectNotFound",message:`Project not found: ${e}`}}}let a=(await t.listProjects()).find(V=>String(V.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(V=>!V.dropped&&V.projectId!==null&&String(V.projectId)===c),l=s.filter(V=>V.projectId!==null&&String(V.projectId)===c),m=d.length+l.length,f=l.length,g=d.length,I=[...d,...l],k=I[0],w=I.length>0&&k!==void 0?I.reduce((V,re)=>re.createdAt<V?re.createdAt:V,k.createdAt):a.createdAt,R=new Date(w).getTime(),T=new Date(a.dueDate).getTime(),D=n.getTime(),_=Math.max(1,(T-R)/864e5),L=Math.max(0,Math.min(_,(D-R)/864e5)),x=Yr(L/_*100),M=m===0?100:Yr(f/m*100),N=Yr((M-x)/100*_),W;return m===0?W="No tasks in project \u2014 burndown is trivially complete.":N>0?W=`Ahead of pace by ${N.toFixed(1)} day(s). Ideal: ${x.toFixed(1)}% done; actual: ${M.toFixed(1)}%.`:N<0?W=`Behind pace by ${Math.abs(N).toFixed(1)} day(s). Ideal: ${x.toFixed(1)}% done; actual: ${M.toFixed(1)}%.`:W=`On pace. Ideal: ${x.toFixed(1)}%; actual: ${M.toFixed(1)}%.`,W+=" (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:w,totalTasks:m,completedTasks:f,remainingTasks:g,idealLinePercent:x,actualPercent:M,deltaDays:N,note:W}}function Lc(t,e){t.registerResource("omnifocus-burndown",new ResourceTemplate(Uc,{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 ey(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Jc="omnifocus://calendar{?from,to}",ny=6e4;function ry(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function oy(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate()+1,0,0,0,0).toISOString()}var Zr=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 ay(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??ny,a=t.sources,i=e.from??ry(r),s=e.to??oy(r),c=t.cache??sy,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 sy=new Zr;function Bc(t,e=new Ye){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 ay({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 Qr=[{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 eo="omnifocus://intents";function iy(t=new Date){return {intents:Qr,count:Qr.length,generatedAt:t.toISOString()}}function $c(t){t.registerResource("omnifocus-intents",eo,{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:eo,mimeType:"application/json",text:JSON.stringify(iy(),null,2)}]}));}var cy=new Set(["today","tomorrow","yesterday","this-week","next-week","end-of-week","end-of-month"]);function tn(t){return typeof t=="string"&&cy.has(t)}function dt(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 nn(t,e=new Date){let n=new Date(e);switch(n.setHours(0,0,0,0),t){case "today":return dt(n);case "tomorrow":{let r=new Date(n);return r.setDate(r.getDate()+1),dt(r)}case "yesterday":{let r=new Date(n);return r.setDate(r.getDate()-1),dt(r)}case "this-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?-6:1-o)),dt(r)}case "next-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?1:8-o)),dt(r)}case "end-of-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?0:7-o)),dt(r)}case "end-of-month":{let r=new Date(n);return r.setMonth(r.getMonth()+1,0),dt(r)}}}var Wc=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;function rn(t){return typeof t=="string"&&Wc.test(t)&&!Number.isNaN(Date.parse(t))}function ae(){return z.string().regex(Wc,"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)=>rn(t)?t:tn(t)?nn(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 Je(t,e){if(t===null||t.length===0)return;let n=`\`\`\`${e}`,o=new RegExp(`(^|\\n)${dy(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 St(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
|
|
202
|
-
`)}function
|
|
199
|
+
Read-only verification first; mutation only on explicit user approval.`}function Kc(t){t.registerPrompt(go,{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:Qh()}}]})),t.registerPrompt(ho,{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:ey()}}]})),t.registerPrompt(yo,{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$1.string().min(1).describe("Raw meeting notes to extract action items from."),projectId:z$1.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:ty(e,n)}}]})),t.registerPrompt(ko,{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$1.string().min(1).describe("Name of the new project."),brief:z$1.string().min(1).describe("One-paragraph description of the project goal. Used to derive subtasks."),folderId:z$1.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:ny(e,n,r)}}]})),t.registerPrompt(Yh,{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:ry()}}]})),t.registerPrompt(Zh,{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$1.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$1.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:oy(e,n)}}]}));}var Yn=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 s=o[0]+this.config.windowSeconds*1e3-n+1;throw new zn(`Rate limit exceeded for tool "${e}". Limit: ${this.config.limit} calls per ${this.config.windowSeconds}s.`,{details:{retryAfterMs:s}})}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,s=o.length>0?new Date(o[0]+this.config.windowSeconds*1e3).toISOString():new Date(n+this.config.windowSeconds*1e3).toISOString();return {remaining:a,resetAt:s}}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 cy(){let t=fileURLToPath(import.meta.url);return $r.resolve($r.dirname(t),"../../bin/calendar-bridge")}var dt=class{binaryPath;spawn;existsSync;constructor(e={}){this.binaryPath=e.binaryPath??cy(),this.spawn=e.spawn??spawn,this.existsSync=e.existsSync??Pt.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 Hn({details:{permission:a.permission}}):new be(`calendar-bridge returned error: ${a.error}`,{details:{error:a.error}});return a.events}async invoke(e,n){if(!this.existsSync(this.binaryPath))throw new be(`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 be(`failed to spawn calendar-bridge: ${o.message}`,{cause:o,details:{binaryPath:this.binaryPath}})}return new Promise((o,a)=>{let s="",i="";r.stdout?.on("data",c=>{s+=typeof c=="string"?c:c.toString("utf8");}),r.stderr?.on("data",c=>{i+=typeof c=="string"?c:c.toString("utf8");}),r.on("error",c=>{a(new be(`calendar-bridge spawn error: ${c.message}`,{cause:c}));}),r.on("close",c=>{if(c!==0){a(new be(`calendar-bridge exited with code ${c}: ${i.trim()}`,{details:{exitCode:c,stderr:i.trim()}}));return}let l=s.trim().split(`
|
|
200
|
+
`)[0]??"";if(!l){a(new be("calendar-bridge produced no output",{details:{stderr:i.trim()}}));return}try{o(JSON.parse(l));}catch(d){a(new be(`calendar-bridge produced unparsable JSON: ${l.slice(0,200)}`,{cause:d,details:{line:l}}));}});})}};var dy=864e5;function Yc(t,e={}){let{limit:n,windowSeconds:r}=t.OMNIFOCUS_TOOL_RATE_LIMIT,o=Math.round(n*60/r),a=e.ofEdition??"standard",s=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:s,forecastTag:s,repetitionRules:s,pluginInvocation:s,rawScriptTools:t.OMNIFOCUS_ALLOW_RAW_SCRIPT},rateLimits:{defaultPerToolPerMinute:o},idempotencyTtlMs:dy,calendarAccess:e.calendarAccess??{available:false,permission:"unknown"},webhooks:e.webhooks??{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:0,names:[]}}}async function Qn(t=new dt){try{let{permission:e}=await t.getPermission();return {available:!0,permission:e}}catch(e){if(e instanceof be)return R.debug({event:"capabilities.calendar_bridge_unavailable",message:e.message}),{available:false,permission:"unknown"};throw e}}var Zn="omnifocus://capabilities";function Zc(t,e){t.registerResource("omnifocus-capabilities",Zn,{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:Zn,mimeType:"application/json",text:JSON.stringify(await e(),null,2)}]}));}var Qc=/^[A-Za-z0-9._-]{3,64}$/;function py(t){return typeof t=="string"&&Qc.test(t)}function yn(t){let e=z$1.string().regex(Qc,`Invalid ${t}: expected 3-64 alphanumeric / _ / - characters`).transform(n=>n);return {kind:t,of(n){return e.parse(n)},is(n){return py(n)},schema:e}}var y=yn("TaskId"),v=yn("ProjectId"),b=yn("TagId"),$=yn("FolderId"),Le=yn("AttachmentId");var rd="omnifocus://agenda{?date}",my=6e4;function ed(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0)}function fy(t){let e=new Date(t);if(Number.isNaN(e.getTime()))throw new k(`agenda: could not parse date as ISO-8601: ${t}`,{details:{raw:t}});return e}function td(t){return t.dueDate!==null}function nd(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 vo=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}};}},gy=new vo;async function hy(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??my,a=t.sources,s=e.date?ed(fy(e.date)):ed(r),i=new Date(s.getTime()+1440*60*1e3),c=s.toISOString(),l=i.toISOString(),d=t.cache??gy,u=`${c}|${l}|${a??""}`,f=d.get(u,r.getTime());if(f)return f;let[g,I]=await Promise.all([t.bridge.readEvents(c,l,a),t.forecastService.get({from:c,to:l})]),h=new Set,w=[];for(let N of [...I.dueToday,...I.deferredToday,...I.flagged,...I.overdue]){let W=String(N.id);h.has(W)||(h.add(W),w.push(N));}let S=g.map(N=>({kind:"calendar-event",...N})),T=w.filter(td),x=w.filter(N=>!td(N)),O=T.map(N=>nd(N,N.dueDate)),L=[...S,...O].sort((N,W)=>N.startsAt!==W.startsAt?N.startsAt<W.startsAt?-1:1:er(N).localeCompare(er(W))),A=x.map(N=>nd(N,"")).sort((N,W)=>er(N).localeCompare(er(W))),M={items:L,floating:A};return d.set(u,M,r.getTime()+o),M}function er(t){return t.kind==="calendar-event"?t.title:t.name}function od(t,e,n=new dt){t.registerResource("omnifocus-agenda",new ResourceTemplate(rd,{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,s=await hy({bridge:n,forecastService:e,sources:process.env.OMNIFOCUS_CALENDAR_SOURCES},{date:a.date});return {contents:[{uri:r.href,mimeType:"application/json",text:JSON.stringify(s,null,2)}]}});}var ad="omnifocus://burndown/{projectId}";function Io(t){return Math.round(t*100)/100}async function ky(t,e,n=new Date){let r;try{r=v.of(e);}catch{return {error:{code:"ProjectNotFound",message:`Project not found: ${e}`}}}let a=(await t.listProjects()).find(V=>String(V.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[s,i]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true})]),c=String(a.id),l=s.filter(V=>!V.dropped&&V.projectId!==null&&String(V.projectId)===c),d=i.filter(V=>V.projectId!==null&&String(V.projectId)===c),u=l.length+d.length,f=d.length,g=l.length,I=[...l,...d],h=I[0],w=I.length>0&&h!==void 0?I.reduce((V,ce)=>ce.createdAt<V?ce.createdAt:V,h.createdAt):a.createdAt,S=new Date(w).getTime(),T=new Date(a.dueDate).getTime(),x=n.getTime(),O=Math.max(1,(T-S)/864e5),L=Math.max(0,Math.min(O,(x-S)/864e5)),A=Io(L/O*100),M=u===0?100:Io(f/u*100),N=Io((M-A)/100*O),W;return u===0?W="No tasks in project \u2014 burndown is trivially complete.":N>0?W=`Ahead of pace by ${N.toFixed(1)} day(s). Ideal: ${A.toFixed(1)}% done; actual: ${M.toFixed(1)}%.`:N<0?W=`Behind pace by ${Math.abs(N).toFixed(1)} day(s). Ideal: ${A.toFixed(1)}% done; actual: ${M.toFixed(1)}%.`:W=`On pace. Ideal: ${A.toFixed(1)}%; actual: ${M.toFixed(1)}%.`,W+=" (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:w,totalTasks:u,completedTasks:f,remainingTasks:g,idealLinePercent:A,actualPercent:M,deltaDays:N,note:W}}function sd(t,e){t.registerResource("omnifocus-burndown",new ResourceTemplate(ad,{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??"",s=await ky(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(s,null,2)}]}});}var id="omnifocus://calendar{?from,to}",Iy=6e4;function Ty(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function wy(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate()+1,0,0,0,0).toISOString()}var To=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 by(t,e={}){let r=(t.now??(()=>new Date))(),o=t.ttlMs??Iy,a=t.sources,s=e.from??Ty(r),i=e.to??wy(r),c=t.cache??Sy,l=`${s}|${i}|${a??""}`,d=c.get(l,r.getTime());if(d)return d;let f={events:await t.bridge.readEvents(s,i,a)};return c.set(l,f,r.getTime()+o),f}var Sy=new To;function cd(t,e=new dt){t.registerResource("omnifocus-calendar",new ResourceTemplate(id,{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 by({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 wo=[{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 bo="omnifocus://intents";function jy(t=new Date){return {intents:wo,count:wo.length,generatedAt:t.toISOString()}}function dd(t){t.registerResource("omnifocus-intents",bo,{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:bo,mimeType:"application/json",text:JSON.stringify(jy(),null,2)}]}));}var _y=new Set(["today","tomorrow","yesterday","this-week","next-week","end-of-week","end-of-month"]);function kn(t){return typeof t=="string"&&_y.has(t)}function bt(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"),s=t.getFullYear(),i=String(t.getMonth()+1).padStart(2,"0"),c=String(t.getDate()).padStart(2,"0");return `${s}-${i}-${c}T00:00:00${n}${o}:${a}`}function vn(t,e=new Date){let n=new Date(e);switch(n.setHours(0,0,0,0),t){case "today":return bt(n);case "tomorrow":{let r=new Date(n);return r.setDate(r.getDate()+1),bt(r)}case "yesterday":{let r=new Date(n);return r.setDate(r.getDate()-1),bt(r)}case "this-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?-6:1-o)),bt(r)}case "next-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?1:8-o)),bt(r)}case "end-of-week":{let r=new Date(n),o=r.getDay();return r.setDate(r.getDate()+(o===0?0:7-o)),bt(r)}case "end-of-month":{let r=new Date(n);return r.setMonth(r.getMonth()+1,0),bt(r)}}}var ld=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;function In(t){return typeof t=="string"&&ld.test(t)&&!Number.isNaN(Date.parse(t))}function H(){return z$1.string().regex(ld,"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 Ye(){return z$1.string().transform((t,e)=>In(t)?t:kn(t)?vn(t):(e.addIssue({code:z$1.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$1.NEVER))}function Ze(t,e){if(t===null||t.length===0)return;let n=`\`\`\`${e}`,o=new RegExp(`(^|\\n)${Oy(n)}[ \\t]*\\n`).exec(t);if(o===null)return;let a=o.index+o[0].length,s=/\n```[ \t]*(?=\n|$)/;s.lastIndex=a;let i=t.slice(a),c=s.exec(i);return c===null?void 0:{body:i.slice(0,c.index),start:o.index+(o[1]===""?0:1),end:a+c.index+c[0].length}}function Ut(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 s=o.slice(0,a).trim();if(s==="")continue;let i=o.slice(a+1).trim();(i.startsWith('"')&&i.endsWith('"')&&i.length>=2||i.startsWith("'")&&i.endsWith("'")&&i.length>=2)&&(i=i.slice(1,-1)),e[s]=i;}return e}function Lt(t){let e=[];for(let[n,r]of Object.entries(t))r!==void 0&&e.push(`${n}: ${r}`);return e.join(`
|
|
202
|
+
`)}function Jt(t,e,n){let r=`\`\`\`${e}
|
|
203
203
|
${n}
|
|
204
|
-
\`\`\``,o=
|
|
204
|
+
\`\`\``,o=Ze(t,e);return o!==void 0&&t!==null?t.slice(0,o.start)+r+t.slice(o.end):t===null||t.length===0?r:`${r}
|
|
205
205
|
|
|
206
|
-
${t}`}function
|
|
206
|
+
${t}`}function nr(t,e){if(t===null)return null;let n=Ze(t,e);if(n===void 0)return t.length===0?null:t;let r=t.slice(0,n.start).replace(/[ \t]*\n+$/,""),o=t.slice(n.end).replace(/^\n+[ \t]*/,""),a;return r===""||o===""?a=r+o:a=`${r}
|
|
207
207
|
|
|
208
|
-
${o}`,a===""?null:a}function dy(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var no="decision-journal",ro=["stall-is-intentional","deferred-by-choice","blocked-on-external","awaiting-decision","acknowledged-zombie"],ly=z.object({kind:z.enum(ro).describe("The kind of judgment recorded."),reason:z.string().min(1).describe("Human-readable reason for the decision."),recordedAt:ae().describe("When the decision was recorded (ISO-8601 with offset)."),until:ae().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 Ce(t){let e=Je(t,no);if(e===void 0)return;let n=St(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=ly.safeParse(r);return o.success?o.data:void 0}function Hc(t,e=new Date){return t.until===void 0?true:new Date(t.until).getTime()>e.getTime()}function oo(t,e){let n=jt({kind:e.kind,reason:e.reason,recordedAt:e.recordedAt,until:e.until});return _t(t,no,n)}function ao(t){return Ln(t,no)}function on(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 zc="omnifocus://project-health{?staleDays}";function uy(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 my(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 Vc(t,e){let n=e.getTime()-new Date(t).getTime();return Math.max(0,Math.floor(n/(1440*60*1e3)))}function fy(t,e,n){let r=e.filter(w=>!w.completed&&!w.dropped),o=e.map(w=>w.modifiedAt),a=my(o),i=a??t.modifiedAt,s=Vc(i,n),c=r.filter(w=>w.available).length,d=r.filter(w=>w.blocked).length,l=r.length===0,m=n.toISOString(),f=r.length>0&&r.every(w=>w.deferDate!==null&&w.deferDate>m),g=t.lastReviewDate,I=g?Vc(g,n):null,k;return t.nextReviewDate===null?k=true:k=t.nextReviewDate<=m,{lastTaskActivityAt:a,daysSinceActivity:s,availableTaskCount:c,blockedTaskCount:d,hasNoActions:l,deferredFutureTasks:f,lastReviewedAt:g,daysSinceReview:I,overdueForReview:k}}function gy(t,e,n){return n?!!(e||t.availableTaskCount===0||t.overdueForReview||t.deferredFutureTasks):false}function hy(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 yy(t,e=14,n=new Date){let[r,o]=await Promise.all([t.listProjects(),t.listTasks({})]),a=uy(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=fy(d,m,n),g=on(d,f.lastTaskActivityAt,n,e);if(!gy(f,g,l))continue;let I=Ce(d.note),k=I!==void 0&&Hc(I,n),w={projectId:String(d.id),name:d.name,status:d.status,signals:f,...I!==void 0&&{decision:I},_severity:hy(f)};k?s.push(w):i.push(w);}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 ky(t){if(!t)return 14;let e=Number.parseInt(t,10);return !Number.isFinite(e)||e<1?14:e}function qc(t,e){t.registerResource("omnifocus-project-health",new ResourceTemplate(zc,{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=ky(r.staleDays),i=await yy(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Gc="omnifocus://recent-activity{?hours}",so=24,Iy=168;function Ty(t){if(t==null||t==="")return so;let e=Number(t);return !Number.isFinite(e)||e<0?so:Math.min(Math.max(1,Math.round(e)),Iy)}async function wy(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 Kc(t,e){t.registerResource("omnifocus-recent-activity",new ResourceTemplate(Gc,{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=Ty(r.hours),a=o===so?"omnifocus://recent-activity":`omnifocus://recent-activity?hours=${o}`,i=await wy(e,o);return {contents:[{uri:a,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var Yc="omnifocus://retrospective{?from,to}",Sy=7;function jy(t,e,n=()=>new Date){let r=n(),o=r.toISOString(),a=new Date(r.getTime()-Sy*864e5).toISOString(),i=Xc(t)?t:a,s=Xc(e)?e:o;return i>s?{from:s,to:i}:{from:i,to:s}}function Xc(t){if(t==null||t==="")return false;let e=new Date(t);return Number.isFinite(e.getTime())}async function _y(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 Zc(t,e){t.registerResource("omnifocus-retrospective",new ResourceTemplate(Yc,{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=jy(o.from,o.to),i=await _y(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 io="omnifocus://stats";function xy(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function Oy(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 Py(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 Ay(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=xy(e),d=Oy(e),l=e.toISOString(),m=0,f=0,g=0,I=0,k=0,w=0,R=0,T=0,D=0;for(let O of n){if(m+=1,O.completed){O.completedAt&&(O.completedAt>=d&&(w+=1),O.completedAt>=c&&(k+=1));continue}if(O.dropped){O.droppedAt&&O.droppedAt>=c&&(D+=1);continue}O.available&&(f+=1),O.blocked&&(g+=1),O.flagged&&(T+=1),O.deferDate&&O.deferDate>l&&(I+=1),O.dueDate&&O.dueDate<l&&(R+=1);}let _=0,L=0,x=0,M=0,N=Py(n),W=0;for(let O of o){O.status==="active"&&!O.completed&&!O.dropped&&(_+=1),O.status==="on-hold"&&(L+=1),(O.completed||O.status==="done")&&(x+=1),(O.dropped||O.status==="dropped")&&(M+=1);let Ke=N.get(String(O.id))??[],pe=Dy(Ke.map(it=>it.modifiedAt));on(O,pe,e)&&(W+=1);}let V=null;if(r.length>0){let O=r.map(pe=>pe.createdAt).reduce((pe,it)=>it<pe?it:pe),Ke=e.getTime()-new Date(O).getTime();V=Math.max(0,Math.floor(Ke/(1440*60*1e3)));}let re=new Set;for(let O of n)for(let Ke of O.tagIds)re.add(String(Ke));let st=re.size,oe=null;if(s.lastSyncAt){let O=e.getTime()-new Date(s.lastSyncAt).getTime();oe=Math.max(0,Math.floor(O/1e3));}return {tasks:{total:m,available:f,blocked:g,deferred:I,completed_today:k,completed_this_week:w,overdue_count:R,flagged_count:T,dropped_today:D},projects:{total:o.length,active:_,on_hold:L,completed:x,dropped:M,stalled_count:W,due_for_review_count:a.length},inbox:{count:r.length,oldest_age_days:V},tags:{total:i.length,with_tasks_count:st},database:{sync_age_seconds:oe,last_sync_at:s.lastSyncAt}}}function Qc(t,e){t.registerResource("omnifocus-stats",io,{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 Ay(e);return {contents:[{uri:io,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function Ry(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 an(t){return t.toLowerCase().trim().replace(/\s+/g," ").replace(/^@/,"")}function ed(t){return an(t).split(/[\s\-_]+/).filter(Boolean).sort()}function Cy(t,e){let n=ed(t),r=ed(e);return n.length!==r.length?false:n.every((o,a)=>o===r[a])}function co(t,e){if(t===e)return "exact-duplicate";let n=an(t),r=an(e);return n===r?"case-difference":n===`${r}s`||r===`${n}s`||n===`${r}es`||r===`${n}es`?"plural-singular":Ry(n,r)<=2||Cy(t,e)?"near-duplicate":null}var Fy=2,Ey=new Set(["a","an","and","as","at","by","for","from","in","is","it","of","on","or","the","to","with"]),My=.7,Ny=.2,Uy=.05,Ly=.05;function Bn(t){return t?t.toLowerCase().split(/[^a-z0-9']+/i).filter(e=>e.length>=Fy&&!Ey.has(e)):[]}function td(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 nd(t,e){let n=new Set(Bn(t.name)),r=new Set(Bn(e.name)),o=td(n,r),a=0;if(t.note&&e.note){let m=new Set(Bn(t.note)),f=new Set(Bn(e.note));m.size>0&&f.size>0&&(a=td(m,f));}let i=[...n][0],s=[...r][0],c=i!==void 0&&i===s?1:0,d=an(t.name)===an(e.name)?1:0,l=o*My+a*Ny+c*Uy+d*Ly;return Math.max(0,Math.min(1,l))}var lo="omnifocus://taxonomy-audit";async function Jy(t){let[e,n]=await Promise.all([t.listTags(),t.listProjects()]),r=By(e),o=$y(n);return {tagCollisions:r,projectCollisions:o}}function By(t){let e=t.map(n=>({tagId:String(n.id),name:n.name,taskCount:n.taskCount}));return ad(e,(n,r)=>co(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function $y(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 ad(e,(n,r)=>co(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function ad(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 k=o.get(f)??"near-duplicate";o.set(f,od(k,m));return}r[g]=f;let I=o.get(f)??o.get(g)??m;o.set(f,od(I,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 rd={"exact-duplicate":0,"case-difference":1,"plural-singular":2,"near-duplicate":3};function od(t,e){return rd[t]<=rd[e]?t:e}function sd(t,e){t.registerResource("omnifocus-taxonomy-audit",lo,{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 Jy(e);return {contents:[{uri:lo,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function po(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 $n(t){let e=new Date(t);return e.setDate(e.getDate()+7),e}function id(t,e=new Date){let n=po(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 dd="omnifocus://velocity{?weeks}",cd=8,Hy=52;function Vy(t){if(t==null||t==="")return cd;let e=Number(t);return !Number.isFinite(e)||e<0?cd:Math.min(Hy,Math.max(1,Math.round(e)))}async function zy(t,e,n=new Date){let r=id(e,n),o=r[0],a=r[r.length-1],i=$n(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 x of m)f.set(String(x.id),x.name);let g=r.map(x=>{let M=$n(x),N=x.toISOString(),W=M.toISOString(),V=d.filter(oe=>oe.createdAt>=N&&oe.createdAt<W).length,re=l.filter(oe=>oe.completedAt!==null&&oe.completedAt>=N&&oe.completedAt<W).length,st=d.filter(oe=>oe.dropped&&oe.droppedAt!==null&&oe.droppedAt>=N&&oe.droppedAt<W).length;return {weekStart:N,created:V,completed:re,dropped:st,netDelta:V-re-st}}),I=[];for(let x of [4,8]){if(e<x)continue;let M=g.slice(-x),N=M.reduce((V,re)=>V+re.completed,0),W=M.reduce((V,re)=>V+re.created,0);I.push({window:x,completedPerWeek:Math.round(N/x*100)/100,createdPerWeek:Math.round(W/x*100)/100});}let k=po(a).toISOString(),w=$n(a).toISOString(),R=r[Math.max(0,r.length-4)].toISOString(),T=new Map,D=new Map;for(let x of l){if(x.completedAt===null||x.projectId===null)continue;let M=String(x.projectId);x.completedAt>=k&&x.completedAt<w&&T.set(M,(T.get(M)??0)+1),x.completedAt>=R&&x.completedAt<c&&D.set(M,(D.get(M)??0)+1);}let _=new Set([...T.keys(),...D.keys()]),L=Array.from(_).map(x=>({projectId:x,name:f.get(x)??x,closedThisWeek:T.get(x)??0,closedTrailing4:D.get(x)??0})).sort((x,M)=>M.closedThisWeek!==x.closedThisWeek?M.closedThisWeek-x.closedThisWeek:M.closedTrailing4-x.closedTrailing4).slice(0,5);return {window:{from:s,to:c},weeklyTotals:g,rollingAverages:I,topClosingProjects:L}}function ld(t,e){t.registerResource("omnifocus-velocity",new ResourceTemplate(dd,{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=Vy(r.weeks),i=await zy(e,a);return {contents:[{uri:`omnifocus://velocity?weeks=${a}`,mimeType:"application/json",text:JSON.stringify(i,null,2)}]}});}var mo="waiting-on",fo=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 xt(t){let e=Je(t,mo);if(e===void 0)return;let n=St(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=fo.safeParse(r);return o.success?o.data:void 0}function pd(t,e){let n=jt({whom:e.whom,what:e.what,since:e.since,followUpAfter:e.followUpAfter});return _t(t,mo,n)}function ud(t){return Ln(t,mo)}function md(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 go="omnifocus://waiting-on";async function qy(t,e=new Date){let n=await t.listTasks({completed:false}),r=[];for(let o of n){let a=xt(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:md(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 fd(t,e){t.registerResource("omnifocus-waiting-on",go,{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 qy(e);return {contents:[{uri:go,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}var cn="omnifocus://snapshot",dn="omnifocus://inbox",ln="omnifocus://forecast/today",pn="omnifocus://overdue",un="omnifocus://flagged",mn="omnifocus://review-due",yo="omnifocus://project/{id}",ko="omnifocus://tag/{id}",vo="omnifocus://perspective/{id}",gd="omnifocus://tasks/inbox",Gy="omnifocus://tasks/project/{projectId}",Ky="omnifocus://tasks/tag/{tagId}";function ho(){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 Te(t,e){return {contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(e,null,2)}]}}function hd(t,e){let{adapter:n,projectService:r,reviewService:o,forecastService:a,perspectiveService:i}=e;t.registerResource("omnifocus-snapshot",cn,{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}=ho(),[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()]),I=l.filter(k=>k.projectId===null&&k.parentId===null).length;return Te(cn,{inboxCount:I,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",dn,{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 Te(dn,d)}),t.registerResource("omnifocus-forecast-today",ln,{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}=ho(),l=await a.get({from:c,to:d});return Te(ln,{overdue:l.overdue,dueToday:l.dueToday,deferredToday:l.deferredToday,flagged:l.flagged})}),t.registerResource("omnifocus-overdue",pn,{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}=ho(),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 Te(pn,l)}),t.registerResource("omnifocus-flagged",un,{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 Te(un,c)}),t.registerResource("omnifocus-review-due",mn,{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 Te(mn,d)}),t.registerResource("omnifocus-project",new ResourceTemplate(yo,{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=v.of(c.id),l=await r.get({id:d,includeTaskTree:true});return Te(`omnifocus://project/${d}`,{project:l.project,tasks:l.tasks??[]})}),t.registerResource("omnifocus-tag",new ResourceTemplate(ko,{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=b.of(c.id),[l,m]=await Promise.all([n.getTag(d),n.listTasks({tagId:d,completed:false})]);return Te(`omnifocus://tag/${d}`,{tag:l,tasks:m})}),t.registerResource("omnifocus-perspective",new ResourceTemplate(vo,{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 Te(`omnifocus://perspective/${d}`,{perspectiveId:d,tasks:l.tasks})}),t.registerResource("omnifocus-tasks-inbox",gd,{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 Te(gd,c)}),t.registerResource("omnifocus-tasks-by-project",new ResourceTemplate(Gy,{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=v.of(c.projectId),l=await n.listTasks({projectId:d,completed:false});return Te(`omnifocus://tasks/project/${d}`,l)}),t.registerResource("omnifocus-tasks-by-tag",new ResourceTemplate(Ky,{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=b.of(c.tagId),l=await n.listTasks({tagId:d,completed:false});return Te(`omnifocus://tasks/tag/${d}`,l)}),Kc(t,n),fd(t,n),Zc(t,n),sd(t,n),ld(t,n),Lc(t,n),Bc(t),Nc(t,a),$c(t),Qc(t,n),qc(t,n);}var Yy=300*1e3,Zy=200,Io=class{_store=new Map;_ttlMs;constructor(e=Yy){this._ttlMs=e;}register(e,n){this._store.size>=Zy&&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);}},we=new Io;function Ot(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 To(t,e){return {error:t.toJSON(),meta:e}}function Pt(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 wo="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()",ek=z.object({});async function tk(t,e){let n=await e.adapter.appLaunch(),r=e.makeMeta();return p({launched:n.launched,alreadyRunning:n.alreadyRunning},r)}function yd(t,e){return t.registerTool("app_launch",{description:wo,inputSchema:ek.shape},async n=>{let r=await tk(n,e);return u(r)})}var Wn=z.object({taskId:h.schema.optional().describe("Persistent ID of the task that owns the attachment. Provide exactly one of taskId or projectId."),projectId:v.schema.optional().describe("Persistent ID of the project that owns the attachment. Provide exactly one of taskId or projectId.")});function Hn(t){if(t.taskId)return {taskId:h.of(t.taskId)};if(t.projectId)return {projectId:v.of(t.projectId)};throw new y("Provide exactly one of taskId or projectId.",{})}var So='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" })',nk=Wn;async function rk(t,e){let n=Hn(t),r=await e.attachmentService.list(n);return p({attachments:r},e.makeMeta())}var jo=`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" })`,ok=Wn.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 ak(t,e){let n=Hn(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 _o=`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" })`,sk=Wn.extend({attachmentId:Pe.schema.describe("Persistent ID of the attachment to remove. Get from attachment_list.")});async function ik(t,e){let n=Hn(t),r=Pe.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 xo=`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" })`,ck=Wn.extend({attachmentId:Pe.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 dk(t,e){let n=Hn(t),r=await e.attachmentService.saveTo({...n,attachmentId:Pe.of(t.attachmentId),destPath:t.destPath});return p(r,e.makeMeta())}function kd(t,e){t.registerTool("attachment_list",{description:So,inputSchema:nk.shape},async n=>{let r=await rk(n,e);return u(r)}),t.registerTool("attachment_add",{description:jo,inputSchema:ok.shape},async n=>{let r=await ak(n,e);return u(r)}),t.registerTool("attachment_remove",{description:_o,inputSchema:sk.shape},async n=>{let r=await ik(n,e);return u(r)}),t.registerTool("attachment_save_to_path",{description:xo,inputSchema:ck.shape},async n=>{let r=await dk(n,e);return u(r)});}function S(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 vd(t,e){t.invalidate(`tag:${e.tagId}`),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function Id(t,e){t.invalidate(`folder:${e.folderId}`),t.invalidate("perspective:*"),t.invalidate("search:*");}function Td(t){t.clear();}function Vn(t){t.clear();}var Oo="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 })",lk=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 pk(t,e){let n=await e.adapter.redoLastMutation();return n.redid&&e.cache!==void 0&&Vn(e.cache),p(n,e.makeMeta({syncPending:n.redid}))}function bd(t,e){return t.registerTool("database_redo",{description:Oo,inputSchema:lk.shape},async n=>{let r=await pk(n,e);return u(r)})}var Po="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 })",uk=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 mk(t,e){let n=await e.adapter.undoLastMutation();return n.undid&&e.cache!==void 0&&Vn(e.cache),p(n,e.makeMeta({syncPending:n.undid}))}function jd(t,e){return t.registerTool("database_undo",{description:Po,inputSchema:uk.shape},async n=>{let r=await mk(n,e);return u(r)})}var Ao='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" }',fk=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 gk(t,e){if(t.targetKind==="task"){let a=h.of(t.targetId),i=await e.adapter.getTask(a),s=ao(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&&S(e.cache,{taskId:a,projectId:i.projectId}),p({targetKind:"task",targetId:a,cleared:true},e.makeMeta({syncPending:true})))}let n=v.of(t.targetId),r=await e.adapter.getProject(n),o=ao(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&&B(e.cache,{projectId:n}),p({targetKind:"project",targetId:n,cleared:true},e.makeMeta({syncPending:true})))}function _d(t,e){return t.registerTool("decision_clear",{description:Ao,inputSchema:fk.shape},async n=>{let r=await gk(n,e);return u(r)})}var Ro=`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" } }`,hk=z.object({kind:z.enum(ro).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.")}),yk=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:hk.describe("The decision payload. `recordedAt` is set automatically on write.")});async function kk(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=oo(c.note,r);return await e.adapter.updateTask(s,{note:d}),e.cache!==void 0&&S(e.cache,{taskId:s,projectId:c.projectId}),p({targetKind:"task",targetId:s,decision:r},e.makeMeta({syncPending:true}))}let o=v.of(t.targetId),a=await e.adapter.getProject(o),i=oo(a.note,r);return await e.adapter.updateProject(o,{note:i}),e.cache!==void 0&&B(e.cache,{projectId:o}),p({targetKind:"project",targetId:o,decision:r},e.makeMeta({syncPending:true}))}function xd(t,e){return t.registerTool("decision_record",{description:Ro,inputSchema:yk.shape},async n=>{let r=await kk(n,e);return u(r)})}var Fo=`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" })`,vk=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 Ik(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:v.of(t.id)}:{kind:"folder",id:$.of(t.id)}}async function Tk(t,e){let n=Ik(t),r=await e.exportService.exportOpml(n),o=e.makeMeta();return p({opml:r.opml,projectCount:r.projectCount,taskCount:r.taskCount},o)}function Od(t,e){return t.registerTool("export_opml",{description:Fo,inputSchema:vk.shape},async n=>{let r=await Tk(n,e);return u(r)})}function q(t){return `'${t}'`}function H(t){return t.length>140?`${t.slice(0,137)}\u2026`:t}function Pd(t){return H(`Created task ${q(t)}.`)}function Dd(t){return H(`Completed task ${q(t)}.`)}function Ad(t){return H(`Uncompleted task ${q(t)}.`)}function Rd(t){return H(`Deleted task ${q(t)}.`)}function Cd(t){return H(`Dropped task ${q(t)}.`)}function Fd(t){return H(`Restored task ${q(t)} from dropped.`)}function Ed(t,e){return H(`Moved task ${q(t)} to ${e}.`)}function Md(t){return H(`Duplicated task ${q(t)}.`)}function Nd(t){return H(`Reordered task ${q(t)}.`)}function Ud(t){return H(`Converted task ${q(t)} to a project.`)}function Ld(t){return H(`Set repetition rule on task ${q(t)}.`)}function Jd(t){return H(`Cleared repetition rule on task ${q(t)}.`)}function Bd(t,e){return H(`Set ${e} ${e===1?"alarm":"alarms"} on task ${q(t)}.`)}function $d(t){return H(`Cleared alarms on task ${q(t)}.`)}function Dt(t){return `Created ${t} task${t===1?"":"s"}.`}function Wd(t){return `Updated ${t} task${t===1?"":"s"}.`}function Hd(t){return `Completed ${t} task${t===1?"":"s"}.`}function Vd(t){return `Uncompleted ${t} task${t===1?"":"s"}.`}function zd(t){return `Deleted ${t} task${t===1?"":"s"}.`}function qd(t){return `Dropped ${t} task${t===1?"":"s"}.`}function Gd(t){return `Restored ${t} dropped task${t===1?"":"s"}.`}function Kd(t,e){return H(`Moved ${t} task${t===1?"":"s"} to ${e}.`)}function Xd(t){return H(`Created project ${q(t)}.`)}function Yd(t){return H(`Updated project ${q(t)}.`)}function Zd(t){return H(`Deleted project ${q(t)}.`)}function Qd(t){return `Completed ${t} project${t===1?"":"s"}.`}function el(t){return `Dropped ${t} project${t===1?"":"s"}.`}function tl(t){return H(`Created tag ${q(t)}.`)}function Ze(t){return H(`Updated tag ${q(t)}.`)}function nl(t,e){return H(`Moved tag ${q(t)} to ${e}.`)}function rl(t){return H(`Created folder ${q(t)}.`)}function ol(t){return H(`Updated folder ${q(t)}.`)}function al(t,e){return H(`Moved folder ${q(t)} to ${e}.`)}function zn(t,e){return H(`Set note on ${t} ${q(e)}.`)}function sl(t,e){return H(`Appended to note on ${t} ${q(e)}.`)}function il(){return "Completed project."}function cl(){return "Dropped project."}function dl(t){return H(`Moved project to ${t}.`)}function ll(){return "Deleted tag."}function pl(){return "Deleted folder."}function qn(){return "Marked project as reviewed."}function ul(t){return t===null?"Cleared project review interval.":`Set project review interval to ${t} day${t===1?"":"s"}.`}function ml(t){return t===null?"Cleared project next review date.":H(`Set project next review date to ${t}.`)}var Mo='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>" })',wk=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 bk(t,e){let n=await e.exportService.importOpml(t.opml,{...t.destinationProjectId!==void 0?{destinationProjectId:v.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:Dt(n.imported)});return p({imported:n.imported,tasks:o},a)}function fl(t,e){return t.registerTool("import_opml",{description:Mo,inputSchema:wk.shape},async n=>{let r=await bk(n,e);return u(r)})}var No=`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" })`,Uo=`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)" })`,Sk=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'.")}),jk=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 _k(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:v.of(t.id)}:{kind:"folder",id:$.of(t.id)}}function gl(t,e){t.registerTool("export_taskpaper",{description:No,inputSchema:Sk.shape},async n=>{let r=_k(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:Uo,inputSchema:jk.shape},async n=>u(await xk(n,e)));}async function xk(t,e){let n=await e.exportService.importTaskPaper(t.text,t.targetProjectId===void 0?void 0:v.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 Lo=`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" })`,Jo=z.object({name:z.string().min(1).describe("Folder name. Must be non-empty."),parentId:$.schema.optional().describe("Parent folder ID. Omit for a root-level folder. Get from folder_list.")});async function Ok(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:rl(t.name)}))}function yl(t,e){return t.registerTool("folder_create",{description:Lo,inputSchema:Jo.shape},async n=>{let r=await Ok(n,e);return u(r)})}function be(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 Rt(t,e){try{return (await t.getProject(e)).name}catch{return e}}async function Ct(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 Qe(t,e){try{return (await t.getFolder(e)).name}catch{return e}}var Bo="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 Pk(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await Qe(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 kl(t,e){return t.registerTool("folder_create_describe",{description:Bo,inputSchema:Jo.shape},async n=>{let r=await Pk(n,e);return u(r)})}var $o='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 })',Wo=z.object({id:$.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:pl()}))}function Il(t,e){return t.registerTool("folder_delete",{description:$o,inputSchema:Wo.shape},async n=>{let r=await Dk(n,e);return u(r)})}var Ho="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 Ak(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 Tl(t,e){return t.registerTool("folder_delete_describe",{description:Ho,inputSchema:Wo.shape},async n=>{let r=await Ak(n,e);return u(r)})}var Be={flagged:{value:false},completed:{value:false},completedAt:{value:null},dropped:{value:false},droppedAt:{value:null},available:{value:true},blocked:{value:false},sequential:{value:false},completedByChildren:{value:false},note:{value:null,equivalentTo:[""]},noteHtml:{value:null,equivalentTo:[""]},parentId:{value:null},tagIds:{value:[]},deferDate:{value:null},dueDate:{value:null},estimatedMinutes:{value:null},repetition:{value:null}},Gn={flagged:{value:false},completed:{value:false},completedAt:{value:null},dropped:{value:false},droppedAt:{value:null},note:{value:null,equivalentTo:[""]},noteHtml:{value:null,equivalentTo:[""]},folderId:{value:null},tagIds:{value:[]},status:{value:"active"},completionCriterion:{value:"parallel"},deferDate:{value:null},dueDate:{value:null},estimatedMinutes:{value:null},reviewIntervalDays:{value:null},nextReviewDate:{value:null},lastReviewDate:{value:null}},Kn={parentId:{value:null},status:{value:"active"},location:{value:null},allowsNextAction:{value:true}},Xn={parentId:{value:null}};function $e(t,e){let n={};for(let r of Object.keys(t)){let o=e[r],a=t[r];if(o===void 0){n[r]=a;continue}Rk(a,o)||(n[r]=a);}return n}function ye(t,e){return t.map(n=>$e(n,e))}function Rk(t,e){if(t===e.value||Array.isArray(e.value)&&Array.isArray(t)&&t.length===0)return true;if(e.equivalentTo!==void 0){for(let n of e.equivalentTo)if(t===n)return true}return false}var Vo='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" })',Ck=z.object({id:$.schema.describe("Persistent folder ID. Get from folder_list. IDs are stable across renames."),verbose:z.boolean().optional().describe("When true, return the full unelided folder shape. Default: false \u2014 `parentId` is omitted when null. See docs/token-cost.md for the defaults table.")});async function Fk(t,e){let n=await e.folderService.get(t.id),r=t.verbose===true?n.folder:$e(n.folder,Xn);return p({folder:r},e.makeMeta({cacheHit:n.cacheHit}))}function bl(t,e){return t.registerTool("folder_get",{description:Vo,inputSchema:Ck.shape},async n=>{let r=await Fk(n,e);return u(r)})}var zo='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" })',Ek=z.object({parentId:$.schema.optional().describe("Return only direct children of this folder. Get the ID from a previous folder_list call. Omit for root folders."),verbose:z.boolean().optional().describe("When true, return the full unelided folder shape. Default: false \u2014 `parentId` is omitted when null (top-level folder). See docs/token-cost.md for the defaults table.")});async function Mk(t,e){let n={...t.parentId!==void 0?{parentId:t.parentId}:{}},r=await e.folderService.list(n),o=t.verbose===true?r.folders:ye(r.folders,Xn);return p({folders:o},e.makeMeta({cacheHit:r.cacheHit}))}function jl(t,e){return t.registerTool("folder_list",{description:zo,inputSchema:Ek.shape},async n=>{let r=await Mk(n,e);return u(r)})}var qo=`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 })`,Go=z.object({id:$.schema.describe("Persistent ID of the folder to move. Get from folder_list."),parentId:$.schema.nullable().describe("New parent folder ID, or null to promote the folder to root level.")});async function Uk(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:al(n.name,t.parentId!=null?"folder":"library root")}))}function _l(t,e){return t.registerTool("folder_move",{description:qo,inputSchema:Go.shape},async n=>{let r=await Uk(n,e);return u(r)})}var Ko="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 Lk(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 Qe(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 xl(t,e){return t.registerTool("folder_move_describe",{description:Ko,inputSchema:Go.shape},async n=>{let r=await Lk(n,e);return u(r)})}var Xo='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" })',Yo=z.object({id:$.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 Jk(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:ol(o.name)}))}function Pl(t,e){return t.registerTool("folder_update",{description:Xo,inputSchema:Yo.shape},async n=>{let r=await Jk(n,e);return u(r)})}var Zo="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 Bk(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 Dl(t,e){return t.registerTool("folder_update_describe",{description:Zo,inputSchema:Yo.shape},async n=>{let r=await Bk(n,e);return u(r)})}var Qo=`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 })`,$k=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 Wk(t){let e;if(tn(t))e=nn(t);else if(rn(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 Hk(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=Wk(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 Vk(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 zk(t,e){let{from:n,to:r,days:o}=Hk(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=Vk(a.dueToday)),p(s,i)}function Al(t,e){return t.registerTool("forecast_get",{description:Qo,inputSchema:$k.shape},async n=>{let r=await zk(n,e);return u(r)})}var ea="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()",Gk=z.object({});async function Kk(t,e){let n=await e.forecastService.getForecastTag();return p(n,e.makeMeta())}function Rl(t,e){return t.registerTool("forecast_get_tag",{description:ea,inputSchema:Gk.shape},async n=>{let r=await Kk(n,e);return u(r)})}var ta=`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" } })`,Xk=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(b.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 Yk(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 Zk(t,e,n){let r=n?.tagIds,a=[...r&&r.length>0?t.filter(d=>d.tagIds.some(l=>r.includes(l))):t].sort(Yk),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 Qk(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 ev(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 tv(t,e){let n=t.filter?.scope??"today",{from:r,to:o}=Qk(n),a=await e.forecastService.get({from:r,to:o,includeOverdue:true,includeDeferred:n==="next7",includeFlagged:true}),i=ev(a),s=Zk(i,t.budgetMinutes,t.filter),c=e.makeMeta({cacheHit:a.cacheHit});return p(s,c)}function Cl(t,e){return t.registerTool("forecast_pack",{description:ta,inputSchema:Xk.shape},async n=>{let r=await tv(n,e);return u(r)})}var ra=`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 })`,nv=z.object({tagId:z.union([b.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 rv(t,e){let n=await e.forecastService.setForecastTag(t.tagId);return e.cache.invalidate("forecast:*"),p(n,e.makeMeta())}function Fl(t,e){return t.registerTool("forecast_set_tag",{description:ra,inputSchema:nv.shape},async n=>{let r=await rv(n,e);return u(r)})}var oa=`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" })`,ov=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 av(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(v.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&&S(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(v.of(t.id),{note:o}),e.cache!==void 0&&B(e.cache,{projectId:v.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:r,note:o},e.makeMeta({syncPending:true,humanReadableSummary:sl(t.targetKind,r)}))}function El(t,e){return t.registerTool("note_append",{description:oa,inputSchema:ov.shape},async n=>{let r=await av(n,e);return u(r)})}var sa=`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" })`,sv=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 iv(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).note:(await e.adapter.getProject(v.of(t.id))).note;return p({note:n??null},e.makeMeta())}function Ml(t,e){return t.registerTool("note_get",{description:sa,inputSchema:sv.shape},async n=>{let r=await iv(n,e);return u(r)})}var ca=`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" })`,cv=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 dv(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).noteHtml:(await e.adapter.getProject(v.of(t.id))).noteHtml;return p({noteHtml:n??null},e.makeMeta())}function Nl(t,e){return t.registerTool("note_get_html",{description:ca,inputSchema:cv.shape},async n=>{let r=await dv(n,e);return u(r)})}var da=`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" })`,lv=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 pv(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(v.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{note:t.note}),e.cache!==void 0&&S(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(v.of(t.id),{note:t.note}),e.cache!==void 0&&B(e.cache,{projectId:v.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,note:t.note},e.makeMeta({syncPending:true,humanReadableSummary:zn(t.targetKind,n)}))}function Ul(t,e){return t.registerTool("note_set",{description:da,inputSchema:lv.shape},async n=>{let r=await pv(n,e);return u(r)})}var la=`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" })`,uv=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 mv(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(h.of(t.id))).name:(await e.adapter.getProject(v.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(h.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&S(e.cache,{taskId:h.of(t.id)})):(await e.adapter.updateProject(v.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&B(e.cache,{projectId:v.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,noteHtml:t.noteHtml},e.makeMeta({syncPending:true,humanReadableSummary:zn(t.targetKind,n)}))}function Ll(t,e){return t.registerTool("note_set_html",{description:la,inputSchema:uv.shape},async n=>{let r=await mv(n,e);return u(r)})}function gv(){let t=fileURLToPath(import.meta.url);return wr.resolve(wr.dirname(t),"../../../reports/mutation/mutation.json")}function hv(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??gv();if(!(t.existsSync??gt.existsSync)(e))return null;let r=t.readFile??(g=>gt.readFileSync(g,"utf8")),o=t.stat??gt.statSync,a;try{a=JSON.parse(r(e));}catch{return null}let{Killed:i,Survived:s,Timeout:c,NoCoverage:d}=hv(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 ua="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, responseStats }. 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). responseStats reports per-tool response-byte aggregates (#778) \u2014 { since, sampleRate, thresholdBytes, tools: { <toolName>: { count, total, max, p50, p95 } } } \u2014 or null when sampling is disabled (sampleRate 0). Read-only; no side effects. Example: internal_status()",kv=z.object({});async function vv(t,e){let n=Date.now()-e.startedAt,r=null;try{let d=await e.adapter.getLastSync();r={lastSyncAt:d.lastSyncAt,inFlight:d.inFlight};}catch{r=null;}let o=e.circuitRegistry.snapshot(),a=null;try{a=await(e.probeCalendarAccess??Mn)();}catch{a=null;}let i=null;try{i=(e.probeMutationScore??Bl)();}catch{i=null;}let s=null;if(e.probeResponseStats!==void 0)try{s=e.probeResponseStats();}catch{s=null;}return p({uptimeMs:n,ofRunning:true,lastSync:r,calendarAccess:a,mutation:i,cache:null,circuits:o,queueDepth:null,responseStats:s},e.makeMeta())}function $l(t,e){return t.registerTool("internal_status",{description:ua,inputSchema:kv.shape},async n=>{let r=await vv(n,e);return u(r)})}var Wl=["actionAvailability","actionStatus","actionHasAllOfTags","actionHasAnyOfTags","actionHasNoProject","actionHasDueDate","actionHasDeferDate","actionIsLeaf","actionIsProject","actionMatchingSearch","actionWithinFocus"];function Iv(t){let e=0;for(let n of Wl)t[n]!==void 0&&(e+=1);return e}var ma=z.array(z.string().min(1,"id must be non-empty")).min(1,"list must contain at least one id"),Tv=z.enum(["all","any","none"]),wv=z.object({actionAvailability:z.enum(["available","remaining","completed","dropped","firstAvailable"]).optional(),actionStatus:z.enum(["flagged","due"]).optional(),actionHasAllOfTags:ma.optional(),actionHasAnyOfTags:ma.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:ma.optional()}).strict(),bv=wv.superRefine((t,e)=>{let n=Iv(t);if(n>1){let r=Wl.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:[]});}}),We=z.lazy(()=>z.union([z.object({aggregateType:Tv,aggregateRules:z.array(We)}).strict(),z.object({disabledRule:We}).strict(),bv]));We.register(z.globalRegistry,{id:"PerspectiveRuleInput"});var fa='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" }] })',Sv=z.enum(["all","any","none"]),jv=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(),_v=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:Sv.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z.array(We).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:jv.optional().describe("Custom icon color in [0, 1] floats { r, g, b, a }. Omit for the OmniFocus-assigned default.")});async function xv(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 Hl(t,e){return t.registerTool("perspective_create",{description:fa,inputSchema:_v.shape},async n=>{let r=await xv(n,e);return u(r)})}var ga='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" }.',Ov=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 Pv(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 zl(t,e){return t.registerTool("perspective_delete",{description:ga,inputSchema:Ov.shape},async n=>{let r=await Pv(n,e);return u(r)})}var ha=`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" })`,Dv=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 Av(t,e){let n=await e.perspectiveService.evaluate(t.perspectiveId),r=e.makeMeta({cacheHit:n.cacheHit});return p({tasks:n.tasks},r)}function Gl(t,e){return t.registerTool("perspective_evaluate",{description:ha,inputSchema:Dv.shape},async n=>{let r=await Av(n,e);return u(r)})}var ka="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' }] })",Rv=z.enum(["all","any","none"]),Cv=z.object({aggregation:Rv.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z.array(We).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 Fv(t,e){let n=await e.perspectiveService.evaluateRules(t.rules,t.aggregation);return p({tasks:n.tasks},e.makeMeta({cacheHit:false}))}function Kl(t,e){return t.registerTool("perspective_evaluate_dry_run",{description:ka,inputSchema:Cv.shape},async n=>{let r=await Fv(n,e);return u(r)})}var va='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 } } }.',Ev=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 Mv(t,e){let n=await e.perspectiveService.get(t.perspectiveId),r=e.makeMeta({cacheHit:false});return p({perspective:n},r)}function Yl(t,e){return t.registerTool("perspective_get",{description:va,inputSchema:Ev.shape},async n=>{let r=await Mv(n,e);return u(r)})}var Ia="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()",Uv=z.object({});async function Lv(t,e){let n=await e.perspectiveService.list(),r=e.makeMeta({cacheHit:n.cacheHit});return p({perspectives:n.perspectives},r)}function Zl(t,e){return t.registerTool("perspective_list",{description:Ia,inputSchema:Uv.shape},async n=>{let r=await Lv(n,e);return u(r)})}var pt=["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 Ql=z.enum(["all","any","none"]),Jv=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()}),Ta=z.lazy(()=>z.union([z.object({aggregateType:Ql,aggregateRules:z.array(Ta)}),z.object({disabledRule:Ta}),Jv])),Bv=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:Ql,rules:z.array(Ta),iconColor:Bv.nullable()});var wa='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 })',$v=z.enum(["all","any","none"]),Wv=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(),Hv=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:$v.optional().describe('New top-level rule aggregation. One of "all", "any", "none".'),rules:z.array(We).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([Wv,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 Vv(t,e){if(pt.includes(t.perspectiveId))throw new y(`Built-in perspectives cannot be updated: ${t.perspectiveId}. Built-in ids: ${pt.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 ep(t,e){return t.registerTool("perspective_update",{description:wa,inputSchema:Hv.shape},async n=>{let r=await Vv(n,e);return u(r)})}var Ft=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 Sa='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" } })',zv=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 qv(t,e){let n=new Ft({adapter:e.adapter}),{result:r}=await n.invoke({identifier:t.identifier,arg:t.arg}),o=e.makeMeta();return p({result:r},o)}function tp(t,e){return t.registerTool("plugin_invoke",{description:Sa,inputSchema:zv.shape},async n=>{let r=await qv(n,e);return u(r)})}var _a='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" }] })',Gv=z.object({id:v.schema.describe("Persistent project ID.")}),Kv=z.object({items:z.array(Gv).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Xv(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:Qd(a.succeeded.length)}))}function np(t,e){return t.registerTool("project_batch_complete",{description:_a,inputSchema:Kv.shape},async n=>{let r=await Xv(n,e);return u(r)})}var Oa='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" }] })',Yv=z.object({id:v.schema.describe("Persistent project ID.")}),Zv=z.object({items:z.array(Yv).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Qv(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:el(a.succeeded.length)}))}function rp(t,e){return t.registerTool("project_batch_drop",{description:Oa,inputSchema:Zv.shape},async n=>{let r=await Qv(n,e);return u(r)})}var Pa=`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" })`,Da=z.object({id:v.schema.describe("Persistent ID of the project to complete.")});async function tI(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:il()}))}function op(t,e){return t.registerTool("project_complete",{description:Pa,inputSchema:Da.shape},async n=>{let r=await tI(n,e);return u(r)})}var Aa="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 ap(t,e){return t.registerTool("project_complete_describe",{description:Aa,inputSchema:Da.shape},async n=>{let r=await nI(n,e);return u(r)})}function ce(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 rI(t,e={}){return {kind:"missing-detail",reason:t,...e}}function Ra(t,e={}){return {kind:"next-natural-step",reason:t,...e}}function oI(t,e={}){return {kind:"consider-alternative",reason:t,...e}}function aI(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 sI(t){return process.env.OMNIFOCUS_HINT_LEVEL==="warn"?t.filter(e=>(e.severity??"info")==="warn"):t}function Et(t,e=3){let n=sI(t),r=aI(n,e);return r.length>0?r:void 0}var iI=/\b(daily|weekly|monthly|every\s+(day|week|month|monday|tuesday|wednesday|thursday|friday|saturday|sunday|weekday|weekend))\b/i;function ip(t,e){if(iI.test(e))return Ra("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 cp(t,e,n){if(!(e===void 0||n!==void 0))return rI("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 dp(t,e=5){if(!(t<e))return oI(`Inbox now has ${t} unrouted tasks \u2014 consider triaging to keep your inbox clear.`,{suggestedTool:"task_list",suggestedArgs:{inbox:true},severity:"info"})}function lp(t,e){if(e===void 0)return Ra("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 pp(t,e){return Ra(`Project '${e}' now has no remaining tasks \u2014 consider completing or reviewing the project.`,{suggestedTool:"project_complete",suggestedArgs:{id:t},severity:"info"})}var Ca=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();}},up=new WeakMap;function cI(t){let e=up.get(t);return e||(e=new Map,up.set(t,e)),e}async function de(t,e,n){if(e===void 0)return n();let r=t.get(e);if(r)return mp(r);let o=cI(t),a=o.get(e);if(a){let s=await a;return mp(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 mp(t){return {...t,meta:{...t.meta,idempotentReplay:true}}}function fp(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 le=new Ca({ttlMs:fp("OMNIFOCUS_IDEMPOTENCY_TTL_MS",6e5),maxEntries:fp("OMNIFOCUS_IDEMPOTENCY_MAX_ENTRIES",1024)});var Ea='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 })',Ma=z.object({name:z.string().min(1).describe("Project name. Required, must be non-empty."),folderId:$.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:ce(["active","on-hold"],{paused:"on-hold"},"Initial project status. Default: active.").optional(),completionCriterion:ce(["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(b.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 dI(t,e){let n=e.idempotencyStore??le,r=e.replayStore??we;if(t.idempotency_key!==void 0&&n.get(t.idempotency_key)!==void 0)return Fa(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()):Fa(t,e,n));return Pt(`A project named "${t.name}" already exists. What should happen?`,c,a,s.map((d,l)=>({index:l,label:d})),{name:t.name})}return Fa(t,e,n)}async function Fa(t,e,n){return de(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=Et([lp(o,t.reviewIntervalDays)].filter(i=>i!=null));return p({created:true,id:o},e.makeMeta({syncPending:true,humanReadableSummary:Xd(t.name)}),void 0,a)})}function gp(t,e){return t.registerTool("project_create",{description:Ea,inputSchema:Ma.shape},async n=>{let r=await dI(n,e);return u(r)})}var Na="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 lI(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.folderId!==void 0){let a=await Qe(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 ${be(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${be(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 hp(t,e){return t.registerTool("project_create_describe",{description:Na,inputSchema:Ma.shape},async n=>{let r=await lI(n,e);return u(r)})}function et(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 It(`${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 pI(r)}function pI(t){return {...t,meta:{...t.meta,dryRun:true,syncPending:false}}}var Ua='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" })',La=z.object({id:v.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 uI(t,e){let n=e.idempotencyStore??le;return de(n,t.idempotency_key,async()=>{let r=await e.adapter.getProject(t.id);et(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:Zd(r.name)})));return Fe(t.dry_run,o,a)})}function yp(t,e){return t.registerTool("project_delete",{description:Ua,inputSchema:La.shape},async n=>{let r=await uI(n,e);return u(r)})}var Ja="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 mI(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 kp(t,e){return t.registerTool("project_delete_describe",{description:Ja,inputSchema:La.shape},async n=>{let r=await mI(n,e);return u(r)})}var Ba='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" })',$a=z.object({id:v.schema.describe("Persistent ID of the project to drop.")});async function gI(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:cl()}))}function vp(t,e){return t.registerTool("project_drop",{description:Ba,inputSchema:$a.shape},async n=>{let r=await gI(n,e);return u(r)})}var Wa="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 hI(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 Ip(t,e){return t.registerTool("project_drop_describe",{description:Wa,inputSchema:$a.shape},async n=>{let r=await hI(n,e);return u(r)})}var Va=`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:v.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."),verbose:z.boolean().optional().describe("When true, return the full unelided shape (project + tasks). Default: false \u2014 fields equal to their documented default are omitted from both. See docs/token-cost.md for the defaults table.")});async function kI(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=Ce(n.project.note),a=t.verbose===true?n.project:$e(n.project,Gn),i=n.tasks===void 0?void 0:t.verbose===true?n.tasks:ye(n.tasks,Be),s={project:a};return i!==void 0&&(s.tasks=i),o!==void 0&&(s.decision=o),p(s,r)}function Tp(t,e){return t.registerTool("project_get",{description:Va,inputSchema:yI.shape},async n=>{let r=await kI(n,e);return u(r)})}var za='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"] })',tr=100,vI=z.object({ids:z.array(v.schema).min(0).max(tr).describe(`Array of project IDs to fetch (0..${tr}). Get IDs from project_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function II(t,e){if(t.ids.length===0)return p({projects:[]},e.makeMeta());if(t.ids.length>tr)throw new y(`ids array exceeds the maximum batch size of ${tr} (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=Ce(d.note);l!==void 0&&(a[d.id]=l);}let i=Object.keys(a).length>0,s=o.length>0?[Ot(o)]:void 0,c=e.makeMeta({...s!==void 0?{warnings:s}:{}});return p({projects:r,...i&&{decisions:a}},c)}function bp(t,e){return t.registerTool("project_get_many",{description:za,inputSchema:vI.shape},async n=>{let r=await II(n,e);return u(r)})}var qa='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" })',TI=z.object({folderId:$.schema.optional().describe("Restrict to projects inside this folder. Get the ID from folder_list. Omit for all folders."),status:ce(["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."),verbose:z.boolean().optional().describe("When true, return the full unelided project shape. Default: false \u2014 fields equal to their documented default (status: 'active', completionCriterion: 'parallel', flagged: false, tagIds: [], note: null, etc.) are omitted. See docs/token-cost.md for the defaults table.")});async function wI(t,e){let{verbose:n,...r}=t,o=await e.projectService.list(r),a={cursor:o.nextCursor,hasMore:o.hasMore},i=e.makeMeta({cacheHit:o.cacheHit}),s=n===true?o.projects:ye(o.projects,Gn);return p({projects:s},i,a)}function Sp(t,e){return t.registerTool("project_list",{description:qa,inputSchema:TI.shape},async n=>{let r=await wI(n,e);return u(r)})}var Ga=`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 })`,Ka=z.object({id:v.schema.describe("Persistent ID of the project to move."),folderId:$.schema.nullable().describe("Target folder ID, or null to move to root.")});async function SI(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:dl(t.folderId!=null?"folder":"library root")}))}function jp(t,e){return t.registerTool("project_move",{description:Ga,inputSchema:Ka.shape},async n=>{let r=await SI(n,e);return u(r)})}var Xa="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 jI(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 Qe(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 _p(t,e){return t.registerTool("project_move_describe",{description:Xa,inputSchema:Ka.shape},async n=>{let r=await jI(n,e);return u(r)})}var Ya="project-template",_I=z.object({name:z.string().min(1),parameterNames:z.array(z.string().min(1)),capturedAt:ae()});function Nt(t){let e=Je(t,Ya);if(e===void 0)return;let n=St(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=_I.safeParse(r);return o.success?o.data:void 0}function xp(t,e){let n=jt({name:t.name,parameters:t.parameterNames.length>0?t.parameterNames.join(","):void 0,capturedAt:t.capturedAt}),r=_t(null,Ya,n);return e.length===0?r:`${r}
|
|
208
|
+
${o}`,a===""?null:a}function Oy(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var jo="decision-journal",_o=["stall-is-intentional","deferred-by-choice","blocked-on-external","awaiting-decision","acknowledged-zombie"],xy=z$1.object({kind:z$1.enum(_o).describe("The kind of judgment recorded."),reason:z$1.string().min(1).describe("Human-readable reason for the decision."),recordedAt:H().describe("When the decision was recorded (ISO-8601 with offset)."),until:H().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 We(t){let e=Ze(t,jo);if(e===void 0)return;let n=Ut(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=xy.safeParse(r);return o.success?o.data:void 0}function pd(t,e=new Date){return t.until===void 0?true:new Date(t.until).getTime()>e.getTime()}function Oo(t,e){let n=Lt({kind:e.kind,reason:e.reason,recordedAt:e.recordedAt,until:e.until});return Jt(t,jo,n)}function xo(t){return nr(t,jo)}function Tn(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 md="omnifocus://project-health{?staleDays}";function Py(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}function ud(t,e){let n=e.getTime()-new Date(t).getTime();return Math.max(0,Math.floor(n/(1440*60*1e3)))}function Ry(t,e,n){let r=e.filter(w=>!w.completed&&!w.dropped),o=e.map(w=>w.modifiedAt),a=Dy(o),s=a??t.modifiedAt,i=ud(s,n),c=r.filter(w=>w.available).length,l=r.filter(w=>w.blocked).length,d=r.length===0,u=n.toISOString(),f=r.length>0&&r.every(w=>w.deferDate!==null&&w.deferDate>u),g=t.lastReviewDate,I=g?ud(g,n):null,h;return t.nextReviewDate===null?h=true:h=t.nextReviewDate<=u,{lastTaskActivityAt:a,daysSinceActivity:i,availableTaskCount:c,blockedTaskCount:l,hasNoActions:d,deferredFutureTasks:f,lastReviewedAt:g,daysSinceReview:I,overdueForReview:h}}function Cy(t,e,n){return n?!!(e||t.availableTaskCount===0||t.overdueForReview||t.deferredFutureTasks):false}function Fy(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 Ey(t,e=14,n=new Date){let[r,o]=await Promise.all([t.listProjects(),t.listTasks({})]),a=Py(o),s=[],i=[];for(let l of r){let d=l.status==="active"&&!l.completed&&!l.dropped;if(!d)continue;let u=a.get(String(l.id))??[],f=Ry(l,u,n),g=Tn(l,f.lastTaskActivityAt,n,e);if(!Cy(f,g,d))continue;let I=We(l.note),h=I!==void 0&&pd(I,n),w={projectId:String(l.id),name:l.name,status:l.status,signals:f,...I!==void 0&&{decision:I},_severity:Fy(f)};h?i.push(w):s.push(w);}let c=(l,d)=>l._severity!==d._severity?d._severity-l._severity:l.name.localeCompare(d.name);return s.sort(c),i.sort(c),{projects:s.map(({_severity:l,...d})=>d),acknowledged:i.map(({_severity:l,...d})=>d),staleDays:e,generatedAt:n.toISOString()}}function My(t){if(!t)return 14;let e=Number.parseInt(t,10);return !Number.isFinite(e)||e<1?14:e}function fd(t,e){t.registerResource("omnifocus-project-health",new ResourceTemplate(md,{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=My(r.staleDays),s=await Ey(e,a);return {contents:[{uri:n.href,mimeType:"application/json",text:JSON.stringify(s,null,2)}]}});}var gd="omnifocus://recent-activity{?hours}",Ao=24,Uy=168;function Ly(t){if(t==null||t==="")return Ao;let e=Number(t);return !Number.isFinite(e)||e<0?Ao:Math.min(Math.max(1,Math.round(e)),Uy)}async function Jy(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()]),s=r.filter(u=>!u.dropped&&u.createdAt>=n).map(u=>({taskId:String(u.id),name:u.name,projectId:u.projectId!==null?String(u.projectId):null,createdAt:u.createdAt})).sort((u,f)=>f.createdAt>u.createdAt?1:f.createdAt<u.createdAt?-1:0),i=o.filter(u=>u.completedAt!==null).map(u=>{let f=u.completedAt,g=new Date(f).getTime()-new Date(u.createdAt).getTime();return {taskId:String(u.id),name:u.name,projectId:u.projectId!==null?String(u.projectId):null,completedAt:f,age_days_at_completion:Math.max(0,Math.round(g/864e5))}}).sort((u,f)=>f.completedAt>u.completedAt?1:f.completedAt<u.completedAt?-1:0),c=r.filter(u=>u.dropped&&u.droppedAt!==null&&u.droppedAt>=n).map(u=>({taskId:String(u.id),name:u.name,projectId:u.projectId!==null?String(u.projectId):null,droppedAt:u.droppedAt})).sort((u,f)=>f.droppedAt>u.droppedAt?1:f.droppedAt<u.droppedAt?-1:0),l=r.filter(u=>!u.dropped&&u.deferDate!==null&&u.modifiedAt>=n).map(u=>({taskId:String(u.id),name:u.name,projectId:u.projectId!==null?String(u.projectId):null,deferDate:u.deferDate})).sort((u,f)=>f.deferDate>u.deferDate?1:f.deferDate<u.deferDate?-1:0),d=a.filter(u=>u.modifiedAt>=n).map(u=>({projectId:String(u.id),name:u.name,status:u.status,modifiedAt:u.modifiedAt})).sort((u,f)=>f.modifiedAt>u.modifiedAt?1:f.modifiedAt<u.modifiedAt?-1:0);return {window:{hours:e,since:n},tasksCreated:s,tasksCompleted:i,tasksDropped:c,tasksDeferred:l,projectsModified:d,summary:{taskCreatedCount:s.length,taskCompletedCount:i.length,taskDroppedCount:c.length,taskDeferredCount:l.length,projectsAffected:d.length}}}function hd(t,e){t.registerResource("omnifocus-recent-activity",new ResourceTemplate(gd,{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=Ly(r.hours),a=o===Ao?"omnifocus://recent-activity":`omnifocus://recent-activity?hours=${o}`,s=await Jy(e,o);return {contents:[{uri:a,mimeType:"application/json",text:JSON.stringify(s,null,2)}]}});}var kd="omnifocus://retrospective{?from,to}",$y=7;function Wy(t,e,n=()=>new Date){let r=n(),o=r.toISOString(),a=new Date(r.getTime()-$y*864e5).toISOString(),s=yd(t)?t:a,i=yd(e)?e:o;return s>i?{from:i,to:s}:{from:s,to:i}}function yd(t){if(t==null||t==="")return false;let e=new Date(t);return Number.isFinite(e.getTime())}async function Hy(t,e){let{from:n,to:r}=e,[o,a]=await Promise.all([t.listTasks({completed:true,completedSince:n}),t.listTasks({completed:false})]),s=o.filter(d=>d.completedAt!==null&&d.completedAt<=r).map(d=>{let u=d.completedAt,f=new Date(u).getTime()-new Date(d.createdAt).getTime();return {taskId:String(d.id),name:d.name,projectId:d.projectId!==null?String(d.projectId):null,completedAt:u,age_days_at_completion:Math.max(0,Math.round(f/864e5))}}).sort((d,u)=>u.completedAt>d.completedAt?1:u.completedAt<d.completedAt?-1:0),i=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,u)=>u.droppedAt>d.droppedAt?1:u.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,u)=>u.deferDate>d.deferDate?1:u.deferDate<d.deferDate?-1:0),l=new Set;for(let d of s)d.projectId!==null&&l.add(d.projectId);for(let d of i)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:s,dropped:i,rolled:c,summary:{completedCount:s.length,droppedCount:i.length,rolledCount:c.length,projectsActive:l.size}}}function vd(t,e){t.registerResource("omnifocus-retrospective",new ResourceTemplate(kd,{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=Wy(o.from,o.to),s=await Hy(e,a);return {contents:[{uri:`omnifocus://retrospective?from=${encodeURIComponent(a.from)}&to=${encodeURIComponent(a.to)}`,mimeType:"application/json",text:JSON.stringify(s,null,2)}]}});}var Po="omnifocus://stats";function zy(t){return new Date(t.getFullYear(),t.getMonth(),t.getDate(),0,0,0,0).toISOString()}function Vy(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 qy(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 Gy(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 Ky(t,e=new Date){let[n,r,o,a,s,i]=await Promise.all([t.listTasks({}),t.listTasks({inbox:true,completed:false}),t.listProjects(),t.listProjectsDueForReview(),t.listTags(),t.getLastSync()]),c=zy(e),l=Vy(e),d=e.toISOString(),u=0,f=0,g=0,I=0,h=0,w=0,S=0,T=0,x=0;for(let P of n){if(u+=1,P.completed){P.completedAt&&(P.completedAt>=l&&(w+=1),P.completedAt>=c&&(h+=1));continue}if(P.dropped){P.droppedAt&&P.droppedAt>=c&&(x+=1);continue}P.available&&(f+=1),P.blocked&&(g+=1),P.flagged&&(T+=1),P.deferDate&&P.deferDate>d&&(I+=1),P.dueDate&&P.dueDate<d&&(S+=1);}let O=0,L=0,A=0,M=0,N=qy(n),W=0;for(let P of o){P.status==="active"&&!P.completed&&!P.dropped&&(O+=1),P.status==="on-hold"&&(L+=1),(P.completed||P.status==="done")&&(A+=1),(P.dropped||P.status==="dropped")&&(M+=1);let st=N.get(String(P.id))??[],ve=Gy(st.map(Tt=>Tt.modifiedAt));Tn(P,ve,e)&&(W+=1);}let V=null;if(r.length>0){let P=r.map(ve=>ve.createdAt).reduce((ve,Tt)=>Tt<ve?Tt:ve),st=e.getTime()-new Date(P).getTime();V=Math.max(0,Math.floor(st/(1440*60*1e3)));}let ce=new Set;for(let P of n)for(let st of P.tagIds)ce.add(String(st));let It=ce.size,de=null;if(i.lastSyncAt){let P=e.getTime()-new Date(i.lastSyncAt).getTime();de=Math.max(0,Math.floor(P/1e3));}return {tasks:{total:u,available:f,blocked:g,deferred:I,completed_today:h,completed_this_week:w,overdue_count:S,flagged_count:T,dropped_today:x},projects:{total:o.length,active:O,on_hold:L,completed:A,dropped:M,stalled_count:W,due_for_review_count:a.length},inbox:{count:r.length,oldest_age_days:V},tags:{total:s.length,with_tasks_count:It},database:{sync_age_seconds:de,last_sync_at:i.lastSyncAt}}}function Id(t,e){t.registerResource("omnifocus-stats",Po,{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 Ky(e);return {contents:[{uri:Po,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function Xy(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 s=t[o-1]===e[a-1]?0:1;r[a]=Math.min(r[a-1]+1,n[a]+1,n[a-1]+s);}[n,r]=[r,n];}return n[e.length]}function wn(t){return t.toLowerCase().trim().replace(/\s+/g," ").replace(/^@/,"")}function Td(t){return wn(t).split(/[\s\-_]+/).filter(Boolean).sort()}function Yy(t,e){let n=Td(t),r=Td(e);return n.length!==r.length?false:n.every((o,a)=>o===r[a])}function Do(t,e){if(t===e)return "exact-duplicate";let n=wn(t),r=wn(e);return n===r?"case-difference":n===`${r}s`||r===`${n}s`||n===`${r}es`||r===`${n}es`?"plural-singular":Xy(n,r)<=2||Yy(t,e)?"near-duplicate":null}var Zy=2,Qy=new Set(["a","an","and","as","at","by","for","from","in","is","it","of","on","or","the","to","with"]),ek=.7,tk=.2,nk=.05,rk=.05;function or(t){return t?t.toLowerCase().split(/[^a-z0-9']+/i).filter(e=>e.length>=Zy&&!Qy.has(e)):[]}function wd(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 bd(t,e){let n=new Set(or(t.name)),r=new Set(or(e.name)),o=wd(n,r),a=0;if(t.note&&e.note){let u=new Set(or(t.note)),f=new Set(or(e.note));u.size>0&&f.size>0&&(a=wd(u,f));}let s=[...n][0],i=[...r][0],c=s!==void 0&&s===i?1:0,l=wn(t.name)===wn(e.name)?1:0,d=o*ek+a*tk+c*nk+l*rk;return Math.max(0,Math.min(1,d))}var Ro="omnifocus://taxonomy-audit";async function ok(t){let[e,n]=await Promise.all([t.listTags(),t.listProjects()]),r=ak(e),o=sk(n);return {tagCollisions:r,projectCollisions:o}}function ak(t){let e=t.map(n=>({tagId:String(n.id),name:n.name,taskCount:n.taskCount}));return _d(e,(n,r)=>Do(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function sk(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 _d(e,(n,r)=>Do(n.name,r.name),(n,r)=>({candidates:n,reason:r}))}function _d(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 s(l,d,u){let f=a(l),g=a(d);if(f===g){let h=o.get(f)??"near-duplicate";o.set(f,jd(h,u));return}r[g]=f;let I=o.get(f)??o.get(g)??u;o.set(f,jd(I,u));}for(let l=0;l<t.length;l++)for(let d=l+1;d<t.length;d++){let u=e(t[l],t[d]);u!==null&&s(l,d,u);}let i=new Map;for(let l=0;l<t.length;l++){let d=a(l),u=i.get(d);u?u.push(t[l]):i.set(d,[t[l]]);}let c=[];for(let[l,d]of i)if(d.length>=2){let u=o.get(l)??"near-duplicate";c.push(n(d,u));}return c}var Sd={"exact-duplicate":0,"case-difference":1,"plural-singular":2,"near-duplicate":3};function jd(t,e){return Sd[t]<=Sd[e]?t:e}function Od(t,e){t.registerResource("omnifocus-taxonomy-audit",Ro,{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 ok(e);return {contents:[{uri:Ro,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}function Co(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 ar(t){let e=new Date(t);return e.setDate(e.getDate()+7),e}function xd(t,e=new Date){let n=Co(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 Pd="omnifocus://velocity{?weeks}",Ad=8,ck=52;function dk(t){if(t==null||t==="")return Ad;let e=Number(t);return !Number.isFinite(e)||e<0?Ad:Math.min(ck,Math.max(1,Math.round(e)))}async function lk(t,e,n=new Date){let r=xd(e,n),o=r[0],a=r[r.length-1],s=ar(a),i=o.toISOString(),c=s.toISOString(),[l,d,u]=await Promise.all([t.listTasks({completed:false}),t.listTasks({completed:true,completedSince:i}),t.listProjects()]),f=new Map;for(let A of u)f.set(String(A.id),A.name);let g=r.map(A=>{let M=ar(A),N=A.toISOString(),W=M.toISOString(),V=l.filter(de=>de.createdAt>=N&&de.createdAt<W).length,ce=d.filter(de=>de.completedAt!==null&&de.completedAt>=N&&de.completedAt<W).length,It=l.filter(de=>de.dropped&&de.droppedAt!==null&&de.droppedAt>=N&&de.droppedAt<W).length;return {weekStart:N,created:V,completed:ce,dropped:It,netDelta:V-ce-It}}),I=[];for(let A of [4,8]){if(e<A)continue;let M=g.slice(-A),N=M.reduce((V,ce)=>V+ce.completed,0),W=M.reduce((V,ce)=>V+ce.created,0);I.push({window:A,completedPerWeek:Math.round(N/A*100)/100,createdPerWeek:Math.round(W/A*100)/100});}let h=Co(a).toISOString(),w=ar(a).toISOString(),S=r[Math.max(0,r.length-4)].toISOString(),T=new Map,x=new Map;for(let A of d){if(A.completedAt===null||A.projectId===null)continue;let M=String(A.projectId);A.completedAt>=h&&A.completedAt<w&&T.set(M,(T.get(M)??0)+1),A.completedAt>=S&&A.completedAt<c&&x.set(M,(x.get(M)??0)+1);}let O=new Set([...T.keys(),...x.keys()]),L=Array.from(O).map(A=>({projectId:A,name:f.get(A)??A,closedThisWeek:T.get(A)??0,closedTrailing4:x.get(A)??0})).sort((A,M)=>M.closedThisWeek!==A.closedThisWeek?M.closedThisWeek-A.closedThisWeek:M.closedTrailing4-A.closedTrailing4).slice(0,5);return {window:{from:i,to:c},weeklyTotals:g,rollingAverages:I,topClosingProjects:L}}function Dd(t,e){t.registerResource("omnifocus-velocity",new ResourceTemplate(Pd,{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=dk(r.weeks),s=await lk(e,a);return {contents:[{uri:`omnifocus://velocity?weeks=${a}`,mimeType:"application/json",text:JSON.stringify(s,null,2)}]}});}var Eo="waiting-on",Mo=z$1.object({whom:z$1.string().min(1).describe("Person, team, or system being waited on."),what:z$1.string().min(1).optional().describe("Short description of what is being waited on."),since:H().describe("When the wait started (ISO-8601 with offset)."),followUpAfter:H().optional().describe("When the agent should nudge if still unresolved (ISO-8601 with offset).")});function Bt(t){let e=Ze(t,Eo);if(e===void 0)return;let n=Ut(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=Mo.safeParse(r);return o.success?o.data:void 0}function Rd(t,e){let n=Lt({whom:e.whom,what:e.what,since:e.since,followUpAfter:e.followUpAfter});return Jt(t,Eo,n)}function Cd(t){return nr(t,Eo)}function Fd(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 No="omnifocus://waiting-on";async function pk(t,e=new Date){let n=await t.listTasks({completed:false}),r=[];for(let o of n){let a=Bt(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:Fd(a,e)});}return r.sort((o,a)=>{let s=o.daysOverdue,i=a.daysOverdue;return s===null&&i===null?o.since<a.since?-1:o.since>a.since?1:0:s===null?1:i===null?-1:s!==i?i-s:o.since<a.since?-1:o.since>a.since?1:0}),{items:r}}function Ed(t,e){t.registerResource("omnifocus-waiting-on",No,{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 pk(e);return {contents:[{uri:No,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}});}var Sn="omnifocus://snapshot",jn="omnifocus://inbox",_n="omnifocus://forecast/today",On="omnifocus://overdue",xn="omnifocus://flagged",An="omnifocus://review-due",Lo="omnifocus://project/{id}",Jo="omnifocus://tag/{id}",Bo="omnifocus://perspective/{id}",Md="omnifocus://tasks/inbox",uk="omnifocus://tasks/project/{projectId}",mk="omnifocus://tasks/tag/{tagId}";function Uo(){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 xe(t,e){return {contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(e,null,2)}]}}function Nd(t,e){let{adapter:n,projectService:r,reviewService:o,forecastService:a,perspectiveService:s}=e;t.registerResource("omnifocus-snapshot",Sn,{description:"Orientation snapshot of the current OmniFocus state: inboxCount, overdueCount, dueTodayCount, flaggedCount, reviewDueCount, and syncStatus { lastSyncAt, inFlight }. Read at session start to orient before calling task_list or forecast_get. Use syncStatus.lastSyncAt to detect stale data before making decisions.",mimeType:"application/json"},async i=>{let{from:c,to:l}=Uo(),[d,u,f,g]=await Promise.all([n.listTasks({completed:false}),n.getForecast({from:c,to:l,includeOverdue:true,includeFlagged:true}),n.listProjectsDueForReview(),n.getLastSync()]),I=d.filter(h=>h.projectId===null&&h.parentId===null).length;return xe(Sn,{inboxCount:I,overdueCount:u.overdue.length,dueTodayCount:u.dueToday.length,flaggedCount:u.flagged.length,reviewDueCount:f.length,syncStatus:{lastSyncAt:g.lastSyncAt,inFlight:g.inFlight}})}),t.registerResource("omnifocus-inbox",jn,{description:"Inbox tasks as Task[]. Incomplete tasks not assigned to any project or parent task. Use to triage the inbox without calling task_list.",mimeType:"application/json"},async i=>{let l=(await n.listTasks({completed:false})).filter(d=>d.projectId===null&&d.parentId===null);return xe(jn,l)}),t.registerResource("omnifocus-forecast-today",_n,{description:"Today's forecast tasks grouped by category: overdue[], dueToday[], deferredToday[], flagged[]. Equivalent to forecast_get with from/to=today. Use for 'what's on my plate today' without a tool call.",mimeType:"application/json"},async i=>{let{from:c,to:l}=Uo(),d=await a.get({from:c,to:l});return xe(_n,{overdue:d.overdue,dueToday:d.dueToday,deferredToday:d.deferredToday,flagged:d.flagged})}),t.registerResource("omnifocus-overdue",On,{description:"All overdue tasks as Task[], sorted by dueDate ascending. Tasks whose dueDate is in the past and are not completed/dropped.",mimeType:"application/json"},async i=>{let{from:c}=Uo(),d=[...(await n.getForecast({from:c,to:c,includeOverdue:true,includeFlagged:false,includeDeferred:false})).overdue].sort((u,f)=>u.dueDate?f.dueDate?u.dueDate<f.dueDate?-1:u.dueDate>f.dueDate?1:0:-1:1);return xe(On,d)}),t.registerResource("omnifocus-flagged",xn,{description:"All flagged available tasks as Task[]. Equivalent to task_list with flagged=true. Use to review the flagged list without a tool call.",mimeType:"application/json"},async i=>{let c=await n.listTasks({flagged:true,completed:false});return xe(xn,c)}),t.registerResource("omnifocus-review-due",An,{description:"Projects due for review as Project[], sorted by nextReviewDate ascending. Equivalent to review_list_due. Use to start a review session without a tool call.",mimeType:"application/json"},async i=>{let l=[...(await o.listDue()).projects].sort((d,u)=>d.nextReviewDate?u.nextReviewDate?d.nextReviewDate<u.nextReviewDate?-1:d.nextReviewDate>u.nextReviewDate?1:0:-1:1);return xe(An,l)}),t.registerResource("omnifocus-project",new ResourceTemplate(Lo,{list:void 0}),{description:"Single project with its full task tree as { project: Project, tasks: Task[] }. Get the project ID from project_list. Tasks are a flat array; rebuild the tree via task.parentId.",mimeType:"application/json"},async(i,c)=>{let l=v.of(c.id),d=await r.get({id:l,includeTaskTree:true});return xe(`omnifocus://project/${l}`,{project:d.project,tasks:d.tasks??[]})}),t.registerResource("omnifocus-tag",new ResourceTemplate(Jo,{list:void 0}),{description:"Single tag with its tasks as { tag: Tag, tasks: Task[] }. Get the tag ID from tag_list.",mimeType:"application/json"},async(i,c)=>{let l=b.of(c.id),[d,u]=await Promise.all([n.getTag(l),n.listTasks({tagId:l,completed:false})]);return xe(`omnifocus://tag/${l}`,{tag:d,tasks:u})}),t.registerResource("omnifocus-perspective",new ResourceTemplate(Bo,{list:void 0}),{description:"Perspective evaluation result as { perspectiveId: string, tasks: Task[] }. Get perspective IDs from perspective_list. Built-in IDs: inbox, projects, tags, forecast, flagged, nearby, review.",mimeType:"application/json"},async(i,c)=>{let l=c.id,d=await s.evaluate(l);return xe(`omnifocus://perspective/${l}`,{perspectiveId:l,tasks:d.tasks})}),t.registerResource("omnifocus-tasks-inbox",Md,{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 xe(Md,c)}),t.registerResource("omnifocus-tasks-by-project",new ResourceTemplate(uk,{list:void 0}),{description:"Active tasks in a project as Task[]. Get the project ID from project_list or project_get. Returns incomplete tasks whose projectId matches the given ID.",mimeType:"application/json"},async(i,c)=>{let l=v.of(c.projectId),d=await n.listTasks({projectId:l,completed:false});return xe(`omnifocus://tasks/project/${l}`,d)}),t.registerResource("omnifocus-tasks-by-tag",new ResourceTemplate(mk,{list:void 0}),{description:"Active tasks with a specific tag as Task[]. Get the tag ID from tag_list or tag_get. Returns incomplete tasks that carry the given tag.",mimeType:"application/json"},async(i,c)=>{let l=b.of(c.tagId),d=await n.listTasks({tagId:l,completed:false});return xe(`omnifocus://tasks/tag/${l}`,d)}),hd(t,n),Ed(t,n),vd(t,n),Od(t,n),Dd(t,n),sd(t,n),cd(t),od(t,a),dd(t),Id(t,n),fd(t,n);}var gk=300*1e3,hk=200,$o=class{_store=new Map;_ttlMs;constructor(e=gk){this._ttlMs=e;}register(e,n){this._store.size>=hk&&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);}},Ae=new $o;function $t(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 Q(t,e){return {code:"WARN_UNKNOWN_FIELDS",message:`Unknown field name(s) in fields[] projection: ${t.join(", ")}.`,suggestion:`Allowed fields: ${[...e].sort().join(", ")}.`,details:{unknown:t,allowed:[...e]}}}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 Wo(t,e){return {error:t.toJSON(),meta:e}}function Wt(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 m(t){return {content:[{type:"text",text:JSON.stringify(t)}],structuredContent:t}}var Ho="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()",kk=z$1.object({});async function vk(t,e){let n=await e.adapter.appLaunch(),r=e.makeMeta();return p({launched:n.launched,alreadyRunning:n.alreadyRunning},r)}function Ud(t,e){return t.registerTool("app_launch",{description:Ho,inputSchema:kk.shape},async n=>{let r=await vk(n,e);return m(r)})}var sr=z$1.object({taskId:y.schema.optional().describe("Persistent ID of the task that owns the attachment. Provide exactly one of taskId or projectId."),projectId:v.schema.optional().describe("Persistent ID of the project that owns the attachment. Provide exactly one of taskId or projectId.")});function ir(t){if(t.taskId)return {taskId:y.of(t.taskId)};if(t.projectId)return {projectId:v.of(t.projectId)};throw new k("Provide exactly one of taskId or projectId.",{})}var Vo='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" })',Tk=sr;async function wk(t,e){let n=ir(t),r=await e.attachmentService.list(n);return p({attachments:r},e.makeMeta())}var qo=`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" })`,bk=sr.extend({filePath:z$1.string().min(1).max(4096,"max 4 KB").describe("Absolute path to the source file to attach. Must be within the allowed attachment path scope.")});async function Sk(t,e){let n=ir(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 Go=`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" })`,jk=sr.extend({attachmentId:Le.schema.describe("Persistent ID of the attachment to remove. Get from attachment_list.")});async function _k(t,e){let n=ir(t),r=Le.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 Ko=`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" })`,Ok=sr.extend({attachmentId:Le.schema.describe("Persistent ID of the attachment to save. Get from attachment_list."),destPath:z$1.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 xk(t,e){let n=ir(t),r=await e.attachmentService.saveTo({...n,attachmentId:Le.of(t.attachmentId),destPath:t.destPath});return p(r,e.makeMeta())}function Ld(t,e){t.registerTool("attachment_list",{description:Vo,inputSchema:Tk.shape},async n=>{let r=await wk(n,e);return m(r)}),t.registerTool("attachment_add",{description:qo,inputSchema:bk.shape},async n=>{let r=await Sk(n,e);return m(r)}),t.registerTool("attachment_remove",{description:Go,inputSchema:jk.shape},async n=>{let r=await _k(n,e);return m(r)}),t.registerTool("attachment_save_to_path",{description:Ko,inputSchema:Ok.shape},async n=>{let r=await xk(n,e);return m(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 Jd(t,e){t.invalidate(`tag:${e.tagId}`),t.invalidate("tag:list"),t.invalidate("forecast:*"),t.invalidate("perspective:*"),t.invalidate("search:*");}function Bd(t,e){t.invalidate(`folder:${e.folderId}`),t.invalidate("folder:list"),t.invalidate("perspective:*"),t.invalidate("search:*");}function $d(t){t.clear();}function cr(t){t.clear();}var Xo="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 })",Ak=z$1.object({confirm:z$1.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 Pk(t,e){let n=await e.adapter.redoLastMutation();return n.redid&&e.cache!==void 0&&cr(e.cache),p(n,e.makeMeta({syncPending:n.redid}))}function Hd(t,e){return t.registerTool("database_redo",{description:Xo,inputSchema:Ak.shape},async n=>{let r=await Pk(n,e);return m(r)})}var Yo="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 })",Dk=z$1.object({confirm:z$1.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 Rk(t,e){let n=await e.adapter.undoLastMutation();return n.undid&&e.cache!==void 0&&cr(e.cache),p(n,e.makeMeta({syncPending:n.undid}))}function Vd(t,e){return t.registerTool("database_undo",{description:Yo,inputSchema:Dk.shape},async n=>{let r=await Rk(n,e);return m(r)})}var Qo='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" }',Ck=z$1.object({targetKind:z$1.enum(["task","project"]).describe("Whether the target is a task or a project."),targetId:z$1.string().min(1).describe("ID of the task or project.")});async function Fk(t,e){if(t.targetKind==="task"){let a=y.of(t.targetId),s=await e.adapter.getTask(a),i=xo(s.note);return i===s.note?p({targetKind:"task",targetId:a,noChange:true},e.makeMeta()):(await e.adapter.updateTask(a,{note:i}),e.cache!==void 0&&j(e.cache,{taskId:a,projectId:s.projectId}),p({targetKind:"task",targetId:a,cleared:true},e.makeMeta({syncPending:true})))}let n=v.of(t.targetId),r=await e.adapter.getProject(n),o=xo(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&&B(e.cache,{projectId:n}),p({targetKind:"project",targetId:n,cleared:true},e.makeMeta({syncPending:true})))}function qd(t,e){return t.registerTool("decision_clear",{description:Qo,inputSchema:Ck.shape},async n=>{let r=await Fk(n,e);return m(r)})}var ea=`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" } }`,Ek=z$1.object({kind:z$1.enum(_o).describe("The kind of judgment recorded."),reason:z$1.string().min(1).describe("Human-readable reason for the decision."),until:z$1.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.")}),Mk=z$1.object({targetKind:z$1.enum(["task","project"]).describe("Whether the decision attaches to a task or a project."),targetId:z$1.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:Ek.describe("The decision payload. `recordedAt` is set automatically on write.")});async function Nk(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 i=y.of(t.targetId),c=await e.adapter.getTask(i),l=Oo(c.note,r);return await e.adapter.updateTask(i,{note:l}),e.cache!==void 0&&j(e.cache,{taskId:i,projectId:c.projectId}),p({targetKind:"task",targetId:i,decision:r},e.makeMeta({syncPending:true}))}let o=v.of(t.targetId),a=await e.adapter.getProject(o),s=Oo(a.note,r);return await e.adapter.updateProject(o,{note:s}),e.cache!==void 0&&B(e.cache,{projectId:o}),p({targetKind:"project",targetId:o,decision:r},e.makeMeta({syncPending:true}))}function Gd(t,e){return t.registerTool("decision_record",{description:ea,inputSchema:Mk.shape},async n=>{let r=await Nk(n,e);return m(r)})}var na=`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" })`,Uk=z$1.object({scope:z$1.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z$1.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 Lk(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new k(`scope='${t.scope}' requires an id`,{details:{field:"id",scope:t.scope}});return t.scope==="project"?{kind:"project",id:v.of(t.id)}:{kind:"folder",id:$.of(t.id)}}async function Jk(t,e){let n=Lk(t),r=await e.exportService.exportOpml(n),o=e.makeMeta();return p({opml:r.opml,projectCount:r.projectCount,taskCount:r.taskCount},o)}function Kd(t,e){return t.registerTool("export_opml",{description:na,inputSchema:Uk.shape},async n=>{let r=await Jk(n,e);return m(r)})}function G(t){return `'${t}'`}function z(t){return t.length>140?`${t.slice(0,137)}\u2026`:t}function Xd(t){return z(`Created task ${G(t)}.`)}function Yd(t){return z(`Completed task ${G(t)}.`)}function Zd(t){return z(`Uncompleted task ${G(t)}.`)}function Qd(t){return z(`Deleted task ${G(t)}.`)}function el(t){return z(`Dropped task ${G(t)}.`)}function tl(t){return z(`Restored task ${G(t)} from dropped.`)}function nl(t,e){return z(`Moved task ${G(t)} to ${e}.`)}function rl(t){return z(`Duplicated task ${G(t)}.`)}function ol(t){return z(`Reordered task ${G(t)}.`)}function al(t){return z(`Converted task ${G(t)} to a project.`)}function sl(t){return z(`Set repetition rule on task ${G(t)}.`)}function il(t){return z(`Cleared repetition rule on task ${G(t)}.`)}function cl(t,e){return z(`Set ${e} ${e===1?"alarm":"alarms"} on task ${G(t)}.`)}function dl(t){return z(`Cleared alarms on task ${G(t)}.`)}function Ht(t){return `Created ${t} task${t===1?"":"s"}.`}function ll(t){return `Updated ${t} task${t===1?"":"s"}.`}function pl(t){return `Completed ${t} task${t===1?"":"s"}.`}function ul(t){return `Uncompleted ${t} task${t===1?"":"s"}.`}function ml(t){return `Deleted ${t} task${t===1?"":"s"}.`}function fl(t){return `Dropped ${t} task${t===1?"":"s"}.`}function gl(t){return `Restored ${t} dropped task${t===1?"":"s"}.`}function hl(t,e){return z(`Moved ${t} task${t===1?"":"s"} to ${e}.`)}function yl(t){return z(`Created project ${G(t)}.`)}function kl(t){return z(`Updated project ${G(t)}.`)}function vl(t){return z(`Deleted project ${G(t)}.`)}function Il(t){return `Completed ${t} project${t===1?"":"s"}.`}function Tl(t){return `Dropped ${t} project${t===1?"":"s"}.`}function wl(t){return z(`Created tag ${G(t)}.`)}function lt(t){return z(`Updated tag ${G(t)}.`)}function bl(t,e){return z(`Moved tag ${G(t)} to ${e}.`)}function Sl(t){return z(`Created folder ${G(t)}.`)}function jl(t){return z(`Updated folder ${G(t)}.`)}function _l(t,e){return z(`Moved folder ${G(t)} to ${e}.`)}function dr(t,e){return z(`Set note on ${t} ${G(e)}.`)}function Ol(t,e){return z(`Appended to note on ${t} ${G(e)}.`)}function xl(){return "Completed project."}function Al(){return "Dropped project."}function Pl(t){return z(`Moved project to ${t}.`)}function Dl(){return "Deleted tag."}function Rl(){return "Deleted folder."}function lr(){return "Marked project as reviewed."}function Cl(t){return t===null?"Cleared project review interval.":`Set project review interval to ${t} day${t===1?"":"s"}.`}function Fl(t){return t===null?"Cleared project next review date.":z(`Set project next review date to ${t}.`)}var oa='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>" })',Bk=z$1.object({opml:z$1.string().min(1).describe("Well-formed OPML XML string to import. Use the output of export_opml for a round-trip."),destinationProjectId:z$1.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 $k(t,e){let n=await e.exportService.importOpml(t.opml,{...t.destinationProjectId!==void 0?{destinationProjectId:v.of(t.destinationProjectId)}:{}}),r=n.taskIds.length>0?await e.adapter.getTasksMany(n.taskIds):[],o=n.taskIds.map((s,i)=>{let c=r[i];return c==null?null:{id:String(s),name:c.name}}).filter(s=>s!==null),a=e.makeMeta({syncPending:true,humanReadableSummary:Ht(n.imported)});return p({imported:n.imported,tasks:o},a)}function El(t,e){return t.registerTool("import_opml",{description:oa,inputSchema:Bk.shape},async n=>{let r=await $k(n,e);return m(r)})}var aa=`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" })`,sa=`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)" })`,Wk=z$1.object({scope:z$1.enum(["project","folder","all"]).describe("What to export: 'project' (one project), 'folder' (all projects in a folder), or 'all' (all active projects)."),id:z$1.string().optional().describe("Required when scope='project' (project ID from project_list) or scope='folder' (folder ID from folder_list). Omit for scope='all'.")}),Hk=z$1.object({text:z$1.string().min(1).describe("TaskPaper-formatted text to import. Each '- Task name' line becomes a task."),targetProjectId:z$1.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 zk(t){if(t.scope==="all")return {kind:"all"};if(!t.id)throw new k(`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:v.of(t.id)}:{kind:"folder",id:$.of(t.id)}}function Ml(t,e){t.registerTool("export_taskpaper",{description:aa,inputSchema:Wk.shape},async n=>{let r=zk(n),o=await e.exportService.exportTaskPaper(r);return m(p({taskpaper:o.taskpaper,projectCount:o.projectCount,taskCount:o.taskCount,warnings:o.warnings},e.makeMeta()))}),t.registerTool("import_taskpaper",{description:sa,inputSchema:Hk.shape},async n=>m(await Vk(n,e)));}async function Vk(t,e){let n=await e.exportService.importTaskPaper(t.text,t.targetProjectId===void 0?void 0:v.of(t.targetProjectId)),r=n.created.length>0?await e.adapter.getTasksMany(n.created):[],o=n.created.map((a,s)=>{let i=r[s];return i==null?null:{id:String(a),name:i.name}}).filter(a=>a!==null);return p({tasks:o,warnings:n.warnings},e.makeMeta())}var ia=`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" })`,ca=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("Folder name. Must be non-empty."),parentId:$.schema.optional().describe("Parent folder ID. Omit for a root-level folder. Get from folder_list.")});async function qk(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:Sl(t.name)}))}function Ul(t,e){return t.registerTool("folder_create",{description:ia,inputSchema:ca.shape},async n=>{let r=await qk(n,e);return m(r)})}function Pe(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(),s=e.getUTCMinutes();return a===0&&s===0?`${n}-${r}-${o}`:`${n}-${r}-${o} ${String(a).padStart(2,"0")}:${String(s).padStart(2,"0")}`}async function Vt(t,e){try{return (await t.getProject(e)).name}catch{return e}}async function qt(t,e){try{return (await t.getTask(e)).name}catch{return e}}async function Se(t,e){try{return (await t.getTag(e)).name}catch{return e}}async function pt(t,e){try{return (await t.getFolder(e)).name}catch{return e}}var da="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 Gk(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await pt(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 Ll(t,e){return t.registerTool("folder_create_describe",{description:da,inputSchema:ca.shape},async n=>{let r=await Gk(n,e);return m(r)})}var la='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 })',pa=z$1.object({id:$.schema.describe("Persistent folder ID to delete. Get from folder_list."),cascade:z$1.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 Kk(t,e){return await e.folderService.delete(t.id,t.cascade??false),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Rl()}))}function Bl(t,e){return t.registerTool("folder_delete",{description:la,inputSchema:pa.shape},async n=>{let r=await Kk(n,e);return m(r)})}var ua="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 Xk(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 $l(t,e){return t.registerTool("folder_delete_describe",{description:ua,inputSchema:pa.shape},async n=>{let r=await Xk(n,e);return m(r)})}var Qe={flagged:{value:false},completed:{value:false},completedAt:{value:null},dropped:{value:false},droppedAt:{value:null},available:{value:true},blocked:{value:false},sequential:{value:false},completedByChildren:{value:false},note:{value:null,equivalentTo:[""]},noteHtml:{value:null,equivalentTo:[""]},parentId:{value:null},tagIds:{value:[]},deferDate:{value:null},dueDate:{value:null},estimatedMinutes:{value:null},repetition:{value:null}},pr={flagged:{value:false},completed:{value:false},completedAt:{value:null},dropped:{value:false},droppedAt:{value:null},note:{value:null,equivalentTo:[""]},noteHtml:{value:null,equivalentTo:[""]},folderId:{value:null},tagIds:{value:[]},status:{value:"active"},completionCriterion:{value:"parallel"},deferDate:{value:null},dueDate:{value:null},estimatedMinutes:{value:null},reviewIntervalDays:{value:null},nextReviewDate:{value:null},lastReviewDate:{value:null}},ur={parentId:{value:null},status:{value:"active"},location:{value:null},allowsNextAction:{value:true}},mr={parentId:{value:null}};function _e(t,e){let n={};for(let r of Object.keys(t)){let o=e[r],a=t[r];if(o===void 0){n[r]=a;continue}Yk(a,o)||(n[r]=a);}return n}function He(t,e){return t.map(n=>_e(n,e))}function Yk(t,e){if(t===e.value||Array.isArray(e.value)&&Array.isArray(t)&&t.length===0)return true;if(e.equivalentTo!==void 0){for(let n of e.equivalentTo)if(t===n)return true}return false}var ma='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" })',Zk=z$1.object({id:$.schema.describe("Persistent folder ID. Get from folder_list. IDs are stable across renames."),verbose:z$1.boolean().optional().describe("When true, return the full unelided folder shape. Default: false \u2014 `parentId` is omitted when null. See docs/token-cost.md for the defaults table.")});async function Qk(t,e){let n=await e.folderService.get(t.id),r=t.verbose===true?n.folder:_e(n.folder,mr);return p({folder:r},e.makeMeta({cacheHit:n.cacheHit}))}function Hl(t,e){return t.registerTool("folder_get",{description:ma,inputSchema:Zk.shape},async n=>{let r=await Qk(n,e);return m(r)})}var fa='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" })',ev=z$1.object({parentId:$.schema.optional().describe("Return only direct children of this folder. Get the ID from a previous folder_list call. Omit for root folders."),verbose:z$1.boolean().optional().describe("When true, return the full unelided folder shape. Default: false \u2014 `parentId` is omitted when null (top-level folder). See docs/token-cost.md for the defaults table.")});async function tv(t,e){let n={...t.parentId!==void 0?{parentId:t.parentId}:{}},r=await e.folderService.list(n),o=t.verbose===true?r.folders:He(r.folders,mr);return p({folders:o},e.makeMeta({cacheHit:r.cacheHit}))}function Vl(t,e){return t.registerTool("folder_list",{description:fa,inputSchema:ev.shape},async n=>{let r=await tv(n,e);return m(r)})}var ga=`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 })`,ha=z$1.object({id:$.schema.describe("Persistent ID of the folder to move. Get from folder_list."),parentId:$.schema.nullable().describe("New parent folder ID, or null to promote the folder to root level.")});async function rv(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:_l(n.name,t.parentId!=null?"folder":"library root")}))}function ql(t,e){return t.registerTool("folder_move",{description:ga,inputSchema:ha.shape},async n=>{let r=await rv(n,e);return m(r)})}var ya="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 ov(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 s=await pt(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`inside '${s}'`;}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 Gl(t,e){return t.registerTool("folder_move_describe",{description:ya,inputSchema:ha.shape},async n=>{let r=await ov(n,e);return m(r)})}var ka='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" })',va=z$1.object({id:$.schema.describe("Persistent folder ID. Get from folder_list."),name:z$1.string().min(1).max(1024,"max 1 KB").optional().describe("New folder name. Must be non-empty if supplied.")});async function av(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:jl(o.name)}))}function Xl(t,e){return t.registerTool("folder_update",{description:ka,inputSchema:va.shape},async n=>{let r=await av(n,e);return m(r)})}var Ia="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 sv(t,e){let n=[],r=[],o=String(t.id);try{let s=await e.adapter.getFolder(t.id);o=s.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:s.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 Yl(t,e){return t.registerTool("folder_update_describe",{description:Ia,inputSchema:va.shape},async n=>{let r=await sv(n,e);return m(r)})}var Zl=z$1.object({self:z$1.string(),project:z$1.string().nullable(),parent:z$1.string().nullable(),tags:z$1.array(z$1.string())}),Ql=z$1.object({self:z$1.string(),folder:z$1.string().nullable()});function ut(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 Ta(t){return {self:`omnifocus://project/${t.id}`,folder:t.folderId!==null?`omnifocus://folder/${t.folderId}`:null}}var ep=z$1.enum(["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]),iv=z$1.union([z$1.object({day:z$1.number().int().min(1).max(31)}),z$1.object({weekday:ep,position:z$1.union([z$1.literal(1),z$1.literal(2),z$1.literal(3),z$1.literal(4),z$1.literal("last")])})]),wa=z$1.discriminatedUnion("kind",[z$1.object({kind:z$1.literal("due-relative"),offsetSeconds:z$1.number().int()}),z$1.object({kind:z$1.literal("defer-relative"),offsetSeconds:z$1.number().int()}),z$1.object({kind:z$1.literal("absolute"),fireAt:H()})]),ba=z$1.object({method:z$1.enum(["fixed","start-again","due-again"]),unit:z$1.enum(["minutes","hours","days","weeks","months","years"]),steps:z$1.number().int().min(1),weekdays:z$1.array(ep).optional(),monthlyAnchor:iv.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"}),fe=["name","note","noteHtml","projectId","parentId","tagIds","deferDate","deferDateFloating","dueDate","dueDateFloating","estimatedMinutes","flagged","completed","completedAt","dropped","droppedAt","available","blocked","sequential","completedByChildren","repetition","notifications","createdAt","modifiedAt","_links"],De=new Set(fe);z$1.object({id:y.schema,name:z$1.string(),note:z$1.string().nullable(),noteHtml:z$1.string().nullable(),projectId:v.schema.nullable(),parentId:y.schema.nullable(),tagIds:z$1.array(b.schema),deferDate:H().nullable(),deferDateFloating:z$1.boolean().optional(),dueDate:H().nullable(),dueDateFloating:z$1.boolean().optional(),estimatedMinutes:z$1.number().int().min(1).nullable(),flagged:z$1.boolean(),completed:z$1.boolean(),completedAt:H().nullable(),dropped:z$1.boolean(),droppedAt:H().nullable(),available:z$1.boolean(),blocked:z$1.boolean(),sequential:z$1.boolean(),completedByChildren:z$1.boolean(),repetition:ba.nullable(),notifications:z$1.array(wa).optional(),createdAt:H(),modifiedAt:H(),_links:Zl.optional()});function ee(t,e){let n=[],r=[],o=new Set;for(let a of t)o.has(a)||(o.add(a),a!=="id"&&(e.has(a)?n.push(a):r.push(a)));return {valid:n,unknown:r}}function X(t,e){if(e===void 0)return t;let n={id:t.id};for(let r of e)r!=="id"&&Object.hasOwn(t,r)&&(n[r]=t[r]);return n}var Sa=`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 task IDs per calendar day (dereference from dueToday[]). Returns { overdue[], dueToday[], deferredToday[], flagged[], byDate? }; byDate entries are { date, taskIds[] }. Safe to call repeatedly; no side effects. Example: forecast_get({ date: "today" }) Example: forecast_get({ date: "today", days: 3, includeFlagged: false })`,cv=z$1.object({date:Ye().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$1.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:Ye().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:Ye().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$1.boolean().optional().default(true).describe("Include tasks overdue before the start of the range. Default true."),includeDeferred:z$1.boolean().optional().default(true).describe("Include tasks whose defer date falls within the range. Default true."),includeFlagged:z$1.boolean().optional().default(true).describe("Include all flagged incomplete tasks. Default true."),fields:z$1.array(z$1.string()).optional().describe(`Restrict each returned task (across overdue/dueToday/deferredToday/flagged/byDate) to this list of top-level fields (id is always returned). Omit for the full task shape. Empty array returns just id. Unknown names are dropped silently and surface in meta.warnings.WARN_UNKNOWN_FIELDS. Allowed: ${fe.join(", ")}.`)});function dv(t){let e;if(kn(t))e=vn(t);else if(In(t))e=t;else throw new k(`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 lv(t){let e=t.date!==void 0,n=t.from!==void 0||t.to!==void 0;if(e&&n)throw new k("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 i=dv(t.date),c=new Date(i);return c.setDate(c.getDate()+r-1),c.setHours(23,59,59,999),{from:i.toISOString(),to:c.toISOString(),days:r}}if(n){let i=new Date,c=new Date(i);c.setHours(0,0,0,0);let l=new Date(i);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 s=new Date(a);return s.setDate(s.getDate()+r-1),s.setHours(23,59,59,999),{from:a.toISOString(),to:s.toISOString(),days:r}}function pv(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.id),e.set(r,o);}return [...e.entries()].sort(([n],[r])=>n.localeCompare(r)).map(([n,r])=>({date:n,taskIds:r}))}async function uv(t,e){let{from:n,to:r,days:o}=lv(t),a=await e.forecastService.get({from:n,to:r,includeOverdue:t.includeOverdue,includeDeferred:t.includeDeferred,includeFlagged:t.includeFlagged}),s=t.fields!==void 0?ee(t.fields,De):void 0,i=s?.valid,c=f=>X(f,i),l={overdue:a.overdue.map(c),dueToday:a.dueToday.map(c),deferredToday:a.deferredToday.map(c),flagged:a.flagged.map(c)};o>1&&(l.byDate=pv(a.dueToday));let d=s!==void 0&&s.unknown.length>0?[Q([...s.unknown],fe)]:void 0,u=e.makeMeta({cacheHit:a.cacheHit,...d!==void 0?{warnings:d}:{}});return p(l,u)}function tp(t,e){return t.registerTool("forecast_get",{description:Sa,inputSchema:cv.shape},async n=>{let r=await uv(n,e);return m(r)})}var ja="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()",fv=z$1.object({});async function gv(t,e){let n=await e.forecastService.getForecastTag();return p(n,e.makeMeta())}function np(t,e){return t.registerTool("forecast_get_tag",{description:ja,inputSchema:fv.shape},async n=>{let r=await gv(n,e);return m(r)})}var _a=`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" } })`,hv=z$1.object({budgetMinutes:z$1.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$1.object({tagIds:z$1.array(b.schema).optional().describe("Restrict to tasks bearing at least one of these tag IDs. Empty array or omitted means no tag filter."),scope:z$1.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 yv(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 kv(t,e,n){let r=n?.tagIds,a=[...r&&r.length>0?t.filter(l=>l.tagIds.some(d=>r.includes(d))):t].sort(yv),s=[],i=[],c=0;for(let l of a){if(l.estimatedMinutes===null){i.push({taskId:l.id,name:l.name,estimatedMinutes:null,reason:"no-estimate"});continue}c+l.estimatedMinutes<=e?(s.push({taskId:l.id,name:l.name,estimatedMinutes:l.estimatedMinutes,flagged:l.flagged,dueDate:l.dueDate}),c+=l.estimatedMinutes):i.push({taskId:l.id,name:l.name,estimatedMinutes:l.estimatedMinutes,reason:"exceeds-budget"});}return {selected:s,totalMinutes:c,skipped:i}}function vv(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 Iv(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 Tv(t,e){let n=t.filter?.scope??"today",{from:r,to:o}=vv(n),a=await e.forecastService.get({from:r,to:o,includeOverdue:true,includeDeferred:n==="next7",includeFlagged:true}),s=Iv(a),i=kv(s,t.budgetMinutes,t.filter),c=e.makeMeta({cacheHit:a.cacheHit});return p(i,c)}function rp(t,e){return t.registerTool("forecast_pack",{description:_a,inputSchema:hv.shape},async n=>{let r=await Tv(n,e);return m(r)})}var xa=`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 })`,wv=z$1.object({tagId:z$1.union([b.schema,z$1.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 bv(t,e){let n=await e.forecastService.setForecastTag(t.tagId);return e.cache.invalidate("forecast:*"),p(n,e.makeMeta())}function op(t,e){return t.registerTool("forecast_set_tag",{description:xa,inputSchema:wv.shape},async n=>{let r=await bv(n,e);return m(r)})}var Aa=`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" })`,Sv=z$1.object({targetKind:z$1.enum(["task","project"]).describe("The kind of OmniFocus item whose note to append to."),id:z$1.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),text:z$1.string().min(1).max(1048576,"max 1 MB").describe("Text to append. A newline separator is inserted before the text if a note exists.")});async function jv(t,e){let n,r;if(t.targetKind==="task"){let a=await e.adapter.getTask(y.of(t.id));n=a.note,r=a.name;}else {let a=await e.adapter.getProject(v.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(y.of(t.id),{note:o}),e.cache!==void 0&&j(e.cache,{taskId:y.of(t.id)})):(await e.adapter.updateProject(v.of(t.id),{note:o}),e.cache!==void 0&&B(e.cache,{projectId:v.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:r,note:o},e.makeMeta({syncPending:true,humanReadableSummary:Ol(t.targetKind,r)}))}function ap(t,e){return t.registerTool("note_append",{description:Aa,inputSchema:Sv.shape},async n=>{let r=await jv(n,e);return m(r)})}var Da=`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" })`,_v=z$1.object({targetKind:z$1.enum(["task","project"]).describe("The kind of OmniFocus item whose note to read."),id:z$1.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function Ov(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(y.of(t.id))).note:(await e.adapter.getProject(v.of(t.id))).note;return p({note:n??null},e.makeMeta())}function sp(t,e){return t.registerTool("note_get",{description:Da,inputSchema:_v.shape},async n=>{let r=await Ov(n,e);return m(r)})}var Ca=`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" })`,xv=z$1.object({targetKind:z$1.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to read."),id:z$1.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list.")});async function Av(t,e){let n=t.targetKind==="task"?y.of(t.id):v.of(t.id),r=await e.adapter.getNoteHtml(t.targetKind,n);return p({noteHtml:r},e.makeMeta())}function ip(t,e){return t.registerTool("note_get_html",{description:Ca,inputSchema:xv.shape},async n=>{let r=await Av(n,e);return m(r)})}var Fa=`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" })`,Pv=z$1.object({targetKind:z$1.enum(["task","project"]).describe("The kind of OmniFocus item whose note to set."),id:z$1.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),note:z$1.string().max(1048576,"max 1 MB").nullable().describe("New note text. Pass null to clear the note entirely.")});async function Dv(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(y.of(t.id))).name:(await e.adapter.getProject(v.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(y.of(t.id),{note:t.note}),e.cache!==void 0&&j(e.cache,{taskId:y.of(t.id)})):(await e.adapter.updateProject(v.of(t.id),{note:t.note}),e.cache!==void 0&&B(e.cache,{projectId:v.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,note:t.note},e.makeMeta({syncPending:true,humanReadableSummary:dr(t.targetKind,n)}))}function cp(t,e){return t.registerTool("note_set",{description:Fa,inputSchema:Pv.shape},async n=>{let r=await Dv(n,e);return m(r)})}var Ea=`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" })`,Cv=z$1.object({targetKind:z$1.enum(["task","project"]).describe("The kind of OmniFocus item whose HTML note to set."),id:z$1.string().min(1).describe("Persistent ID of the task or project. Get task IDs from task_list; project IDs from project_list."),noteHtml:z$1.string().max(1048576,"max 1 MB").nullable().describe("HTML fragment to set as the note. Pass null to clear the note entirely.")});async function Fv(t,e){let n=t.targetKind==="task"?(await e.adapter.getTask(y.of(t.id))).name:(await e.adapter.getProject(v.of(t.id))).name;return t.targetKind==="task"?(await e.adapter.updateTask(y.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&j(e.cache,{taskId:y.of(t.id)})):(await e.adapter.updateProject(v.of(t.id),{noteHtml:t.noteHtml}),e.cache!==void 0&&B(e.cache,{projectId:v.of(t.id)})),p({updated:true,id:t.id,targetKind:t.targetKind,name:n,noteHtml:t.noteHtml},e.makeMeta({syncPending:true,humanReadableSummary:dr(t.targetKind,n)}))}function dp(t,e){return t.registerTool("note_set_html",{description:Ea,inputSchema:Cv.shape},async n=>{let r=await Fv(n,e);return m(r)})}function Mv(){let t=fileURLToPath(import.meta.url);return $r.resolve($r.dirname(t),"../../../reports/mutation/mutation.json")}function Nv(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 pp(t={}){let e=t.reportPath??Mv();if(!(t.existsSync??Pt.existsSync)(e))return null;let r=t.readFile??(g=>Pt.readFileSync(g,"utf8")),o=t.stat??Pt.statSync,a;try{a=JSON.parse(r(e));}catch{return null}let{Killed:s,Survived:i,Timeout:c,NoCoverage:l}=Nv(a),d=s+i+c+l;if(d===0)return null;let u=(s+c)/d*100,f=o(e).mtime.toISOString();return {score:u,lastRunAt:f}}var Na="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, responseStats, stores }. cache.services maps key prefixes (tag, folder, forecast, task, project) to { hits, misses, hitRate }. 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: macOS Calendar bridge state \u2014 { available, permission: granted|denied|restricted|not-determined|unknown }. Read-only; does NOT trigger 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). responseStats: per-tool response-byte aggregates { since, sampleRate, thresholdBytes, tools } \u2014 null when sampleRate is 0. stores: { idempotencyEntries, loopDetectorKeys } live retention-store sizes \u2014 null when not wired. Read-only; no side effects. Example: internal_status()",Lv=z$1.object({});async function Jv(t,e){let n=Date.now()-e.startedAt,r=null;try{let u=await e.adapter.getLastSync();r={lastSyncAt:u.lastSyncAt,inFlight:u.inFlight};}catch{r=null;}let o=e.circuitRegistry.snapshot(),a=null;try{a=await(e.probeCalendarAccess??Qn)();}catch{a=null;}let s=null;try{s=(e.probeMutationScore??pp)();}catch{s=null;}let i=null;if(e.probeResponseStats!==void 0)try{i=e.probeResponseStats();}catch{i=null;}let c=null;if(e.probeCache!==void 0)try{c=e.probeCache();}catch{c=null;}let l=null;if(e.probeStores!==void 0)try{l=e.probeStores();}catch{l=null;}return p({uptimeMs:n,ofRunning:true,lastSync:r,calendarAccess:a,mutation:s,cache:c,circuits:o,queueDepth:null,responseStats:i,stores:l},e.makeMeta())}function up(t,e){return t.registerTool("internal_status",{description:Na,inputSchema:Lv.shape},async n=>{let r=await Jv(n,e);return m(r)})}var mp=["actionAvailability","actionStatus","actionHasAllOfTags","actionHasAnyOfTags","actionHasNoProject","actionHasDueDate","actionHasDeferDate","actionIsLeaf","actionIsProject","actionMatchingSearch","actionWithinFocus"];function Bv(t){let e=0;for(let n of mp)t[n]!==void 0&&(e+=1);return e}var Ua=z$1.array(z$1.string().min(1,"id must be non-empty")).min(1,"list must contain at least one id"),$v=z$1.enum(["all","any","none"]),Wv=z$1.object({actionAvailability:z$1.enum(["available","remaining","completed","dropped","firstAvailable"]).optional(),actionStatus:z$1.enum(["flagged","due"]).optional(),actionHasAllOfTags:Ua.optional(),actionHasAnyOfTags:Ua.optional(),actionHasNoProject:z$1.boolean().optional(),actionHasDueDate:z$1.boolean().optional(),actionHasDeferDate:z$1.boolean().optional(),actionIsLeaf:z$1.boolean().optional(),actionIsProject:z$1.boolean().optional(),actionMatchingSearch:z$1.array(z$1.string().min(1)).min(1).optional(),actionWithinFocus:Ua.optional()}).strict(),Hv=Wv.superRefine((t,e)=>{let n=Bv(t);if(n>1){let r=mp.filter(o=>t[o]!==void 0);e.addIssue({code:z$1.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:[]});}}),tt=z$1.lazy(()=>z$1.union([z$1.object({aggregateType:$v,aggregateRules:z$1.array(tt)}).strict(),z$1.object({disabledRule:tt}).strict(),Hv]));tt.register(z$1.globalRegistry,{id:"PerspectiveRuleInput"});var La='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" }] })',zv=z$1.enum(["all","any","none"]),Vv=z$1.object({r:z$1.number().min(0).max(1).describe("Red channel as a [0, 1] float."),g:z$1.number().min(0).max(1).describe("Green channel as a [0, 1] float."),b:z$1.number().min(0).max(1).describe("Blue channel as a [0, 1] float."),a:z$1.number().min(0).max(1).describe("Alpha channel as a [0, 1] float; 1 is opaque.")}).strict(),qv=z$1.object({name:z$1.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:zv.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z$1.array(tt).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:Vv.optional().describe("Custom icon color in [0, 1] floats { r, g, b, a }. Omit for the OmniFocus-assigned default.")});async function Gv(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 fp(t,e){return t.registerTool("perspective_create",{description:La,inputSchema:qv.shape},async n=>{let r=await Gv(n,e);return m(r)})}var Ja='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" }.',Kv=z$1.object({perspectiveId:z$1.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 Xv(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 hp(t,e){return t.registerTool("perspective_delete",{description:Ja,inputSchema:Kv.shape},async n=>{let r=await Xv(n,e);return m(r)})}var Ba=`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" })`,Yv=z$1.object({perspectiveId:z$1.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)."),fields:z$1.array(z$1.string()).optional().describe(`Restrict each returned task to this list of top-level fields (id is always returned). Omit for the full task shape. Empty array returns just id. Unknown names are dropped silently and surface in meta.warnings.WARN_UNKNOWN_FIELDS. Allowed: ${fe.join(", ")}.`)});async function Zv(t,e){let n=await e.perspectiveService.evaluate(t.perspectiveId),r=t.fields!==void 0?ee(t.fields,De):void 0,o=r?.valid,a=n.tasks.map(c=>X(c,o)),s=r!==void 0&&r.unknown.length>0?[Q([...r.unknown],fe)]:void 0,i=e.makeMeta({cacheHit:n.cacheHit,...s!==void 0?{warnings:s}:{}});return p({tasks:a},i)}function yp(t,e){return t.registerTool("perspective_evaluate",{description:Ba,inputSchema:Yv.shape},async n=>{let r=await Zv(n,e);return m(r)})}var Wa="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' }] })",Qv=z$1.enum(["all","any","none"]),eI=z$1.object({aggregation:Qv.optional().describe('Top-level rule aggregation. One of "all", "any", "none". Defaults to "all" when omitted.'),rules:z$1.array(tt).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 tI(t,e){let n=await e.perspectiveService.evaluateRules(t.rules,t.aggregation);return p({tasks:n.tasks},e.makeMeta({cacheHit:false}))}function kp(t,e){return t.registerTool("perspective_evaluate_dry_run",{description:Wa,inputSchema:eI.shape},async n=>{let r=await tI(n,e);return m(r)})}var Ha='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 } } }.',nI=z$1.object({perspectiveId:z$1.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 rI(t,e){let n=await e.perspectiveService.get(t.perspectiveId),r=e.makeMeta({cacheHit:false});return p({perspective:n},r)}function Ip(t,e){return t.registerTool("perspective_get",{description:Ha,inputSchema:nI.shape},async n=>{let r=await rI(n,e);return m(r)})}var za="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()",aI=z$1.object({});async function sI(t,e){let n=await e.perspectiveService.list(),r=e.makeMeta({cacheHit:n.cacheHit});return p({perspectives:n.perspectives},r)}function Tp(t,e){return t.registerTool("perspective_list",{description:za,inputSchema:aI.shape},async n=>{let r=await sI(n,e);return m(r)})}var _t=["inbox","projects","tags","forecast","flagged","nearby","review"];z$1.object({id:z$1.string().min(1),name:z$1.string().min(1),kind:z$1.enum(["builtin","custom"]),requiresPro:z$1.boolean(),icon:z$1.string().nullable()});var wp=z$1.enum(["all","any","none"]),iI=z$1.object({actionAvailability:z$1.enum(["available","remaining","completed","dropped","firstAvailable"]).optional(),actionStatus:z$1.enum(["flagged","due"]).optional(),actionHasAllOfTags:z$1.array(z$1.string()).optional(),actionHasAnyOfTags:z$1.array(z$1.string()).optional(),actionHasNoProject:z$1.boolean().optional(),actionHasDueDate:z$1.boolean().optional(),actionHasDeferDate:z$1.boolean().optional(),actionIsLeaf:z$1.boolean().optional(),actionIsProject:z$1.boolean().optional(),actionMatchingSearch:z$1.array(z$1.string()).optional(),actionWithinFocus:z$1.array(z$1.string()).optional(),unknown:z$1.record(z$1.string(),z$1.unknown()).optional()}),Va=z$1.lazy(()=>z$1.union([z$1.object({aggregateType:wp,aggregateRules:z$1.array(Va)}),z$1.object({disabledRule:Va}),iI])),cI=z$1.object({r:z$1.number().min(0).max(1),g:z$1.number().min(0).max(1),b:z$1.number().min(0).max(1),a:z$1.number().min(0).max(1)});z$1.object({id:z$1.string().min(1),name:z$1.string().min(1),aggregation:wp,rules:z$1.array(Va),iconColor:cI.nullable()});var qa='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 })',dI=z$1.enum(["all","any","none"]),lI=z$1.object({r:z$1.number().min(0).max(1).describe("Red channel as a [0, 1] float."),g:z$1.number().min(0).max(1).describe("Green channel as a [0, 1] float."),b:z$1.number().min(0).max(1).describe("Blue channel as a [0, 1] float."),a:z$1.number().min(0).max(1).describe("Alpha channel as a [0, 1] float; 1 is opaque.")}).strict(),pI=z$1.object({perspectiveId:z$1.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$1.string().min(1).optional().describe("New display name. Must be non-empty when provided. OmniFocus rejects duplicate names."),aggregation:dI.optional().describe('New top-level rule aggregation. One of "all", "any", "none".'),rules:z$1.array(tt).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$1.union([lI,z$1.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 uI(t,e){if(_t.includes(t.perspectiveId))throw new k(`Built-in perspectives cannot be updated: ${t.perspectiveId}. Built-in ids: ${_t.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 bp(t,e){return t.registerTool("perspective_update",{description:qa,inputSchema:pI.shape},async n=>{let r=await uI(n,e);return m(r)})}var Gt=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 Ka='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" } })',mI=z$1.object({identifier:z$1.string().min(1).describe('Bundle identifier of the Omni Automation plug-in to invoke (e.g. "com.example.my-plugin").'),arg:z$1.unknown().optional().describe("Optional JSON-serialisable argument forwarded to the plug-in action as Action.args[0]. Defaults to null.")});async function fI(t,e){let n=new Gt({adapter:e.adapter}),{result:r}=await n.invoke({identifier:t.identifier,arg:t.arg}),o=e.makeMeta();return p({result:r},o)}function Sp(t,e){return t.registerTool("plugin_invoke",{description:Ka,inputSchema:mI.shape},async n=>{let r=await fI(n,e);return m(r)})}var Ya='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" }] })',gI=z$1.object({id:v.schema.describe("Persistent project ID.")}),hI=z$1.object({items:z$1.array(gI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function yI(t,e){let n=t.items.map(i=>i.id),r=await e.adapter.getProjectsMany(n),o=new Map;for(let i=0;i<n.length;i++){let c=r[i];c!=null&&o.set(n[i],c.name);}let a=await e.adapter.batchCompleteProjects(t.items.map(i=>({id:i.id})));if(e.cache!==void 0)for(let i of a.succeeded){let c=t.items[i.index];c!==void 0&&B(e.cache,{projectId:c.id});}let s=a.succeeded.map(i=>({index:i.index,value:{id:i.value,name:o.get(i.value)??""}}));return p({completed:s,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Il(a.succeeded.length)}))}function jp(t,e){return t.registerTool("project_batch_complete",{description:Ya,inputSchema:hI.shape},async n=>{let r=await yI(n,e);return m(r)})}var Qa='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" }] })',kI=z$1.object({id:v.schema.describe("Persistent project ID.")}),vI=z$1.object({items:z$1.array(kI).min(1).describe("Array of { id } items. Must contain at least one item.")});async function II(t,e){let n=t.items.map(i=>i.id),r=await e.adapter.getProjectsMany(n),o=new Map;for(let i=0;i<n.length;i++){let c=r[i];c!=null&&o.set(n[i],c.name);}let a=await e.adapter.batchDropProjects(t.items.map(i=>({id:i.id})));if(e.cache!==void 0)for(let i of a.succeeded){let c=t.items[i.index];c!==void 0&&B(e.cache,{projectId:c.id});}let s=a.succeeded.map(i=>({index:i.index,value:{id:i.value,name:o.get(i.value)??""}}));return p({dropped:s,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Tl(a.succeeded.length)}))}function _p(t,e){return t.registerTool("project_batch_drop",{description:Qa,inputSchema:vI.shape},async n=>{let r=await II(n,e);return m(r)})}var es=`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" })`,ts=z$1.object({id:v.schema.describe("Persistent ID of the project to complete.")});async function wI(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:xl()}))}function Op(t,e){return t.registerTool("project_complete",{description:es,inputSchema:ts.shape},async n=>{let r=await wI(n,e);return m(r)})}var ns="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 bI(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 xp(t,e){return t.registerTool("project_complete_describe",{description:ns,inputSchema:ts.shape},async n=>{let r=await bI(n,e);return m(r)})}function ge(t,e,n){let r={};for(let[s,i]of Object.entries(e)){if(!t.includes(i))throw new TypeError(`aliasedEnum: alias '${s}' points at '${i}' which is not in canonical set ${JSON.stringify(t)}`);r[s.toLowerCase()]=i;}let o=Object.entries(e).map(([s,i])=>`'${s}' \u2192 ${i}`).join(", "),a=o?`${n} Accepts: ${o}.`:n;return z$1.preprocess(s=>typeof s!="string"?s:r[s.toLowerCase()]??s,z$1.enum(t)).describe(a)}function SI(t,e={}){return {kind:"missing-detail",reason:t,...e}}function rs(t,e={}){return {kind:"next-natural-step",reason:t,...e}}function jI(t,e={}){return {kind:"consider-alternative",reason:t,...e}}function _I(t,e=3){return t.length<=e?t:[...t].sort((r,o)=>{let a=(r.severity??"info")==="warn"?0:1,s=(o.severity??"info")==="warn"?0:1;return a-s}).slice(0,e)}function OI(t){return process.env.OMNIFOCUS_HINT_LEVEL==="warn"?t.filter(e=>(e.severity??"info")==="warn"):t}function Kt(t,e=3){let n=OI(t),r=_I(n,e);return r.length>0?r:void 0}var xI=/\b(daily|weekly|monthly|every\s+(day|week|month|monday|tuesday|wednesday|thursday|friday|saturday|sunday|weekday|weekend))\b/i;function Pp(t,e){if(xI.test(e))return rs("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 Dp(t,e,n){if(!(e===void 0||n!==void 0))return SI("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 Rp(t,e=5){if(!(t<e))return jI(`Inbox now has ${t} unrouted tasks \u2014 consider triaging to keep your inbox clear.`,{suggestedTool:"task_list",suggestedArgs:{inbox:true},severity:"info"})}function Cp(t,e){if(e===void 0)return rs("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 Fp(t,e){return rs(`Project '${e}' now has no remaining tasks \u2014 consider completing or reviewing the project.`,{suggestedTool:"project_complete",suggestedArgs:{id:t},severity:"info"})}var os=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();}},Ep=new WeakMap;function AI(t){let e=Ep.get(t);return e||(e=new Map,Ep.set(t,e)),e}async function he(t,e,n){if(e===void 0)return n();let r=t.get(e);if(r)return Mp(r);let o=AI(t),a=o.get(e);if(a){let i=await a;return Mp(i)}let s=(async()=>{let i=await n();return t.set(e,i),i})();o.set(e,s);try{return await s}finally{o.delete(e);}}function Mp(t){return {...t,meta:{...t.meta,idempotentReplay:true}}}function Np(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 le=new os({ttlMs:Np("OMNIFOCUS_IDEMPOTENCY_TTL_MS",6e5),maxEntries:Np("OMNIFOCUS_IDEMPOTENCY_MAX_ENTRIES",1024)});var ss='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 })',is=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("Project name. Required, must be non-empty."),folderId:$.schema.optional().describe("Folder ID to place the project in. Omit for root."),note:z$1.string().max(1048576,"max 1 MB").optional().describe("Plain-text note for the project."),status:ge(["active","on-hold"],{paused:"on-hold"},"Initial project status. Default: active.").optional(),completionCriterion:ge(["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$1.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with UTC offset."),deferDateFloating:z$1.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z$1.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with UTC offset."),dueDateFloating:z$1.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z$1.number().int().min(1).optional().describe("Estimated total duration in minutes."),flagged:z$1.boolean().optional().describe("Flag the project."),tagIds:z$1.array(b.schema).optional().describe("Tag IDs to apply to the project."),reviewIntervalDays:z$1.number().int().min(1).optional().describe("Review interval in days. Omit to use OmniFocus default."),idempotency_key:z$1.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 PI(t,e){let n=e.idempotencyStore??le,r=e.replayStore??Ae;if(t.idempotency_key!==void 0&&n.get(t.idempotency_key)!==void 0)return as(t,e,n);let o=[];try{o=(await e.adapter.listProjects()).filter(s=>s.name.toLowerCase()===t.name.toLowerCase()&&s.status!=="dropped");}catch{}if(o.length>0){let a=e.makeMeta(),s=o[0],i=[`Use existing project "${s.name}" (id: ${s.id})`,"Create a new project with the same name"],c=r.register(i,async l=>l===0?p({created:false,id:s.id,existing:true},e.makeMeta()):as(t,e,n));return Wt(`A project named "${t.name}" already exists. What should happen?`,c,a,i.map((l,d)=>({index:d,label:l})),{name:t.name})}return as(t,e,n)}async function as(t,e,n){return he(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=Kt([Cp(o,t.reviewIntervalDays)].filter(s=>s!=null));return p({created:true,id:o},e.makeMeta({syncPending:true,humanReadableSummary:yl(t.name)}),void 0,a)})}function Up(t,e){return t.registerTool("project_create",{description:ss,inputSchema:is.shape},async n=>{let r=await PI(n,e);return m(r)})}var cs="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 DI(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.folderId!==void 0){let a=await pt(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 ${Pe(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${Pe(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(s=>Se(e.adapter,s)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`tagged ${a.map(s=>`'${s}'`).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 Lp(t,e){return t.registerTool("project_create_describe",{description:cs,inputSchema:is.shape},async n=>{let r=await DI(n,e);return m(r)})}function mt(t,e,n){if(t===void 0)return;let r=Date.parse(t);if(Number.isNaN(r))throw new k(`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 k(`observed modifiedAt for ${n} is not a valid ISO-8601 timestamp.`,{details:{resource:n,observed:e}});if(r!==o)throw new it(`${n} was modified since expectedModifiedAt.`,{details:{resource:n,expected:t,observed:e}})}async function ze(t,e,n){if(t!==true)return n();let r=await e();return RI(r)}function RI(t){return {...t,meta:{...t.meta,dryRun:true,syncPending:false}}}var ds='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" })',ls=z$1.object({id:v.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$1.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$1.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$1.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 CI(t,e){let n=e.idempotencyStore??le;return he(n,t.idempotency_key,async()=>{let r=await e.adapter.getProject(t.id);mt(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:vl(r.name)})));return ze(t.dry_run,o,a)})}function Jp(t,e){return t.registerTool("project_delete",{description:ds,inputSchema:ls.shape},async n=>{let r=await CI(n,e);return m(r)})}var ps="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 FI(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 Bp(t,e){return t.registerTool("project_delete_describe",{description:ps,inputSchema:ls.shape},async n=>{let r=await FI(n,e);return m(r)})}var us='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" })',ms=z$1.object({id:v.schema.describe("Persistent ID of the project to drop.")});async function MI(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:Al()}))}function $p(t,e){return t.registerTool("project_drop",{description:us,inputSchema:ms.shape},async n=>{let r=await MI(n,e);return m(r)})}var fs="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 NI(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 Wp(t,e){return t.registerTool("project_drop_describe",{description:fs,inputSchema:ms.shape},async n=>{let r=await NI(n,e);return m(r)})}var ft=["name","note","noteHtml","folderId","tagIds","status","completionCriterion","deferDate","deferDateFloating","dueDate","dueDateFloating","estimatedMinutes","flagged","reviewIntervalDays","nextReviewDate","lastReviewDate","completed","completedAt","dropped","droppedAt","taskCount","completedTaskCount","createdAt","modifiedAt","_links"],Xt=new Set(ft);z$1.object({id:v.schema,name:z$1.string(),note:z$1.string().nullable(),noteHtml:z$1.string().nullable(),folderId:$.schema.nullable(),tagIds:z$1.array(b.schema),status:z$1.enum(["active","on-hold","done","dropped"]),completionCriterion:z$1.enum(["parallel","sequential","singleActions"]),deferDate:H().nullable(),deferDateFloating:z$1.boolean().optional(),dueDate:H().nullable(),dueDateFloating:z$1.boolean().optional(),estimatedMinutes:z$1.number().int().min(1).nullable(),flagged:z$1.boolean(),reviewIntervalDays:z$1.number().int().min(1).nullable(),nextReviewDate:H().nullable(),lastReviewDate:H().nullable(),completed:z$1.boolean(),completedAt:H().nullable(),dropped:z$1.boolean(),droppedAt:H().nullable(),taskCount:z$1.number().int().min(0),completedTaskCount:z$1.number().int().min(0),createdAt:H(),modifiedAt:H(),_links:Ql.optional()});var gs=`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 })`,UI=z$1.object({id:v.schema.describe("Persistent project ID. Get from project_list or search_query."),includeTaskTree:z$1.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."),verbose:z$1.boolean().optional().describe("When true, return the full unelided shape (project + tasks). Default: false \u2014 fields equal to their documented default are omitted from both. See docs/token-cost.md for the defaults table."),fields:z$1.array(z$1.string()).optional().describe("Restrict the returned project to this list of top-level fields (id is always returned). Omit for the full project shape. Empty array returns just id. Unknown names surface in meta.warnings.WARN_UNKNOWN_FIELDS. Note: only the project record is projected \u2014 attached tasks keep their full shape.")});async function LI(t,e){let n=await e.projectService.get({id:t.id,...t.includeTaskTree!==void 0?{includeTaskTree:t.includeTaskTree}:{}}),r=We(n.project.note),o=t.fields!==void 0?ee(t.fields,Xt):void 0,a=o?.valid,s=o!==void 0&&o.unknown.length>0?[Q([...o.unknown],ft)]:void 0,i=X(n.project,a),l=t.verbose!==true&&a===void 0?_e(i,pr):i,d=n.tasks===void 0?void 0:t.verbose===true?n.tasks:He(n.tasks,Qe),u=e.makeMeta({cacheHit:n.cacheHit,...s!==void 0?{warnings:s}:{}}),f={project:l};return d!==void 0&&(f.tasks=d),r!==void 0&&(f.decision=r),p(f,u)}function Hp(t,e){return t.registerTool("project_get",{description:gs,inputSchema:UI.shape},async n=>{let r=await LI(n,e);return m(r)})}var hs='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"] })',Ir=100,JI=z$1.object({ids:z$1.array(v.schema).min(0).max(Ir).describe(`Array of project IDs to fetch (0..${Ir}). Get IDs from project_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`),fields:z$1.array(z$1.string()).optional().describe(`Restrict each returned project to this list of top-level fields (id is always returned). Omit for the full project shape. Empty array returns just id. Unknown names are dropped silently and surface in meta.warnings.WARN_UNKNOWN_FIELDS. Allowed: ${ft.join(", ")}.`)});async function BI(t,e){if(t.ids.length===0)return p({projects:[]},e.makeMeta());if(t.ids.length>Ir)throw new k(`ids array exceeds the maximum batch size of ${Ir} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getProjectsMany(t.ids),r=n.filter(f=>f!==null),o=t.ids.filter((f,g)=>n[g]===null),a={};for(let f of r){let g=We(f.note);g!==void 0&&(a[f.id]=g);}let s=Object.keys(a).length>0,i=t.fields!==void 0?ee(t.fields,Xt):void 0,c=i?.valid,l=r.map(f=>X(f,c)),d=[];o.length>0&&d.push($t(o)),i!==void 0&&i.unknown.length>0&&d.push(Q([...i.unknown],ft));let u=e.makeMeta({...d.length>0?{warnings:d}:{}});return p({projects:l,...s&&{decisions:a}},u)}function zp(t,e){return t.registerTool("project_get_many",{description:hs,inputSchema:JI.shape},async n=>{let r=await BI(n,e);return m(r)})}var ys='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" })',$I=z$1.object({folderId:$.schema.optional().describe("Restrict to projects inside this folder. Get the ID from folder_list. Omit for all folders."),status:ge(["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$1.boolean().optional().describe("true = flagged only; false = unflagged only; omit = both."),reviewDueBefore:z$1.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$1.number().int().min(1).max(1e3).optional().describe("Max projects per page (1..1000). Default 50. Use `cursor` to fetch subsequent pages."),cursor:z$1.string().optional().describe("Opaque cursor from a previous project_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError."),verbose:z$1.boolean().optional().describe("When true, return the full unelided project shape. Default: false \u2014 fields equal to their documented default (status: 'active', completionCriterion: 'parallel', flagged: false, tagIds: [], note: null, etc.) are omitted. See docs/token-cost.md for the defaults table."),fields:z$1.array(z$1.string()).optional().describe("Restrict each returned project to this list of top-level fields (id is always returned). Omit for the full project shape. Empty array returns just id. Unknown names surface in meta.warnings.WARN_UNKNOWN_FIELDS.")});async function WI(t,e){let{verbose:n,fields:r,...o}=t,a=await e.projectService.list(o),s={cursor:a.nextCursor,hasMore:a.hasMore},i=r!==void 0?ee(r,Xt):void 0,c=i?.valid,l=i!==void 0&&i.unknown.length>0?[Q([...i.unknown],ft)]:void 0,d=n!==true&&c===void 0,u=a.projects.map(g=>{let I=X(g,c);return d?_e(I,pr):I}),f=e.makeMeta({cacheHit:a.cacheHit,...l!==void 0?{warnings:l}:{}});return p({projects:u},f,s)}function Vp(t,e){return t.registerTool("project_list",{description:ys,inputSchema:$I.shape},async n=>{let r=await WI(n,e);return m(r)})}var ks=`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 })`,vs=z$1.object({id:v.schema.describe("Persistent ID of the project to move."),folderId:$.schema.nullable().describe("Target folder ID, or null to move to root.")});async function zI(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:Pl(t.folderId!=null?"folder":"library root")}))}function qp(t,e){return t.registerTool("project_move",{description:ks,inputSchema:vs.shape},async n=>{let r=await zI(n,e);return m(r)})}var Is="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 VI(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 s=await pt(e.adapter,t.folderId);n.push({field:"folderId",newValue:t.folderId}),o=`folder '${s}'`;}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 Gp(t,e){return t.registerTool("project_move_describe",{description:Is,inputSchema:vs.shape},async n=>{let r=await VI(n,e);return m(r)})}var Ts="project-template",qI=z$1.object({name:z$1.string().min(1),parameterNames:z$1.array(z$1.string().min(1)),capturedAt:H()});function Yt(t){let e=Ze(t,Ts);if(e===void 0)return;let n=Ut(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=qI.safeParse(r);return o.success?o.data:void 0}function Kp(t,e){let n=Lt({name:t.name,parameters:t.parameterNames.length>0?t.parameterNames.join(","):void 0,capturedAt:t.capturedAt}),r=Jt(null,Ts,n);return e.length===0?r:`${r}
|
|
210
210
|
|
|
211
|
-
${e}`}function
|
|
211
|
+
${e}`}function Xp(t){let e=Ze(t,Ts);return e===void 0||t===null?"":t.slice(e.end).replace(/^\n+/,"")}function Yp(t,e){return t.replace(/\{\{\s*([A-Za-z0-9_-]+)\s*\}\}/g,(n,r)=>Object.hasOwn(e,r)?e[r]:n)}function Zp(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 Qp(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 s=i=>{let c=Date.UTC(Number(i.slice(0,4)),Number(i.slice(5,7))-1,Number(i.slice(8,10)))+a,l=new Date(c),d=l.getUTCFullYear().toString().padStart(4,"0"),u=(l.getUTCMonth()+1).toString().padStart(2,"0"),f=l.getUTCDate().toString().padStart(2,"0");return `${d}-${u}-${f}`};return t.replace(/@(due|defer)\((\d{4}-\d{2}-\d{2})\)/g,(i,c,l)=>`@${c}(${s(l)})`)}async function wr(t,e){let n=await t.listTasks({projectId:e}),r=[...n],o=[...n];for(;;){let a=o.shift();if(a===void 0)break;let s=await t.listTasks({parentId:a.id});for(let i of s)r.push(i),o.push(i);}return r}function Zt(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 s=String(o.parentId),i=r.get(s);i?i.push(o):r.set(s,[o]);}return {rootTasks:n,byParent:r}}function Je(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}function eu(t,e,n){let r=[`text="${Je(t.name)}"`,'type="omnifocus:task"',`id="${Je(String(t.id))}"`];t.dueDate&&r.push(`due="${Je(t.dueDate)}"`),t.deferDate&&r.push(`defer="${Je(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="${Je(t.note)}"`);let o=e.get(String(t.id))??[];if(o.length===0)return `${n}<outline ${r.join(" ")} />`;let a=`${n} `,s=o.map(i=>eu(i,e,a)).join(`
|
|
212
212
|
`);return `${n}<outline ${r.join(" ")}>
|
|
213
|
-
${i}
|
|
214
|
-
${n}</outline>`}function Cp(t,e,n){let{rootTasks:r,byParent:o}=Ut(e),a=[`text="${De(t.name)}"`,'type="omnifocus:project"',`id="${De(String(t.id))}"`,`status="${De(t.status)}"`];t.dueDate&&a.push(`due="${De(t.dueDate)}"`),t.deferDate&&a.push(`defer="${De(t.deferDate)}"`),t.flagged&&a.push('flagged="true"'),t.note&&a.push(`note="${De(t.note)}"`);let i=`${n} `;if(r.length===0)return `${n}<outline ${a.join(" ")} />`;let s=r.map(c=>Rp(c,o,i)).join(`
|
|
215
|
-
`);return `${n}<outline ${a.join(" ")}>
|
|
216
213
|
${s}
|
|
217
|
-
${n}</outline>`}function
|
|
218
|
-
`)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
`))o.push(`
|
|
222
|
-
`),
|
|
223
|
-
`)
|
|
224
|
-
`))
|
|
225
|
-
`),f={name:t.templateName,parameterNames:t.parameterNames??[],capturedAt:new Date().toISOString()},g=xp(f,m),I=await e.adapter.createProject({name:t.templateName,folderId:n,note:g});return e.cache!==void 0&&B(e.cache,{projectId:I}),p({templateId:I,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:ns,inputSchema:FI.shape},async n=>{let r=await MI(n,e);return u(r)})}var rs='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 })',os=z.object({id:v.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:ce(["active","on-hold"],{paused:"on-hold"},"Project status. Use project_complete or project_drop to close a project.").optional(),completionCriterion:ce(["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(b.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 NI(t,e){let{id:n,...r}=t,o=e.idempotencyStore??le;return de(o,t.idempotency_key,async()=>{let a=await e.adapter.getProject(n);et(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&&B(e.cache,{projectId:n}),p({updated:true,id:n,name:s},e.makeMeta({syncPending:true,humanReadableSummary:Yd(a.name)})));return Fe(t.dry_run,c,d)})}function Wp(t,e){return t.registerTool("project_update",{description:rs,inputSchema:os.shape},async n=>{let r=await NI(n,e);return u(r)})}var as="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 UI(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 ${be(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 ${be(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 Hp(t,e){return t.registerTool("project_update_describe",{description:as,inputSchema:os.shape},async n=>{let r=await UI(n,e);return u(r)})}var is="\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()); }\" })",LI=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 JI(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??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 Vp(t,e,n){return n.allowRawScript?t.registerTool("run_jxa_script",{description:is,inputSchema:LI.shape},async r=>{let o=await JI(r,e);return u(o)}):null}var ds='\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" })',BI=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 $I(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??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 zp(t,e,n){return n.allowRawScript?t.registerTool("run_omnijs_script",{description:ds,inputSchema:BI.shape},async r=>{let o=await $I(r,e);return u(o)}):null}var WI=[{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"}],ar=new Map(WI.flatMap(({aliases:t,weekday:e})=>t.map(n=>[n,e]))),HI=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]]),VI=new Map([["first",1],["1st",1],["second",2],["2nd",2],["third",3],["3rd",3],["fourth",4],["4th",4],["last","last"],["final","last"]]);function zI(t){return t.toLowerCase().replace(/\s+/g," ").trim()}function ps(t){if(!t)return null;let e=Number.parseInt(t,10);if(Number.isFinite(e)&&e>=1)return e;let n=HI.get(t);return n!==void 0?n:null}var qp=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];function Gp(t){return [...t].sort((e,n)=>qp.indexOf(e)-qp.indexOf(n))}function qI(t){if(t.length===0)return "";if(t.length===7)return "every day";let e=Gp(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 GI(t){return t==="last"?"last":["first","second","third","fourth"][t-1]??String(t)}function KI(t){return t==="start-again"?"after I complete it":t==="due-again"?"from the due date":""}function ls(t,e){let n;if(t.weekdays&&t.weekdays.length>0)n=qI(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 ${GI(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=KI(t.method);return [n,e,r].filter(a=>a.length>0).join(", ")}function XI(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 YI(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 ZI(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=ps(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=ps(r[1]);if(o!==null)return `${o} times`}return ""}function QI(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=VI.get(e[1]??""),s=ar.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=ps(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&&ar.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=ar.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 Kp(t){if(!t?.trim())return {kind:"error",reason:"no-repetition-detected"};let e=zI(t),n=XI(e),r=n.consumed?e.replace(n.consumed," ").replace(/\s+/g," ").trim():e,o=YI(r),a=ZI(r),i=QI(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:Gp(i.rule.weekdays)}:{},...i.rule.monthlyAnchor?{monthlyAnchor:i.rule.monthlyAnchor}:{}},c=r.match(/\bevery other\s+([a-z]+)\b/);if(c){let l=ar.get(c[1]??"");if(l){let m=[o,a].filter(Boolean).join(", ");return {kind:"ambiguous",interpretations:[{rule:s,description:ls(s,m)},{rule:{method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:l,position:1}},description:ls({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:ls(s,d)}}var us=`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" })`,eT=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 tT(t,e){let n=Kp(t.prose),r=e.makeMeta();if(n.kind!=="ambiguous")return p(n,r);let o=e.replayStore??we,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 Pt(`"${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 Xp(t,e){return t.registerTool("repetition_from_prose",{description:us,inputSchema:eT.shape},async n=>{let r=await tT(n,e);return u(r)})}var ms="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()",rT=z.object({});async function oT(t,e){let n=await e.reviewService.listDue(),r=e.makeMeta({cacheHit:n.cacheHit});return p({projects:n.projects},r)}function Yp(t,e){return t.registerTool("review_list_due",{description:ms,inputSchema:rT.shape},async n=>{let r=await oT(n,e);return u(r)})}var fs=`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" })`,aT=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function sT(t,e){let n=await e.reviewService.markReviewed(v.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:qn()}))}function Qp(t,e){return t.registerTool("review_mark_reviewed",{description:fs,inputSchema:aT.shape},async n=>{let r=await sT(n,e);return u(r)})}var gs=`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" })`,iT=z.object({id:z.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function cT(t,e){let n=await e.reviewService.markReviewed(v.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:qn()}))}function tu(t,e){return t.registerTool("project_mark_reviewed",{description:gs,inputSchema:iT.shape},async n=>{let r=await cT(n,e);return u(r)})}var ys=`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 })`,dT=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 lT(t,e){let n=await e.reviewService.setInterval(v.of(t.id),t.days);return p({id:t.id,name:n.name,reviewIntervalDays:n.reviewIntervalDays},e.makeMeta({syncPending:true,humanReadableSummary:ul(t.days)}))}function nu(t,e){return t.registerTool("review_set_interval",{description:ys,inputSchema:dT.shape},async n=>{let r=await lT(n,e);return u(r)})}var ks=`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 })`,pT=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 uT(t,e){let n=await e.reviewService.setNextReviewDate(v.of(t.projectId),t.nextReviewDate);return p({id:t.projectId,name:n.name,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:ml(t.nextReviewDate)}))}function ru(t,e){return t.registerTool("project_set_next_review_date",{description:ks,inputSchema:pT.shape},async n=>{let r=await uT(n,e);return u(r)})}var vs=`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" })`,mT=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:v.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list."),tagIds:z.array(b.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 fT(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 ou(t,e){return t.registerTool("search_query",{description:vs,inputSchema:mT.shape},async n=>{let r=await fT(n,e);return u(r)})}var Is="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()",hT=z.object({});async function yT(t,e){let n=await e.adapter.getLastSync();return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},e.makeMeta())}function au(t,e){return t.registerTool("sync_status",{description:Is,inputSchema:hT.shape},async n=>{let r=await yT(n,e);return u(r)})}var Ts="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()",vT=z.object({});async function IT(t,e){let n=await e.adapter.syncTrigger();e.cache!==void 0&&Td(e.cache);let r=e.makeMeta({syncPending:false});return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},r)}function su(t,e){return t.registerTool("sync_trigger",{description:Ts,inputSchema:vT.shape},async n=>{let r=await IT(n,e);return u(r)})}var bs=`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" })`,Ss=z.object({name:z.string().min(1).describe("Tag name. Must be non-empty."),parentId:b.schema.optional().describe("Parent tag ID to nest under. Omit for a root tag. Get from tag_list."),status:ce(["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:tl(t.name)});return p({tag:r},o)}function iu(t,e){return t.registerTool("tag_create",{description:bs,inputSchema:Ss.shape},async n=>{let r=await TT(n,e);return u(r)})}var js="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 wT(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 cu(t,e){return t.registerTool("tag_create_describe",{description:js,inputSchema:Ss.shape},async n=>{let r=await wT(n,e);return u(r)})}var _s=`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" })`,xs=z.object({id:b.schema.describe("Persistent tag ID to delete. Get from tag_list.")});async function ST(t,e){return await e.tagService.delete(t.id),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:ll()}))}function du(t,e){return t.registerTool("tag_delete",{description:_s,inputSchema:xs.shape},async n=>{let r=await ST(n,e);return u(r)})}var Os="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 jT(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 lu(t,e){return t.registerTool("tag_delete_describe",{description:Os,inputSchema:xs.shape},async n=>{let r=await jT(n,e);return u(r)})}var Ps='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" })',_T=z.object({id:b.schema.describe("Persistent tag ID. Get from tag_list. IDs are stable across renames."),verbose:z.boolean().optional().describe("When true, return the full unelided tag shape. Default: false \u2014 fields equal to their documented default are omitted. See docs/token-cost.md for the defaults table.")});async function xT(t,e){let n=await e.tagService.get(t.id),r=e.makeMeta({cacheHit:n.cacheHit}),o=t.verbose===true?n.tag:$e(n.tag,Kn);return p({tag:o},r)}function uu(t,e){return t.registerTool("tag_get",{description:Ps,inputSchema:_T.shape},async n=>{let r=await xT(n,e);return u(r)})}var Ds='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" })',PT=z.object({id:b.schema.describe("Persistent tag ID. Get from tag_list.")});async function DT(t,e){let n=await e.tagService.getLocation(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({location:n.location},r)}function mu(t,e){return t.registerTool("tag_get_location",{description:Ds,inputSchema:PT.shape},async n=>{let r=await DT(n,e);return u(r)})}var As='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"] })',ir=100,AT=z.object({ids:z.array(b.schema).min(0).max(ir).describe(`Array of tag IDs to fetch (0..${ir}). Get IDs from tag_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`)});async function RT(t,e){if(t.ids.length===0)return p({tags:[]},e.makeMeta());if(t.ids.length>ir)throw new y(`ids array exceeds the maximum batch size of ${ir} (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?[Ot(o)]:void 0,i=e.makeMeta({...a!==void 0?{warnings:a}:{}});return p({tags:r},i)}function gu(t,e){return t.registerTool("tag_get_many",{description:As,inputSchema:AT.shape},async n=>{let r=await RT(n,e);return u(r)})}var Rs='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" })',CT=z.object({parentId:b.schema.optional().describe("Return only direct children of this tag. Get the ID from a previous tag_list call. Omit for root tags."),status:ce(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"Filter by tag status. Omit to return tags of all statuses.").optional(),verbose:z.boolean().optional().describe("When true, return the full unelided tag shape. Default: false \u2014 fields equal to their documented default (status: 'active', parentId: null, location: null, allowsNextAction: true) are omitted. See docs/token-cost.md for the defaults table.")});async function FT(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}),a=t.verbose===true?r.tags:ye(r.tags,Kn);return p({tags:a},o)}function yu(t,e){return t.registerTool("tag_list",{description:Rs,inputSchema:CT.shape},async n=>{let r=await FT(n,e);return u(r)})}var Cs=`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 })`,Fs=z.object({id:b.schema.describe("Persistent ID of the tag to move. Get from tag_list."),parentId:b.schema.nullable().describe("New parent tag ID, or null to promote the tag to root level.")});async function MT(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:nl(n.name,"root")}))}function ku(t,e){return t.registerTool("tag_move",{description:Cs,inputSchema:Fs.shape},async n=>{let r=await MT(n,e);return u(r)})}var Es="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 NT(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 vu(t,e){return t.registerTool("tag_move_describe",{description:Es,inputSchema:Fs.shape},async n=>{let r=await NT(n,e);return u(r)})}var Ms='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 })',UT=z.object({id:b.schema.describe("Persistent tag ID. Get from tag_list."),allowsNextAction:z.boolean().describe("true to enable next-action selection; false to disable.")});async function LT(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:Ze(n.name)}))}function Tu(t,e){return t.registerTool("tag_set_allows_next_action",{description:Ms,inputSchema:UT.shape},async n=>{let r=await LT(n,e);return u(r)})}var Ns='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" })',JT=z.object({id:b.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 BT(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:Ze(n.name)}))}function wu(t,e){return t.registerTool("tag_set_location",{description:Ns,inputSchema:JT.shape},async n=>{let r=await BT(n,e);return u(r)})}var Us='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" })',WT=z.object({id:b.schema.describe("Persistent tag ID. Get from tag_list."),status:ce(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status for the tag.")});async function HT(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:Ze(n.name)}))}function bu(t,e){return t.registerTool("tag_set_status",{description:Us,inputSchema:WT.shape},async n=>{let r=await HT(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" })',Bs=z.object({id:b.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:b.schema.nullable().optional().describe("New parent tag ID. Pass null to promote to root. Get from tag_list."),status:ce(["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 VT(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:Ze(o.name)}))}function Su(t,e){return t.registerTool("tag_update",{description:Js,inputSchema:Bs.shape},async n=>{let r=await VT(n,e);return u(r)})}var $s="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 zT(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 ju(t,e){return t.registerTool("tag_update_describe",{description:$s,inputSchema:Bs.shape},async n=>{let r=await zT(n,e);return u(r)})}var Ws=`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"] }] })`,qT=z.object({taskId:h.schema.describe("Persistent task ID."),projectId:v.schema.optional().describe("If set, move the task to this project before applying other changes."),addTagIds:z.array(b.schema).optional().describe("Tag IDs to add. Combined with removeTagIds via current-tagIds pre-read."),removeTagIds:z.array(b.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"}),GT=z.object({assignments:z.array(qT).min(1).describe("Triage assignments \u2014 one per task. Must contain at least one item.")});function KT(t){return t.map((e,n)=>e.projectId!==void 0?n:-1).filter(e=>e>=0)}function XT(t){return t.map((e,n)=>e.addTagIds!==void 0||e.removeTagIds!==void 0?n:-1).filter(e=>e>=0)}function Hs(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=>b.of(o))}function YT(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=Hs(e??[],t.addTagIds,t.removeTagIds)),Object.keys(n).length>0?n:null}async function Vs(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=XT(n),s=new Map;if(i.length>0){let T=i.map(_=>n[_].taskId),D=await e.adapter.getTasksMany(T);for(let _=0;_<i.length;_++){let L=D[_];L&&s.set(i[_],L.tagIds);}}let c=KT(n),d=c.length>0?await e.adapter.batchMoveTasks(c.map(T=>({id:n[T].taskId,destination:{projectId:n[T].projectId}}))):{succeeded:[],failed:[]},l=new Map;for(let T of d.succeeded){let D=c[T.index];l.set(D,"ok");}for(let T of d.failed){let D=c[T.index];l.set(D,{errorCode:T.errorCode,message:T.message});}let m=[];for(let T=0;T<n.length;T++){let D=n[T],_=l.get(T);if(_!==void 0&&_!=="ok")continue;let L=YT(D,s.get(T));L!==null&&m.push({origIdx:T,id:D.taskId,patch:L});}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)),I=new Map;for(let T of f.failed)I.set(T.index,{errorCode:T.errorCode,message:T.message});let k=[],w=[];for(let T=0;T<n.length;T++){let D=n[T],_=l.get(T);if(_!==void 0&&_!=="ok"){w.push({index:T,errorCode:`move:${_.errorCode}`,message:_.message});continue}let L=m.findIndex(x=>x.origIdx===T);if(L>=0){if(g.has(L))k.push({index:T,value:D.taskId});else if(I.has(L)){let x=I.get(L);w.push({index:T,errorCode:`update:${x.errorCode}`,message:x.message});}}else k.push({index:T,value:D.taskId});}if(e.cache!==void 0&&k.length>0)for(let T of k){let D=n[T.index];S(e.cache,{taskId:D.taskId,...D.projectId!==void 0&&{projectId:D.projectId}});}let R=k.map(T=>({index:T.index,value:{id:T.value,name:a.get(T.value)??""}}));return p({assigned:R,failed:w},e.makeMeta({syncPending:k.length>0}))}function _u(t,e){return t.registerTool("task_batch_assign",{description:Ws,inputSchema:GT.shape},async n=>{let r=await Vs(n,e);return u(r)})}var zs='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" }] })',ZT=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.")}),QT=z.object({items:z.array(ZT).min(1).describe("Array of { id, at? } items. Must contain at least one item.")});async function ew(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&&S(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:Hd(i.succeeded.length)}))}function xu(t,e){return t.registerTool("task_batch_complete",{description:zs,inputSchema:QT.shape},async n=>{let r=await ew(n,e);return u(r)})}var qs='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" }] })',tw=z.object({name:z.string().min(1).describe("Task name. Required, non-empty."),projectId:v.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(b.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"]}),Gs=z.object({items:z.array(tw).min(1).describe("Array of task inputs. Must contain at least one item.")});async function nw(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),S(e.cache,{projectId:c}));}a.size===0&&S(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:Dt(r.succeeded.length)}))}function Ou(t,e){return t.registerTool("task_batch_create",{description:qs,inputSchema:Gs.shape},async n=>{let r=await nw(n,e);return u(r)})}var Ks="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 rw(t,e){let n=[],r=[];for(let a of t.items){let i;a.projectId!==void 0?i=`project '${await Rt(e.adapter,a.projectId)}'`:a.parentTaskId!==void 0?i=`subtask of '${await Ct(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:Ks,inputSchema:Gs.shape},async n=>{let r=await rw(n,e);return u(r)})}var dr=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function lr(t,e){switch(t.kind){case "next-work-day":return ow(t.at??"morning",e);case "next-weekday":return aw(t.weekday,t.at??"morning",e);case "in-business-days":return sw(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 iw(e);case "explicit-with-skip-weekends":return cw(t.date)}}function ow(t,e){let n=new Date(e.now);for(n.setDate(n.getDate()+1);Xs(n);)n.setDate(n.getDate()+1);return Ys(n,Au(t,e)),{resolvedDeferDate:vn(n),reason:`next work ${t} (${dr[n.getDay()]} ${Ru(n)})`}}function aw(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),Ys(r,Au(e,n)),{resolvedDeferDate:vn(r),reason:`next ${dr[t]} ${e} (${pr(r)} ${Ru(r)})`}}function sw(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),Xs(n)||r--;return Ys(n,e.morningHour),{resolvedDeferDate:vn(n),reason:`${t} business day${t===1?"":"s"} from now (${dr[n.getDay()]} ${pr(n)})`}}function iw(t){let e=new Date(t.now.getFullYear(),t.now.getMonth()+1,1,0,0,0,0);return {resolvedDeferDate:vn(e),reason:`start of next month (${pr(e)})`}}function cw(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(;Xs(r);)r.setDate(r.getDate()+1),o=true;return {resolvedDeferDate:vn(r),reason:o?`${t} \u2192 snapped to ${dr[r.getDay()]} ${pr(r)} (skipped weekend)`:`${t} (no weekend skip needed)`}}function Xs(t){let e=t.getDay();return e===0||e===6}function Au(t,e){return t==="morning"?e.morningHour:e.afternoonHour}function Ys(t,e){t.setHours(e,0,0,0);}function Ae(t){return t<10?`0${t}`:String(t)}function pr(t){return `${t.getFullYear()}-${Ae(t.getMonth()+1)}-${Ae(t.getDate())}`}function Ru(t){return `${Ae(t.getHours())}:${Ae(t.getMinutes())}`}function vn(t){let e=-t.getTimezoneOffset(),n=e>=0?"+":"-",r=Math.abs(e),o=`${n}${Ae(Math.floor(r/60))}:${Ae(r%60)}`;return `${t.getFullYear()}-${Ae(t.getMonth()+1)}-${Ae(t.getDate())}T${Ae(t.getHours())}:${Ae(t.getMinutes())}:${Ae(t.getSeconds())}${o}`}var dw=9,lw=14;function ur(t=process.env){return {morningHour:Du(t.OMNIFOCUS_MORNING_HOUR,dw),afternoonHour:Du(t.OMNIFOCUS_AFTERNOON_HOUR,lw)}}function Du(t,e){if(t===void 0||t==="")return e;let n=Number(t);return !Number.isInteger(n)||n<0||n>23?e:n}var Zs="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' } }] })",Cu=z.enum(["morning","afternoon"]),pw=z.discriminatedUnion("kind",[z.object({kind:z.literal("next-work-day"),at:Cu.optional()}),z.object({kind:z.literal("next-weekday"),weekday:z.number().int().min(0).max(6),at:Cu.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)})]),uw=z.object({entries:z.array(z.object({taskId:h.schema,intent:pw})).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 mw(t,e){let n=e.idempotencyStore??le,r=e.now?e.now():new Date,o=e.hours??ur();return de(n,t.idempotency_key,async()=>{let a=[];for(let s of t.entries)try{let c=lr(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);S(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 Fu(t,e){return t.registerTool("task_batch_defer_smart",{description:Zs,inputSchema:uw.shape},async n=>{let r=await mw(n,e);return u(r)})}var Qs='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" }] })',fw=z.object({id:h.schema.describe("Persistent task ID.")}),gw=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(fw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function hw(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&&S(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:zd(a.succeeded.length)}))}function Eu(t,e){return t.registerTool("task_batch_delete",{description:Qs,inputSchema:gw.shape},async n=>{let r=await hw(n,e);return u(r)})}var ti='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" }] })',yw=z.object({id:h.schema.describe("Persistent task ID.")}),kw=z.object({items:z.array(yw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function vw(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&&S(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:qd(a.succeeded.length)}))}function Mu(t,e){return t.registerTool("task_batch_drop",{description:ti,inputSchema:kw.shape},async n=>{let r=await vw(n,e);return u(r)})}var ni='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" }] })',Iw=z.object({projectId:v.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"}),Tw=z.object({id:h.schema.describe("Persistent task ID."),destination:Iw.describe("Where to move the task. Provide projectId, parentId, or neither (inbox).")}),ww=z.object({items:z.array(Tw).min(1).describe("Array of { id, destination } items. Must contain at least one item.")});async function bw(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&&S(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:Kd(a.succeeded.length,"destination")}))}function Nu(t,e){return t.registerTool("task_batch_move",{description:ni,inputSchema:ww.shape},async n=>{let r=await bw(n,e);return u(r)})}var oi='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.")}),jw=z.object({items:z.array(Sw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function _w(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&&S(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:Vd(a.succeeded.length)}))}function Uu(t,e){return t.registerTool("task_batch_uncomplete",{description:oi,inputSchema:jw.shape},async n=>{let r=await _w(n,e);return u(r)})}var si='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" }] })',xw=z.object({id:h.schema.describe("Persistent task ID.")}),Ow=z.object({items:z.array(xw).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&&S(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:Gd(a.succeeded.length)}))}function Lu(t,e){return t.registerTool("task_batch_undrop",{description:si,inputSchema:Ow.shape},async n=>{let r=await Pw(n,e);return u(r)})}var ii=`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" } }] })`,Dw=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(b.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"}),Aw=z.object({id:h.schema.describe("Persistent task ID."),patch:Dw.describe("Fields to change. At least one field required.")}),ci=z.object({items:z.array(Aw).min(1).describe("Array of { id, patch } pairs. Must contain at least one item.")});async function Rw(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&&S(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:Wd(i.succeeded.length)}))}function Ju(t,e){return t.registerTool("task_batch_update",{description:ii,inputSchema:ci.shape},async n=>{let r=await Rw(n,e);return u(r)})}var di="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 Cw(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 Bu(t,e){return t.registerTool("task_batch_update_describe",{description:di,inputSchema:ci.shape},async n=>{let r=await Cw(n,e);return u(r)})}var li='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" })',Ew=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function Mw(t,e){await e.adapter.clearTaskAlarms(t.id);let n=await e.adapter.getTask(t.id);e.cache!==void 0&&S(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:$d(n.name)});return p({task:n},r)}function $u(t,e){return t.registerTool("task_clear_alarms",{description:li,inputSchema:Ew.shape},async n=>{let r=await Mw(n,e);return u(r)})}var pi='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" })',Uw=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function Lw(t,e){await e.adapter.updateTask(t.id,{repetition:null});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&S(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:Jd(n.name)});return p({task:n},r)}function Wu(t,e){return t.registerTool("task_clear_repetition",{description:pi,inputSchema:Uw.shape},async n=>{let r=await Lw(n,e);return u(r)})}var ui='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" })',mi=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 Jw(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??we,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=>Vu(t,n,e,d===0?s:[]));return Pt(`"${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 Vu(t,n,e,[])}async function Vu(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&&S(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=Et([pp(e.projectId,s.name)]);}}catch{}return p({done:true,id:t.id,name:e.name},n.makeMeta({syncPending:true,humanReadableSummary:Dd(e.name)}),void 0,a)}function zu(t,e){return t.registerTool("task_complete",{description:ui,inputSchema:mi.shape},async n=>{let r=await Jw(n,e);return u(r)})}var fi="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 Bw(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 qu(t,e){return t.registerTool("task_complete_describe",{description:fi,inputSchema:mi.shape},async n=>{let r=await Bw(n,e);return u(r)})}var gi=`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" })`,$w=z.object({id:h.schema.describe("Persistent ID of the task to promote."),folderId:$.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 Ww(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&&(S(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:Ud(r.name)}))}function Ku(t,e){return t.registerTool("task_convert_to_project",{description:gi,inputSchema:$w.shape},async n=>{let r=await Ww(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 Xu(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 Hw(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 Vw(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 Yu(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 Zu(t,e){let n=r=>e===void 0?void 0:Xu(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: ${Hw(r)}`,...r.length>0&&{examples:r.slice(0,3)}}]}case "invalid_format":{let r=Vw(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:Yu("min",Number(t.minimum),t.inclusive??true,t.origin)}];case "too_big":return [{field:Ee(t.path),sent:n(t.path),expected:Yu("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:Xu(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(...Zu(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 Qu(t,e){let n=[];for(let r of t.issues)n.push(...Zu(r,e));return n}function xe(t,e,n="Cross-field validation failed"){let r=t.safeParse(e);if(r.success)return r.data;let o=Qu(r.error,e);throw new y(n,{details:{failures:o}})}var hi='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" })',gr=z.object({name:z.string().min(1).describe("Task name. Required, must be non-empty."),projectId:v.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(b.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.")}),zw=gr.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 qw(t,e){xe(zw,t);let n=e.idempotencyStore??le;return de(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&&S(e.cache,{...t.projectId!==void 0&&{projectId:t.projectId}});let a=[ip(o,t.name),cp(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(dp(s.length));}catch{}let i=Et(a.filter(s=>s!=null));return p({id:o,name:t.name},e.makeMeta({syncPending:true,humanReadableSummary:Pd(t.name)}),void 0,i)})}function em(t,e){return t.registerTool("task_create",{description:hi,inputSchema:gr.shape},async n=>{let r=await qw(n,e);return u(r)})}var yi="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 Gw(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.projectId!==void 0){let a=await Rt(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 Ct(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 ${be(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${be(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 tm(t,e){return t.registerTool("task_create_describe",{description:yi,inputSchema:gr.shape},async n=>{let r=await Gw(n,e);return u(r)})}var ki="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' } })",nm=z.enum(["morning","afternoon"]),Kw=z.discriminatedUnion("kind",[z.object({kind:z.literal("next-work-day"),at:nm.optional()}),z.object({kind:z.literal("next-weekday"),weekday:z.number().int().min(0).max(6),at:nm.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)})]),Xw=z.object({taskId:h.schema.describe("ID of the task to defer."),intent:Kw.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 Yw(t,e){let n=e.idempotencyStore??le,r=e.now?e.now():new Date,o=e.hours??ur();return de(n,t.idempotency_key,async()=>{let a=lr(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&&S(e.cache,{taskId:t.taskId,projectId:c.projectId}),p({taskId:t.taskId,resolvedDeferDate:a.resolvedDeferDate,reason:a.reason},e.makeMeta({syncPending:true}))};return Fe(t.dry_run,i,s)})}function rm(t,e){return t.registerTool("task_defer_smart",{description:ki,inputSchema:Xw.shape},async n=>{let r=await Yw(n,e);return u(r)})}var vi='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 })',Ii=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 Zw(t,e){let n=e.idempotencyStore??le;return de(n,t.idempotency_key,async()=>{let r=await e.adapter.getTask(t.id);et(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&&S(e.cache,{taskId:t.id,projectId:r.projectId}),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Rd(r.name)})));return Fe(t.dry_run,o,a)})}function om(t,e){return t.registerTool("task_delete",{description:vi,inputSchema:Ii.shape},async n=>{let r=await Zw(n,e);return u(r)})}var Ti="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 Qw(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 am(t,e){return t.registerTool("task_delete_describe",{description:Ti,inputSchema:Ii.shape},async n=>{let r=await Qw(n,e);return u(r)})}var wi='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" })',bi=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 eb(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&&S(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Cd(n.name)}))}function im(t,e){return t.registerTool("task_drop",{description:wi,inputSchema:bi.shape},async n=>{let r=await eb(n,e);return u(r)})}var Si="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 tb(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 cm(t,e){return t.registerTool("task_drop_describe",{description:Si,inputSchema:bi.shape},async n=>{let r=await tb(n,e);return u(r)})}var ji=`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" } })`,nb=z.union([z.object({projectId:v.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."),rb=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:nb.optional()}).describe("Duplicate options. `destination` overrides the default same-container placement.");async function ob(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&&(S(e.cache,{taskId:r,projectId:n.projectId}),t.destination!==void 0&&"projectId"in t.destination&&t.destination.projectId!==n.projectId&&S(e.cache,{projectId:t.destination.projectId})),p({duplicated:true,sourceId:t.id,newId:r,descendantCount:o,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Md(n.name)}))}function dm(t,e){return t.registerTool("task_duplicate",{description:ji,inputSchema:rb.shape},async n=>{let r=await ob(n,e);return u(r)})}var _i=[".png",".jpg",".jpeg",".heic",".heif",".gif",".webp",".pdf"];function pm(t){return _i.includes(extname(t).toLowerCase())}function sb(t){return t.mimeType!==null?t.mimeType.startsWith("image/")||t.mimeType==="application/pdf":pm(t.name)}var xi=`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 })`,lm=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.")}),ib=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 (${_i.join(",")}). Subject to the configured size cap.`).refine(pm,{message:`imagePath must use one of ${_i.join(",")}`})}).describe("Image read from a filesystem path."),z.object({kind:z.literal("attachment"),attachmentId:Pe.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:v.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."),cb=z.enum(["parent-task","each-task","none"]).default("parent-task").describe("Re-attachment mode after task creation."),um=z.object({source:ib,targetProjectId:v.schema.describe("Project that receives the captured tasks (and the wrapper, if `parent-task` mode)."),proposed:z.array(lm).min(1).describe("Agent-supplied extraction."),attachSourceTo:cb,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(lm).optional().describe("Required when dryRun=false. (Possibly-edited) confirmed tasks.")}),db=um.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 lb(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(!sb(r))throw new y(`Attachment is not an image: ${r.name}`,{details:{field:"source.attachmentId"}});return {kind:"attachment",attachment:r}}function pb(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 ub(t,e){xe(db,t);let n=await lb(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=>pb(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&&S(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 mm(t,e){return t.registerTool("task_extract_from_image",{description:xi,inputSchema:um.shape},async n=>{let r=await ub(n,e);return u(r)})}var mb=["add","build","call","check","create","draft","email","file","finish","fix","follow up","plan","prepare","research","review","schedule","send","set up","start","write"],fb=/^\s*(?:\(\d+\)|\d+[.)])\s+(.+)$/,gb=/^\s*(?:[-*•–—])\s+(.+)$/,hb=/^\s*(?:✅|⏰|📝|📌|✏️|📞|📧|📤|📩|🔔|🚨|⚡️|➡️|→)\s+(.+)$/u;function yb(t){return t.replace(/[\s.;,:]+$/,"").trim()}function kb(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 fm(t,e={}){let n=e.verbs??mb,r=kb(n),o=[],a=[],i=t.replace(/\r\n?/g,`
|
|
214
|
+
${n}</outline>`}function tu(t,e,n){let{rootTasks:r,byParent:o}=Zt(e),a=[`text="${Je(t.name)}"`,'type="omnifocus:project"',`id="${Je(String(t.id))}"`,`status="${Je(t.status)}"`];t.dueDate&&a.push(`due="${Je(t.dueDate)}"`),t.deferDate&&a.push(`defer="${Je(t.deferDate)}"`),t.flagged&&a.push('flagged="true"'),t.note&&a.push(`note="${Je(t.note)}"`);let s=`${n} `;if(r.length===0)return `${n}<outline ${a.join(" ")} />`;let i=r.map(c=>eu(c,o,s)).join(`
|
|
215
|
+
`);return `${n}<outline ${a.join(" ")}>
|
|
216
|
+
${i}
|
|
217
|
+
${n}</outline>`}function Fe(t,e){let r=new RegExp(`\\b${e}=(?:"([^"]*)"|'([^']*)')`,"i").exec(t);if(!r)return;let o=r[1]??r[2]??"";return GI(o)}function GI(t){return t.replace(/"/g,'"').replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&")}function KI(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 s=r[3];if(s){let i=(r[4]??"").trim(),c=(r[5]??"")==="/";e.push({kind:"open",tag:s.toLowerCase(),attrs:i,selfClose:c});}}return e}function nu(t,e,n){let o={text:Fe(n,"text")??"",children:[]},a=Fe(n,"type");a!==void 0&&(o.type=a);let s=Fe(n,"id");s!==void 0&&(o.id=s);let i=Fe(n,"due");i!==void 0&&(o.due=i);let c=Fe(n,"defer");c!==void 0&&(o.defer=c),Fe(n,"flagged")==="true"&&(o.flagged=true);let d=e;for(;d<t.length;){let u=t[d];if(!u)break;if(u.kind==="close"&&u.tag==="outline")return [o,d+1];if(u.kind==="open"&&u.tag==="outline")if(u.selfClose){let f=ru(u.attrs);o.children.push(f),d++;}else {let[f,g]=nu(t,d+1,u.attrs);o.children.push(f),d=g;}else d++;}return [o,d]}function ru(t){let n={text:Fe(t,"text")??"",children:[]},r=Fe(t,"type");r!==void 0&&(n.type=r);let o=Fe(t,"id");o!==void 0&&(n.id=o);let a=Fe(t,"due");a!==void 0&&(n.due=a);let s=Fe(t,"defer");return s!==void 0&&(n.defer=s),Fe(t,"flagged")==="true"&&(n.flagged=true),n}function ou(t){let e=t.trim();if(!/<opml\b/i.test(e))throw new k("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 k("Not valid OPML: missing <body> element.",{suggestion:"Provide valid OPML XML, e.g. as produced by export_opml."});let n=KI(e),r=-1;for(let s=0;s<n.length;s++){let i=n[s];if(i&&i.kind==="open"&&i.tag==="body"){r=s+1;break}}if(r===-1)throw new k("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 s=n[a];if(!s||s.kind==="close"&&s.tag==="body")break;if(s.kind==="open"&&s.tag==="outline")if(s.selfClose)o.push(ru(s.attrs)),a++;else {let[i,c]=nu(n,a+1,s.attrs);o.push(i),a=c;}else a++;}return {body:o}}function Rn(t,e,n,r,o){let a=" ".repeat(n),s=[];t.dueDate&&s.push(`@due(${t.dueDate.slice(0,10)})`),t.deferDate&&s.push(`@defer(${t.deferDate.slice(0,10)})`),t.flagged&&s.push("@flagged"),t.completed&&s.push("@done"),t.dropped&&s.push("@dropped");let i=s.length>0?` ${s.join(" ")}`:"";r.push(`${a}- ${t.name}${i}`);let c=t.note??(t.noteHtml?t.noteHtml.replace(/<[^>]*>/g,""):null);if(c){for(let d of c.split(`
|
|
218
|
+
`))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)Rn(d,e,n+1,r,o);}function su(t,e,n){let r=t,o,a,s=false,i=false,c=[];r=r.replace(/@due\(([^)]+)\)/g,(f,g)=>(o=au(g.trim(),e,n,"due"),"")),r=r.replace(/@defer\(([^)]+)\)/g,(f,g)=>(a=au(g.trim(),e,n,"defer"),"")),r=r.replace(/@flagged/g,()=>(s=true,"")),r=r.replace(/@done/g,()=>(i=true,"")),r=r.replace(/@dropped/g,()=>(i=true,"")),r=r.replace(/@([\w-]+)/g,(f,g)=>(g!=="due"&&g!=="defer"&&g!=="flagged"&&g!=="done"&&g!=="dropped"&&c.push(g),""));let l=r.split("//"),d=(l[0]??"").trim(),u=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:s,done:i,tagNames:c,note:u}}function au(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 iu(t){let e=0;for(let n of t)if(n===" ")e++;else break;return e}var Qt=class{adapter;constructor(e){this.adapter=e.adapter;}async exportOpml(e){let n=await this.resolveProjects(e),r=await Promise.all(n.map(i=>this.adapter.listTasks({projectId:i.id}).then(c=>({project:i,tasks:c})))),o=r.map(({project:i,tasks:c})=>tu(i,c," ")).join(`
|
|
219
|
+
`),a=r.reduce((i,{tasks:c})=>i+c.length,0);return {opml:['<?xml version="1.0" encoding="UTF-8"?>','<opml version="2.0">'," <head>"," <title>OmniFocus Export</title>"," </head>"," <body>",o," </body>","</opml>"].join(`
|
|
220
|
+
`),projectCount:n.length,taskCount:a}}async resolveProjects(e){if(e.kind==="all")return this.adapter.listProjects({status:"active"});if(e.kind==="folder"){let r=await this.adapter.listProjects({folderId:e.id});if(r.length===0)try{await this.adapter.getFolder(e.id);}catch{throw new _(`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:s,tasks:i}of await Promise.all(n.map(c=>wr(this.adapter,c.id).then(l=>({project:c,tasks:l}))))){let{rootTasks:c,byParent:l}=Zt(i);if(o.push(`${s.name}:`),s.note)for(let d of s.note.split(`
|
|
221
|
+
`))o.push(` ${d}`);for(let d of c)Rn(d,l,1,o,r);a+=i.length,o.push("");}return {taskpaper:o.join(`
|
|
222
|
+
`),projectCount:n.length,taskCount:a,warnings:r}}async importTaskPaper(e,n){if(!e.trim())throw new k("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 I=g.toLowerCase(),h=o.get(I);if(h)return h;let w=await this.adapter.createTag({name:g});return o.set(I,w),w},s=await this.adapter.listProjects(),i=new Map(s.map(g=>[g.name.toLowerCase(),g.id])),c=[],l=[],d=[],u=n,f=e.split(`
|
|
223
|
+
`);for(let g=0;g<f.length;g++){let I=f[g];if(!I)continue;let h=iu(I),w=I.trimStart();if(!w.startsWith("- ")&&!w.startsWith("- ")&&w.endsWith(":")&&h===0){if(!n){let M=w.slice(0,-1).trim(),N=i.get(M.toLowerCase());N?u=N:(l.push(`Project "${M}" not found in OmniFocus \u2014 tasks will land in inbox`),u=void 0);}d.length=0;continue}if(!w.startsWith("- ")&&!w.startsWith("- "))continue;for(;d.length>0&&(d[d.length-1]?.depth??0)>=h;)d.pop();let S=w.slice(2).trim(),T=su(S,g+1,l),x=[];for(let M of T.tagNames)try{x.push(await a(M));}catch{l.push(`Line ${g+1}: could not create tag "${M}" \u2014 skipped`);}let O=d[d.length-1],L={name:T.name,...O?{parentId:O.id}:{},...u&&!O?{projectId:u}:{},...T.dueDate?{dueDate:T.dueDate}:{},...T.deferDate?{deferDate:T.deferDate}:{},...T.flagged?{flagged:true}:{},...T.note?{note:T.note}:{},...x.length>0?{tagIds:x}:{}},A=await this.adapter.createTask(L);c.push(A),T.done&&await this.adapter.completeTask(A),d.push({depth:h,id:A});}return {created:c,warnings:l}}async importOpml(e,n={}){if(!e.trim())throw new k("opml is empty",{suggestion:"Provide a non-empty OPML XML string."});let r=ou(e),o=await this.adapter.listProjects(),a=new Map(o.map(l=>[String(l.id),l.id])),s=new Map(o.map(l=>[l.name.toLowerCase(),l.id])),i=[],c=async(l,d,u)=>{for(let f of l){let g={name:f.text||"(untitled)",...u!==void 0?{parentId:u}:d!==void 0?{projectId:d}:{},...f.due!==void 0?{dueDate:f.due}:{},...f.defer!==void 0?{deferDate:f.defer}:{},...f.flagged===true?{flagged:true}:{}},I=await this.adapter.createTask(g);i.push(I),f.children.length>0&&await c(f.children,d,I);}};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)??s.get(l.text.toLowerCase());await c(l.children,d,void 0);}else await c([l],void 0,void 0);}return {imported:i.length,taskIds:i}}};var bs='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" }.',XI=z$1.object({templateName:z$1.string().min(1).describe("Saved template to instantiate."),parameters:z$1.record(z$1.string(),z$1.string()).default({}).describe("Map of placeholder name \u2192 substitution value."),targetFolderId:$.schema.optional().describe("Folder to create the new project in. Defaults to the library root."),dueDate:z$1.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.")}),Cn=class extends _{constructor(e){super(`No template named "${e}" was found in the Templates folder.`,{suggestion:"List available templates with project_template_list, then retry with a valid name.",details:{templateName:e}});}},ws=class extends k{missing;constructor(e){super(`Template requires parameters not supplied: ${e.map(n=>`"${n}"`).join(", ")}.`,{suggestion:"Provide values for all required template parameters listed in `details.missing`.",details:{missing:e}}),this.missing=e;}};async function YI(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(I=>I.name.toLowerCase()===r);if(o===void 0)throw new Cn(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),s=t.templateName.toLowerCase(),i=a.find(I=>I.name.toLowerCase()===s);if(i===void 0)throw new Cn(t.templateName);let c=Yt(i.note);if(c===void 0)throw new Cn(t.templateName);let l=c.parameterNames.filter(I=>!Object.hasOwn(t.parameters,I));if(l.length>0)throw new ws(l);let d=Xp(i.note);if(d=Yp(d,t.parameters),t.dueDate!==void 0){let I=Zp(d);I!==void 0&&(d=Qp(d,I,t.dueDate));}let u=await e.adapter.createProject({name:t.templateName,...t.targetFolderId!==void 0&&{folderId:t.targetFolderId}}),g=await new Qt({adapter:e.adapter}).importTaskPaper(d,u);return e.cache!==void 0&&B(e.cache,{projectId:u}),p({projectId:u,taskCount:g.created.length,...g.warnings.length>0&&{importWarnings:g.warnings}},e.makeMeta({syncPending:true}))}function cu(t,e){return t.registerTool("project_template_instantiate",{description:bs,inputSchema:XI.shape},async n=>{let r=await YI(n,e);return m(r)})}var Ss="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.",QI=z$1.object({});async function eT(t,e){let n=await e.adapter.listFolders(),r=e.templatesFolderName.toLowerCase(),o=n.find(i=>i.name.toLowerCase()===r);if(o===void 0)return p({templates:[]},e.makeMeta());let a=await e.adapter.listProjects({folderId:o.id}),s=[];for(let i of a){let c=Yt(i.note);c!==void 0&&s.push({templateId:i.id,templateName:c.name,parameterNames:c.parameterNames,capturedAt:c.capturedAt});}return s.sort((i,c)=>i.capturedAt!==c.capturedAt?i.capturedAt<c.capturedAt?1:-1:i.templateName<c.templateName?-1:i.templateName>c.templateName?1:0),p({templates:s},e.makeMeta())}function du(t,e){return t.registerTool("project_template_list",{description:Ss,inputSchema:QI.shape},async n=>{let r=await eT(n,e);return m(r)})}var _s='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"] }.',tT=z$1.object({projectId:v.schema.describe("Source project to capture."),templateName:z$1.string().min(1).describe("Display name; must be unique within the Templates folder."),parameterNames:z$1.array(z$1.string().min(1)).optional().describe("Optional placeholder names for future _instantiate substitution.")}),js=class extends it{constructor(e){super(`A template named "${e}" already exists in the Templates folder.`,{suggestion:"Delete the existing template with project_template_delete first, or choose a different name.",details:{templateName:e}});}};async function nT(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 rT(t,e){await e.adapter.getProject(t.projectId);let n=await nT(e.adapter,e.templatesFolderName),r=await e.adapter.listProjects({folderId:n}),o=t.templateName.toLowerCase();if(r.some(h=>h.name.toLowerCase()===o))throw new js(t.templateName);let a=await e.adapter.getProject(t.projectId),s=await wr(e.adapter,t.projectId),{rootTasks:i,byParent:c}=Zt(s),l=[],d=[`${a.name}:`];if(a.note)for(let h of a.note.split(`
|
|
224
|
+
`))d.push(` ${h}`);for(let h of i)Rn(h,c,1,d,l);let u=d.join(`
|
|
225
|
+
`),f={name:t.templateName,parameterNames:t.parameterNames??[],capturedAt:new Date().toISOString()},g=Kp(f,u),I=await e.adapter.createProject({name:t.templateName,folderId:n,note:g});return e.cache!==void 0&&B(e.cache,{projectId:I}),p({templateId:I,templateName:t.templateName,capturedAt:f.capturedAt,...l.length>0&&{exportWarnings:l}},e.makeMeta({syncPending:true}))}function lu(t,e){return t.registerTool("project_template_save",{description:_s,inputSchema:tT.shape},async n=>{let r=await rT(n,e);return m(r)})}var Os='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 })',xs=z$1.object({id:v.schema.describe("Persistent project ID. Get from project_list or project_get."),name:z$1.string().min(1).max(1024,"max 1 KB").optional().describe("New project name. Must be non-empty if supplied."),note:z$1.string().max(1048576,"max 1 MB").nullable().optional().describe("Plain-text note. Pass null to clear."),noteHtml:z$1.string().nullable().optional().describe("HTML note. Pass null to clear. Prefer note for plain-text edits."),status:ge(["active","on-hold"],{paused:"on-hold"},"Project status. Use project_complete or project_drop to close a project.").optional(),completionCriterion:ge(["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$1.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),deferDateFloating:z$1.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z$1.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),dueDateFloating:z$1.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z$1.number().int().positive().nullable().optional().describe("Estimated total duration in minutes. Pass null to clear."),flagged:z$1.boolean().optional().describe("Flag or unflag the project."),tagIds:z$1.array(b.schema).optional().describe("Full-replacement tag list. Replaces all existing tags."),reviewIntervalDays:z$1.number().int().positive().nullable().optional().describe("Review interval in days. Pass null to clear."),expectedModifiedAt:z$1.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$1.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$1.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 oT(t,e){let{id:n,...r}=t,o=e.idempotencyStore??le;return he(o,t.idempotency_key,async()=>{let a=await e.adapter.getProject(n);mt(t.expectedModifiedAt,a.modifiedAt,`project:${n}`);let s={...r.name!==void 0&&{name:r.name},...r.note!==void 0&&{note:r.note},...r.noteHtml!==void 0&&{noteHtml:r.noteHtml},...r.status!==void 0&&{status:r.status},...r.completionCriterion!==void 0&&{completionCriterion:r.completionCriterion},...r.deferDate!==void 0&&{deferDate:r.deferDate},...r.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}},i=r.name??a.name,c=()=>p({updated:true,id:n,name:i},e.makeMeta({syncPending:false})),l=async()=>(await e.adapter.updateProject(n,s),e.cache!==void 0&&B(e.cache,{projectId:n}),p({updated:true,id:n,name:i},e.makeMeta({syncPending:true,humanReadableSummary:kl(a.name)})));return ze(t.dry_run,c,l)})}function pu(t,e){return t.registerTool("project_update",{description:Os,inputSchema:xs.shape},async n=>{let r=await oT(n,e);return m(r)})}var As="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 aT(t,e){let n=[],r=[],o=String(t.id);try{let s=await e.adapter.getProject(t.id);if(o=s.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:s.name}),r.push(`rename to '${t.name}'`)),t.status!==void 0&&(n.push({field:"status",newValue:t.status,oldValue:s.status}),r.push(`set status to '${t.status}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate,oldValue:s.dueDate??null}),r.push(t.dueDate===null?"clear due date":`set due date to ${Pe(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate,oldValue:s.deferDate??null}),r.push(t.deferDate===null?"clear defer date":`set defer date to ${Pe(t.deferDate)}`)),t.flagged!==void 0&&(n.push({field:"flagged",newValue:String(t.flagged),oldValue:String(s.flagged)}),r.push(t.flagged?"flag":"unflag")),t.tagIds!==void 0){let i=await Promise.all(t.tagIds.map(c=>Se(e.adapter,c)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`set tags to [${i.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 uu(t,e){return t.registerTool("project_update_describe",{description:As,inputSchema:xs.shape},async n=>{let r=await aT(n,e);return m(r)})}var Ds="\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()); }\" })",sT=z$1.object({script:z$1.string().min(1).describe("Raw JXA script body. Must define `function run(argv)` and return a JSON-encoded string."),arg:z$1.unknown().optional().describe("Optional JSON-serialisable argument passed to `run()` as argv[0]. Defaults to `{}`.")});async function iT(t,e){if(typeof e.adapter.runJxaScript!="function")throw new k("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 mu(t,e,n){return n.allowRawScript?t.registerTool("run_jxa_script",{description:Ds,inputSchema:sT.shape},async r=>{let o=await iT(r,e);return m(o)}):null}var Cs='\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" })',cT=z$1.object({script:z$1.string().min(1).describe("Raw OmniJS script body. Must produce a JSON-encodable result."),arg:z$1.unknown().optional().describe("Optional JSON-serialisable argument forwarded through the callback-file bridge. Defaults to `{}`.")});async function dT(t,e){if(typeof e.adapter.runOmniJsScript!="function")throw new k("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 fu(t,e,n){return n.allowRawScript?t.registerTool("run_omnijs_script",{description:Cs,inputSchema:cT.shape},async r=>{let o=await dT(r,e);return m(o)}):null}var lT=[{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"}],Sr=new Map(lT.flatMap(({aliases:t,weekday:e})=>t.map(n=>[n,e]))),pT=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]]),uT=new Map([["first",1],["1st",1],["second",2],["2nd",2],["third",3],["3rd",3],["fourth",4],["4th",4],["last","last"],["final","last"]]);function mT(t){return t.toLowerCase().replace(/\s+/g," ").trim()}function Es(t){if(!t)return null;let e=Number.parseInt(t,10);if(Number.isFinite(e)&&e>=1)return e;let n=pT.get(t);return n!==void 0?n:null}var gu=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];function hu(t){return [...t].sort((e,n)=>gu.indexOf(e)-gu.indexOf(n))}function fT(t){if(t.length===0)return "";if(t.length===7)return "every day";let e=hu(t),n=new Set(e),r=new Set(["monday","tuesday","wednesday","thursday","friday"]);if(n.size===5&&[...r].every(s=>n.has(s)))return "every weekday";if(n.size===2&&n.has("saturday")&&n.has("sunday"))return "every weekend";let o=s=>s.charAt(0).toUpperCase()+s.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 gT(t){return t==="last"?"last":["first","second","third","fourth"][t-1]??String(t)}function hT(t){return t==="start-again"?"after I complete it":t==="due-again"?"from the due date":""}function Fs(t,e){let n;if(t.weekdays&&t.weekdays.length>0)n=fT(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,s=i=>i.charAt(0).toUpperCase()+i.slice(1);n=`the ${gT(a.position)} ${s(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=hT(t.method);return [n,e,r].filter(a=>a.length>0).join(", ")}function yT(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 kT(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 vT(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=Es(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=Es(r[1]);if(o!==null)return `${o} times`}return ""}function IT(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 s=uT.get(e[1]??""),i=Sr.get(e[2]??"");if(s!==void 0&&i!==void 0)return {rule:{unit:"months",steps:1,monthlyAnchor:{weekday:i,position:s}}}}let n=t.match(/\bthe (\d{1,2})(?:st|nd|rd|th)?\s+of (every|each)?\s*month\b/);if(n){let s=Number.parseInt(n[1]??"",10);if(Number.isFinite(s)&&s>=1&&s<=31)return {rule:{unit:"months",steps:1,monthlyAnchor:{day:s}}}}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 s=Es(r[1]),i=`${r[2]}s`;if(s!==null&&s>=1)return {rule:{unit:i,steps:s}}}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 s=t.match(/\bevery other\s+([a-z]+)\b/),i=s&&Sr.get(s[1]??"");if(i)return {rule:{unit:"weeks",steps:2,weekdays:[i]}}}let a=t.match(/\bevery\s+([a-z, ]+?)(?=$|\s+(at|after|from|once|until|for))/);if(a){let i=(a[1]??"").replace(/\band\b/g,",").split(/[, ]+/).map(l=>l.trim()).filter(l=>l.length>0),c=[];for(let l of i){let d=Sr.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 yu(t){if(!t?.trim())return {kind:"error",reason:"no-repetition-detected"};let e=mT(t),n=yT(e),r=n.consumed?e.replace(n.consumed," ").replace(/\s+/g," ").trim():e,o=kT(r),a=vT(r),s=IT(r);if(!s)return {kind:"error",reason:"no-repetition-detected",suggestion:"Try a phrase like 'every Monday', 'weekly', 'every 3 days', or 'monthly'."};let i={method:n.method,unit:s.rule.unit,steps:s.rule.steps,...s.rule.weekdays?{weekdays:hu(s.rule.weekdays)}:{},...s.rule.monthlyAnchor?{monthlyAnchor:s.rule.monthlyAnchor}:{}},c=r.match(/\bevery other\s+([a-z]+)\b/);if(c){let d=Sr.get(c[1]??"");if(d){let u=[o,a].filter(Boolean).join(", ");return {kind:"ambiguous",interpretations:[{rule:i,description:Fs(i,u)},{rule:{method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:d,position:1}},description:Fs({method:n.method,unit:"months",steps:1,monthlyAnchor:{weekday:d,position:1}},u)}]}}}let l=[o,a].filter(Boolean).join(", ");return {kind:"ok",rule:i,normalizedDescription:Fs(i,l)}}var Ms=`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" })`,TT=z$1.object({prose:z$1.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$1.object({dueDate:z$1.string().optional().describe("Optional ISO-8601 due-date anchor."),deferDate:z$1.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 wT(t,e){let n=yu(t.prose),r=e.makeMeta();if(n.kind!=="ambiguous")return p(n,r);let o=e.replayStore??Ae,a=n.interpretations.map(i=>i.description),s=o.register(a,async i=>{let c=n.interpretations[i];return p({kind:"ok",rule:c.rule,normalizedDescription:c.description},e.makeMeta())});return Wt(`"${t.prose}" matches multiple repetition patterns. Which did you mean?`,s,r,a.map((i,c)=>({index:c,label:i})),t.anchor!==void 0?{prose:t.prose,anchor:t.anchor}:{prose:t.prose})}function ku(t,e){return t.registerTool("repetition_from_prose",{description:Ms,inputSchema:TT.shape},async n=>{let r=await wT(n,e);return m(r)})}var Ns="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()",ST=z$1.object({});async function jT(t,e){let n=await e.reviewService.listDue(),r=e.makeMeta({cacheHit:n.cacheHit}),o=n.projects.map(a=>({id:a.id,name:a.name,nextReviewDate:a.nextReviewDate,lastReviewDate:a.lastReviewDate,reviewIntervalDays:a.reviewIntervalDays}));return p({projects:o},r)}function vu(t,e){return t.registerTool("review_list_due",{description:Ns,inputSchema:ST.shape},async n=>{let r=await jT(n,e);return m(r)})}var Us=`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" })`,_T=z$1.object({id:z$1.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function OT(t,e){let n=await e.reviewService.markReviewed(v.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:lr()}))}function Tu(t,e){return t.registerTool("review_mark_reviewed",{description:Us,inputSchema:_T.shape},async n=>{let r=await OT(n,e);return m(r)})}var Ls=`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" })`,xT=z$1.object({id:z$1.string().min(1).describe("Persistent ID of the project to mark as reviewed.")});async function AT(t,e){let n=await e.reviewService.markReviewed(v.of(t.id));return p({id:t.id,name:n.name,lastReviewDate:n.lastReviewDate,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:lr()}))}function bu(t,e){return t.registerTool("project_mark_reviewed",{description:Ls,inputSchema:xT.shape},async n=>{let r=await AT(n,e);return m(r)})}var Bs=`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 })`,PT=z$1.object({id:z$1.string().min(1).describe("Persistent ID of the project to update."),days:z$1.number().int().min(1).nullable().describe("Review interval in days. Pass null to remove the recurring review schedule.")});async function DT(t,e){let n=await e.reviewService.setInterval(v.of(t.id),t.days);return p({id:t.id,name:n.name,reviewIntervalDays:n.reviewIntervalDays},e.makeMeta({syncPending:true,humanReadableSummary:Cl(t.days)}))}function Su(t,e){return t.registerTool("review_set_interval",{description:Bs,inputSchema:PT.shape},async n=>{let r=await DT(n,e);return m(r)})}var $s=`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 })`,RT=z$1.object({projectId:z$1.string().min(1).describe("Persistent ID of the project whose next review date should change."),nextReviewDate:z$1.union([H(),z$1.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 CT(t,e){let n=await e.reviewService.setNextReviewDate(v.of(t.projectId),t.nextReviewDate);return p({id:t.projectId,name:n.name,nextReviewDate:n.nextReviewDate},e.makeMeta({syncPending:true,humanReadableSummary:Fl(t.nextReviewDate)}))}function ju(t,e){return t.registerTool("project_set_next_review_date",{description:$s,inputSchema:RT.shape},async n=>{let r=await CT(n,e);return m(r)})}var Ws=`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" })`,ET=z$1.object({q:z$1.string().max(4096,"max 4 KB").describe("Search query. Case-insensitive substring match. Empty string matches all tasks (useful with filters)."),scope:z$1.enum(["name","note","all"]).optional().describe("'name' = search task names only; 'note' = search notes only; 'all' = both. Default 'all'."),projectId:v.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list."),tagIds:z$1.array(b.schema).optional().describe("Restrict to tasks carrying ALL of these tags. Get IDs from tag_list."),flagged:z$1.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z$1.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed only; 'any' = both. Default 'any'."),limit:z$1.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 50."),cursor:z$1.string().optional().describe("Opaque cursor from a previous search_query response. Must use identical filters \u2014 changing filters returns a ValidationError."),fields:z$1.array(z$1.string()).optional().describe(`Restrict each returned task to this list of top-level fields (id is always returned). Omit for the full task shape. Empty array returns just id. Unknown names are dropped silently and surface in meta.warnings.WARN_UNKNOWN_FIELDS. Allowed: ${fe.join(", ")}.`)});async function MT(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=t.fields!==void 0?ee(t.fields,De):void 0,s=a?.valid,i=r.tasks.map(d=>X(d,s)),c=a!==void 0&&a.unknown.length>0?[Q([...a.unknown],fe)]:void 0,l=e.makeMeta({cacheHit:r.cacheHit,...c!==void 0?{warnings:c}:{}});return p({tasks:i},l,o)}function _u(t,e){return t.registerTool("search_query",{description:Ws,inputSchema:ET.shape},async n=>{let r=await MT(n,e);return m(r)})}var Hs="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()",UT=z$1.object({});async function LT(t,e){let n=await e.adapter.getLastSync();return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},e.makeMeta())}function Ou(t,e){return t.registerTool("sync_status",{description:Hs,inputSchema:UT.shape},async n=>{let r=await LT(n,e);return m(r)})}var zs="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()",BT=z$1.object({});async function $T(t,e){let n=await e.adapter.syncTrigger();e.cache!==void 0&&$d(e.cache);let r=e.makeMeta({syncPending:false});return p({lastSyncAt:n.lastSyncAt,inFlight:n.inFlight},r)}function xu(t,e){return t.registerTool("sync_trigger",{description:zs,inputSchema:BT.shape},async n=>{let r=await $T(n,e);return m(r)})}var qs=`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" })`,Gs=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("Tag name. Must be non-empty."),parentId:b.schema.optional().describe("Parent tag ID to nest under. Omit for a root tag. Get from tag_list."),status:ge(["active","on-hold"],{paused:"on-hold"},"Initial status. Defaults to 'active'. Cannot create a tag in 'dropped' state.").optional(),allowsNextAction:z$1.boolean().optional().describe("Whether the tag allows next-action selection. Defaults to true.")});async function WT(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:wl(t.name)});return p({tag:r},o)}function Au(t,e){return t.registerTool("tag_create",{description:qs,inputSchema:Gs.shape},async n=>{let r=await WT(n,e);return m(r)})}var Ks="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 HT(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.parentId!==void 0){let a=await Se(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 Pu(t,e){return t.registerTool("tag_create_describe",{description:Ks,inputSchema:Gs.shape},async n=>{let r=await HT(n,e);return m(r)})}var Xs=`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" })`,Ys=z$1.object({id:b.schema.describe("Persistent tag ID to delete. Get from tag_list.")});async function VT(t,e){return await e.tagService.delete(t.id),p({deleted:true,id:t.id},e.makeMeta({syncPending:true,humanReadableSummary:Dl()}))}function Du(t,e){return t.registerTool("tag_delete",{description:Xs,inputSchema:Ys.shape},async n=>{let r=await VT(n,e);return m(r)})}var Zs="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 qT(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 Ru(t,e){return t.registerTool("tag_delete_describe",{description:Zs,inputSchema:Ys.shape},async n=>{let r=await qT(n,e);return m(r)})}var GT=z$1.object({name:z$1.string().nullable(),latitude:z$1.number(),longitude:z$1.number(),radiusMeters:z$1.number().min(0),trigger:z$1.enum(["entering","leaving","both"])}),ht=["name","parentId","status","location","allowsNextAction","taskCount","createdAt","modifiedAt"],tn=new Set(ht);z$1.object({id:b.schema,name:z$1.string(),parentId:b.schema.nullable(),status:z$1.enum(["active","on-hold","dropped"]),location:GT.nullable(),allowsNextAction:z$1.boolean(),taskCount:z$1.number().int().min(0),createdAt:H(),modifiedAt:H()});var Qs='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" })',KT=z$1.object({id:b.schema.describe("Persistent tag ID. Get from tag_list. IDs are stable across renames."),verbose:z$1.boolean().optional().describe("When true, return the full unelided tag shape. Default: false \u2014 fields equal to their documented default are omitted. See docs/token-cost.md for the defaults table."),fields:z$1.array(z$1.string()).optional().describe("Restrict the returned tag to this list of top-level fields (id is always returned). Omit for the full tag shape. Empty array returns just id. Unknown names surface in meta.warnings.WARN_UNKNOWN_FIELDS.")});async function XT(t,e){let n=await e.tagService.get(t.id),r=t.fields!==void 0?ee(t.fields,tn):void 0,o=r?.valid,a=r!==void 0&&r.unknown.length>0?[Q([...r.unknown],ht)]:void 0,s=X(n.tag,o),c=t.verbose!==true&&o===void 0?_e(n.tag,ur):s,l=e.makeMeta({cacheHit:n.cacheHit,...a!==void 0?{warnings:a}:{}});return p({tag:c},l)}function Cu(t,e){return t.registerTool("tag_get",{description:Qs,inputSchema:KT.shape},async n=>{let r=await XT(n,e);return m(r)})}var ei='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" })',ZT=z$1.object({id:b.schema.describe("Persistent tag ID. Get from tag_list.")});async function QT(t,e){let n=await e.tagService.getLocation(t.id),r=e.makeMeta({cacheHit:n.cacheHit});return p({location:n.location},r)}function Fu(t,e){return t.registerTool("tag_get_location",{description:ei,inputSchema:ZT.shape},async n=>{let r=await QT(n,e);return m(r)})}var ti='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"] })',xr=100,ew=z$1.object({ids:z$1.array(b.schema).min(0).max(xr).describe(`Array of tag IDs to fetch (0..${xr}). Get IDs from tag_list. Missing IDs are omitted (not errors) and appear in meta.warnings.`),fields:z$1.array(z$1.string()).optional().describe(`Restrict each returned tag to this list of top-level fields (id is always returned). Omit for the full tag shape. Empty array returns just id. Unknown names are dropped silently and surface in meta.warnings.WARN_UNKNOWN_FIELDS. Allowed: ${ht.join(", ")}.`)});async function tw(t,e){if(t.ids.length===0)return p({tags:[]},e.makeMeta());if(t.ids.length>xr)throw new k(`ids array exceeds the maximum batch size of ${xr} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTagsMany(t.ids),r=n.filter(d=>d!==null),o=t.ids.filter((d,u)=>n[u]===null),a=t.fields!==void 0?ee(t.fields,tn):void 0,s=a?.valid,i=r.map(d=>X(d,s)),c=[];o.length>0&&c.push($t(o)),a!==void 0&&a.unknown.length>0&&c.push(Q([...a.unknown],ht));let l=e.makeMeta({...c.length>0?{warnings:c}:{}});return p({tags:i},l)}function Eu(t,e){return t.registerTool("tag_get_many",{description:ti,inputSchema:ew.shape},async n=>{let r=await tw(n,e);return m(r)})}var ni='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" })',nw=z$1.object({parentId:b.schema.optional().describe("Return only direct children of this tag. Get the ID from a previous tag_list call. Omit for root tags."),status:ge(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"Filter by tag status. Omit to return tags of all statuses.").optional(),verbose:z$1.boolean().optional().describe("When true, return the full unelided tag shape. Default: false \u2014 fields equal to their documented default (status: 'active', parentId: null, location: null, allowsNextAction: true) are omitted. See docs/token-cost.md for the defaults table."),fields:z$1.array(z$1.string()).optional().describe("Restrict each returned tag to this list of top-level fields (id is always returned). Omit for the full tag shape. Empty array returns just id. Unknown names surface in meta.warnings.WARN_UNKNOWN_FIELDS.")});async function rw(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=t.fields!==void 0?ee(t.fields,tn):void 0,a=o?.valid,s=o!==void 0&&o.unknown.length>0?[Q([...o.unknown],ht)]:void 0,c=t.verbose!==true&&a===void 0?He(r.tags,ur):r.tags.map(d=>X(d,a)),l=e.makeMeta({cacheHit:r.cacheHit,...s!==void 0?{warnings:s}:{}});return p({tags:c},l)}function Mu(t,e){return t.registerTool("tag_list",{description:ni,inputSchema:nw.shape},async n=>{let r=await rw(n,e);return m(r)})}var ri=`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 })`,oi=z$1.object({id:b.schema.describe("Persistent ID of the tag to move. Get from tag_list."),parentId:b.schema.nullable().describe("New parent tag ID, or null to promote the tag to root level.")});async function aw(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:bl(n.name,"root")}))}function Nu(t,e){return t.registerTool("tag_move",{description:ri,inputSchema:oi.shape},async n=>{let r=await aw(n,e);return m(r)})}var ai="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 sw(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 s=await Se(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`under '${s}'`;}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 Uu(t,e){return t.registerTool("tag_move_describe",{description:ai,inputSchema:oi.shape},async n=>{let r=await sw(n,e);return m(r)})}var si='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 })',iw=z$1.object({id:b.schema.describe("Persistent tag ID. Get from tag_list."),allowsNextAction:z$1.boolean().describe("true to enable next-action selection; false to disable.")});async function cw(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:lt(n.name)}))}function Ju(t,e){return t.registerTool("tag_set_allows_next_action",{description:si,inputSchema:iw.shape},async n=>{let r=await cw(n,e);return m(r)})}var ii='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" })',dw=z$1.object({id:b.schema.describe("Persistent tag ID. Get from tag_list."),latitude:z$1.number().min(-90).max(90).describe("Latitude in decimal degrees (\u221290 to 90)."),longitude:z$1.number().min(-180).max(180).describe("Longitude in decimal degrees (\u2212180 to 180)."),radiusMeters:z$1.number().min(0).describe("Trigger radius in metres. Must be \u2265 0."),trigger:z$1.enum(["entering","leaving","both"]).describe("When to trigger: 'entering', 'leaving', or 'both'."),name:z$1.string().nullable().optional().describe("Optional human-readable name for the location (e.g. 'Home', 'Office').")});async function lw(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:lt(n.name)}))}function Bu(t,e){return t.registerTool("tag_set_location",{description:ii,inputSchema:dw.shape},async n=>{let r=await lw(n,e);return m(r)})}var ci='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" })',uw=z$1.object({id:b.schema.describe("Persistent tag ID. Get from tag_list."),status:ge(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status for the tag.")});async function mw(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:lt(n.name)}))}function $u(t,e){return t.registerTool("tag_set_status",{description:ci,inputSchema:uw.shape},async n=>{let r=await mw(n,e);return m(r)})}var li='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" })',pi=z$1.object({id:b.schema.describe("Persistent tag ID. Get from tag_list."),name:z$1.string().min(1).max(1024,"max 1 KB").optional().describe("New tag name. Must be non-empty if supplied."),parentId:b.schema.nullable().optional().describe("New parent tag ID. Pass null to promote to root. Get from tag_list."),status:ge(["active","on-hold","dropped"],{paused:"on-hold",cancelled:"dropped",archived:"dropped"},"New lifecycle status.").optional(),allowsNextAction:z$1.boolean().optional().describe("Whether the tag allows next-action selection in OmniFocus.")});async function fw(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:lt(o.name)}))}function Wu(t,e){return t.registerTool("tag_update",{description:li,inputSchema:pi.shape},async n=>{let r=await fw(n,e);return m(r)})}var ui="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 gw(t,e){let n=[],r=[],o=String(t.id);try{let s=await e.adapter.getTag(t.id);o=s.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:s.name}),r.push(`rename to '${t.name}'`)),t.status!==void 0&&(n.push({field:"status",newValue:t.status,oldValue:s.status}),r.push(`set status to '${t.status}'`)),t.allowsNextAction!==void 0&&(n.push({field:"allowsNextAction",newValue:String(t.allowsNextAction),oldValue:String(s.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 Hu(t,e){return t.registerTool("tag_update_describe",{description:ui,inputSchema:pi.shape},async n=>{let r=await gw(n,e);return m(r)})}var mi=`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"] }] })`,hw=z$1.object({taskId:y.schema.describe("Persistent task ID."),projectId:v.schema.optional().describe("If set, move the task to this project before applying other changes."),addTagIds:z$1.array(b.schema).optional().describe("Tag IDs to add. Combined with removeTagIds via current-tagIds pre-read."),removeTagIds:z$1.array(b.schema).optional().describe("Tag IDs to remove. Wins over addTagIds when the same ID appears in both."),deferDate:z$1.string().datetime({offset:true}).nullable().optional().describe("Defer date as ISO-8601 with offset. Null clears the date."),dueDate:z$1.string().datetime({offset:true}).nullable().optional().describe("Due date as ISO-8601 with offset. Null clears the date."),flagged:z$1.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"}),yw=z$1.object({assignments:z$1.array(hw).min(1).describe("Triage assignments \u2014 one per task. Must contain at least one item.")});function kw(t){return t.map((e,n)=>e.projectId!==void 0?n:-1).filter(e=>e>=0)}function vw(t){return t.map((e,n)=>e.addTagIds!==void 0||e.removeTagIds!==void 0?n:-1).filter(e=>e>=0)}function fi(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=>b.of(o))}function Iw(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=fi(e??[],t.addTagIds,t.removeTagIds)),Object.keys(n).length>0?n:null}async function gi(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 x=o[T];x!=null&&a.set(r[T],x.name);}let s=vw(n),i=new Map;if(s.length>0){let T=s.map(O=>n[O].taskId),x=await e.adapter.getTasksMany(T);for(let O=0;O<s.length;O++){let L=x[O];L&&i.set(s[O],L.tagIds);}}let c=kw(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 x=c[T.index];d.set(x,"ok");}for(let T of l.failed){let x=c[T.index];d.set(x,{errorCode:T.errorCode,message:T.message});}let u=[];for(let T=0;T<n.length;T++){let x=n[T],O=d.get(T);if(O!==void 0&&O!=="ok")continue;let L=Iw(x,i.get(T));L!==null&&u.push({origIdx:T,id:x.taskId,patch:L});}let f=u.length>0?await e.adapter.batchUpdateTasks(u.map(T=>({id:T.id,patch:T.patch}))):{succeeded:[],failed:[]},g=new Set(f.succeeded.map(T=>T.index)),I=new Map;for(let T of f.failed)I.set(T.index,{errorCode:T.errorCode,message:T.message});let h=[],w=[];for(let T=0;T<n.length;T++){let x=n[T],O=d.get(T);if(O!==void 0&&O!=="ok"){w.push({index:T,errorCode:`move:${O.errorCode}`,message:O.message});continue}let L=u.findIndex(A=>A.origIdx===T);if(L>=0){if(g.has(L))h.push({index:T,value:x.taskId});else if(I.has(L)){let A=I.get(L);w.push({index:T,errorCode:`update:${A.errorCode}`,message:A.message});}}else h.push({index:T,value:x.taskId});}if(e.cache!==void 0&&h.length>0)for(let T of h){let x=n[T.index];j(e.cache,{taskId:x.taskId,...x.projectId!==void 0&&{projectId:x.projectId}});}let S=h.map(T=>({index:T.index,value:{id:T.value,name:a.get(T.value)??""}}));return p({assigned:S,failed:w},e.makeMeta({syncPending:h.length>0}))}function zu(t,e){return t.registerTool("task_batch_assign",{description:mi,inputSchema:yw.shape},async n=>{let r=await gi(n,e);return m(r)})}var hi='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" }] })',Tw=z$1.object({id:y.schema.describe("Persistent task ID."),at:z$1.string().datetime({offset:true}).optional().describe("Optional ISO-8601 completion time; defaults to now.")}),ww=z$1.object({items:z$1.array(Tw).min(1).describe("Array of { id, at? } items. Must contain at least one item.")});async function bw(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)}})),s=await e.adapter.batchCompleteTasks(a);if(e.cache!==void 0)for(let c of s.succeeded){let l=t.items[c.index];l!==void 0&&j(e.cache,{taskId:l.id});}let i=s.succeeded.map(c=>({index:c.index,value:{id:c.value,name:o.get(c.value)??""}}));return p({completed:i,failed:s.failed},e.makeMeta({syncPending:s.succeeded.length>0,humanReadableSummary:pl(s.succeeded.length)}))}function Vu(t,e){return t.registerTool("task_batch_complete",{description:hi,inputSchema:ww.shape},async n=>{let r=await bw(n,e);return m(r)})}var yi='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" }] })',Sw=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("Task name. Required, non-empty."),projectId:v.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:y.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z$1.string().max(1048576,"max 1 MB").optional().describe("Plain-text note."),flagged:z$1.boolean().optional().describe("Flag the task."),dueDate:z$1.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset."),dueDateFloating:z$1.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),deferDate:z$1.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset."),deferDateFloating:z$1.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z$1.number().int().positive().optional().describe("Estimated duration in minutes."),tagIds:z$1.array(b.schema).optional().describe("Tag IDs to apply."),sequential:z$1.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z$1.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"]}),ki=z$1.object({items:z$1.array(Sw).min(1).describe("Array of task inputs. Must contain at least one item.")});async function jw(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 s of r.succeeded){let c=t.items[s.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:Ht(r.succeeded.length)}))}function qu(t,e){return t.registerTool("task_batch_create",{description:yi,inputSchema:ki.shape},async n=>{let r=await jw(n,e);return m(r)})}var vi="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 _w(t,e){let n=[],r=[];for(let a of t.items){let s;a.projectId!==void 0?s=`project '${await Vt(e.adapter,a.projectId)}'`:a.parentTaskId!==void 0?s=`subtask of '${await qt(e.adapter,a.parentTaskId)}'`:s="Inbox",r.push(`'${a.name}' (${s})`),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 Gu(t,e){return t.registerTool("task_batch_create_describe",{description:vi,inputSchema:ki.shape},async n=>{let r=await _w(n,e);return m(r)})}var Dr=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function Rr(t,e){switch(t.kind){case "next-work-day":return Ow(t.at??"morning",e);case "next-weekday":return xw(t.weekday,t.at??"morning",e);case "in-business-days":return Aw(t.days,e);case "after-event":throw new be("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 Pw(e);case "explicit-with-skip-weekends":return Dw(t.date)}}function Ow(t,e){let n=new Date(e.now);for(n.setDate(n.getDate()+1);Ii(n);)n.setDate(n.getDate()+1);return Ti(n,Xu(t,e)),{resolvedDeferDate:En(n),reason:`next work ${t} (${Dr[n.getDay()]} ${Yu(n)})`}}function xw(t,e,n){if(!Number.isInteger(t)||t<0||t>6)throw new k(`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),Ti(r,Xu(e,n)),{resolvedDeferDate:En(r),reason:`next ${Dr[t]} ${e} (${Cr(r)} ${Yu(r)})`}}function Aw(t,e){if(!Number.isInteger(t)||t<1)throw new k(`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),Ii(n)||r--;return Ti(n,e.morningHour),{resolvedDeferDate:En(n),reason:`${t} business day${t===1?"":"s"} from now (${Dr[n.getDay()]} ${Cr(n)})`}}function Pw(t){let e=new Date(t.now.getFullYear(),t.now.getMonth()+1,1,0,0,0,0);return {resolvedDeferDate:En(e),reason:`start of next month (${Cr(e)})`}}function Dw(t,e){let n=new Date(t);if(Number.isNaN(n.getTime()))throw new k(`date must be a parseable ISO-8601 timestamp; got ${t}`);let r=new Date(n),o=false;for(;Ii(r);)r.setDate(r.getDate()+1),o=true;return {resolvedDeferDate:En(r),reason:o?`${t} \u2192 snapped to ${Dr[r.getDay()]} ${Cr(r)} (skipped weekend)`:`${t} (no weekend skip needed)`}}function Ii(t){let e=t.getDay();return e===0||e===6}function Xu(t,e){return t==="morning"?e.morningHour:e.afternoonHour}function Ti(t,e){t.setHours(e,0,0,0);}function $e(t){return t<10?`0${t}`:String(t)}function Cr(t){return `${t.getFullYear()}-${$e(t.getMonth()+1)}-${$e(t.getDate())}`}function Yu(t){return `${$e(t.getHours())}:${$e(t.getMinutes())}`}function En(t){let e=-t.getTimezoneOffset(),n=e>=0?"+":"-",r=Math.abs(e),o=`${n}${$e(Math.floor(r/60))}:${$e(r%60)}`;return `${t.getFullYear()}-${$e(t.getMonth()+1)}-${$e(t.getDate())}T${$e(t.getHours())}:${$e(t.getMinutes())}:${$e(t.getSeconds())}${o}`}var Rw=9,Cw=14;function Fr(t=process.env){return {morningHour:Ku(t.OMNIFOCUS_MORNING_HOUR,Rw),afternoonHour:Ku(t.OMNIFOCUS_AFTERNOON_HOUR,Cw)}}function Ku(t,e){if(t===void 0||t==="")return e;let n=Number(t);return !Number.isInteger(n)||n<0||n>23?e:n}var wi="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' } }] })",Zu=z$1.enum(["morning","afternoon"]),Fw=z$1.discriminatedUnion("kind",[z$1.object({kind:z$1.literal("next-work-day"),at:Zu.optional()}),z$1.object({kind:z$1.literal("next-weekday"),weekday:z$1.number().int().min(0).max(6),at:Zu.optional()}),z$1.object({kind:z$1.literal("in-business-days"),days:z$1.number().int().positive()}),z$1.object({kind:z$1.literal("after-event"),eventId:z$1.string().min(1)}),z$1.object({kind:z$1.literal("next-month-start")}),z$1.object({kind:z$1.literal("explicit-with-skip-weekends"),date:z$1.string().min(1)})]),Ew=z$1.object({entries:z$1.array(z$1.object({taskId:y.schema,intent:Fw})).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$1.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$1.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 Mw(t,e){let n=e.idempotencyStore??le,r=e.now?e.now():new Date,o=e.hours??Fr();return he(n,t.idempotency_key,async()=>{let a=[];for(let i of t.entries)try{let c=Rr(i.intent,{now:r,morningHour:o.morningHour,afternoonHour:o.afternoonHour});if(!t.dry_run&&(await e.adapter.updateTask(i.taskId,{deferDate:c.resolvedDeferDate}),e.cache!==void 0)){let l=await e.adapter.getTask(i.taskId);j(e.cache,{taskId:i.taskId,projectId:l.projectId});}a.push({taskId:i.taskId,ok:!0,resolvedDeferDate:c.resolvedDeferDate,reason:c.reason});}catch(c){a.push({taskId:i.taskId,ok:false,error:c instanceof Error?c.message:String(c)});}let s={syncPending:!t.dry_run};return t.dry_run&&(s.dryRun=true),p({results:a},e.makeMeta(s))})}function Qu(t,e){return t.registerTool("task_batch_defer_smart",{description:wi,inputSchema:Ew.shape},async n=>{let r=await Mw(n,e);return m(r)})}var bi='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" }] })',Nw=z$1.object({id:y.schema.describe("Persistent task ID.")}),Uw=z$1.object({confirm:z$1.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$1.array(Nw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Lw(t,e){let n=t.items.map(i=>i.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let i=0;i<n.length;i++){let c=r[i];c!=null&&o.set(n[i],c.name);}let a=await e.adapter.batchDeleteTasks(t.items.map(i=>({id:i.id})));if(e.cache!==void 0)for(let i of a.succeeded){let c=t.items[i.index];c!==void 0&&j(e.cache,{taskId:c.id});}let s=a.succeeded.map(i=>({index:i.index,value:{id:i.value,name:o.get(i.value)??""}}));return p({deleted:s,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:ml(a.succeeded.length)}))}function em(t,e){return t.registerTool("task_batch_delete",{description:bi,inputSchema:Uw.shape},async n=>{let r=await Lw(n,e);return m(r)})}var ji='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" }] })',Jw=z$1.object({id:y.schema.describe("Persistent task ID.")}),Bw=z$1.object({items:z$1.array(Jw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function $w(t,e){let n=t.items.map(i=>i.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let i=0;i<n.length;i++){let c=r[i];c!=null&&o.set(n[i],c.name);}let a=await e.adapter.batchDropTasks(t.items.map(i=>({id:i.id})));if(e.cache!==void 0)for(let i of a.succeeded){let c=t.items[i.index];c!==void 0&&j(e.cache,{taskId:c.id});}let s=a.succeeded.map(i=>({index:i.index,value:{id:i.value,name:o.get(i.value)??""}}));return p({dropped:s,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:fl(a.succeeded.length)}))}function tm(t,e){return t.registerTool("task_batch_drop",{description:ji,inputSchema:Bw.shape},async n=>{let r=await $w(n,e);return m(r)})}var _i='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" }] })',Ww=z$1.object({projectId:v.schema.optional().describe("Move into a project as a top-level action. Mutually exclusive with parentId."),parentId:y.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"}),Hw=z$1.object({id:y.schema.describe("Persistent task ID."),destination:Ww.describe("Where to move the task. Provide projectId, parentId, or neither (inbox).")}),zw=z$1.object({items:z$1.array(Hw).min(1).describe("Array of { id, destination } items. Must contain at least one item.")});async function Vw(t,e){let n=t.items.map(i=>i.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let i=0;i<n.length;i++){let c=r[i];c!=null&&o.set(n[i],c.name);}let a=await e.adapter.batchMoveTasks(t.items.map(i=>({id:i.id,destination:{...i.destination.projectId!==void 0&&{projectId:i.destination.projectId},...i.destination.parentId!==void 0&&{parentId:i.destination.parentId}}})));if(e.cache!==void 0)for(let i of a.succeeded){let c=t.items[i.index];c!==void 0&&j(e.cache,{taskId:c.id});}let s=a.succeeded.map(i=>({index:i.index,value:{id:i.value,name:o.get(i.value)??""}}));return p({moved:s,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:hl(a.succeeded.length,"destination")}))}function nm(t,e){return t.registerTool("task_batch_move",{description:_i,inputSchema:zw.shape},async n=>{let r=await Vw(n,e);return m(r)})}var xi='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" }] })',qw=z$1.object({id:y.schema.describe("Persistent task ID.")}),Gw=z$1.object({items:z$1.array(qw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Kw(t,e){let n=t.items.map(i=>i.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let i=0;i<n.length;i++){let c=r[i];c!=null&&o.set(n[i],c.name);}let a=await e.adapter.batchUncompleteTasks(t.items.map(i=>({id:i.id})));if(e.cache!==void 0)for(let i of a.succeeded){let c=t.items[i.index];c!==void 0&&j(e.cache,{taskId:c.id});}let s=a.succeeded.map(i=>({index:i.index,value:{id:i.value,name:o.get(i.value)??""}}));return p({uncompleted:s,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:ul(a.succeeded.length)}))}function rm(t,e){return t.registerTool("task_batch_uncomplete",{description:xi,inputSchema:Gw.shape},async n=>{let r=await Kw(n,e);return m(r)})}var Pi='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" }] })',Xw=z$1.object({id:y.schema.describe("Persistent task ID.")}),Yw=z$1.object({items:z$1.array(Xw).min(1).describe("Array of { id } items. Must contain at least one item.")});async function Zw(t,e){let n=t.items.map(i=>i.id),r=await e.adapter.getTasksMany(n),o=new Map;for(let i=0;i<n.length;i++){let c=r[i];c!=null&&o.set(n[i],c.name);}let a=await e.adapter.batchUndropTasks(t.items.map(i=>({id:i.id})));if(e.cache!==void 0)for(let i of a.succeeded){let c=t.items[i.index];c!==void 0&&j(e.cache,{taskId:c.id});}let s=a.succeeded.map(i=>({index:i.index,value:{id:i.value,name:o.get(i.value)??""}}));return p({undropped:s,failed:a.failed},e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:gl(a.succeeded.length)}))}function om(t,e){return t.registerTool("task_batch_undrop",{description:Pi,inputSchema:Yw.shape},async n=>{let r=await Zw(n,e);return m(r)})}var Di=`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" } }] })`,Qw=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").optional().describe("New task name."),note:z$1.string().max(1048576,"max 1 MB").nullable().optional().describe("Plain-text note. Null clears the note."),flagged:z$1.boolean().optional().describe("Flag or unflag the task."),dueDate:z$1.string().datetime({offset:true}).nullable().optional().describe("Due date as ISO-8601 with offset. Null clears the date."),dueDateFloating:z$1.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),deferDate:z$1.string().datetime({offset:true}).nullable().optional().describe("Defer date as ISO-8601 with offset. Null clears the date."),deferDateFloating:z$1.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z$1.number().int().positive().nullable().optional().describe("Estimated duration in minutes. Null clears the estimate."),tagIds:z$1.array(b.schema).optional().describe("Full replacement tag ID list."),sequential:z$1.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z$1.boolean().optional().describe("Complete when all subtasks complete.")}).refine(t=>Object.keys(t).length>0,{message:"Patch must contain at least one field"}),eb=z$1.object({id:y.schema.describe("Persistent task ID."),patch:Qw.describe("Fields to change. At least one field required.")}),Ri=z$1.object({items:z$1.array(eb).min(1).describe("Array of { id, patch } pairs. Must contain at least one item.")});async function tb(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})),s=await e.adapter.batchUpdateTasks(a);if(e.cache!==void 0)for(let c of s.succeeded){let l=t.items[c.index];l!==void 0&&j(e.cache,{taskId:l.id});}let i=s.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:i,failed:s.failed},e.makeMeta({syncPending:s.succeeded.length>0,humanReadableSummary:ll(s.succeeded.length)}))}function am(t,e){return t.registerTool("task_batch_update",{description:Di,inputSchema:Ri.shape},async n=>{let r=await tb(n,e);return m(r)})}var Ci="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 nb(t,e){let n=[],r=[];for(let a of t.items){let s=String(a.id);try{s=(await e.adapter.getTask(a.id)).name;}catch{}let i=Object.keys(a.patch).join(", ");r.push(`'${s}' [${i}]`);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 sm(t,e){return t.registerTool("task_batch_update_describe",{description:Ci,inputSchema:Ri.shape},async n=>{let r=await nb(n,e);return m(r)})}var Fi='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" })',ob=z$1.object({id:y.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function ab(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:dl(n.name)});return p({task:n},r)}function im(t,e){return t.registerTool("task_clear_alarms",{description:Fi,inputSchema:ob.shape},async n=>{let r=await ab(n,e);return m(r)})}var Ei='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" })',ib=z$1.object({id:y.schema.describe("ID of the task to update. Get from task_list or search_query.")});async function cb(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:il(n.name)});return p({task:n},r)}function cm(t,e){return t.registerTool("task_clear_repetition",{description:Ei,inputSchema:ib.shape},async n=>{let r=await cb(n,e);return m(r)})}var Mi='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" })',Ni=z$1.object({id:y.schema.describe("Persistent task ID."),at:z$1.string().datetime({offset:true}).optional().describe("ISO-8601 completion time. Defaults to now.")});async function db(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??Ae,o=[];try{o=await e.adapter.listTasks({parentId:t.id,completed:!1});}catch{}if(o.length>0){let a=e.makeMeta(),s=["Complete this task and all incomplete children","Complete this task only \u2014 leave children incomplete"],i=o.map(l=>l.id),c=r.register(s,async l=>lm(t,n,e,l===0?i:[]));return Wt(`"${n.name}" has ${o.length} incomplete child task(s). How should they be handled?`,c,a,s.map((l,d)=>({index:d,label:l})),{id:t.id,...t.at!==void 0?{at:t.at}:{}})}return lm(t,n,e,[])}async function lm(t,e,n,r){for(let s of r)try{await n.adapter.completeTask(s);}catch{}let o=t.at!==void 0?new Date(t.at):void 0;await n.adapter.completeTask(t.id,o),n.cache!==void 0&&j(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 i=await n.adapter.getProject(e.projectId);a=Kt([Fp(e.projectId,i.name)]);}}catch{}return p({done:true,id:t.id,name:e.name},n.makeMeta({syncPending:true,humanReadableSummary:Yd(e.name)}),void 0,a)}function pm(t,e){return t.registerTool("task_complete",{description:Mi,inputSchema:Ni.shape},async n=>{let r=await db(n,e);return m(r)})}var Ui="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 lb(t,e){let n=[],r=String(t.id),o=false;try{let i=await e.adapter.getTask(t.id);r=i.name,o=i.completed;}catch{}if(o){let i=`Task '${r}' is already completed \u2014 would be a no-op.`;return p({description:i,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}`:"",s=`Would mark task '${r}' as done${a}.`;return p({description:s,plannedChanges:n},e.makeMeta())}function um(t,e){return t.registerTool("task_complete_describe",{description:Ui,inputSchema:Ni.shape},async n=>{let r=await lb(n,e);return m(r)})}var Li=`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" })`,pb=z$1.object({id:y.schema.describe("Persistent ID of the task to promote."),folderId:$.schema.optional().describe("Place the new project inside this folder. Omit to place at the top of the library."),position:z$1.enum(["beginning","ending"]).optional().describe('Where within the folder or library to insert the new project. Defaults to "ending".')});async function ub(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:al(r.name)}))}function fm(t,e){return t.registerTool("task_convert_to_project",{description:Li,inputSchema:pb.shape},async n=>{let r=await ub(n,e);return m(r)})}function qe(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 gm(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 mb(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 fb(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 hm(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 ym(t,e){let n=r=>e===void 0?void 0:gm(e,r);switch(t.code){case "invalid_type":{let r=t.expected;return [{field:qe(t.path),sent:n(t.path),expected:`${r}`}]}case "invalid_value":{let r=t.values;return [{field:qe(t.path),sent:n(t.path),expected:`one of: ${mb(r)}`,...r.length>0&&{examples:r.slice(0,3)}}]}case "invalid_format":{let r=fb(t.format);return [{field:qe(t.path),sent:n(t.path),expected:r.expected,...r.examples&&{examples:r.examples}}]}case "too_small":return [{field:qe(t.path),sent:n(t.path),expected:hm("min",Number(t.minimum),t.inclusive??true,t.origin)}];case "too_big":return [{field:qe(t.path),sent:n(t.path),expected:hm("max",Number(t.maximum),t.inclusive??true,t.origin)}];case "unrecognized_keys":{let r=qe(t.path);return t.keys.map(o=>({field:r==="<root>"?o:`${r}.${o}`,sent:gm(e,[...t.path,o]),expected:"key is not part of the schema; remove it"}))}case "not_multiple_of":return [{field:qe(t.path),sent:n(t.path),expected:`multiple of ${String(t.divisor)}`}];case "invalid_union":{let r=[];for(let a of t.errors){let s=a.slice().sort((i,c)=>c.path.length-i.path.length)[0];s!==void 0&&r.push(...ym(s,e));}if(r.length===0)return [{field:qe(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:qe(t.path),sent:n(t.path),expected:o}]}default:return [{field:qe(t.path),sent:n(t.path),expected:t.message||`value satisfying schema constraint "${String(t.code)}"`}]}}function km(t,e){let n=[];for(let r of t.issues)n.push(...ym(r,e));return n}function Ee(t,e,n="Cross-field validation failed"){let r=t.safeParse(e);if(r.success)return r.data;let o=km(r.error,e);throw new k(n,{details:{failures:o}})}var Ji='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" })',Nr=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("Task name. Required, must be non-empty."),projectId:v.schema.optional().describe("Project to add the task to. Omit for inbox or subtask."),parentTaskId:y.schema.optional().describe("Parent task ID for a subtask. Omit for inbox or project task."),note:z$1.string().max(1048576,"max 1 MB").optional().describe("Plain-text note."),flagged:z$1.boolean().optional().describe("Flag the task."),dueDate:z$1.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset."),dueDateFloating:z$1.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$1.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset."),deferDateFloating:z$1.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),estimatedMinutes:z$1.number().int().min(1).optional().describe("Estimated duration in minutes."),tagIds:z$1.array(b.schema).optional().describe("Tag IDs to apply."),sequential:z$1.boolean().optional().describe("If true, subtasks must be completed in order."),completedByChildren:z$1.boolean().optional().describe("Complete when all subtasks complete."),idempotency_key:z$1.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.")}),gb=Nr.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 hb(t,e){Ee(gb,t);let n=e.idempotencyStore??le;return he(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=[Pp(o,t.name),Dp(o,t.dueDate,t.estimatedMinutes)];if(t.projectId===void 0&&t.parentTaskId===void 0)try{let i=await e.adapter.listTasks({inbox:!0,completed:!1});a.push(Rp(i.length));}catch{}let s=Kt(a.filter(i=>i!=null));return p({id:o,name:t.name},e.makeMeta({syncPending:true,humanReadableSummary:Xd(t.name)}),void 0,s)})}function vm(t,e){return t.registerTool("task_create",{description:Ji,inputSchema:Nr.shape},async n=>{let r=await hb(n,e);return m(r)})}var Bi="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 yb(t,e){let n=[],r=[];if(n.push({field:"name",newValue:t.name}),r.push(`'${t.name}'`),t.projectId!==void 0){let a=await Vt(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 qt(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 ${Pe(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate}),r.push(`deferred until ${Pe(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(s=>Se(e.adapter,s)));n.push({field:"tagIds",newValue:t.tagIds.join(",")}),r.push(`tagged ${a.map(s=>`'${s}'`).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 Im(t,e){return t.registerTool("task_create_describe",{description:Bi,inputSchema:Nr.shape},async n=>{let r=await yb(n,e);return m(r)})}var $i="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' } })",Tm=z$1.enum(["morning","afternoon"]),kb=z$1.discriminatedUnion("kind",[z$1.object({kind:z$1.literal("next-work-day"),at:Tm.optional()}),z$1.object({kind:z$1.literal("next-weekday"),weekday:z$1.number().int().min(0).max(6),at:Tm.optional()}),z$1.object({kind:z$1.literal("in-business-days"),days:z$1.number().int().positive()}),z$1.object({kind:z$1.literal("after-event"),eventId:z$1.string().min(1)}),z$1.object({kind:z$1.literal("next-month-start")}),z$1.object({kind:z$1.literal("explicit-with-skip-weekends"),date:z$1.string().min(1)})]),vb=z$1.object({taskId:y.schema.describe("ID of the task to defer."),intent:kb.describe("High-level defer intent. Discriminated union on `kind` \u2014 see tool description for variants."),expectedModifiedAt:z$1.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$1.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$1.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 Ib(t,e){let n=e.idempotencyStore??le,r=e.now?e.now():new Date,o=e.hours??Fr();return he(n,t.idempotency_key,async()=>{let a=Rr(t.intent,{now:r,morningHour:o.morningHour,afternoonHour:o.afternoonHour}),s=()=>p({taskId:t.taskId,resolvedDeferDate:a.resolvedDeferDate,reason:a.reason},e.makeMeta({syncPending:false})),i=async()=>{await e.adapter.updateTask(t.taskId,{deferDate:a.resolvedDeferDate});let c=await e.adapter.getTask(t.taskId);return e.cache!==void 0&&j(e.cache,{taskId:t.taskId,projectId:c.projectId}),p({taskId:t.taskId,resolvedDeferDate:a.resolvedDeferDate,reason:a.reason},e.makeMeta({syncPending:true}))};return ze(t.dry_run,s,i)})}function wm(t,e){return t.registerTool("task_defer_smart",{description:$i,inputSchema:vb.shape},async n=>{let r=await Ib(n,e);return m(r)})}var Wi='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 })',Hi=z$1.object({confirm:z$1.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:y.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$1.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$1.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$1.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 Tb(t,e){let n=e.idempotencyStore??le;return he(n,t.idempotency_key,async()=>{let r=await e.adapter.getTask(t.id);mt(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:Qd(r.name)})));return ze(t.dry_run,o,a)})}function bm(t,e){return t.registerTool("task_delete",{description:Wi,inputSchema:Hi.shape},async n=>{let r=await Tb(n,e);return m(r)})}var zi="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 wb(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 Sm(t,e){return t.registerTool("task_delete_describe",{description:zi,inputSchema:Hi.shape},async n=>{let r=await wb(n,e);return m(r)})}var Vi='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" })',qi=z$1.object({id:y.schema.describe("Persistent task ID."),at:z$1.string().datetime({offset:true}).optional().describe("ISO-8601 drop time. Defaults to now.")});async function bb(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:el(n.name)}))}function _m(t,e){return t.registerTool("task_drop",{description:Vi,inputSchema:qi.shape},async n=>{let r=await bb(n,e);return m(r)})}var Gi="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 Sb(t,e){let n=[],r=String(t.id),o=false;try{let i=await e.adapter.getTask(t.id);r=i.name,o=i.dropped;}catch{}if(o){let i=`Task '${r}' is already dropped \u2014 would be a no-op.`;return p({description:i,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}`:"",s=`Would drop task '${r}' (mark as dropped/on-hold)${a}.`;return p({description:s,plannedChanges:n},e.makeMeta())}function Om(t,e){return t.registerTool("task_drop_describe",{description:Gi,inputSchema:qi.shape},async n=>{let r=await Sb(n,e);return m(r)})}var Ki=`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" } })`,jb=z$1.union([z$1.object({projectId:v.schema}),z$1.object({parentId:y.schema}),z$1.object({toInbox:z$1.literal(true)})]).describe("Where to place the duplicate. Exactly one of projectId, parentId, or toInbox: true. Omit to clone alongside the source."),_b=z$1.object({id:y.schema.describe("Persistent ID of the task to duplicate."),recursive:z$1.boolean().optional().default(false).describe("When true, clone the full subtask subtree depth-first. Default: false (clone only the task itself)."),destination:jb.optional()}).describe("Duplicate options. `destination` overrides the default same-container placement.");async function Ob(t,e){if(t.destination!==void 0){let a=t.destination,s=("projectId"in a?1:0)+("parentId"in a?1:0)+("toInbox"in a&&a.toInbox===true?1:0);if(s!==1)throw new k("task_duplicate: destination must set exactly one of projectId, parentId, or toInbox",{details:{field:"destination",provided:s},suggestion:"Set exactly one destination field or omit destination entirely."})}let 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:rl(n.name)}))}function xm(t,e){return t.registerTool("task_duplicate",{description:Ki,inputSchema:_b.shape},async n=>{let r=await Ob(n,e);return m(r)})}var Xi=[".png",".jpg",".jpeg",".heic",".heif",".gif",".webp",".pdf"];function Pm(t){return Xi.includes(extname(t).toLowerCase())}function Ab(t){return t.mimeType!==null?t.mimeType.startsWith("image/")||t.mimeType==="application/pdf":Pm(t.name)}var Yi=`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 })`,Am=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("Task name extracted from the image."),note:z$1.string().max(1048576,"max 1 MB").optional().describe("Additional context or plain-text note for the task."),deferDate:z$1.string().datetime({offset:true}).optional().describe("Defer date as ISO-8601 with offset, if detected in the image."),dueDate:z$1.string().datetime({offset:true}).optional().describe("Due date as ISO-8601 with offset, if detected in the image.")}),Pb=z$1.discriminatedUnion("kind",[z$1.object({kind:z$1.literal("path"),imagePath:z$1.string().min(1).describe(`Absolute path within attachment-path-scope, with one of the supported image extensions (${Xi.join(",")}). Subject to the configured size cap.`).refine(Pm,{message:`imagePath must use one of ${Xi.join(",")}`})}).describe("Image read from a filesystem path."),z$1.object({kind:z$1.literal("attachment"),attachmentId:Le.schema.describe("Persistent ID of the OF attachment carrying the image."),ownerTaskId:y.schema.optional().describe("Owner task that holds the attachment. Mutually exclusive with ownerProjectId."),ownerProjectId:v.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."),Db=z$1.enum(["parent-task","each-task","none"]).default("parent-task").describe("Re-attachment mode after task creation."),Dm=z$1.object({source:Pb,targetProjectId:v.schema.describe("Project that receives the captured tasks (and the wrapper, if `parent-task` mode)."),proposed:z$1.array(Am).min(1).describe("Agent-supplied extraction."),attachSourceTo:Db,parentTaskName:z$1.string().min(1).optional().describe("Wrapper parent task name; default 'Captured from image'."),dryRun:z$1.boolean().default(true).describe("true (default) = preview; false requires confirmation[]."),confirmation:z$1.array(Am).optional().describe("Required when dryRun=false. (Possibly-edited) confirmed tasks.")}),Rb=Dm.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 Cb(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 k(`Attachment not found: ${t.attachmentId}`,{details:{field:"source.attachmentId"}});if(!Ab(r))throw new k(`Attachment is not an image: ${r.name}`,{details:{field:"source.attachmentId"}});return {kind:"attachment",attachment:r}}function Fb(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 Eb(t,e){Ee(Rb,t);let n=await Cb(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,s={projectId:t.targetProjectId};if(t.attachSourceTo==="parent-task"){let d=t.parentTaskName??"Captured from image",u=await e.adapter.createTask({name:d,projectId:t.targetProjectId});o!==void 0&&await e.attachmentService.add({taskId:u,filePath:o}),a={taskId:u,name:d,...o!==void 0&&{attachedSourcePath:o}},s={parentId:u};}let i=await e.adapter.batchCreateTasks(r.map(d=>Fb(d,s))),c=[];for(let d of i.succeeded){let u=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:u?.name??"(unknown)",...f!==void 0&&{attachedSourcePath:f}});}e.cache!==void 0&&i.succeeded.length>0&&j(e.cache,{projectId:t.targetProjectId});let l=e.makeMeta({syncPending:i.succeeded.length>0});return p({phase:"created",parent:a,created:c,outcome:i},l)}function Rm(t,e){return t.registerTool("task_extract_from_image",{description:Yi,inputSchema:Dm.shape},async n=>{let r=await Eb(n,e);return m(r)})}var Mb=["add","build","call","check","create","draft","email","file","finish","fix","follow up","plan","prepare","research","review","schedule","send","set up","start","write"],Nb=/^\s*(?:\(\d+\)|\d+[.)])\s+(.+)$/,Ub=/^\s*(?:[-*•–—])\s+(.+)$/,Lb=/^\s*(?:✅|⏰|📝|📌|✏️|📞|📧|📤|📩|🔔|🚨|⚡️|➡️|→)\s+(.+)$/u;function Jb(t){return t.replace(/[\s.;,:]+$/,"").trim()}function Bb(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 Cm(t,e={}){let n=e.verbs??Mb,r=Bb(n),o=[],a=[],s=t.replace(/\r\n?/g,`
|
|
226
226
|
`).split(`
|
|
227
|
-
`);for(let s=0;s<i.length;s++){let c=s+1,d=i[s]??"";if(!d.trim())continue;let l=fb.exec(d),m=l?null:gb.exec(d),f=!l&&!m?hb.exec(d):null,g=!l&&!m&&!f?r.exec(d):null,I=null;if(l)I=l[1]??null;else if(m)I=m[1]??null;else if(f)I=f[1]??null;else if(g){let w=g[1]??"",R=g[2]??"";I=w&&R?`${w} ${R}`:null;}if(I===null){a.push(`L${c}: ${d.trim()}`);continue}let k=yb(I);if(!k){a.push(`L${c}: ${d.trim()}`);continue}o.push({name:k,sourceLines:[c]});}return {proposed:o,unmappedLines:a}}var Oi=`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 })`,vb=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.")}),Ib=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:v.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."),gm=z.object({source:Ib,targetProjectId:v.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(vb).optional().describe("Required when dryRun is false. The (possibly-edited) ProposedTask[] the agent has confirmed with the user.")}),Tb=gm.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation[] is required when dryRun is false",path:["confirmation"]});async function wb(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 bb(t,e){xe(Tb,t);let n=await wb(t.source,e.adapter),r=fm(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&&S(e.cache,{projectId:t.targetProjectId});let i=e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Dt(a.succeeded.length)});return p({phase:"created",outcome:a},i)}function hm(t,e){return t.registerTool("task_extract_from_note",{description:Oi,inputSchema:gm.shape},async n=>{let r=await bb(n,e);return u(r)})}var Pi='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" })',Sb=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 jb(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 ym(t,e){return t.registerTool("task_find_by_name",{description:Pi,inputSchema:Sb.shape},async n=>{let r=await jb(n,e);return u(r)})}var Di=`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 })`,km=5,vm=50,_b=z.object({projectId:v.schema.optional(),tagId:b.schema.optional()}).refine(t=>!(t.projectId!==void 0&&t.tagId!==void 0),{message:"Supply at most one of projectId or tagId"}),xb=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:_b.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(vm).default(km).describe(`Top-K candidates to return. Default ${km}, max ${vm}.`),includeCompleted:z.boolean().default(false).describe("When true, include completed and dropped tasks. Default false (open tasks only).")});async function Ob(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(k=>({task:k,s:nd(o,{name:k.name,note:k.note})})).filter(k=>k.s>0).sort((k,w)=>w.s-k.s).slice(0,t.limit),i=new Set,s=new Set;for(let{task:k}of a){k.projectId!==null&&i.add(k.projectId);for(let w of k.tagIds)s.add(w);}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((k,w)=>{let R=l[w];R!=null&&f.set(String(k),R.name);});let g=new Map;d.forEach((k,w)=>{let R=m[w];R!=null&&g.set(String(k),R.name);});let I=a.map(({task:k,s:w})=>{let R=k.projectId===null?null:String(k.projectId),T=R===null?null:f.get(R)??null;return {taskId:String(k.id),name:k.name,score:w,project:R===null||T===null?null:{id:R,name:T},tags:k.tagIds.map(D=>{let _=String(D),L=g.get(_);return L===void 0?null:{id:_,name:L}}).filter(D=>D!==null)}});return p({candidates:I},e.makeMeta())}function Im(t,e){return t.registerTool("task_find_similar",{description:Di,inputSchema:xb.shape},async n=>{let r=await Ob(n,e);return u(r)})}var Pb=new TextEncoder;function mt(t,e){if(e<0||t.note===null)return t;let n=Array.from(t.note);if(n.length<=e)return t;let{note:r,...o}=t;return {...o,notePreview:n.slice(0,e).join(""),noteTruncated:true,noteLength:Pb.encode(r).length}}var Ai='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" })',Db=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."),notePreviewChars:z.number().int().optional().describe(`Maximum characters of the task's note (and each subtask's note) to return. Default ${200}. When a note exceeds this length, the response replaces \`note\` with \`notePreview\` (the truncated text), \`noteTruncated: true\`, and \`noteLength\` (full UTF-8 byte length) \u2014 fetch the full text with note_get. Pass -1 to disable truncation and return full notes inline.`),verbose:z.boolean().optional().describe("When true, return the full unelided task shape (every field present, even at defaults). Default: false \u2014 fields equal to their documented default are omitted. See docs/token-cost.md for the defaults table.")});async function Ab(t,e){let{notePreviewChars:n,verbose:r,...o}=t,a=await e.taskService.get(o),i=xt(a.task.note),s=Ce(a.task.note),c=n??200,d=mt(a.task,c),l=a.subtasks?.map(g=>mt(g,c)),m=r===true?d:$e(d,Be),f=l===void 0?void 0:r===true?l:ye(l,Be);return p({task:m,...f!==void 0&&{subtasks:f},...i!==void 0&&{waitingOn:i},...s!==void 0&&{decision:s}},e.makeMeta({cacheHit:a.cacheHit}))}function Tm(t,e){return t.registerTool("task_get",{description:Ai,inputSchema:Db.shape},async n=>{let r=await Ab(n,e);return u(r)})}var Ri='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"] })',kr=100,Rb=z.object({ids:z.array(h.schema).min(0).max(kr).describe(`Array of task IDs to fetch (0..${kr}). Get IDs from task_list, search_query, or task_find_by_name. Missing IDs are omitted (not errors) and appear in meta.warnings.`),notePreviewChars:z.number().int().optional().describe(`Maximum characters of each task's note to return. Default ${200}. When a note exceeds this length, the response replaces \`note\` with \`notePreview\` (the truncated text), \`noteTruncated: true\`, and \`noteLength\` (full UTF-8 byte length) \u2014 fetch the full text with note_get. Pass -1 to disable truncation and return full notes inline.`),verbose:z.boolean().optional().describe("When true, return the full unelided task shape. Default: false \u2014 fields equal to their documented default are omitted. See docs/token-cost.md for the defaults table.")});async function Cb(t,e){if(t.ids.length===0)return p({tasks:[]},e.makeMeta());if(t.ids.length>kr)throw new y(`ids array exceeds the maximum batch size of ${kr} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTasksMany(t.ids),r=n.filter(I=>I!==null),o=t.ids.filter((I,k)=>n[k]===null),a={},i={};for(let I of r){let k=xt(I.note);k!==void 0&&(a[I.id]=k);let w=Ce(I.note);w!==void 0&&(i[I.id]=w);}let s=Object.keys(a).length>0,c=Object.keys(i).length>0,d=t.notePreviewChars??200,l=r.map(I=>mt(I,d)),m=t.verbose===true?l:ye(l,Be),f=o.length>0?[Ot(o)]:void 0,g=e.makeMeta({...f!==void 0?{warnings:f}:{}});return p({tasks:m,...s&&{waitingOn:a},...c&&{decisions:i}},g)}function wm(t,e){return t.registerTool("task_get_many",{description:Ri,inputSchema:Rb.shape},async n=>{let r=await Cb(n,e);return u(r)})}var bm=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 ot(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 Ci(t){return {self:`omnifocus://project/${t.id}`,folder:t.folderId!==null?`omnifocus://folder/${t.folderId}`:null}}function Wt(t){return createHash("sha256").update(Xe(t)).digest("hex")}function Ht(t){let e=JSON.stringify(t);return Buffer.from(e,"utf8").toString("base64url")}function Vt(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 zt(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 jm=z.enum(["dueDate","createdAt","modifiedAt","name"]),Sm=200,Fi=1e3,vr=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=Wt(r),a=e.cursor!==void 0?Vt(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:ot(i)};if(!n)return {task:s};let d=(await this.adapter.listTasks({parentId:e.id})).map(l=>({...l,_links:ot(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(_=>n.tagIds.every(L=>_.tagIds.includes(L))):s,{updatedSince:d}=n,l=d!==void 0?c.filter(_=>_.modifiedAt>d):c,{sortBy:m,sortDirection:f}=n,g=_=>{switch(m){case "dueDate":return _.dueDate??null;case "modifiedAt":return _.modifiedAt;case "name":return _.name;default:return _.createdAt}},I=[...l].sort((_,L)=>{let x=g(_),M=g(L);if(x===null&&M===null)return _.id<L.id?-1:1;if(x===null)return 1;if(M===null)return -1;if(x!==M){let N=x<M?-1:1;return f==="asc"?N:-N}return _.id<L.id?-1:1}),k=r!==void 0?I.filter(_=>zt({id:_.id,sortValue:g(_)},r,f)):I,w=k.slice(0,o),T=k.length>o?this.encodeNextCursor(w,a,g):null;return {tasks:w.map(_=>({..._,_links:ot(_)})),nextCursor:T}}resolveLimit(e){if(e.limit===void 0)return Sm;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>Fi)throw new y(`limit must be an integer between 1 and ${Fi}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${Fi}, or omit to use the default of ${Sm}.`,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(rn(e.updatedSince))r=e.updatedSince;else if(tn(e.updatedSince))r=nn(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 Ht({lastId:o.id,lastSortValue:r(o),filterHash:n})}};var Ei='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" })',Mb=z.object({projectId:v.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list. Omit for all projects."),tagIds:z.array(b.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:jm.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."),notePreviewChars:z.number().int().optional().describe(`Maximum characters of each task's note to return. Default ${200}. When a note exceeds this length, the response replaces \`note\` with \`notePreview\` (the truncated text), \`noteTruncated: true\`, and \`noteLength\` (full UTF-8 byte length) \u2014 fetch the full text with note_get. Pass -1 to disable truncation and return full notes inline.`),verbose:z.boolean().optional().describe("When true, return the full unelided task shape (every field present, even at defaults). Default: false \u2014 fields equal to their documented default (flagged: false, completed: false, tagIds: [], note: null, dueDate: null, etc.) are omitted from the wire payload. An omitted field means the default applies. See docs/token-cost.md for the full defaults table.")});async function Nb(t,e){let{notePreviewChars:n,verbose:r,...o}=t,a=o,i=await e.taskService.list(a),s={cursor:i.nextCursor,hasMore:i.hasMore},c=e.makeMeta({cacheHit:i.cacheHit}),d=n??200,l=i.tasks.map(f=>mt(f,d)),m=r===true?l:ye(l,Be);return p({tasks:m},c,s)}function _m(t,e){return t.registerTool("task_list",{description:Ei,inputSchema:Mb.shape},async n=>{let r=await Nb(n,e);return u(r)})}var Ni='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" })',Ui=z.object({id:h.schema.describe("Persistent ID of the task to move."),projectId:v.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 Ub(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:Mi(r.projectId,r.parentId)},e.makeMeta());let s=Mi(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&&(S(e.cache,{taskId:t.id,projectId:r.projectId}),t.projectId!==void 0&&t.projectId!==r.projectId&&S(e.cache,{projectId:t.projectId}));let d=t.toInbox===true?{inbox:true}:Mi(t.projectId??null,t.parentId??null);return p({moved:true,id:t.id,from:s,to:d},e.makeMeta({syncPending:true,humanReadableSummary:Ed(r.name,t.toInbox===true?"inbox":t.projectId!=null?"project":"parent task")}))}function Mi(t,e){return e!==null?{parentId:e}:t!==null?{projectId:t}:{inbox:true}}function Om(t,e){return t.registerTool("task_move",{description:Ni,inputSchema:Ui.shape},async n=>{let r=await Ub(n,e);return u(r)})}var Li="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 Lb(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 Rt(e.adapter,t.projectId);n.push({field:"projectId",newValue:t.projectId}),o=`project '${i}'`;}else if(t.parentId!==void 0){let i=await Ct(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 Pm(t,e){return t.registerTool("task_move_describe",{description:Li,inputSchema:Ui.shape},async n=>{let r=await Lb(n,e);return u(r)})}function Ji(t){let e=(g,I=2)=>String(g).padStart(I,"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 Dm(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 Am(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,I=c.indexOf("//");I!==-1&&(g=c.slice(I+2).trim(),c=c.slice(0,I).trim());let k=c.split(/\s+/).filter(D=>D.length>0),w=[];for(let D of k)if(D==="!!")f=true;else if(D.startsWith("::")){let _=D.slice(2);if(_.length>0){let{value:L,warning:x}=Dm(_,i,"Defer");m=L,x&&r.push(x);}}else if(D.startsWith("@")){let _=D.slice(1);_.length>0&&d.push(_);}else if(D.startsWith("#")){let _=D.slice(1);if(_.length>0){let{value:L,warning:x}=Dm(_,i,"Due");l=L,x&&r.push(x);}}else w.push(D);let R=w.join(" ").trim();if(R===""){r.push(`Line ${i}: task line has no name after removing tokens; skipping`);continue}let T={name:R};g!==void 0&&g.length>0&&(T.note=g),f&&(T.flagged=true),l!==void 0&&(T.dueDate=l),m!==void 0&&(T.deferDate=m),d.length>0&&(T.tagNames=d),o!==void 0&&(T.projectName=o),n.push(T);}return {tasks:n,warnings:r}}var Bi=`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" })`,Jb=z.object({text:z.string().min(1).describe("Transport text to parse. One task per line; 'Project: Name' prefix sets project context.")});async function Bb(t,e){let n=Am(t.text),r=e.makeMeta();return p({tasks:n.tasks,count:n.tasks.length,...n.warnings.length>0&&{warnings:n.warnings}},r)}function Cm(t,e){return t.registerTool("task_parse_transport_text",{description:Bi,inputSchema:Jb.shape},async n=>{let r=await Bb(n,e);return u(r)})}function wn(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=>wn(n,e));case "or":return t.predicates.some(n=>wn(n,e));case "not":return !wn(t.predicate,e)}}var Wi=`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" })`,$i=200,bn=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:b.schema.describe("Match tasks carrying this tag.")}),z.object({kind:z.literal("project"),projectId:v.schema.describe("Match tasks in this project.")}),z.object({kind:z.literal("and"),predicates:z.array(bn)}),z.object({kind:z.literal("or"),predicates:z.array(bn)}),z.object({kind:z.literal("not"),predicate:bn})]));bn.register(z.globalRegistry,{id:"TaskPredicate"});var $b=z.object({addTags:z.array(b.schema).optional().describe("Tag IDs to add to every match."),removeTags:z.array(b.schema).optional().describe("Tag IDs to remove from every match."),setProject:v.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"}),Fm=z.object({predicate:bn.describe("AST for selecting tasks. Composable via and/or/not. Always evaluated against open (non-completed, non-dropped) tasks."),changes:$b.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.')}),Wb=Fm.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation is required when dryRun is false",path:["confirmation"]});function Hb(t,e){let n=e.setProject!==void 0?String(e.setProject):t.projectId===null?null:String(t.projectId),r=Hs(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 Vb(t){return {projectId:t.projectId===null?null:String(t.projectId),tagIds:t.tagIds.map(String),flagged:t.flagged}}async function zb(t,e){xe(Wb,t);let r=(await e.adapter.listTasks({completed:false})).filter(s=>wn(t.predicate,s));if(t.dryRun){let s=r.map(c=>({taskId:String(c.id),name:c.name,before:Vb(c),after:Hb(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>$i)return p({phase:"over-cap",matched:r.length,cap:$i,message:`${r.length} matches exceeds the ${$i}-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 Vs({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 Em(t,e){return t.registerTool("task_reclassify",{description:Wi,inputSchema:Fm.shape},async n=>{let r=await zb(n,e);return u(r)})}var Hi='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" } })',qb=z.union([z.object({projectId:v.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."),Gb=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:qb.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 Kb(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&&(S(e.cache,{taskId:t.id,projectId:o}),t.at!==void 0&&t.in!==void 0&&"projectId"in t.in&&t.in.projectId!==o&&S(e.cache,{projectId:t.in.projectId})),p({reordered:true,id:t.id,position:a},e.makeMeta({syncPending:true,humanReadableSummary:Nd(r.name)}))}function Mm(t,e){return t.registerTool("task_reorder",{description:Hi,inputSchema:Gb.shape},async n=>{let r=await Kb(n,e);return u(r)})}var Vi='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" })',Nm={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:v.schema.optional().describe("Restrict search to tasks within this project."),tagIds:z.array(b.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.")},Xb=z.object(Nm).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 Yb(t,e){xe(Xb,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 Um(t,e){return t.registerTool("task_search",{description:Vi,inputSchema:Nm},async n=>{let r=await Yb(n,e);return u(r)})}var Lm=z.enum(["sunday","monday","tuesday","wednesday","thursday","friday","saturday"]),Zb=z.union([z.object({day:z.number().int().min(1).max(31)}),z.object({weekday:Lm,position:z.union([z.literal(1),z.literal(2),z.literal(3),z.literal(4),z.literal("last")])})]),zi=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()})]),qi=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(Lm).optional(),monthlyAnchor:Zb.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:v.schema.nullable(),parentId:h.schema.nullable(),tagIds:z.array(b.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:qi.nullable(),notifications:z.array(zi).optional(),createdAt:ae(),modifiedAt:ae(),_links:bm.optional()});var Gi=`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" }] })`,Qb=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),alarms:z.array(zi).describe("Full replacement set of alarms. Empty array is permitted and equivalent to task_clear_alarms.")});async function eS(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&&S(e.cache,{taskId:t.id,projectId:r.projectId});let o=e.makeMeta({syncPending:true,humanReadableSummary:Bd(n.name,t.alarms.length)});return p({task:r},o)}function Bm(t,e){return t.registerTool("task_set_alarms",{description:Gi,inputSchema:Qb.shape},async n=>{let r=await eS(n,e);return u(r)})}var Ki='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 } })',nS=z.object({id:h.schema.describe("ID of the task to update. Get from task_list or search_query."),rule:qi.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 rS(t,e){await e.adapter.updateTask(t.id,{repetition:t.rule});let n=await e.adapter.getTask(t.id);e.cache!==void 0&&S(e.cache,{taskId:t.id,projectId:n.projectId});let r=e.makeMeta({syncPending:true,humanReadableSummary:Ld(n.name)});return p({task:n},r)}function $m(t,e){return t.registerTool("task_set_repetition",{description:Ki,inputSchema:nS.shape},async n=>{let r=await rS(n,e);return u(r)})}var Xi='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" })',aS=z.object({id:h.schema.describe("Persistent task ID.")});async function sS(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&&S(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Ad(n.name)})))}function Wm(t,e){return t.registerTool("task_uncomplete",{description:Xi,inputSchema:aS.shape},async n=>{let r=await sS(n,e);return u(r)})}var Yi='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" })',cS=z.object({id:h.schema.describe("Persistent task ID.")});async function dS(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&&S(e.cache,{taskId:t.id,projectId:n.projectId}),p({done:true,id:t.id,name:n.name},e.makeMeta({syncPending:true,humanReadableSummary:Fd(n.name)})))}function Hm(t,e){return t.registerTool("task_undrop",{description:Yi,inputSchema:cS.shape},async n=>{let r=await dS(n,e);return u(r)})}var Zi='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"] })',Ir=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(b.schema).optional().describe("Full-replacement tag list. Replaces all existing tags. Mutually exclusive with addTags/removeTags."),addTags:z.array(b.schema).optional().describe("Tags to add. No-op for tags the task already has. Mutually exclusive with tagIds."),removeTags:z.array(b.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.")}),lS=Ir.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 pS(t,e){xe(lS,t);let{id:n,addTags:r,removeTags:o,setFlagged:a,tagIds:i,...s}=t,c=e.idempotencyStore??le;return de(c,t.idempotency_key,async()=>{let d=await e.adapter.getTask(n);et(t.expectedModifiedAt,d.modifiedAt,`task:${n}`);let l;if(r!==void 0||o!==void 0){let k=new Set(d.tagIds);for(let w of r??[])k.add(w);for(let w of o??[])k.delete(w);l=[...k];}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 k={...d,...f};return p({task:k},e.makeMeta({syncPending:false}))},I=async()=>{await e.adapter.updateTask(n,f);let k=await e.adapter.getTask(n);return e.cache!==void 0&&S(e.cache,{taskId:n,projectId:k.projectId}),p({task:k},e.makeMeta({syncPending:true}))};return Fe(t.dry_run,g,I)})}function Vm(t,e){return t.registerTool("task_update",{description:Zi,inputSchema:Ir.shape},async n=>{let r=await pS(n,e);return u(r)})}var Qi="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 uS(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 ${be(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 ${be(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 zm(t,e){return t.registerTool("task_update_describe",{description:Qi,inputSchema:Ir.shape},async n=>{let r=await uS(n,e);return u(r)})}var ec='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" }',tc='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" }',mS=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.")}),fS=z.object({taskId:h.schema.describe("Persistent task ID.")});async function gS(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 hS(t,e){let n=await e.adapter.getTask(t.taskId),r=fo.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=pd(n.note,r),a=await gS(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&&S(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,waitingOn:r},e.makeMeta({syncPending:true}))}function qm(t,e){return t.registerTool("task_set_waiting_on",{description:ec,inputSchema:mS.shape},async n=>{let r=await hS(n,e);return u(r)})}async function yS(t,e){let n=await e.adapter.getTask(t.taskId),r=ud(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&&S(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,cleared:true},e.makeMeta({syncPending:true}))}function Gm(t,e){return t.registerTool("task_clear_waiting_on",{description:tc,inputSchema:fS.shape},async n=>{let r=await yS(n,e);return u(r)})}var Km=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."),kS=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."),Xm=z.discriminatedUnion("on",[z.object({on:z.literal("task-completed"),filter:Km}),z.object({on:z.literal("task-created"),filter:Km}),z.object({on:z.literal("project-status-changed"),filter:kS})]);function nc(t){return {name:t.name,trigger:t.trigger,secretSet:t.secret!==void 0&&t.secret.length>0,createdAt:t.createdAt}}var Gt=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."),Ym=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."),Zm=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 rc='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" } } })',IS=z.object({name:Gt.describe("Stable name for the webhook. Unique within the registry; used as the lookup key. \u226464 chars, no whitespace."),url:Ym.describe("Outbound HTTPS URL. http:// is rejected at registration (per ADR-0016 \xA74b)."),trigger:Xm.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:Zm.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 Sn(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 TS(t,e){Sn(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 Qm(t,e){return t.registerTool("webhook_register",{description:rc,inputSchema:IS.shape},async n=>{let r=await TS(n,e);return u(r)})}var oc='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" })',bS=z.object({name:Gt.describe("Name of the registered webhook to delete. Idempotent \u2014 unknown names return noChange:true.")});async function SS(t,e){return Sn(e),e.registry.delete(t.name)?p({name:t.name,deleted:true},e.makeMeta()):p({name:t.name,noChange:true},e.makeMeta())}function ef(t,e){return t.registerTool("webhook_delete",{description:oc,inputSchema:bS.shape},async n=>{let r=await SS(n,e);return u(r)})}var ac="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()",_S=z.object({});async function xS(t,e){return Sn(e),p({webhooks:e.registry.list()},e.makeMeta())}function tf(t,e){return t.registerTool("webhook_list",{description:ac,inputSchema:_S.shape},async n=>{let r=await xS(n,e);return u(r)})}var sc=`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" })`,PS=z.object({name:Gt.describe("Name of the registered webhook to fire a synthetic event through.")});async function DS(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 nf(t,e){return t.registerTool("webhook_test",{description:sc,inputSchema:PS.shape},async n=>{let r=await DS(n,e);return u(r)})}var ic="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()",AS=z.object({});async function RS(t,e){let n=await e.adapter.getWindowState();return p(n,e.makeMeta())}function rf(t,e){return t.registerTool("window_get_state",{description:ic,inputSchema:AS.shape},async n=>u(await RS(n,e)))}var cc=`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" })`,CS=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 FS(t,e){let n=await e.adapter.setWindowPerspective(t.perspectiveName);return p(n,e.makeMeta())}function of(t,e){return t.registerTool("window_set_perspective",{description:cc,inputSchema:CS.shape},async n=>u(await FS(n,e)))}var dc="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 })",ES=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 MS(t,e){let n=await e.adapter.setWindowFocus(t.containerId);return p(n,e.makeMeta())}function af(t,e){return t.registerTool("window_set_focus",{description:dc,inputSchema:ES.shape},async n=>u(await MS(n,e)))}var lc="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()",NS=z.object({});async function US(t,e){let n=await e.adapter.appWindowNew();return p(n,e.makeMeta())}function sf(t,e){return t.registerTool("app_window_new",{description:lc,inputSchema:NS.shape},async n=>u(await US(n,e)))}var pc="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()",LS=z.object({});async function JS(t,e){let n=await e.adapter.appWindowNewTab();return p(n,e.makeMeta())}function cf(t,e){return t.registerTool("app_window_new_tab",{description:pc,inputSchema:LS.shape},async n=>u(await JS(n,e)))}var df={app_launch:wo,attachment_add:jo,attachment_list:So,attachment_remove:_o,attachment_save_to_path:xo,database_redo:Oo,database_undo:Po,decision_clear:Ao,decision_record:Ro,export_opml:Fo,export_taskpaper:No,forecast_get:Qo,forecast_get_tag:ea,forecast_pack:ta,forecast_set_tag:ra,folder_create:Lo,folder_create_describe:Bo,folder_delete:$o,folder_delete_describe:Ho,folder_get:Vo,folder_list:zo,folder_move:qo,folder_move_describe:Ko,folder_update:Xo,folder_update_describe:Zo,import_opml:Mo,import_taskpaper:Uo,internal_status:ua,note_append:oa,note_get:sa,note_get_html:ca,note_set:da,note_set_html:la,perspective_create:fa,perspective_delete:ga,perspective_evaluate:ha,perspective_evaluate_dry_run:ka,perspective_get:va,perspective_list:Ia,perspective_update:wa,plugin_invoke:Sa,project_batch_complete:_a,project_batch_drop:Oa,project_complete:Pa,project_complete_describe:Aa,project_create:Ea,project_create_describe:Na,project_delete:Ua,project_delete_describe:Ja,project_drop:Ba,project_drop_describe:Wa,project_get:Va,project_get_many:za,project_list:qa,project_mark_reviewed:gs,project_move:Ga,project_move_describe:Xa,project_template_instantiate:Qa,project_template_list:es,project_template_save:ns,project_update:rs,repetition_from_prose:us,project_update_describe:as,review_list_due:ms,review_mark_reviewed:fs,project_set_next_review_date:ks,review_set_interval:ys,run_jxa_script:is,run_omnijs_script:ds,search_query:vs,sync_status:Is,sync_trigger:Ts,tag_create:bs,tag_create_describe:js,tag_delete:_s,tag_delete_describe:Os,tag_get:Ps,tag_get_many:As,tag_get_location:Ds,tag_list:Rs,tag_move:Cs,tag_move_describe:Es,tag_set_allows_next_action:Ms,tag_set_location:Ns,tag_set_status:Us,tag_update:Js,task_batch_assign:Ws,tag_update_describe:$s,task_batch_complete:zs,task_batch_create:qs,task_batch_create_describe:Ks,task_batch_defer_smart:Zs,task_batch_delete:Qs,task_batch_drop:ti,task_batch_move:ni,task_batch_uncomplete:oi,task_batch_undrop:si,task_batch_update:ii,task_batch_update_describe:di,task_complete:ui,task_complete_describe:fi,task_create:hi,task_create_describe:yi,task_defer_smart:ki,task_drop:wi,task_drop_describe:Si,task_clear_alarms:li,task_clear_repetition:pi,task_delete:vi,task_delete_describe:Ti,task_duplicate:ji,task_extract_from_image:xi,task_extract_from_note:Oi,task_find_by_name:Pi,task_find_similar:Di,task_get:Ai,task_get_many:Ri,task_list:Ei,task_convert_to_project:gi,task_move:Ni,task_move_describe:Li,task_search:Vi,task_parse_transport_text:Bi,task_reclassify:Wi,task_reorder:Hi,task_clear_waiting_on:tc,task_set_alarms:Gi,task_set_repetition:Ki,task_set_waiting_on:ec,task_uncomplete:Xi,task_undrop:Yi,task_update:Zi,app_window_new:lc,app_window_new_tab:pc,window_get_state:ic,window_set_focus:dc,window_set_perspective:cc,task_update_describe:Qi,webhook_delete:oc,webhook_list:ac,webhook_register:rc,webhook_test:sc};var BS=`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 })`,$S=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 WS(t,e){let n=e.replayStore??we,r=e.makeMeta(),o=n.consume(t.replayToken);return o===void 0?To(new j(`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?To(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 lf(t,e){return t.registerTool("clarify",{description:BS,inputSchema:$S.shape},async n=>{let r=await WS(n,e);return u(r)})}var HS=`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" }.`,VS=z.object({templateName:z.string().min(1).describe("Name of the template to delete. Matched case-insensitively within the Templates folder.")}),Tr=class extends j{constructor(e){super(`No template named "${e}" was found in the Templates folder.`,{details:{templateName:e}});}};async function zS(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 Tr(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),i=t.templateName.toLowerCase(),s=a.find(c=>c.name.toLowerCase()!==i?false:Nt(c.note)!==void 0);if(s===void 0)throw new Tr(t.templateName);return await e.adapter.deleteProject(s.id),e.cache!==void 0&&B(e.cache,{projectId:s.id}),p({deleted:true,templateName:t.templateName},e.makeMeta({syncPending:true}))}function uf(t,e){return t.registerTool("project_template_delete",{description:HS,inputSchema:VS.shape},async n=>{let r=await zS(n,e);return u(r)})}function mf(){let t=process.env.OF_DB_PATH;if(t&&t.length>0)return t;let e=GS.homedir(),n=wr.join(e,"Library","Application Support","OmniFocus","OmniFocus.ofocus"),r=a=>wr.join(e,"Library","Containers",a,"Data","Library","Application Support","OmniFocus","OmniFocus.ofocus"),o=[r("com.omnigroup.OmniFocus4"),r("com.omnigroup.OmniFocus3"),n];for(let a of o)if(gt.existsSync(a))return a;return n}mf();function YS(){let t=fileURLToPath(import.meta.url);return wr.resolve(wr.dirname(t),"../../bin/omnifocus-watcher")}var br=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??mf(),this.binaryPath=n.binaryPath!==void 0?n.binaryPath:YS();}start(){if(this.started)return;if(!gt.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{gt.accessSync(this.binaryPath,gt.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(gt.existsSync(this.dbPath))try{this.nodeWatcher=gt.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 ej=5e3,jn=[1e3,5e3,3e4],ff=10,gf=3600*1e3,tj="X-OmniFocus-Signature";function nj(t){return ({url:e,body:n,headers:r,timeoutMs:o})=>new Promise((a,i)=>{let s=new URL(e),c=t({method:"POST",host:s.host,path:`${s.pathname}${s.search}`,headers:{"Content-Length":Buffer.byteLength(n),...r},timeout:o},d=>{d.on("error",i),d.on("data",()=>{}),d.on("end",()=>{a({statusCode:d.statusCode??0});});});c.on("error",i),c.on("timeout",()=>{c.destroy(new Error(`request timeout after ${o}ms`));}),c.write(n),c.end();})}var rj=nj(QS.request),Sr=class{request;now;sleep;write;circuits=new Map;constructor(e={}){this.request=e.request??rj,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: ${hf(l)}; dropping.
|
|
227
|
+
`);for(let i=0;i<s.length;i++){let c=i+1,l=s[i]??"";if(!l.trim())continue;let d=Nb.exec(l),u=d?null:Ub.exec(l),f=!d&&!u?Lb.exec(l):null,g=!d&&!u&&!f?r.exec(l):null,I=null;if(d)I=d[1]??null;else if(u)I=u[1]??null;else if(f)I=f[1]??null;else if(g){let w=g[1]??"",S=g[2]??"";I=w&&S?`${w} ${S}`:null;}if(I===null){a.push(`L${c}: ${l.trim()}`);continue}let h=Jb(I);if(!h){a.push(`L${c}: ${l.trim()}`);continue}o.push({name:h,sourceLines:[c]});}return {proposed:o,unmappedLines:a}}var Zi=`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 })`,$b=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("Task name."),note:z$1.string().max(1048576,"max 1 MB").optional().describe("Optional note body for the created task."),deferDate:z$1.string().datetime({offset:true}).optional(),dueDate:z$1.string().datetime({offset:true}).optional(),tags:z$1.array(z$1.string()).optional().describe("Tag NAMES \u2014 resolved by the agent before passing here, since this tool does not look up tag IDs."),sourceLines:z$1.array(z$1.number().int().nonnegative()).optional().describe("1-based source line numbers from the original prose; preserved when surfacing proposals to the user.")}),Wb=z$1.discriminatedUnion("kind",[z$1.object({kind:z$1.literal("task"),taskId:y.schema.describe("Task whose note will be parsed.")}),z$1.object({kind:z$1.literal("project"),projectId:v.schema.describe("Project whose note will be parsed.")}),z$1.object({kind:z$1.literal("inline"),text:z$1.string().min(1).describe("Raw prose to parse \u2014 agent supplies directly.")})]).describe("Where to read prose from."),Fm=z$1.object({source:Wb,targetProjectId:v.schema.describe("Project that will receive created tasks on dryRun=false. Read-only on dryRun=true."),dryRun:z$1.boolean().default(true).describe("Default true \u2014 return proposals without creating. false requires confirmation[]."),confirmation:z$1.array($b).optional().describe("Required when dryRun is false. The (possibly-edited) ProposedTask[] the agent has confirmed with the user.")}),Hb=Fm.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation[] is required when dryRun is false",path:["confirmation"]});async function zb(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 Vb(t,e){Ee(Hb,t);let n=await zb(t.source,e.adapter),r=Cm(n);if(t.dryRun||!t.confirmation){let i=e.makeMeta();return p({phase:"dryRun",proposed:r.proposed,unmappedLines:r.unmappedLines},i)}let o=t.confirmation.map(i=>({name:i.name,projectId:t.targetProjectId,...i.note!==void 0&&{note:i.note},...i.deferDate!==void 0&&{deferDate:i.deferDate},...i.dueDate!==void 0&&{dueDate:i.dueDate}})),a=await e.adapter.batchCreateTasks(o);e.cache!==void 0&&a.succeeded.length>0&&j(e.cache,{projectId:t.targetProjectId});let s=e.makeMeta({syncPending:a.succeeded.length>0,humanReadableSummary:Ht(a.succeeded.length)});return p({phase:"created",outcome:a},s)}function Em(t,e){return t.registerTool("task_extract_from_note",{description:Zi,inputSchema:Fm.shape},async n=>{let r=await Vb(n,e);return m(r)})}var Qi='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" })',qb=z$1.object({query:z$1.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$1.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$1.boolean().optional().describe("true = match is case-sensitive; false = case-insensitive (default false)."),limit:z$1.number().int().min(1).max(500).optional().describe("Maximum number of results to return (1..500). Default 50.")});async function Gb(t,e){let n=t.mode??"exact",r=t.caseSensitive??false,o=t.limit??50,a=await e.adapter.listTasks({}),s=u=>r?u:u.toLowerCase(),i=s(t.query),c=a.filter(u=>{let f=s(u.name);switch(n){case "exact":return f===i;case "prefix":return f.startsWith(i);case "contains":return f.includes(i);default:return false}}),l=c.slice(0,o),d=e.makeMeta();return p({tasks:l,matchCount:c.length},d)}function Mm(t,e){return t.registerTool("task_find_by_name",{description:Qi,inputSchema:qb.shape},async n=>{let r=await Gb(n,e);return m(r)})}var ec=`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 })`,Nm=5,Um=50,Kb=z$1.object({projectId:v.schema.optional(),tagId:b.schema.optional()}).refine(t=>!(t.projectId!==void 0&&t.tagId!==void 0),{message:"Supply at most one of projectId or tagId"}),Xb=z$1.object({name:z$1.string().min(1).max(1024,"max 1 KB").describe("The candidate task name to compare against existing tasks."),note:z$1.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:Kb.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$1.number().int().min(1).max(Um).default(Nm).describe(`Top-K candidates to return. Default ${Nm}, max ${Um}.`),includeCompleted:z$1.boolean().default(false).describe("When true, include completed and dropped tasks. Default false (open tasks only).")});async function Yb(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(h=>({task:h,s:bd(o,{name:h.name,note:h.note})})).filter(h=>h.s>0).sort((h,w)=>w.s-h.s).slice(0,t.limit),s=new Set,i=new Set;for(let{task:h}of a){h.projectId!==null&&s.add(h.projectId);for(let w of h.tagIds)i.add(w);}let c=[...s],l=[...i],[d,u]=await Promise.all([c.length>0?e.adapter.getProjectsMany(c):Promise.resolve([]),l.length>0?e.adapter.getTagsMany(l):Promise.resolve([])]),f=new Map;c.forEach((h,w)=>{let S=d[w];S!=null&&f.set(String(h),S.name);});let g=new Map;l.forEach((h,w)=>{let S=u[w];S!=null&&g.set(String(h),S.name);});let I=a.map(({task:h,s:w})=>{let S=h.projectId===null?null:String(h.projectId),T=S===null?null:f.get(S)??null;return {taskId:String(h.id),name:h.name,score:w,project:S===null||T===null?null:{id:S,name:T},tags:h.tagIds.map(x=>{let O=String(x),L=g.get(O);return L===void 0?null:{id:O,name:L}}).filter(x=>x!==null)}});return p({candidates:I},e.makeMeta())}function Lm(t,e){return t.registerTool("task_find_similar",{description:ec,inputSchema:Xb.shape},async n=>{let r=await Yb(n,e);return m(r)})}var Zb=new TextEncoder;function xt(t,e){if(e<0||t.note===void 0||t.note===null)return t;let n=Array.from(t.note);if(n.length<=e)return t;let{note:r,...o}=t;return {...o,notePreview:n.slice(0,e).join(""),noteTruncated:true,noteLength:Zb.encode(r).length}}var tc='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 subtaskIds[] and subtaskCount (when includeSubtasks omitted or false). Pass includeSubtasks: true to get full subtask bodies; use task_get_many to fetch specific subtasks by ID. Read-only; safe to retry. Example: task_get({ id: "abc123" })',Qb=z$1.object({id:y.schema.describe("Persistent ID of the task to fetch. Get from task_list or task_get_many."),includeSubtasks:z$1.boolean().optional().describe("Include full subtask bodies in the response. Default false \u2014 returns subtaskIds[] and subtaskCount instead. Pass true only when you need subtask detail; otherwise use task_get_many with subtaskIds."),notePreviewChars:z$1.number().int().optional().describe(`Maximum characters of the task's note (and each subtask's note) to return. Default ${200}. When a note exceeds this length, the response replaces \`note\` with \`notePreview\` (the truncated text), \`noteTruncated: true\`, and \`noteLength\` (full UTF-8 byte length) \u2014 fetch the full text with note_get. Pass -1 to disable truncation and return full notes inline.`),verbose:z$1.boolean().optional().describe("When true, return the full unelided task shape (every field present, even at defaults). Default: false \u2014 fields equal to their documented default are omitted. See docs/token-cost.md for the defaults table."),fields:z$1.array(z$1.string()).optional().describe("Restrict the returned task (and each subtask) to this list of top-level fields (id is always returned). Omit for the full task shape. Empty array returns just id. Unknown names surface in meta.warnings.WARN_UNKNOWN_FIELDS.")});async function eS(t,e){let{notePreviewChars:n,verbose:r,fields:o,...a}=t,s=await e.taskService.get(a),i=Bt(s.task.note),c=We(s.task.note),l=n??200,d=o!==void 0?ee(o,De):void 0,u=d?.valid,f=d!==void 0&&d.unknown.length>0?[Q([...d.unknown],fe)]:void 0,g=xt(X(s.task,u),l),I=s.subtasks?.map(T=>xt(X(T,u),l)),h=r!==true&&u===void 0,w=h?_e(g,Qe):g,S=I===void 0?void 0:h?He(I,Qe):I;return p({task:w,...s.subtaskIds!==void 0&&{subtaskIds:s.subtaskIds},...s.subtaskCount!==void 0&&{subtaskCount:s.subtaskCount},...S!==void 0&&{subtasks:S},...i!==void 0&&{waitingOn:i},...c!==void 0&&{decision:c}},e.makeMeta({cacheHit:s.cacheHit,...f!==void 0?{warnings:f}:{}}))}function Jm(t,e){return t.registerTool("task_get",{description:tc,inputSchema:Qb.shape},async n=>{let r=await eS(n,e);return m(r)})}var nc='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"] })',Ur=100,tS=z$1.object({ids:z$1.array(y.schema).min(0).max(Ur).describe(`Array of task IDs to fetch (0..${Ur}). Get IDs from task_list, search_query, or task_find_by_name. Missing IDs are omitted (not errors) and appear in meta.warnings.`),notePreviewChars:z$1.number().int().optional().describe(`Maximum characters of each task's note to return. Default ${200}. When a note exceeds this length, the response replaces \`note\` with \`notePreview\` (the truncated text), \`noteTruncated: true\`, and \`noteLength\` (full UTF-8 byte length) \u2014 fetch the full text with note_get. Pass -1 to disable truncation and return full notes inline.`),verbose:z$1.boolean().optional().describe("When true, return the full unelided task shape. Default: false \u2014 fields equal to their documented default are omitted. See docs/token-cost.md for the defaults table."),fields:z$1.array(z$1.string()).optional().describe("Restrict each returned task to this list of top-level fields (id is always returned). Omit for the full task shape. Empty array returns just id. Unknown names surface in meta.warnings.WARN_UNKNOWN_FIELDS.")});async function nS(t,e){if(t.ids.length===0)return p({tasks:[]},e.makeMeta());if(t.ids.length>Ur)throw new k(`ids array exceeds the maximum batch size of ${Ur} (got ${t.ids.length})`,{details:{field:"ids"}});let n=await e.adapter.getTasksMany(t.ids),r=n.filter(S=>S!==null),o=t.ids.filter((S,T)=>n[T]===null),a={},s={};for(let S of r){let T=Bt(S.note);T!==void 0&&(a[S.id]=T);let x=We(S.note);x!==void 0&&(s[S.id]=x);}let i=Object.keys(a).length>0,c=Object.keys(s).length>0,l=t.notePreviewChars??200,d=t.fields!==void 0?ee(t.fields,De):void 0,u=d?.valid,f=r.map(S=>xt(X(S,u),l)),I=t.verbose!==true&&u===void 0?He(f,Qe):f,h=[];o.length>0&&h.push($t(o)),d!==void 0&&d.unknown.length>0&&h.push(Q([...d.unknown],fe));let w=e.makeMeta({...h.length>0?{warnings:h}:{}});return p({tasks:I,...i&&{waitingOn:a},...c&&{decisions:s}},w)}function Bm(t,e){return t.registerTool("task_get_many",{description:nc,inputSchema:tS.shape},async n=>{let r=await nS(n,e);return m(r)})}function Me(t){return createHash("sha256").update(ct(t)).digest("hex").slice(0,16)}function sn(t){let e=JSON.stringify(t);return Buffer.from(e,"utf8").toString("base64url")}function cn(t,e){let n;try{n=Buffer.from(t,"base64url").toString("utf8");}catch{throw new k("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 k("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 k("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 k("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 dn(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 Wm=z$1.enum(["dueDate","createdAt","modifiedAt","name"]),$m=50,rc=1e3,Lr=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=Me(r),a=e.cursor!==void 0?cn(e.cursor,o):void 0,s=this.cacheKeyFor(o,e.cursor),i=this.cache.has(s),{tasks:c,nextCursor:l}=await this.cache.wrap(s,async()=>this.fetchPage(e,r,a,n,o));return {tasks:c,nextCursor:l,hasMore:l!==null,cacheHit:i}}async get(e){let n=e.includeSubtasks??false,r=`task:${e.id}:${n?"with-subtasks":"ids-only"}`,o=this.cache.has(r);return {...await this.cache.wrap(r,async()=>{let s=await this.adapter.getTask(e.id),i={...s,_links:ut(s)},c=await this.adapter.listTasks({parentId:e.id});if(n){let d=c.map(u=>({...u,_links:ut(u)}));return {task:i,subtasks:d}}let l=c.map(d=>d.id);return {task:i,subtaskIds:l,subtaskCount:l.length}}),cacheHit:o}}async fetchPage(e,n,r,o,a){let s=this.toAdapterFilter(e,n),i=await this.adapter.listTasks(s),c=n.tagIds.length>1?i.filter(O=>n.tagIds.every(L=>O.tagIds.includes(L))):i,{updatedSince:l}=n,d=l!==void 0?c.filter(O=>O.modifiedAt>l):c,{sortBy:u,sortDirection:f}=n,g=O=>{switch(u){case "dueDate":return O.dueDate??null;case "modifiedAt":return O.modifiedAt;case "name":return O.name;default:return O.createdAt}},I=[...d].sort((O,L)=>{let A=g(O),M=g(L);if(A===null&&M===null)return O.id<L.id?-1:1;if(A===null)return 1;if(M===null)return -1;if(A!==M){let N=A<M?-1:1;return f==="asc"?N:-N}return O.id<L.id?-1:1}),h=r!==void 0?I.filter(O=>dn({id:O.id,sortValue:g(O)},r,f)):I,w=h.slice(0,o),T=h.length>o?this.encodeNextCursor(w,a,g):null;return {tasks:w.map(O=>({...O,_links:ut(O)})),nextCursor:T}}resolveLimit(e){if(e.limit===void 0)return $m;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>rc)throw new k(`limit must be an integer between 1 and ${rc}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${rc}, or omit to use the default of ${$m}.`,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 k("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(In(e.updatedSince))r=e.updatedSince;else if(kn(e.updatedSince))r=vn(e.updatedSince);else throw new k(`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 k("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 k("Internal: cannot encode cursor for empty page.");return sn({lastId:o.id,lastSortValue:r(o),filterHash:n})}};var oc='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" })',aS=z$1.object({projectId:v.schema.optional().describe("Restrict to tasks in this project. Get the ID from project_list. Omit for all projects."),tagIds:z$1.array(b.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs. Get IDs from tag_list."),flagged:z$1.boolean().optional().describe("true = flagged only; false = unflagged only; omit = all."),available:z$1.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred). Omit = all."),completed:z$1.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only; 'only' = completed tasks only; 'any' = both. Omit for adapter default."),dueBefore:z$1.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$1.string().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset."),deferredBefore:z$1.string().optional().describe("Tasks deferred until before this moment (already unlocked or soon). ISO-8601 with offset."),parentId:y.schema.optional().describe("Restrict to direct children of this task (subtasks). Get the ID from task_get or task_list."),limit:z$1.number().int().min(1).max(1e3).optional().describe("Max tasks per page (1..1000). Default 50. Use `cursor` to fetch subsequent pages."),sortBy:Wm.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$1.enum(["asc","desc"]).optional().describe("Sort direction: 'asc' (default, oldest/lowest first) or 'desc' (newest/highest first)."),updatedSince:Ye().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$1.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$1.string().optional().describe("Opaque cursor from a previous task_list response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError."),notePreviewChars:z$1.number().int().optional().describe(`Maximum characters of each task's note to return. Default ${200}. When a note exceeds this length, the response replaces \`note\` with \`notePreview\` (the truncated text), \`noteTruncated: true\`, and \`noteLength\` (full UTF-8 byte length) \u2014 fetch the full text with note_get. Pass -1 to disable truncation and return full notes inline.`),verbose:z$1.boolean().optional().describe("When true, return the full unelided task shape (every field present, even at defaults). Default: false \u2014 fields equal to their documented default (flagged: false, completed: false, tagIds: [], note: null, dueDate: null, etc.) are omitted from the wire payload. An omitted field means the default applies. See docs/token-cost.md for the full defaults table."),fields:z$1.array(z$1.string()).optional().describe("Restrict each returned task to this list of top-level fields (id is always returned). Omit for the full task shape. Empty array returns just id. Unknown names surface in meta.warnings.WARN_UNKNOWN_FIELDS.")});async function sS(t,e){let{notePreviewChars:n,verbose:r,fields:o,...a}=t,s=a,i=await e.taskService.list(s),c={cursor:i.nextCursor,hasMore:i.hasMore},l=n??200,d=o!==void 0?ee(o,De):void 0,u=d?.valid,f=d!==void 0&&d.unknown.length>0?[Q([...d.unknown],fe)]:void 0,g=r!==true&&u===void 0,I=i.tasks.map(w=>{let S=X(w,u),T=xt(S,l);return g?_e(T,Qe):T}),h=e.makeMeta({cacheHit:i.cacheHit,...f!==void 0?{warnings:f}:{}});return p({tasks:I},h,c)}function Hm(t,e){return t.registerTool("task_list",{description:oc,inputSchema:aS.shape},async n=>{let r=await sS(n,e);return m(r)})}var sc='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" })',ic=z$1.object({id:y.schema.describe("Persistent ID of the task to move."),projectId:v.schema.optional().describe("Move into this project. Mutually exclusive with parentId and toInbox."),parentId:y.schema.optional().describe("Move under this parent task (as a subtask). Mutually exclusive with projectId and toInbox."),toInbox:z$1.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 k("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,s=t.toInbox===true&&r.projectId===null&&r.parentId===null;if(o||a||s)return p({noChange:true,id:t.id,at:ac(r.projectId,r.parentId)},e.makeMeta());let i=ac(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}:ac(t.projectId??null,t.parentId??null);return p({moved:true,id:t.id,from:i,to:l},e.makeMeta({syncPending:true,humanReadableSummary:nl(r.name,t.toInbox===true?"inbox":t.projectId!=null?"project":"parent task")}))}function ac(t,e){return e!==null?{parentId:e}:t!==null?{projectId:t}:{inbox:true}}function Vm(t,e){return t.registerTool("task_move",{description:sc,inputSchema:ic.shape},async n=>{let r=await iS(n,e);return m(r)})}var cc="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 cS(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 s=await Vt(e.adapter,t.projectId);n.push({field:"projectId",newValue:t.projectId}),o=`project '${s}'`;}else if(t.parentId!==void 0){let s=await qt(e.adapter,t.parentId);n.push({field:"parentId",newValue:t.parentId}),o=`subtask of '${s}'`;}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 qm(t,e){return t.registerTool("task_move_describe",{description:cc,inputSchema:ic.shape},async n=>{let r=await cS(n,e);return m(r)})}function dc(t){let e=(g,I=2)=>String(g).padStart(I,"0"),n=t.getFullYear(),r=e(t.getMonth()+1),o=e(t.getDate()),a=e(t.getHours()),s=e(t.getMinutes()),i=e(t.getSeconds()),c=-t.getTimezoneOffset(),l=c>=0?"+":"-",d=Math.abs(c),u=e(Math.floor(d/60)),f=e(d%60);return `${n}-${r}-${o}T${a}:${s}:${i}${l}${u}:${f}`}function Gm(t,e,n){let r=t.toLowerCase(),o=new Date;if(r==="today"){let s=new Date(o.getFullYear(),o.getMonth(),o.getDate(),0,0,0);return {value:dc(s)}}if(r==="tomorrow"){let s=new Date(o.getFullYear(),o.getMonth(),o.getDate()+1,0,0,0);return {value:dc(s)}}if(/^\d{4}-\d{2}-\d{2}(T.*)?$/.test(t)){let s=new Date(t);if(!Number.isNaN(s.getTime())){if(/^\d{4}-\d{2}-\d{2}$/.test(t)){let i=t.split("-"),c=Number(i[0]),l=Number(i[1]),d=Number(i[2]),u=new Date(c,l-1,d,0,0,0);return {value:dc(u)}}return {value:t}}}return {value:t,warning:`Line ${e}: ${n} date '${t}' is not a recognized date format; passing through as-is`}}function Km(t){let e=t.split(`
|
|
228
|
+
`),n=[],r=[],o;for(let a=0;a<e.length;a++){let s=a+1,i=(e[a]??"").trim();if(i==="")continue;if(/^Project:\s*/i.test(i)){o=i.replace(/^Project:\s*/i,"").trim()||void 0;continue}let c=i,l=[],d,u,f=false,g,I=c.indexOf("//");I!==-1&&(g=c.slice(I+2).trim(),c=c.slice(0,I).trim());let h=c.split(/\s+/).filter(x=>x.length>0),w=[];for(let x of h)if(x==="!!")f=true;else if(x.startsWith("::")){let O=x.slice(2);if(O.length>0){let{value:L,warning:A}=Gm(O,s,"Defer");u=L,A&&r.push(A);}}else if(x.startsWith("@")){let O=x.slice(1);O.length>0&&l.push(O);}else if(x.startsWith("#")){let O=x.slice(1);if(O.length>0){let{value:L,warning:A}=Gm(O,s,"Due");d=L,A&&r.push(A);}}else w.push(x);let S=w.join(" ").trim();if(S===""){r.push(`Line ${s}: task line has no name after removing tokens; skipping`);continue}let T={name:S};g!==void 0&&g.length>0&&(T.note=g),f&&(T.flagged=true),d!==void 0&&(T.dueDate=d),u!==void 0&&(T.deferDate=u),l.length>0&&(T.tagNames=l),o!==void 0&&(T.projectName=o),n.push(T);}return {tasks:n,warnings:r}}var lc=`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" })`,dS=z$1.object({text:z$1.string().min(1).describe("Transport text to parse. One task per line; 'Project: Name' prefix sets project context.")});async function lS(t,e){let n=Km(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:lc,inputSchema:dS.shape},async n=>{let r=await lS(n,e);return m(r)})}function Un(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=>Un(n,e));case "or":return t.predicates.some(n=>Un(n,e));case "not":return !Un(t.predicate,e)}}var uc=`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" })`,pc=200,Ln=z$1.lazy(()=>z$1.discriminatedUnion("kind",[z$1.object({kind:z$1.literal("title-contains"),value:z$1.string().max(4096,"max 4 KB").describe("Substring to search for in task names."),caseSensitive:z$1.boolean().optional().describe("When true, exact-case match. Default false (case-insensitive).")}),z$1.object({kind:z$1.literal("tag"),tagId:b.schema.describe("Match tasks carrying this tag.")}),z$1.object({kind:z$1.literal("project"),projectId:v.schema.describe("Match tasks in this project.")}),z$1.object({kind:z$1.literal("and"),predicates:z$1.array(Ln)}),z$1.object({kind:z$1.literal("or"),predicates:z$1.array(Ln)}),z$1.object({kind:z$1.literal("not"),predicate:Ln})]));Ln.register(z$1.globalRegistry,{id:"TaskPredicate"});var uS=z$1.object({addTags:z$1.array(b.schema).optional().describe("Tag IDs to add to every match."),removeTags:z$1.array(b.schema).optional().describe("Tag IDs to remove from every match."),setProject:v.schema.optional().describe("Move every match to this project."),setFlagged:z$1.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"}),Zm=z$1.object({predicate:Ln.describe("AST for selecting tasks. Composable via and/or/not. Always evaluated against open (non-completed, non-dropped) tasks."),changes:uS.describe("Changes applied uniformly to every matched task."),dryRun:z$1.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$1.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.')}),mS=Zm.refine(t=>t.dryRun||t.confirmation!==void 0,{message:"confirmation is required when dryRun is false",path:["confirmation"]});function fS(t,e){let n=e.setProject!==void 0?String(e.setProject):t.projectId===null?null:String(t.projectId),r=fi(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 gS(t){return {projectId:t.projectId===null?null:String(t.projectId),tagIds:t.tagIds.map(String),flagged:t.flagged}}async function hS(t,e){Ee(mS,t);let r=(await e.adapter.listTasks({completed:false})).filter(i=>Un(t.predicate,i));if(t.dryRun){let i=r.map(c=>({taskId:String(c.id),name:c.name,before:gS(c),after:fS(c,t.changes)}));return p({phase:"dryRun",matched:r.length,proposed:i},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>pc)return p({phase:"over-cap",matched:r.length,cap:pc,message:`${r.length} matches exceeds the ${pc}-task hard cap; use task_batch_update with explicit IDs for larger sets`},e.makeMeta());let a=r.map(i=>({taskId:y.of(String(i.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 s=await gi({assignments:a},e);return "data"in s?p({phase:"applied",matched:r.length,assigned:s.data.assigned,failed:s.data.failed},e.makeMeta({syncPending:s.data.assigned.length>0})):s}function Qm(t,e){return t.registerTool("task_reclassify",{description:uc,inputSchema:Zm.shape},async n=>{let r=await hS(n,e);return m(r)})}var mc='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" } })',yS=z$1.union([z$1.object({projectId:v.schema}),z$1.object({parentId:y.schema}),z$1.object({inbox:z$1.literal(true)})]).describe("Container for start/end positioning. Exactly one of projectId, parentId, or inbox: true."),kS=z$1.object({id:y.schema.describe("Persistent ID of the task to reorder."),before:y.schema.optional().describe("Position the task immediately before this sibling. Reference must share the same parent."),after:y.schema.optional().describe("Position the task immediately after this sibling. Reference must share the same parent."),at:z$1.enum(["start","end"]).optional().describe("Absolute position within a container. Requires `in` to identify the container."),in:yS.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 vS(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 k("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 k("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 k("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 k("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:ol(r.name)}))}function ef(t,e){return t.registerTool("task_reorder",{description:mc,inputSchema:kS.shape},async n=>{let r=await vS(n,e);return m(r)})}var fc='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" })',tf={q:z$1.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$1.enum(["name","note","all"]).optional().describe("'name' = search task name only; 'note' = search note only; 'all' = both (default). Ignored when q is omitted."),projectId:v.schema.optional().describe("Restrict search to tasks within this project."),tagIds:z$1.array(b.schema).optional().describe("Restrict to tasks carrying ALL of these tag IDs."),available:z$1.boolean().optional().describe("true = only tasks available to work on now (not blocked, not deferred, not completed). Omit = all."),dueBefore:Ye().optional().describe("Tasks with dueDate strictly before this moment. ISO-8601 with offset or relative shortcut."),dueAfter:Ye().optional().describe("Tasks with dueDate strictly after this moment. ISO-8601 with offset or relative shortcut."),flagged:z$1.boolean().optional().describe("true = flagged tasks only; false = unflagged only; omit = all."),completed:z$1.enum(["any","only","exclude"]).optional().describe("'exclude' = active tasks only (default); 'only' = completed tasks only; 'any' = both."),limit:z$1.number().int().min(1).max(500).optional().describe("Max results per page (1..500). Default 100. Use cursor to fetch subsequent pages."),cursor:z$1.string().optional().describe("Opaque cursor from a previous task_search response. Must use the same filters \u2014 changing filters mid-sequence returns a ValidationError.")},IS=z$1.object(tf).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 TS(t,e){Ee(IS,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 nf(t,e){return t.registerTool("task_search",{description:fc,inputSchema:tf},async n=>{let r=await TS(n,e);return m(r)})}var gc=`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" }] })`,wS=z$1.object({id:y.schema.describe("ID of the task to update. Get from task_list or search_query."),alarms:z$1.array(wa).describe("Full replacement set of alarms. Empty array is permitted and equivalent to task_clear_alarms.")});async function bS(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 k("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 k("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:cl(n.name,t.alarms.length)});return p({task:r},o)}function of(t,e){return t.registerTool("task_set_alarms",{description:gc,inputSchema:wS.shape},async n=>{let r=await bS(n,e);return m(r)})}var hc='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 } })',jS=z$1.object({id:y.schema.describe("ID of the task to update. Get from task_list or search_query."),rule:ba.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 _S(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:sl(n.name)});return p({task:n},r)}function af(t,e){return t.registerTool("task_set_repetition",{description:hc,inputSchema:jS.shape},async n=>{let r=await _S(n,e);return m(r)})}var yc='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" })',xS=z$1.object({id:y.schema.describe("Persistent task ID.")});async function AS(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:Zd(n.name)})))}function sf(t,e){return t.registerTool("task_uncomplete",{description:yc,inputSchema:xS.shape},async n=>{let r=await AS(n,e);return m(r)})}var kc='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" })',DS=z$1.object({id:y.schema.describe("Persistent task ID.")});async function RS(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:tl(n.name)})))}function cf(t,e){return t.registerTool("task_undrop",{description:kc,inputSchema:DS.shape},async n=>{let r=await RS(n,e);return m(r)})}var vc='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"] })',Jr=z$1.object({id:y.schema.describe("Persistent task ID. Get from task_list or search_query."),name:z$1.string().min(1).max(1024,"max 1 KB").optional().describe("New task name. Must be non-empty if supplied."),note:z$1.string().nullable().optional().describe("Plain-text note. Pass null to clear. HTML round-trip available in M3."),flagged:z$1.boolean().optional().describe("Flag or unflag the task. Alias: setFlagged."),setFlagged:z$1.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$1.string().nullable().optional().describe("ISO-8601 defer date with UTC offset. Pass null to clear."),deferDateFloating:z$1.boolean().optional().describe("When true, the defer time is floating (follows the user across time zones)."),dueDate:z$1.string().nullable().optional().describe("ISO-8601 due date with UTC offset. Pass null to clear."),dueDateFloating:z$1.boolean().optional().describe("When true, the due time is floating (follows the user across time zones)."),estimatedMinutes:z$1.number().int().positive().nullable().optional().describe("Estimated duration in minutes. Pass null to clear."),sequential:z$1.boolean().optional().describe("Whether subtasks must be completed in order."),completedByChildren:z$1.boolean().optional().describe("Whether the task completes when all children are complete."),tagIds:z$1.array(b.schema).optional().describe("Full-replacement tag list. Replaces all existing tags. Mutually exclusive with addTags/removeTags."),addTags:z$1.array(b.schema).optional().describe("Tags to add. No-op for tags the task already has. Mutually exclusive with tagIds."),removeTags:z$1.array(b.schema).optional().describe("Tags to remove. No-op for tags the task doesn't have. Mutually exclusive with tagIds."),expectedModifiedAt:z$1.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$1.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$1.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.")}),CS=Jr.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 FS(t,e){Ee(CS,t);let{id:n,addTags:r,removeTags:o,setFlagged:a,tagIds:s,...i}=t,c=e.idempotencyStore??le;return he(c,t.idempotency_key,async()=>{let l=await e.adapter.getTask(n);mt(t.expectedModifiedAt,l.modifiedAt,`task:${n}`);let d;if(r!==void 0||o!==void 0){let h=new Set(l.tagIds);for(let w of r??[])h.add(w);for(let w of o??[])h.delete(w);d=[...h];}else s!==void 0&&(d=s);let u=a!==void 0?a:i.flagged,f={...i.name!==void 0?{name:i.name}:{},...i.note!==void 0?{note:i.note}:{},...u!==void 0?{flagged:u}:{},...i.deferDate!==void 0?{deferDate:i.deferDate}:{},...i.deferDateFloating!==void 0?{deferDateFloating:i.deferDateFloating}:{},...i.dueDate!==void 0?{dueDate:i.dueDate}:{},...i.dueDateFloating!==void 0?{dueDateFloating:i.dueDateFloating}:{},...i.estimatedMinutes!==void 0?{estimatedMinutes:i.estimatedMinutes}:{},...i.sequential!==void 0?{sequential:i.sequential}:{},...i.completedByChildren!==void 0?{completedByChildren:i.completedByChildren}:{},...d!==void 0?{tagIds:d}:{}},g=()=>{let h={...l,...f};return p({task:h},e.makeMeta({syncPending:false}))},I=async()=>{await e.adapter.updateTask(n,f);let h=await e.adapter.getTask(n);return e.cache!==void 0&&j(e.cache,{taskId:n,projectId:h.projectId}),p({task:h},e.makeMeta({syncPending:true}))};return ze(t.dry_run,g,I)})}function df(t,e){return t.registerTool("task_update",{description:vc,inputSchema:Jr.shape},async n=>{let r=await FS(n,e);return m(r)})}var Ic="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 ES(t,e){let n=[],r=[],o=String(t.id);try{let s=await e.adapter.getTask(t.id);o=s.name,t.name!==void 0&&(n.push({field:"name",newValue:t.name,oldValue:s.name}),r.push(`rename to '${t.name}'`)),t.dueDate!==void 0&&(n.push({field:"dueDate",newValue:t.dueDate,oldValue:s.dueDate??null}),r.push(t.dueDate===null?"clear due date":`set due date to ${Pe(t.dueDate)}`)),t.deferDate!==void 0&&(n.push({field:"deferDate",newValue:t.deferDate,oldValue:s.deferDate??null}),r.push(t.deferDate===null?"clear defer date":`set defer date to ${Pe(t.deferDate)}`));let i=t.setFlagged!==void 0?t.setFlagged:t.flagged;if(i!==void 0&&(n.push({field:"flagged",newValue:String(i),oldValue:String(s.flagged)}),r.push(i?"flag":"unflag")),t.estimatedMinutes!==void 0&&(n.push({field:"estimatedMinutes",newValue:t.estimatedMinutes===null?null:String(t.estimatedMinutes),oldValue:s.estimatedMinutes!=null?String(s.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=>Se(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=>Se(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=>Se(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 lf(t,e){return t.registerTool("task_update_describe",{description:Ic,inputSchema:Jr.shape},async n=>{let r=await ES(n,e);return m(r)})}var Tc='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" }',wc='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" }',MS=z$1.object({taskId:y.schema.describe("Persistent task ID."),whom:z$1.string().min(1).describe("Person, team, or system being waited on. Required."),what:z$1.string().min(1).optional().describe("Optional short description of what is being waited on."),since:z$1.string().datetime({offset:true}).optional().describe("ISO-8601 date the wait began. Defaults to now. Use to backfill historical waits."),followUpAfter:z$1.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.")}),NS=z$1.object({taskId:y.schema.describe("Persistent task ID.")});async function US(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 LS(t,e){let n=await e.adapter.getTask(t.taskId),r=Mo.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=Rd(n.note,r),a=await US(e.adapter,e.waitingTagName),s=n.tagIds.includes(a)?n.tagIds:[...n.tagIds,a];return await e.adapter.updateTask(t.taskId,{note:o,tagIds:s}),e.cache!==void 0&&j(e.cache,{taskId:t.taskId,projectId:n.projectId}),p({id:t.taskId,waitingOn:r},e.makeMeta({syncPending:true}))}function pf(t,e){return t.registerTool("task_set_waiting_on",{description:Tc,inputSchema:MS.shape},async n=>{let r=await LS(n,e);return m(r)})}async function JS(t,e){let n=await e.adapter.getTask(t.taskId),r=Cd(n.note),o=await e.adapter.listTags(),a=e.waitingTagName.toLowerCase(),s=o.find(d=>d.name.toLowerCase()===a)?.id,i=s!==void 0&&n.tagIds.includes(s),c=r!==n.note;if(!i&&!c)return p({id:t.taskId,noChange:true},e.makeMeta());let l={};return c&&(l.note=r),i&&s!==void 0&&(l.tagIds=n.tagIds.filter(d=>d!==s)),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 uf(t,e){return t.registerTool("task_clear_waiting_on",{description:wc,inputSchema:NS.shape},async n=>{let r=await JS(n,e);return m(r)})}var mf=z$1.object({tagId:z$1.string().min(1).optional().describe("Restrict to tasks carrying this tag."),projectId:z$1.string().min(1).optional().describe("Restrict to tasks in this project.")}).optional().describe("Optional filter narrowing which task events fire this webhook."),BS=z$1.object({projectId:z$1.string().min(1).optional().describe("Restrict to status changes on this project.")}).optional().describe("Optional filter narrowing which project events fire this webhook."),ff=z$1.discriminatedUnion("on",[z$1.object({on:z$1.literal("task-completed"),filter:mf}),z$1.object({on:z$1.literal("task-created"),filter:mf}),z$1.object({on:z$1.literal("project-status-changed"),filter:BS})]);function bc(t){return {name:t.name,trigger:t.trigger,secretSet:t.secret!==void 0&&t.secret.length>0,createdAt:t.createdAt}}var pn=z$1.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."),gf=z$1.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."),hf=z$1.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 Sc='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" } } })',WS=z$1.object({name:pn.describe("Stable name for the webhook. Unique within the registry; used as the lookup key. \u226464 chars, no whitespace."),url:gf.describe("Outbound HTTPS URL. http:// is rejected at registration (per ADR-0016 \xA74b)."),trigger:ff.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:hf.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 Jn(t){if(!t.enabled)throw new k("Webhook subsystem is disabled. Set OMNIFOCUS_WEBHOOKS_ENABLED=1 in the environment to enable.",{details:{field:"OMNIFOCUS_WEBHOOKS_ENABLED"}})}async function HS(t,e){Jn(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 yf(t,e){return t.registerTool("webhook_register",{description:Sc,inputSchema:WS.shape},async n=>{let r=await HS(n,e);return m(r)})}var jc='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" })',VS=z$1.object({name:pn.describe("Name of the registered webhook to delete. Idempotent \u2014 unknown names return noChange:true.")});async function qS(t,e){return Jn(e),e.registry.delete(t.name)?p({name:t.name,deleted:true},e.makeMeta()):p({name:t.name,noChange:true},e.makeMeta())}function kf(t,e){return t.registerTool("webhook_delete",{description:jc,inputSchema:VS.shape},async n=>{let r=await qS(n,e);return m(r)})}var _c="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()",KS=z$1.object({});async function XS(t,e){return Jn(e),p({webhooks:e.registry.list()},e.makeMeta())}function vf(t,e){return t.registerTool("webhook_list",{description:_c,inputSchema:KS.shape},async n=>{let r=await XS(n,e);return m(r)})}var Oc=`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" })`,ZS=z$1.object({name:pn.describe("Name of the registered webhook to fire a synthetic event through.")});async function QS(t,e){if(!e.enabled)throw new k("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 If(t,e){return t.registerTool("webhook_test",{description:Oc,inputSchema:ZS.shape},async n=>{let r=await QS(n,e);return m(r)})}var xc="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()",ej=z$1.object({});async function tj(t,e){let n=await e.adapter.getWindowState();return p(n,e.makeMeta())}function Tf(t,e){return t.registerTool("window_get_state",{description:xc,inputSchema:ej.shape},async n=>m(await tj(n,e)))}var Ac=`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" })`,nj=z$1.object({perspectiveName:z$1.string().min(1).describe("Name of the perspective to activate. Case-sensitive. Built-in or custom perspectives both work.")});async function rj(t,e){let n=await e.adapter.setWindowPerspective(t.perspectiveName);return p(n,e.makeMeta())}function wf(t,e){return t.registerTool("window_set_perspective",{description:Ac,inputSchema:nj.shape},async n=>m(await rj(n,e)))}var Pc="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 })",oj=z$1.object({containerId:z$1.union([z$1.string().min(1),z$1.null()]).describe("ProjectId or FolderId to focus the front window on, or null to clear focus.")});async function aj(t,e){let n=await e.adapter.setWindowFocus(t.containerId);return p(n,e.makeMeta())}function bf(t,e){return t.registerTool("window_set_focus",{description:Pc,inputSchema:oj.shape},async n=>m(await aj(n,e)))}var Dc="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()",sj=z$1.object({});async function ij(t,e){let n=await e.adapter.appWindowNew();return p(n,e.makeMeta())}function Sf(t,e){return t.registerTool("app_window_new",{description:Dc,inputSchema:sj.shape},async n=>m(await ij(n,e)))}var Rc="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()",cj=z$1.object({});async function dj(t,e){let n=await e.adapter.appWindowNewTab();return p(n,e.makeMeta())}function jf(t,e){return t.registerTool("app_window_new_tab",{description:Rc,inputSchema:cj.shape},async n=>m(await dj(n,e)))}var _f={app_launch:Ho,attachment_add:qo,attachment_list:Vo,attachment_remove:Go,attachment_save_to_path:Ko,database_redo:Xo,database_undo:Yo,decision_clear:Qo,decision_record:ea,export_opml:na,export_taskpaper:aa,forecast_get:Sa,forecast_get_tag:ja,forecast_pack:_a,forecast_set_tag:xa,folder_create:ia,folder_create_describe:da,folder_delete:la,folder_delete_describe:ua,folder_get:ma,folder_list:fa,folder_move:ga,folder_move_describe:ya,folder_update:ka,folder_update_describe:Ia,import_opml:oa,import_taskpaper:sa,internal_status:Na,note_append:Aa,note_get:Da,note_get_html:Ca,note_set:Fa,note_set_html:Ea,perspective_create:La,perspective_delete:Ja,perspective_evaluate:Ba,perspective_evaluate_dry_run:Wa,perspective_get:Ha,perspective_list:za,perspective_update:qa,plugin_invoke:Ka,project_batch_complete:Ya,project_batch_drop:Qa,project_complete:es,project_complete_describe:ns,project_create:ss,project_create_describe:cs,project_delete:ds,project_delete_describe:ps,project_drop:us,project_drop_describe:fs,project_get:gs,project_get_many:hs,project_list:ys,project_mark_reviewed:Ls,project_move:ks,project_move_describe:Is,project_template_instantiate:bs,project_template_list:Ss,project_template_save:_s,project_update:Os,repetition_from_prose:Ms,project_update_describe:As,review_list_due:Ns,review_mark_reviewed:Us,project_set_next_review_date:$s,review_set_interval:Bs,run_jxa_script:Ds,run_omnijs_script:Cs,search_query:Ws,sync_status:Hs,sync_trigger:zs,tag_create:qs,tag_create_describe:Ks,tag_delete:Xs,tag_delete_describe:Zs,tag_get:Qs,tag_get_many:ti,tag_get_location:ei,tag_list:ni,tag_move:ri,tag_move_describe:ai,tag_set_allows_next_action:si,tag_set_location:ii,tag_set_status:ci,tag_update:li,task_batch_assign:mi,tag_update_describe:ui,task_batch_complete:hi,task_batch_create:yi,task_batch_create_describe:vi,task_batch_defer_smart:wi,task_batch_delete:bi,task_batch_drop:ji,task_batch_move:_i,task_batch_uncomplete:xi,task_batch_undrop:Pi,task_batch_update:Di,task_batch_update_describe:Ci,task_complete:Mi,task_complete_describe:Ui,task_create:Ji,task_create_describe:Bi,task_defer_smart:$i,task_drop:Vi,task_drop_describe:Gi,task_clear_alarms:Fi,task_clear_repetition:Ei,task_delete:Wi,task_delete_describe:zi,task_duplicate:Ki,task_extract_from_image:Yi,task_extract_from_note:Zi,task_find_by_name:Qi,task_find_similar:ec,task_get:tc,task_get_many:nc,task_list:oc,task_convert_to_project:Li,task_move:sc,task_move_describe:cc,task_search:fc,task_parse_transport_text:lc,task_reclassify:uc,task_reorder:mc,task_clear_waiting_on:wc,task_set_alarms:gc,task_set_repetition:hc,task_set_waiting_on:Tc,task_uncomplete:yc,task_undrop:kc,task_update:vc,app_window_new:Dc,app_window_new_tab:Rc,window_get_state:xc,window_set_focus:Pc,window_set_perspective:Ac,task_update_describe:Ic,webhook_delete:jc,webhook_list:_c,webhook_register:Sc,webhook_test:Oc};var lj=`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 })`,pj=z$1.object({replayToken:z$1.string().min(1).describe("Opaque token from the clarification-needed envelope's replayToken field."),choice:z$1.number().int().min(0).describe("Zero-based index of the option the user selected (matches ClarificationOption.index).")});async function uj(t,e){let n=e.replayStore??Ae,r=e.makeMeta(),o=n.consume(t.replayToken);return o===void 0?Wo(new _(`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?Wo(new k(`choice ${t.choice} is out of range; valid indices are 0\u2013${o.options.length-1}.`,{suggestion:`Valid options: ${o.options.map((s,i)=>`${i}: ${s}`).join(", ")}.`}),r):await o.callback(t.choice)}function Of(t,e){return t.registerTool("clarify",{description:lj,inputSchema:pj.shape},async n=>{let r=await uj(n,e);return m(r)})}var mj=`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" }.`,fj=z$1.object({templateName:z$1.string().min(1).describe("Name of the template to delete. Matched case-insensitively within the Templates folder.")}),Br=class extends _{constructor(e){super(`No template named "${e}" was found in the Templates folder.`,{details:{templateName:e}});}};async function gj(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 Br(t.templateName);let a=await e.adapter.listProjects({folderId:o.id}),s=t.templateName.toLowerCase(),i=a.find(c=>c.name.toLowerCase()!==s?false:Yt(c.note)!==void 0);if(i===void 0)throw new Br(t.templateName);return await e.adapter.deleteProject(i.id),e.cache!==void 0&&B(e.cache,{projectId:i.id}),p({deleted:true,templateName:t.templateName},e.makeMeta({syncPending:true}))}function Af(t,e){return t.registerTool("project_template_delete",{description:mj,inputSchema:fj.shape},async n=>{let r=await gj(n,e);return m(r)})}function Pf(){let t=process.env.OF_DB_PATH;if(t&&t.length>0)return t;let e=yj.homedir(),n=$r.join(e,"Library","Application Support","OmniFocus","OmniFocus.ofocus"),r=a=>$r.join(e,"Library","Containers",a,"Data","Library","Application Support","OmniFocus","OmniFocus.ofocus"),o=[r("com.omnigroup.OmniFocus4"),r("com.omnigroup.OmniFocus3"),n];for(let a of o)if(Pt.existsSync(a))return a;return n}Pf();function Ij(){let t=fileURLToPath(import.meta.url);return $r.resolve($r.dirname(t),"../../bin/omnifocus-watcher")}var Wr=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??Pf(),this.binaryPath=n.binaryPath!==void 0?n.binaryPath:Ij();}start(){if(this.started)return;if(!Pt.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{Pt.accessSync(this.binaryPath,Pt.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(Pt.existsSync(this.dbPath))try{this.nodeWatcher=Pt.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 bj=5e3,Bn=[1e3,5e3,3e4],Df=10,Rf=3600*1e3,Sj="X-OmniFocus-Signature";function jj(t){return ({url:e,body:n,headers:r,timeoutMs:o})=>new Promise((a,s)=>{let i=new URL(e),c=t({method:"POST",host:i.host,path:`${i.pathname}${i.search}`,headers:{"Content-Length":Buffer.byteLength(n),...r},timeout:o},l=>{l.on("error",s),l.on("data",()=>{}),l.on("end",()=>{a({statusCode:l.statusCode??0});});});c.on("error",s),c.on("timeout",()=>{c.destroy(new Error(`request timeout after ${o}ms`));}),c.write(n),c.end();})}var _j=jj(wj.request),Hr=class{request;now;sleep;write;circuits=new Map;constructor(e={}){this.request=e.request??_j,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(d){this.write(`[webhook] ${e.kind} for "${e.webhookName}" \u2014 registry lookup threw: ${Cf(d)}; dropping.
|
|
229
229
|
`);return}if(r===void 0){this.write(`[webhook] ${e.kind} for "${e.webhookName}" \u2014 webhook not found in registry; dropping.
|
|
230
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
231
|
`);return}o.openUntil!==null&&(o.openUntil=null,this.write(`[webhook] circuit auto-resumed for "${r.name}".
|
|
232
|
-
`));let a=JSON.stringify(e),
|
|
233
|
-
`),o.consecutiveFailures=0):this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 failed after ${
|
|
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 hf(t){return (t instanceof Error?t.message:String(t)).slice(0,80)}function kf(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 vf(t){return {id:String(t.id),name:t.name,status:t.status}}function oj(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 aj(t,e){return e===void 0?true:!(e.projectId!==void 0&&t.id!==e.projectId)}function If(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){yf(c,"task-created",n,o,a);continue}!d.completed&&c.completed&&yf(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"&&aj(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 yf(t,e,n,r,o){for(let a of n){if(a.trigger.on!==e)continue;let i=a.trigger.filter;oj(t,i)&&o.push({kind:e,webhookName:a.name,taskId:t.id,taskName:t.name,projectId:t.projectId,tagIds:t.tagIds,occurredAt:r});}}var jr=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(kf),o=n.map(vf);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=If({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 ij(){return wr.join(homedir(),"Library","Application Support","omnifocus-mcp","webhooks.json")}var Tf=1,_r=class{filePath;webhooks=[];constructor(e={}){this.filePath=e.filePath??ij(),this.load();}path(){return this.filePath}listFull(){return this.webhooks}list(){return this.webhooks.map(nc)}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(),nc(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(!gt.existsSync(this.filePath)){this.webhooks=[];return}try{let e=gt.readFileSync(this.filePath,"utf8"),n=JSON.parse(e);if(n.version!==Tf||!Array.isArray(n.webhooks)){this.webhooks=[];return}this.webhooks=n.webhooks;}catch{this.webhooks=[];}}persist(){let e=wr.dirname(this.filePath);gt.existsSync(e)||gt.mkdirSync(e,{recursive:true,mode:448});let n={version:Tf,webhooks:this.webhooks},r=JSON.stringify(n,null,2),o=`${this.filePath}.tmp`;gt.writeFileSync(o,r,{mode:384}),gt.renameSync(o,this.filePath);try{gt.chmodSync(this.filePath,384);}catch{}}};var mc=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 Zt(`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 Zt(`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)}},fc=class{_breakers=new Map;_defaults;constructor(e={}){this._defaults=e;}get(e){let n=this._breakers.get(e);return n||(n=new mc(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}))}},gc=new fc;function cj(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,...cj(i)});}}return {succeeded:n,failed:r}}function X(t){return t.toISOString()}var xr=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 j(`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 j(`Project not found: ${e.projectId}`,{details:{resource:"project",id:e.projectId}});if(e.parentId!==void 0&&!this.tasks.has(e.parentId))throw new j(`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 j(`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 j(`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())}),n!==o.completed&&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 j(`Project not found: ${n.projectId}`,{details:{resource:"project",id:n.projectId}});if(n.parentId!==void 0&&!this.tasks.has(n.parentId))throw new j(`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=v.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 j(`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 j(`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),I=X(this.now()),k={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:I,modifiedAt:I};return this.tasks.set(g,k),this.bumpProjectTaskCount(m,1),g},c=s(r,o,a),d=0;if(n.recursive){let l=(m,f,g)=>{for(let I of i(m)){let k=s(I,g,f);d+=1,l(I.id,k,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,I]of this.tasks)g!==e&&l.push([g,I]);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(([,I],k)=>{m(I)&&(g=k);}),f=g===-1?l.length:g+1;}else {let g=l.findIndex(([I])=>I===s);f=i==="before"?g:g+1;}this.tasks.clear(),l.forEach(([g,I],k)=>{k===f&&this.tasks.set(e,d),this.tasks.set(g,I);}),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 j(`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 j(`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 j(`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 j(`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 j(`Folder not found: ${e.folderId}`,{details:{resource:"folder",id:e.folderId}});let n=this.nextId("proj",v),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 j(`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 j(`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 j(`Parent tag not found: ${e.parentId}`,{details:{resource:"tag",id:e.parentId}});let n=this.nextId("tag",b),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 j(`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 j(`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 j(`Parent folder not found: ${e.parentId}`,{details:{resource:"folder",id:e.parentId}});let n=this.nextId("fold",$),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 j(`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 j(`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 j(`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 pt.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 j(`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 j(`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 j(`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 j(`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 j(`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 j(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new j(`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 j(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new j(`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",Pe),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 j(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new j(`Project not found: ${e.projectId}`);let n=this.ownerKey(e),r=this.attachments.get(n);if(!r?.has(e.attachmentId))throw new j(`Attachment not found: ${e.attachmentId}`);r.delete(e.attachmentId);}async saveAttachmentToPath(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new j(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new j(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);if(!this.attachments.get(n)?.has(e.attachmentId))throw new j(`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 j(`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 j("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 Y(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"&&t.error!==null}function Oe(t,e){return {succeeded:t.succeeded.map(n=>({index:n.index,value:e(n.value)})),failed:t.failed}}var bf=`/**
|
|
232
|
+
`));let a=JSON.stringify(e),s={"Content-Type":"application/json","User-Agent":"omnifocus-mcp-webhook/1"};if(r.secret!==void 0&&r.secret.length>0){let d=createHmac("sha256",r.secret).update(a).digest("hex");s[Sj]=`sha256=${d}`;}let i="",c=0;for(let d=0;d<=Bn.length;d++){try{let{statusCode:f}=await this.request({url:r.url,body:a,headers:s,timeoutMs:bj});if(f>=200&&f<300){o.consecutiveFailures=0;return}c=f,i=`non-2xx status ${f}`;}catch(f){i=Cf(f);}if(d===Bn.length)break;let u=Bn[d];await this.sleep(u);}o.consecutiveFailures++,o.consecutiveFailures>=Df?(o.openUntil=this.now()+Rf,this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 failed after ${Bn.length+1} attempts (last: ${i}). Circuit OPEN for ${Rf/1e3}s.
|
|
233
|
+
`),o.consecutiveFailures=0):this.write(`[webhook] ${e.kind} for "${r.name}" \u2014 failed after ${Bn.length+1} attempts (last: ${i}). ${o.consecutiveFailures}/${Df} 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 Cf(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 Mf(t){return {id:String(t.id),name:t.name,status:t.status}}function Oj(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 xj(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=[],s=new Map(t.tasks.map(c=>[c.id,c])),i=new Map(t.projects.map(c=>[c.id,c]));for(let c of e.tasks){let l=s.get(c.id);if(l===void 0){Ff(c,"task-created",n,o,a);continue}!l.completed&&c.completed&&Ff(c,"task-completed",n,o,a);}for(let c of e.projects){let l=i.get(c.id);if(l!==void 0&&l.status!==c.status)for(let d of n)d.trigger.on==="project-status-changed"&&xj(c,d.trigger.filter)&&a.push({kind:"project-status-changed",webhookName:d.name,projectId:c.id,projectName:c.name,previousStatus:l.status,currentStatus:c.status,occurredAt:o});}return a}function Ff(t,e,n,r,o){for(let a of n){if(a.trigger.on!==e)continue;let s=a.trigger.filter;Oj(t,s)&&o.push({kind:e,webhookName:a.name,taskId:t.id,taskName:t.name,projectId:t.projectId,tagIds:t.tagIds,occurredAt:r});}}var zr=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(Mf);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 s=Nf({tasks:this.lastTasks,projects:this.lastProjects},{tasks:r,projects:o},a,{now:this.now()});this.lastTasks=r,this.lastProjects=o,await Promise.all(s.map(i=>this.dispatcher.deliver(i,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 Pj(){return $r.join(homedir(),"Library","Application Support","omnifocus-mcp","webhooks.json")}var Uf=1,Vr=class{filePath;webhooks=[];constructor(e={}){this.filePath=e.filePath??Pj(),this.load();}path(){return this.filePath}listFull(){return this.webhooks}list(){return this.webhooks.map(bc)}has(e){return this.webhooks.some(n=>n.name===e)}register(e){if(this.has(e.name))throw new k(`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(),bc(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(!Pt.existsSync(this.filePath)){this.webhooks=[];return}try{let e=Pt.readFileSync(this.filePath,"utf8"),n=JSON.parse(e);if(n.version!==Uf||!Array.isArray(n.webhooks)){this.webhooks=[];return}this.webhooks=n.webhooks;}catch{this.webhooks=[];}}persist(){let e=$r.dirname(this.filePath);Pt.existsSync(e)||Pt.mkdirSync(e,{recursive:true,mode:448});let n={version:Uf,webhooks:this.webhooks},r=JSON.stringify(n,null,2),o=`${this.filePath}.tmp`;Pt.writeFileSync(o,r,{mode:384}),Pt.renameSync(o,this.filePath);try{Pt.chmodSync(this.filePath,384);}catch{}}};var Fc=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 gn(`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 gn(`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 Fc(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}))}},Mc=new Ec;function Dj(t){return t instanceof Error?{errorCode:t.code??"OF_UNKNOWN",message:t.message}:{errorCode:"OF_UNKNOWN",message:String(t)}}async function Ge(t,e){let n=[],r=[];for(let o=0;o<t.length;o++){let a=t[o];if(a!==void 0)try{let s=await e(a,o);n.push({index:o,value:s});}catch(s){r.push({index:o,...Dj(s)});}}return {succeeded:n,failed:r}}function ne(t){return t.toISOString()}var qr=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 _(`Task not found: ${e}`,{details:{resource:"task",id:e}});return this.withAlarms(n)}async getNoteHtml(e,n){return e==="task"?(await this.getTask(y.of(String(n)))).noteHtml??null:(await this.getProject(v.of(String(n)))).noteHtml??null}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 k("Task name must be non-empty",{details:{field:"name"}});if(e.projectId!==void 0&&e.parentId!==void 0)throw new k("createTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(e.projectId!==void 0&&!this.projects.has(e.projectId))throw new _(`Project not found: ${e.projectId}`,{details:{resource:"project",id:e.projectId}});if(e.parentId!==void 0&&!this.tasks.has(e.parentId))throw new _(`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 _(`Tag not found: ${a}`,{details:{resource:"tag",id:a}});let n=this.nextId("task",y),r=ne(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 k("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 _(`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:ne(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?ne(r??this.now()):null;this.tasks.set(e,{...o,completed:n,completedAt:a,modifiedAt:a??ne(this.now())}),n!==o.completed&&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?ne(r??this.now()):null;this.tasks.set(e,{...o,dropped:n,droppedAt:a,modifiedAt:a??ne(this.now())});}async batchCreateTasks(e){return Ge(e,n=>this.createTask(n))}async batchUpdateTasks(e){return Ge(e,async({id:n,patch:r})=>(await this.updateTask(n,r),n))}async batchCompleteTasks(e){return Ge(e,async({id:n,at:r})=>(await this.completeTask(n,r),n))}async batchUncompleteTasks(e){return Ge(e,async({id:n})=>(await this.uncompleteTask(n),n))}async batchDeleteTasks(e){return Ge(e,async({id:n})=>(await this.deleteTask(n),n))}async batchDropTasks(e){return Ge(e,async({id:n})=>(await this.dropTask(n),n))}async batchUndropTasks(e){return Ge(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 k("moveTask: provide projectId OR parentId, not both",{details:{field:"projectId|parentId"}});if(n.projectId!==void 0&&!this.projects.has(n.projectId))throw new _(`Project not found: ${n.projectId}`,{details:{resource:"project",id:n.projectId}});if(n.parentId!==void 0&&!this.tasks.has(n.parentId))throw new _(`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:ne(this.now())}),this.adjustProjectCountsForTask(o,r,1);}async convertTaskToProject(e,n){let r=await this.getTask(e),o=v.of(e),a=ne(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 Ge(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 k("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 _(`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 _(`Parent task not found: ${d.parentId}`,{details:{resource:"task",id:d.parentId}});o=f.projectId,a=d.parentId;}else o=null,a=null;}let s=d=>Array.from(this.tasks.values()).filter(u=>u.parentId===d),i=(d,u,f)=>{let g=this.nextId("task",y),I=ne(this.now()),h={id:g,name:d.name,note:d.note,noteHtml:d.noteHtml,projectId:u,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:I,modifiedAt:I};return this.tasks.set(g,h),this.bumpProjectTaskCount(u,1),g},c=i(r,o,a),l=0;if(n.recursive){let d=(u,f,g)=>{for(let I of s(u)){let h=i(I,g,f);l+=1,d(I.id,h,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:s,anchorId:i}=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:ne(this.now())},d=[];for(let[g,I]of this.tasks)g!==e&&d.push([g,I]);let u=g=>g.projectId===o&&g.parentId===a,f;if(s==="start")f=d.findIndex(([,g])=>u(g)),f===-1&&(f=d.length);else if(s==="end"){let g=-1;d.forEach(([,I],h)=>{u(I)&&(g=h);}),f=g===-1?d.length:g+1;}else {let g=d.findIndex(([I])=>I===i);f=s==="before"?g:g+1;}this.tasks.clear(),d.forEach(([g,I],h)=>{h===f&&this.tasks.set(e,l),this.tasks.set(g,I);}),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 _(`Reference task not found: ${c}`,{details:{resource:"task",id:c}});if(c===e)throw new k("reorderTask: reference must differ from the task id",{details:{field:"position"}});if(l.projectId!==n.projectId||l.parentId!==n.parentId)throw new k("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,s,i;if("projectId"in a){if(!this.projects.has(a.projectId))throw new _(`Project not found: ${a.projectId}`,{details:{resource:"project",id:a.projectId}});s=a.projectId,i=null;}else if("parentId"in a){if(!this.tasks.has(a.parentId))throw new _(`Parent task not found: ${a.parentId}`,{details:{resource:"task",id:a.parentId}});if(a.parentId===e)throw new k("reorderTask: cannot reparent a task under itself",{details:{field:"position.in.parentId"}});s=this.tasks.get(a.parentId)?.projectId??null,i=a.parentId;}else s=null,i=null;return {newProjectId:s,newParentId:i,anchorMode:o,anchorId:null}}async listProjects(e={}){return Array.from(this.projects.values()).filter(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 _(`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 k("Project name must be non-empty",{details:{field:"name"}});if(e.folderId!==void 0&&!this.folders.has(e.folderId))throw new _(`Folder not found: ${e.folderId}`,{details:{resource:"folder",id:e.folderId}});let n=this.nextId("proj",v),r=ne(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 k("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:ne(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=ne(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=ne(n??this.now());this.projects.set(e,{...r,status:"dropped",dropped:true,droppedAt:o,modifiedAt:o});}async batchCompleteProjects(e){return Ge(e,async({id:n})=>(await this.completeProject(n),n))}async batchDropProjects(e){return Ge(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 _(`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:ne(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=ne(r),a=null;if(n.reviewIntervalDays!==null){let s=new Date(r);s.setUTCDate(s.getUTCDate()+n.reviewIntervalDays),a=ne(s);}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 _(`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 k("Tag name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.tags.has(e.parentId))throw new _(`Parent tag not found: ${e.parentId}`,{details:{resource:"tag",id:e.parentId}});let n=this.nextId("tag",b),r=ne(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 k("Tag name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.tags.has(n.parentId))throw new _(`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:ne(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 _(`Folder not found: ${e}`,{details:{resource:"folder",id:e}});return n}async createFolder(e){if(e.name.trim()==="")throw new k("Folder name must be non-empty",{details:{field:"name"}});if(e.parentId!==void 0&&!this.folders.has(e.parentId))throw new _(`Parent folder not found: ${e.parentId}`,{details:{resource:"folder",id:e.parentId}});let n=this.nextId("fold",$),r=ne(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 k("Folder name must be non-empty",{details:{field:"name"}});if(n.parentId!==void 0&&n.parentId!==null&&!this.folders.has(n.parentId))throw new _(`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:ne(this.now())});}async deleteFolder(e){let n=await this.getFolder(e);if(n.projectCount>0||n.subfolderCount>0)throw new k(`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 s=r!=="note"&&o.name.toLowerCase().includes(n),i=r!=="name"&&(o.note??"").toLowerCase().includes(n);if(!s&&!i)return false}if(e.projectId!==void 0&&o.projectId!==e.projectId)return false;if(e.tagIds!==void 0&&e.tagIds.length>0){let s=new Set(o.tagIds);if(!e.tagIds.every(i=>s.has(i)))return false}if(e.available!==void 0&&o.available!==e.available)return false;if(e.dueBefore!==void 0||e.dueAfter!==void 0){if(o.dueDate===null)return false;let s=new Date(o.dueDate);if(e.dueBefore!==void 0&&s>=new Date(e.dueBefore)||e.dueAfter!==void 0&&s<=new Date(e.dueAfter))return false}if(e.flagged!==void 0&&o.flagged!==e.flagged)return false;let a=o.completedAt!==null;return !(e.completed==="only"&&!a||e.completed==="exclude"&&a)})}async syncTrigger(){return this.lastSyncAt=ne(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 _(`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 _(`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 _t.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 _(`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 s of o){let i=this.tasks.get(s);i!==void 0&&a.push(i);}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 _(`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 _(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}})}async createCustomPerspective(e){if(e.name.length===0)throw new k("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 k(`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 _(`Custom perspective not found: ${e}`,{details:{resource:"perspective",id:e}});if(n.name!==void 0){if(n.name.length===0)throw new k("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 k(`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:s=true,includeFlagged:i=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=s?n.filter(f=>f.deferDate!==null&&f.deferDate>=r&&f.deferDate<=o):[],u=i?n.filter(f=>f.flagged):[];return {overdue:c,dueToday:l,deferredToday:d,flagged:u}}async getForecastTag(){return {tagId:this.forecastTagId}}async setForecastTag(e){if(e!==null&&!this.tags.has(e))throw new _(`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 _(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new _(`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 _(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new _(`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",Le),o=e.filePath.split("/").pop()??"attachment",a={id:r,name:o,mimeType:null,sizeBytes:null,addedAt:ne(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 _(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new _(`Project not found: ${e.projectId}`);let n=this.ownerKey(e),r=this.attachments.get(n);if(!r?.has(e.attachmentId))throw new _(`Attachment not found: ${e.attachmentId}`);r.delete(e.attachmentId);}async saveAttachmentToPath(e){if(e.taskId&&!this.tasks.has(e.taskId))throw new _(`Task not found: ${e.taskId}`);if(e.projectId&&!this.projects.has(e.projectId))throw new _(`Project not found: ${e.projectId}`);let n=this.ownerKey(e);if(!this.attachments.get(n)?.has(e.attachmentId))throw new _(`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 _(`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 _("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 Ne(t,e){return {succeeded:t.succeeded.map(n=>({index:n.index,value:e(n.value)})),failed:t.failed}}var Jf=`/**
|
|
235
235
|
* app_launch.js \u2014 launch OmniFocus explicitly (never automatic).
|
|
236
236
|
*
|
|
237
237
|
* Per SPEC resolved-decisions: automatic launch is out of scope \u2014 agents must
|
|
@@ -259,7 +259,7 @@ function run(_argv) {
|
|
|
259
259
|
|
|
260
260
|
return JSON.stringify({ launched: !alreadyRunning, alreadyRunning });
|
|
261
261
|
}
|
|
262
|
-
`;var
|
|
262
|
+
`;var Bf=`/**
|
|
263
263
|
* JXA: add an attachment to a task or project from a local file path.
|
|
264
264
|
*
|
|
265
265
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string, filePath: string }
|
|
@@ -307,7 +307,7 @@ function run(argv) {
|
|
|
307
307
|
const newAtt = atts[atts.length - 1];
|
|
308
308
|
return JSON.stringify({ id: newAtt.id() });
|
|
309
309
|
}
|
|
310
|
-
`;var
|
|
310
|
+
`;var $f=`/**
|
|
311
311
|
* JXA: list all attachments on a task or project.
|
|
312
312
|
*
|
|
313
313
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string }
|
|
@@ -403,7 +403,7 @@ function run(argv) {
|
|
|
403
403
|
|
|
404
404
|
return JSON.stringify({ attachments: result });
|
|
405
405
|
}
|
|
406
|
-
`;var
|
|
406
|
+
`;var Wf=`/**
|
|
407
407
|
* JXA: remove an attachment by ID from a task or project.
|
|
408
408
|
*
|
|
409
409
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string }
|
|
@@ -455,7 +455,7 @@ function run(argv) {
|
|
|
455
455
|
|
|
456
456
|
return JSON.stringify({});
|
|
457
457
|
}
|
|
458
|
-
`;var
|
|
458
|
+
`;var Hf=`/**
|
|
459
459
|
* JXA: copy an attachment's content to a local file path.
|
|
460
460
|
*
|
|
461
461
|
* Args (argv[0] JSON): { taskId?: string, projectId?: string, attachmentId: string, destPath: string }
|
|
@@ -554,7 +554,7 @@ function run(argv) {
|
|
|
554
554
|
|
|
555
555
|
return JSON.stringify({ saved: true, path: destPath, sizeBytes: sizeBytes });
|
|
556
556
|
}
|
|
557
|
-
`;var
|
|
557
|
+
`;var zf=`/**
|
|
558
558
|
* JXA: return task and project IDs modified since a given timestamp.
|
|
559
559
|
*
|
|
560
560
|
* This script powers the richer change-semantics layer: when the
|
|
@@ -572,10 +572,16 @@ function run(argv) {
|
|
|
572
572
|
* projects: Array<{ id: string, modificationDate: string }>
|
|
573
573
|
* }
|
|
574
574
|
*
|
|
575
|
-
* Performance:
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
575
|
+
* Performance: pushes the \`modificationDate >= since\` predicate into OF's
|
|
576
|
+
* runtime via \`whose({...})\` (see #789, mirrors \`forecast_get.js\`'s 25\xD7
|
|
577
|
+
* pattern). On databases with thousands of tasks this avoids materializing
|
|
578
|
+
* every task's accessor \u2014 typically a sub-second query vs the original
|
|
579
|
+
* full-scan loop's hundreds-of-ms-per-thousand-tasks. The \`try\`/\`catch\`
|
|
580
|
+
* around the \`whose()\` call is a safety net: if OF rejects the predicate
|
|
581
|
+
* for any reason, we fall back to the previous client-side filter so this
|
|
582
|
+
* script never returns wrong results because of a query-engine surprise.
|
|
583
|
+
*
|
|
584
|
+
* The Node side debounces change events to avoid stampeding this script.
|
|
579
585
|
*
|
|
580
586
|
* Note: \`modificationDate\` in OF reflects the last local write including
|
|
581
587
|
* sync-incoming changes. It does NOT distinguish between field-level changes;
|
|
@@ -584,6 +590,7 @@ function run(argv) {
|
|
|
584
590
|
*
|
|
585
591
|
* @see src/watcher/types.ts \u2014 ChangedObjects shape
|
|
586
592
|
* @see src/adapter/jxa/JxaTransport.ts \u2014 caller (getChangesSince)
|
|
593
|
+
* @see src/scripts/jxa/forecast_get.js \u2014 same whose() pushdown pattern
|
|
587
594
|
*/
|
|
588
595
|
|
|
589
596
|
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv)
|
|
@@ -593,42 +600,42 @@ function run(argv) {
|
|
|
593
600
|
|
|
594
601
|
const ofApp = Application("OmniFocus");
|
|
595
602
|
ofApp.includeStandardAdditions = false;
|
|
603
|
+
const doc = ofApp.defaultDocument;
|
|
596
604
|
|
|
597
|
-
//
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
for (let i = 0; i < rawTasks.length; i++) {
|
|
605
|
+
// Try the whose() pushdown first; fall back to a full scan if OF rejects
|
|
606
|
+
// the predicate for any reason (older OF, unexpected accessor behavior).
|
|
607
|
+
function collectModifiedSince(collection) {
|
|
608
|
+
let raw;
|
|
602
609
|
try {
|
|
603
|
-
|
|
604
|
-
const modDate = t.modificationDate();
|
|
605
|
-
if (modDate && modDate >= since) {
|
|
606
|
-
tasks.push({ id: t.id(), modificationDate: modDate.toISOString() });
|
|
607
|
-
}
|
|
610
|
+
raw = collection.whose({ modificationDate: { _greaterThanEquals: since } })();
|
|
608
611
|
} catch (_e) {
|
|
609
|
-
|
|
612
|
+
raw = collection();
|
|
610
613
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
614
|
+
const out = [];
|
|
615
|
+
for (let i = 0; i < raw.length; i++) {
|
|
616
|
+
try {
|
|
617
|
+
const item = raw[i];
|
|
618
|
+
const modDate = item.modificationDate();
|
|
619
|
+
// The whose() filter already excludes earlier dates, but the
|
|
620
|
+
// post-loop guard keeps the fallback path correct and defends
|
|
621
|
+
// against whose() silently falling back to client-side
|
|
622
|
+
// matching for some operators.
|
|
623
|
+
if (modDate && modDate >= since) {
|
|
624
|
+
out.push({ id: item.id(), modificationDate: modDate.toISOString() });
|
|
625
|
+
}
|
|
626
|
+
} catch (_e) {
|
|
627
|
+
// Skip items that error (e.g. inbox pseudo-tasks with no stable id)
|
|
623
628
|
}
|
|
624
|
-
} catch (_e) {
|
|
625
|
-
// Skip
|
|
626
629
|
}
|
|
630
|
+
return out;
|
|
627
631
|
}
|
|
628
632
|
|
|
633
|
+
const tasks = collectModifiedSince(doc.flattenedTasks);
|
|
634
|
+
const projects = collectModifiedSince(doc.flattenedProjects);
|
|
635
|
+
|
|
629
636
|
return JSON.stringify({ tasks, projects });
|
|
630
637
|
}
|
|
631
|
-
`;var
|
|
638
|
+
`;var Vf=`/**
|
|
632
639
|
* JXA: create a folder, optionally under a parent folder.
|
|
633
640
|
*
|
|
634
641
|
* Args (argv[0] JSON): { name: string, parentId?: string }
|
|
@@ -769,7 +776,7 @@ function buildFolder(folder, options) {
|
|
|
769
776
|
const parentMap = args.parentId ? { [newFolder.id()]: args.parentId } : {};
|
|
770
777
|
return JSON.stringify({ folder: buildFolder(newFolder, { parentMap }) });
|
|
771
778
|
}
|
|
772
|
-
`;var
|
|
779
|
+
`;var qf=`/**
|
|
773
780
|
* JXA: delete a folder by ID. Refuses if the folder has projects or subfolders.
|
|
774
781
|
*
|
|
775
782
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -807,7 +814,7 @@ function run(argv) {
|
|
|
807
814
|
|
|
808
815
|
return JSON.stringify({ id: args.id });
|
|
809
816
|
}
|
|
810
|
-
`;var
|
|
817
|
+
`;var Gf=`/**
|
|
811
818
|
* JXA: fetch one folder by ID.
|
|
812
819
|
*
|
|
813
820
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -945,7 +952,7 @@ function buildFolder(folder, options) {
|
|
|
945
952
|
|
|
946
953
|
throw new Error(\`Folder not found: \${args.id}\`);
|
|
947
954
|
}
|
|
948
|
-
`;var
|
|
955
|
+
`;var Kf=`/**
|
|
949
956
|
* JXA: list all folders, optionally filtered by parentId.
|
|
950
957
|
*
|
|
951
958
|
* Args (argv[0] JSON): { parentId?: string }
|
|
@@ -1088,7 +1095,7 @@ function buildFolder(folder, options) {
|
|
|
1088
1095
|
|
|
1089
1096
|
return JSON.stringify({ folders: result });
|
|
1090
1097
|
}
|
|
1091
|
-
`;var
|
|
1098
|
+
`;var Xf=`/**
|
|
1092
1099
|
* JXA: rename a folder.
|
|
1093
1100
|
*
|
|
1094
1101
|
* Args (argv[0] JSON): { id: string, name: string }
|
|
@@ -1231,7 +1238,7 @@ function buildFolder(folder, options) {
|
|
|
1231
1238
|
|
|
1232
1239
|
return JSON.stringify({ folder: buildFolder(target, { parentMap }) });
|
|
1233
1240
|
}
|
|
1234
|
-
`;var
|
|
1241
|
+
`;var Yf=`/**
|
|
1235
1242
|
* JXA: get forecast-view tasks grouped by category (overdue, dueToday,
|
|
1236
1243
|
* deferredToday, flagged).
|
|
1237
1244
|
*
|
|
@@ -1395,12 +1402,8 @@ function buildTask(task, options) {
|
|
|
1395
1402
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
1396
1403
|
}
|
|
1397
1404
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
1401
|
-
} catch (_e) {
|
|
1402
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
1403
|
-
}
|
|
1405
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
1406
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
1404
1407
|
|
|
1405
1408
|
let flagged = false;
|
|
1406
1409
|
try {
|
|
@@ -1472,7 +1475,7 @@ function buildTask(task, options) {
|
|
|
1472
1475
|
id: task.id(),
|
|
1473
1476
|
name: task.name(),
|
|
1474
1477
|
note: note,
|
|
1475
|
-
noteHtml:
|
|
1478
|
+
noteHtml: null,
|
|
1476
1479
|
projectId: projectId,
|
|
1477
1480
|
parentId: parentId,
|
|
1478
1481
|
tagIds: tagIds,
|
|
@@ -1603,14 +1606,77 @@ function buildRepetition(task) {
|
|
|
1603
1606
|
flagged: flaggedTasks,
|
|
1604
1607
|
});
|
|
1605
1608
|
}
|
|
1606
|
-
`;var
|
|
1609
|
+
`;var Zf=`/**
|
|
1610
|
+
* JXA: fetch the HTML note for a task or project by ID.
|
|
1611
|
+
*
|
|
1612
|
+
* Args (argv[0] JSON): { kind: "task" | "project", id: string }
|
|
1613
|
+
* Returns JSON: { noteHtml: string | null }
|
|
1614
|
+
*
|
|
1615
|
+
* @see src/adapter/jxa/JxaTransport.ts \u2014 caller
|
|
1616
|
+
* @see src/tools/note/get_html.ts \u2014 MCP tool
|
|
1617
|
+
*/
|
|
1618
|
+
|
|
1619
|
+
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
|
|
1620
|
+
function run(argv) {
|
|
1621
|
+
const args = JSON.parse(argv[0]);
|
|
1622
|
+
const ofApp = Application("OmniFocus");
|
|
1623
|
+
ofApp.includeStandardAdditions = false;
|
|
1624
|
+
|
|
1625
|
+
// lookupOrThrow \u2014 force a JXA byId(...) lookup and throw a structured
|
|
1626
|
+
// "<kindLabel> not found: <idValue>" error if the id doesn't exist.
|
|
1627
|
+
// JXA byId is lazy and always truthy; the lookup fires on the next method
|
|
1628
|
+
// call as "(-1728)". Calling .id() here forces resolution while kindLabel
|
|
1629
|
+
// and idValue are in scope. See #687, #674, ADR-0020.
|
|
1630
|
+
// Inlined into consumers via \`// @inline _helpers/lookup_or_throw.js\`.
|
|
1631
|
+
// biome-ignore lint/correctness/noUnusedVariables: inlined via @inline directive (ADR-0020).
|
|
1632
|
+
function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
1633
|
+
try {
|
|
1634
|
+
specifier.id();
|
|
1635
|
+
} catch (_e) {
|
|
1636
|
+
throw new Error(\`\${kindLabel} not found: \${idValue}\`);
|
|
1637
|
+
}
|
|
1638
|
+
return specifier;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
|
|
1642
|
+
let item;
|
|
1643
|
+
if (args.kind === "task") {
|
|
1644
|
+
item = lookupOrThrow(ofApp.defaultDocument.flattenedTasks.byId(args.id), "Task", args.id);
|
|
1645
|
+
} else {
|
|
1646
|
+
item = lookupOrThrow(ofApp.defaultDocument.flattenedProjects.byId(args.id), "Project", args.id);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
let noteHtml = null;
|
|
1650
|
+
try {
|
|
1651
|
+
if (item.noteHtml) noteHtml = item.noteHtml() || null;
|
|
1652
|
+
} catch (_e) {
|
|
1653
|
+
/* OF 4.x: noteHtml may not exist on all item types \u2014 null used */
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
return JSON.stringify({ noteHtml });
|
|
1657
|
+
}
|
|
1658
|
+
`;var Qf=`/**
|
|
1607
1659
|
* JXA: evaluate a built-in OmniFocus perspective and return its task list.
|
|
1608
1660
|
*
|
|
1609
1661
|
* Args (argv[0] JSON): { "perspectiveId": "inbox" | "projects" | "tags" | "forecast" | "flagged" | "nearby" | "review" }
|
|
1610
1662
|
* Returns JSON: { tasks: Task[] }
|
|
1611
1663
|
*
|
|
1664
|
+
* Performance: the \`flagged\` and \`forecast\` branches push their predicates
|
|
1665
|
+
* into OF's runtime via \`whose({...})\` (#789 / #894), mirroring the
|
|
1666
|
+
* \`forecast_get.js\` 25\xD7 speedup pattern. Try/catch fallback to the
|
|
1667
|
+
* full-scan keeps results correct on whose() rejection.
|
|
1668
|
+
*
|
|
1669
|
+
* The \`projects\` and \`tags\` branches still scan \`flattenedTasks()\` because
|
|
1670
|
+
* their predicates ("has a containingProject" / "has at least one tag")
|
|
1671
|
+
* don't translate to whose() \u2014 OF's whose() rejects \`_isnt: null\`
|
|
1672
|
+
* (documented in \`forecast_get.js\`'s comment block) and there's no clean
|
|
1673
|
+
* cardinality predicate. Source-collection rethink (e.g. iterating
|
|
1674
|
+
* \`flattenedProjects()\` per project, or \`flattenedTags().tasks()\` per
|
|
1675
|
+
* tag) is tracked separately \u2014 see #894's follow-up.
|
|
1676
|
+
*
|
|
1612
1677
|
* @see src/adapter/jxa/JxaTransport.ts \u2014 caller
|
|
1613
1678
|
* @see src/domain/task.ts \u2014 Task domain type
|
|
1679
|
+
* @see src/scripts/jxa/forecast_get.js \u2014 same whose() pushdown pattern
|
|
1614
1680
|
*/
|
|
1615
1681
|
|
|
1616
1682
|
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
|
|
@@ -1755,12 +1821,8 @@ function buildTask(task, options) {
|
|
|
1755
1821
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
1756
1822
|
}
|
|
1757
1823
|
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
1761
|
-
} catch (_e) {
|
|
1762
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
1763
|
-
}
|
|
1824
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
1825
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
1764
1826
|
|
|
1765
1827
|
let flagged = false;
|
|
1766
1828
|
try {
|
|
@@ -1832,7 +1894,7 @@ function buildTask(task, options) {
|
|
|
1832
1894
|
id: task.id(),
|
|
1833
1895
|
name: task.name(),
|
|
1834
1896
|
note: note,
|
|
1835
|
-
noteHtml:
|
|
1897
|
+
noteHtml: null,
|
|
1836
1898
|
projectId: projectId,
|
|
1837
1899
|
parentId: parentId,
|
|
1838
1900
|
tagIds: tagIds,
|
|
@@ -1869,6 +1931,17 @@ function buildRepetition(task) {
|
|
|
1869
1931
|
}
|
|
1870
1932
|
|
|
1871
1933
|
|
|
1934
|
+
// whose() pushdown helper \u2014 try the predicate, fall back to a full
|
|
1935
|
+
// scan on rejection so the post-loop guard still produces correct
|
|
1936
|
+
// results.
|
|
1937
|
+
function tasksMatching(predicate) {
|
|
1938
|
+
try {
|
|
1939
|
+
return ofApp.defaultDocument.flattenedTasks.whose(predicate)();
|
|
1940
|
+
} catch (_e) {
|
|
1941
|
+
return ofApp.flattenedTasks();
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1872
1945
|
const result = [];
|
|
1873
1946
|
|
|
1874
1947
|
if (perspectiveId === "inbox") {
|
|
@@ -1878,22 +1951,30 @@ function buildRepetition(task) {
|
|
|
1878
1951
|
result.push(buildTask(inboxTasks[i]));
|
|
1879
1952
|
}
|
|
1880
1953
|
} else if (perspectiveId === "flagged") {
|
|
1881
|
-
// Flagged: flagged tasks that are not completed/dropped
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1954
|
+
// Flagged: flagged tasks that are not completed/dropped \u2014 pushed into
|
|
1955
|
+
// whose() so the long tail of unflagged tasks is never iterated.
|
|
1956
|
+
const matches = tasksMatching({ flagged: true, completed: false, dropped: false });
|
|
1957
|
+
for (let i = 0; i < matches.length; i++) {
|
|
1958
|
+
const t = matches[i];
|
|
1885
1959
|
const built = buildTask(t);
|
|
1960
|
+
// Post-loop guard kept as a safety net in case whose() silently
|
|
1961
|
+
// falls back to client-side matching for a given operator.
|
|
1886
1962
|
if (built.flagged && !built.completed && !built.dropped) {
|
|
1887
1963
|
result.push(built);
|
|
1888
1964
|
}
|
|
1889
1965
|
}
|
|
1890
1966
|
} else if (perspectiveId === "forecast") {
|
|
1891
|
-
// Forecast: tasks with dueDate <= end of today and not completed/dropped
|
|
1967
|
+
// Forecast: tasks with dueDate <= end of today and not completed/dropped.
|
|
1968
|
+
// The dueDate predicate naturally excludes tasks with no dueDate.
|
|
1892
1969
|
const now = new Date();
|
|
1893
1970
|
const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
|
1894
|
-
const
|
|
1895
|
-
|
|
1896
|
-
|
|
1971
|
+
const matches = tasksMatching({
|
|
1972
|
+
completed: false,
|
|
1973
|
+
dropped: false,
|
|
1974
|
+
dueDate: { _lessThanEquals: endOfDay },
|
|
1975
|
+
});
|
|
1976
|
+
for (let i = 0; i < matches.length; i++) {
|
|
1977
|
+
const t = matches[i];
|
|
1897
1978
|
const built = buildTask(t);
|
|
1898
1979
|
if (built.dueDate && !built.completed && !built.dropped) {
|
|
1899
1980
|
const due = new Date(built.dueDate);
|
|
@@ -1903,7 +1984,10 @@ function buildRepetition(task) {
|
|
|
1903
1984
|
}
|
|
1904
1985
|
}
|
|
1905
1986
|
} else if (perspectiveId === "projects") {
|
|
1906
|
-
// Projects: top-level tasks of active projects (not completed/dropped)
|
|
1987
|
+
// Projects: top-level tasks of active projects (not completed/dropped).
|
|
1988
|
+
// No clean whose() predicate \u2014 \`containingProject\` is not nullable
|
|
1989
|
+
// through OF's whose() (rejects _isnt: null). Stays a full scan;
|
|
1990
|
+
// see #894's follow-up for source-collection rethink.
|
|
1907
1991
|
const allTasks = ofApp.flattenedTasks();
|
|
1908
1992
|
for (let i = 0; i < allTasks.length; i++) {
|
|
1909
1993
|
const t = allTasks[i];
|
|
@@ -1913,7 +1997,9 @@ function buildRepetition(task) {
|
|
|
1913
1997
|
}
|
|
1914
1998
|
}
|
|
1915
1999
|
} else if (perspectiveId === "tags") {
|
|
1916
|
-
// Tags: tasks that have at least one tag and are not completed/dropped
|
|
2000
|
+
// Tags: tasks that have at least one tag and are not completed/dropped.
|
|
2001
|
+
// Same constraint as \`projects\` \u2014 no clean cardinality predicate via
|
|
2002
|
+
// whose(). Stays a full scan; see #894's follow-up.
|
|
1917
2003
|
const allTasks = ofApp.flattenedTasks();
|
|
1918
2004
|
for (let i = 0; i < allTasks.length; i++) {
|
|
1919
2005
|
const t = allTasks[i];
|
|
@@ -1929,7 +2015,7 @@ function buildRepetition(task) {
|
|
|
1929
2015
|
return JSON.stringify({ error: String(e) });
|
|
1930
2016
|
}
|
|
1931
2017
|
}
|
|
1932
|
-
`;var
|
|
2018
|
+
`;var eg=`/**
|
|
1933
2019
|
* JXA: list all perspectives (built-in + custom).
|
|
1934
2020
|
*
|
|
1935
2021
|
* Args (argv[0] JSON): {} (no arguments)
|
|
@@ -2020,7 +2106,7 @@ function run(_argv) {
|
|
|
2020
2106
|
|
|
2021
2107
|
return JSON.stringify({ perspectives });
|
|
2022
2108
|
}
|
|
2023
|
-
`;var
|
|
2109
|
+
`;var tg=`/**
|
|
2024
2110
|
* JXA: batch-complete projects in a single round-trip.
|
|
2025
2111
|
*
|
|
2026
2112
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -2080,7 +2166,7 @@ function run(argv) {
|
|
|
2080
2166
|
|
|
2081
2167
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
2082
2168
|
}
|
|
2083
|
-
`;var
|
|
2169
|
+
`;var ng=`/**
|
|
2084
2170
|
* JXA: batch-drop projects in a single round-trip.
|
|
2085
2171
|
*
|
|
2086
2172
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -2141,7 +2227,7 @@ function run(argv) {
|
|
|
2141
2227
|
|
|
2142
2228
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
2143
2229
|
}
|
|
2144
|
-
`;var
|
|
2230
|
+
`;var rg=`/**
|
|
2145
2231
|
* JXA: mark a project complete.
|
|
2146
2232
|
*
|
|
2147
2233
|
* Args (argv[0] JSON): { id: string, completionDate?: string|null }
|
|
@@ -2179,7 +2265,7 @@ function run(argv) {
|
|
|
2179
2265
|
|
|
2180
2266
|
return JSON.stringify({ id: args.id });
|
|
2181
2267
|
}
|
|
2182
|
-
`;var
|
|
2268
|
+
`;var og=`/**
|
|
2183
2269
|
* JXA: create a new project.
|
|
2184
2270
|
*
|
|
2185
2271
|
* Args (argv[0] JSON): { name: string, folderId?: string|null, note?: string|null,
|
|
@@ -2309,12 +2395,8 @@ function buildProject(proj) {
|
|
|
2309
2395
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
2310
2396
|
}
|
|
2311
2397
|
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
if (proj.noteHtml) noteHtml = proj.noteHtml() || null;
|
|
2315
|
-
} catch (_e) {
|
|
2316
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
2317
|
-
}
|
|
2398
|
+
// noteHtml is intentionally omitted from all project responses \u2014 use note_get_html
|
|
2399
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
2318
2400
|
|
|
2319
2401
|
let flagged = false;
|
|
2320
2402
|
try {
|
|
@@ -2391,7 +2473,7 @@ function buildProject(proj) {
|
|
|
2391
2473
|
id: proj.id(),
|
|
2392
2474
|
name: proj.name(),
|
|
2393
2475
|
note: note,
|
|
2394
|
-
noteHtml:
|
|
2476
|
+
noteHtml: null,
|
|
2395
2477
|
folderId: folderId,
|
|
2396
2478
|
tagIds: tagIds,
|
|
2397
2479
|
status: status,
|
|
@@ -2479,7 +2561,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
2479
2561
|
|
|
2480
2562
|
return JSON.stringify({ project: buildProject(newProj) });
|
|
2481
2563
|
}
|
|
2482
|
-
`;var
|
|
2564
|
+
`;var ag=`/**
|
|
2483
2565
|
* JXA: delete a project by ID.
|
|
2484
2566
|
*
|
|
2485
2567
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -2509,7 +2591,7 @@ function run(argv) {
|
|
|
2509
2591
|
|
|
2510
2592
|
return JSON.stringify({ id: args.id });
|
|
2511
2593
|
}
|
|
2512
|
-
`;var
|
|
2594
|
+
`;var sg=`/**
|
|
2513
2595
|
* JXA: mark a project dropped.
|
|
2514
2596
|
*
|
|
2515
2597
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -2539,7 +2621,7 @@ function run(argv) {
|
|
|
2539
2621
|
|
|
2540
2622
|
return JSON.stringify({ id: args.id });
|
|
2541
2623
|
}
|
|
2542
|
-
`;var
|
|
2624
|
+
`;var ig=`/**
|
|
2543
2625
|
* JXA: fetch one project by ID.
|
|
2544
2626
|
*
|
|
2545
2627
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -2667,12 +2749,8 @@ function buildProject(proj) {
|
|
|
2667
2749
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
2668
2750
|
}
|
|
2669
2751
|
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
if (proj.noteHtml) noteHtml = proj.noteHtml() || null;
|
|
2673
|
-
} catch (_e) {
|
|
2674
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
2675
|
-
}
|
|
2752
|
+
// noteHtml is intentionally omitted from all project responses \u2014 use note_get_html
|
|
2753
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
2676
2754
|
|
|
2677
2755
|
let flagged = false;
|
|
2678
2756
|
try {
|
|
@@ -2749,7 +2827,7 @@ function buildProject(proj) {
|
|
|
2749
2827
|
id: proj.id(),
|
|
2750
2828
|
name: proj.name(),
|
|
2751
2829
|
note: note,
|
|
2752
|
-
noteHtml:
|
|
2830
|
+
noteHtml: null,
|
|
2753
2831
|
folderId: folderId,
|
|
2754
2832
|
tagIds: tagIds,
|
|
2755
2833
|
status: status,
|
|
@@ -2791,7 +2869,7 @@ function normalizeStatus(raw) {
|
|
|
2791
2869
|
|
|
2792
2870
|
throw new Error(\`Project not found: \${args.id}\`);
|
|
2793
2871
|
}
|
|
2794
|
-
`;var
|
|
2872
|
+
`;var cg=`/**
|
|
2795
2873
|
* JXA: fetch multiple projects by IDs.
|
|
2796
2874
|
*
|
|
2797
2875
|
* Args (argv[0] JSON): { ids: string[] }
|
|
@@ -2919,12 +2997,8 @@ function buildProject(proj) {
|
|
|
2919
2997
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
2920
2998
|
}
|
|
2921
2999
|
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
if (proj.noteHtml) noteHtml = proj.noteHtml() || null;
|
|
2925
|
-
} catch (_e) {
|
|
2926
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
2927
|
-
}
|
|
3000
|
+
// noteHtml is intentionally omitted from all project responses \u2014 use note_get_html
|
|
3001
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
2928
3002
|
|
|
2929
3003
|
let flagged = false;
|
|
2930
3004
|
try {
|
|
@@ -3001,7 +3075,7 @@ function buildProject(proj) {
|
|
|
3001
3075
|
id: proj.id(),
|
|
3002
3076
|
name: proj.name(),
|
|
3003
3077
|
note: note,
|
|
3004
|
-
noteHtml:
|
|
3078
|
+
noteHtml: null,
|
|
3005
3079
|
folderId: folderId,
|
|
3006
3080
|
tagIds: tagIds,
|
|
3007
3081
|
status: status,
|
|
@@ -3054,7 +3128,7 @@ function normalizeStatus(raw) {
|
|
|
3054
3128
|
|
|
3055
3129
|
return JSON.stringify({ projects: results });
|
|
3056
3130
|
}
|
|
3057
|
-
`;var
|
|
3131
|
+
`;var dg=`/**
|
|
3058
3132
|
* JXA: list projects, optionally filtered by folderId or status.
|
|
3059
3133
|
*
|
|
3060
3134
|
* Args (argv[0] JSON): { folderId?: string|null, status?: string|null }
|
|
@@ -3182,12 +3256,8 @@ function buildProject(proj) {
|
|
|
3182
3256
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
3183
3257
|
}
|
|
3184
3258
|
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
if (proj.noteHtml) noteHtml = proj.noteHtml() || null;
|
|
3188
|
-
} catch (_e) {
|
|
3189
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
3190
|
-
}
|
|
3259
|
+
// noteHtml is intentionally omitted from all project responses \u2014 use note_get_html
|
|
3260
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
3191
3261
|
|
|
3192
3262
|
let flagged = false;
|
|
3193
3263
|
try {
|
|
@@ -3264,7 +3334,7 @@ function buildProject(proj) {
|
|
|
3264
3334
|
id: proj.id(),
|
|
3265
3335
|
name: proj.name(),
|
|
3266
3336
|
note: note,
|
|
3267
|
-
noteHtml:
|
|
3337
|
+
noteHtml: null,
|
|
3268
3338
|
folderId: folderId,
|
|
3269
3339
|
tagIds: tagIds,
|
|
3270
3340
|
status: status,
|
|
@@ -3318,7 +3388,7 @@ function normalizeStatus(raw) {
|
|
|
3318
3388
|
|
|
3319
3389
|
return JSON.stringify({ projects: result });
|
|
3320
3390
|
}
|
|
3321
|
-
`;var
|
|
3391
|
+
`;var lg=`/**
|
|
3322
3392
|
* JXA: mark a project as reviewed (sets lastReviewDate to now).
|
|
3323
3393
|
*
|
|
3324
3394
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -3349,7 +3419,7 @@ function run(argv) {
|
|
|
3349
3419
|
|
|
3350
3420
|
return JSON.stringify({ id: args.id });
|
|
3351
3421
|
}
|
|
3352
|
-
`;var
|
|
3422
|
+
`;var pg=`/**
|
|
3353
3423
|
* JXA: move a project to a folder (or to the root if folderId is null).
|
|
3354
3424
|
*
|
|
3355
3425
|
* Args (argv[0] JSON): { id: string, folderId?: string|null }
|
|
@@ -3396,7 +3466,7 @@ function run(argv) {
|
|
|
3396
3466
|
|
|
3397
3467
|
return JSON.stringify({ id: args.id });
|
|
3398
3468
|
}
|
|
3399
|
-
`;var
|
|
3469
|
+
`;var ug=`/**
|
|
3400
3470
|
* JXA: set a project's next review date.
|
|
3401
3471
|
*
|
|
3402
3472
|
* The third axis of OmniFocus's review schedule (the others being
|
|
@@ -3438,7 +3508,7 @@ function run(argv) {
|
|
|
3438
3508
|
|
|
3439
3509
|
return JSON.stringify({ id: args.id });
|
|
3440
3510
|
}
|
|
3441
|
-
`;var
|
|
3511
|
+
`;var mg=`/**
|
|
3442
3512
|
* JXA: set a project's review interval.
|
|
3443
3513
|
*
|
|
3444
3514
|
* Args (argv[0] JSON): { id: string, days: number | null }
|
|
@@ -3468,7 +3538,7 @@ function run(argv) {
|
|
|
3468
3538
|
|
|
3469
3539
|
return JSON.stringify({ id: args.id });
|
|
3470
3540
|
}
|
|
3471
|
-
`;var
|
|
3541
|
+
`;var fg=`/**
|
|
3472
3542
|
* JXA: update mutable fields on an existing project.
|
|
3473
3543
|
*
|
|
3474
3544
|
* Args (argv[0] JSON): { id: string, name?: string, note?: string|null,
|
|
@@ -3599,12 +3669,8 @@ function buildProject(proj) {
|
|
|
3599
3669
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
3600
3670
|
}
|
|
3601
3671
|
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
if (proj.noteHtml) noteHtml = proj.noteHtml() || null;
|
|
3605
|
-
} catch (_e) {
|
|
3606
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
3607
|
-
}
|
|
3672
|
+
// noteHtml is intentionally omitted from all project responses \u2014 use note_get_html
|
|
3673
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
3608
3674
|
|
|
3609
3675
|
let flagged = false;
|
|
3610
3676
|
try {
|
|
@@ -3681,7 +3747,7 @@ function buildProject(proj) {
|
|
|
3681
3747
|
id: proj.id(),
|
|
3682
3748
|
name: proj.name(),
|
|
3683
3749
|
note: note,
|
|
3684
|
-
noteHtml:
|
|
3750
|
+
noteHtml: null,
|
|
3685
3751
|
folderId: folderId,
|
|
3686
3752
|
tagIds: tagIds,
|
|
3687
3753
|
status: status,
|
|
@@ -3753,7 +3819,7 @@ function normalizeStatus(raw) {
|
|
|
3753
3819
|
|
|
3754
3820
|
return JSON.stringify({ project: buildProject(target) });
|
|
3755
3821
|
}
|
|
3756
|
-
`;var
|
|
3822
|
+
`;var gg=`/**
|
|
3757
3823
|
* JXA: list projects due for review (nextReviewDate <= today, or null).
|
|
3758
3824
|
*
|
|
3759
3825
|
* Args (argv[0] JSON): {}
|
|
@@ -3820,7 +3886,7 @@ function run(argv) {
|
|
|
3820
3886
|
|
|
3821
3887
|
return JSON.stringify({ projects: due });
|
|
3822
3888
|
}
|
|
3823
|
-
`;var
|
|
3889
|
+
`;var hg=`/**
|
|
3824
3890
|
* JXA: trigger Omni Sync.
|
|
3825
3891
|
*
|
|
3826
3892
|
* \`Application("OmniFocus").defaultDocument.synchronize()\` kicks off a sync
|
|
@@ -3843,7 +3909,7 @@ function run(_argv) {
|
|
|
3843
3909
|
const lastSyncAt = new Date().toISOString();
|
|
3844
3910
|
return JSON.stringify({ lastSyncAt: lastSyncAt, inFlight: false });
|
|
3845
3911
|
}
|
|
3846
|
-
`;var
|
|
3912
|
+
`;var yg=`/**
|
|
3847
3913
|
* JXA: create a tag, optionally under a parent tag.
|
|
3848
3914
|
*
|
|
3849
3915
|
* Args (argv[0] JSON): { name: string, parentId?: string }
|
|
@@ -4025,7 +4091,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
4025
4091
|
const fetchedTag = doc.flattenedTags.byId(tagId);
|
|
4026
4092
|
return JSON.stringify({ tag: buildTag(fetchedTag, docId) });
|
|
4027
4093
|
}
|
|
4028
|
-
`;var
|
|
4094
|
+
`;var kg=`/**
|
|
4029
4095
|
* JXA: delete a tag by ID.
|
|
4030
4096
|
*
|
|
4031
4097
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -4055,7 +4121,7 @@ function run(argv) {
|
|
|
4055
4121
|
|
|
4056
4122
|
return JSON.stringify({ id: args.id });
|
|
4057
4123
|
}
|
|
4058
|
-
`;var
|
|
4124
|
+
`;var vg=`/**
|
|
4059
4125
|
* JXA: fetch one tag by ID.
|
|
4060
4126
|
*
|
|
4061
4127
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -4199,7 +4265,7 @@ function buildTag(tag, docId) {
|
|
|
4199
4265
|
|
|
4200
4266
|
throw new Error(\`Tag not found: \${args.id}\`);
|
|
4201
4267
|
}
|
|
4202
|
-
`;var
|
|
4268
|
+
`;var Ig=`/**
|
|
4203
4269
|
* JXA: fetch multiple tags by IDs.
|
|
4204
4270
|
*
|
|
4205
4271
|
* Args (argv[0] JSON): { ids: string[] }
|
|
@@ -4354,7 +4420,7 @@ function buildTag(tag, docId) {
|
|
|
4354
4420
|
|
|
4355
4421
|
return JSON.stringify({ tags: results });
|
|
4356
4422
|
}
|
|
4357
|
-
`;var
|
|
4423
|
+
`;var Tg=`/**
|
|
4358
4424
|
* JXA: list all tags, optionally filtered by parentId or status.
|
|
4359
4425
|
*
|
|
4360
4426
|
* Args (argv[0] JSON): { parentId?: string, status?: string }
|
|
@@ -4507,7 +4573,7 @@ function buildTag(tag, docId) {
|
|
|
4507
4573
|
|
|
4508
4574
|
return JSON.stringify({ tags: result });
|
|
4509
4575
|
}
|
|
4510
|
-
`;var
|
|
4576
|
+
`;var wg=`/**
|
|
4511
4577
|
* JXA: update mutable fields on an existing tag.
|
|
4512
4578
|
*
|
|
4513
4579
|
* Args (argv[0] JSON): { id: string, name?: string, status?: string, allowsNextAction?: boolean }
|
|
@@ -4662,7 +4728,7 @@ function buildTag(tag, docId) {
|
|
|
4662
4728
|
|
|
4663
4729
|
return JSON.stringify({ tag: buildTag(target, docId) });
|
|
4664
4730
|
}
|
|
4665
|
-
`;var
|
|
4731
|
+
`;var bg=`/**
|
|
4666
4732
|
* JXA: batch-complete tasks in a single round-trip.
|
|
4667
4733
|
*
|
|
4668
4734
|
* Args (argv[0] JSON): { items: Array<{ id, at? }> }
|
|
@@ -4727,7 +4793,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
4727
4793
|
|
|
4728
4794
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
4729
4795
|
}
|
|
4730
|
-
`;var
|
|
4796
|
+
`;var Sg=`/**
|
|
4731
4797
|
* JXA: batch-create tasks in a single round-trip.
|
|
4732
4798
|
*
|
|
4733
4799
|
* Args (argv[0] JSON): { inputs: CreateTaskInput[] }
|
|
@@ -4830,7 +4896,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
4830
4896
|
|
|
4831
4897
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
4832
4898
|
}
|
|
4833
|
-
`;var
|
|
4899
|
+
`;var jg=`/**
|
|
4834
4900
|
* JXA: batch-delete tasks permanently in a single round-trip.
|
|
4835
4901
|
*
|
|
4836
4902
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -4886,7 +4952,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
4886
4952
|
|
|
4887
4953
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
4888
4954
|
}
|
|
4889
|
-
`;var
|
|
4955
|
+
`;var _g=`/**
|
|
4890
4956
|
* JXA: batch-drop (cancel) tasks in a single round-trip.
|
|
4891
4957
|
*
|
|
4892
4958
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -4945,7 +5011,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
4945
5011
|
|
|
4946
5012
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
4947
5013
|
}
|
|
4948
|
-
`;var
|
|
5014
|
+
`;var Og=`/**
|
|
4949
5015
|
* JXA: batch-uncomplete (mark incomplete) tasks in a single round-trip.
|
|
4950
5016
|
*
|
|
4951
5017
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -5007,7 +5073,7 @@ function run(argv) {
|
|
|
5007
5073
|
|
|
5008
5074
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
5009
5075
|
}
|
|
5010
|
-
`;var
|
|
5076
|
+
`;var xg=`/**
|
|
5011
5077
|
* JXA: batch-undrop (restore) tasks in a single round-trip.
|
|
5012
5078
|
*
|
|
5013
5079
|
* Args (argv[0] JSON): { items: Array<{ id: string }> }
|
|
@@ -5067,7 +5133,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
5067
5133
|
|
|
5068
5134
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
5069
5135
|
}
|
|
5070
|
-
`;var
|
|
5136
|
+
`;var Ag=`/**
|
|
5071
5137
|
* JXA: batch-update tasks in a single round-trip.
|
|
5072
5138
|
*
|
|
5073
5139
|
* Args (argv[0] JSON): { updates: Array<{ id, patch }> }
|
|
@@ -5163,7 +5229,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
5163
5229
|
|
|
5164
5230
|
return JSON.stringify({ succeeded: succeeded, failed: failed });
|
|
5165
5231
|
}
|
|
5166
|
-
`;var
|
|
5232
|
+
`;var Pg=`/**
|
|
5167
5233
|
* JXA: mark a task complete.
|
|
5168
5234
|
*
|
|
5169
5235
|
* Args (argv[0] JSON): { id: string, completionDate?: string|null }
|
|
@@ -5200,7 +5266,7 @@ function run(argv) {
|
|
|
5200
5266
|
|
|
5201
5267
|
return JSON.stringify({ id: args.id });
|
|
5202
5268
|
}
|
|
5203
|
-
`;var
|
|
5269
|
+
`;var Dg=`/**
|
|
5204
5270
|
* JXA: create a new task.
|
|
5205
5271
|
*
|
|
5206
5272
|
* Args (argv[0] JSON): {
|
|
@@ -5350,12 +5416,8 @@ function buildTask(task, options) {
|
|
|
5350
5416
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
5351
5417
|
}
|
|
5352
5418
|
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
5356
|
-
} catch (_e) {
|
|
5357
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
5358
|
-
}
|
|
5419
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
5420
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
5359
5421
|
|
|
5360
5422
|
let flagged = false;
|
|
5361
5423
|
try {
|
|
@@ -5427,7 +5489,7 @@ function buildTask(task, options) {
|
|
|
5427
5489
|
id: task.id(),
|
|
5428
5490
|
name: task.name(),
|
|
5429
5491
|
note: note,
|
|
5430
|
-
noteHtml:
|
|
5492
|
+
noteHtml: null,
|
|
5431
5493
|
projectId: projectId,
|
|
5432
5494
|
parentId: parentId,
|
|
5433
5495
|
tagIds: tagIds,
|
|
@@ -5551,12 +5613,16 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
5551
5613
|
// events. .id() is safe immediately; all other properties require re-fetch.
|
|
5552
5614
|
if (args.parentId || args.projectId) {
|
|
5553
5615
|
const taskId = newTask.id();
|
|
5554
|
-
const fetchedTask =
|
|
5616
|
+
const fetchedTask = lookupOrThrow(
|
|
5617
|
+
ofApp.defaultDocument.flattenedTasks.byId(taskId),
|
|
5618
|
+
"Newly created task",
|
|
5619
|
+
taskId,
|
|
5620
|
+
);
|
|
5555
5621
|
return JSON.stringify({ task: buildTask(fetchedTask) });
|
|
5556
5622
|
}
|
|
5557
5623
|
return JSON.stringify({ task: buildTask(newTask) });
|
|
5558
5624
|
}
|
|
5559
|
-
`;var
|
|
5625
|
+
`;var Rg=`/**
|
|
5560
5626
|
* JXA: delete a task permanently.
|
|
5561
5627
|
*
|
|
5562
5628
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -5585,7 +5651,7 @@ function run(argv) {
|
|
|
5585
5651
|
|
|
5586
5652
|
return JSON.stringify({ id: args.id });
|
|
5587
5653
|
}
|
|
5588
|
-
`;var
|
|
5654
|
+
`;var Cg=`/**
|
|
5589
5655
|
* JXA: mark a task as dropped.
|
|
5590
5656
|
*
|
|
5591
5657
|
* Args (argv[0] JSON): { id: string, droppedAt?: string|null }
|
|
@@ -5625,7 +5691,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
5625
5691
|
|
|
5626
5692
|
return JSON.stringify({ id: args.id });
|
|
5627
5693
|
}
|
|
5628
|
-
`;var
|
|
5694
|
+
`;var Fg=`/**
|
|
5629
5695
|
* JXA: duplicate a task. Editable fields copy; completed/dropped state reset.
|
|
5630
5696
|
* When recursive=true, subtask tree is cloned depth-first, preserving order.
|
|
5631
5697
|
*
|
|
@@ -5794,7 +5860,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
5794
5860
|
|
|
5795
5861
|
return JSON.stringify({ newId: rootClone.id(), descendantCount: descendantCount });
|
|
5796
5862
|
}
|
|
5797
|
-
`;var
|
|
5863
|
+
`;var Eg=`/**
|
|
5798
5864
|
* JXA: fetch one task by ID.
|
|
5799
5865
|
*
|
|
5800
5866
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -5939,12 +6005,8 @@ function buildTask(task, options) {
|
|
|
5939
6005
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
5940
6006
|
}
|
|
5941
6007
|
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
5945
|
-
} catch (_e) {
|
|
5946
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
5947
|
-
}
|
|
6008
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
6009
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
5948
6010
|
|
|
5949
6011
|
let flagged = false;
|
|
5950
6012
|
try {
|
|
@@ -6016,7 +6078,7 @@ function buildTask(task, options) {
|
|
|
6016
6078
|
id: task.id(),
|
|
6017
6079
|
name: task.name(),
|
|
6018
6080
|
note: note,
|
|
6019
|
-
noteHtml:
|
|
6081
|
+
noteHtml: null,
|
|
6020
6082
|
projectId: projectId,
|
|
6021
6083
|
parentId: parentId,
|
|
6022
6084
|
tagIds: tagIds,
|
|
@@ -6062,7 +6124,7 @@ function buildRepetition(task) {
|
|
|
6062
6124
|
|
|
6063
6125
|
throw new Error(\`Task not found: \${args.id}\`);
|
|
6064
6126
|
}
|
|
6065
|
-
`;var
|
|
6127
|
+
`;var Mg=`/**
|
|
6066
6128
|
* JXA: fetch multiple tasks by IDs.
|
|
6067
6129
|
*
|
|
6068
6130
|
* Args (argv[0] JSON): { ids: string[] }
|
|
@@ -6207,12 +6269,8 @@ function buildTask(task, options) {
|
|
|
6207
6269
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
6208
6270
|
}
|
|
6209
6271
|
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
6213
|
-
} catch (_e) {
|
|
6214
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
6215
|
-
}
|
|
6272
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
6273
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
6216
6274
|
|
|
6217
6275
|
let flagged = false;
|
|
6218
6276
|
try {
|
|
@@ -6284,7 +6342,7 @@ function buildTask(task, options) {
|
|
|
6284
6342
|
id: task.id(),
|
|
6285
6343
|
name: task.name(),
|
|
6286
6344
|
note: note,
|
|
6287
|
-
noteHtml:
|
|
6345
|
+
noteHtml: null,
|
|
6288
6346
|
projectId: projectId,
|
|
6289
6347
|
parentId: parentId,
|
|
6290
6348
|
tagIds: tagIds,
|
|
@@ -6341,7 +6399,7 @@ function buildRepetition(task) {
|
|
|
6341
6399
|
|
|
6342
6400
|
return JSON.stringify({ tasks: results });
|
|
6343
6401
|
}
|
|
6344
|
-
`;var
|
|
6402
|
+
`;var Ng=`/**
|
|
6345
6403
|
* JXA: list tasks, optionally filtered.
|
|
6346
6404
|
*
|
|
6347
6405
|
* Args (argv[0] JSON): {
|
|
@@ -6353,8 +6411,21 @@ function buildRepetition(task) {
|
|
|
6353
6411
|
* }
|
|
6354
6412
|
* Returns JSON: { tasks: Task[] }
|
|
6355
6413
|
*
|
|
6414
|
+
* Performance: when no source-narrowing branch applies (\`projectId\` /
|
|
6415
|
+
* \`tagId\` / \`parentId\` / \`inbox\`), the no-filter branch pushes the
|
|
6416
|
+
* boolean and date-range predicates into OF's runtime via \`whose({...})\`
|
|
6417
|
+
* (#789 / #893; mirrors the 25\xD7 speedup pattern from \`forecast_get.js\`
|
|
6418
|
+
* and \`changes_since.js\`). On a real-user DB (10k+ tasks) this avoids
|
|
6419
|
+
* materializing the long tail of unrelated tasks. The \`try\`/\`catch\`
|
|
6420
|
+
* fallback to a full scan keeps the script correct if OF rejects the
|
|
6421
|
+
* predicate for any reason; the post-loop filters are kept intact as a
|
|
6422
|
+
* safety net and to handle filters that don't push down (\`tagId\`,
|
|
6423
|
+
* \`available\`, \`blocked\`, \`completedSince\` \u2014 all need \`buildTask\`'s
|
|
6424
|
+
* computed values).
|
|
6425
|
+
*
|
|
6356
6426
|
* @see src/adapter/jxa/JxaTransport.ts \u2014 caller
|
|
6357
6427
|
* @see src/domain/task.ts \u2014 Task domain type
|
|
6428
|
+
* @see src/scripts/jxa/forecast_get.js \u2014 same whose() pushdown pattern
|
|
6358
6429
|
*/
|
|
6359
6430
|
|
|
6360
6431
|
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
|
|
@@ -6492,12 +6563,8 @@ function buildTask(task, options) {
|
|
|
6492
6563
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
6493
6564
|
}
|
|
6494
6565
|
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
6498
|
-
} catch (_e) {
|
|
6499
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
6500
|
-
}
|
|
6566
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
6567
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
6501
6568
|
|
|
6502
6569
|
let flagged = false;
|
|
6503
6570
|
try {
|
|
@@ -6569,7 +6636,7 @@ function buildTask(task, options) {
|
|
|
6569
6636
|
id: task.id(),
|
|
6570
6637
|
name: task.name(),
|
|
6571
6638
|
note: note,
|
|
6572
|
-
noteHtml:
|
|
6639
|
+
noteHtml: null,
|
|
6573
6640
|
projectId: projectId,
|
|
6574
6641
|
parentId: parentId,
|
|
6575
6642
|
tagIds: tagIds,
|
|
@@ -6622,6 +6689,12 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
6622
6689
|
}
|
|
6623
6690
|
|
|
6624
6691
|
|
|
6692
|
+
const completedSince = args.completedSince ? new Date(args.completedSince) : null;
|
|
6693
|
+
const dueBefore = args.dueBefore ? new Date(args.dueBefore) : null;
|
|
6694
|
+
const dueAfter = args.dueAfter ? new Date(args.dueAfter) : null;
|
|
6695
|
+
const deferredBefore = args.deferredBefore ? new Date(args.deferredBefore) : null;
|
|
6696
|
+
const deferredAfter = args.deferredAfter ? new Date(args.deferredAfter) : null;
|
|
6697
|
+
|
|
6625
6698
|
let tasks;
|
|
6626
6699
|
if (args.inbox) {
|
|
6627
6700
|
// inboxTasks() returns only tasks with no project assignment \u2014 exactly the Inbox scope.
|
|
@@ -6656,15 +6729,43 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
6656
6729
|
);
|
|
6657
6730
|
tasks = tag.tasks();
|
|
6658
6731
|
} else {
|
|
6659
|
-
|
|
6732
|
+
// No source-narrowing branch \u2014 push every pushable predicate into
|
|
6733
|
+
// OF's runtime via whose(). Predicates that don't push down (tag /
|
|
6734
|
+
// available / blocked / completedSince \u2014 all need buildTask's
|
|
6735
|
+
// computed values) stay client-side in the loop below.
|
|
6736
|
+
const predicate = {};
|
|
6737
|
+
if (args.flagged !== null && args.flagged !== undefined) {
|
|
6738
|
+
predicate.flagged = args.flagged;
|
|
6739
|
+
}
|
|
6740
|
+
if (args.completed !== null && args.completed !== undefined) {
|
|
6741
|
+
predicate.completed = args.completed;
|
|
6742
|
+
}
|
|
6743
|
+
if (dueBefore !== null || dueAfter !== null) {
|
|
6744
|
+
predicate.dueDate = {};
|
|
6745
|
+
if (dueBefore !== null) predicate.dueDate._lessThan = dueBefore;
|
|
6746
|
+
if (dueAfter !== null) predicate.dueDate._greaterThan = dueAfter;
|
|
6747
|
+
}
|
|
6748
|
+
if (deferredBefore !== null || deferredAfter !== null) {
|
|
6749
|
+
predicate.deferDate = {};
|
|
6750
|
+
if (deferredBefore !== null) predicate.deferDate._lessThan = deferredBefore;
|
|
6751
|
+
if (deferredAfter !== null) predicate.deferDate._greaterThan = deferredAfter;
|
|
6752
|
+
}
|
|
6753
|
+
const hasPushable = Object.keys(predicate).length > 0;
|
|
6754
|
+
if (hasPushable) {
|
|
6755
|
+
try {
|
|
6756
|
+
tasks = ofApp.defaultDocument.flattenedTasks.whose(predicate)();
|
|
6757
|
+
} catch (_e) {
|
|
6758
|
+
// OF rejected the predicate for some reason \u2014 fall back to the
|
|
6759
|
+
// full scan so the post-loop filters still produce correct results.
|
|
6760
|
+
tasks =
|
|
6761
|
+
ofApp.defaultDocument.flattenedTasks(); /* narrow-scan-ok: whose() pushdown rejected by OF, full scan is the documented fallback */
|
|
6762
|
+
}
|
|
6763
|
+
} else {
|
|
6764
|
+
tasks =
|
|
6765
|
+
ofApp.defaultDocument.flattenedTasks(); /* narrow-scan-ok: else-branch fallback when no scope filter provided */
|
|
6766
|
+
}
|
|
6660
6767
|
}
|
|
6661
6768
|
|
|
6662
|
-
const completedSince = args.completedSince ? new Date(args.completedSince) : null;
|
|
6663
|
-
const dueBefore = args.dueBefore ? new Date(args.dueBefore) : null;
|
|
6664
|
-
const dueAfter = args.dueAfter ? new Date(args.dueAfter) : null;
|
|
6665
|
-
const deferredBefore = args.deferredBefore ? new Date(args.deferredBefore) : null;
|
|
6666
|
-
const deferredAfter = args.deferredAfter ? new Date(args.deferredAfter) : null;
|
|
6667
|
-
|
|
6668
6769
|
const result = [];
|
|
6669
6770
|
for (let i = 0; i < tasks.length; i++) {
|
|
6670
6771
|
const t = tasks[i];
|
|
@@ -6710,7 +6811,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
6710
6811
|
|
|
6711
6812
|
return JSON.stringify({ tasks: result });
|
|
6712
6813
|
}
|
|
6713
|
-
`;var
|
|
6814
|
+
`;var Ug=`/**
|
|
6714
6815
|
* JXA: move a task to a different project or parent task.
|
|
6715
6816
|
*
|
|
6716
6817
|
* Args (argv[0] JSON): { id: string, projectId?: string|null, parentId?: string|null }
|
|
@@ -6725,7 +6826,8 @@ function run(argv) {
|
|
|
6725
6826
|
const ofApp = Application("OmniFocus");
|
|
6726
6827
|
ofApp.includeStandardAdditions = false;
|
|
6727
6828
|
|
|
6728
|
-
const allTasks =
|
|
6829
|
+
const allTasks =
|
|
6830
|
+
ofApp.defaultDocument.flattenedTasks(); /* narrow-scan-ok: must resolve source task by id before knowing its container */
|
|
6729
6831
|
let found = null;
|
|
6730
6832
|
for (let i = 0; i < allTasks.length; i++) {
|
|
6731
6833
|
if (allTasks[i].id() === args.id) {
|
|
@@ -6772,7 +6874,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
6772
6874
|
|
|
6773
6875
|
return JSON.stringify({ id: args.id });
|
|
6774
6876
|
}
|
|
6775
|
-
`;var
|
|
6877
|
+
`;var Lg=`/**
|
|
6776
6878
|
* JXA: search tasks by keyword and/or structured filters.
|
|
6777
6879
|
*
|
|
6778
6880
|
* Args (argv[0] JSON): {
|
|
@@ -6788,8 +6890,18 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
6788
6890
|
* }
|
|
6789
6891
|
* Returns JSON: { tasks: Task[] }
|
|
6790
6892
|
*
|
|
6893
|
+
* Performance: when no \`projectId\` is provided, the script pushes
|
|
6894
|
+
* \`flagged\` / \`completed\` / \`dueDate\` predicates into OF's runtime via
|
|
6895
|
+
* \`whose({...})\` (#789 / #895; mirrors the \`forecast_get.js\` pattern).
|
|
6896
|
+
* Tag, available, and text-search predicates stay client-side because
|
|
6897
|
+
* they need \`buildTask\`'s computed values. The text-search \`_contains\`
|
|
6898
|
+
* pushdown is intentionally skipped for v1 \u2014 \`_contains\` support in OF
|
|
6899
|
+
* 4.x's whose() is unverified; landing it would need a hands-on test
|
|
6900
|
+
* against live OF that's outside this loop. See the parent #789 audit.
|
|
6901
|
+
*
|
|
6791
6902
|
* @see src/adapter/jxa/JxaTransport.ts \u2014 searchTasks() caller
|
|
6792
6903
|
* @see src/domain/task.ts \u2014 Task domain type
|
|
6904
|
+
* @see src/scripts/jxa/forecast_get.js \u2014 same whose() pushdown pattern
|
|
6793
6905
|
*/
|
|
6794
6906
|
|
|
6795
6907
|
// biome-ignore lint/correctness/noUnusedVariables: osascript invokes run(argv) by convention.
|
|
@@ -6934,12 +7046,8 @@ function buildTask(task, options) {
|
|
|
6934
7046
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
6935
7047
|
}
|
|
6936
7048
|
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
6940
|
-
} catch (_e) {
|
|
6941
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
6942
|
-
}
|
|
7049
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
7050
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
6943
7051
|
|
|
6944
7052
|
let flagged = false;
|
|
6945
7053
|
try {
|
|
@@ -7011,7 +7119,7 @@ function buildTask(task, options) {
|
|
|
7011
7119
|
id: task.id(),
|
|
7012
7120
|
name: task.name(),
|
|
7013
7121
|
note: note,
|
|
7014
|
-
noteHtml:
|
|
7122
|
+
noteHtml: null,
|
|
7015
7123
|
projectId: projectId,
|
|
7016
7124
|
parentId: parentId,
|
|
7017
7125
|
tagIds: tagIds,
|
|
@@ -7070,6 +7178,9 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
7070
7178
|
|
|
7071
7179
|
let tasks;
|
|
7072
7180
|
if (args.projectId) {
|
|
7181
|
+
// Source-narrowed: project's own tasks. The whose() pushdown still
|
|
7182
|
+
// applies but we'd need to attach it to proj.flattenedTasks \u2014 skip
|
|
7183
|
+
// for v1 since the project scope is already bounded.
|
|
7073
7184
|
const proj = lookupOrThrow(
|
|
7074
7185
|
ofApp.defaultDocument.flattenedProjects.byId(args.projectId),
|
|
7075
7186
|
"Project",
|
|
@@ -7077,11 +7188,42 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
7077
7188
|
);
|
|
7078
7189
|
tasks = proj.flattenedTasks();
|
|
7079
7190
|
} else {
|
|
7080
|
-
|
|
7191
|
+
// No source-narrowing \u2014 push pushable predicates into whose() so the
|
|
7192
|
+
// long tail of non-matching tasks is never iterated.
|
|
7193
|
+
const predicate = {};
|
|
7194
|
+
if (args.flagged !== null && args.flagged !== undefined) {
|
|
7195
|
+
predicate.flagged = args.flagged;
|
|
7196
|
+
}
|
|
7197
|
+
if (completed === "exclude") {
|
|
7198
|
+
predicate.completed = false;
|
|
7199
|
+
} else if (completed === "only") {
|
|
7200
|
+
predicate.completed = true;
|
|
7201
|
+
}
|
|
7202
|
+
// "any" passes through \u2014 no completed predicate.
|
|
7203
|
+
if (dueBefore !== null || dueAfter !== null) {
|
|
7204
|
+
predicate.dueDate = {};
|
|
7205
|
+
if (dueBefore !== null) predicate.dueDate._lessThan = dueBefore;
|
|
7206
|
+
if (dueAfter !== null) predicate.dueDate._greaterThan = dueAfter;
|
|
7207
|
+
}
|
|
7208
|
+
const hasPushable = Object.keys(predicate).length > 0;
|
|
7209
|
+
if (hasPushable) {
|
|
7210
|
+
try {
|
|
7211
|
+
tasks = ofApp.defaultDocument.flattenedTasks.whose(predicate)();
|
|
7212
|
+
} catch (_e) {
|
|
7213
|
+
// OF rejected the predicate \u2014 fall back to the full scan so the
|
|
7214
|
+
// post-loop filters still produce correct results.
|
|
7215
|
+
tasks =
|
|
7216
|
+
ofApp.defaultDocument.flattenedTasks(); /* narrow-scan-ok: whose() pushdown rejected by OF, full scan is the documented fallback */
|
|
7217
|
+
}
|
|
7218
|
+
} else {
|
|
7219
|
+
tasks =
|
|
7220
|
+
ofApp.defaultDocument.flattenedTasks(); /* narrow-scan-ok: else-branch fallback when no scope filter provided */
|
|
7221
|
+
}
|
|
7081
7222
|
}
|
|
7082
7223
|
|
|
7083
7224
|
// ---------------------------------------------------------------------------
|
|
7084
|
-
// Filter
|
|
7225
|
+
// Filter (post-loop guards \u2014 defensive against whose() partial coverage,
|
|
7226
|
+
// and required for predicates that didn't push down: tag, available, q)
|
|
7085
7227
|
// ---------------------------------------------------------------------------
|
|
7086
7228
|
|
|
7087
7229
|
const result = [];
|
|
@@ -7110,9 +7252,12 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
7110
7252
|
if (dueAfter !== null && due <= dueAfter) continue;
|
|
7111
7253
|
}
|
|
7112
7254
|
|
|
7113
|
-
// Tag filter \u2014 task must carry ALL listed tags
|
|
7255
|
+
// Tag filter \u2014 task must carry ALL listed tags.
|
|
7256
|
+
// Build a Set<string> once per task for O(1) membership checks instead of
|
|
7257
|
+
// O(filterTags \xD7 taskTags) nested scan (#803).
|
|
7114
7258
|
if (args.tagIds && args.tagIds.length > 0) {
|
|
7115
|
-
const
|
|
7259
|
+
const taskTagSet = new Set(built.tagIds);
|
|
7260
|
+
const allPresent = args.tagIds.every((tid) => taskTagSet.has(tid));
|
|
7116
7261
|
if (!allPresent) continue;
|
|
7117
7262
|
}
|
|
7118
7263
|
|
|
@@ -7130,7 +7275,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
7130
7275
|
|
|
7131
7276
|
return JSON.stringify({ tasks: result });
|
|
7132
7277
|
}
|
|
7133
|
-
`;var
|
|
7278
|
+
`;var Jg=`/**
|
|
7134
7279
|
* JXA: mark a task incomplete.
|
|
7135
7280
|
*
|
|
7136
7281
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -7159,7 +7304,7 @@ function run(argv) {
|
|
|
7159
7304
|
|
|
7160
7305
|
return JSON.stringify({ id: args.id });
|
|
7161
7306
|
}
|
|
7162
|
-
`;var
|
|
7307
|
+
`;var Bg=`/**
|
|
7163
7308
|
* JXA: remove dropped status from a task.
|
|
7164
7309
|
*
|
|
7165
7310
|
* Args (argv[0] JSON): { id: string }
|
|
@@ -7199,7 +7344,7 @@ function lookupOrThrow(specifier, kindLabel, idValue) {
|
|
|
7199
7344
|
|
|
7200
7345
|
return JSON.stringify({ id: args.id });
|
|
7201
7346
|
}
|
|
7202
|
-
`;var
|
|
7347
|
+
`;var $g=`/**
|
|
7203
7348
|
* JXA: update task fields.
|
|
7204
7349
|
*
|
|
7205
7350
|
* Args (argv[0] JSON): {
|
|
@@ -7350,12 +7495,8 @@ function buildTask(task, options) {
|
|
|
7350
7495
|
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
7351
7496
|
}
|
|
7352
7497
|
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
if (task.noteHtml) noteHtml = task.noteHtml() || null;
|
|
7356
|
-
} catch (_e) {
|
|
7357
|
-
/* OF 4.x: property access may not exist on all object types \u2014 default used */
|
|
7358
|
-
}
|
|
7498
|
+
// noteHtml is intentionally omitted from all task responses \u2014 use note_get_html
|
|
7499
|
+
// to retrieve HTML note content on demand. See perf issue #791.
|
|
7359
7500
|
|
|
7360
7501
|
let flagged = false;
|
|
7361
7502
|
try {
|
|
@@ -7427,7 +7568,7 @@ function buildTask(task, options) {
|
|
|
7427
7568
|
id: task.id(),
|
|
7428
7569
|
name: task.name(),
|
|
7429
7570
|
note: note,
|
|
7430
|
-
noteHtml:
|
|
7571
|
+
noteHtml: null,
|
|
7431
7572
|
projectId: projectId,
|
|
7432
7573
|
parentId: parentId,
|
|
7433
7574
|
tagIds: tagIds,
|
|
@@ -7464,7 +7605,8 @@ function buildRepetition(task) {
|
|
|
7464
7605
|
}
|
|
7465
7606
|
|
|
7466
7607
|
|
|
7467
|
-
const allTasks =
|
|
7608
|
+
const allTasks =
|
|
7609
|
+
ofApp.defaultDocument.flattenedTasks(); /* narrow-scan-ok: must resolve task by id; no scope hint available */
|
|
7468
7610
|
let found = null;
|
|
7469
7611
|
for (let i = 0; i < allTasks.length; i++) {
|
|
7470
7612
|
if (allTasks[i].id() === args.id) {
|
|
@@ -7526,7 +7668,7 @@ function buildRepetition(task) {
|
|
|
7526
7668
|
|
|
7527
7669
|
return JSON.stringify({ task: buildTask(found) });
|
|
7528
7670
|
}
|
|
7529
|
-
`;var
|
|
7671
|
+
`;var Wg=`/**
|
|
7530
7672
|
* JXA: read the current state of OmniFocus's front window.
|
|
7531
7673
|
*
|
|
7532
7674
|
* Returns JSON: { perspectiveName, focusContainerIds }
|
|
@@ -7579,7 +7721,7 @@ function run(_argv) {
|
|
|
7579
7721
|
|
|
7580
7722
|
return JSON.stringify({ perspectiveName, focusContainerIds });
|
|
7581
7723
|
}
|
|
7582
|
-
`;var
|
|
7724
|
+
`;var Hg=`/**
|
|
7583
7725
|
* JXA: set or clear the front window's focus container.
|
|
7584
7726
|
*
|
|
7585
7727
|
* Args (argv[0] JSON): { containerId: string | null }
|
|
@@ -7655,7 +7797,7 @@ function run(argv) {
|
|
|
7655
7797
|
w.focus = [target];
|
|
7656
7798
|
return JSON.stringify({ focusContainerIds: [args.containerId] });
|
|
7657
7799
|
}
|
|
7658
|
-
`;var
|
|
7800
|
+
`;var zg=`/**
|
|
7659
7801
|
* JXA: switch the front window to a named perspective.
|
|
7660
7802
|
*
|
|
7661
7803
|
* Args (argv[0] JSON): { perspectiveName: string }
|
|
@@ -7707,7 +7849,7 @@ function run(argv) {
|
|
|
7707
7849
|
w.perspective = target;
|
|
7708
7850
|
return JSON.stringify({ perspectiveName: args.perspectiveName });
|
|
7709
7851
|
}
|
|
7710
|
-
`;var Pg=new AsyncLocalStorage;function Dg(t,e){let n=ulid();return Pg.run(n,t)}function Ge(){return Pg.getStore()}function Ag(){return ulid()}function pj(t){return createHash("sha1").update(Xe(t)).digest("hex").slice(0,16)}function Or(t,e,n,r,o){C.debug({event:"transport.call",transport:t,scriptName:e,argsHash:pj(n),durationMs:r,outcome:o,correlationId:Ge()},"transport call");}var mj=16*1024*1024,fj=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-",e],{timeout:n,maxBuffer:mj,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 P(t,e={},n={}){let r=n.spawner??fj,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(Or("jxa",i,e,d,l),c.spawnError!==void 0)throw new wt("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 Tt(`JXA script exceeded ${o}ms timeout${f}`,{details:{transport:"jxa",timeoutMs:o,...i!==void 0?{scriptName:i}:{}}})}if(c.exitCode!==0){let f=gj(c.stderr,i);if(f!==null)throw f;let g=i!==void 0?` [${i}]`:"";throw new E(`JXA script failed (exit ${c.exitCode})${g}`,{details:{transport:"jxa",exitCode:c.exitCode,stderr:ht(c.stderr,1024),...i!==void 0?{scriptName:i}:{}}})}let m=c.stdout.trim();if(m==="")throw new E("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 E("JXA script returned malformed JSON",{cause:f,details:{transport:"jxa",stdoutPreview:ht(m,200),...i!==void 0?{scriptName:i}:{}}})}}function gj(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 kt({details:{transport:"jxa",stderr:ht(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 vt({details:{transport:"jxa",stderr:ht(t,512),...e!==void 0?{scriptName:e}:{}}}):/\bnot found\b/i.test(t)||/^OF_NOT_FOUND\b/m.test(t)||/Can['’]t get object\.?\s*\(-1728\)/i.test(t)||/\(-1728\)/.test(t)?new j(t,{details:{transport:"jxa",stderr:ht(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:ht(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_CONFLICT\b/m.test(t)?new It(t,{details:{transport:"jxa",stderr:ht(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function ht(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function hc(t){return typeof t=="object"&&t!==null&&"error"in t&&typeof t.error=="object"}var Pr=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 P(vg,{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 P(yg,{id:e},{...this.runOpts,scriptName:"task_get"});return {...n.task,id:h.of(n.task.id)}}async getTasksMany(e){return (await P(kg,{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 P(mg,{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 P(Sg,{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 P(ug,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"task_complete"});}async uncompleteTask(e){await P(wg,{id:e},{...this.runOpts,scriptName:"task_uncomplete"});}async dropTask(e,n){await P(gg,{id:e,droppedAt:n?.toISOString()??null},{...this.runOpts,scriptName:"task_drop"});}async undropTask(e){await P(bg,{id:e},{...this.runOpts,scriptName:"task_undrop"});}async deleteTask(e){await P(fg,{id:e},{...this.runOpts,scriptName:"task_delete"});}async moveTask(e,n){await P(Ig,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});}async convertTaskToProject(e,n){throw new E("convertTaskToProject routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"convertTaskToProject"}})}async batchMoveTasks(e){throw new E("batchMoveTasks routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"batchMoveTasks"}})}async reorderTask(e,n){throw new E("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 P(hg,{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 P(sg,{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 Oe(n,h.of)}async batchUpdateTasks(e){let n=await P(pg,{updates:e},{...this.runOpts,scriptName:"task_batch_update"});return Oe(n,h.of)}async batchCompleteTasks(e){let n=await P(ag,{items:e.map(r=>({id:r.id,at:r.at?.toISOString()??null}))},{...this.runOpts,scriptName:"task_batch_complete"});return Oe(n,h.of)}async batchUncompleteTasks(e){let n=await P(dg,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_uncomplete"});return Oe(n,h.of)}async batchDeleteTasks(e){let n=await P(ig,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_delete"});return Oe(n,h.of)}async batchDropTasks(e){let n=await P(cg,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_drop"});return Oe(n,h.of)}async batchUndropTasks(e){let n=await P(lg,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_undrop"});return Oe(n,h.of)}async listProjects(e){return (await P(Vf,{folderId:e?.folderId??null,status:e?.status??null},{...this.runOpts,scriptName:"project_list"})).projects.map(r=>({...r,id:v.of(r.id)}))}async getProject(e){let n=await P(Wf,{id:e},{...this.runOpts,scriptName:"project_get"});return {...n.project,id:v.of(n.project.id)}}async getProjectsMany(e){return (await P(Hf,{ids:e},{...this.runOpts,scriptName:"project_get_many"})).projects.map(r=>r?{...r,id:v.of(r.id)}:null)}async createProject(e){let n=await P(Jf,{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 v.of(n.project.id)}async updateProject(e,n){await P(Xf,{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 P(Lf,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"project_complete"});}async dropProject(e){await P($f,{id:e},{...this.runOpts,scriptName:"project_drop"});}async batchCompleteProjects(e){let n=await P(Nf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_complete"});return Oe(n,v.of)}async batchDropProjects(e){let n=await P(Uf,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_drop"});return Oe(n,v.of)}async moveProject(e,n){await P(qf,{id:e,folderId:n.folderId??null},{...this.runOpts,scriptName:"project_move"});}async deleteProject(e){await P(Bf,{id:e},{...this.runOpts,scriptName:"project_delete"});}async markProjectReviewed(e){await P(zf,{id:e},{...this.runOpts,scriptName:"project_mark_reviewed"});}async listProjectsDueForReview(){return (await P(Yf,{},{...this.runOpts,scriptName:"review_list_due"})).projects}async setProjectReviewInterval(e,n){await P(Kf,{id:e,days:n},{...this.runOpts,scriptName:"project_set_review_interval"});}async setProjectNextReviewDate(e,n){await P(Gf,{id:e,nextReviewDate:n},{...this.runOpts,scriptName:"project_set_next_review_date"});}async listTags(e){return (await P(rg,{parentId:e?.parentId??null,status:e?.status??null},{...this.runOpts,scriptName:"tag_list"})).tags.map(r=>({...r,id:b.of(r.id)}))}async getTag(e){let n=await P(tg,{id:e},{...this.runOpts,scriptName:"tag_get"});return {...n.tag,id:b.of(n.tag.id)}}async getTagsMany(e){return (await P(ng,{ids:e},{...this.runOpts,scriptName:"tag_get_many"})).tags.map(r=>r?{...r,id:b.of(r.id)}:null)}async createTag(e){let n=await P(Qf,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"tag_create"});return b.of(n.tag.id)}async updateTag(e,n){await P(og,{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 P(eg,{id:e},{...this.runOpts,scriptName:"tag_delete"});}async listFolders(e){return (await P(Rf,{parentId:e?.parentId??null},{...this.runOpts,scriptName:"folder_list"})).folders.map(r=>({...r,id:$.of(r.id)}))}async getFolder(e){let n=await P(Af,{id:e},{...this.runOpts,scriptName:"folder_get"});return {...n.folder,id:$.of(n.folder.id)}}async createFolder(e){let n=await P(Pf,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"folder_create"});return $.of(n.folder.id)}async updateFolder(e,n){await P(Cf,{id:e,...n.name!==void 0?{name:n.name}:{}},{...this.runOpts,scriptName:"folder_update"});}async deleteFolder(e){await P(Df,{id:e},{...this.runOpts,scriptName:"folder_delete"});}async listPerspectives(){return (await P(Mf,{},{...this.runOpts,scriptName:"perspective_list"})).perspectives}async evaluatePerspective(e){return (await P(Ef,{perspectiveId:e},{...this.runOpts,scriptName:"perspective_evaluate"})).tasks.map(r=>({...r,id:h.of(r.id)}))}async evaluateCustomPerspective(e){throw new E("evaluateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluateCustomPerspective"}})}async evaluatePerspectiveRules(e,n){throw new E("evaluatePerspectiveRules requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluatePerspectiveRules"}})}async getCustomPerspective(e){throw new E("getCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"getCustomPerspective"}})}async deleteCustomPerspective(e){throw new E("deleteCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"deleteCustomPerspective"}})}async createCustomPerspective(e){throw new E("createCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"createCustomPerspective"}})}async updateCustomPerspective(e,n){throw new E("updateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"updateCustomPerspective"}})}async searchTasks(e){return (await P(Tg,{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 P(Ff,{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 E("getForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"getForecastTag"}})}async setForecastTag(e){throw new E("setForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setForecastTag"}})}async undoLastMutation(){throw new E("undoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"undoLastMutation"}})}async redoLastMutation(){throw new E("redoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"redoLastMutation"}})}async setTaskAlarms(e,n){throw new E("setTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setTaskAlarms"}})}async clearTaskAlarms(e){throw new E("clearTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"clearTaskAlarms"}})}async listAttachments(e){return (await P(jf,e,{...this.runOpts,scriptName:"attachment_list"})).attachments}async addAttachment(e){let n=await P(Sf,e,{...this.runOpts,scriptName:"attachment_add"});return Pe.of(n.id)}async removeAttachment(e){await P(_f,e,{...this.runOpts,scriptName:"attachment_remove"});}async saveAttachmentToPath(e){return P(xf,e,{...this.runOpts,scriptName:"attachment_save_to_path"})}async appLaunch(){return P(bf,{},{...this.runOpts,scriptName:"app_launch"})}async getWindowState(){let e=await P(jg,{},{...this.runOpts,scriptName:"window_get_state"});if(hc(e))throw new bt(e.error.message,{details:{transport:"jxa",scriptName:"window_get_state"}});return e}async setWindowPerspective(e){let n=await P(xg,{perspectiveName:e},{...this.runOpts,scriptName:"window_set_perspective"});if(hc(n))throw n.error.code==="NO_FRONT_WINDOW"?new bt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}}):new j(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}});return n}async setWindowFocus(e){let n=await P(_g,{containerId:e},{...this.runOpts,scriptName:"window_set_focus"});if(hc(n))throw n.error.code==="NO_FRONT_WINDOW"?new bt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}}):new j(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}});return n}async appWindowNew(){throw new E("appWindowNew routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNew"}})}async appWindowNewTab(){throw new E("appWindowNewTab routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNewTab"}})}async pluginInvoke(e){throw new E("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 P(Zf,{},{...this.runOpts,scriptName:"sync_trigger"})}async getLastSync(){return {lastSyncAt:null,inFlight:false}}async getChangesSince(e){let n=await P(Of,{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 P(e,n??{},{...this.runOpts,scriptName:"raw"})}};var Rg=`/**
|
|
7852
|
+
`;var qg=new AsyncLocalStorage;function Gg(t,e){let n=ulid();return qg.run(n,t)}function at(){return qg.getStore()}function Kg(){return ulid()}function Fj(t){return createHash("sha1").update(ct(t)).digest("hex").slice(0,16)}function Gr(t,e,n,r,o){R.debug({event:"transport.call",transport:t,scriptName:e,argsHash:Fj(n),durationMs:r,outcome:o,correlationId:at()},"transport call");}var Nj=16*1024*1024,Uj=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-",e],{timeout:n,maxBuffer:Nj,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,s,i)=>{let c=s,l=i,d=a!==null&&a.killed===true,u=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:l,exitCode:a===null?0:a.code??1,timedOut:d,...u!==void 0?{spawnError:u}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");}),Lj=new Set(["attachment_list","changes_since","folder_get","folder_list","forecast_get","perspective_evaluate","perspective_list","ping","project_get","project_get_many","project_list","review_list_due","tag_get","tag_get_many","tag_list","task_get","task_get_many","task_list","task_search","window_get_state"]),Jj=/\(-(?:1728|10024|10003)\)/;function Bj(t){return t.spawnError!==void 0?false:t.timedOut?true:t.exitCode===0?false:Jj.test(t.stderr)}function $j(t){return t.timedOut?"timeout":/\(-1728\)/.test(t.stderr)?"errno-1728":/\(-10024\)/.test(t.stderr)?"errno-10024":/\(-10003\)/.test(t.stderr)?"errno-10003":"other"}async function D(t,e={},n={}){let r=n.spawner??Uj,o=n.timeoutMs??3e4,a=JSON.stringify(e??{}),s=n.scriptName,i=$n(n.retry),c=performance.now(),l=await r(t,a,o),d=s!==void 0&&Lj.has(s);if(i.enabled&&d&&Bj(l)){let h=$j(l),w=performance.now();i.delayMs>0&&await setTimeout$1(i.delayMs);let S=await r(t,a,o),T=Math.round(performance.now()-w),x=S.spawnError!==void 0||S.timedOut||S.exitCode!==0||S.stdout.trim()===""?"error":"ok";R.info({event:"transport.retry",transport:"jxa",scriptName:s,reason:h,outcome:x,delayMs:i.delayMs,durationMs:T},"transport.retry"),l=S;}let f=Math.round(performance.now()-c),g=l.spawnError!==void 0||l.timedOut||l.exitCode!==0||l.stdout.trim()===""?"error":"ok";if(Gr("jxa",s,e,f,g),l.spawnError!==void 0)throw new Mt("Failed to spawn osascript",{cause:l.spawnError,details:{transport:"jxa",reason:l.spawnError.code??"spawn-failed",...s!==void 0?{scriptName:s}:{}}});if(l.timedOut){let h=s!==void 0?` (script: ${s})`:"";throw new Et(`JXA script exceeded ${o}ms timeout${h}`,{details:{transport:"jxa",timeoutMs:o,...s!==void 0?{scriptName:s}:{}}})}if(l.exitCode!==0){let h=Wj(l.stderr,s);if(h!==null)throw h;let w=s!==void 0?` [${s}]`:"";throw new E(`JXA script failed (exit ${l.exitCode})${w}`,{details:{transport:"jxa",exitCode:l.exitCode,stderr:Dt(l.stderr,1024),...s!==void 0?{scriptName:s}:{}}})}let I=l.stdout.trim();if(I==="")throw new E("JXA script returned empty stdout \u2014 `run()` must return a JSON-encoded string",{details:{transport:"jxa",...s!==void 0?{scriptName:s}:{}}});try{return JSON.parse(I)}catch(h){throw new E("JXA script returned malformed JSON",{cause:h,details:{transport:"jxa",stdoutPreview:Dt(I,200),...s!==void 0?{scriptName:s}:{}}})}}function Wj(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 Ct({details:{transport:"jxa",stderr:Dt(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 Ft({details:{transport:"jxa",stderr:Dt(t,512),...e!==void 0?{scriptName:e}:{}}}):/\bnot found\b/i.test(t)||/^OF_NOT_FOUND\b/m.test(t)||/Can['’]t get object\.?\s*\(-1728\)/i.test(t)||/\(-1728\)/.test(t)?new _(t,{details:{transport:"jxa",stderr:Dt(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_VALIDATION\b/m.test(t)||/\bValidationError:/m.test(t)||/\bis required\b/i.test(t)?new k(t,{details:{transport:"jxa",stderr:Dt(t,512),...e!==void 0?{scriptName:e}:{}}}):/^OF_CONFLICT\b/m.test(t)?new it(t,{details:{transport:"jxa",stderr:Dt(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function Dt(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 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 (await D(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:y.of(r.id)}))}async getTask(e){let n=await D(Eg,{id:e},{...this.runOpts,scriptName:"task_get"});return {...n.task,id:y.of(n.task.id)}}async getNoteHtml(e,n){return (await D(Zf,{kind:e,id:n},{...this.runOpts,scriptName:"note_get_html"})).noteHtml}async getTasksMany(e){return (await D(Mg,{ids:e},{...this.runOpts,scriptName:"task_get_many"})).tasks.map(r=>r?{...r,id:y.of(r.id)}:null)}async createTask(e){let n=await D(Dg,{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 y.of(n.task.id)}async updateTask(e,n){await D($g,{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 D(Pg,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"task_complete"});}async uncompleteTask(e){await D(Jg,{id:e},{...this.runOpts,scriptName:"task_uncomplete"});}async dropTask(e,n){await D(Cg,{id:e,droppedAt:n?.toISOString()??null},{...this.runOpts,scriptName:"task_drop"});}async undropTask(e){await D(Bg,{id:e},{...this.runOpts,scriptName:"task_undrop"});}async deleteTask(e){await D(Rg,{id:e},{...this.runOpts,scriptName:"task_delete"});}async moveTask(e,n){await D(Ug,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});}async convertTaskToProject(e,n){throw new E("convertTaskToProject routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"convertTaskToProject"}})}async batchMoveTasks(e){throw new E("batchMoveTasks routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"batchMoveTasks"}})}async reorderTask(e,n){throw new E("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 D(Fg,{id:e,recursive:n.recursive,destination:r},{...this.runOpts,scriptName:"task_duplicate"});return {newId:y.of(o.newId),descendantCount:o.descendantCount}}async batchCreateTasks(e){let n=await D(Sg,{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 Ne(n,y.of)}async batchUpdateTasks(e){let n=await D(Ag,{updates:e},{...this.runOpts,scriptName:"task_batch_update"});return Ne(n,y.of)}async batchCompleteTasks(e){let n=await D(bg,{items:e.map(r=>({id:r.id,at:r.at?.toISOString()??null}))},{...this.runOpts,scriptName:"task_batch_complete"});return Ne(n,y.of)}async batchUncompleteTasks(e){let n=await D(Og,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_uncomplete"});return Ne(n,y.of)}async batchDeleteTasks(e){let n=await D(jg,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_delete"});return Ne(n,y.of)}async batchDropTasks(e){let n=await D(_g,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_drop"});return Ne(n,y.of)}async batchUndropTasks(e){let n=await D(xg,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"task_batch_undrop"});return Ne(n,y.of)}async listProjects(e){return (await D(dg,{folderId:e?.folderId??null,status:e?.status??null},{...this.runOpts,scriptName:"project_list"})).projects.map(r=>({...r,id:v.of(r.id)}))}async getProject(e){let n=await D(ig,{id:e},{...this.runOpts,scriptName:"project_get"});return {...n.project,id:v.of(n.project.id)}}async getProjectsMany(e){return (await D(cg,{ids:e},{...this.runOpts,scriptName:"project_get_many"})).projects.map(r=>r?{...r,id:v.of(r.id)}:null)}async createProject(e){let n=await D(og,{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 v.of(n.project.id)}async updateProject(e,n){await D(fg,{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 D(rg,{id:e,completionDate:n?.toISOString()??null},{...this.runOpts,scriptName:"project_complete"});}async dropProject(e){await D(sg,{id:e},{...this.runOpts,scriptName:"project_drop"});}async batchCompleteProjects(e){let n=await D(tg,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_complete"});return Ne(n,v.of)}async batchDropProjects(e){let n=await D(ng,{items:e.map(r=>({id:r.id}))},{...this.runOpts,scriptName:"project_batch_drop"});return Ne(n,v.of)}async moveProject(e,n){await D(pg,{id:e,folderId:n.folderId??null},{...this.runOpts,scriptName:"project_move"});}async deleteProject(e){await D(ag,{id:e},{...this.runOpts,scriptName:"project_delete"});}async markProjectReviewed(e){await D(lg,{id:e},{...this.runOpts,scriptName:"project_mark_reviewed"});}async listProjectsDueForReview(){return (await D(gg,{},{...this.runOpts,scriptName:"review_list_due"})).projects}async setProjectReviewInterval(e,n){await D(mg,{id:e,days:n},{...this.runOpts,scriptName:"project_set_review_interval"});}async setProjectNextReviewDate(e,n){await D(ug,{id:e,nextReviewDate:n},{...this.runOpts,scriptName:"project_set_next_review_date"});}async listTags(e){return (await D(Tg,{parentId:e?.parentId??null,status:e?.status??null},{...this.runOpts,scriptName:"tag_list"})).tags.map(r=>({...r,id:b.of(r.id)}))}async getTag(e){let n=await D(vg,{id:e},{...this.runOpts,scriptName:"tag_get"});return {...n.tag,id:b.of(n.tag.id)}}async getTagsMany(e){return (await D(Ig,{ids:e},{...this.runOpts,scriptName:"tag_get_many"})).tags.map(r=>r?{...r,id:b.of(r.id)}:null)}async createTag(e){let n=await D(yg,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"tag_create"});return b.of(n.tag.id)}async updateTag(e,n){await D(wg,{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 D(kg,{id:e},{...this.runOpts,scriptName:"tag_delete"});}async listFolders(e){return (await D(Kf,{parentId:e?.parentId??null},{...this.runOpts,scriptName:"folder_list"})).folders.map(r=>({...r,id:$.of(r.id)}))}async getFolder(e){let n=await D(Gf,{id:e},{...this.runOpts,scriptName:"folder_get"});return {...n.folder,id:$.of(n.folder.id)}}async createFolder(e){let n=await D(Vf,{name:e.name,parentId:e.parentId??null},{...this.runOpts,scriptName:"folder_create"});return $.of(n.folder.id)}async updateFolder(e,n){await D(Xf,{id:e,...n.name!==void 0?{name:n.name}:{}},{...this.runOpts,scriptName:"folder_update"});}async deleteFolder(e){await D(qf,{id:e},{...this.runOpts,scriptName:"folder_delete"});}async listPerspectives(){return (await D(eg,{},{...this.runOpts,scriptName:"perspective_list"})).perspectives}async evaluatePerspective(e){return (await D(Qf,{perspectiveId:e},{...this.runOpts,scriptName:"perspective_evaluate"})).tasks.map(r=>({...r,id:y.of(r.id)}))}async evaluateCustomPerspective(e){throw new E("evaluateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluateCustomPerspective"}})}async evaluatePerspectiveRules(e,n){throw new E("evaluatePerspectiveRules requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"evaluatePerspectiveRules"}})}async getCustomPerspective(e){throw new E("getCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"getCustomPerspective"}})}async deleteCustomPerspective(e){throw new E("deleteCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"deleteCustomPerspective"}})}async createCustomPerspective(e){throw new E("createCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"createCustomPerspective"}})}async updateCustomPerspective(e,n){throw new E("updateCustomPerspective requires the OmniJS transport",{details:{transport:"jxa",reason:"omnijs-only",method:"updateCustomPerspective"}})}async searchTasks(e){return (await D(Lg,{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:y.of(r.id)}))}async getForecast(e){let n=await D(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:y.of(r.id)})),dueToday:n.dueToday.map(r=>({...r,id:y.of(r.id)})),deferredToday:n.deferredToday.map(r=>({...r,id:y.of(r.id)})),flagged:n.flagged.map(r=>({...r,id:y.of(r.id)}))}}async getForecastTag(){throw new E("getForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"getForecastTag"}})}async setForecastTag(e){throw new E("setForecastTag routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setForecastTag"}})}async undoLastMutation(){throw new E("undoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"undoLastMutation"}})}async redoLastMutation(){throw new E("redoLastMutation routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"redoLastMutation"}})}async setTaskAlarms(e,n){throw new E("setTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"setTaskAlarms"}})}async clearTaskAlarms(e){throw new E("clearTaskAlarms routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"clearTaskAlarms"}})}async listAttachments(e){return (await D($f,e,{...this.runOpts,scriptName:"attachment_list"})).attachments}async addAttachment(e){let n=await D(Bf,e,{...this.runOpts,scriptName:"attachment_add"});return Le.of(n.id)}async removeAttachment(e){await D(Wf,e,{...this.runOpts,scriptName:"attachment_remove"});}async saveAttachmentToPath(e){return D(Hf,e,{...this.runOpts,scriptName:"attachment_save_to_path"})}async appLaunch(){return D(Jf,{},{...this.runOpts,scriptName:"app_launch"})}async getWindowState(){let e=await D(Wg,{},{...this.runOpts,scriptName:"window_get_state"});if(Nc(e))throw new Nt(e.error.message,{details:{transport:"jxa",scriptName:"window_get_state"}});return e}async setWindowPerspective(e){let n=await D(zg,{perspectiveName:e},{...this.runOpts,scriptName:"window_set_perspective"});if(Nc(n))throw n.error.code==="NO_FRONT_WINDOW"?new Nt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}}):new _(n.error.message,{details:{transport:"jxa",scriptName:"window_set_perspective"}});return n}async setWindowFocus(e){let n=await D(Hg,{containerId:e},{...this.runOpts,scriptName:"window_set_focus"});if(Nc(n))throw n.error.code==="NO_FRONT_WINDOW"?new Nt(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}}):new _(n.error.message,{details:{transport:"jxa",scriptName:"window_set_focus"}});return n}async appWindowNew(){throw new E("appWindowNew routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNew"}})}async appWindowNewTab(){throw new E("appWindowNewTab routes to OmniJsTransport \u2014 JXA transport unavailable",{details:{transport:"jxa",reason:"routes-to-omnijs",method:"appWindowNewTab"}})}async pluginInvoke(e){throw new E("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 D(hg,{},{...this.runOpts,scriptName:"sync_trigger"})}async getLastSync(){return {lastSyncAt:null,inFlight:false}}async getChangesSince(e){let n=await D(zf,{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 D(e,n??{},{...this.runOpts,scriptName:"raw"})}};var Xg=`/**
|
|
7711
7853
|
* OmniJS: open a new OmniFocus window.
|
|
7712
7854
|
*
|
|
7713
7855
|
* Wraps \`document.newWindow()\`. The new window opens in the foreground with
|
|
@@ -7727,7 +7869,7 @@ function run(argv) {
|
|
|
7727
7869
|
return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
|
|
7728
7870
|
}
|
|
7729
7871
|
})();
|
|
7730
|
-
`;var
|
|
7872
|
+
`;var Yg=`/**
|
|
7731
7873
|
* OmniJS: add a new tab to the front OmniFocus window.
|
|
7732
7874
|
*
|
|
7733
7875
|
* Wraps \`document.newTabOnWindow(window)\` using the front window (index 0).
|
|
@@ -7756,7 +7898,7 @@ function run(argv) {
|
|
|
7756
7898
|
return JSON.stringify({ error: { code: "WINDOW_OPEN_FAILED", message: msg } });
|
|
7757
7899
|
}
|
|
7758
7900
|
})();
|
|
7759
|
-
`;var
|
|
7901
|
+
`;var Zg=`/**
|
|
7760
7902
|
* OmniJS: redo the most recently undone mutation.
|
|
7761
7903
|
*
|
|
7762
7904
|
* Wraps \`Database.redo()\`. Behaves identically to \u2318\u21E7Z in the OmniFocus UI:
|
|
@@ -7783,7 +7925,7 @@ function run(argv) {
|
|
|
7783
7925
|
return JSON.stringify({ error: { code: "REDO_FAILED", message: msg } });
|
|
7784
7926
|
}
|
|
7785
7927
|
})();
|
|
7786
|
-
`;var
|
|
7928
|
+
`;var Qg=`/**
|
|
7787
7929
|
* OmniJS: undo the most recent document mutation.
|
|
7788
7930
|
*
|
|
7789
7931
|
* Wraps \`Database.undo()\`. Behaves identically to \u2318Z in the OmniFocus UI:
|
|
@@ -7814,7 +7956,7 @@ function run(argv) {
|
|
|
7814
7956
|
return JSON.stringify({ error: { code: "UNDO_FAILED", message: msg } });
|
|
7815
7957
|
}
|
|
7816
7958
|
})();
|
|
7817
|
-
`;var
|
|
7959
|
+
`;var eh=`/**
|
|
7818
7960
|
* OmniJS: read the forecast-tag preference (the tag whose tasks always
|
|
7819
7961
|
* appear on the Forecast view alongside dated items).
|
|
7820
7962
|
*
|
|
@@ -7833,7 +7975,7 @@ function run(argv) {
|
|
|
7833
7975
|
const tag = Database.forecastTag;
|
|
7834
7976
|
return JSON.stringify({ tagId: tag === null ? null : tag.id.primaryKey });
|
|
7835
7977
|
})();
|
|
7836
|
-
`;var
|
|
7978
|
+
`;var th=`/**
|
|
7837
7979
|
* OmniJS: set or clear the forecast-tag preference.
|
|
7838
7980
|
*
|
|
7839
7981
|
* Args injected as \`globalThis.__args\`:
|
|
@@ -7870,7 +8012,7 @@ function run(argv) {
|
|
|
7870
8012
|
Database.forecastTag = tag;
|
|
7871
8013
|
return JSON.stringify({ tagId });
|
|
7872
8014
|
})();
|
|
7873
|
-
`;var
|
|
8015
|
+
`;var nh=`/**
|
|
7874
8016
|
* perspective_create.js \u2014 create a new custom OmniFocus perspective.
|
|
7875
8017
|
*
|
|
7876
8018
|
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
@@ -8022,7 +8164,7 @@ function run(argv) {
|
|
|
8022
8164
|
|
|
8023
8165
|
return JSON.stringify({ id: identifier });
|
|
8024
8166
|
})();
|
|
8025
|
-
`;var
|
|
8167
|
+
`;var rh=`/**
|
|
8026
8168
|
* perspective_delete.js \u2014 delete a custom OmniFocus perspective by identifier.
|
|
8027
8169
|
*
|
|
8028
8170
|
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
@@ -8063,7 +8205,7 @@ function run(argv) {
|
|
|
8063
8205
|
|
|
8064
8206
|
return JSON.stringify({ id: identifier });
|
|
8065
8207
|
})();
|
|
8066
|
-
`;var
|
|
8208
|
+
`;var oh=`/**
|
|
8067
8209
|
* perspective_evaluate.js \u2014 evaluate a custom OmniFocus perspective and
|
|
8068
8210
|
* return its task list as domain \`Task\` objects.
|
|
8069
8211
|
*
|
|
@@ -8187,7 +8329,7 @@ function run(argv) {
|
|
|
8187
8329
|
|
|
8188
8330
|
return JSON.stringify({ tasks });
|
|
8189
8331
|
})();
|
|
8190
|
-
`;var
|
|
8332
|
+
`;var ah=`/**
|
|
8191
8333
|
* perspective_evaluate_dry_run.js \u2014 evaluate a *proposed* perspective rule
|
|
8192
8334
|
* tree and return its task list, without persisting the perspective.
|
|
8193
8335
|
*
|
|
@@ -8392,7 +8534,7 @@ function run(argv) {
|
|
|
8392
8534
|
|
|
8393
8535
|
return JSON.stringify({ tasks });
|
|
8394
8536
|
})();
|
|
8395
|
-
`;var
|
|
8537
|
+
`;var sh=`/**
|
|
8396
8538
|
* perspective_get.js \u2014 read a custom OmniFocus perspective's full configuration.
|
|
8397
8539
|
*
|
|
8398
8540
|
* Called via the OmniJS transport. Args injected as \`globalThis.__args\`:
|
|
@@ -8542,7 +8684,7 @@ function run(argv) {
|
|
|
8542
8684
|
},
|
|
8543
8685
|
});
|
|
8544
8686
|
})();
|
|
8545
|
-
`;var
|
|
8687
|
+
`;var ih=`/**
|
|
8546
8688
|
* perspective_update.js \u2014 partial-patch update of a custom OmniFocus
|
|
8547
8689
|
* perspective.
|
|
8548
8690
|
*
|
|
@@ -8640,7 +8782,7 @@ function run(argv) {
|
|
|
8640
8782
|
|
|
8641
8783
|
return JSON.stringify({ id: identifier });
|
|
8642
8784
|
})();
|
|
8643
|
-
`;var
|
|
8785
|
+
`;var ch=`/**
|
|
8644
8786
|
* plugin_invoke.js \u2014 invoke a named Omni Automation plug-in action.
|
|
8645
8787
|
*
|
|
8646
8788
|
* Called via the OmniJS transport (evaluateJavascript bridge).
|
|
@@ -8672,7 +8814,7 @@ function run(argv) {
|
|
|
8672
8814
|
const rawResult = action.perform([arg]);
|
|
8673
8815
|
return JSON.stringify({ result: rawResult ?? null });
|
|
8674
8816
|
})();
|
|
8675
|
-
`;var
|
|
8817
|
+
`;var dh=`/**
|
|
8676
8818
|
* OmniJS: create a new project.
|
|
8677
8819
|
*
|
|
8678
8820
|
* Routes through OmniJS rather than JXA per ADR-0019: JXA's
|
|
@@ -8831,7 +8973,7 @@ function run(argv) {
|
|
|
8831
8973
|
},
|
|
8832
8974
|
});
|
|
8833
8975
|
})();
|
|
8834
|
-
`;var
|
|
8976
|
+
`;var lh=`/**
|
|
8835
8977
|
* OmniJS: move a project to a different folder, or to the library root
|
|
8836
8978
|
* (when \`folderId\` is null).
|
|
8837
8979
|
*
|
|
@@ -8892,7 +9034,7 @@ function run(argv) {
|
|
|
8892
9034
|
|
|
8893
9035
|
return JSON.stringify({ id: args.id });
|
|
8894
9036
|
})();
|
|
8895
|
-
`;var
|
|
9037
|
+
`;var ph=`/**
|
|
8896
9038
|
* OmniJS: batch-move tasks to different destinations in a single round-trip.
|
|
8897
9039
|
*
|
|
8898
9040
|
* JXA's \`task.move()\` is unimplemented in OmniFocus 4.x (error 9 "Replacement
|
|
@@ -8968,7 +9110,7 @@ function run(argv) {
|
|
|
8968
9110
|
|
|
8969
9111
|
return JSON.stringify({ succeeded, failed });
|
|
8970
9112
|
})();
|
|
8971
|
-
`;var
|
|
9113
|
+
`;var uh=`/**
|
|
8972
9114
|
* OmniJS: clear all alarms/notifications from a task.
|
|
8973
9115
|
*
|
|
8974
9116
|
* Args injected as \`globalThis.__args\`: { taskId: string }
|
|
@@ -9009,7 +9151,7 @@ function run(argv) {
|
|
|
9009
9151
|
|
|
9010
9152
|
return JSON.stringify({ ok: true });
|
|
9011
9153
|
})();
|
|
9012
|
-
`;var
|
|
9154
|
+
`;var mh=`/**
|
|
9013
9155
|
* OmniJS: convert a task to a project via Database.convertTasksToProjects().
|
|
9014
9156
|
*
|
|
9015
9157
|
* This operation is only available via OmniJS \u2014 JXA has no equivalent.
|
|
@@ -9072,7 +9214,7 @@ function run(argv) {
|
|
|
9072
9214
|
|
|
9073
9215
|
return JSON.stringify({ projectId: project.id.primaryKey });
|
|
9074
9216
|
})();
|
|
9075
|
-
`;var
|
|
9217
|
+
`;var fh=`/**
|
|
9076
9218
|
* OmniJS: create a new task.
|
|
9077
9219
|
*
|
|
9078
9220
|
* Routes through OmniJS rather than JXA per ADR-0019: JXA's
|
|
@@ -9223,7 +9365,7 @@ function run(argv) {
|
|
|
9223
9365
|
},
|
|
9224
9366
|
});
|
|
9225
9367
|
})();
|
|
9226
|
-
`;var
|
|
9368
|
+
`;var gh=`/**
|
|
9227
9369
|
* OmniJS: duplicate a task. Editable fields copy; completed/dropped state
|
|
9228
9370
|
* reset on every clone. When \`recursive: true\`, the entire subtree is
|
|
9229
9371
|
* cloned via \`duplicateTasks(...)\` (preserves order); otherwise the clone
|
|
@@ -9376,7 +9518,7 @@ function run(argv) {
|
|
|
9376
9518
|
|
|
9377
9519
|
return JSON.stringify({ newId, descendantCount });
|
|
9378
9520
|
})();
|
|
9379
|
-
`;var
|
|
9521
|
+
`;var hh=`/**
|
|
9380
9522
|
* OmniJS: move a task to a different project, parent task, or the inbox.
|
|
9381
9523
|
*
|
|
9382
9524
|
* JXA's \`task.move({ to: container })\` is unimplemented in OmniFocus 4.x
|
|
@@ -9432,7 +9574,7 @@ function run(argv) {
|
|
|
9432
9574
|
|
|
9433
9575
|
return JSON.stringify({ id });
|
|
9434
9576
|
})();
|
|
9435
|
-
`;var
|
|
9577
|
+
`;var yh=`/**
|
|
9436
9578
|
* OmniJS: reorder a task among its siblings.
|
|
9437
9579
|
*
|
|
9438
9580
|
* JXA's \`task.move({ to: ref, positioned: ... })\` shares the same broken
|
|
@@ -9534,7 +9676,7 @@ function run(argv) {
|
|
|
9534
9676
|
moveTasks([task], location);
|
|
9535
9677
|
return JSON.stringify({ id });
|
|
9536
9678
|
})();
|
|
9537
|
-
`;var
|
|
9679
|
+
`;var kh=`/**
|
|
9538
9680
|
* OmniJS: replace a task's alarm/notification set atomically.
|
|
9539
9681
|
*
|
|
9540
9682
|
* Args injected as \`globalThis.__args\`: { taskId: string, alarms: TaskAlarm[] }
|
|
@@ -9630,17 +9772,17 @@ function run(argv) {
|
|
|
9630
9772
|
|
|
9631
9773
|
return JSON.stringify({ ok: true });
|
|
9632
9774
|
})();
|
|
9633
|
-
`;var
|
|
9775
|
+
`;var Vj=16*1024*1024,qj=(t,e,n)=>new Promise(r=>{let o=execFile("osascript",["-l","JavaScript","-"],{timeout:n,maxBuffer:Vj,env:{...process.env,LANG:"en_US.UTF-8"},encoding:"utf8"},(a,s,i)=>{let c=s,l=i,d=a!==null&&a.killed===true,u=a&&a.code==="ENOENT"?a:void 0;r({stdout:c,stderr:l,exitCode:a===null?0:a.code??1,timedOut:d,...u!==void 0?{spawnError:u}:{}});});o.stdin!==null&&o.stdin.end(t,"utf8");}),Gj=new Set(["forecast_get_tag","perspective_evaluate","perspective_evaluate_dry_run","perspective_get","ping"]);function Kj(t){return t.spawnError!==void 0?false:t.timedOut}function Xj(t){return t.timedOut?"timeout":"other"}async function Y(t,e={},n={}){let r=n.spawner??qj,o=n.timeoutMs??45e3,a=JSON.stringify(e??{}),s=n.scriptName,i=$n(n.retry),c=Yj(t,a),l=performance.now(),d=await r(c,a,o),u=s!==void 0&&Gj.has(s);if(i.enabled&&u&&Kj(d)){let w=Xj(d),S=performance.now();i.delayMs>0&&await setTimeout$1(i.delayMs);let T=await r(c,a,o),x=Math.round(performance.now()-S),O=T.spawnError!==void 0||T.timedOut||T.exitCode!==0||T.stdout.trim()===""?"error":"ok";R.info({event:"transport.retry",transport:"omnijs",scriptName:s,reason:w,outcome:O,delayMs:i.delayMs,durationMs:x},"transport.retry"),d=T;}let g=Math.round(performance.now()-l),I=d.spawnError!==void 0||d.timedOut||d.exitCode!==0||d.stdout.trim()===""?"error":"ok";if(Gr("omnijs",s,e,g,I),d.spawnError!==void 0)throw new Mt("Failed to spawn osascript",{cause:d.spawnError,details:{transport:"omnijs",reason:d.spawnError.code??"spawn-failed",...s!==void 0?{scriptName:s}:{}}});if(d.timedOut){let w=s!==void 0?` (script: ${s})`:"";throw new Et(`OmniJS script exceeded ${o}ms timeout${w}`,{details:{transport:"omnijs",timeoutMs:o,...s!==void 0?{scriptName:s}:{}}})}if(d.exitCode!==0){let w=Zj(d.stderr,s);if(w!==null)throw w;let S=s!==void 0?` [${s}]`:"";throw new E(`OmniJS script failed (exit ${d.exitCode})${S}`,{details:{transport:"omnijs",exitCode:d.exitCode,stderr:Xr(d.stderr,1024),...s!==void 0?{scriptName:s}:{}}})}let h=d.stdout.trim();if(h==="")throw new E("OmniJS script returned empty stdout \u2014 the IIFE must return a JSON-encoded string",{details:{transport:"omnijs",...s!==void 0?{scriptName:s}:{}}});try{return JSON.parse(h)}catch(w){throw new E("OmniJS script returned malformed JSON",{cause:w,details:{transport:"omnijs",stdoutPreview:Xr(h,200),...s!==void 0?{scriptName:s}:{}}})}}function Yj(t,e){let n=`globalThis.__args = ${e};
|
|
9634
9776
|
${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(`
|
|
9635
|
-
`)}function Ij(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 kt({details:{transport:"omnijs",stderr:Dr(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 vt({details:{transport:"omnijs",stderr:Dr(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function Dr(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function A(t){throw new E(`OmniJsTransport.${t} is not wired yet`,{details:{transport:"omnijs",reason:"not-yet-wired",method:t}})}var Ar=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return A("listTasks")}async getTask(e){return A("getTask")}async getTasksMany(e){return A("getTasksMany")}async createTask(e){let n=await G(Xg,{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"});if(Y(n))throw n.error.code==="NOT_FOUND"?new j(n.error.message,{details:{transport:"omnijs",scriptName:"task_create"}}):new y(n.error.message,{details:{transport:"omnijs",scriptName:"task_create"}});return h.of(n.task.id)}async updateTask(e,n){return A("updateTask")}async completeTask(e,n){return A("completeTask")}async uncompleteTask(e){return A("uncompleteTask")}async dropTask(e,n){return A("dropTask")}async undropTask(e){return A("undropTask")}async deleteTask(e){return A("deleteTask")}async moveTask(e,n){let o=await G(Zg,{id:e,projectId:n.projectId??null,parentId:n.parentId??null},{...this.runOpts,scriptName:"task_move"});if(Y(o))throw o.error.code==="NOT_FOUND"?new j(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 G(Kg,{id:e,folderId:n.folderId??null,position:n.position??"ending"},{...this.runOpts,scriptName:"task_convert_to_project"});if(Y(r))throw r.error.code==="NOT_FOUND"?new j(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 v.of(r.projectId)}async batchMoveTasks(e){let r=await G(qg,{items:e.map(o=>({id:o.id,projectId:o.destination.projectId??null,parentId:o.destination.parentId??null}))},{...this.runOpts,scriptName:"task_batch_move"});if(Y(r))throw new y(r.error.message,{details:{transport:"omnijs",scriptName:"task_batch_move"}});return Oe(r,h.of)}async reorderTask(e,n){let r=Qg,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 G(r,o,{...this.runOpts,scriptName:"task_reorder"});if(Y(a))throw a.error.code==="NOT_FOUND"?new j(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}}):new y(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}})}async duplicateTask(e,n){let r=n.destination===void 0?null:"projectId"in n.destination?{projectId:n.destination.projectId}:"parentId"in n.destination?{parentId:n.destination.parentId}:{toInbox:true},o=await G(Yg,{id:e,recursive:n.recursive,destination:r},{...this.runOpts,scriptName:"task_duplicate"});if(Y(o))throw o.error.code==="NOT_FOUND"?new j(o.error.message,{details:{transport:"omnijs",scriptName:"task_duplicate"}}):new y(o.error.message,{details:{transport:"omnijs",scriptName:"task_duplicate"}});return {newId:h.of(o.newId),descendantCount:o.descendantCount}}async batchCreateTasks(e){return A("batchCreateTasks")}async batchUpdateTasks(e){return A("batchUpdateTasks")}async batchCompleteTasks(e){return A("batchCompleteTasks")}async batchUncompleteTasks(e){return A("batchUncompleteTasks")}async batchDeleteTasks(e){return A("batchDeleteTasks")}async batchDropTasks(e){return A("batchDropTasks")}async batchUndropTasks(e){return A("batchUndropTasks")}async listProjects(e){return A("listProjects")}async getProject(e){return A("getProject")}async getProjectsMany(e){return A("getProjectsMany")}async createProject(e){let n=await G(Vg,{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,completionCriterion:e.completionCriterion??null,reviewIntervalDays:e.reviewIntervalDays??null},{...this.runOpts,scriptName:"project_create"});if(Y(n))throw n.error.code==="NOT_FOUND"?new j(n.error.message,{details:{transport:"omnijs",scriptName:"project_create"}}):new y(n.error.message,{details:{transport:"omnijs",scriptName:"project_create"}});return v.of(n.project.id)}async updateProject(e,n){return A("updateProject")}async completeProject(e,n){return A("completeProject")}async dropProject(e,n){return A("dropProject")}async batchCompleteProjects(e){return A("batchCompleteProjects")}async batchDropProjects(e){return A("batchDropProjects")}async moveProject(e,n){let r=await G(zg,{id:e,folderId:n.folderId??null},{...this.runOpts,scriptName:"project_move"});if(Y(r))throw r.error.code==="NOT_FOUND"?new j(r.error.message,{details:{transport:"omnijs",scriptName:"project_move"}}):new y(r.error.message,{details:{transport:"omnijs",scriptName:"project_move"}})}async deleteProject(e){return A("deleteProject")}async markProjectReviewed(e){return A("markProjectReviewed")}async listProjectsDueForReview(){return A("listProjectsDueForReview")}async setProjectReviewInterval(e,n){return A("setProjectReviewInterval")}async setProjectNextReviewDate(e,n){return A("setProjectNextReviewDate")}async listTags(e){return A("listTags")}async getTag(e){return A("getTag")}async getTagsMany(e){return A("getTagsMany")}async createTag(e){return A("createTag")}async updateTag(e,n){return A("updateTag")}async deleteTag(e){return A("deleteTag")}async listFolders(e){return A("listFolders")}async getFolder(e){return A("getFolder")}async createFolder(e){return A("createFolder")}async updateFolder(e,n){return A("updateFolder")}async deleteFolder(e){return A("deleteFolder")}async searchTasks(e){return A("searchTasks")}async getForecast(e){return A("getForecast")}async getForecastTag(){let n=await G(Mg,{},{...this.runOpts,scriptName:"forecast_get_tag"});if(Y(n))throw new E(n.error.message,{details:{transport:"omnijs",scriptName:"forecast_get_tag"}});return {tagId:n.tagId===null?null:b.of(n.tagId)}}async setForecastTag(e){let r=await G(Ng,{tagId:e},{...this.runOpts,scriptName:"forecast_set_tag"});if(Y(r))throw r.error.code==="NOT_FOUND"?new j(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:b.of(r.tagId)}}async undoLastMutation(){let e=await G(Eg,{},{...this.runOpts,scriptName:"database_undo"});if(Y(e))throw new E(e.error.message,{details:{transport:"omnijs",scriptName:"database_undo",code:e.error.code}});return {undid:e.undid}}async redoLastMutation(){let e=await G(Fg,{},{...this.runOpts,scriptName:"database_redo"});if(Y(e))throw new E(e.error.message,{details:{transport:"omnijs",scriptName:"database_redo",code:e.error.code}});return {redid:e.redid}}async setTaskAlarms(e,n){let r=await G(eh,{taskId:String(e),alarms:n},{...this.runOpts,scriptName:"task_set_alarms"});if(Y(r))throw r.error.code==="NOT_FOUND"?new j(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 E(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms",code:r.error.code}})}async clearTaskAlarms(e){let n=await G(Gg,{taskId:String(e)},{...this.runOpts,scriptName:"task_clear_alarms"});if(Y(n))throw n.error.code==="NOT_FOUND"?new j(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms"}}):new E(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms",code:n.error.code}})}async listAttachments(e){return A("listAttachments")}async addAttachment(e){return A("addAttachment")}async removeAttachment(e){return A("removeAttachment")}async saveAttachmentToPath(e){return A("saveAttachmentToPath")}async appLaunch(){return A("appLaunch")}async getWindowState(){return A("getWindowState")}async setWindowPerspective(e){return A("setWindowPerspective")}async setWindowFocus(e){return A("setWindowFocus")}async appWindowNew(){let e=await G(Rg,{},{...this.runOpts,scriptName:"app_window_new"});if(Y(e))throw new E(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new",code:e.error.code}});return e}async appWindowNewTab(){let e=await G(Cg,{},{...this.runOpts,scriptName:"app_window_new_tab"});if(Y(e))throw e.error.code==="WINDOW_UNAVAILABLE"?new E(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}}):new E(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}});return e}async pluginInvoke(e){return G(Hg,{identifier:e.identifier,arg:e.arg??null},{...this.runOpts,scriptName:"plugin_invoke"})}async listPerspectives(){return A("listPerspectives")}async evaluatePerspective(e){return A("evaluatePerspective")}async evaluateCustomPerspective(e){let r=await G(Jg,{identifier:e},{...this.runOpts,scriptName:"perspective_evaluate"});if(Y(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Ue(r.error.message,{details:{feature:"custom-perspectives"}}):new j(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 G(Bg,{rules:e,...n!==void 0&&{aggregation:n}},{...this.runOpts,scriptName:"perspective_evaluate_dry_run"});if(Y(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Ue(r.error.message,{details:{feature:"custom-perspectives"}}):new E(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 G($g,{identifier:e},{...this.runOpts,scriptName:"perspective_get"});if(Y(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):new j(n.error.message,{details:{resource:"perspective",id:e}});return n.perspective}async deleteCustomPerspective(e){let n=await G(Lg,{identifier:e},{...this.runOpts,scriptName:"perspective_delete"});if(Y(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Ue(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="NOT_FOUND"?new j(n.error.message,{details:{resource:"perspective",id:e}}):new E(n.error.message,{details:{transport:"omnijs",script:"perspective_delete",id:e}})}async createCustomPerspective(e){let n=await G(Ug,{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(Y(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 E(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 G(Wg,r,{...this.runOpts,scriptName:"perspective_update"});if(Y(o))throw o.error.code==="FEATURE_REQUIRES_PRO"?new Ue(o.error.message,{details:{feature:"custom-perspectives"}}):o.error.code==="NOT_FOUND"?new j(o.error.message,{details:{resource:"perspective",id:e}}):o.error.code==="VALIDATION_ERROR"?new y(o.error.message,{details:{id:e}}):new E(o.error.message,{details:{transport:"omnijs",script:"perspective_update",id:e}})}async syncTrigger(){return A("syncTrigger")}async getLastSync(){return A("getLastSync")}async getChangesSince(e){return {taskIds:[],projectIds:[]}}async runOmniJsScript(e,n){return G(e,n??{},{...this.runOpts,scriptName:"raw"})}};var Rr=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=Ge(),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 Sj=["/System/","/private/System/","/Library/","/private/Library/"];async function yc(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 Sj)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 th=1024*1024;async function nh(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/th;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*th}})}function rh(t){return "taskId"in t&&t.taskId!==void 0?{taskId:t.taskId}:{projectId:t.projectId}}function oh(t){return "taskId"in t?"task":"project"}var Fr=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 yc(e.filePath,this.allowedPaths),await nh(e.filePath,this.maxMb);let n=await this.adapter.addAttachment(e),r=rh(e);return {id:n,ownerKind:oh(r),ownerName:await this.lookupOwnerName(r)}}async remove(e){let n=rh(e),r=await this.lookupOwnerName(n);return await this.adapter.removeAttachment(e),{ownerKind:oh(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 yc(n,this.allowedPaths),this.adapter.saveAttachmentToPath(e)}};function Er(t,e){t!==void 0&&Id(t,{folderId:e});}var Mr=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 Er(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateFolder(e,n),Er(this.cache,e);}async delete(e,n=false){n&&await this._cascadeEmpty(e),await this.adapter.deleteFolder(e),Er(this.cache,e);}async move(e,n){await this.adapter.updateFolder(e,{parentId:n}),Er(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 Nr=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 kc(t){return pt.includes(t)}var Ur=class{adapter;constructor(e){this.adapter=e.adapter;}async list(){return {perspectives:await this.adapter.listPerspectives(),cacheHit:false}}async evaluate(e){return {tasks:kc(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(kc(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(kc(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 ah=200,vc=1e3,Lr=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=Wt(r),a=e.cursor!==void 0?Vt(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:Ci(i)};if(!n)return {project:s};let d=(await this.adapter.listTasks({projectId:e.id})).map(l=>({...l,_links:ot(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(w=>!(s!==void 0&&w.flagged!==s||c!==void 0&&(w.nextReviewDate===null||w.nextReviewDate>=c)))].sort((w,R)=>w.createdAt!==R.createdAt?w.createdAt<R.createdAt?-1:1:w.id<R.id?-1:1),m=n!==void 0?l.filter(w=>zt({id:w.id,sortValue:w.createdAt},n,"asc")):l,f=m.slice(0,r),I=m.length>r?this.encodeNextCursor(f,o):null;return {projects:f.map(w=>({...w,_links:Ci(w)})),nextCursor:I}}resolveLimit(e){if(e.limit===void 0)return ah;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>vc)throw new y(`limit must be an integer between 1 and ${vc}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${vc}, or omit to use the default of ${ah}.`,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 Ht({lastId:r.id,lastSortValue:r.createdAt,filterHash:n})}};var Jr=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});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&&B(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&&B(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 _j=100,xj=500,Br=class{adapter;constructor({adapter:e}){this.adapter=e;}async search(e){let n=Math.min(e.limit??_j,xj),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=Wt(r),a=null;e.cursor&&(a=Vt(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((k,w)=>{let R=k.createdAt.localeCompare(w.createdAt);return R!==0?R:k.id.localeCompare(w.id)}),l=(a?c.filter(k=>zt({id:k.id,sortValue:k.createdAt},a,"asc")):c).slice(0,n+1),m=l.length>n,f=l.slice(0,n).map(k=>({...k,_links:ot(k)})),g=f.at(-1),I=m&&g?Ht({lastId:g.id,lastSortValue:g.createdAt,filterHash:o}):null;return {tasks:f,nextCursor:I,hasMore:m,cacheHit:false}}};function at(t,e){t!==void 0&&vd(t,{tagId:e});}var $r=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 at(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateTag(e,n),at(this.cache,e);}async delete(e){await this.adapter.deleteTag(e),at(this.cache,e);}async move(e,n){await this.adapter.updateTag(e,{parentId:n}),at(this.cache,e);}async setStatus(e,n){await this.adapter.updateTag(e,{status:n}),at(this.cache,e);}async setAllowsNextAction(e,n){await this.adapter.updateTag(e,{allowsNextAction:n}),at(this.cache,e);}async setLocation(e,n){await this.adapter.updateTag(e,{location:n}),at(this.cache,e);}async clearLocation(e){await this.adapter.updateTag(e,{location:null}),at(this.cache,e);}async getLocation(e){return {location:(await this.adapter.getTag(e)).location,cacheHit:false}}};function sh(t){if(t.OMNIFOCUS_E2E_USE_MEMORY){let r=new xr;return new Yt({jxa:r,omnijs:r})}let e=new Pr({timeoutMs:t.OMNIFOCUS_JXA_TIMEOUT_MS}),n=new Ar({timeoutMs:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS});return Yt.fromTransports(e,n)}function ih(t,e){let n=new Rr({capacity:e.OMNIFOCUS_CACHE_CAPACITY,ttlMs:e.OMNIFOCUS_CACHE_TTL_MS});return {cache:n,taskService:new vr({adapter:t,cache:n}),projectService:new Lr({adapter:t,cache:n}),tagService:new $r({adapter:t,cache:n}),folderService:new Mr({adapter:t,cache:n}),attachmentService:new Fr({adapter:t,allowedPaths:e.OMNIFOCUS_ATTACHMENT_PATHS,maxAttachmentMb:e.OMNIFOCUS_MAX_ATTACHMENT_MB}),exportService:new Lt({adapter:t}),forecastService:new Nr({adapter:t}),perspectiveService:new Ur({adapter:t}),pluginService:new Ft({adapter:t}),reviewService:new Jr({adapter:t,cache:n}),searchService:new Br({adapter:t})}}var Oj="jxa",Pj="unknown";function F(t={}){return {correlationId:Ge()??Ag(),durationMs:0,cacheHit:false,transport:Oj,ofVersion:Pj,...t}}function ch(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){C.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){C.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(()=>{});C.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 dh(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:Ge(),durationMs:o,transport:r.meta.transport,cacheHit:r.meta.cacheHit},"tool invoked"),r}catch(r){let o=Math.round(performance.now()-n),a=Sc(r)?r.code:"UNKNOWN";throw C.warn({event:"tool.error",tool:t,correlationId:Ge(),durationMs:o,code:a,err:r},"tool error"),r}}function lh(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 An(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 ph(t,e,n){return e.check(t),n().then(r=>{let o=e.remaining(t);return {...r,meta:{...r.meta,rateLimit:o}}})}function Dj(t,e,n){return (r,o)=>Dg(async()=>(n.shutdown.assertNotShuttingDown(),n.circuitRegistry.get(t).call(async()=>{let i=async()=>(await e(r,o)).structuredContent,s=await ph(t,n.rateLimiter,()=>lh(t,r,n.loopDetector,()=>dh(t,i))),c=u(s);if(n.responseStats!==void 0&&c.structuredContent!==void 0)try{let d=Buffer.byteLength(JSON.stringify(c.structuredContent),"utf-8");n.responseStats.record(t,d);}catch{}return c})))}function uh(t,e){let n=t;if(n.__omnifocusMiddlewareInstalled===true)return;let r=t.registerTool.bind(t),o=(...a)=>{let[i,s,c]=a,d=Dj(i,c,e);return r(i,s,d)};t.registerTool=o,n.__omnifocusMiddlewareInstalled=true;}var Aj=5e3,Rj=1e4,Cj=50,Ic=class{_shuttingDown=false;_queues=[];_readGraceMs;_writeGraceMs;constructor(e={}){this._readGraceMs=e.readGraceMs??Number(process.env.OMNIFOCUS_READ_GRACE_MS??Aj),this._writeGraceMs=e.writeGraceMs??Number(process.env.OMNIFOCUS_WRITE_GRACE_MS??Rj);}get isShuttingDown(){return this._shuttingDown}assertNotShuttingDown(){if(this._shuttingDown)throw new Dn}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,Cj));}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");}},yt=new Ic;var Fj=["@modelcontextprotocol/sdk","node_modules/@modelcontextprotocol"];function Ej(t){return Fj.some(e=>t.includes(e))}var mh=null,fh=false;function gh(){if(fh)return;mh=process.stdout.write,fh=true;let t=mh;process.stdout.write=function(n,r,o){let a=new Error().stack??"";if(Ej(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 Z("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 hh=Ne.version,Uj=Ne.name.split("/").pop()??"omnifocus-mcp",Lj=new Set(["run_jxa_script","run_omnijs_script"]),Jj=Date.now();function Bj(){return new McpServer({name:Uj,version:hh})}async function yh(){gh();let t=jc();C.level=t.OMNIFOCUS_LOG_LEVEL;let e=Bj(),n=new Fn(t.OMNIFOCUS_TOOL_RATE_LIMIT),r=new Rn,o=new Cn({sampleRate:t.OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE,thresholdBytes:t.OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES,logger:C});uh(e,{rateLimiter:n,loopDetector:r,circuitRegistry:gc,shutdown:yt,responseStats:o});let a=new StdioServerTransport,i=sh(t),s=new _n({size:t.OMNIFOCUS_READ_POOL_SIZE,name:"jxa-read"}),c=new Qt({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"jxa-write"}),d=new Qt({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"omnijs"});yt.registerQueue(s),yt.registerQueue(c),yt.registerQueue(d);let l=bc(i,{readPool:s,jxaWriteQueue:c,omniJsQueue:d}),m=ih(l,t);$l(e,{startedAt:Jj,adapter:l,circuitRegistry:gc,makeMeta:F,probeResponseStats:()=>t.OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE>0?o.snapshot():null}),Oc(e);let f=new _r,g={registry:f,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:F};Qm(e,g),tf(e,g),ef(e,g);let I=new jr({registry:f,dispatcher:new Sr});nf(e,{orchestrator:I,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:F}),Ac(e,async()=>{let Re=f.list();return Dc(t,{calendarAccess:await Mn(),webhooks:{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:Re.length,names:Re.map(Hr=>Hr.name)}})}),hd(e,{adapter:l,projectService:m.projectService,reviewService:m.reviewService,forecastService:m.forecastService,perspectiveService:m.perspectiveService});let k={folderService:m.folderService,makeMeta:F};yl(e,k),Il(e,k),bl(e,k),jl(e,k),_l(e,k),Pl(e,k);let w={adapter:l,makeMeta:F};kl(e,w),Tl(e,w),xl(e,w),Dl(e,w);let R={tagService:m.tagService,makeMeta:F};iu(e,R),du(e,R),gu(e,{adapter:l,makeMeta:F}),uu(e,R),mu(e,R),yu(e,R),ku(e,R),Tu(e,R),wu(e,R),bu(e,R),Su(e,R);let T={adapter:l,makeMeta:F};cu(e,T),lu(e,T),vu(e,T),ju(e,T);let D={adapter:l,makeMeta:F,cache:m.cache};El(e,D),Ml(e,D),Nl(e,D),Ul(e,D),Ll(e,D),ou(e,{searchService:m.searchService,makeMeta:F}),Al(e,{forecastService:m.forecastService,makeMeta:F}),Cl(e,{forecastService:m.forecastService,makeMeta:F}),Rl(e,{forecastService:m.forecastService,makeMeta:F}),Fl(e,{forecastService:m.forecastService,cache:m.cache,makeMeta:F});let _={perspectiveService:m.perspectiveService,makeMeta:F};Zl(e,_),Gl(e,_),Kl(e,_),Yl(e,_),zl(e,{perspectiveService:m.perspectiveService,cache:m.cache,makeMeta:F}),Hl(e,{adapter:l,cache:m.cache,makeMeta:F}),ep(e,{adapter:l,cache:m.cache,makeMeta:F}),tp(e,{adapter:l,makeMeta:F}),au(e,{adapter:l,makeMeta:F}),su(e,{adapter:l,makeMeta:F,cache:m.cache}),jd(e,{adapter:l,makeMeta:F,cache:m.cache}),bd(e,{adapter:l,makeMeta:F,cache:m.cache});let L={reviewService:m.reviewService,makeMeta:F};Yp(e,L),Qp(e,L),tu(e,L),nu(e,L),ru(e,L);let x={exportService:m.exportService,adapter:l,makeMeta:F};Od(e,x),fl(e,x),gl(e,x),yd(e,{adapter:l,makeMeta:F});let M={adapter:l,makeMeta:F};rf(e,M),of(e,M),af(e,M),sf(e,M),cf(e,M);let N={projectService:m.projectService,makeMeta:F,cache:m.cache},W={adapter:l,makeMeta:F,cache:m.cache};np(e,W),rp(e,W),op(e,N),gp(e,{...W,replayStore:we}),yp(e,W),vp(e,N),bp(e,{adapter:l,makeMeta:F}),Tp(e,{projectService:m.projectService,makeMeta:F}),Sp(e,{projectService:m.projectService,makeMeta:F}),jp(e,N),Wp(e,W);let V={adapter:l,makeMeta:F,cache:m.cache,templatesFolderName:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME};$p(e,V),Bp(e,V),uf(e,V),Jp(e,V);let re={adapter:l,makeMeta:F};ap(e,re),hp(e,re),kp(e,re),Ip(e,re),_p(e,re),Hp(e,re);let st={taskService:m.taskService,makeMeta:F},oe={adapter:l,makeMeta:F},O={adapter:l,makeMeta:F,cache:m.cache};Tm(e,st),_m(e,st),ym(e,oe),Im(e,oe),Um(e,{searchService:m.searchService,makeMeta:F}),wm(e,oe),Cm(e,{makeMeta:F}),lf(e,{makeMeta:F}),Xp(e,{makeMeta:F,replayStore:we}),xd(e,O),_d(e,O),rm(e,O),Fu(e,O),Em(e,O),_u(e,O),xu(e,O),Ou(e,O),Eu(e,O),Mu(e,O),Nu(e,O),Uu(e,O),Lu(e,O),Ju(e,O),$u(e,O),Wu(e,O),zu(e,{...O,replayStore:we}),im(e,O),dm(e,O),mm(e,{...O,attachmentService:m.attachmentService}),hm(e,O),Ku(e,O);let Ke={adapter:l,makeMeta:F,cache:m.cache,waitingTagName:t.OMNIFOCUS_WAITING_TAG_NAME};qm(e,Ke),Gm(e,Ke),Om(e,O),Mm(e,O),Bm(e,O),$m(e,O),Wm(e,O),Hm(e,O),em(e,O),om(e,O),Vm(e,O);let pe={adapter:l,makeMeta:F};Pu(e,pe),Bu(e,pe),qu(e,pe),tm(e,pe),am(e,pe),cm(e,pe),Pm(e,pe),zm(e,pe),kd(e,{attachmentService:m.attachmentService,makeMeta:F});let it={adapter:l,makeMeta:F},Tc={allowRawScript:t.OMNIFOCUS_ALLOW_RAW_SCRIPT};Vp(e,it,Tc),zp(e,it,Tc),process.on("SIGINT",()=>{yt.initiate("SIGINT");}),process.on("SIGTERM",()=>{yt.initiate("SIGTERM");}),process.on("unhandledRejection",Re=>{C.fatal({event:"server.unhandled_rejection",reason:Re},"unhandled rejection"),C.flush(),process.exit(1);}),process.on("uncaughtException",Re=>{C.fatal({event:"server.uncaught_exception",err:Re},"uncaught exception"),C.flush(),process.exit(1);}),await e.connect(a);let kh=ch({adapter:l,cache:m.cache,server:e,aggregateUris:[cn,dn,ln,pn,un,mn],orchestrator:I}),wc=new br(Re=>{kh(Re).catch(Hr=>{C.error({event:"database.changed.handler_error",err:Hr});});});wc.start(),process.on("exit",()=>wc.stop());let vh=Object.keys(df).filter(Re=>t.OMNIFOCUS_ALLOW_RAW_SCRIPT||!Lj.has(Re)).sort();C.info({event:"server.started",version:hh,config:_c(t),tools:vh,prompts:[zr,qr,Gr,Kr],resources:[En,cn,dn,ln,pn,un,mn,yo,ko,vo]},"server started");}var Wr=process.argv.slice(2);(Wr.includes("--version")||Wr.includes("-v"))&&(process.stdout.write(`${Ne.version}
|
|
9636
|
-
`),process.exit(0));(
|
|
9637
|
-
${
|
|
9777
|
+
`)}function Zj(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 Ct({details:{transport:"omnijs",stderr:Xr(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 Ft({details:{transport:"omnijs",stderr:Xr(t,512),...e!==void 0?{scriptName:e}:{}}}):null}function Xr(t,e){return t.length<=e?t:`${t.slice(0,e)}\u2026`}function C(t){throw new E(`OmniJsTransport.${t} is not wired yet`,{details:{transport:"omnijs",reason:"not-yet-wired",method:t}})}var Yr=class{runOpts;constructor(e={}){this.runOpts={...e.timeoutMs!==void 0?{timeoutMs:e.timeoutMs}:{},...e.spawner!==void 0?{spawner:e.spawner}:{}};}async listTasks(e){return C("listTasks")}async getTask(e){return C("getTask")}async getNoteHtml(e,n){return C("getNoteHtml")}async getTasksMany(e){return C("getTasksMany")}async createTask(e){let n=await Y(fh,{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"});if(re(n))throw n.error.code==="NOT_FOUND"?new _(n.error.message,{details:{transport:"omnijs",scriptName:"task_create"}}):new k(n.error.message,{details:{transport:"omnijs",scriptName:"task_create"}});return y.of(n.task.id)}async updateTask(e,n){return C("updateTask")}async completeTask(e,n){return C("completeTask")}async uncompleteTask(e){return C("uncompleteTask")}async dropTask(e,n){return C("dropTask")}async undropTask(e){return C("undropTask")}async deleteTask(e){return C("deleteTask")}async moveTask(e,n){let o=await Y(hh,{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.error.message,{details:{transport:"omnijs",scriptName:"task_move"}}):new k(o.error.message,{details:{transport:"omnijs",scriptName:"task_move"}})}async convertTaskToProject(e,n){let r=await Y(mh,{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 _(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}}):new k(r.error.message,{details:{transport:"omnijs",scriptName:"task_convert_to_project"}});return v.of(r.projectId)}async batchMoveTasks(e){let r=await Y(ph,{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 k(r.error.message,{details:{transport:"omnijs",scriptName:"task_batch_move"}});return Ne(r,y.of)}async reorderTask(e,n){let r=yh,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 s="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:s};}let a=await Y(r,o,{...this.runOpts,scriptName:"task_reorder"});if(re(a))throw a.error.code==="NOT_FOUND"?new _(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}}):new k(a.error.message,{details:{transport:"omnijs",scriptName:"task_reorder"}})}async duplicateTask(e,n){let r=n.destination===void 0?null:"projectId"in n.destination?{projectId:n.destination.projectId}:"parentId"in n.destination?{parentId:n.destination.parentId}:{toInbox:true},o=await Y(gh,{id:e,recursive:n.recursive,destination:r},{...this.runOpts,scriptName:"task_duplicate"});if(re(o))throw o.error.code==="NOT_FOUND"?new _(o.error.message,{details:{transport:"omnijs",scriptName:"task_duplicate"}}):new k(o.error.message,{details:{transport:"omnijs",scriptName:"task_duplicate"}});return {newId:y.of(o.newId),descendantCount:o.descendantCount}}async batchCreateTasks(e){return C("batchCreateTasks")}async batchUpdateTasks(e){return C("batchUpdateTasks")}async batchCompleteTasks(e){return C("batchCompleteTasks")}async batchUncompleteTasks(e){return C("batchUncompleteTasks")}async batchDeleteTasks(e){return C("batchDeleteTasks")}async batchDropTasks(e){return C("batchDropTasks")}async batchUndropTasks(e){return C("batchUndropTasks")}async listProjects(e){return C("listProjects")}async getProject(e){return C("getProject")}async getProjectsMany(e){return C("getProjectsMany")}async createProject(e){let n=await Y(dh,{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,completionCriterion:e.completionCriterion??null,reviewIntervalDays:e.reviewIntervalDays??null},{...this.runOpts,scriptName:"project_create"});if(re(n))throw n.error.code==="NOT_FOUND"?new _(n.error.message,{details:{transport:"omnijs",scriptName:"project_create"}}):new k(n.error.message,{details:{transport:"omnijs",scriptName:"project_create"}});return v.of(n.project.id)}async updateProject(e,n){return C("updateProject")}async completeProject(e,n){return C("completeProject")}async dropProject(e,n){return C("dropProject")}async batchCompleteProjects(e){return C("batchCompleteProjects")}async batchDropProjects(e){return C("batchDropProjects")}async moveProject(e,n){let r=await Y(lh,{id:e,folderId:n.folderId??null},{...this.runOpts,scriptName:"project_move"});if(re(r))throw r.error.code==="NOT_FOUND"?new _(r.error.message,{details:{transport:"omnijs",scriptName:"project_move"}}):new k(r.error.message,{details:{transport:"omnijs",scriptName:"project_move"}})}async deleteProject(e){return C("deleteProject")}async markProjectReviewed(e){return C("markProjectReviewed")}async listProjectsDueForReview(){return C("listProjectsDueForReview")}async setProjectReviewInterval(e,n){return C("setProjectReviewInterval")}async setProjectNextReviewDate(e,n){return C("setProjectNextReviewDate")}async listTags(e){return C("listTags")}async getTag(e){return C("getTag")}async getTagsMany(e){return C("getTagsMany")}async createTag(e){return C("createTag")}async updateTag(e,n){return C("updateTag")}async deleteTag(e){return C("deleteTag")}async listFolders(e){return C("listFolders")}async getFolder(e){return C("getFolder")}async createFolder(e){return C("createFolder")}async updateFolder(e,n){return C("updateFolder")}async deleteFolder(e){return C("deleteFolder")}async searchTasks(e){return C("searchTasks")}async getForecast(e){return C("getForecast")}async getForecastTag(){let n=await Y(eh,{},{...this.runOpts,scriptName:"forecast_get_tag"});if(re(n))throw new E(n.error.message,{details:{transport:"omnijs",scriptName:"forecast_get_tag"}});return {tagId:n.tagId===null?null:b.of(n.tagId)}}async setForecastTag(e){let r=await Y(th,{tagId:e},{...this.runOpts,scriptName:"forecast_set_tag"});if(re(r))throw r.error.code==="NOT_FOUND"?new _(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}}):new k(r.error.message,{details:{transport:"omnijs",scriptName:"forecast_set_tag"}});return {tagId:r.tagId===null?null:b.of(r.tagId)}}async undoLastMutation(){let e=await Y(Qg,{},{...this.runOpts,scriptName:"database_undo"});if(re(e))throw new E(e.error.message,{details:{transport:"omnijs",scriptName:"database_undo",code:e.error.code}});return {undid:e.undid}}async redoLastMutation(){let e=await Y(Zg,{},{...this.runOpts,scriptName:"database_redo"});if(re(e))throw new E(e.error.message,{details:{transport:"omnijs",scriptName:"database_redo",code:e.error.code}});return {redid:e.redid}}async setTaskAlarms(e,n){let r=await Y(kh,{taskId:String(e),alarms:n},{...this.runOpts,scriptName:"task_set_alarms"});if(re(r))throw r.error.code==="NOT_FOUND"?new _(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms"}}):r.error.code==="VALIDATION"?new k(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms"}}):new E(r.error.message,{details:{transport:"omnijs",scriptName:"task_set_alarms",code:r.error.code}})}async clearTaskAlarms(e){let n=await Y(uh,{taskId:String(e)},{...this.runOpts,scriptName:"task_clear_alarms"});if(re(n))throw n.error.code==="NOT_FOUND"?new _(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms"}}):new E(n.error.message,{details:{transport:"omnijs",scriptName:"task_clear_alarms",code:n.error.code}})}async listAttachments(e){return C("listAttachments")}async addAttachment(e){return C("addAttachment")}async removeAttachment(e){return C("removeAttachment")}async saveAttachmentToPath(e){return C("saveAttachmentToPath")}async appLaunch(){return C("appLaunch")}async getWindowState(){return C("getWindowState")}async setWindowPerspective(e){return C("setWindowPerspective")}async setWindowFocus(e){return C("setWindowFocus")}async appWindowNew(){let e=await Y(Xg,{},{...this.runOpts,scriptName:"app_window_new"});if(re(e))throw new E(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new",code:e.error.code}});return e}async appWindowNewTab(){let e=await Y(Yg,{},{...this.runOpts,scriptName:"app_window_new_tab"});if(re(e))throw e.error.code==="WINDOW_UNAVAILABLE"?new E(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}}):new E(e.error.message,{details:{transport:"omnijs",scriptName:"app_window_new_tab",code:e.error.code}});return e}async pluginInvoke(e){return Y(ch,{identifier:e.identifier,arg:e.arg??null},{...this.runOpts,scriptName:"plugin_invoke"})}async listPerspectives(){return C("listPerspectives")}async evaluatePerspective(e){return C("evaluatePerspective")}async evaluateCustomPerspective(e){let r=await Y(oh,{identifier:e},{...this.runOpts,scriptName:"perspective_evaluate"});if(re(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Xe(r.error.message,{details:{feature:"custom-perspectives"}}):new _(r.error.message,{details:{resource:"perspective",id:e}});return r.tasks.map(o=>({...o,id:y.of(o.id)}))}async evaluatePerspectiveRules(e,n){let r=await Y(ah,{rules:e,...n!==void 0&&{aggregation:n}},{...this.runOpts,scriptName:"perspective_evaluate_dry_run"});if(re(r))throw r.error.code==="FEATURE_REQUIRES_PRO"?new Xe(r.error.message,{details:{feature:"custom-perspectives"}}):new E(r.error.message,{details:{script:"perspective_evaluate_dry_run"}});return r.tasks.map(o=>({...o,id:y.of(o.id)}))}async getCustomPerspective(e){let n=await Y(sh,{identifier:e},{...this.runOpts,scriptName:"perspective_get"});if(re(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Xe(n.error.message,{details:{feature:"custom-perspectives"}}):new _(n.error.message,{details:{resource:"perspective",id:e}});return n.perspective}async deleteCustomPerspective(e){let n=await Y(rh,{identifier:e},{...this.runOpts,scriptName:"perspective_delete"});if(re(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Xe(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="NOT_FOUND"?new _(n.error.message,{details:{resource:"perspective",id:e}}):new E(n.error.message,{details:{transport:"omnijs",script:"perspective_delete",id:e}})}async createCustomPerspective(e){let n=await Y(nh,{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(re(n))throw n.error.code==="FEATURE_REQUIRES_PRO"?new Xe(n.error.message,{details:{feature:"custom-perspectives"}}):n.error.code==="VALIDATION_ERROR"?new k(n.error.message,{details:{name:e.name}}):new E(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 Y(ih,r,{...this.runOpts,scriptName:"perspective_update"});if(re(o))throw o.error.code==="FEATURE_REQUIRES_PRO"?new Xe(o.error.message,{details:{feature:"custom-perspectives"}}):o.error.code==="NOT_FOUND"?new _(o.error.message,{details:{resource:"perspective",id:e}}):o.error.code==="VALIDATION_ERROR"?new k(o.error.message,{details:{id:e}}):new E(o.error.message,{details:{transport:"omnijs",script:"perspective_update",id:e}})}async syncTrigger(){return C("syncTrigger")}async getLastSync(){return C("getLastSync")}async getChangesSince(e){return {taskIds:[],projectIds:[]}}async runOmniJsScript(e,n){return Y(e,n??{},{...this.runOpts,scriptName:"raw"})}};var Zr=class extends EventEmitter{cache;maxBytes;inflight=new Map;hits=0;misses=0;coalesced=0;evictions=0;serviceCounts=new Map;hitRateThreshold;constructor({capacity:e=256,ttlMs:n=3e4,hitRateThreshold:r=.5,maxBytes:o}={}){super(),this.hitRateThreshold=r,this.maxBytes=o!==void 0&&o>0?o:null,this.cache=new LRUCache({max:e,ttl:n,...this.maxBytes!==null?{maxSize:this.maxBytes,sizeCalculation:a=>a.bytes??1}:{},disposeAfter:()=>{this.evictions++;}});}_measureBytes(e){try{return Buffer.byteLength(JSON.stringify(e)??"","utf8")||1}catch{return 1}}async wrap(e,n){let r=e.split(":")[0]??e,o=this.cache.get(e);if(o!==void 0)return this.hits++,this._trackService(r,true),o.v;let a=this.inflight.get(e);if(a!==void 0)return this.coalesced++,await a.promise;this.misses++,this._trackService(r,false);let s=Symbol(e),i=(async()=>{try{let c=await n();return this.inflight.get(e)?.token===s&&this.cache.set(e,this.maxBytes!==null?{v:c,bytes:this._measureBytes(c)}:{v:c}),c}finally{this.inflight.get(e)?.token===s&&this.inflight.delete(e);}})();return this.inflight.set(e,{token:s,promise:i}),await i}invalidate(e){let n=e.endsWith(":*")?e.slice(0,-1):`${e}:`,r=[];for(let i of this.cache.keys())(i.startsWith(n)||i===e)&&r.push(i);for(let i of r)this.cache.delete(i);for(let i of this.inflight.keys())(i.startsWith(n)||i===e)&&this.inflight.delete(i);let o=r.length,a=at(),s={event:"cache.invalidated",scopes:[e],evicted:o,...a!==void 0?{correlationId:a}:{}};o>0&&R.info(s,"cache.invalidated"),this.emit("cache.invalidated",s);}_trackService(e,n){let r=this.serviceCounts.get(e);if(r===void 0&&(r={hits:0,misses:0},this.serviceCounts.set(e,r)),n)r.hits++;else if(r.misses++,this.hitRateThreshold>0){let o=r.hits+r.misses,a=r.hits/o;if(a<this.hitRateThreshold){let s={event:"cache.lowHitRate",service:e,hitRate:a,threshold:this.hitRateThreshold};R.warn(s,"cache.lowHitRate"),this.emit("cache.lowHitRate",s);}}}serviceStats(){let e={};for(let[n,{hits:r,misses:o}]of this.serviceCounts){let a=r+o;e[n]={hits:r,misses:o,hitRate:a>0?r/a:null};}return e}stats(){return {size:this.cache.size,hits:this.hits,misses:this.misses,evictions:this.evictions,coalesced:this.coalesced,bytes:this.maxBytes!==null?this.cache.calculatedSize:null,maxBytes:this.maxBytes}}set(e,n){this.cache.set(e,this.maxBytes!==null?{v:n,bytes:this._measureBytes(n)}:{v:n});}has(e){return this.cache.has(e)}clear(){this.cache.clear(),this.inflight.clear(),this.serviceCounts.clear();}};var n_=["/System/","/private/System/","/Library/","/private/Library/"];async function Uc(t,e){if(/[\x00-\x1f\x7f]/.test(t))throw new k(`Attachment path contains control characters: ${JSON.stringify(t)}`,{suggestion:"Remove null bytes and ASCII control characters from the path; use only printable ASCII or UTF-8.",details:{field:"filePath",value:t}});let n;try{n=await realpath(t);}catch{throw new k(`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 n_)if(r.startsWith(a))throw new k(`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 s=a.endsWith(sep)?a:a+sep;return r.startsWith(s)}))throw new k(`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 vh=1024*1024;async function Ih(t,e){if(e<=0)return;let n;try{n=await stat(t);}catch{throw new k(`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/vh;if(r>e)throw new k(`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*vh}})}function Th(t){return "taskId"in t&&t.taskId!==void 0?{taskId:t.taskId}:{projectId:t.projectId}}function wh(t){return "taskId"in t?"task":"project"}var eo=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 Uc(e.filePath,this.allowedPaths),await Ih(e.filePath,this.maxMb);let n=await this.adapter.addAttachment(e),r=Th(e);return {id:n,ownerKind:wh(r),ownerName:await this.lookupOwnerName(r)}}async remove(e){let n=Th(e),r=await this.lookupOwnerName(n);return await this.adapter.removeAttachment(e),{ownerKind:wh(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 Uc(n,this.allowedPaths),this.adapter.saveAttachmentToPath(e)}};function to(t,e){t!==void 0&&Bd(t,{folderId:e});}var no=class{adapter;cache;constructor({adapter:e,cache:n}){this.adapter=e,this.cache=n;}async list(e={}){let n=e.parentId!==void 0?{parentId:e.parentId}:{};if(this.cache!==void 0){let o=`folder:list:${Me(e)}`,a=this.cache.has(o);return {folders:await this.cache.wrap(o,()=>this.adapter.listFolders(n)),cacheHit:a}}return {folders:await this.adapter.listFolders(n),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 to(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateFolder(e,n),to(this.cache,e);}async delete(e,n=false){n&&await this._cascadeEmpty(e),await this.adapter.deleteFolder(e),to(this.cache,e);}async move(e,n){await this.adapter.updateFolder(e,{parentId:n}),to(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 ro=class{adapter;cache;constructor(e){this.adapter=e.adapter,this.cache=e.cache;}async get(e){if(this.cache!==void 0){let r=`forecast:${Me(e)}`,o=this.cache.has(r);return {...await this.cache.wrap(r,()=>this.adapter.getForecast(e)),cacheHit:o}}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 Lc(t){return _t.includes(t)}var oo=class{adapter;constructor(e){this.adapter=e.adapter;}async list(){return {perspectives:await this.adapter.listPerspectives(),cacheHit:false}}async evaluate(e){return {tasks:Lc(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(Lc(e))throw new k(`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(Lc(e))throw new k(`perspective_delete cannot delete built-in perspectives; got "${e}"`,{details:{field:"perspectiveId",value:e,kind:"builtin"}});await this.adapter.deleteCustomPerspective(e);}};var bh=50,Jc=1e3,ao=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=Me(r),a=e.cursor!==void 0?cn(e.cursor,o):void 0,s=this.listCacheKey(o,e.cursor),i=this.cache.has(s),{projects:c,nextCursor:l}=await this.cache.wrap(s,async()=>this.fetchPage(r,a,n,o));return {projects:c,nextCursor:l,hasMore:l!==null,cacheHit:i}}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 s=await this.adapter.getProject(e.id),i={...s,_links:Ta(s)};if(!n)return {project:i};let l=(await this.adapter.listTasks({projectId:e.id})).map(d=>({...d,_links:ut(d)}));return {project:i,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 s=await this.adapter.listProjects(a),{flagged:i,reviewDueBefore:c}=e,d=[...s.filter(w=>!(i!==void 0&&w.flagged!==i||c!==void 0&&(w.nextReviewDate===null||w.nextReviewDate>=c)))].sort((w,S)=>w.createdAt!==S.createdAt?w.createdAt<S.createdAt?-1:1:w.id<S.id?-1:1),u=n!==void 0?d.filter(w=>dn({id:w.id,sortValue:w.createdAt},n,"asc")):d,f=u.slice(0,r),I=u.length>r?this.encodeNextCursor(f,o):null;return {projects:f.map(w=>({...w,_links:Ta(w)})),nextCursor:I}}resolveLimit(e){if(e.limit===void 0)return bh;if(!Number.isInteger(e.limit)||e.limit<1||e.limit>Jc)throw new k(`limit must be an integer between 1 and ${Jc}; got ${e.limit}.`,{suggestion:`Pass a limit between 1 and ${Jc}, or omit to use the default of ${bh}.`,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 k("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 k("Internal: cannot encode cursor for empty page.");return sn({lastId:r.id,lastSortValue:r.createdAt,filterHash:n})}};var so=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});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&&B(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&&B(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 o_=50,a_=500,io=class{adapter;constructor({adapter:e}){this.adapter=e;}async search(e){let n=Math.min(e.limit??o_,a_),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=Me(r),a=null;e.cursor&&(a=cn(e.cursor,o));let s={...e.q!==void 0?{q:e.q}:{},...e.scope!==void 0?{scope:e.scope}:{},...e.projectId!==void 0?{projectId:e.projectId}:{},...e.tagIds!==void 0?{tagIds:e.tagIds}:{},...e.available!==void 0?{available:e.available}:{},...e.dueBefore!==void 0?{dueBefore:e.dueBefore}:{},...e.dueAfter!==void 0?{dueAfter:e.dueAfter}:{},...e.flagged!==void 0?{flagged:e.flagged}:{},...e.completed!==void 0?{completed:e.completed}:{}},c=[...await this.adapter.searchTasks(s)].sort((h,w)=>{let S=h.createdAt.localeCompare(w.createdAt);return S!==0?S:h.id.localeCompare(w.id)}),d=(a?c.filter(h=>dn({id:h.id,sortValue:h.createdAt},a,"asc")):c).slice(0,n+1),u=d.length>n,f=d.slice(0,n).map(h=>({...h,_links:ut(h)})),g=f.at(-1),I=u&&g?sn({lastId:g.id,lastSortValue:g.createdAt,filterHash:o}):null;return {tasks:f,nextCursor:I,hasMore:u,cacheHit:false}}};function vt(t,e){t!==void 0&&Jd(t,{tagId:e});}var co=class{adapter;cache;constructor({adapter:e,cache:n}){this.adapter=e,this.cache=n;}async list(e={}){let n={...e.parentId!==void 0?{parentId:e.parentId}:{},...e.status!==void 0?{status:e.status}:{}};if(this.cache!==void 0){let o=`tag:list:${Me(e)}`,a=this.cache.has(o);return {tags:await this.cache.wrap(o,()=>this.adapter.listTags(n)),cacheHit:a}}return {tags:await this.adapter.listTags(n),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 vt(this.cache,n),{id:n}}async update(e,n){await this.adapter.updateTag(e,n),vt(this.cache,e);}async delete(e){await this.adapter.deleteTag(e),vt(this.cache,e);}async move(e,n){await this.adapter.updateTag(e,{parentId:n}),vt(this.cache,e);}async setStatus(e,n){await this.adapter.updateTag(e,{status:n}),vt(this.cache,e);}async setAllowsNextAction(e,n){await this.adapter.updateTag(e,{allowsNextAction:n}),vt(this.cache,e);}async setLocation(e,n){await this.adapter.updateTag(e,{location:n}),vt(this.cache,e);}async clearLocation(e){await this.adapter.updateTag(e,{location:null}),vt(this.cache,e);}async getLocation(e){return {location:(await this.adapter.getTag(e)).location,cacheHit:false}}};function Sh(t){if(t.OMNIFOCUS_E2E_USE_MEMORY){let r=new qr;return new fn({jxa:r,omnijs:r})}let e=new Kr({timeoutMs:t.OMNIFOCUS_JXA_TIMEOUT_MS}),n=new Yr({timeoutMs:t.OMNIFOCUS_OMNIJS_TIMEOUT_MS});return fn.fromTransports(e,n)}function jh(t,e){let n=new Zr({capacity:e.OMNIFOCUS_CACHE_CAPACITY,ttlMs:e.OMNIFOCUS_CACHE_TTL_MS,...e.OMNIFOCUS_READ_CACHE_MAX_BYTES>0?{maxBytes:e.OMNIFOCUS_READ_CACHE_MAX_BYTES}:{}});return {cache:n,taskService:new Lr({adapter:t,cache:n}),projectService:new ao({adapter:t,cache:n}),tagService:new co({adapter:t,cache:n}),folderService:new no({adapter:t,cache:n}),attachmentService:new eo({adapter:t,allowedPaths:e.OMNIFOCUS_ATTACHMENT_PATHS,maxAttachmentMb:e.OMNIFOCUS_MAX_ATTACHMENT_MB}),exportService:new Qt({adapter:t}),forecastService:new ro({adapter:t,cache:n}),perspectiveService:new oo({adapter:t}),pluginService:new Gt({adapter:t}),reviewService:new so({adapter:t,cache:n}),searchService:new io({adapter:t})}}var s_="jxa",i_="unknown";function F(t={}){return {correlationId:at()??Kg(),durationMs:0,cacheHit:false,transport:s_,ofVersion:i_,...t}}function _h(t){let{adapter:e,cache:n,server:r,aggregateUris:o,orchestrator:a}=t;return async s=>{let i=new Date(s.detectedAt).getTime()-200,c=new Date(i).toISOString(),l={taskIds:[],projectIds:[]},d=false;try{l=await e.getChangesSince(c),d=!0;}catch(u){R.debug({event:"database.changed.query_failed",err:u});}if(d&&(l.taskIds.length>0||l.projectIds.length>0)){for(let u of l.taskIds)n.invalidate(`task:${u}`);for(let u of l.projectIds)n.invalidate(`project:${u}`);}else n.clear();if(a?.shouldObserve())try{let[u,f]=await Promise.all([e.listTasks({}),e.listProjects()]);await a.observeSnapshot(u,f);}catch(u){R.debug({event:"database.changed.webhook_observe_failed",err:u});}for(let u of l.taskIds)r.server.sendResourceUpdated({uri:`omnifocus://task/${u}`}).catch(()=>{});for(let u of l.projectIds)r.server.sendResourceUpdated({uri:`omnifocus://project/${u}`}).catch(()=>{});for(let u of o)r.server.sendResourceUpdated({uri:u}).catch(()=>{});R.debug({event:"database.changed",source:s.source,detectedAt:s.detectedAt,changedTasks:l.taskIds.length,changedProjects:l.projectIds.length,cacheStrategy:d?"targeted":"full-clear"});}}async function Oh(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:at(),durationMs:o,transport:r.meta.transport,cacheHit:r.meta.cacheHit},"tool invoked"),r}catch(r){let o=Math.round(performance.now()-n),a=zc(r)?r.code:"UNKNOWN";throw R.warn({event:"tool.error",tool:t,correlationId:at(),durationMs:o,code:a,err:r},"tool error"),r}}function xh(t,e,n,r){let o=n.record(t,e);if(o!==void 0){let{level:a,warning:s}=o,i=s.details?.count??0,c=(s.details?.windowSeconds??60)*1e3;if(R.warn({event:"loop.detected",tool:t,callCount:i,windowMs:c,level:a}),a==="error")return Promise.reject(new Gn(t,i,s.details?.windowSeconds??60))}return r().then(a=>{if(o===void 0)return a;let s=a.meta.warnings??[];return {...a,meta:{...a.meta,warnings:[...s,o.warning]}}})}function Ah(t,e,n){return e.check(t),n().then(r=>{let o=e.remaining(t);return {...r,meta:{...r.meta,rateLimit:o}}})}function c_(t,e,n){return (r,o)=>Gg(async()=>(n.shutdown.assertNotShuttingDown(),n.circuitRegistry.get(t).call(async()=>{let s=async()=>(await e(r,o)).structuredContent,i=await Ah(t,n.rateLimiter,()=>xh(t,r,n.loopDetector,()=>Oh(t,s))),c=m(i);if(n.responseStats!==void 0)try{let l=Buffer.byteLength(JSON.stringify(c),"utf-8");n.responseStats.record(t,l);}catch{}return c})))}function Ph(t,e){let n=t;if(n.__omnifocusMiddlewareInstalled===true)return;let r=t.registerTool.bind(t),o=(...a)=>{let[s,i,c]=a,l=c_(s,c,e);return r(s,i,l)};t.registerTool=o,n.__omnifocusMiddlewareInstalled=true;}var d_=5e3,l_=1e4,p_=50,Bc=class{_shuttingDown=false;_queues=[];_readGraceMs;_writeGraceMs;constructor(e={}){this._readGraceMs=e.readGraceMs??Number(process.env.OMNIFOCUS_READ_GRACE_MS??d_),this._writeGraceMs=e.writeGraceMs??Number(process.env.OMNIFOCUS_WRITE_GRACE_MS??l_);}get isShuttingDown(){return this._shuttingDown}assertNotShuttingDown(){if(this._shuttingDown)throw new qn}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,p_));}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");}},Rt=new Bc;var u_=["@modelcontextprotocol/sdk","node_modules/@modelcontextprotocol"];function m_(t){return u_.some(e=>t.includes(e))}var Dh=null,Rh=false;function Ch(){if(Rh)return;Dh=process.stdout.write,Rh=true;let t=Dh;process.stdout.write=function(n,r,o){let a=new Error().stack??"";if(m_(a))return typeof r=="function"?t.call(process.stdout,n,r):t.call(process.stdout,n,r,o);let s=typeof n=="string"?n.slice(0,120):`<${n.byteLength} bytes>`;throw new oe("OF_STRAY_STDOUT",`Stray stdout write detected \u2014 stdout is reserved for MCP transport. Use process.stderr / logger for diagnostics. Attempted: ${JSON.stringify(s)}`,{suggestion:"Replace console.log / process.stdout.write with process.stderr.write or logger."})};}var Fh=Ke.version,h_=Ke.name.split("/").pop()??"omnifocus-mcp",y_=new Set(["run_jxa_script","run_omnijs_script"]),k_=Date.now();function v_(){return new McpServer({name:h_,version:Fh})}async function Eh(){Ch();let t=Vc();R.level=t.OMNIFOCUS_LOG_LEVEL,mo({enabled:t.OMNIFOCUS_TRANSIENT_RETRY_ENABLED,delayMs:t.OMNIFOCUS_TRANSIENT_RETRY_DELAY_MS});let e=v_(),n=new Yn(t.OMNIFOCUS_TOOL_RATE_LIMIT),r=new Kn({maxKeys:t.OMNIFOCUS_LOOP_DETECTOR_MAX_KEYS}),o=new Xn({sampleRate:t.OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE,thresholdBytes:t.OMNIFOCUS_RESPONSE_STATS_THRESHOLD_BYTES,logger:R});Ph(e,{rateLimiter:n,loopDetector:r,circuitRegistry:Mc,shutdown:Rt,responseStats:o});let a=new StdioServerTransport,s=Sh(t),i=new Wn({size:t.OMNIFOCUS_READ_POOL_SIZE,name:"jxa-read"}),c=new hn({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"jxa-write"}),l=new hn({cap:t.OMNIFOCUS_WRITE_QUEUE_CAP,name:"omnijs"});Rt.registerQueue(i),Rt.registerQueue(c),Rt.registerQueue(l);let d=Hc(s,{readPool:i,jxaWriteQueue:c,omniJsQueue:l}),u=jh(d,t);up(e,{startedAt:k_,adapter:d,circuitRegistry:Mc,makeMeta:F,probeResponseStats:()=>t.OMNIFOCUS_RESPONSE_STATS_SAMPLE_RATE>0?o.snapshot():null,probeCache:()=>({...u.cache.stats(),services:u.cache.serviceStats()}),probeStores:()=>({idempotencyEntries:le.size,loopDetectorKeys:r.size})}),Kc(e);let f=new Vr,g={registry:f,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:F};yf(e,g),vf(e,g),kf(e,g);let I=new zr({registry:f,dispatcher:new Hr});If(e,{orchestrator:I,enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,makeMeta:F}),Zc(e,async()=>{let Ue=f.list();return Yc(t,{calendarAccess:await Qn(),webhooks:{enabled:t.OMNIFOCUS_WEBHOOKS_ENABLED,count:Ue.length,names:Ue.map(po=>po.name)}})}),Nd(e,{adapter:d,projectService:u.projectService,reviewService:u.reviewService,forecastService:u.forecastService,perspectiveService:u.perspectiveService});let h={folderService:u.folderService,makeMeta:F};Ul(e,h),Bl(e,h),Hl(e,h),Vl(e,h),ql(e,h),Xl(e,h);let w={adapter:d,makeMeta:F};Ll(e,w),$l(e,w),Gl(e,w),Yl(e,w);let S={tagService:u.tagService,makeMeta:F};Au(e,S),Du(e,S),Eu(e,{adapter:d,makeMeta:F}),Cu(e,S),Fu(e,S),Mu(e,S),Nu(e,S),Ju(e,S),Bu(e,S),$u(e,S),Wu(e,S);let T={adapter:d,makeMeta:F};Pu(e,T),Ru(e,T),Uu(e,T),Hu(e,T);let x={adapter:d,makeMeta:F,cache:u.cache};ap(e,x),sp(e,x),ip(e,x),cp(e,x),dp(e,x),_u(e,{searchService:u.searchService,makeMeta:F}),tp(e,{forecastService:u.forecastService,makeMeta:F}),rp(e,{forecastService:u.forecastService,makeMeta:F}),np(e,{forecastService:u.forecastService,makeMeta:F}),op(e,{forecastService:u.forecastService,cache:u.cache,makeMeta:F});let O={perspectiveService:u.perspectiveService,makeMeta:F};Tp(e,O),yp(e,O),kp(e,O),Ip(e,O),hp(e,{perspectiveService:u.perspectiveService,cache:u.cache,makeMeta:F}),fp(e,{adapter:d,cache:u.cache,makeMeta:F}),bp(e,{adapter:d,cache:u.cache,makeMeta:F}),Sp(e,{adapter:d,makeMeta:F}),Ou(e,{adapter:d,makeMeta:F}),xu(e,{adapter:d,makeMeta:F,cache:u.cache}),Vd(e,{adapter:d,makeMeta:F,cache:u.cache}),Hd(e,{adapter:d,makeMeta:F,cache:u.cache});let L={reviewService:u.reviewService,makeMeta:F};vu(e,L),Tu(e,L),bu(e,L),Su(e,L),ju(e,L);let A={exportService:u.exportService,adapter:d,makeMeta:F};Kd(e,A),El(e,A),Ml(e,A),Ud(e,{adapter:d,makeMeta:F});let M={adapter:d,makeMeta:F};Tf(e,M),wf(e,M),bf(e,M),Sf(e,M),jf(e,M);let N={projectService:u.projectService,makeMeta:F,cache:u.cache},W={adapter:d,makeMeta:F,cache:u.cache};jp(e,W),_p(e,W),Op(e,N),Up(e,{...W,replayStore:Ae}),Jp(e,W),$p(e,N),zp(e,{adapter:d,makeMeta:F}),Hp(e,{projectService:u.projectService,makeMeta:F}),Vp(e,{projectService:u.projectService,makeMeta:F}),qp(e,N),pu(e,W);let V={adapter:d,makeMeta:F,cache:u.cache,templatesFolderName:t.OMNIFOCUS_TEMPLATES_FOLDER_NAME};lu(e,V),du(e,V),Af(e,V),cu(e,V);let ce={adapter:d,makeMeta:F};xp(e,ce),Lp(e,ce),Bp(e,ce),Wp(e,ce),Gp(e,ce),uu(e,ce);let It={taskService:u.taskService,makeMeta:F},de={adapter:d,makeMeta:F},P={adapter:d,makeMeta:F,cache:u.cache};Jm(e,It),Hm(e,It),Mm(e,de),Lm(e,de),nf(e,{searchService:u.searchService,makeMeta:F}),Bm(e,de),Ym(e,{makeMeta:F}),Of(e,{makeMeta:F}),ku(e,{makeMeta:F,replayStore:Ae}),Gd(e,P),qd(e,P),wm(e,P),Qu(e,P),Qm(e,P),zu(e,P),Vu(e,P),qu(e,P),em(e,P),tm(e,P),nm(e,P),rm(e,P),om(e,P),am(e,P),im(e,P),cm(e,P),pm(e,{...P,replayStore:Ae}),_m(e,P),xm(e,P),Rm(e,{...P,attachmentService:u.attachmentService}),Em(e,P),fm(e,P);let st={adapter:d,makeMeta:F,cache:u.cache,waitingTagName:t.OMNIFOCUS_WAITING_TAG_NAME};pf(e,st),uf(e,st),Vm(e,P),ef(e,P),of(e,P),af(e,P),sf(e,P),cf(e,P),vm(e,P),bm(e,P),df(e,P);let ve={adapter:d,makeMeta:F};Gu(e,ve),sm(e,ve),um(e,ve),Im(e,ve),Sm(e,ve),Om(e,ve),qm(e,ve),lf(e,ve),Ld(e,{attachmentService:u.attachmentService,makeMeta:F});let Tt={adapter:d,makeMeta:F},$c={allowRawScript:t.OMNIFOCUS_ALLOW_RAW_SCRIPT};mu(e,Tt,$c),fu(e,Tt,$c),process.on("SIGINT",()=>{Rt.initiate("SIGINT");}),process.on("SIGTERM",()=>{Rt.initiate("SIGTERM");}),process.on("unhandledRejection",Ue=>{R.fatal({event:"server.unhandled_rejection",reason:Ue},"unhandled rejection"),R.flush(),process.exit(1);}),process.on("uncaughtException",Ue=>{R.fatal({event:"server.uncaught_exception",err:Ue},"uncaught exception"),R.flush(),process.exit(1);}),await e.connect(a);let Mh=_h({adapter:d,cache:u.cache,server:e,aggregateUris:[Sn,jn,_n,On,xn,An],orchestrator:I}),Wc=new Wr(Ue=>{Mh(Ue).catch(po=>{R.error({event:"database.changed.handler_error",err:po});});});Wc.start(),process.on("exit",()=>Wc.stop());let Nh=Object.keys(_f).filter(Ue=>t.OMNIFOCUS_ALLOW_RAW_SCRIPT||!y_.has(Ue)).sort();R.info({event:"server.started",version:Fh,config:qc(t),tools:Nh,prompts:[go,ho,yo,ko],resources:[Zn,Sn,jn,_n,On,xn,An,Lo,Jo,Bo]},"server started");}var lo=process.argv.slice(2);(lo.includes("--version")||lo.includes("-v"))&&(process.stdout.write(`${Ke.version}
|
|
9778
|
+
`),process.exit(0));(lo.includes("--help")||lo.includes("-h"))&&(process.stdout.write(`${Ke.name} v${Ke.version}
|
|
9779
|
+
${Ke.description}
|
|
9638
9780
|
|
|
9639
9781
|
Usage: omnifocus-mcp
|
|
9640
9782
|
|
|
9641
9783
|
Speaks Model Context Protocol over stdio. Add to your MCP client's
|
|
9642
9784
|
configuration (Claude Desktop, Claude Code, etc.) \u2014 see the README
|
|
9643
|
-
for client-specific setup: ${
|
|
9785
|
+
for client-specific setup: ${Ke.homepage}
|
|
9644
9786
|
|
|
9645
9787
|
Options:
|
|
9646
9788
|
-v, --version Print version and exit
|
|
@@ -9651,5 +9793,5 @@ Environment:
|
|
|
9651
9793
|
OMNIFOCUS_ALLOW_RAW_SCRIPT Enable raw-script tools (off by default)
|
|
9652
9794
|
OMNIFOCUS_JXA_TIMEOUT_MS Per-call JXA timeout (default: 30000)
|
|
9653
9795
|
OMNIFOCUS_OMNIJS_TIMEOUT_MS Per-call OmniJS timeout (default: 45000)
|
|
9654
|
-
`),process.exit(0));
|
|
9796
|
+
`),process.exit(0));Eh().catch(t=>{process.stderr.write(`[omnifocus-mcp] Fatal startup error: ${String(t)}
|
|
9655
9797
|
`),process.exit(1);});
|