shortcutkit 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -208
- package/dist/generated/actions.d.ts +12893 -1125
- package/dist/generated/params.d.ts +99 -83
- package/dist/index.d.ts +36 -18
- package/dist/index.js +2149 -262
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,274 +1,148 @@
|
|
|
1
1
|
# shortcutkit
|
|
2
2
|
|
|
3
|
-
Build, validate and sign Apple Shortcuts (`.shortcut`) files from TypeScript or Python.
|
|
3
|
+
Build, validate, and sign Apple Shortcuts (`.shortcut`) files from TypeScript or Python.
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://github.com/frontboat/shortcutkit/actions)
|
|
7
|
+
[](https://www.npmjs.com/package/shortcutkit)
|
|
8
|
+
[](https://pypi.org/project/shortcutkit/)
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Apple has never documented the `.shortcut` format or what its actions accept. shortcutkit
|
|
11
|
-
does not guess at either. The tools in this repository load WorkflowKit and ActionKit, the private
|
|
12
|
-
frameworks behind the Shortcuts app, and ask their own classes to describe and serialize
|
|
13
|
-
every built-in action. Apple's own apps' actions come from the Shortcuts app's action index,
|
|
14
|
-
the only readable copy of the App Intents registry. Every action identifier, parameter key, value encoding, icon colour, and even the
|
|
15
|
-
UTF-16 offset rule for embedded references came out of the engine, not from a person reading
|
|
16
|
-
files. The package cannot disagree with the app.
|
|
17
|
-
|
|
18
|
-
That turns an undocumented format into a typed one. In TypeScript, a wrong parameter key or a
|
|
19
|
-
string where a boolean belongs is a compile error. In Python the same checks run when you call
|
|
20
|
-
`action()`. A file built from plain code signs, imports and runs.
|
|
21
|
-
|
|
22
|
-
The data stays current the same way it was made. After a macOS update, one command re-extracts
|
|
23
|
-
everything in about a minute and prints a diff of what Apple changed, along with the semver
|
|
24
|
-
bump it deserves. `data/provenance.json` records which macOS and Shortcuts build produced the
|
|
25
|
-
files you are using.
|
|
26
|
-
|
|
27
|
-
## Features
|
|
28
|
-
|
|
29
|
-
- **Every built-in action, typed.** `actions.*` lists all 434 identifiers, with each action's
|
|
30
|
-
name, description, summary, output and parameter keys as hover documentation.
|
|
31
|
-
- **Apple's apps too.** 1,441 App Intents actions from 77 Apple apps and system components,
|
|
32
|
-
Create Reminder, Create Note, Send Message, the System Settings toggles, under the keys the
|
|
33
|
-
Shortcuts app itself assigns them: `actions.reminders_create_reminder`. Enumeration cases are
|
|
34
|
-
typed, and the `AppIntentDescriptor` the file needs is added for you.
|
|
35
|
-
- **Parameters checked before you run anything.** `{ WFStoredContentGlobalValue: "yes" }` is a
|
|
36
|
-
compile error; a switch takes a boolean or a reference. Enumeration choices are suggested.
|
|
37
|
-
A plain value slot also accepts a reference where the engine reads one, and the type says
|
|
38
|
-
which: a Filter action's sort key takes none, a variable picker takes any except Ask, and the
|
|
39
|
-
engine was asked about every key (`PARAM_VARIABLE_TYPES`). Every action has a named
|
|
40
|
-
parameter type (`ShowresultParams`, `RemindersCreateReminderParams`), and its output content
|
|
41
|
-
types are on `ACTIONS[id].outputTypes` and on the returned `Action`.
|
|
42
|
-
- **Value helpers that match the engine's serialization.** `ref()` to another action's output,
|
|
43
|
-
`variable()`, `shortcutInput()`, `clipboard()`, `currentDate()`, `ask()`, `deviceDetails()`,
|
|
44
|
-
`currentApp()`, `text()` for strings with embedded references. A bare `ref()` is what every
|
|
45
|
-
picker parameter takes; for a text parameter it is wrapped into the token string the engine
|
|
46
|
-
writes, and for an If subject into the form the app writes.
|
|
47
|
-
- **Control flow.** `if()` / `otherwise()` / `endIf()`, `repeatEach()` / `endRepeatEach()`,
|
|
48
|
-
`repeatCount()` / `endRepeatCount()` and `chooseFromMenu()` / `menuItem()` / `endMenu()`
|
|
49
|
-
manage the grouping identifiers for you.
|
|
50
|
-
- **App Intents from installed apps.** Any identifier outside the built-in set is accepted;
|
|
51
|
-
built-in identifiers and their parameter keys are also validated at run time.
|
|
52
|
-
- **Definitions and provenance.** `getAction(id)` describes any identifier, built-in or App
|
|
53
|
-
Intent: name, parameter keys and kinds, output, descriptor. `getDefinition(id)` returns the
|
|
54
|
-
engine's full record for a built-in (icon, keywords, parameter classes, required resources);
|
|
55
|
-
`provenance` says which build produced the data.
|
|
56
|
-
- **Signing.** `Shortcut.sign()` wraps `shortcuts sign` so the output imports on any device.
|
|
57
|
-
|
|
58
|
-
## How it compares
|
|
59
|
-
|
|
60
|
-
Every earlier library for writing Shortcuts as code kept its action list by hand, and that is
|
|
61
|
-
how the two most used ones ended: [shortcuts-js](https://github.com/joshfarrant/shortcuts-js)
|
|
62
|
-
(TypeScript, 129 actions, archived 2023) and
|
|
63
|
-
[python-shortcuts](https://github.com/alexander-akhmetov/python-shortcuts) (Python, archived
|
|
64
|
-
2024) both stopped once keeping up with Apple became a chore.
|
|
65
|
-
[Cherri](https://github.com/electrikmilk/cherri) is the active project in this space and takes
|
|
66
|
-
a different shape: a programming language with its own compiler, editor extension and package
|
|
67
|
-
manager, whose actions are defined in that language.
|
|
68
|
-
|
|
69
|
-
shortcutkit is a library, not a language. You write TypeScript or Python you already know, and
|
|
70
|
-
the action catalogue comes from the Shortcuts engine rather than from a maintainer. All 434
|
|
71
|
-
built-in actions and 1,441 Apple App Intents are covered today, and the next macOS update is
|
|
72
|
-
one `bun run extract` away.
|
|
73
|
-
|
|
74
|
-
## Installation
|
|
10
|
+
A shortcut that asks your name and says hello:
|
|
75
11
|
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
```
|
|
12
|
+
```ts
|
|
13
|
+
import { Shortcut, actions, ref, text } from "shortcutkit";
|
|
79
14
|
|
|
80
|
-
|
|
15
|
+
const s = new Shortcut("Greeting");
|
|
16
|
+
const name = s.action(actions.ask, { WFAskActionPrompt: "What's your name?" });
|
|
17
|
+
s.action(actions.showresult, { Text: text("Hello, ", ref(name), "!") });
|
|
81
18
|
|
|
82
|
-
|
|
83
|
-
|
|
19
|
+
await s.write(); // Greeting.shortcut
|
|
20
|
+
Shortcut.sign("Greeting.shortcut", "Greeting-signed.shortcut");
|
|
84
21
|
```
|
|
85
22
|
|
|
86
|
-
|
|
23
|
+
Open `Greeting-signed.shortcut` and Shortcuts adds it to your library. Shortcuts only imports
|
|
24
|
+
signed files, and signing needs a Mac with an iCloud login; everything else runs on any OS.
|
|
87
25
|
|
|
88
|
-
|
|
89
|
-
module JavaScript with type declarations. Python 3.9 or newer for the Python package.
|
|
90
|
-
- A Mac signed into iCloud for `sign()`. It runs `shortcuts sign`, which refuses to work
|
|
91
|
-
without an iCloud login even in `anyone` mode, so it cannot run on CI runners. `write()`
|
|
92
|
-
converts the plist to binary with `plutil` when present and otherwise leaves the XML form.
|
|
93
|
-
Building, validating and writing the unsigned file works anywhere.
|
|
26
|
+
## Why
|
|
94
27
|
|
|
95
|
-
|
|
28
|
+
The action catalog is extracted from the Shortcuts app's own frameworks, not written by hand,
|
|
29
|
+
and one command refreshes it after a macOS update. Earlier libraries kept hand-written lists and
|
|
30
|
+
are archived ([shortcuts-js](https://github.com/joshfarrant/shortcuts-js),
|
|
31
|
+
[python-shortcuts](https://github.com/alexander-akhmetov/python-shortcuts));
|
|
32
|
+
[Cherri](https://github.com/electrikmilk/cherri) is a separate language with its own compiler.
|
|
96
33
|
|
|
97
|
-
|
|
98
|
-
|
|
34
|
+
- All 434 built-in actions, typed, with documentation on hover.
|
|
35
|
+
- 1,449 actions from Apple's apps: Reminders, Notes, Messages, Settings, and 73 more.
|
|
36
|
+
- Third-party app actions, given their descriptor.
|
|
37
|
+
- If, Repeat, Repeat with Each, and Choose from Menu blocks.
|
|
38
|
+
- A wrong key or value fails at compile time in TypeScript, and at `action()` in Python.
|
|
99
39
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install shortcutkit # Node 20+ or Bun
|
|
44
|
+
pip install shortcutkit # Python 3.9+
|
|
105
45
|
```
|
|
106
46
|
|
|
107
|
-
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
These examples build on the one at the top.
|
|
108
50
|
|
|
109
51
|
### Share sheet
|
|
110
52
|
|
|
111
|
-
A shortcut offered from the share sheet declares its surfaces and the input it accepts, and
|
|
112
|
-
reads that input with `shortcutInput()`:
|
|
113
|
-
|
|
114
53
|
```ts
|
|
54
|
+
import { shortcutInput } from "shortcutkit";
|
|
55
|
+
|
|
115
56
|
const s = new Shortcut("Page Links", { types: ["ActionExtension"], inputClasses: ["WFSafariWebPageContentItem"] });
|
|
116
57
|
const links = s.action(actions.runjavascriptonwebpage, { WFInput: shortcutInput(), WFJavaScript: "completion(document.links.length)" });
|
|
117
58
|
s.action(actions.showresult, { Text: text("Links: ", ref(links)) });
|
|
118
59
|
```
|
|
119
60
|
|
|
120
|
-
`types` takes `ActionExtension` (share sheet), `QuickActions` (Finder and Services menus),
|
|
121
|
-
`MenuBar`, `NCWidget`, `WatchKit`, `Sleep` and `ReceivesOnScreenContent`.
|
|
122
|
-
|
|
123
61
|
### Control flow
|
|
124
62
|
|
|
125
|
-
`if()` returns a grouping identifier that the matching `otherwise()` and `endIf()` calls take
|
|
126
|
-
back:
|
|
127
|
-
|
|
128
63
|
```ts
|
|
129
|
-
const gid = s.if(ref(
|
|
130
|
-
s.action(actions.showresult, { Text: text("
|
|
64
|
+
const gid = s.if(ref(name), "has_any_value");
|
|
65
|
+
s.action(actions.showresult, { Text: text("Hello, ", ref(name)) });
|
|
131
66
|
s.otherwise(gid);
|
|
132
|
-
s.action(actions.showresult, { Text: text("
|
|
67
|
+
s.action(actions.showresult, { Text: text("Hello, stranger") });
|
|
133
68
|
s.endIf(gid);
|
|
134
69
|
```
|
|
135
70
|
|
|
136
71
|
### Apple's apps
|
|
137
72
|
|
|
138
|
-
Apple's App Intents actions are in the catalogue under the Shortcuts app's own keys. Their
|
|
139
|
-
parameters are typed like the built-ins, and the `AppIntentDescriptor` every such action
|
|
140
|
-
carries in the file is added automatically:
|
|
141
|
-
|
|
142
73
|
```ts
|
|
143
74
|
const r = s.action(actions.reminders_create_reminder, { title: text("Buy milk"), priorityLevel: "high" });
|
|
144
75
|
s.action(actions.notes_create_note, { name: "Shopping", content: text("Added ", ref(r)) });
|
|
145
76
|
```
|
|
146
77
|
|
|
147
|
-
###
|
|
78
|
+
### Other apps
|
|
148
79
|
|
|
149
|
-
|
|
150
|
-
and the descriptor yourself. Parameter keys are then typed as `Record<string, Value>` and
|
|
151
|
-
passed through as given:
|
|
80
|
+
Pass the identifier and descriptor yourself; the parameters are untyped.
|
|
152
81
|
|
|
153
82
|
```ts
|
|
154
|
-
s.action("com.example.app.
|
|
83
|
+
s.action("com.example.app.CreateNoteIntent", {
|
|
84
|
+
AppIntentDescriptor: {
|
|
85
|
+
BundleIdentifier: "com.example.app", Name: "Example", TeamIdentifier: "ABCDE12345",
|
|
86
|
+
AppIntentIdentifier: "CreateNoteIntent", ActionRequiresAppInstallation: true,
|
|
87
|
+
},
|
|
88
|
+
title: text("Hello"),
|
|
89
|
+
body: ref(name),
|
|
90
|
+
});
|
|
155
91
|
```
|
|
156
92
|
|
|
157
93
|
### Python
|
|
158
94
|
|
|
159
|
-
Same API
|
|
160
|
-
compile-time ones:
|
|
95
|
+
Same API in snake_case. See [`python/README.md`](python/README.md).
|
|
161
96
|
|
|
162
97
|
```python
|
|
163
98
|
from shortcutkit import Shortcut, actions, ref, text
|
|
164
99
|
|
|
165
|
-
s = Shortcut("Greeting"
|
|
166
|
-
|
|
167
|
-
s.action(actions.SHOWRESULT, Text=text("
|
|
100
|
+
s = Shortcut("Greeting")
|
|
101
|
+
name = s.action(actions.ASK, WFAskActionPrompt="What's your name?")
|
|
102
|
+
s.action(actions.SHOWRESULT, Text=text("Hello, ", ref(name), "!"))
|
|
103
|
+
|
|
168
104
|
s.write()
|
|
169
105
|
Shortcut.sign("Greeting.shortcut", "Greeting-signed.shortcut")
|
|
170
106
|
```
|
|
171
107
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### Demo
|
|
175
|
-
|
|
176
|
-
Both packages ship a demo that builds a working shortcut with storage, an If/Otherwise block
|
|
177
|
-
and output:
|
|
178
|
-
|
|
179
|
-
```bash
|
|
180
|
-
bun run demo out.shortcut
|
|
181
|
-
python -m shortcutkit demo out.shortcut
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## Documentation
|
|
185
|
-
|
|
186
|
-
- [`docs/shortcut-file-format.md`](docs/shortcut-file-format.md): the `.shortcut` format end
|
|
187
|
-
to end, every field.
|
|
188
|
-
- [`docs/builtin-actions-reference.md`](docs/builtin-actions-reference.md): all 434 built-in
|
|
189
|
-
actions with their parameters.
|
|
190
|
-
- [`docs/apple-app-intents-reference.md`](docs/apple-app-intents-reference.md): the 1,441 App
|
|
191
|
-
Intents actions of Apple's apps, by app, with parameter kinds and enumeration cases.
|
|
192
|
-
- [`docs/parameter-encodings.md`](docs/parameter-encodings.md): how each parameter class is
|
|
193
|
-
serialized.
|
|
194
|
-
- [`docs/extraction.md`](docs/extraction.md): how the data was obtained, what did not work,
|
|
195
|
-
and what remains approximate. A handful of condition codes rest on community documentation.
|
|
196
|
-
|
|
197
|
-
## Development
|
|
198
|
-
|
|
199
|
-
```bash
|
|
200
|
-
git clone https://github.com/frontboat/shortcutkit.git && cd shortcutkit
|
|
201
|
-
bun install
|
|
202
|
-
bun test # all tests
|
|
203
|
-
bun run typecheck # tsc --noEmit; also verifies the @ts-expect-error assertions in tests
|
|
204
|
-
bun run build # bundles src/ into dist/index.js for Node and emits the .d.ts files
|
|
205
|
-
|
|
206
|
-
cd python && python3 -m venv .venv && .venv/bin/pip install -e .
|
|
207
|
-
.venv/bin/python -m shortcutkit demo /tmp/Demo.shortcut
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
CI runs the type check, the tests, a Node import of the built package, a Python install, the
|
|
211
|
-
engine verification below and an unsigned build of the demo shortcut on macOS. Signing is not
|
|
212
|
-
exercised in CI because the runner has no iCloud login.
|
|
213
|
-
|
|
214
|
-
### Verifying against the engine
|
|
215
|
-
|
|
216
|
-
```bash
|
|
217
|
-
bun run verify # ~3 min on a Mac that has opened Shortcuts.app once
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Both packages are driven over every action, every parameter key and every value form their
|
|
221
|
-
types admit, and the results are diffed against each other and then loaded through the
|
|
222
|
-
Shortcuts engine's own action objects, which report whether each value was read. Forms the
|
|
223
|
-
library refuses are written raw too, and the engine must refuse them as well. Whole shortcuts
|
|
224
|
-
built through every block helper are imported the way the app imports a file. The run fails on
|
|
225
|
-
any case the engine does not read; see `docs/extraction.md` for what it reports instead of
|
|
226
|
-
failing.
|
|
108
|
+
## API
|
|
227
109
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
| Path | Contents |
|
|
110
|
+
| Export | What it does |
|
|
231
111
|
|---|---|
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
112
|
+
| `new Shortcut(name, { color, glyph, types, inputClasses })` | A shortcut. |
|
|
113
|
+
| `s.action(id, params)` | Appends an action and returns it. |
|
|
114
|
+
| `s.if(subject, condition, value?)`, `s.otherwise(gid)`, `s.endIf(gid)` | An If block. |
|
|
115
|
+
| `s.repeatEach(items)`, `s.endRepeatEach(gid)` | A Repeat with Each block. |
|
|
116
|
+
| `s.repeatCount(n)`, `s.endRepeatCount(gid)` | A Repeat block. |
|
|
117
|
+
| `s.chooseFromMenu(prompt, items)`, `s.menuItem(gid, title)`, `s.endMenu(gid)` | A Choose from Menu block. |
|
|
118
|
+
| `s.write(path?)` | Writes the unsigned file and returns its path. |
|
|
119
|
+
| `Shortcut.sign(input, output, mode?)` | Signs a file with `shortcuts sign`. |
|
|
120
|
+
| `s.toPlist()`, `s.toXml()` | The shortcut as a plist object or XML. |
|
|
121
|
+
| `ref(action)` | An action's output. |
|
|
122
|
+
| `text(...parts)` | A string with references in it. |
|
|
123
|
+
| `variable(name)`, `repeatItem()`, `repeatIndex()` | Named and repeat variables. |
|
|
124
|
+
| `shortcutInput()`, `clipboard()`, `currentDate()`, `ask()`, `deviceDetails()`, `currentApp()` | Special variables. |
|
|
125
|
+
| `actions`, `getAction(id)`, `getDefinition(id)` | Action identifiers and their catalog entries. |
|
|
126
|
+
| `PARAM_KINDS`, `PARAM_CHOICES`, `PARAM_VARIABLE_TYPES` | What each parameter accepts. |
|
|
127
|
+
| `CONDITION`, `ICON_COLORS`, `provenance` | If conditions, icon colors, and the build the data came from. |
|
|
241
128
|
|
|
242
|
-
|
|
243
|
-
API that changes between releases, so committing it is what makes the package reproducible.
|
|
244
|
-
To refresh after a macOS update, on any Mac that has opened Shortcuts.app at least once. No
|
|
245
|
-
Xcode is needed: the probes are JavaScript for Automation scripts run by `osascript`, because
|
|
246
|
-
ActionKit, which defines about a quarter of the built-in actions, only loads into Apple's own
|
|
247
|
-
platform binaries. Apple's App Intents come from the Shortcuts app's action index, which
|
|
248
|
-
Shortcuts writes on launch.
|
|
129
|
+
## Documentation
|
|
249
130
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
```
|
|
131
|
+
- [Shortcut file format](docs/shortcut-file-format.md)
|
|
132
|
+
- [Built-in actions](docs/builtin-actions-reference.md)
|
|
133
|
+
- [Apple app actions](docs/apple-app-intents-reference.md)
|
|
134
|
+
- [Parameter encodings](docs/parameter-encodings.md)
|
|
135
|
+
- [How the data was extracted](docs/extraction.md), and what is still approximate
|
|
256
136
|
|
|
257
|
-
|
|
258
|
-
parameters is a minor release. One that removes or retypes any is a major release, because the
|
|
259
|
-
generated catalogue is part of the type surface. Bump `package.json` and
|
|
260
|
-
`python/pyproject.toml` together and add the `bun run changelog` output to `CHANGELOG.md`.
|
|
137
|
+
## Status
|
|
261
138
|
|
|
262
|
-
|
|
263
|
-
|
|
139
|
+
Extracted from macOS 27.2 and Shortcuts 10.0. A future macOS can break re-extraction, but not
|
|
140
|
+
the published packages, which ship the data.
|
|
264
141
|
|
|
265
|
-
##
|
|
142
|
+
## Contributing
|
|
266
143
|
|
|
267
|
-
|
|
268
|
-
declare minimum client version 900, which any current Shortcuts accepts. The private classes
|
|
269
|
-
the tools call can change with any release; the package itself depends only on the committed
|
|
270
|
-
data. Generated shortcuts import and run.
|
|
144
|
+
Issues and pull requests are welcome. See [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
271
145
|
|
|
272
146
|
## License
|
|
273
147
|
|
|
274
|
-
|
|
148
|
+
[MIT](LICENSE).
|