Skip to main content
shortcuts-authoring Author, sign, and package macOS Shortcuts as binary plists for distribution as Claude Code plugins. Teaches agents to build production-quality .shortcut files using the full Shortcuts action library.
インストールへ移動 Skills Marketplace コミュニティが作成したAIスキルを発見・探索
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/earchibald/tinycuts --skill shortcuts-authoringコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Zipをダウンロード ダウンロード中... name shortcuts-authoring description Author, sign, and package macOS Shortcuts as binary plists for distribution as Claude Code plugins. Teaches agents to build production-quality .shortcut files using the full Shortcuts action library. tags ["macos","shortcuts","automation","apple-intelligence"]
Shortcuts Authoring
Author macOS Shortcuts as binary property lists (.shortcut files), sign them for distribution, and package them as Claude Code plugins. This skill enables a marketplace of macOS automation capabilities delivered as Shortcuts — bridging macOS's walled garden for agentic use without MCP servers.
Use this skill when building a shortcut plugin that automates macOS: app control, file operations, clipboard/notifications, scripting (JXA), or Apple Intelligence. The skill provides a Python builder template, a complete action catalog, and a plugin packaging template.
Ref: Shortcuts User Guide , Run shortcuts from the command line
Quick Start
Copy the builder template:
cp resources/builder_template.py my_shortcut.py
Fill in the actions list using the Action Catalog below. Each action is a Python dict with WFWorkflowActionIdentifier and WFWorkflowActionParameters.
Build the shortcut:
This writes unsigned.shortcut and validates it with plutil -lint.
Sign for distribution:
shortcuts sign -m anyone -i unsigned.shortcut -o signed.shortcut
Requires Apple ID and network for iCloud notarization.
Import into Shortcuts.app:
open signed.shortcut
Confirm in the import dialog.
Test from CLI:
shortcuts run "My Shortcut"
echo "input text" | shortcuts run "My Shortcut"
Package as a plugin using resources/plugin_template/ — see Plugin Packaging section.
Ref: See resources/action_examples/ for complete, runnable examples.
Format Reference
Binary Plist Structure .shortcut files are binary property lists (bplist). The format is not officially documented by Apple — all knowledge comes from community reverse-engineering.
Top-Level Schema Key Type Description WFWorkflowActionsArray The action sequence (core of the shortcut) WFWorkflowClientVersionString Client version string (e.g., "1700" for macOS 26). Must be a string — WorkflowKit's migration code calls string methods on this value. WFWorkflowClientReleaseString Semantic version string (e.g., "26.0") WFWorkflowMinimumClientVersionInteger Minimum version required to run WFWorkflowMinimumClientVersionStringString Minimum version as string (e.g., "1700") WFWorkflowIconDict WFWorkflowIconStartColor (int) + WFWorkflowIconGlyphNumber (int)WFWorkflowInputContentItemClassesArray Accepted input types (e.g., WFStringContentItem, WFURLContentItem) WFWorkflowTypesArray Where it appears: MenuBar, QuickActions, ActionExtension, etc. WFWorkflowImportQuestionsArray Prompts shown during import (usually []) WFWorkflowHasShortcutInputVariablesBool Whether the shortcut accepts input variables
The builder template's make_shortcut() sets all these with sensible defaults.
Action Structure Each entry in WFWorkflowActions is a dict with two keys:
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.notification" ,
"WFWorkflowActionParameters" : {
"WFNotificationActionBody" : "Hello!" ,
"WFNotificationActionTitle" : "Alert" ,
},
}
WFWorkflowActionIdentifier — reverse-domain string (e.g., is.workflow.actions.* for built-in actions)
WFWorkflowActionParameters — dict of action-specific configuration
Actions that produce output include a "UUID" key in their parameters. Downstream actions reference it via OutputUUID.
Variable System
Single Variable Reference (WFTextTokenAttachment) When a parameter is purely a variable reference (no surrounding text):
make_attachment(output_uuid, "Clipboard" )
{
"Value" : {
"OutputName" : "Clipboard" ,
"OutputUUID" : "09B5964F-CBB7-429E-B6D3-3BE92C08E3FF" ,
"Type" : "ActionOutput" ,
},
"WFSerializationType" : "WFTextTokenAttachment" ,
}
The Type field can be: ActionOutput (references another action's UUID), Variable (named/magic variable), ExtensionInput, Ask, or CurrentDate.
Variable Embedded in Text (WFTextTokenString) When a variable is interpolated within a string, the string contains U+FFFC (Object Replacement Character) as a placeholder:
make_text_with_variable("Summary: " , summary_uuid, "Summarize Text" )
{
"Value" : {
"string" : "Summary: \ufffc" ,
"attachmentsByRange" : {
"{9, 1}" : {
"OutputName" : "Summarize Text" ,
"OutputUUID" : "..." ,
"Type" : "ActionOutput" ,
}
},
},
"WFSerializationType" : "WFTextTokenString" ,
}
The {position, 1} key uses NSString character indices (UTF-16 code units). len() works for ASCII/BMP text. For emoji/non-BMP: position = len(text.encode('utf-16-le')) // 2.
Magic Variables Built-in variables like Repeat Item use Type: Variable with VariableName:
make_magic_variable("Repeat Item" )
{
"Value" : {
"Type" : "Variable" ,
"VariableName" : "Repeat Item" ,
},
"WFSerializationType" : "WFTextTokenAttachment" ,
}
Ref: Variable structures extracted from Apple gallery shortcuts at WorkflowKit.framework/Resources/Gallery.bundle/ (MorningReport.wflow, ActionItems.wflow, DocumentReview.wflow). See also 0xdevalias decompilation notes .
Control Flow Control flow actions (If, Repeat, Menu) use a shared GroupingIdentifier UUID and WFControlFlowMode integer:
Mode Meaning 0Start of block (If / Repeat / Choose from Menu) 1Middle of block (Otherwise / menu case) 2End of block (End If / End Repeat / End Menu)
All actions in a block share the same GroupingIdentifier. The end action also has a UUID key for output referencing.
Helpers: make_if_block(), make_repeat_count(), make_repeat_each(), make_menu() — all handle GroupingIdentifier and mode values automatically.
Signing Exported .shortcut files must be cryptographically signed before import (since macOS 12):
shortcuts sign -m anyone -i unsigned.shortcut -o signed.shortcut
anyone — notarized via iCloud, suitable for public sharing
people-who-know-me — local signing, contacts-only
Import via open signed.shortcut (triggers Shortcuts.app import dialog — user must confirm).
Action Catalog
Tier 1: App Automation
Open App Identifier: is.workflow.actions.openapp
Key Type Required Description WFAppIdentifierString No Bundle ID of the app to open (e.g., "com.apple.Safari") WFSelectedAppDict No Dict with BundleIdentifier and Name keys
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.openapp" ,
"WFWorkflowActionParameters" : {
"WFAppIdentifier" : "com.apple.Safari" ,
"WFSelectedApp" : {
"BundleIdentifier" : "com.apple.Safari" ,
"Name" : "Safari" ,
},
},
}
Variable wiring example: Open App does not produce output; no chaining needed.
Note on app-specific actions: Mail, Calendar, Reminders, Notes, Finder, Safari, and Maps have their own action identifiers that follow the pattern is.workflow.actions.<verb><appname> (e.g., is.workflow.actions.mail.send). Exact identifiers for app-specific actions should be extracted at runtime from the Shortcuts.app action library or from Apple gallery shortcuts.
Tier 2: File System
Get File Identifier: is.workflow.actions.documentpicker.open
Key Type Required Description WFFileVariable ref No Variable reference to the file to open WFGetFilePathString No Path to the file to open UUIDString Yes UUID for referencing this action's output
get_file_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.documentpicker.open" ,
"WFWorkflowActionParameters" : {
"WFGetFilePath" : "/Users/me/Documents/notes.txt" ,
"UUID" : get_file_uuid,
},
}
get_file_uuid = new_uuid()
notify_uuid = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.documentpicker.open" ,
"WFWorkflowActionParameters" : {
"WFGetFilePath" : "/Users/me/Documents/notes.txt" ,
"UUID" : get_file_uuid,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.notification" ,
"WFWorkflowActionParameters" : {
"WFNotificationActionBody" : make_attachment(get_file_uuid, "Get File" ),
"WFNotificationActionTitle" : "File Contents" ,
},
},
]
Save File Identifier: is.workflow.actions.documentpicker.save
Key Type Required Description WFInputVariable ref Yes Variable reference to the content to save WFFileDestinationPathString/Variable No Destination path for the file
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.documentpicker.save" ,
"WFWorkflowActionParameters" : {
"WFInput" : make_attachment(source_uuid, "Previous Action" ),
"WFFileDestinationPath" : "/Users/me/Documents/output.txt" ,
},
}
clipboard_uuid = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getclipboard" ,
"WFWorkflowActionParameters" : {"UUID" : clipboard_uuid},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.documentpicker.save" ,
"WFWorkflowActionParameters" : {
"WFInput" : make_attachment(clipboard_uuid, "Clipboard" ),
"WFFileDestinationPath" : "/Users/me/Desktop/clipboard.txt" ,
},
},
]
Get Folder Contents Identifier: is.workflow.actions.file.getfoldercontents
Key Type Required Description WFFolderVariable ref Yes Variable reference to the folder UUIDString Yes UUID for referencing this action's output
folder_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.file.getfoldercontents" ,
"WFWorkflowActionParameters" : {
"WFFolder" : make_attachment(source_uuid, "Folder" ),
"UUID" : folder_uuid,
},
}
Rename File Identifier: is.workflow.actions.file.rename
Key Type Required Description WFFileVariable ref Yes Variable reference to the file to rename WFNewFilenameString Yes New filename (without path)
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.file.rename" ,
"WFWorkflowActionParameters" : {
"WFFile" : make_attachment(file_uuid, "Get File" ),
"WFNewFilename" : "renamed_file.txt" ,
},
}
Tier 3: System Services
Get Clipboard Identifier: is.workflow.actions.getclipboard
Key Type Required Description UUIDString Yes UUID for referencing this action's output
clipboard_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getclipboard" ,
"WFWorkflowActionParameters" : {"UUID" : clipboard_uuid},
}
Set Clipboard Identifier: is.workflow.actions.setclipboard
Key Type Required Description WFInputVariable ref Yes Variable reference to the content to copy to clipboard
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.setclipboard" ,
"WFWorkflowActionParameters" : {
"WFInput" : make_attachment(source_uuid, "Previous Action" ),
},
}
Take Screenshot Identifier: is.workflow.actions.takescreenshot
Key Type Required Description WFScreenshotTypeString No "Full Screen", "Window", or "Interactive" (default: "Full Screen")UUIDString Yes UUID for referencing this action's output
screenshot_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.takescreenshot" ,
"WFWorkflowActionParameters" : {
"WFScreenshotType" : "Full Screen" ,
"UUID" : screenshot_uuid,
},
}
Show Notification Identifier: is.workflow.actions.notification
Key Type Required Description WFNotificationActionBodyString/Variable Yes Body text of the notification WFNotificationActionTitleString No Title of the notification WFNotificationActionSoundBool No Whether to play a sound (default: True)
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.notification" ,
"WFWorkflowActionParameters" : {
"WFNotificationActionBody" : make_attachment(source_uuid, "Previous Action" ),
"WFNotificationActionTitle" : "Alert" ,
"WFNotificationActionSound" : True ,
},
}
Common pattern: Get Clipboard → Summarize → Notify
clipboard_uuid = new_uuid()
summary_uuid = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getclipboard" ,
"WFWorkflowActionParameters" : {"UUID" : clipboard_uuid},
},
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.SummarizeTextIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(clipboard_uuid, "Clipboard" ),
"summaryType" : "summarize" ,
"UUID" : summary_uuid,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.notification" ,
"WFWorkflowActionParameters" : {
"WFNotificationActionBody" : make_attachment(summary_uuid, "Summarize Text" ),
"WFNotificationActionTitle" : "Clipboard Summary" ,
},
},
]
Tier 4: Scripting
Run JavaScript for Automation (JXA) Identifier: is.workflow.actions.runjavascript
Key Type Required Description WFJavaScriptString Yes JavaScript for Automation (JXA) source code to run UUIDString Yes UUID for referencing this action's output
jxa_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.runjavascript" ,
"WFWorkflowActionParameters" : {
"WFJavaScript" : "Application('Finder').home().name()" ,
"UUID" : jxa_uuid,
},
}
jxa_uuid = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.runjavascript" ,
"WFWorkflowActionParameters" : {
"WFJavaScript" : "Application('System Events').frontmost().name()" ,
"UUID" : jxa_uuid,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.notification" ,
"WFWorkflowActionParameters" : {
"WFNotificationActionBody" : make_attachment(jxa_uuid, "Run JavaScript for Automation" ),
"WFNotificationActionTitle" : "Front App" ,
},
},
]
URL Identifier: is.workflow.actions.url
Key Type Required Description WFURLActionURLString/Variable Yes The URL string or variable UUIDString Yes UUID for referencing this action's output
url_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.url" ,
"WFWorkflowActionParameters" : {
"WFURLActionURL" : "https://example.com" ,
"UUID" : url_uuid,
},
}
Open URL Identifier: is.workflow.actions.openurl
Key Type Required Description WFInputVariable ref Yes Variable reference to the URL to open
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.openurl" ,
"WFWorkflowActionParameters" : {
"WFInput" : make_attachment(url_uuid, "URL" ),
},
}
url_uuid = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.url" ,
"WFWorkflowActionParameters" : {
"WFURLActionURL" : "https://apple.com" ,
"UUID" : url_uuid,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.openurl" ,
"WFWorkflowActionParameters" : {
"WFInput" : make_attachment(url_uuid, "URL" ),
},
},
]
Tier 5: Apple Intelligence
Requires: macOS 15.1+ (Sequoia) with Apple Intelligence enabled. All identifiers below are verified from the macOS 26 system.
Use Model Identifier: is.workflow.actions.askllm
Requires: macOS 15.1+, Apple Intelligence
com.apple.Shortcuts.AskAFMAction3B — on-device model
com.apple.Shortcuts.AskMontaraAction — server/PCC model
Key Type Required Description WFLLMPromptText/Variable Yes The prompt to send to the model WFLLMModelString No Model to use (default: "Apple Intelligence") WFGenerativeResultTypeString No Output format: "Text", "List", "Dictionary", "Automatic" FollowUpBool No Whether this is a follow-up in a conversation UUIDString Yes UUID for referencing this action's output
llm_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.askllm" ,
"WFWorkflowActionParameters" : {
"WFLLMPrompt" : make_attachment(input_uuid, "Input" ),
"WFLLMModel" : "Apple Intelligence" ,
"WFGenerativeResultType" : "Text" ,
"FollowUp" : False ,
"UUID" : llm_uuid,
},
}
clipboard_uuid = new_uuid()
llm_uuid = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getclipboard" ,
"WFWorkflowActionParameters" : {"UUID" : clipboard_uuid},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.askllm" ,
"WFWorkflowActionParameters" : {
"WFLLMPrompt" : make_text_with_variable("Improve this text: " , clipboard_uuid, "Clipboard" ),
"WFGenerativeResultType" : "Text" ,
"UUID" : llm_uuid,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.notification" ,
"WFWorkflowActionParameters" : {
"WFNotificationActionBody" : make_attachment(llm_uuid, "Use Model" ),
"WFNotificationActionTitle" : "Improved Text" ,
},
},
]
Summarize Text Identifier: com.apple.WritingTools.WritingToolsAppIntentsExtension.SummarizeTextIntent
Requires: macOS 15.1+, Apple Intelligence
Key Type Required Description textVariable Yes Variable reference to the text to summarize summaryTypeString No "summarize" (default) or "createKeyPoints"UUIDString Yes UUID for referencing this action's output
summary_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.SummarizeTextIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(input_uuid, "Input" ),
"summaryType" : "summarize" ,
"UUID" : summary_uuid,
},
}
Rewrite Text Identifier: com.apple.WritingTools.WritingToolsAppIntentsExtension.RewriteTextIntent
Requires: macOS 15.1+, Apple Intelligence
Key Type Required Description textVariable Yes Variable reference to the text to rewrite UUIDString Yes UUID for referencing this action's output
rewrite_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.RewriteTextIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(input_uuid, "Input" ),
"UUID" : rewrite_uuid,
},
}
Proofread Text Identifier: com.apple.WritingTools.WritingToolsAppIntentsExtension.ProofreadIntent
Requires: macOS 15.1+, Apple Intelligence
Key Type Required Description textVariable Yes Variable reference to the text to proofread UUIDString Yes UUID for referencing this action's output
proofread_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.ProofreadIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(input_uuid, "Input" ),
"UUID" : proofread_uuid,
},
}
Adjust Tone Identifier: com.apple.WritingTools.WritingToolsAppIntentsExtension.AdjustToneIntent
Requires: macOS 15.1+, Apple Intelligence
Key Type Required Description textVariable Yes Variable reference to the text toneString Yes "friendly", "professional", or "concise"UUIDString Yes UUID for referencing this action's output
tone_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.AdjustToneIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(input_uuid, "Input" ),
"tone" : "professional" ,
"UUID" : tone_uuid,
},
}
Make List Identifier: com.apple.WritingTools.WritingToolsAppIntentsExtension.FormatListIntent
Requires: macOS 15.1+, Apple Intelligence
Key Type Required Description textVariable Yes Variable reference to the text to format as a list UUIDString Yes UUID for referencing this action's output
list_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.FormatListIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(input_uuid, "Input" ),
"UUID" : list_uuid,
},
}
Make Table Identifier: com.apple.WritingTools.WritingToolsAppIntentsExtension.FormatTableIntent
Requires: macOS 15.1+, Apple Intelligence
Key Type Required Description textVariable Yes Variable reference to the text to format as a table UUIDString Yes UUID for referencing this action's output
table_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.FormatTableIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(input_uuid, "Input" ),
"UUID" : table_uuid,
},
}
Note on Image Playground: The Image Playground action identifier is TBD — it needs extraction from macOS 26 with Image Playground enabled.
Tier 6: Control Flow
Note: Prefer using the builder template helpers (make_if_block, make_repeat_count, make_repeat_each, make_menu) over manual construction of control flow actions. The helpers handle GroupingIdentifier and WFControlFlowMode values automatically and are less error-prone.
Comment Identifier: is.workflow.actions.comment
Key Type Required Description WFCommentActionTextString Yes Comment text (displayed in Shortcuts editor, not executed)
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.comment" ,
"WFWorkflowActionParameters" : {
"WFCommentActionText" : "This section handles the file processing" ,
},
}
If / Otherwise / End If Identifier: is.workflow.actions.conditional
Key Type Required Description WFConditionInt/String Yes Condition type (e.g., 100 for "is", 101 for "is not") WFConditionalActionStringString No Value to compare against GroupingIdentifierString Yes Shared UUID linking If/Otherwise/End If WFControlFlowModeInt Yes 0 = If, 1 = Otherwise, 2 = End IfWFInputVariable ref No The variable to evaluate
group_id = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.conditional" ,
"WFWorkflowActionParameters" : {
"WFCondition" : 100 ,
"WFConditionalActionString" : "hello" ,
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 0 ,
"WFInput" : make_attachment(input_uuid, "Input" ),
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.conditional" ,
"WFWorkflowActionParameters" : {
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 1 ,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.conditional" ,
"WFWorkflowActionParameters" : {
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 2 ,
},
},
]
Repeat (Count) Identifier: is.workflow.actions.repeat.count
Key Type Required Description WFRepeatCountInt Yes Number of times to repeat GroupingIdentifierString Yes Shared UUID linking Start/End Repeat WFControlFlowModeInt Yes 0 = Start Repeat, 2 = End Repeat
group_id = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.repeat.count" ,
"WFWorkflowActionParameters" : {
"WFRepeatCount" : 3 ,
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 0 ,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.repeat.count" ,
"WFWorkflowActionParameters" : {
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 2 ,
},
},
]
Repeat with Each Identifier: is.workflow.actions.repeat.each
Key Type Required Description WFInputVariable Yes Variable reference to the list to iterate over GroupingIdentifierString Yes Shared UUID linking Start/End Repeat WFControlFlowModeInt Yes 0 = Start Repeat, 2 = End Repeat
group_id = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.repeat.each" ,
"WFWorkflowActionParameters" : {
"WFInput" : make_attachment(list_uuid, "List" ),
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 0 ,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.repeat.each" ,
"WFWorkflowActionParameters" : {
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 2 ,
},
},
]
Choose from Menu Identifier: is.workflow.actions.choosefrommenu
Key Type Required Description WFMenuPromptString No Prompt shown to the user WFMenuItemsArray Yes Array of menu item dicts with WFItemType and WFMenuItemTitle WFMenuItemTitleString Yes (in cases) Title of this menu item branch GroupingIdentifierString Yes Shared UUID linking all menu actions WFControlFlowModeInt Yes 0 = Menu start, 1 = menu case, 2 = End Menu
group_id = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.choosefrommenu" ,
"WFWorkflowActionParameters" : {
"WFMenuPrompt" : "Choose an option" ,
"WFMenuItems" : [
{"WFItemType" : 0 , "WFMenuItemTitle" : "Option A" },
{"WFItemType" : 0 , "WFMenuItemTitle" : "Option B" },
],
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 0 ,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.choosefrommenu" ,
"WFWorkflowActionParameters" : {
"WFMenuItemTitle" : "Option A" ,
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 1 ,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.choosefrommenu" ,
"WFWorkflowActionParameters" : {
"WFMenuItemTitle" : "Option B" ,
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 1 ,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.choosefrommenu" ,
"WFWorkflowActionParameters" : {
"GroupingIdentifier" : group_id,
"WFControlFlowMode" : 2 ,
},
},
]
Set Variable Identifier: is.workflow.actions.setvariable
Key Type Required Description WFVariableNameString Yes Name to assign to the variable WFInputVariable Yes Variable reference to store
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.setvariable" ,
"WFWorkflowActionParameters" : {
"WFVariableName" : "MyResult" ,
"WFInput" : make_attachment(source_uuid, "Previous Action" ),
},
}
Get Variable Identifier: is.workflow.actions.getvariable
Key Type Required Description WFVariableVariable ref Yes Reference to the named variable to retrieve UUIDString Yes UUID for referencing this action's output
get_var_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getvariable" ,
"WFWorkflowActionParameters" : {
"WFVariable" : {
"Value" : {"Type" : "Variable" , "VariableName" : "MyResult" },
"WFSerializationType" : "WFTextTokenAttachment" ,
},
"UUID" : get_var_uuid,
},
}
Dictionary Identifier: is.workflow.actions.dictionary
Key Type Required Description WFItemsDict Yes The dictionary data structure UUIDString Yes UUID for referencing this action's output
dict_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.dictionary" ,
"WFWorkflowActionParameters" : {
"WFItems" : {
"Value" : {
"WFDictionaryFieldValueItems" : [
{
"WFItemType" : 0 ,
"WFKey" : {"Value" : {"string" : "name" }, "WFSerializationType" : "WFTextTokenString" },
"WFValue" : {"Value" : {"string" : "Alice" }, "WFSerializationType" : "WFTextTokenString" },
}
]
},
"WFSerializationType" : "WFDictionaryFieldValue" ,
},
"UUID" : dict_uuid,
},
}
Get Dictionary Value Identifier: is.workflow.actions.getvalueforkey
Key Type Required Description WFDictionaryKeyString Yes Key to retrieve from the dictionary WFInputVariable Yes Variable reference to the dictionary UUIDString Yes UUID for referencing this action's output
value_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getvalueforkey" ,
"WFWorkflowActionParameters" : {
"WFDictionaryKey" : "name" ,
"WFInput" : make_attachment(dict_uuid, "Dictionary" ),
"UUID" : value_uuid,
},
}
List Identifier: is.workflow.actions.list
Key Type Required Description WFItemsArray Yes Array of items (strings, variables, or mixed) UUIDString Yes UUID for referencing this action's output
list_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.list" ,
"WFWorkflowActionParameters" : {
"WFItems" : [
{"WFItemType" : 0 , "WFValue" : {"Value" : {"string" : "apple" }, "WFSerializationType" : "WFTextTokenString" }},
{"WFItemType" : 0 , "WFValue" : {"Value" : {"string" : "banana" }, "WFSerializationType" : "WFTextTokenString" }},
],
"UUID" : list_uuid,
},
}
Get Item from List Identifier: is.workflow.actions.getitemfromlist
Key Type Required Description WFItemSpecifierString Yes "First Item", "Last Item", "Random Item", or "Item At Index"WFItemIndexInt No Zero-based index (only when WFItemSpecifier is "Item At Index") WFInputVariable Yes Variable reference to the list UUIDString Yes UUID for referencing this action's output
item_uuid = new_uuid()
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getitemfromlist" ,
"WFWorkflowActionParameters" : {
"WFItemSpecifier" : "First Item" ,
"WFInput" : make_attachment(list_uuid, "List" ),
"UUID" : item_uuid,
},
}
Output Identifier: is.workflow.actions.output
Key Type Required Description WFOutputVariable ref Yes Variable reference to the value to output
Note: This action provides stdout output when running via shortcuts run from the command line. Use it as the final action in shortcuts intended for CLI use.
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.output" ,
"WFWorkflowActionParameters" : {
"WFOutput" : make_attachment(result_uuid, "Previous Action" ),
},
}
clipboard_uuid = new_uuid()
summary_uuid = new_uuid()
actions = [
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.getclipboard" ,
"WFWorkflowActionParameters" : {"UUID" : clipboard_uuid},
},
{
"WFWorkflowActionIdentifier" :
"com.apple.WritingTools.WritingToolsAppIntentsExtension.SummarizeTextIntent" ,
"WFWorkflowActionParameters" : {
"text" : make_attachment(clipboard_uuid, "Clipboard" ),
"summaryType" : "summarize" ,
"UUID" : summary_uuid,
},
},
{
"WFWorkflowActionIdentifier" : "is.workflow.actions.output" ,
"WFWorkflowActionParameters" : {
"WFOutput" : make_attachment(summary_uuid, "Summarize Text" ),
},
},
]
Build & Sign Workflow Follow these steps for every shortcut:
Define the shortcut's purpose and I/O contract
Copy resources/builder_template.py
Assemble actions from the catalog into the actions list
Run the script to produce unsigned.shortcut
Validate with plutil -lint unsigned.shortcut
Debug with plutil -convert xml1 -o debug.xml unsigned.shortcut (if needed)
Sign with shortcuts sign -m anyone -i unsigned.shortcut -o signed.shortcut
Import with open signed.shortcut (user confirms import dialog)
Test with shortcuts run <name> --input-path - --output-path -
Iterate on steps 3–9 until correct
Copy resources/plugin_template/, fill in SKILL.md with I/O contract
Publish
Debugging & Troubleshooting
Validation Steps
plutil -lint unsigned.shortcut — catches malformed plists
plutil -convert xml1 -o debug.xml unsigned.shortcut — human-readable XML
If shortcuts sign fails — check stderr for cause
open signed.shortcut — test import dialog appears
echo "test" | shortcuts run "Name" --input-path - --output-path - 2>&1 — test execution
Common Failure Modes Symptom Cause Fix shortcuts sign exits non-zeroMalformed plist Run plutil -lint and fix structure Import dialog shows but shortcut is empty WFWorkflowActions is empty/malformedInspect XML, check action dicts Shortcut runs but produces no output Missing UUID on output-producing actions Add UUID key Variable shows as blank OutputUUID doesn't match producing action's UUIDVerify UUID strings match exactly Control flow doesn't group correctly Mismatched GroupingIdentifier All actions in a block must share the same GID Apple Intelligence action fails AI not enabled or wrong identifier Check System Settings and identifier shortcuts sign prints ObjC warnings/crash trace to stderrmacOS 26 beta runtime warnings Check exit code, not stderr — signing succeeds if exit code is 0
Limitations
No automated import (confirmation dialog required)
No programmatic uninstall
Signing requires network for -m anyone
Plugin Packaging
Directory Structure Published plugin directories follow this layout:
my-shortcut-plugin/
├── SKILL.md # I/O contract, usage docs
├── shortcuts/
│ └── my-shortcut.shortcut # Signed .shortcut file
├── scripts/
│ ├── install.sh # Imports via `open`
│ └── uninstall.sh # Best-effort removal guidance
└── src/
└── build.py # Builder script (for transparency)
See resources/plugin_template/ for skeleton templates.
I/O Contract Schema Each plugin's SKILL.md must include a contract block:
shortcut:
name: "My Shortcut"
input: stdin/text
output: stdout/text
requires:
macos: "26"
apple-intelligence: true
permissions:
- clipboard-read
- notifications
Allowed input Values Value Meaning CLI invocation stdin/textAccepts text via stdin echo "..." | shortcuts run "Name" --input-path -stdin/fileAccepts file data via stdin cat file.pdf | shortcuts run "Name" --input-path -file-pathAccepts a file path as text input echo "/path/to/file" | shortcuts run "Name" --input-path -clipboardReads from system clipboard shortcuts run "Name"noneNo input required shortcuts run "Name"
Allowed output Values Value Meaning CLI invocation stdout/textOutputs text to stdout shortcuts run "Name" --output-path -stdout/fileOutputs file data to stdout shortcuts run "Name" --output-path - --output-type public.pngfile-pathWrites to a file, outputs path shortcuts run "Name" --output-path -clipboardSets system clipboard shortcuts run "Name"notificationShows a notification shortcuts run "Name"noneNo output (side-effect only) shortcuts run "Name"
Allowed permissions Values clipboard-read, clipboard-write, notifications, screenshots, file-read, file-write, calendar-read, calendar-write, reminders-read, reminders-write, mail-send, location, network
Commenting Requirements Shortcuts must be thoroughly commented . Users can inspect shortcuts when choosing whether to install them.
Header comment — First action: what it does, I/O, requirements
Section comments — Before each logical group
Control flow comments — Explain branching logic
Variable comments — Explain non-obvious variables
Integration comments — Note permissions for app-specific/AI actions
Use the make_comment() helper (defined in resources/builder_template.py) to insert comment actions.
External References
Apple Official
Community Format Documentation
Tools & Projects (Reference Only)
Apple Intelligence Coverage