shortcutkit 0.7.0 → 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 +88 -183
- package/dist/generated/actions.d.ts +17230 -1567
- package/dist/generated/params.d.ts +99 -83
- package/dist/index.d.ts +65 -29
- package/dist/index.js +3164 -248
- package/dist/values.d.ts +16 -5
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -1,243 +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
|
-
Any plain value slot also accepts an attachment, because that is how Shortcuts works. Every
|
|
38
|
-
action has a named parameter type (`ShowresultParams`, `RemindersCreateReminderParams`), and
|
|
39
|
-
its output content types are on `ACTIONS[id].outputTypes` and on the returned `Action`.
|
|
40
|
-
- **Value helpers that match the engine's serialization.** `ref()` to another action's output,
|
|
41
|
-
`variable()`, `shortcutInput()`, `clipboard()`, `currentDate()`, `ask()`, `text()` for
|
|
42
|
-
strings with embedded references, and `picker()` for variable-picker parameters. A bare
|
|
43
|
-
`ref()` given to a text parameter is wrapped into the token string the engine writes.
|
|
44
|
-
- **Control flow.** `if()` / `otherwise()` / `endIf()`, `repeatEach()` / `endRepeatEach()`,
|
|
45
|
-
`repeatCount()` / `endRepeatCount()` and `chooseFromMenu()` / `menuItem()` / `endMenu()`
|
|
46
|
-
manage the grouping identifiers for you.
|
|
47
|
-
- **App Intents from installed apps.** Any identifier outside the built-in set is accepted;
|
|
48
|
-
built-in identifiers and their parameter keys are also validated at run time.
|
|
49
|
-
- **Definitions and provenance.** `getAction(id)` describes any identifier, built-in or App
|
|
50
|
-
Intent: name, parameter keys and kinds, output, descriptor. `getDefinition(id)` returns the
|
|
51
|
-
engine's full record for a built-in (icon, keywords, parameter classes, required resources);
|
|
52
|
-
`provenance` says which build produced the data.
|
|
53
|
-
- **Signing.** `Shortcut.sign()` wraps `shortcuts sign` so the output imports on any device.
|
|
54
|
-
|
|
55
|
-
## How it compares
|
|
56
|
-
|
|
57
|
-
Every earlier library for writing Shortcuts as code kept its action list by hand, and that is
|
|
58
|
-
how the two most used ones ended: [shortcuts-js](https://github.com/joshfarrant/shortcuts-js)
|
|
59
|
-
(TypeScript, 129 actions, archived 2023) and
|
|
60
|
-
[python-shortcuts](https://github.com/alexander-akhmetov/python-shortcuts) (Python, archived
|
|
61
|
-
2024) both stopped once keeping up with Apple became a chore.
|
|
62
|
-
[Cherri](https://github.com/electrikmilk/cherri) is the active project in this space and takes
|
|
63
|
-
a different shape: a programming language with its own compiler, editor extension and package
|
|
64
|
-
manager, whose actions are defined in that language.
|
|
65
|
-
|
|
66
|
-
shortcutkit is a library, not a language. You write TypeScript or Python you already know, and
|
|
67
|
-
the action catalogue comes from the Shortcuts engine rather than from a maintainer. All 434
|
|
68
|
-
built-in actions and 1,441 Apple App Intents are covered today, and the next macOS update is
|
|
69
|
-
one `bun run extract` away.
|
|
70
|
-
|
|
71
|
-
## Installation
|
|
10
|
+
A shortcut that asks your name and says hello:
|
|
72
11
|
|
|
73
|
-
```
|
|
74
|
-
|
|
12
|
+
```ts
|
|
13
|
+
import { Shortcut, actions, ref, text } from "shortcutkit";
|
|
14
|
+
|
|
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), "!") });
|
|
18
|
+
|
|
19
|
+
await s.write(); // Greeting.shortcut
|
|
20
|
+
Shortcut.sign("Greeting.shortcut", "Greeting-signed.shortcut");
|
|
75
21
|
```
|
|
76
22
|
|
|
77
|
-
|
|
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.
|
|
25
|
+
|
|
26
|
+
## Why
|
|
27
|
+
|
|
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.
|
|
33
|
+
|
|
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.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
78
41
|
|
|
79
42
|
```bash
|
|
80
|
-
|
|
43
|
+
npm install shortcutkit # Node 20+ or Bun
|
|
44
|
+
pip install shortcutkit # Python 3.9+
|
|
81
45
|
```
|
|
82
46
|
|
|
83
|
-
|
|
47
|
+
## Usage
|
|
84
48
|
|
|
85
|
-
|
|
86
|
-
module JavaScript with type declarations. Python 3.9 or newer for the Python package.
|
|
87
|
-
- A Mac signed into iCloud for `sign()`. It runs `shortcuts sign`, which refuses to work
|
|
88
|
-
without an iCloud login even in `anyone` mode, so it cannot run on CI runners. `write()`
|
|
89
|
-
converts the plist to binary with `plutil` when present and otherwise leaves the XML form.
|
|
90
|
-
Building, validating and writing the unsigned file works anywhere.
|
|
49
|
+
These examples build on the one at the top.
|
|
91
50
|
|
|
92
|
-
|
|
51
|
+
### Share sheet
|
|
93
52
|
|
|
94
53
|
```ts
|
|
95
|
-
import {
|
|
54
|
+
import { shortcutInput } from "shortcutkit";
|
|
96
55
|
|
|
97
|
-
const s = new Shortcut("
|
|
98
|
-
const
|
|
99
|
-
s.action(actions.showresult, { Text: text("
|
|
100
|
-
await s.write(); // Greeting.shortcut
|
|
101
|
-
Shortcut.sign("Greeting.shortcut", "Greeting-signed.shortcut");
|
|
56
|
+
const s = new Shortcut("Page Links", { types: ["ActionExtension"], inputClasses: ["WFSafariWebPageContentItem"] });
|
|
57
|
+
const links = s.action(actions.runjavascriptonwebpage, { WFInput: shortcutInput(), WFJavaScript: "completion(document.links.length)" });
|
|
58
|
+
s.action(actions.showresult, { Text: text("Links: ", ref(links)) });
|
|
102
59
|
```
|
|
103
60
|
|
|
104
|
-
Open the signed file and Shortcuts imports it.
|
|
105
|
-
|
|
106
61
|
### Control flow
|
|
107
62
|
|
|
108
|
-
`if()` returns a grouping identifier that the matching `otherwise()` and `endIf()` calls take
|
|
109
|
-
back:
|
|
110
|
-
|
|
111
63
|
```ts
|
|
112
|
-
const gid = s.if(ref(
|
|
113
|
-
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)) });
|
|
114
66
|
s.otherwise(gid);
|
|
115
|
-
s.action(actions.showresult, { Text: text("
|
|
67
|
+
s.action(actions.showresult, { Text: text("Hello, stranger") });
|
|
116
68
|
s.endIf(gid);
|
|
117
69
|
```
|
|
118
70
|
|
|
119
71
|
### Apple's apps
|
|
120
72
|
|
|
121
|
-
Apple's App Intents actions are in the catalogue under the Shortcuts app's own keys. Their
|
|
122
|
-
parameters are typed like the built-ins, and the `AppIntentDescriptor` every such action
|
|
123
|
-
carries in the file is added automatically:
|
|
124
|
-
|
|
125
73
|
```ts
|
|
126
74
|
const r = s.action(actions.reminders_create_reminder, { title: text("Buy milk"), priorityLevel: "high" });
|
|
127
75
|
s.action(actions.notes_create_note, { name: "Shopping", content: text("Added ", ref(r)) });
|
|
128
76
|
```
|
|
129
77
|
|
|
130
|
-
###
|
|
78
|
+
### Other apps
|
|
131
79
|
|
|
132
|
-
|
|
133
|
-
and the descriptor yourself. Parameter keys are then typed as `Record<string, Value>` and
|
|
134
|
-
passed through as given:
|
|
80
|
+
Pass the identifier and descriptor yourself; the parameters are untyped.
|
|
135
81
|
|
|
136
82
|
```ts
|
|
137
|
-
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
|
+
});
|
|
138
91
|
```
|
|
139
92
|
|
|
140
93
|
### Python
|
|
141
94
|
|
|
142
|
-
Same API
|
|
143
|
-
compile-time ones:
|
|
95
|
+
Same API in snake_case. See [`python/README.md`](python/README.md).
|
|
144
96
|
|
|
145
97
|
```python
|
|
146
98
|
from shortcutkit import Shortcut, actions, ref, text
|
|
147
99
|
|
|
148
|
-
s = Shortcut("Greeting"
|
|
149
|
-
|
|
150
|
-
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
|
+
|
|
151
104
|
s.write()
|
|
152
105
|
Shortcut.sign("Greeting.shortcut", "Greeting-signed.shortcut")
|
|
153
106
|
```
|
|
154
107
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
### Demo
|
|
158
|
-
|
|
159
|
-
Both packages ship a demo that builds a working shortcut with storage, an If/Otherwise block
|
|
160
|
-
and output:
|
|
108
|
+
## API
|
|
161
109
|
|
|
162
|
-
|
|
163
|
-
bun run demo out.shortcut
|
|
164
|
-
python -m shortcutkit demo out.shortcut
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## Documentation
|
|
168
|
-
|
|
169
|
-
- [`docs/shortcut-file-format.md`](docs/shortcut-file-format.md): the `.shortcut` format end
|
|
170
|
-
to end, every field.
|
|
171
|
-
- [`docs/builtin-actions-reference.md`](docs/builtin-actions-reference.md): all 434 built-in
|
|
172
|
-
actions with their parameters.
|
|
173
|
-
- [`docs/apple-app-intents-reference.md`](docs/apple-app-intents-reference.md): the 1,441 App
|
|
174
|
-
Intents actions of Apple's apps, by app, with parameter kinds and enumeration cases.
|
|
175
|
-
- [`docs/parameter-encodings.md`](docs/parameter-encodings.md): how each parameter class is
|
|
176
|
-
serialized.
|
|
177
|
-
- [`docs/extraction.md`](docs/extraction.md): how the data was obtained, what did not work,
|
|
178
|
-
and what remains approximate. A handful of condition codes rest on community documentation.
|
|
179
|
-
|
|
180
|
-
## Development
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
git clone https://github.com/frontboat/shortcutkit.git && cd shortcutkit
|
|
184
|
-
bun install
|
|
185
|
-
bun test # all tests
|
|
186
|
-
bun run typecheck # tsc --noEmit; also verifies the @ts-expect-error assertions in tests
|
|
187
|
-
bun run build # bundles src/ into dist/index.js for Node and emits the .d.ts files
|
|
188
|
-
|
|
189
|
-
cd python && python3 -m venv .venv && .venv/bin/pip install -e .
|
|
190
|
-
.venv/bin/python -m shortcutkit demo /tmp/Demo.shortcut
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
CI runs the type check, the tests, a Node import of the built package, a Python install and an
|
|
194
|
-
unsigned build of the demo shortcut on macOS. Signing is not exercised in CI because the runner
|
|
195
|
-
has no iCloud login.
|
|
196
|
-
|
|
197
|
-
### Repository layout
|
|
198
|
-
|
|
199
|
-
| Path | Contents |
|
|
110
|
+
| Export | What it does |
|
|
200
111
|
|---|---|
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
|
|
207
|
-
|
|
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. |
|
|
208
128
|
|
|
209
|
-
|
|
129
|
+
## Documentation
|
|
210
130
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
platform binaries. Apple's App Intents come from the Shortcuts app's action index, which
|
|
217
|
-
Shortcuts writes on launch.
|
|
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
|
|
218
136
|
|
|
219
|
-
|
|
220
|
-
bun run extract # ~10 s: loads WorkflowKit and ActionKit in osascript, dumps and serializes everything
|
|
221
|
-
bun install && bun test
|
|
222
|
-
bun run diff-data # what changed vs the committed data, and the semver bump it implies
|
|
223
|
-
bun run changelog # the same as a CHANGELOG.md section
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Both packages and the data are versioned together. A refresh that only adds actions or
|
|
227
|
-
parameters is a minor release. One that removes or retypes any is a major release, because the
|
|
228
|
-
generated catalogue is part of the type surface. Bump `package.json` and
|
|
229
|
-
`python/pyproject.toml` together and add the `bun run changelog` output to `CHANGELOG.md`.
|
|
137
|
+
## Status
|
|
230
138
|
|
|
231
|
-
|
|
232
|
-
|
|
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.
|
|
233
141
|
|
|
234
|
-
##
|
|
142
|
+
## Contributing
|
|
235
143
|
|
|
236
|
-
|
|
237
|
-
declare minimum client version 900, which any current Shortcuts accepts. The private classes
|
|
238
|
-
the tools call can change with any release; the package itself depends only on the committed
|
|
239
|
-
data. Generated shortcuts import and run.
|
|
144
|
+
Issues and pull requests are welcome. See [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
240
145
|
|
|
241
146
|
## License
|
|
242
147
|
|
|
243
|
-
|
|
148
|
+
[MIT](LICENSE).
|