shortcuts-playground 1.2.1

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.
Files changed (68) hide show
  1. package/README.md +54 -0
  2. package/agents/shortcut-builder.md +172 -0
  3. package/agents/shortcut-remixer.md +239 -0
  4. package/commands/build-shortcut.md +32 -0
  5. package/commands/remix-shortcut.md +34 -0
  6. package/opencode.json +30 -0
  7. package/package.json +47 -0
  8. package/plugin/validator.ts +179 -0
  9. package/skills/shortcuts-playground/ACTIONS.md +713 -0
  10. package/skills/shortcuts-playground/APPINTENTS.md +2760 -0
  11. package/skills/shortcuts-playground/AUTOMATION_TRIGGERS.md +133 -0
  12. package/skills/shortcuts-playground/BEST_PRACTICES.md +357 -0
  13. package/skills/shortcuts-playground/CHANGELOG.md +585 -0
  14. package/skills/shortcuts-playground/CONTROL_FLOW.md +777 -0
  15. package/skills/shortcuts-playground/DATE_TIME.md +75 -0
  16. package/skills/shortcuts-playground/EXAMPLES.md +738 -0
  17. package/skills/shortcuts-playground/FILTERS.md +697 -0
  18. package/skills/shortcuts-playground/HEALTHKIT.md +317 -0
  19. package/skills/shortcuts-playground/ICONS_AND_COLORS.md +89 -0
  20. package/skills/shortcuts-playground/JAVASCRIPT_WEBPAGE.md +51 -0
  21. package/skills/shortcuts-playground/PARAMETER_TYPES.md +1117 -0
  22. package/skills/shortcuts-playground/PLIST_FORMAT.md +296 -0
  23. package/skills/shortcuts-playground/README.md +77 -0
  24. package/skills/shortcuts-playground/SKILL.md +459 -0
  25. package/skills/shortcuts-playground/THIRD_PARTY_ACTIONS.md +77 -0
  26. package/skills/shortcuts-playground/TOOLKIT_SNAPSHOT.md +62 -0
  27. package/skills/shortcuts-playground/URL_SCHEMES.md +59 -0
  28. package/skills/shortcuts-playground/VARIABLES.md +569 -0
  29. package/skills/shortcuts-playground/assets/shortcuts-small.svg +14 -0
  30. package/skills/shortcuts-playground/assets/shortcuts.png +0 -0
  31. package/skills/shortcuts-playground/data/healthkit-ios26.2-reference.json +2603 -0
  32. package/skills/shortcuts-playground/data/macos27-shortpy-grounding.json +4085 -0
  33. package/skills/shortcuts-playground/data/macos27-workflow-trigger-samples.json +1 -0
  34. package/skills/shortcuts-playground/data/shortcuts-glyph-synonyms.json +5102 -0
  35. package/skills/shortcuts-playground/data/shortcuts-icon-colors.json +107 -0
  36. package/skills/shortcuts-playground/data/shortcuts-official-glyph-mapping.json +509 -0
  37. package/skills/shortcuts-playground/data/toolkit-v63-tool-ids.json +1806 -0
  38. package/skills/shortcuts-playground/data/toolkit-v78-first-party-enum-cases.json +1 -0
  39. package/skills/shortcuts-playground/data/toolkit-v78-first-party-parameter-keys.json +1 -0
  40. package/skills/shortcuts-playground/data/toolkit-v78-ios27-tool-ids.json +1222 -0
  41. package/skills/shortcuts-playground/data/toolkit-v78-tool-ids.json +2745 -0
  42. package/skills/shortcuts-playground/data/toolkit-v78-trigger-parameter-keys.json +1051 -0
  43. package/skills/shortcuts-playground/golden-shortcuts/index.jsonl +19 -0
  44. package/skills/shortcuts-playground/golden-shortcuts/xml/1be4dde95b794253bf82438e201b33e7.xml +174 -0
  45. package/skills/shortcuts-playground/golden-shortcuts/xml/2e0fb675e45948aaacee7e534f910492.xml +1016 -0
  46. package/skills/shortcuts-playground/golden-shortcuts/xml/332c12a0060043b388b22b806be7ab58.xml +1924 -0
  47. package/skills/shortcuts-playground/golden-shortcuts/xml/3dd4ee24e43f464f92adaa70a0311eaa.xml +1177 -0
  48. package/skills/shortcuts-playground/golden-shortcuts/xml/4d102301c6e646faa7a8a221f4f4ec98.xml +321 -0
  49. package/skills/shortcuts-playground/golden-shortcuts/xml/51cc4e26d1044893a0c3f2f3630cf2d2.xml +1933 -0
  50. package/skills/shortcuts-playground/golden-shortcuts/xml/623e7f1ca5f948e2bd53811fec63e544.xml +136 -0
  51. package/skills/shortcuts-playground/golden-shortcuts/xml/6a18b76843ac45c384ac3400f3740997.xml +1791 -0
  52. package/skills/shortcuts-playground/golden-shortcuts/xml/71f0cacb0f604b399b76c5dcb7286e7c.xml +2228 -0
  53. package/skills/shortcuts-playground/golden-shortcuts/xml/807525ed9f974829bc8494defac923a8.xml +259 -0
  54. package/skills/shortcuts-playground/golden-shortcuts/xml/8ab0d39826ab4249be58672763caa3ba.xml +420 -0
  55. package/skills/shortcuts-playground/golden-shortcuts/xml/91c45fd3fd3b427897d9ba485efb1227.xml +800 -0
  56. package/skills/shortcuts-playground/golden-shortcuts/xml/97be626bb25c41709d175646a7f6d8f2.xml +424 -0
  57. package/skills/shortcuts-playground/golden-shortcuts/xml/ae59e10d409348f9bd33894f03f9beb4.xml +568 -0
  58. package/skills/shortcuts-playground/golden-shortcuts/xml/afa83b6be811483b9c32189c41eb9312.xml +391 -0
  59. package/skills/shortcuts-playground/golden-shortcuts/xml/cfdb083b857e4ac189629fa386d27cdc.xml +193 -0
  60. package/skills/shortcuts-playground/golden-shortcuts/xml/e01cebe192d64b2fbca80204d03d92ab.xml +111 -0
  61. package/skills/shortcuts-playground/golden-shortcuts/xml/ef669bcf8bad489d9ef4b88bfaf5772f.xml +780 -0
  62. package/skills/shortcuts-playground/golden-shortcuts/xml/f44f5caf5e3e48d4817e73af450c4404.xml +570 -0
  63. package/skills/shortcuts-playground/scripts/generate_healthkit_reference.py +394 -0
  64. package/skills/shortcuts-playground/scripts/lookup_action_grounding.py +1164 -0
  65. package/skills/shortcuts-playground/scripts/select_shortcut_icon_color.py +597 -0
  66. package/skills/shortcuts-playground/scripts/test_random_mixed_shortcuts.py +1067 -0
  67. package/skills/shortcuts-playground/scripts/test_wiring_regressions.py +2247 -0
  68. package/skills/shortcuts-playground/scripts/validate_shortcut.py +4526 -0
