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 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: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
6
  [![CI](https://img.shields.io/github/actions/workflow/status/frontboat/shortcutkit/ci.yml?branch=main)](https://github.com/frontboat/shortcutkit/actions)
7
+ [![npm](https://img.shields.io/npm/v/shortcutkit)](https://www.npmjs.com/package/shortcutkit)
8
+ [![PyPI](https://img.shields.io/pypi/v/shortcutkit)](https://pypi.org/project/shortcutkit/)
7
9
 
8
- ## About
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
- ```bash
74
- npm install shortcutkit # or: bun add shortcutkit
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
- Python:
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
- pip install shortcutkit
43
+ npm install shortcutkit # Node 20+ or Bun
44
+ pip install shortcutkit # Python 3.9+
81
45
  ```
82
46
 
83
- ### Requirements
47
+ ## Usage
84
48
 
85
- - Node 20 or newer, or Bun, for the TypeScript package. The published package is plain ES
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
- ## Usage
51
+ ### Share sheet
93
52
 
94
53
  ```ts
95
- import { Shortcut, actions, ref, text } from "shortcutkit";
54
+ import { shortcutInput } from "shortcutkit";
96
55
 
97
- const s = new Shortcut("Greeting", { color: "Teal" });
98
- const got = s.action(actions.getstoredcontent, { WFStoredContentKey: "greeting" });
99
- s.action(actions.showresult, { Text: text("Stored: ", ref(got)) });
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(got), "has_any_value");
113
- s.action(actions.showresult, { Text: text("Stored value: ", ref(got)) });
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("Nothing stored") });
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
- ### Actions from other apps
78
+ ### Other apps
131
79
 
132
- Third-party App Intents actions are not in the catalogue, so pass the identifier as a string
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.CreateNote", { title: text("Hello"), body: ref(got) });
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 and same bundled data, with snake_case names and run-time value checks instead of
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", color="Teal")
149
- got = s.action(actions.GETSTOREDCONTENT, WFStoredContentKey="greeting")
150
- s.action(actions.SHOWRESULT, Text=text("Stored: ", ref(got)))
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
- See [`python/README.md`](python/README.md) for the full Python surface.
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
- ```bash
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
- | `src/` | The TypeScript package source. `src/generated/actions.ts` is produced by the tools. `bun run build` bundles it into `dist/`, which is what npm ships. |
202
- | `python/` | The Python package. `actions.py` and `data/` are produced by the tools. |
203
- | `data/` | Everything extracted from the engine: definitions, parameter encodings, serialization table, `apple-app-intents.json` from the Shortcuts app's registry, and `provenance.json`. |
204
- | `docs/` | The format reference, action reference, encodings reference and extraction notes. |
205
- | `tools/` | The extraction pipeline. |
206
-
207
- Never hand-edit the generated files. Change the generator or the data and re-run the pipeline.
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
- ### Regenerating the data
129
+ ## Documentation
210
130
 
211
- The extracted data is committed on purpose. It can only be produced on a Mac, through private
212
- API that changes between releases, so committing it is what makes the package reproducible.
213
- To refresh after a macOS update, on any Mac that has opened Shortcuts.app at least once. No
214
- Xcode is needed: the probes are JavaScript for Automation scripts run by `osascript`, because
215
- ActionKit, which defines about a quarter of the built-in actions, only loads into Apple's own
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
- ```bash
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
- The tools also dump Apple's gallery workflows, installed apps' App Intents actions and the raw
232
- identifier list. Those files are local and gitignored.
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
- ## Status
142
+ ## Contributing
235
143
 
236
- Data extracted on macOS 27.0 with Shortcuts 10.0 (see `data/provenance.json`). Generated files
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
- shortcutkit is licensed under the MIT license. See [`LICENSE`](LICENSE) for details.
148
+ [MIT](LICENSE).