Plug-In: Help Me Estimate

REQUIRES: macOS 26, iOS 26, iPados 26, visionOS 26 • OmniFocus 4.8

This plug-in uses the on-device Apple Foundation Models (AFM) frameworks to assist in the estimation of time required to complete the selected task.

NOTE: Both the task title and its note text serve as the prompt for the AFM.

Related Links: AFM and Omni Automation documentationAFM Plug-In Collection

Help Me Estimate
  

/*{ "author": "Ken Case", "targets": ["omnifocus"], "type": "action", "identifier": "com.omnigroup.kcase.help-me-estimate", "version": "1.1", "description": "A plug-in that helps me estimate a task.", "label": "Help Me Estimate", "mediumLabel": "Help Me Estimate", "longLabel": "Help Me Estimate", "paletteLabel": "Help Me Estimate", "image": "apple.intelligence" }*/ (() => { const schema = LanguageModel.Schema.fromJSON({ name: "task-schema", properties: [ {name: "estimatedMinutes", type: "integer", isOptional: false}, ] }); const session = new LanguageModel.Session(); const propertiesForTask = (task, depth, properties) => { let result = {}; for (const property of properties) { const value = task[property]; if (value != null) { result[property] = value; } } if (depth > 0) { const children = task.children; if (children && children.length > 0) { result.children = children.map((child) => propertiesForTask(child, depth - 1, properties)); } } return result; } const helpMeEstimateTask = async (task, selection) => { const temporaryItem = addResponseItemToTask({"name": "Thinking…", "note": ""}, task); console.log("Processing Task:", task.name); console.log("Schema:", schema); console.log("Session:", session); const taskProperties = propertiesForTask(task, 9, ["name", "note", "estimatedMinutes"]); console.log("Task properties:", taskProperties); const prompt = "You're a GTD expert helping someone estimate the duration of a task or group of tasks in OmniFocus. Provide a time estimate in minutes for the following JSON task: " + JSON.stringify(taskProperties); const responseJSON = await session.respondWithSchema(prompt, schema); console.log(responseJSON); selection.database.deleteObject(temporaryItem); const response = JSON.parse(responseJSON); console.log(response); task.estimatedMinutes = Number(response.estimatedMinutes); } const addResponseItemToTask = (responseItem, task) => { let responseTask = new Task(responseItem.name, task); responseTask.note = responseItem.note; return responseTask } const saveEdits = (selection) => { const previousSelection = selection.databaseObjects; selection.window.selectObjects([]); selection.window.selectObjects(previousSelection); } const action = new PlugIn.Action(async (selection) => { try { console.log("Help me estimate…"); saveEdits(selection); const projects = selection.projects; for (const project of projects) { await helpMeEstimateTask(project.task, selection); } const tasks = selection.tasks; for (const task of tasks) { await helpMeEstimateTask(task, selection); } } catch (err) { console.error(err.name, err.message) new Alert(err.name, err.message).show() } }); return action; })();