@doist/automations-cli 0.0.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 +377 -29
- package/dist/chunk-RI34UI2L.js +646 -0
- package/dist/chunk-RI34UI2L.js.map +7 -0
- package/dist/index.js +27593 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/skills/update-installed.js +7 -0
- package/dist/lib/skills/update-installed.js.map +7 -0
- package/package.json +67 -8
- package/scripts/refresh-skills.mjs +10 -0
package/README.md
CHANGED
|
@@ -1,45 +1,393 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `tda`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Terminal client for Todoist Automations. It can inspect and manage automations, drafts,
|
|
4
|
+
conversations, and runs, and it uses the same persisted agent conversations and client-managed tools
|
|
5
|
+
as the Automations frontend.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
The agent can create and edit automations, inspect existing automations and runs, troubleshoot
|
|
8
|
+
failures, and answer other automation-related questions. The CLI sends the initial message unchanged;
|
|
9
|
+
it does not assume that every conversation is an authoring request.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
## Installing
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @doist/automations-cli@internal
|
|
15
|
+
```
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
17
|
+
The `@internal` part is required. Releases are published under that dist tag and no other, so the
|
|
18
|
+
usual `npm install -g @doist/automations-cli` finds no version to install for as long as Automations
|
|
19
|
+
is invite-only. Upgrading takes the same command.
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
Signing in still needs an account with Automations access, so installing the CLI on its own grants
|
|
22
|
+
nothing.
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
## Local development
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
From the monorepo root, install dependencies and create the global link once:
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
```bash
|
|
29
|
+
npm install
|
|
30
|
+
npm run setup:cli
|
|
31
|
+
```
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
33
|
+
This builds the shared workspace packages and CLI, then creates an npm link for the `tda` executable.
|
|
34
|
+
The link is persistent. After editing `apps/cli/src`, rebuild only the CLI:
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
```bash
|
|
37
|
+
npm run build:cli
|
|
38
|
+
```
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
40
|
+
For active development, keep the bundler running instead:
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
```bash
|
|
43
|
+
npm run dev:cli
|
|
44
|
+
```
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
46
|
+
Both commands update the linked executable without recreating the link. Run `npm run build:core`
|
|
47
|
+
after editing a shared package. Run `npm unlink --global @doist/automations-cli` to remove the link.
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
A global install and a link cannot both own `tda`, so uninstall the published package first if you
|
|
50
|
+
have one.
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
## Releases
|
|
53
|
+
|
|
54
|
+
`@doist/automations-cli` is published from `main` by
|
|
55
|
+
[`.github/workflows/release-cli.yml`](../../.github/workflows/release-cli.yml), which runs
|
|
56
|
+
semantic-release with the configuration in [`release.config.js`](./release.config.js). The version
|
|
57
|
+
comes from the conventional-commit messages that touched this directory, so nothing has to be bumped
|
|
58
|
+
by hand and the repository holds no version number of its own.
|
|
59
|
+
|
|
60
|
+
The workspace libraries the CLI uses are not published, so `npm run build` compiles them into
|
|
61
|
+
`dist/` rather than resolving them at install time. Only the packages listed under `dependencies`
|
|
62
|
+
are left external. Because a release is scoped to commits under `apps/cli`, a change confined to one
|
|
63
|
+
of those libraries does not trigger a release even though it changes what gets published; it ships
|
|
64
|
+
with the next release instead. Publish it sooner by landing it together with a change here.
|
|
65
|
+
|
|
66
|
+
Once linked, `tda skill install claude-code` gives your coding agent instructions for driving the
|
|
67
|
+
CLI. See [Agent skill](#agent-skill).
|
|
68
|
+
|
|
69
|
+
## Authentication
|
|
70
|
+
|
|
71
|
+
Sign in with your Todoist account:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
tda auth login
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This opens a browser, asks Todoist to authorize the CLI, and stores the resulting token in your
|
|
78
|
+
operating system credential manager. `tda auth status` shows which account is signed in, and
|
|
79
|
+
`tda auth logout` removes the credential and tells Todoist to revoke it.
|
|
80
|
+
|
|
81
|
+
The token is minted for Automations specifically rather than for Todoist as a whole, so it grants
|
|
82
|
+
access to your automations and not to your tasks, projects or comments. The one exception is your
|
|
83
|
+
identity: signing in also asks for Todoist's `user:read` scope and reads your account id and email,
|
|
84
|
+
which is how the CLI knows who it stored the credential for. It lasts an hour and is renewed
|
|
85
|
+
automatically as commands run, so signing in is a one-off.
|
|
86
|
+
|
|
87
|
+
### Scopes
|
|
88
|
+
|
|
89
|
+
By default the CLI asks for write access to automations, connections and chat, plus permission to
|
|
90
|
+
run automations. `--read-only` asks for read access instead, which leaves `automation trigger`,
|
|
91
|
+
`chat`, and every connector change failing with a permissions error.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
tda auth login --read-only
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Scopes that no command needs yet — workspaces, notifications, settings and webhooks — are not
|
|
98
|
+
requested by default. Ask for them up front if you want them, rather than signing in a second time
|
|
99
|
+
later:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
tda auth login --additional-scopes workspaces,notifications,webhooks
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`workspaces` covers automations that live in a Todoist workspace rather than your personal space.
|
|
106
|
+
They are a separate resource with their own scopes, because a workspace holds other people's
|
|
107
|
+
automations too.
|
|
108
|
+
|
|
109
|
+
`tda auth login --help` lists them with what each one covers.
|
|
110
|
+
|
|
111
|
+
### Environments
|
|
112
|
+
|
|
113
|
+
`--env` selects which Automations deployment to talk to, and the account you sign in as is stored
|
|
114
|
+
per environment, so a staging login never leaks into a production command:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
tda --env staging auth login
|
|
118
|
+
tda --env staging automation list
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`prod` is the default. `TDA_ENV` sets it for a shell session. `--api-url` (or `TDA_API_URL`)
|
|
122
|
+
overrides just the API address, for a proxy or a local backend, and takes precedence over `--env`.
|
|
123
|
+
|
|
124
|
+
Signing in against `dev` needs the local backend running with `APP_URL=https://local-agentist.todoist.com`.
|
|
125
|
+
On its default of `http://localhost:24368`, Todoist cannot fetch the document that identifies the
|
|
126
|
+
CLI, so the login fails.
|
|
127
|
+
|
|
128
|
+
### Personal API tokens
|
|
129
|
+
|
|
130
|
+
A Todoist personal API token still works, and takes over when no account is signed in:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
export TODOIST_API_TOKEN="<your Todoist personal API token>"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A signed-in account wins over the variable, so logging in does not require unsetting it. The
|
|
137
|
+
variable is useful for CI and scripts, where opening a browser is not an option.
|
|
138
|
+
|
|
139
|
+
### Where the credential is stored
|
|
140
|
+
|
|
141
|
+
By default, only in your operating system credential manager. On a machine without one — a
|
|
142
|
+
container, a headless host, some WSL setups — login fails rather than quietly writing the token to
|
|
143
|
+
a file. Use `--credential-store` to choose deliberately:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
tda auth login --credential-store fallback # keyring if available, config file if not
|
|
147
|
+
tda auth login --credential-store plaintext # config file, always
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`plaintext` always writes the token to `~/.config/tda/config.json`; `fallback` does so only when no
|
|
151
|
+
keyring is available, and otherwise behaves like the default. That file is readable by anything
|
|
152
|
+
running as you, so it is worth knowing which one you get. It is created `0600`.
|
|
153
|
+
|
|
154
|
+
### Known limitation
|
|
155
|
+
|
|
156
|
+
A `tda chat` turn starts with at least ten minutes of token life, which covers any normal turn. A
|
|
157
|
+
turn running longer than that fails partway through, because the connection carries the token it
|
|
158
|
+
opened with. Re-run `tda chat continue` to resume. Fixing this properly means the SDK resolving the
|
|
159
|
+
token per request rather than holding the one it was built with.
|
|
160
|
+
|
|
161
|
+
## Inspecting resources
|
|
162
|
+
|
|
163
|
+
Every resource has `list` and `view` commands. Use `--json` for complete machine-readable output:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
tda --json automation list --all
|
|
167
|
+
tda --json automation view <automation-id>
|
|
168
|
+
|
|
169
|
+
tda --json draft list --all
|
|
170
|
+
tda --json draft view <draft-id>
|
|
171
|
+
|
|
172
|
+
tda --json conversation list --status all --limit 200
|
|
173
|
+
tda --json conversation view <conversation-id>
|
|
174
|
+
|
|
175
|
+
tda --json run list <automation-id> --status failed --all
|
|
176
|
+
tda --json run list <automation-id> --status failed --since 2026-08-01 --until 2026-08-07
|
|
177
|
+
tda --json run view <automation-id> <run-id>
|
|
178
|
+
tda --json run view <automation-id> <run-id> --full
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Automation, draft, and run lists support `--limit`, `--cursor`, and `--all`. `--limit` controls the
|
|
182
|
+
total number of returned resources; `--all` removes that limit. JSON responses retain the API's
|
|
183
|
+
`data` and `nextCursor` fields. NDJSON emits one resource per line and adds a final
|
|
184
|
+
`{"_meta":true,"nextCursor":"..."}` record when another page is available.
|
|
185
|
+
|
|
186
|
+
`run list` filters on when a run was created. `--since` and `--until` each take a full ISO timestamp
|
|
187
|
+
(`2026-08-01T09:00:00Z`) or a plain `YYYY-MM-DD` date, and both bounds are inclusive: a plain date
|
|
188
|
+
covers that entire UTC day, so `--since 2026-08-01 --until 2026-08-07` returns everything from the
|
|
189
|
+
start of the 1st to the end of the 7th. Either bound can be used on its own; with only `--since`,
|
|
190
|
+
the range runs up to now.
|
|
191
|
+
|
|
192
|
+
`conversation view` returns the persisted conversation and its raw messages. `chat status` remains
|
|
193
|
+
focused on resumable agent state, including pending client actions.
|
|
194
|
+
|
|
195
|
+
To investigate a failing automation, an agent can list automations, inspect the selected automation,
|
|
196
|
+
list its failed runs, and inspect one failed run. Run details default to a compact diagnostic view
|
|
197
|
+
with failure, step, and tool-call errors. Use `run view --full` when raw step results and tool payloads
|
|
198
|
+
are needed.
|
|
199
|
+
|
|
200
|
+
## Managing automations
|
|
201
|
+
|
|
202
|
+
Enable or disable an automation, or start a live run:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
tda automation enable <automation-id>
|
|
206
|
+
tda automation disable <automation-id>
|
|
207
|
+
tda automation trigger <automation-id>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`automation trigger` only supports scheduled automations. It starts a live run and exits after the
|
|
211
|
+
backend accepts it; it does not wait for the run to finish. API triggers are not supported because
|
|
212
|
+
the command cannot provide their input payload. Webhook automations run through their configured
|
|
213
|
+
event.
|
|
214
|
+
|
|
215
|
+
Replace an automation's name and description together:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
tda automation update <automation-id> \
|
|
219
|
+
--name "Daily summary" \
|
|
220
|
+
--description "Creates a morning summary"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Replace its schedule with either an interval or a five-part cron expression:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
tda automation schedule <automation-id> --interval "45m"
|
|
227
|
+
|
|
228
|
+
tda automation schedule <automation-id> \
|
|
229
|
+
--cron "0 9 * * *" \
|
|
230
|
+
--timezone "America/Santiago"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Cron schedules require an IANA timezone. `--cron` and `--interval` are mutually exclusive, and
|
|
234
|
+
natural-language values such as `every day at 9am` are not supported.
|
|
235
|
+
|
|
236
|
+
These explicit mutation commands do not prompt for confirmation and do not support `--dry-run`.
|
|
237
|
+
Use the global `--json` or `--ndjson` option for structured results.
|
|
238
|
+
|
|
239
|
+
## Conversations
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
tda --ndjson chat start \
|
|
243
|
+
"Why did my daily summary automation fail yesterday?"
|
|
244
|
+
|
|
245
|
+
tda --ndjson chat continue <conversation-id> \
|
|
246
|
+
"Inspect its most recent run"
|
|
247
|
+
|
|
248
|
+
tda --json chat status <conversation-id>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Creating an automation is another kind of conversation. State the desired publication behavior in
|
|
252
|
+
the message:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
tda chat start "Create and publish an automation that reschedules overdue tasks every night"
|
|
256
|
+
|
|
257
|
+
tda chat start \
|
|
258
|
+
"Create an automation that reschedules overdue tasks every night, but leave it as a draft"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The CLI does not add publication or draft instructions to these messages.
|
|
262
|
+
|
|
263
|
+
## Agent usage
|
|
264
|
+
|
|
265
|
+
The CLI streams one durable server turn and exits. In NDJSON mode, `turn_started` means the process is
|
|
266
|
+
still active, and silence produces periodic `turn_progress` heartbeats. Keep monitoring the same
|
|
267
|
+
process until both of these are true:
|
|
268
|
+
|
|
269
|
+
1. It emits `turn_completed` with `terminal: true`.
|
|
270
|
+
2. The process exits.
|
|
271
|
+
|
|
272
|
+
Only then should an agent invoke `tda chat continue` with the emitted conversation ID. A local
|
|
273
|
+
per-conversation lock rejects a concurrent turn and reports the PID that is already active. The lock
|
|
274
|
+
protects processes on the same machine; it is not a distributed backend lock.
|
|
275
|
+
|
|
276
|
+
In a human terminal without `--json`, `--ndjson`, or `--no-interactive`, the command remains open as
|
|
277
|
+
an interactive conversation.
|
|
278
|
+
|
|
279
|
+
```ndjson
|
|
280
|
+
{"type":"turn_started","conversationId":"...","state":"running","terminal":false}
|
|
281
|
+
{"type":"turn_progress","conversationId":"...","state":"running","terminal":false,"elapsedSeconds":30}
|
|
282
|
+
{"type":"turn_completed","conversationId":"...","state":"completed","terminal":true,"outcome":"awaiting_input","resumeCommand":"tda chat continue ..."}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
If a conversation publishes an automation, `tda` archives the conversation before emitting the
|
|
286
|
+
terminal `turn_completed` event. Successful machine output includes `conversation_archived` and
|
|
287
|
+
`conversationArchived: true`. If publication succeeds but archival fails, the CLI emits a
|
|
288
|
+
`TDA_CHAT_ARCHIVE_FAILED` warning and completes with `conversationArchived: false`; agents must not
|
|
289
|
+
retry publication in response to that warning.
|
|
290
|
+
|
|
291
|
+
## Connections
|
|
292
|
+
|
|
293
|
+
Discover every service supported by Automations and include the current user's connection status:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
tda connector list
|
|
297
|
+
tda connector list --query github
|
|
298
|
+
tda connector list --capability mcp
|
|
299
|
+
tda connector list --connection-status authorized
|
|
300
|
+
tda connector list --capability trigger --connection-status expired
|
|
301
|
+
tda connector list --without-connections
|
|
302
|
+
tda connector list --no-connections
|
|
303
|
+
tda connector view github
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
`--capability` filters connectors by what they support but does not remove other capabilities or
|
|
307
|
+
connections from the output. When combined with `--connection-status`, both conditions must match
|
|
308
|
+
the same connection. `--without-connections` returns only connectors for which the user has no
|
|
309
|
+
connection records. `--no-connections` skips fetching connection data and omits the `connections`
|
|
310
|
+
field; it cannot be combined with connection filters.
|
|
311
|
+
|
|
312
|
+
List the published automations that require a connector:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
tda connector automations github
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Start a connection directly, or resume and repair an existing setup:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
tda connector connect github --capability mcp
|
|
322
|
+
tda connector wait <connection-id>
|
|
323
|
+
tda connector repair <connection-id>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
OAuth setup opens in the default browser. Managed connectors print their instructions and setup
|
|
327
|
+
code. Use the global `--no-open` option to print setup links without opening them.
|
|
328
|
+
|
|
329
|
+
Disconnecting is preview-only unless `--yes` is present. The CLI refuses to disconnect an exact
|
|
330
|
+
connection used by any enabled automation. Disabled automations do not block disconnection, but they
|
|
331
|
+
will need a valid connection before they can run again.
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
tda connector disconnect <connection-id>
|
|
335
|
+
tda connector disconnect <connection-id> --yes
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
The dependency check currently runs in the CLI before deletion and is not atomic with the backend
|
|
339
|
+
operation. Backend enforcement is tracked in
|
|
340
|
+
[#3928](https://github.com/Doist/automations/issues/3928).
|
|
341
|
+
|
|
342
|
+
### Connections requested by chat
|
|
343
|
+
|
|
344
|
+
The backend and CLI automatically reuse the sole authorized account for a requested service. When
|
|
345
|
+
more than one authorized account exists, machine mode emits an `action_required` event containing
|
|
346
|
+
the available connection IDs. Resolve it with:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
tda --ndjson chat continue <conversation-id> \
|
|
350
|
+
--connection slack:mcp=<connection-id>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Repeat `--connection` when several service choices are required.
|
|
354
|
+
|
|
355
|
+
When no authorized account exists, the CLI creates a connection and waits for setup to complete. For
|
|
356
|
+
OAuth connectors it prints the authorization URL and opens it in the default browser. For managed
|
|
357
|
+
connectors it prints the setup instructions and code, and opens the deep link when one is available.
|
|
358
|
+
Machine mode emits `connection_authorization_started`, periodic
|
|
359
|
+
`connection_authorization_waiting`, and `connection_authorized` events while the same process stays
|
|
360
|
+
alive. Pending setup is reused if the command is interrupted and resumed, avoiding duplicate
|
|
361
|
+
connections.
|
|
362
|
+
|
|
363
|
+
Use `--no-open` to print OAuth and managed deep-link URLs without trying to launch a browser:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
tda --no-open --ndjson chat start "Help me troubleshoot my Slack automation"
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Agent skill
|
|
370
|
+
|
|
371
|
+
`tda` ships a skill document that teaches a coding agent how to drive it: which command answers which
|
|
372
|
+
question, what each flag really does, the chat turn protocol, and the error codes worth branching on.
|
|
373
|
+
Install it for whichever agents you use:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
tda skill install claude-code # writes ~/.claude/skills/automations-cli/SKILL.md
|
|
377
|
+
tda skill install claude-code --local # writes ./.claude/skills/automations-cli/SKILL.md
|
|
378
|
+
tda skill list
|
|
379
|
+
tda skill update all
|
|
380
|
+
tda skill uninstall claude-code
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
`tda skill list` shows every supported agent (`claude-code`, `codex`, `copilot`, `cursor`, `gemini`,
|
|
384
|
+
`pi`, and `universal` for `~/.agents`) along with whether its skill file is missing, current, or
|
|
385
|
+
outdated. A global install refuses when the agent's home directory does not exist, so a typo cannot
|
|
386
|
+
create a directory nothing reads; use `--local` or the `universal` target instead. Already-installed
|
|
387
|
+
skill files are rewritten by `npm run build:cli`, which is when the document can change, so a rebuilt
|
|
388
|
+
CLI never leaves an agent reading instructions that no longer match it. `tda skill update all` does
|
|
389
|
+
the same on demand.
|
|
390
|
+
|
|
391
|
+
The document's source is `src/lib/skills/content.ts`, and `skills/automations-cli/SKILL.md` is the
|
|
392
|
+
generated copy committed for review. Edit the former and run `npm run sync:skill`; `npm run check`
|
|
393
|
+
fails when they diverge.
|