Plug-In: Step-List Generator

REQUIRES: macOS 26, iOS 26, iPados 26, visionOS 26 • OmniOutliner 6.0+

This plug-in uses the on-device Apple Foundation Models (AFM) frameworks to generate an outline in OmniOutliner based upon the steps required to perform the prompted task.

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

AI • Step-List Generator
  

/*{ "type": "action", "targets": ["omnioutliner"], "author": "Otto Automator", "identifier": "com.omni-automation.oo.step-list-generator", "version": "1.0", "description": "This plug-in will display a text input, into which you enter a prompt for the Apple Foundation Model requesting the steps and sub-steps for performing a specific task. A resulting outline is appended to the existing document.", "label": "AI • Step List Generator", "shortLabel": "AI Step List", "paletteLabel": "AI Step List", "image": "apple.intelligence" }*/ (() => { var preferences = new Preferences() // NO ID = PLUG-IN ID const addResponseItemToParent = (responseItem, parent) => { newRow = parent.addChild( null, item => { item.topic = responseItem["step-title"] item.note = responseItem["step-description"] } ); return newRow }; const insertResponseItems = (responseItems, parent) => { responseItems.forEach(responseItem => { newRow = addResponseItemToParent(responseItem, parent) if (responseItem['sub-steps']){ insertResponseItems(responseItem['sub-steps'], newRow); } }); }; const action = new PlugIn.Action(async function(selection, sender){ try { storedPrompt = preferences.readString("storedPrompt") if(!storedPrompt){ storedPrompt = "Provide the steps for XXXXX" } shouldUsePasteboard = preferences.readBoolean("shouldUsePasteboard") if(!shouldUsePasteboard instanceof Boolean){ shouldUsePasteboard = false } inputForm = new Form() textField = new Form.Field.String( "textInputValue", null, storedPrompt ) checkSwitchField = new Form.Field.Checkbox( "shouldUsePasteboard", "Use clipboard text for prompt", shouldUsePasteboard ) inputForm.addField(textField) inputForm.addField(checkSwitchField) inputForm.validate = function(formObject){ shouldUsePasteboard = formObject.values["shouldUsePasteboard"] if(shouldUsePasteboard){ if(!Pasteboard.general.hasStrings){throw "No text on clipboard"} return true } textValue = formObject.values["textInputValue"] textStatus = (textValue && textValue.length > 0) ? true:false return textStatus } formPrompt = "Enter prompt:" formObject = await inputForm.show(formPrompt, "Continue") shouldUsePasteboard = formObject.values["shouldUsePasteboard"] console.log("PROMPT:", prompt) if(shouldUsePasteboard){ var prompt = Pasteboard.general.string preferences.write("shouldUsePasteboard", true) } else { var prompt = formObject.values["textInputValue"] preferences.write("shouldUsePasteboard", false) } preferences.write("storedPrompt", prompt) console.log("PROMPT:", prompt) const schema = LanguageModel.Schema.fromJSON({ arrayOf: { name: "step-schema", properties: [ { name: "step-title", isOptional: false }, { name: "step-description", isOptional: false }, { name: "sub-steps", description: "A breakdown of steps.", isOptional: true, schema: { arrayOf: { referenceTo: "step-schema" }, minimumElements: 1 } } ] } }) session = new LanguageModel.Session() console.log("Begin query…") responseStr = await session.respondWithSchema(prompt, schema) console.log("Processing response…") responseObj = JSON.parse(responseStr) console.log(JSON.stringify(responseObj, null, 2)) insertResponseItems(responseObj, rootItem) document.editors[0].rootNode.children.forEach(node => { node.expand(true) node.expandNote(true) }) } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) new Alert(err.name, err.message).show() } } }); action.validate = function(selection, sender){ return (Device.current.operatingSystemVersion.atLeast(new Version("26"))) }; return action; })();