@@ -0,0 +1,459 @@
1
+ ---
2
+ name: shortcuts-playground
3
+ description: Build, validate, sign, archive, and remix macOS/iOS Shortcuts in OpenCode by creating plist files. Use when asked to create, modify, or remix shortcuts; automate workflows; build .shortcut files; or generate Shortcuts plists. Covers WF actions, AppIntents, third-party actions, HealthKit, variable references, and control flow using bundled target-gated ToolKit snapshots.
4
+ ---
5
+
6
+ # Shortcuts Playground
7
+
8
+ Generate valid `.shortcut` files that can be signed and imported into Apple's Shortcuts app. This OpenCode plugin supports two workflows: building new shortcuts from scratch and remixing existing unsigned XML shortcuts with a natural-language diff.
9
+
10
+ ## OpenCode Compatibility
11
+
12
+ OpenCode plugins do not expose Claude Code's PATH wrapper commands from the Claude package. In OpenCode, use this skill directly and call the bundled scripts by path, or use the `/build-shortcut` and `/remix-shortcut` commands which delegate to the specialized subagents.
13
+
14
+ The OpenCode package also bundles a `tool.execute.after` auto-validation hook (PostToolUse-equivalent) via `plugin/validator.ts`. It validates changed `.xml`/`.shortcut` files that contain `WFWorkflowActions` after every `write`/`edit` tool call.
15
+
16
+ Resolve the skill directory as the folder containing this `SKILL.md`. In examples below, `SKILL_DIR` means that directory.
17
+
18
+ | Task | OpenCode command pattern |
19
+ |------|-----------------------|
20
+ | Resolve icon/color | `python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "$USER_PROMPT"` |
21
+ | Validate XML | `python3 "$SKILL_DIR/scripts/validate_shortcut.py" /path/to/Shortcut.xml` |
22
+ | Archive + sign | `"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/Shortcut.xml --name "Shortcut Name"` |
23
+
24
+ `sign_shortcut.sh` defaults to `SHORTCUTS_PLAYGROUND_OUTPUT_DIR` or `~/Documents/Shortcuts Playground`, and `SHORTCUTS_PLAYGROUND_SIGNING_MODE` or `anyone`. It requires macOS and Apple's `shortcuts` CLI. The validator only requires Python 3.10+.
25
+
26
+ **Mandatory**: Follow the guidelines in [BEST_PRACTICES.md](BEST_PRACTICES.md) for every shortcut.
27
+ If guidance here conflicts with [BEST_PRACTICES.md](BEST_PRACTICES.md), follow `BEST_PRACTICES.md`.
28
+
29
+ **Definition of done**: a new build is not complete when XML validation passes. It is complete only after `scripts/sign_shortcut.sh` archives the unsigned XML, writes the signed `.shortcut`, and you verify the signed file exists with non-zero size.
30
+
31
+ **Pipeline-first rule**: write the smallest complete shortcut that implements the request, validate it, sign it, and verify the signed file before spending turns on cosmetic polish. Comments only need to be concise and repair-oriented. A valid XML draft without a signed `.shortcut` is not a useful stopping point.
32
+
33
+ ## Recommended Reading Order
34
+
35
+ 1. [BEST_PRACTICES.md](BEST_PRACTICES.md) for mandatory rules and validation expectations
36
+ 2. [PLIST_FORMAT.md](PLIST_FORMAT.md) for plist structure and serialization details
37
+ 3. [ACTIONS.md](ACTIONS.md), [APPINTENTS.md](APPINTENTS.md), [AUTOMATION_TRIGGERS.md](AUTOMATION_TRIGGERS.md), and [THIRD_PARTY_ACTIONS.md](THIRD_PARTY_ACTIONS.md) for action IDs, AppIntent parameters, and OS 27 trigger metadata
38
+ 4. [HEALTHKIT.md](HEALTHKIT.md) when building or remixing Health actions
39
+ 5. [VARIABLES.md](VARIABLES.md), [CONTROL_FLOW.md](CONTROL_FLOW.md), and [FILTERS.md](FILTERS.md) for wiring patterns
40
+ 6. [ICONS_AND_COLORS.md](ICONS_AND_COLORS.md), [PARAMETER_TYPES.md](PARAMETER_TYPES.md), and [EXAMPLES.md](EXAMPLES.md) for implementation details
41
+
42
+ ## Quick Start
43
+
44
+ A shortcut is an XML plist that gets signed into a binary package. Generate the XML form:
45
+
46
+ ```xml
47
+ <?xml version="1.0" encoding="UTF-8"?>
48
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
49
+ <plist version="1.0">
50
+ <dict>
51
+ <key>WFWorkflowActions</key>
52
+ <array>
53
+ <!-- Actions go here -->
54
+ </array>
55
+ <key>WFWorkflowClientVersion</key>
56
+ <string>2700.0.4</string>
57
+ <key>WFWorkflowHasOutputFallback</key>
58
+ <false/>
59
+ <key>WFWorkflowIcon</key>
60
+ <dict>
61
+ <key>WFWorkflowIconGlyphNumber</key>
62
+ <integer>61440</integer>
63
+ <key>WFWorkflowIconStartColor</key>
64
+ <integer>431817727</integer>
65
+ </dict>
66
+ <key>WFWorkflowImportQuestions</key>
67
+ <array/>
68
+ <key>WFWorkflowInputContentItemClasses</key>
69
+ <array/>
70
+ <key>WFWorkflowMinimumClientVersion</key>
71
+ <integer>900</integer>
72
+ <key>WFWorkflowMinimumClientVersionString</key>
73
+ <string>900</string>
74
+ <key>WFWorkflowName</key>
75
+ <string>My Shortcut</string>
76
+ <key>WFWorkflowOutputContentItemClasses</key>
77
+ <array/>
78
+ <key>WFWorkflowTypes</key>
79
+ <array/>
80
+ </dict>
81
+ </plist>
82
+ ```
83
+
84
+ ### Minimal Hello World
85
+
86
+ ```xml
87
+ <dict>
88
+ <key>WFWorkflowActionIdentifier</key>
89
+ <string>is.workflow.actions.gettext</string>
90
+ <key>WFWorkflowActionParameters</key>
91
+ <dict>
92
+ <key>UUID</key>
93
+ <string>A1B2C3D4-E5F6-7890-ABCD-EF1234567890</string>
94
+ <key>WFTextActionText</key>
95
+ <string>Hello World!</string>
96
+ </dict>
97
+ </dict>
98
+ <dict>
99
+ <key>WFWorkflowActionIdentifier</key>
100
+ <string>is.workflow.actions.showresult</string>
101
+ <key>WFWorkflowActionParameters</key>
102
+ <dict>
103
+ <key>Text</key>
104
+ <dict>
105
+ <key>Value</key>
106
+ <dict>
107
+ <key>attachmentsByRange</key>
108
+ <dict>
109
+ <key>{0, 1}</key>
110
+ <dict>
111
+ <key>OutputName</key>
112
+ <string>Text</string>
113
+ <key>OutputUUID</key>
114
+ <string>A1B2C3D4-E5F6-7890-ABCD-EF1234567890</string>
115
+ <key>Type</key>
116
+ <string>ActionOutput</string>
117
+ </dict>
118
+ </dict>
119
+ <key>string</key>
120
+ <string></string>
121
+ </dict>
122
+ <key>WFSerializationType</key>
123
+ <string>WFTextTokenString</string>
124
+ </dict>
125
+ </dict>
126
+ </dict>
127
+ ```
128
+
129
+ ## Core Concepts
130
+
131
+ ### 1. Actions
132
+ Every action has:
133
+ - **Identifier**: `is.workflow.actions.<name>` (e.g., `is.workflow.actions.showresult`)
134
+ - **Parameters**: Action-specific configuration in `WFWorkflowActionParameters`
135
+ - **UUID**: Unique identifier for referencing this action's output
136
+
137
+ ### 2. Variable References
138
+ To use output from a previous action:
139
+ 1. The source action needs a `UUID` parameter
140
+ 2. Reference it using `OutputUUID` in an `attachmentsByRange` dictionary
141
+ 3. Use `` (U+FFFC) as placeholder in the string where the variable goes
142
+ 4. Set `WFSerializationType` to `WFTextTokenString`
143
+
144
+ ### 3. Control Flow
145
+ Control flow actions (repeat, conditional, menu) use:
146
+ - `GroupingIdentifier`: UUID linking start/middle/end actions
147
+ - `WFControlFlowMode`: 0=start, 1=middle (else/case), 2=end
148
+
149
+ ## Common Actions Quick Reference
150
+
151
+ | Action | Identifier | Key Parameters |
152
+ |--------|------------|----------------|
153
+ | Text | `is.workflow.actions.gettext` | `WFTextActionText` |
154
+ | Show Result | `is.workflow.actions.showresult` | `Text` |
155
+ | Ask for Input | `is.workflow.actions.ask` | `WFAskActionPrompt`, `WFInputType` |
156
+ | Use AI Model | `is.workflow.actions.askllm` | `WFLLMPrompt`, `WFLLMModel`, `WFGenerativeResultType`, `WFAllowWebSearch`, `FollowUp` (OS 27+) |
157
+ | Comment | `is.workflow.actions.comment` | `WFCommentActionText` |
158
+ | URL | `is.workflow.actions.url` | `WFURLActionURL` |
159
+ | Get Contents of URL | `is.workflow.actions.downloadurl` | `WFURL`, `WFHTTPMethod` |
160
+ | Get Weather | `is.workflow.actions.weather.currentconditions` | (none required) |
161
+ | Open App | `is.workflow.actions.openapp` | `WFAppIdentifier` |
162
+ | Open URL | `is.workflow.actions.openurl` | `WFInput` |
163
+ | Alert | `is.workflow.actions.alert` | `WFAlertActionTitle`, `WFAlertActionMessage` |
164
+ | Notification | `is.workflow.actions.notification` | `WFNotificationActionTitle`, `WFNotificationActionBody` |
165
+ | Set Variable | `is.workflow.actions.setvariable` | `WFVariableName`, `WFInput` |
166
+ | Get Variable | `is.workflow.actions.getvariable` | `WFVariable` |
167
+ | Number | `is.workflow.actions.number` | `WFNumberActionNumber` |
168
+ | List | `is.workflow.actions.list` | `WFItems` |
169
+ | Dictionary | `is.workflow.actions.dictionary` | `WFItems` |
170
+ | Repeat (count) | `is.workflow.actions.repeat.count` | `WFRepeatCount`, `GroupingIdentifier`, `WFControlFlowMode` |
171
+ | Repeat (each) | `is.workflow.actions.repeat.each` | `WFInput`, `GroupingIdentifier`, `WFControlFlowMode` |
172
+ | If/Otherwise | `is.workflow.actions.conditional` | `WFInput`, `WFCondition`, `GroupingIdentifier`, `WFControlFlowMode` |
173
+ | Choose from Menu | `is.workflow.actions.choosefrommenu` | `WFMenuPrompt`, `WFMenuItems`, `GroupingIdentifier`, `WFControlFlowMode` |
174
+ | Find Photos | `is.workflow.actions.filter.photos` | `WFContentItemFilter` (see FILTERS.md) |
175
+ | Delete Photos | `is.workflow.actions.deletephotos` | `photos` (**NOT** `WFInput`!) |
176
+
177
+ ## Detailed Reference Files
178
+
179
+ For complete documentation, see:
180
+ - [PLIST_FORMAT.md](PLIST_FORMAT.md) - Complete plist structure
181
+ - [ICONS_AND_COLORS.md](ICONS_AND_COLORS.md) - Icon glyph + color selection (explicit and inferred)
182
+ - [ACTIONS.md](ACTIONS.md) - WF*Action identifiers and parameters
183
+ - [APPINTENTS.md](APPINTENTS.md) - AppIntent actions (ToolKit + backups)
184
+ - [AUTOMATION_TRIGGERS.md](AUTOMATION_TRIGGERS.md) - OS 27 ToolKit automation trigger metadata for research and future automation support
185
+ - [PARAMETER_TYPES.md](PARAMETER_TYPES.md) - All parameter value types and serialization formats
186
+ - [HEALTHKIT.md](HEALTHKIT.md) - iOS/iPadOS Health actions, bundled anonymized XML examples, and HealthKit value coverage
187
+ - [URL_SCHEMES.md](URL_SCHEMES.md) - Apple-documented Shortcuts URL schemes and x-callback-url patterns
188
+ - [JAVASCRIPT_WEBPAGE.md](JAVASCRIPT_WEBPAGE.md) - Run JavaScript on Webpage runtime requirements and script rules
189
+ - [DATE_TIME.md](DATE_TIME.md) - Apple-aligned date/time recipes, UNIX timestamps, ISO 8601, RFC 2822, and custom formats
190
+ - [VARIABLES.md](VARIABLES.md) - Variable reference system
191
+ - [CONTROL_FLOW.md](CONTROL_FLOW.md) - Repeat, Conditional, Menu patterns
192
+ - [FILTERS.md](FILTERS.md) - Content filters for Find/Filter actions (photos, files, etc.)
193
+ - [EXAMPLES.md](EXAMPLES.md) - Complete working examples
194
+ - [BEST_PRACTICES.md](BEST_PRACTICES.md) - Mandatory build guidelines
195
+ - [THIRD_PARTY_ACTIONS.md](THIRD_PARTY_ACTIONS.md) - Third-party actions (ToolKit + backups)
196
+ - [TOOLKIT_SNAPSHOT.md](TOOLKIT_SNAPSHOT.md) - Bundled ToolKit action-ID allowlists
197
+ - [CHANGELOG.md](CHANGELOG.md) - Change history: autoresearch findings, documentation updates, and version notes
198
+
199
+ When you need to verify an unfamiliar action identifier, check the packaged `data/toolkit-v*-tool-ids.json` snapshots, then `ACTIONS.md`, `APPINTENTS.md`, and `THIRD_PARTY_ACTIONS.md` before inventing anything.
200
+
201
+ ## Golden Example Library (On-Demand)
202
+
203
+ A curated set of shortcut XMLs is available for on-demand reference. Use the index first; only load XML sources that match the current task.
204
+
205
+ - Index: `golden-shortcuts/index.jsonl` (token-efficient metadata: title, purpose, tags, xml path)
206
+ - XMLs: `golden-shortcuts/xml/<shortcut_id>.xml`
207
+ - Note: Golden XMLs are pattern references and may predate current validator/comment standards; treat them as wiring examples, not pass/fail baselines.
208
+
209
+ Workflow:
210
+ 1) Read `golden-shortcuts/index.jsonl` to find relevant examples by tags/purpose.
211
+ 2) Load only the single XML file(s) needed for the current task.
212
+ 3) Do **not** bulk-load the entire library.
213
+
214
+ ## Icon and Color Resolver (Required)
215
+
216
+ For every generated shortcut, choose icon and color using the resolver unless the user gave explicit integer values already:
217
+
218
+ ```bash
219
+ python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "${USER_PROMPT}"
220
+ ```
221
+
222
+ Optional explicit overrides:
223
+
224
+ ```bash
225
+ python3 "$SKILL_DIR/scripts/select_shortcut_icon_color.py" --prompt "${USER_PROMPT}" --icon "robot" --color "purple"
226
+ ```
227
+
228
+ Then set:
229
+ - `WFWorkflowIconGlyphNumber = icon.glyph_number`
230
+ - `WFWorkflowIconStartColor = color.value`
231
+
232
+ The resolver supports natural-language icon requests (e.g. `paper airplane icon`, `terminal icon`, `expense icon`) and automatic icon selection when no icon is requested.
233
+
234
+ ## Preflight Validator — Craig Loop (Required)
235
+
236
+ After generating a shortcut, run the validator in a **fix loop** (Craig Loop). Each iteration: read the errors, make a targeted fix, re-validate. Do not re-run without changing something. If the OpenCode `tool.execute.after` hook reports validator feedback after an edit, treat that as the current validator run and fix the reported errors before signing.
237
+
238
+ ```bash
239
+ python3 "$SKILL_DIR/scripts/validate_shortcut.py" /path/to/Shortcut.xml
240
+ ```
241
+
242
+ ### Craig Loop Protocol
243
+
244
+ 1. **Run the validator.** If it passes, proceed to signing.
245
+ 2. **Read ALL error messages.** The validator prints every error it finds — fix as many as possible in one pass, not just the first one.
246
+ 3. **Make targeted fixes** in the plist XML based on the error messages. Each error includes the action index and identifier so you know exactly where to edit.
247
+ 4. **Re-run the validator.** Repeat from step 1.
248
+ 5. **Exit conditions** (stop looping and report to the user):
249
+ - **Max 5 iterations.** If the validator still fails after 5 fix attempts, stop. Summarize the remaining errors and ask the user for guidance.
250
+ - **Same errors repeating.** If the same error persists across 2 consecutive iterations despite attempted fixes, stop. The fix approach is wrong — do not keep trying the same thing.
251
+ - **Known validator gaps.** Only waive validator failures if `BEST_PRACTICES.md` lists a current, runtime-verified false positive. Otherwise fix the shortcut or stop and report the exact remaining errors.
252
+
253
+ ### Anti-patterns (do NOT do these)
254
+ - **Chatting the validator**: Running the validator repeatedly without making meaningful code changes between runs. Every re-run must follow a real edit.
255
+ - **Cosmetic fixes**: Rearranging comments or renaming variables to "try something" when the error is about wiring or missing parameters.
256
+ - **Regenerating from scratch** when only 1-2 specific actions need fixing. Targeted edits preserve working wiring.
257
+
258
+ ### Data sources
259
+ The validator uses bundled ToolKit snapshot IDs from packaged `data/toolkit-v*-tool-ids.json` files, filtered by target OS version and target platform, then augments with [`ACTIONS.md`](ACTIONS.md), [`APPINTENTS.md`](APPINTENTS.md), and [`THIRD_PARTY_ACTIONS.md`](THIRD_PARTY_ACTIONS.md). The default OS target is `auto` (`sw_vers` on macOS, macOS 26 when the host cannot be detected). The default platform target is `macos`. Use `--target-macos 27` or `SHORTCUTS_PLAYGROUND_TARGET_MACOS=27` only when building OS 27-era shortcuts that need target-gated macOS v78 identifiers or OS 27-only parameters such as `WFAllowWebSearch` / `FollowUp` on Use Model, `interpretAsMarkdown`, `WFAvoidTolls`, and Safari Tab Group `contents`. Use `--target-platform ios` / `SHORTCUTS_PLAYGROUND_TARGET_PLATFORM=ios` only for iPhone/iPad authoring, and `--target-platform all` only for intentional cross-platform metadata audits. For OS 27 targets, the validator also uses `data/toolkit-v78-first-party-parameter-keys.json` to reject unknown top-level keys on first-party `com.apple.*` AppIntent-style actions. It does not read the user's live ToolKit SQLite database during normal validation.
260
+
261
+ For reviewed Apple-derived macOS 27 schema grounding and automation trigger metadata, use the static catalogs only:
262
+
263
+ ```bash
264
+ python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --identifier additemtolist --target-macos 27
265
+ python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --python-name when_app_opened --target-macos 27
266
+ python3 "$SKILL_DIR/scripts/lookup_action_grounding.py" --identifier com.apple.HearingApp.MuteVolumeIntent --target-macos 27 --target-platform ios
267
+ ```
268
+
269
+ `data/macos27-shortpy-grounding.json` may improve parameter/schema confidence, `data/toolkit-v78-first-party-enum-cases.json` may improve picker-value selection, `data/toolkit-v78-trigger-parameter-keys.json` may improve automation-trigger discovery, and `data/macos27-workflow-trigger-samples.json` may show sanitized exported `WFWorkflowTriggers` shapes for observed OS 27 automation headers. None of them overrides validator target gating. The lookup helper reports target-platform availability notes for iOS-only/macOS-only ToolKit rows. Exported automation-bearing shortcuts are the authority for portable automation-header authoring; do not infer that carrier from ToolKit metadata alone.
270
+
271
+ ### Escape-hatch comments
272
+ If a request explicitly requires vCard/VCF formatting or file-based token loading, add a Comment containing `ALLOW_VCARD` or `ALLOW_TOKEN_FILE` so the validator can allow it. Other escape hatches: `ALLOW_MANUAL_UNIT_CONVERSION`, `ALLOW_DATETIME_FORMAT`.
273
+
274
+ ### Wiring Regression Suite (Recommended)
275
+
276
+ When changing wiring logic or validator rules for Weather/Location actions, run the bulk regression suite:
277
+
278
+ ```bash
279
+ python3 "$SKILL_DIR/scripts/test_wiring_regressions.py" --write-fixtures /tmp/shortcuts-wiring-regressions
280
+ ```
281
+
282
+ The suite generates and validates:
283
+ - 43 Weather Detail cases (21 valid + 22 invalid)
284
+ - 40 Location parameter cases (20 valid + 20 invalid)
285
+ - 16 Set Name/Rename File cases (8 valid + 8 invalid)
286
+
287
+ It exits non-zero if any case behavior regresses.
288
+
289
+ ### Random Mixed-Action Stress Suite (Recommended)
290
+
291
+ For broad randomized coverage (brand-new shortcuts, 10+ distinct actions each), run:
292
+
293
+ ```bash
294
+ python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10
295
+ ```
296
+
297
+ Behavior:
298
+ - Generates brand-new random shortcuts under `--output-dir` when provided, otherwise `SHORTCUTS_PLAYGROUND_OUTPUT_DIR/<YYYY-MM-DD>/random-mixed-actions-<runid>/` or `~/Documents/Shortcuts Playground/<YYYY-MM-DD>/random-mixed-actions-<runid>/`.
299
+ - Enforces a minimum distinct action count per shortcut (`--min-actions`, default `10`).
300
+ - Runs validate/retry loops per case (`--max-attempts`, default `20`).
301
+ - Writes `manifest.json`, `results.json`, and `summary.md` for documentation.
302
+
303
+ Optional:
304
+
305
+ ```bash
306
+ python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10 --sign
307
+ ```
308
+
309
+ For OS 27 coverage, opt in explicitly so older users and CI jobs do not validate Golden Gate-only actions by accident:
310
+
311
+ ```bash
312
+ python3 "$SKILL_DIR/scripts/test_random_mixed_shortcuts.py" --count 50 --min-actions 10 --target-macos 27 --include-os27-actions --sign
313
+ ```
314
+
315
+ The OS 27 module adds Stored Content, Add Item to List, Otherwise If, Get Selected Text, Get What's On Screen, and Get Current VPN to every generated shortcut.
316
+
317
+ Use this suite when you need randomized multi-action regression coverage beyond targeted wiring tests.
318
+
319
+ ## Signing Shortcuts
320
+
321
+ Shortcuts MUST be signed before they can be imported. This skill ships `scripts/sign_shortcut.sh`, which combines **archive + sign** into a single command:
322
+
323
+ ```bash
324
+ # Archives the unsigned XML under $output_dir/$(date +%F)/ and writes a signed .shortcut to $output_dir.
325
+ "$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/MyShortcut.xml --name "My Shortcut"
326
+
327
+ # Override the signing mode (default is SHORTCUTS_PLAYGROUND_SIGNING_MODE, falling back to 'anyone').
328
+ "$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/MyShortcut.xml --name "My Shortcut" --mode people-who-know-me
329
+ ```
330
+
331
+ The underlying pipeline is still the macOS `shortcuts` CLI:
332
+
333
+ ```bash
334
+ shortcuts sign --mode anyone --input MyShortcut.shortcut --output MyShortcut.shortcut
335
+ ```
336
+
337
+ The signing process:
338
+ 1. Write your plist as XML to a `.shortcut` file by default.
339
+ 2. Run `scripts/sign_shortcut.sh` (or `shortcuts sign` directly) to add the cryptographic signature (~19KB added). The wrapper archives the XML, then retries a validator-clean format failure after binary plist conversion when Apple's XML signer chokes.
340
+ 3. Keep the signed output filename equal to the intended display name (no `_signed` suffix).
341
+ 4. The signed file can be opened/imported into Shortcuts.app.
342
+
343
+ Signing gotchas:
344
+ - If `shortcuts sign` reports `Error: The file doesn't exist.` but the file exists, copy the XML plist directly to a clean `.shortcut` path and retry (example: `cp source.xml /tmp/MyShortcut.shortcut`).
345
+ - If `shortcuts sign` reports `Error: The file couldn't be opened because it isn't in the correct format.` while `validate_shortcut.py` and `plutil -lint` pass, retry after `plutil -convert binary1` on the final `.shortcut` copy before blaming the plist. `scripts/sign_shortcut.sh` performs this retry automatically; if both attempts fail, suspect sandbox restrictions before treating the XML as malformed.
346
+ - `ERROR: Unrecognized attribute string flag '?'` warnings are noisy but can be non-fatal if the output file is produced.
347
+ - The `shortcuts` CLI supports `run`, `list`, `view`, and `sign`; do not assume `delete`, `rename`, or `import` subcommands.
348
+
349
+ ## Archive Raw XML (Required)
350
+
351
+ Before signing, **archive the unsigned XML** in a date/time folder for inspection. `scripts/sign_shortcut.sh` does this for you, but the rules below still apply when you invoke `shortcuts sign` directly.
352
+
353
+ Folder structure rule:
354
+ - The archive root is `SHORTCUTS_PLAYGROUND_OUTPUT_DIR` (falls back to `~/Documents/Shortcuts Playground/` when unset).
355
+ - Inside the archive root, create a **date folder** for the current day (`YYYY-MM-DD`) if it doesn't exist.
356
+ - Copy the unsigned XML into that date folder with a time-stamped filename.
357
+
358
+ Example (output dir = `~/Documents/Shortcuts Playground`):
359
+ - Archive folder: `~/Documents/Shortcuts Playground/2026-02-03/`
360
+ - Archive file: `My Shortcut-142355.xml`
361
+
362
+ Command pattern (the one-liner `scripts/sign_shortcut.sh` wraps):
363
+ ```bash
364
+ OUTPUT_DIR="${SHORTCUTS_PLAYGROUND_OUTPUT_DIR:-$HOME/Documents/Shortcuts Playground}"
365
+ ARCHIVE_ROOT="$OUTPUT_DIR/$(date +%F)"
366
+ mkdir -p "$ARCHIVE_ROOT"
367
+ cp "/path/to/My Shortcut.xml" "$ARCHIVE_ROOT/My Shortcut-$(date +%H%M%S).xml"
368
+ ```
369
+
370
+ The archive copy must be the **unsigned, raw XML** (not the signed `.shortcut`).
371
+
372
+ ## Workflow for Creating Shortcuts
373
+
374
+ 0. **Research external APIs** - For complex/unfamiliar APIs, read the latest official docs before drafting request code.
375
+ 1. **Define actions** - List what the shortcut should do
376
+ 2. **Generate UUIDs** - Each action that produces output needs a unique UUID
377
+ 3. **Build action array** - Create each action dictionary with identifier and parameters
378
+ 4. **Wire variable references** - Connect outputs to inputs using `OutputUUID`
379
+ 5. **Resolve icon and color** - Run `scripts/select_shortcut_icon_color.py` with the full user prompt (plus any explicit icon/color hints) and use the returned values
380
+ 6. **Wrap in plist** - Add the root structure with icon, name, version
381
+ 7. **Write to file** - Save as `.shortcut` (XML plist format is fine)
382
+ 8. **Preflight validation** - Run the Craig Loop (see above): validate → fix → re-validate, max 5 iterations.
383
+ 9. **Archive + Sign (required)** - Run `"$SKILL_DIR/scripts/sign_shortcut.sh" /path/to/file.xml --name "Final Name"`. This script archives the unsigned XML to `SHORTCUTS_PLAYGROUND_OUTPUT_DIR/$(date +%F)/` or `~/Documents/Shortcuts Playground/$(date +%F)/` and writes the signed `.shortcut` alongside it. Never leave the signing output filename with a `_signed` suffix.
384
+ 10. **Verify signed output (required)** - Confirm the signed `.shortcut` path reported by `sign_shortcut.sh` exists and has non-zero size before reporting done. Stopping at "validation passed" is a failed build.
385
+
386
+ After step 8 succeeds, go directly to step 9. Any edit after validation, including comment or wording polish, requires another validation and another signing pass.
387
+
388
+ ## Comment Blocks (Repair-Oriented)
389
+
390
+ Because variable wiring can require manual fixes, add a **concise Comment before each major block** with a **bulleted list** describing which variables must be connected. Keep it short, specific, and focused on wiring (e.g., “Use Repeat Item 2 inside inner loop”).
391
+ Write comments with Shortcuts UI wording (for example, `Input`, `Date`, `Provided Input`, `Repeat Item`, `Text`) and readable action names (`Ask for Input`, `Text`, `Save File`). Do **not** use plist key jargon like `WFInput` / `WFDate` / `WFImage` in Comment text. **NEVER include UUIDs, OutputUUID references, or technical plist details in Comment text** — comments must be descriptive natural language only.
392
+ Prefer wording like `- Input uses the text output from the Text action above` and `• Date uses the user's answer from Ask for Input` instead of `WF*` field names or UUID references.
393
+
394
+ ## Key Rules
395
+
396
+ 1. **UUIDs must be uppercase and generated via `uuidgen`, not hand-picked.** Before emitting a shortcut, run a single Bash call to generate all the UUIDs you'll need:
397
+ ```bash
398
+ for i in $(seq 1 <N>); do uuidgen | tr '[:lower:]' '[:upper:]'; done
399
+ ```
400
+ Where `<N>` is the number of action UUIDs the shortcut requires (one per action that produces output or is referenced by downstream actions). Assign each output line to a specific action in your working map, then paste them into the plist. **Never use sequential placeholders** like `11111111-1111-1111-1111-111111111111`, `AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA`, or any other pattern where every hex character is the same — the validator rejects repeating-hex UUIDs as a hard error. Valid example shape: `7F3A4E91-C2D8-4B56-BE5A-0242AC120002`.
401
+ 2. **WFControlFlowMode is an integer**: Use `<integer>0</integer>` not `<string>0</string>`
402
+ 3. **Range keys use format**: `{position, length}` - e.g., `{0, 1}` for first character
403
+ 4. **The placeholder character**: `` (U+FFFC) marks where variables are inserted
404
+ 5. **Control flow needs matching ends**: Every repeat/if/menu start needs an end action with same `GroupingIdentifier`
405
+ 6. **Match placeholder positions**: `attachmentsByRange` must point to the exact index of each `` in the final string
406
+ 7. **No out-of-bounds ranges**: `attachmentsByRange` positions beyond string length can crash Shortcuts on import
407
+ 8. **Avoid empty placeholders**: Omit unused keys or fields; do not leave empty values where the action expects data
408
+ 9. **WFURL serialization**: For `WFURL` parameters (especially `downloadurl`), use `WFTextTokenString` with `` placeholders even when the URL is entirely a variable; reserve `WFTextTokenAttachment` for parameters that are explicitly variable-only (e.g., `WFVariable`, `WFRequestVariable`)
409
+ 10. **Form file fields and WFRequestVariable**: For `WFHTTPBodyType = Form`, file fields (`WFItemType = 5`) must wrap the file reference in `WFTokenAttachmentParameterState` with an inner `WFTextTokenAttachment` so the UI shows the connected file variable. Set `WFRequestVariable` only when body type is `File` (JSON Text fallback pattern)
410
+ 11. **Array fields**: For `WFDictionaryFieldValue` items with `WFItemType = 2`, use `WFArrayParameterState` with a list of items (`WFItemType` + `WFValue`), not numeric keys inside another dictionary
411
+ 12. **Format Date input**: Set `WFDate` (not `WFInput`) as a `WFTextTokenString` placeholder wired to a Date output; never leave `WFDate` empty
412
+ 13. **Repeat loops**: Inside **Repeat with Each**, use **Repeat Item** for per-item extraction; avoid **Repeat Results** inside the loop
413
+ 14. **Reuse extractions**: If you extract multiple fields from a dictionary/API response, reuse each value later or remove the unused extraction
414
+ 15. **Variable placeholders**: When composing strings or URLs with variables (Ask for Input, Track/Artist, etc.), insert a placeholder for every variable and align `attachmentsByRange` positions
415
+ 16. **Dictionary dot notation**: Use dot notation (with 1-based indexes) in `WFDictionaryKey` to access nested keys (e.g., `results.tracks.items`, `artists.1.name`), but guard optional parents first (avoid direct `error.message` on raw API responses)
416
+ 17. **Conditional inputs (verified against Apple-built samples)**: For `is.workflow.actions.conditional` with `WFControlFlowMode = 0`, **every** condition code requires an explicit `WFInput` set as `{ Type: "Variable", Variable: { Value: <ActionOutput or Variable>, WFSerializationType: "WFTextTokenAttachment" } }`. There is no implicit-input mode. Per-code literal field requirements: string codes `4`/`5`/`8`/`9`/`99`/`999` need `WFConditionalActionString`; numeric codes `0`/`1`/`2`/`3` need `WFNumberValue`; numeric `1003` (`is between`) needs both `WFNumberValue` (lower) and `WFAnotherNumber` (upper, attachment); existence codes `100`/`101` need neither literal field. Code `0` is `is less than` (NOT equals); codes `0`–`3` are inequalities. Multi-condition Ifs use `WFConditions` with `WFContentPredicateTableTemplate` serialization; do not mix `WFConditions` with top-level `WFCondition`. macOS 27 `Otherwise If` is the same conditional identifier with `WFControlFlowMode = 1` plus condition fields. Plain Otherwise is mode 1 with no condition fields; End If is mode 2. For JSON booleans, compare numerically (`1`/`0`); treat JSON `null` as empty. See `CONTROL_FLOW.md` "Condition Codes" and "Multi-condition If" for the complete reference and templates.
417
+ 18. **Workflow icon keys are mandatory**: Always set both `WFWorkflowIconGlyphNumber` and `WFWorkflowIconStartColor` in `WFWorkflowIcon` (use resolver output)
418
+ 19. **Runtime file picking**: If the user needs to choose a file, use `is.workflow.actions.file.select` and connect its output
419
+ 20. **Text token validation**: Run a final validation pass over every `WFTextTokenString` and refuse to output if any `attachmentsByRange` key does not map to a placeholder position or if counts mismatch
420
+ 21. **No unrequested services**: Do not introduce third-party APIs, external CDNs, or extra import questions unless the user explicitly requests them
421
+ 22. **Notion image uploads**: Default to Notion `file_uploads` + `file_upload` block type; avoid `external` image blocks unless the user supplies a URL or asks for external hosting
422
+ 23. **Notion title filters**: Use the Notion `title` property name unless the user explicitly says their database uses a different title property name (e.g., `Name`)
423
+ 24. **API research and endpoint accuracy**: For complex or unfamiliar external APIs, verify auth, endpoints, parameters, and payload formats against the latest official docs before assembling the shortcut. Validate endpoint strings exactly (underscore vs hyphen matters)
424
+ 25. **API string sanity checks**: Before output, scan API strings for `//` (beyond protocol) and empty JSON fields where variables are expected (e.g., `equals:””`, `id:””`, `url:””`, `filename:””`), and fix them
425
+ 26. **JSON request bodies**: For `WFHTTPBodyType = JSON`, use `WFJSONValues` for flat key/value payloads so the body is preserved in Shortcuts UI
426
+ 27. **Format Date custom style**: When `WFDateFormatStyle` is `Custom`, set `WFDateFormat=Custom` and put the pattern in `WFDateFormatString` (e.g., `MMMM d, yyyy`, `yyyy-MM-dd`, `yyyy-MM-dd'T'HH:mm:ssXXXXX`). See DATE_TIME.md for UNIX timestamp, ISO 8601, RFC 2822, and Unicode TR35 guidance
427
+ 28. **Complex JSON fallback**: If the JSON body includes arrays of objects or deep nesting and the UI renders it as `Number 0`/empty rows, use a JSON Text action and set `WFRequestVariable` to that text with `WFHTTPBodyType = File` and `Content-Type: application/json`
428
+ 29. **Filename extensions**: When using **Get Name** for file uploads, append the correct extension in the JSON payload (e.g., `.png`) and do not rely on the base name alone
429
+ 30. **Count input visibility**: For `is.workflow.actions.count`, set both `WFInput` and `Input` to the same variable so the UI shows the selected list
430
+ 31. **Get Name web titles**: For file names, set **Get Web Page Title** to **Off** in **Get Name** so the action returns the file name, not a URL title
431
+ 32. **String If workaround**: If validator rules conflict on string conditionals, use `Match Text` + `Count` + numeric `If` instead of direct string `If`
432
+ 33. **Replace Text empty replacement**: For delete-match patterns, prefer omitting `WFReplaceTextReplace`; an explicit empty string is allowed, but omission is cleaner and more portable
433
+ 34. **Base64 input wiring**: Always set `WFInput` for `is.workflow.actions.base64encode`; implicit input can import as an empty field and break runtime
434
+ 35. **Replace Text input visibility**: For `is.workflow.actions.text.replace`, use a `WFTextTokenString` placeholder (or wrapped variable input), not a bare `WFTextTokenAttachment`
435
+ 36. **Adjust Date reliability**: Use `WFDate` + non-empty `WFDuration` for `is.workflow.actions.adjustdate` (optionally mirror with `WFInput`); include `WFAdjustOperation` when explicit Add/Subtract is required. Offset-picker-only payloads can import as `Add 0 seconds` on iOS
436
+ 37. **Convert Image wiring**: For `is.workflow.actions.image.convert`, always set `WFInput` explicitly; do not rely on implicit input chaining
437
+ 38. **Weather detail wiring**: For `is.workflow.actions.properties.weather.conditions`, set both `WFInput` and `WFContentItemPropertyName`, keep `WFContentItemPropertyName` concrete (never placeholder `Detail`), and wire `WFInput` directly to an ActionOutput from `is.workflow.actions.weather.currentconditions` / `is.workflow.actions.weather.forecast` (no named-variable hop). Supported detail names are `Date`, `Location`, `Temperature`, `Low`, `High`, `Feels Like`, `Condition`, `Visibility`, `Dewpoint`, `Humidity`, `Pressure`, `Precipitation Amount`, `Precipitation Chance`, `Wind Speed`, `Wind Direction`, `UV Index`, `Sunrise Time`, `Sunset Time`, `Air Quality Index`, `Air Quality Category`, `Air Pollutants`, and `Name`. `Sunrise Time` and `Sunset Time` from Daily forecasts are lists: insert `Get Item from List` before `Format Date`, using First Item for sunrise and Last Item for sunset
438
+ 39. **Time Between Dates input wiring**: For `is.workflow.actions.gettimebetweendates`, set `WFInput` and exactly one non-empty date operand (`WFDate` or `WFTimeUntilCustomDate` or `WFTimeUntilFromDate`) as `WFTextTokenString` placeholders. To compare with now, first add a Date action set to Current Date and reference that action output; never put a direct `CurrentDate` magic token in the action. `WFTimeUntilUnit` may be omitted only when intentionally using the default unit. Never emit empty unused date keys
439
+ 40. **Extract from Image input wiring**: For `is.workflow.actions.extracttextfromimage`, set exactly one non-empty image input key. On OS 27+, prefer `imageFile`; older exported shortcuts may use `WFImage` or, when intentionally required by an existing pattern, `WFInput`.
440
+ 41. **API error extraction safety**: Do not read `error.message` directly from a raw response; extract `error`, guard it with `If Has Any Value`, then read `message`
441
+ 42. **Continuation JSON array closure**: When appending to a JSON array via `Replace Text` on `\]$`, the replacement must end with `]`; missing the closing bracket corrupts JSON and causes `Detect Dictionary` to return empty
442
+ 43. **No raw object/list tokens inside JSON text**: Do not inject Dictionary/List outputs (for example, raw API `content` arrays) directly into JSON Text templates; Shortcuts may stringify them as newline-separated blocks rather than valid JSON
443
+ 44. **Continuation payload safe pattern**: For multi-turn handoff payloads, append assistant text from a plain text variable (for example, `Response Text`) and keep `messages_json` as valid JSON before rerunning the shortcut
444
+ 45. **JSON string interpolation safety**: Before inserting freeform text into JSON Text templates (`”content”:””`), sanitize it first (at minimum handle backslashes, double quotes, and control whitespace/newlines) or the next `Detect Dictionary` step will fail
445
+ 46. **Shortcuts URL schemes**: Only use Apple-documented `shortcuts://` routes from URL_SCHEMES.md. URL-encode every query value; do not invent import/install routes or extra parameters
446
+ 47. **Run JavaScript on Webpage**: Use `is.workflow.actions.runjavascriptonwebpage` only for Safari webpage share-sheet shortcuts. Include `ActionExtension`, scope input to `WFSafariWebPageContentItem`, call `completion(...)` or `completion()`, return JSON-compatible values, and avoid synchronous dialogs/long timers
447
+ 46. **API response parse stability**: For `downloadurl` JSON APIs, keep `ShowHeaders` off unless explicitly needed and run `Detect Dictionary` on `Contents of URL` before any `Get Dictionary Value` extraction
448
+ 47. **Action input keys matter**: `Replace Text` uses `WFInput`, while `Change Case` and `Split Text` use `text`; wrong keys import but show empty inputs in the editor
449
+ 48. **Split Text custom separator**: If `WFTextSeparator` is `Custom`, always include `WFTextCustomSeparator` (a single space `” “` is valid)
450
+ 49. **Find Notes filter state**: `WFContentItemFilter` must be `WFContentPredicateTableTemplate` with non-empty templates; for `Folder` filters use `WFLinkDynamicOptionSubstitutableState` wrapping a tokenized variable
451
+ 50. **Direct variable wiring**: Prefer inserting named variables directly into action input fields; avoid redundant `Get Variable → next action` hops unless there is a clear transformation need
452
+ 51. **Location parameter wiring**: Never emit empty location parameters. For `WFLocation`, `WFWeatherCustomLocation`, and `WFWeatherLocation`, use `WFTextTokenAttachment` (not token strings) and reference a Get Current Location/Location output (directly or via a variable sourced from those outputs). `is.workflow.actions.location` must include a non-empty `WFLocation` attachment; missing/blank payloads import as empty “Location” fields
453
+ 52. **Set Name vs Rename File**: Use **Set Name** as `is.workflow.actions.setitemname` with `WFInput` (source file/item) and `WFName` (target filename, for example `Test.txt`) when a workflow needs a renamed file object to save or share elsewhere. Its output is **Renamed Item**. Do not confuse this with **Rename File** (`is.workflow.actions.file.rename` with `WFFile` and `WFNewFilename`), which renames the original file in place at its existing path. For "rename, save/share elsewhere, then delete original" workflows, store the picked file as **Original File**, run Set Name on **Original File**, save/share **Renamed Item**, then delete **Original File** only after the save/share step if requested
454
+ 53. **⚠️ WFMathOperation syntax (verified against Shortcuts app)**: For `is.workflow.actions.math`: (a) **Addition**: OMIT the `WFMathOperation` key entirely — no key means addition; (b) **Subtraction**: `-` (ASCII minus, U+002D); (c) **Multiplication**: `×` (U+00D7, ord 215, Unicode MULTIPLICATION SIGN) — NEVER `*`; (d) **Division**: `÷` (U+00F7, ord 247, Unicode DIVISION SIGN) — NEVER `/`; (e) **Scientific ops** (Modulus, Power, etc.): `WFMathOperation='…'` (U+2026 horizontal ellipsis) as placeholder, with real op in `WFScientificMathOperation` and operand in `WFScientificMathOperand`. **Literal operands** (`WFMathOperand`) must be plain strings like `"10"`, not wrapped dicts. Shortcuts silently renders ASCII `/` as `+` in the UI with no error. See PARAMETER_TYPES.md "Math and Counting Operations" for verified examples.
455
+ 54. **Never inspect the user's local system for authoring discovery unless the user explicitly asks for that local evidence.** If an action identifier is allowlisted in `data/toolkit-v*-tool-ids.json` but its parameter schema is not documented in the bundled reference files (`ACTIONS.md`, `APPINTENTS.md`, `PARAMETER_TYPES.md`, `FILTERS.md`, `EXAMPLES.md`, `BEST_PRACTICES.md`, `HEALTHKIT.md`, static `data/macos27-shortpy-grounding.json`, or the `golden-shortcuts/` library), **stop and ask the user** - do not try to reverse-engineer the schema by reading local databases, inspecting system binaries, querying Shortcuts.app internals, or searching cloud-backup folders without explicit permission. Escalate to the user with three options: (a) best-effort guess + iterate after they import, (b) use a simpler alternative action you propose, or (c) they paste a working example for you to mirror. When the user explicitly supplies or requests local exported XML, prefer that evidence over web references.
456
+ 55. **Automation triggers are sample-gated.** `AUTOMATION_TRIGGERS.md`, `data/toolkit-v78-trigger-parameter-keys.json`, and `data/macos27-workflow-trigger-samples.json` document OS 27 ToolKit trigger IDs, Python names, parameter keys, output types, and sanitized exported `WFWorkflowTriggers` payloads. Use exported automation-bearing shortcuts as the source of truth for portable authoring. Do not invent trigger plists from ToolKit metadata alone. Do not write live Shortcuts database rows; generate shortcut XML with a top-level `WFWorkflowTriggers` array, fresh `WFTriggerUUID` values, and `--target-macos 27` validation.
457
+ 56. **Bottom-align in Combine Images via the flip trick**: `is.workflow.actions.image.combine` in horizontal mode top-aligns images. To bottom-align without transparent canvas padding: (1) flip each input image upside-down before Combine, (2) run Combine (now "top" is the original bottom), (3) flip the combined result upside-down. ⚠️ **`is.workflow.actions.image.flip` behaves OPPOSITELY on iOS vs macOS — this is a genuine Apple bug across platforms**: on **iOS/iPadOS**, `WFImageFlipDirection='Vertical'` produces upside-down (direction-of-motion naming); on **macOS**, `WFImageFlipDirection='Horizontal'` produces upside-down (axis-of-reflection naming). The same plist value renders differently. **Workaround**: wrap the flip in an If block checking `Device Model is Mac` → Flip Horizontally / Otherwise → Flip Vertically. Do this for BOTH flips in the trick (per-image and combined result). Proven on Apple Frames 4 proportional scaling — worked on iOS but silently failed on macOS until the device check was added
458
+ 57. **Reminders: always use `is.workflow.actions.setters.reminders` for editing, never `UpdateReminderAppIntent`.** For every property of an existing reminder (Due Date, Title, Notes, Priority, Is Completed, Is Flagged, List, Subtasks, URL, Tags, Images, Parent Reminder, When Messaging Person), use one `setters.reminders` action per property with `Mode="Set"`, `WFContentItemPropertyName=<property name>`, and the matching `WFReminderContentItem<CamelCaseProperty>` value key. Chain multiple setters by pointing each one's `WFInput` at the previous setter's `ActionOutput` (`OutputName="Edited Reminder"`) so the "Edited Reminder" variable propagates. The `List` property is the only one that takes a plain string (the list name), not a token attachment. For date filtering in `filter.reminders`, operator `1002` ("is today") takes empty `Values`, operator `1003` ("is between") takes `Values.Date` (literal ISO `<date>`) + `Values.AnotherDate` (token attachment, typically `{Type: "CurrentDate"}`). See [PARAMETER_TYPES.md → Reminders — Filter & Setter Schemas](PARAMETER_TYPES.md#reminders--filter--setter-schemas-definitive) for the complete verified schema, the per-property value-key table, and verbatim templates.
459
+ 58. **HealthKit actions are iOS/iPadOS-only and must use `HEALTHKIT.md`.** For `filter.health.quantity`, put the Health sample kind in a non-removable `WFContentItemFilter` `Type` predicate row backed by `Values.Enumeration` / `WFStringSubstitutableState`; never use obsolete top-level `WFHealthQuantityType`, and never use `Property = Value` with a plain string such as `Step Count`. For summaries, use observed picker labels such as `Sleep`, `Exercise Minutes`, and `Active Calories`; do not emit `Sleep Analysis`, `Active Energy`, `Active Energy Burned`, `Apple Exercise Time`, or `Exercise Time` for HealthKit actions. Treat Sleep `Duration` math as seconds if coerced through Math: divide by `3600` for decimal hours, not `60`. For `properties.health.quantity`, `health.quantity.log`, and `health.workout.log`, use the bundled anonymized iOS XML examples and `data/healthkit-ios26.2-reference.json`. Do not guess the Log Health Sample schema: use `WFQuantitySampleType` plus `WFQuantitySampleQuantity` (`WFQuantityFieldValue`), allow unit-only `WFQuantitySampleAdditionalQuantity`, and add `WFCategorySampleEnumeration` only when a category picker value is needed. Do not run Health-writing shortcuts just to test syntax.
@@ -0,0 +1,77 @@
1
+ # Third-Party Actions Reference
2
+
3
+ This file lists third-party actions discovered from:
4
+ - macOS Shortcuts ToolKit database (installed apps)
5
+ - Local shortcut backup exports
6
+
7
+ Installed third-party actions (ToolKit): 11
8
+
9
+ ## Third-Party Actions (ToolKit)
10
+ - `io.tailscale.ipn.macsys.ConnectIntent`
11
+ - `io.tailscale.ipn.macsys.DeviceAppEntity`
12
+ - `io.tailscale.ipn.macsys.DisconnectIntent`
13
+ - `io.tailscale.ipn.macsys.PingAppIntent`
14
+ - `io.tailscale.ipn.macsys.ProfileAppEntity`
15
+ - `io.tailscale.ipn.macsys.SetDNSAppIntent`
16
+ - `io.tailscale.ipn.macsys.StopUsingExitNodeIntent`
17
+ - `io.tailscale.ipn.macsys.SwitchProfileIntent`
18
+ - `io.tailscale.ipn.macsys.TaildropAppIntent`
19
+ - `io.tailscale.ipn.macsys.ToggleAppIntent`
20
+ - `is.workflow.actions.lightroom.import`
21
+
22
+ Third-party actions referenced in backups: 49
23
+
24
+ ## Third-Party Actions (Backups)
25
+ - `betamagic.News-Explorer.mobile.ArticleBySelection`
26
+ - `betamagic.News-Explorer.mobile.ArticleEntity`
27
+ - `co.montanafloss.Shareshot.FrameScreenshotsIntent`
28
+ - `co.zottmann.ActionsForObsidian.CreateNote`
29
+ - `co.zottmann.ActionsForObsidian.GetNoteLink`
30
+ - `co.zottmann.ActionsForObsidian.SetNoteProperties`
31
+ - `com.agiletortoise.Drafts-OSX.CaptureIntent`
32
+ - `com.alexhay.ToolboxProForShortcuts.FindAlbumsIntent`
33
+ - `com.alexhay.ToolboxProForShortcuts.FindSongsIntent`
34
+ - `com.alexhay.ToolboxProForShortcuts.PlayMusicIntent`
35
+ - `com.alexhay.nautomate.AddValuesToDatabaseIntent`
36
+ - `com.alexhay.nautomate.CreateBookmarkBlockIntent`
37
+ - `com.alexhay.nautomate.CreateDividerBlockIntent`
38
+ - `com.alexhay.nautomate.CreateListBlockIntent`
39
+ - `com.alexhay.nautomate.CreateSmartBlocksFromTextIntent`
40
+ - `com.alexhay.nautomate.CreateTextBlockIntent`
41
+ - `com.culturedcode.ThingsMac.TAIAddTodo2`
42
+ - `com.culturedcode.ThingsMac.TAIItemEntity`
43
+ - `com.finnvoorhees.ShortcutButtons.NewButtonForShortcutIntent`
44
+ - `com.google.gemini.TypeToGeminiIntent`
45
+ - `com.gtrigonakis.TextWorkflow.TransformTextIntent`
46
+ - `com.gtrigonakis.TextWorkflowIOS.TransformTextIntent`
47
+ - `com.joehribar.toggl.StopTimeEntryIntent`
48
+ - `com.lireapp.smilingAlpaca.LIREGetArticleIdentifiersIntent`
49
+ - `com.lireapp.smilingAlpaca.LIREGetArticlePropertyIntent`
50
+ - `com.lireapp.smilingAlpaca.LIREGetCurrentArticleIdentifierIntent`
51
+ - `com.ngocluu.goodlinks.AddLinkIntent`
52
+ - `com.ngocluu.goodlinks.AddTags`
53
+ - `com.ngocluu.goodlinks.GetCurrentLink`
54
+ - `com.ngocluu.goodlinks.GetCurrentSelection`
55
+ - `com.ngocluu.goodlinks.GetTagsIntent`
56
+ - `com.ngocluu.goodlinks.HighlightEntity`
57
+ - `com.ngocluu.goodlinks.LinkEntity`
58
+ - `com.ngocluu.goodlinks.OpenHighlight`
59
+ - `com.ngocluu.goodlinks.OpenLinkIntent`
60
+ - `com.openai.chat.OpenVoiceModeIntent`
61
+ - `com.sindresorhus.AI-Actions.AskClaudeIntent`
62
+ - `com.sindresorhus.Actions.GenerateUUIDIntent`
63
+ - `com.sindresorhus.Actions.GetAudioPlaybackDestinationIntent`
64
+ - `com.sindresorhus.Actions.GetFilePathIntent`
65
+ - `com.sindresorhus.Actions.GetTitleOfURLIntent`
66
+ - `com.sindresorhus.Actions.TruncateNumber`
67
+ - `com.sindresorhus.Shortcutie.SetDefaultSoundDevice`
68
+ - `com.todoist.ios.OpenRambleAppIntent`
69
+ - `dk.simonbs.Jayson.ViewJSONIntent`
70
+ - `net.matrixteo.app.Collections.IntentsAddArrayValue`
71
+ - `net.matrixteo.app.Collections.IntentsAddDocument`
72
+ - `net.matrixteo.app.Collections.IntentsDocumentEntity`
73
+ - `net.matrixteo.app.Collections.IntentsUpdateValue`
74
+
75
+ Notes:
76
+ - Backup-only entries may come from apps not installed on this Mac.
77
+ - WF-style third-party actions can still use the `is.workflow.actions.*` prefix (e.g., Lightroom).
@@ -0,0 +1,62 @@
1
+ # Action ID Snapshots
2
+
3
+ This skill bundles precomputed action-ID allowlists so it can be shared without requiring any extra setup on the user's machine.
4
+
5
+ ## Bundled files
6
+
7
+ - `data/toolkit-v63-tool-ids.json` - flat list of 1,794 action/intent identifiers from the original ToolKit snapshot.
8
+ - `data/toolkit-v78-tool-ids.json` - flat list of 2,731 action/intent identifiers from macOS 27 Golden Gate build 26A5353q.
9
+ - `data/toolkit-v78-ios27-tool-ids.json` - flat list of 1,206 action/intent identifiers from an iOS 27.0 Simulator ToolKit v78 database.
10
+ - `data/toolkit-v78-first-party-parameter-keys.json` - compact first-party ToolKit v78 parameter-key/name/type catalog with per-parameter platform provenance, used by `lookup_action_grounding.py` for broad AppIntent/schema discovery.
11
+ - `data/toolkit-v78-first-party-enum-cases.json` - compact first-party ToolKit v78 enum-case catalog for action and automation-trigger parameter type names.
12
+ - `data/toolkit-v78-trigger-parameter-keys.json` - compact ToolKit v78 automation trigger catalog with trigger IDs, Python names, parameter keys, and output type identifiers.
13
+ - `data/macos27-workflow-trigger-samples.json` - sanitized exported OS 27 `WFWorkflowTriggers` samples for automation headers that have been observed in real shortcut exports.
14
+ - `data/macos27-shortpy-grounding.json` - reviewed static Apple-derived macOS 27 grounding catalog with ToolKit `pythonName`, Apple Shortpy keyword, ToolRenderer utility, and ShortcutsLanguage syntax evidence.
15
+
16
+ Only the identifiers and compact parameter/enum metadata needed for validation and lookup are bundled. This keeps the plugin lightweight compared with the full ToolKit SQLite metadata.
17
+
18
+ ## Validator behavior
19
+
20
+ `scripts/validate_shortcut.py` loads packaged `data/toolkit-v*-tool-ids.json` files according to the target OS version and target platform, then augments those snapshots with the markdown references (`ACTIONS.md`, `APPINTENTS.md`, `THIRD_PARTY_ACTIONS.md`). OS 27 snapshots include both macOS 27 and iOS 27 Simulator ToolKit evidence, but iOS-only snapshot rows are rejected for the default macOS platform target. It also target-gates the reviewed Automators OS 26 to 27 parameter deltas, so OS 27-only parameter keys are rejected on macOS 26 targets even when the action identifier itself predates OS 27.
21
+
22
+ For macOS/iOS 27 targets, the validator also loads `data/toolkit-v78-first-party-parameter-keys.json` as a schema check for first-party `com.apple.*` AppIntent-style actions. If a known AppIntent carries a top-level parameter key that does not appear in the active target's ToolKit v78 schema, validation fails. The catalog is filtered at the parameter level, so a field observed only in the iOS 27 Simulator ToolKit is rejected for the default macOS target even if the action row itself exists on both platforms. The validator also loads `data/toolkit-v78-first-party-enum-cases.json` and rejects invalid simple literal enum values for single-enum AppIntent parameters. It intentionally skips dynamic token values and multi-type parameters, and it does not apply broad unknown-key checks to regular `is.workflow.actions.*` actions because legacy WF actions can include valid plist/UI state keys that are absent from ToolKit parameter metadata.
23
+
24
+ The default OS target is `auto`: on macOS it reads `sw_vers -productVersion`; when the host cannot be detected it falls back to macOS 26 rather than latest. Override with `--target-macos 26`, `--target-macos 27`, or `--target-macos latest`. The same override is available via `SHORTCUTS_PLAYGROUND_TARGET_MACOS`.
25
+
26
+ The default platform target is `macos`. Override with `--target-platform ios` / `SHORTCUTS_PLAYGROUND_TARGET_PLATFORM=ios` for iPhone/iPad authoring, or `--target-platform all` only when intentionally validating every packaged platform.
27
+
28
+ This keeps validation portable and self-contained while avoiding false compatibility on machines that do not have macOS 27 installed.
29
+
30
+ ## macOS 27 scope
31
+
32
+ The v78 snapshot expands validation coverage for shortcuts created on macOS 27. It is only active when the target is macOS 27+ or `latest`. The action-ID snapshots are identifier allowlists, not complete authoring schemas. For first-party `com.apple.*` AppIntent-style actions, the v78 parameter catalog can reject unknown top-level keys, but it does not prove full value serialization, requiredness, picker state, or runtime behavior. If a v78-only action identifier is present but its parameter serialization is not documented in the reference files or a golden XML, do not guess the payload. Ask for an exported XML sample or use a documented fallback.
33
+
34
+ OS 27-era parameter keys are also target-gated. Examples include `WFAllowWebSearch` and `FollowUp` on Use Model, `interpretAsMarkdown` on Notes actions, `WFAvoidTolls`/`WFAvoidHighways` on Maps route actions, `WFAppsExcept` on Hide/Quit App, `imageFile` on Scan QR or Barcode / Extract from Image, and `contents` on Safari Create Tab Group.
35
+
36
+ ## iOS 27 simulator scope
37
+
38
+ The iOS 27 Simulator v78 snapshot adds iOS-only AppIntents that are not present in the macOS ToolKit database, such as `com.apple.HearingApp.MuteVolumeIntent` and iOS Settings/Wallet/Health/Fitness identifiers. These identifiers are included for iOS/iPadOS shortcut authoring and are also target-gated to OS 27+. They do not validate for the default macOS target; use `--target-platform ios` only when authoring for iPhone/iPad. Do not use them for macOS shortcuts unless a macOS ToolKit snapshot or exported shortcut confirms runtime support.
39
+
40
+ ## Apple-derived grounding catalog
41
+
42
+ `data/macos27-shortpy-grounding.json` is a portable metadata catalog generated from local macOS 27 Shortcuts ToolKit, ToolRenderer, WorkflowKit source-export, and installed sample evidence. It is static package data: normal validation does not read the user's live Shortcuts database and does not load private frameworks.
43
+
44
+ Use `scripts/lookup_action_grounding.py` to inspect it:
45
+
46
+ ```bash
47
+ python3 scripts/lookup_action_grounding.py --identifier additemtolist --target-macos 27
48
+ python3 scripts/lookup_action_grounding.py --python-name com_apple_shortcuts_add_item_to_list --json
49
+ python3 scripts/lookup_action_grounding.py --identifier com.apple.HearingApp.MuteVolumeIntent --target-macos 27 --target-platform ios --json
50
+ ```
51
+
52
+ The grounding catalog can improve authoring confidence, but it does not override target availability. `lookup_action_grounding.py` reports the requested target macOS and target platform, including a target-platform availability note when an entry is only observed in the opposite platform's ToolKit rows. If `validate_shortcut.py --target-macos 26` rejects a v78-only identifier or parameter key, the static grounding entry is only a note that the action or parameter exists in OS 27-era ToolKit metadata.
53
+
54
+ `lookup_action_grounding.py` computes per-action availability from the packaged ToolKit snapshots, not from the grounding catalog's source OS. Older actions that appear in the macOS 27 grounding catalog still remain available to older targets when they are present in `toolkit-v63`.
55
+
56
+ The lookup helper also reads `data/toolkit-v78-first-party-enum-cases.json` and attaches `enumCases` / `enumTypes` to ToolKit parameter summaries. This exposes concrete picker values such as Add Item to List `Beginning` / `End` / `Index`, Find Places `Relevance` / `Distance`, Set Multitasking Mode `fullScreenApps` / `windowedApps` / `stageManager`, VPN operations, and trigger state values. Dynamic user-local picker cases, such as personal Reminders list names or Contacts groups, are intentionally redacted from the packaged enum catalog. These cases improve authoring and catalog review, but they still do not prove full plist serialization for complex picker states.
57
+
58
+ ## Automation trigger metadata
59
+
60
+ `data/toolkit-v78-trigger-parameter-keys.json` packages the 42 automation triggers exposed in local macOS 27 and iOS 27 Simulator ToolKit v78 databases. This includes identifiers such as `com.apple.shortcuts.WFTimeOfDayTrigger.at_time_on_recurring_day`, Apple Shortpy-style names such as `when_app_opened`, trigger parameter keys, and raw output type identifiers.
61
+
62
+ ToolKit trigger metadata is discovery metadata only. Portable automation headers must come from exported shortcut evidence: use `data/macos27-workflow-trigger-samples.json` and [AUTOMATION_TRIGGERS.md](AUTOMATION_TRIGGERS.md) for sanitized `WFWorkflowTriggers` payload shapes. The validator checks top-level `WFWorkflowTriggers` for OS 27 targets, rejects placeholder values, and target-gates automation headers away from macOS 26/iOS 26 output. Do not infer trigger payloads from ToolKit rows alone.