@junoflow/cli 0.15.0 → 0.17.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
@@ -45,11 +45,24 @@ writes the descriptions and classifies each command's effect.
45
45
 
46
46
  ### Help
47
47
 
48
- | Command | Shows |
49
- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
50
- | `juno --help` | the groups and top-level commands. Offline. |
51
- | `juno <group> --help` | the group's commands, each with its effect and the first sentence of its description. Offline, it lists the commands alone. |
52
- | `juno <group> <verb> --help` | the tool's full description, its effect, its input schema, and what the positional and flags fill in. Needs the instance. |
48
+ | Command | Shows |
49
+ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
50
+ | `juno --help` (or `juno`) | the guide to working with Juno, every group and top-level command, and the skill index. See below. |
51
+ | `juno <group> --help` | the skills to read first, then the group's commands, each with its effect and the first sentence of its description. Offline, it lists the commands alone. |
52
+ | `juno <group> <verb> --help` | the skills to read first, the tool's full description, its effect, its input schema, and what the positional and flags fill in. Needs the instance. |
53
+
54
+ `juno --help` is where an agent starts, with nothing installed: after the usage
55
+ lines comes the instance's guide (what Juno is made of, how to work with it from
56
+ the CLI, what only the user does), then each group and top-level command by one
57
+ line (the working-copy commands are in their group's help), and the skills
58
+ `juno skill <name>` prints, each by its first sentence. The guide and the skills
59
+ come from the instance; offline the rest is still printed, with one line on how
60
+ to connect and the reason on stderr. Bare `juno` prints the same and exits 0.
61
+
62
+ A group's or a command's help starts with a `Read first:` line when the instance
63
+ names skills for it — `Read first: juno skill diagnose state` above
64
+ `juno state --help`'s commands. Run it before the command: one call prints them
65
+ all.
53
66
 
54
67
  `juno <group>` on its own prints the group's help and exits 2. An unknown
55
68
  command suggests the closest one: `juno stat get` answers
@@ -65,9 +78,9 @@ Every command has one effect:
65
78
  - **act** does something now (calls a service, presses a button, sets a value,
66
79
  starts a run, executes code), with nothing to roll back to.
67
80
 
68
- `config set` and `agent-skill install` are **local**: they write a file on your
69
- machine and change nothing on the instance. So is `app dev`, which serves an app
70
- working copy on localhost and never connects to an instance.
81
+ `config set` is **local**: it writes a file on your machine and changes nothing
82
+ on the instance. So is `app dev`, which serves an app working copy on localhost
83
+ and never connects to an instance.
71
84
 
72
85
  No command of a lower effect is a string prefix of a command of a higher one.
73
86
  Claude Code matches a Bash permission rule by prefix, so an allowlist can list
@@ -86,27 +99,32 @@ positional; its input is a whole document (`device write`) or has no id
86
99
  | --------------------------------------- | ------------- | ----------------------------------- | ------ |
87
100
  | `juno discover <query> [--kind k]…` | `discover` | `query`; `--kind` → `kinds` | read |
88
101
  | `juno related <kind>:<id> [--degree n]` | `get_related` | `kind`, `id`; `--degree` → `degree` | read |
89
- | `juno skill [<name>] [--json]` | none | skill name | read |
102
+ | `juno skill [<name>...] [--json]` | none | skill name | read |
90
103
  | `juno ask ["<prompt>"]` | none | the prompt | act |
91
- | `juno agent-skill install` | none | none | local |
104
+ | `juno whoami` | none | none | read |
105
+ | `juno version` | none | none | read |
106
+ | `juno permissions` | none | none | read |
92
107
 
93
108
  `related` splits its positional on the first `:` only, so
94
109
  `endpoint:ha:light.hallway` is kind `endpoint`, id `ha:light.hallway`.
95
110
 
96
111
  **`state`**
97
112
 
98
- | Command | Tool | Positional | Effect |
99
- | --------------------------- | ------------------------------------- | ---------- | ------ |
100
- | `juno state get` | `get_state` | `id` | read |
101
- | `juno state get-history` | `get_state_history` | `id` | read |
102
- | `juno state get-layers` | `get_state_layers` | `id` | read |
103
- | `juno state evaluate-level` | `evaluate_level` | — | read |
104
- | `juno state preview-layers` | `edit_state_layers`, `dry_run: true` | `id` | read |
105
- | `juno state create` | `create_state` | `id` | change |
106
- | `juno state edit-layers` | `edit_state_layers`, `dry_run: false` | `id` | change |
107
- | `juno state set-value` | `set_state_value` | `id` | act |
108
- | `juno state claim` | `claim_state` | `id` | act |
109
- | `juno state release` | `release_state` | `id` | act |
113
+ | Command | Tool | Positional | Effect |
114
+ | -------------------------------- | ------------------------------------- | ---------- | ------ |
115
+ | `juno state get` | `get_state` | `id` | read |
116
+ | `juno state get-history` | `get_state_history` | `id` | read |
117
+ | `juno state get-layers` | `get_state_layers` | `id` | read |
118
+ | `juno state evaluate-level` | `evaluate_level` | — | read |
119
+ | `juno state preview-layers` | `edit_state_layers`, `dry_run: true` | `id` | read |
120
+ | `juno state list-layer-versions` | `list_state_layer_versions` | `id` | read |
121
+ | `juno state create` | `create_state` | `id` | change |
122
+ | `juno state edit-layers` | `edit_state_layers`, `dry_run: false` | `id` | change |
123
+ | `juno state restore-layers` | `restore_state_layers` | `id` | change |
124
+ | `juno state set-value` | `set_state_value` | `id` | act |
125
+ | `juno state claim` | `claim_state` | `id` | act |
126
+ | `juno state release` | `release_state` | `id` | act |
127
+ | `juno state delete` | `delete_state` | `id` | act |
110
128
 
111
129
  `preview-layers` and `edit-layers` call the same tool. The preview always sends
112
130
  `dry_run: true`, so a permission rule can allow the preview and not the edit; an
@@ -114,12 +132,23 @@ positional; its input is a whole document (`device write`) or has no id
114
132
 
115
133
  **`flow`**
116
134
 
117
- | Command | Tool | Positional | Effect |
118
- | ------------------------ | ------------------ | ---------- | ------ |
119
- | `juno flow get` | `get_flow` | `id` | read |
120
- | `juno flow set-enabled` | `set_flow_enabled` | `flow_id` | change |
121
- | `juno flow trigger` | `trigger_flow` | `flow_id` | act |
122
- | `juno flow fire-trigger` | `fire_trigger` | `flow_id` | act |
135
+ | Command | Tool | Positional | Effect |
136
+ | --------------------------- | ---------------------- | ---------- | ------ |
137
+ | `juno flow get` | `get_flow` | `id` | read |
138
+ | `juno flow list-versions` | `list_flow_versions` | `id` | read |
139
+ | `juno flow get-next-fire` | `get_next_fire` | `flow_id` | read |
140
+ | `juno flow restore-version` | `restore_flow_version` | `id` | change |
141
+ | `juno flow set-enabled` | `set_flow_enabled` | `flow_id` | change |
142
+ | `juno flow rename` | `rename_flow` | `id` | change |
143
+ | `juno flow trigger` | `trigger_flow` | `flow_id` | act |
144
+ | `juno flow fire-trigger` | `fire_trigger` | `flow_id` | act |
145
+ | `juno flow delete` | `delete_flow` | `id` | act |
146
+
147
+ Every `restore-version` row, and `state restore-layers`, takes the version as
148
+ `--version N`, numbered as its `list-versions` row lists them. `flow rename`
149
+ takes `--slug` (the new id) and `--name`, either or both, and answers the flows
150
+ whose targets named the old id as `broken_callers`. `get-next-fire` takes
151
+ `--trigger` to answer one schedule trigger rather than all of the flow's.
123
152
 
124
153
  The group's working-copy commands (`flow init`, `gen-types`, `check`, `deploy`,
125
154
  `pull`) are in [Flows as files](#flows-as-files).
@@ -131,14 +160,47 @@ The group's working-copy commands (`flow init`, `gen-types`, `check`, `deploy`,
131
160
  | `juno run list` | `list_runs` | — | read |
132
161
  | `juno run get` | `get_run` | `run_id` | read |
133
162
 
163
+ `run get --follow` streams the run's steps to stderr as they finish, then prints
164
+ the run as `run get` does once it has ended. Ctrl-C stops following (exit 130);
165
+ the run goes on.
166
+
167
+ **`timer`** and **`activity`**
168
+
169
+ | Command | Tool | Positional | Effect |
170
+ | -------------------- | --------------- | ---------- | ------ |
171
+ | `juno timer list` | `list_timers` | — | read |
172
+ | `juno activity list` | `list_activity` | — | read |
173
+
174
+ `timer list` is the Upcoming page: every flow schedule trigger and integration
175
+ schedule, soonest first. `activity list` is the dashboard's run timeline, the
176
+ last `--hours` (24), of every flow or `--flow <id>`, the newest `--limit` (200).
177
+
134
178
  **`integration`**
135
179
 
136
- | Command | Tool | Positional | Effect |
137
- | ------------------------------- | -------------------------- | ------------- | ------ |
138
- | `juno integration get` | `get_integration` | `id` | read |
139
- | `juno integration configure` | `configure_integration` | `id` | change |
140
- | `juno integration install` | `install_integration` | — | act |
141
- | `juno integration run-schedule` | `run_integration_schedule` | `integration` | act |
180
+ | Command | Tool | Positional | Effect |
181
+ | ----------------------------------- | ------------------------------ | ------------- | ------ |
182
+ | `juno integration get` | `get_integration` | `id` | read |
183
+ | `juno integration list-versions` | `list_integration_versions` | `id` | read |
184
+ | `juno integration preview` | `preview_integration` | — | read |
185
+ | `juno integration list-invocations` | `list_integration_invocations` | `id` | read |
186
+ | `juno integration get-deliveries` | `get_integration_deliveries` | `id` | read |
187
+ | `juno integration get-usage` | `get_integration_usage` | `id` | read |
188
+ | `juno integration list-registry` | `list_registry` | — | read |
189
+ | `juno integration configure` | `configure_integration` | `id` | change |
190
+ | `juno integration restore-version` | `restore_integration_version` | `id` | change |
191
+ | `juno integration set-enabled` | `set_integration_enabled` | `id` | change |
192
+ | `juno integration bind-webhook` | `bind_integration_webhook` | `id` | change |
193
+ | `juno integration install` | `install_integration` | — | act |
194
+ | `juno integration invoke-action` | `invoke_integration_action` | `id` | act |
195
+ | `juno integration run-schedule` | `run_integration_schedule` | `integration` | act |
196
+ | `juno integration upgrade` | `upgrade_integration` | `id` | act |
197
+ | `juno integration uninstall` | `uninstall_integration` | `id` | act |
198
+
199
+ `upgrade` takes the new tag or commit as `--ref`; `preview` and `install` take
200
+ their `{url, ref?, path?}` as `--input`. `invoke-action` takes the action as
201
+ `--action` and its params as `--input '{"params": {…}}'`; `bind-webhook` takes
202
+ `--slot` and `--webhook`; `list-invocations` and `get-deliveries` take
203
+ `--limit`.
142
204
 
143
205
  The group's working-copy commands (`integration init`, `gen-types`,
144
206
  `verify-types`, `check`, `deploy`, `pull`) are in
@@ -146,22 +208,53 @@ The group's working-copy commands (`integration init`, `gen-types`,
146
208
 
147
209
  **`device`**
148
210
 
149
- | Command | Tool | Positional | Effect |
150
- | --------------------- | ---------------- | ---------- | ------ |
151
- | `juno device list` | `list_devices` | — | read |
152
- | `juno device get` | `get_device` | `id` | read |
153
- | `juno device preview` | `preview_device` | — | read |
154
- | `juno device write` | `write_device` | — | change |
155
- | `juno device suspend` | `suspend_device` | `id` | act |
156
- | `juno device release` | `release_device` | `id` | act |
211
+ | Command | Tool | Positional | Effect |
212
+ | ----------------------------- | ------------------------ | ---------- | ------ |
213
+ | `juno device list` | `list_devices` | — | read |
214
+ | `juno device get` | `get_device` | `id` | read |
215
+ | `juno device preview` | `preview_device` | — | read |
216
+ | `juno device list-versions` | `list_device_versions` | `id` | read |
217
+ | `juno device write` | `write_device` | — | change |
218
+ | `juno device restore-version` | `restore_device_version` | `id` | change |
219
+ | `juno device suspend` | `suspend_device` | `id` | act |
220
+ | `juno device release` | `release_device` | `id` | act |
221
+ | `juno device delete` | `delete_device` | `id` | act |
157
222
 
158
223
  **`control`**
159
224
 
160
- | Command | Tool | Positional | Effect |
161
- | ---------------------- | ----------------- | ---------- | ------ |
162
- | `juno control write` | `write_control` | — | change |
163
- | `juno control use` | `use_control` | `id` | act |
164
- | `juno control release` | `release_control` | `id` | act |
225
+ | Command | Tool | Positional | Effect |
226
+ | ------------------------------ | ------------------------- | ---------- | ------ |
227
+ | `juno control get` | `get_control` | `id` | read |
228
+ | `juno control list` | `list_controls` | — | read |
229
+ | `juno control get-history` | `get_control_history` | `id` | read |
230
+ | `juno control get-activity` | `get_control_activity` | `id` | read |
231
+ | `juno control list-versions` | `list_control_versions` | `id` | read |
232
+ | `juno control write` | `write_control` | — | change |
233
+ | `juno control restore-version` | `restore_control_version` | `id` | change |
234
+ | `juno control use` | `use_control` | `id` | act |
235
+ | `juno control extend` | `extend_control` | `id` | act |
236
+ | `juno control release` | `release_control` | `id` | act |
237
+ | `juno control delete` | `delete_control` | `id` | act |
238
+
239
+ **`app`**
240
+
241
+ | Command | Tool | Positional | Effect |
242
+ | --------------------------- | ---------------------- | ---------- | ------ |
243
+ | `juno app get` | `get_app` | `id` | read |
244
+ | `juno app list-invocations` | `list_app_invocations` | `id` | read |
245
+ | `juno app list-versions` | `list_app_versions` | `id` | read |
246
+ | `juno app list-links` | `list_app_links` | `id` | read |
247
+ | `juno app restore-version` | `restore_app_version` | `id` | change |
248
+ | `juno app set-enabled` | `set_app_enabled` | `id` | change |
249
+ | `juno app create-link` | `create_app_link` | `id` | act |
250
+ | `juno app revoke-link` | `revoke_app_link` | `id` | act |
251
+ | `juno app delete` | `delete_app` | `id` | act |
252
+
253
+ An app link lets whoever holds it use the app without logging in. `create-link`
254
+ takes `--name` and `--expires-at` (RFC 3339); `revoke-link` takes `--link <id>`
255
+ and waits for a person, as a delete does. Each link is answered with its `url`,
256
+ never its bare token. The working-copy commands are in
257
+ [Working copies](#working-copies).
165
258
 
166
259
  **`finding`**
167
260
 
@@ -186,17 +279,35 @@ for a keyed finding; `get-history` also takes `--limit`.
186
279
  | `juno webhook get-deliveries` | `get_webhook_deliveries` | `id` | read |
187
280
  | `juno webhook write` | `write_webhook` | `id` | change |
188
281
  | `juno webhook fire` | `fire_webhook` | `id` | act |
282
+ | `juno webhook delete` | `delete_webhook` | `id` | act |
283
+ | `juno webhook rotate-token` | `rotate_webhook_token` | `id` | act |
189
284
 
190
285
  **`task`** (tasks)
191
286
 
192
- | Command | Tool | Positional | Effect |
193
- | ------------------------------------- | --------------- | ---------- | ------ |
194
- | `juno task list` | `list_tasks` | — | read |
195
- | `juno task get-runs` | `get_task_runs` | `id` | read |
196
- | `juno task write` | `write_task` | `id` | change |
197
- | `juno task delete` | `delete_task` | `id` | act |
198
- | `juno task run [<url>] -- <command>…` | none | ping URL | act |
199
- | `juno task ping [<url>]` | none | ping URL | act |
287
+ | Command | Tool | Positional | Effect |
288
+ | ------------------------------------- | --------------------- | ---------- | ------ |
289
+ | `juno task list` | `list_tasks` | — | read |
290
+ | `juno task get-runs` | `get_task_runs` | `id` | read |
291
+ | `juno task get-occurrence` | `get_task_occurrence` | `id` | read |
292
+ | `juno task inbox` | `list_task_inbox` | — | read |
293
+ | `juno task write` | `write_task` | `id` | change |
294
+ | `juno task delete` | `delete_task` | `id` | act |
295
+ | `juno task rotate-token` | `rotate_task_token` | `id` | act |
296
+ | `juno task open` | `command_task` | `id` | act |
297
+ | `juno task snooze` | `command_task` | `id` | act |
298
+ | `juno task set-due` | `command_task` | `id` | act |
299
+ | `juno task complete` | `command_task` | `id` | act |
300
+ | `juno task skip` | `command_task` | `id` | act |
301
+ | `juno task cancel` | `command_task` | `id` | act |
302
+ | `juno task run [<url>] -- <command>…` | none | ping URL | act |
303
+ | `juno task ping [<url>]` | none | ping URL | act |
304
+
305
+ The six verb commands name the work with `--key` (the schedule's when omitted)
306
+ and take their verb's fields as flags: `open` `--at` `--within` `--reason`,
307
+ `snooze` `--until`, `set-due` `--at`, `complete` `--outcome` `--note` and
308
+ `--check <n>` (repeatable; the checklist numbers `get-occurrence` shows), `skip`
309
+ `--note`, `cancel` `--reason`. `inbox` is the people's due work, `mine` only for
310
+ a signed-in user linked to a person.
200
311
 
201
312
  **`series`**
202
313
 
@@ -206,18 +317,35 @@ for a keyed finding; `get-history` also takes `--limit`.
206
317
 
207
318
  **`credential`**
208
319
 
209
- | Command | Tool | Positional | Effect |
210
- | -------------------------- | ------------------ | ------------- | ------ |
211
- | `juno credential list` | `list_credentials` | — | read |
212
- | `juno credential set <id>` | none | credential id | act |
320
+ | Command | Tool | Positional | Effect |
321
+ | -------------------------- | ------------------- | ------------- | ------ |
322
+ | `juno credential list` | `list_credentials` | — | read |
323
+ | `juno credential delete` | `delete_credential` | `id` | act |
324
+ | `juno credential set <id>` | none | credential id | act |
213
325
 
214
326
  **`person`**
215
327
 
216
- | Command | Tool | Positional | Effect |
217
- | ------------------- | -------------- | ---------- | ------ |
218
- | `juno person list` | `list_people` | — | read |
219
- | `juno person get` | `get_person` | `id` | read |
220
- | `juno person write` | `write_person` | `id` | change |
328
+ | Command | Tool | Positional | Effect |
329
+ | -------------------------- | --------------- | ---------- | ------ |
330
+ | `juno person list` | `list_people` | — | read |
331
+ | `juno person get` | `get_person` | `id` | read |
332
+ | `juno person write` | `write_person` | `id` | change |
333
+ | `juno person test-contact` | `test_contact` | `id` | act |
334
+ | `juno person delete` | `delete_person` | `id` | act |
335
+
336
+ `test-contact` takes `--contact <id>` and really sends the test message.
337
+
338
+ **`approval`**
339
+
340
+ | Command | Tool | Positional | Effect |
341
+ | ------------------------- | ---------------- | ----------- | ------ |
342
+ | `juno approval list` | `list_approvals` | — | read |
343
+ | `juno approval get` | `get_approval` | `id` | read |
344
+ | `juno approval wait <id>` | none | approval id | read |
345
+
346
+ `list` takes `--status` (`pending`, `executed`, `denied`, …). There is no
347
+ command that approves one: see
348
+ [Destructive commands wait for a person](#destructive-commands-wait-for-a-person).
221
349
 
222
350
  **`ha`** (Home Assistant)
223
351
 
@@ -226,8 +354,22 @@ for a keyed finding; `get-history` also takes `--limit`.
226
354
  | `juno ha get-entity` | `get_entity` | `entity_id` | read |
227
355
  | `juno ha get-entity-history` | `get_entity_history` | `entity_id` | read |
228
356
  | `juno ha list-services` | `list_services` | — | read |
357
+ | `juno ha list-entities` | `list_entities` | — | read |
358
+ | `juno ha get-diagnostics` | `get_ha_diagnostics` | — | read |
229
359
  | `juno ha call-service` | `call_service` | — | act |
230
360
 
361
+ `list-entities` takes `--query` and `--limit`.
362
+
363
+ **`mqtt`**
364
+
365
+ | Command | Tool | Positional | Effect |
366
+ | --------------------------- | ---------------------- | ---------- | ------ |
367
+ | `juno mqtt list-topics` | `list_topics` | — | read |
368
+ | `juno mqtt get-diagnostics` | `get_mqtt_diagnostics` | — | read |
369
+
370
+ `list-topics` takes `--query` (a topic filter with `+`/`#`, or a substring),
371
+ `--sort` (`recent`, `topic`, `count`) and `--limit`.
372
+
231
373
  **`script`**
232
374
 
233
375
  | Command | Tool | Positional | Effect |
@@ -241,10 +383,18 @@ under `files` by its basename and makes it the `entry`.
241
383
 
242
384
  | Command | Tool | Positional | Effect |
243
385
  | ----------------------------- | ---- | ------------- | ------ |
386
+ | `juno session list` | none | none | read |
387
+ | `juno session get <id>` | none | session id | read |
388
+ | `juno session export <id>` | none | session id | read |
389
+ | `juno session rate <id>` | none | session id | change |
244
390
  | `juno session approve <id>` | none | session id | act |
391
+ | `juno session cancel <id>` | none | session id | act |
245
392
  | `juno config set url <url>` | none | `url <url>` | local |
246
393
  | `juno config set token <key>` | none | `token <key>` | local |
247
394
 
395
+ `session list` takes `--rating`, `--profile` and `--limit`; `session get` takes
396
+ `--transcript`; `session rate` takes `--rating up|down|addressed|none`.
397
+
248
398
  There is no command that calls a tool by name. One permission rule for such a
249
399
  command would allow every act, so each tool has its own command instead, and a
250
400
  tool added to the server needs a CLI release to reach.
@@ -283,6 +433,10 @@ the same bytes. A tool that fails in its own terms answers `{"error": "…"}`;
283
433
  that is printed on stdout like any answer, and the exit code is 1. Warnings,
284
434
  progress and notes go to standard error.
285
435
 
436
+ When the instance refuses a call — that `{"error": "…"}`, or input the tool
437
+ rejects (exit 2) — and a skill explains the tool, the last line on standard
438
+ error names it: `see: juno skill state`. Read it before trying again.
439
+
286
440
  `--jq <expr>` runs the answer through [`jq`](https://jqlang.org), which must be
287
441
  on `PATH` (`jq -c` off a terminal, plain `jq` on one). A filter given this way
288
442
  stays inside the command's own permission rule, where a pipe to `jq` would need
@@ -292,19 +446,44 @@ its own. A missing `jq` or an expression `jq` rejects exits 2.
292
446
 
293
447
  One table for the whole CLI, the working-copy commands included:
294
448
 
295
- | Code | When |
296
- | ---- | ---------------------------------------------------------------------------------------------------- |
297
- | 0 | Success |
298
- | 1 | A tool answer with a top-level string `error`, a failed check or deploy, or any other failure |
299
- | 2 | Usage: an unknown command or flag, bad `--input`, input the instance rejects, a bad `--jq` |
300
- | 3 | The instance is unreachable |
301
- | 4 | A working-copy `deploy` or `check`, any kind: the instance changed since the working copy was pulled |
302
- | 5 | `juno ask` / `session approve`: the turn is still running |
303
- | 6 | `juno ask` / `session approve`: the assistant is waiting for an answer or an approval |
449
+ | Code | When |
450
+ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
451
+ | 0 | Success |
452
+ | 1 | A tool answer with a top-level string `error`, a failed check or deploy, or any other failure |
453
+ | 2 | Usage: an unknown command or flag, bad `--input`, input the instance rejects, a bad `--jq` |
454
+ | 3 | The instance is unreachable |
455
+ | 4 | A working-copy `deploy` or `check`, any kind: the instance changed since the working copy was pulled |
456
+ | 5 | `juno ask` / `session approve`: the turn is still running; a destructive command or `approval wait`: nobody decided the approval within `--wait` |
457
+ | 6 | `juno ask` / `session approve`: the assistant is waiting for an answer or an approval |
458
+ | 130 | Ctrl-C while a destructive command waits for its approval (the request stays open), or while `run get --follow` follows (the run goes on) |
304
459
 
305
460
  An instance that does not serve a tool the CLI names is older than the CLI; that
306
461
  is also exit 2, and the message says so.
307
462
 
463
+ ### Destructive commands wait for a person
464
+
465
+ A command that deletes something, rotates a token or revokes an app link (every
466
+ `delete` command, `juno integration uninstall`, both `rotate-token` commands,
467
+ `juno app revoke-link`, and any tool the instance marks `destructive`) runs
468
+ nothing by itself. The instance holds the request until a person approves it in
469
+ Juno, in the browser; no command, tool or API key can approve it. The command
470
+ prints what needs approving and where on stderr, then waits for the decision:
471
+
472
+ ```
473
+ $ juno task delete smoke
474
+ Approval needed: delete task "smoke" (Smoke test).
475
+ Approve or deny in Juno: https://juno.example/approvals/apr_7Kq2…
476
+ waiting up to 300s… (Ctrl-C leaves the request open: juno approval wait apr_7Kq2…)
477
+ {"deleted":true,"id":"smoke","ok":true,"approval":{"id":"apr_7Kq2…","approved_by":"user:victor"}}
478
+ ```
479
+
480
+ Approved, it prints the action's result with `approval` beside it and exits as
481
+ the action would (0, or 1 on an `error`). Denied, expired, or failed when it
482
+ ran: `{"error": "denied by user:victor"}` and the like, exit 1.
483
+ `--wait <seconds>` (default 300; 0 does not wait) bounds the wait; when it runs
484
+ out the request stays open, the `approval_required` answer is printed and the
485
+ exit code is 5. `juno approval wait <id>` picks the wait up again.
486
+
308
487
  ### `juno ask` and `juno session approve`
309
488
 
310
489
  `juno ask` sends a prompt to Juno's in-app assistant through the
@@ -417,26 +596,30 @@ Claude Code. For the same reason there is no tool that writes a credential.
417
596
  ### `juno skill`
418
597
 
419
598
  `juno skill` lists the skills the assistant can load, one
420
- `<name> — <description>` line each. `juno skill <name>` prints one skill's body.
421
- `--json` prints the instance's JSON instead. The skills are the in-app
422
- assistant's, rendered for a reader outside Juno: where the assistant reads a
423
- tool name (`edit_state_layers`), you read the command in front of it
424
- (`juno state edit-layers`), or what to do in a working copy instead
425
- (`npm run deploy` for `submit_code`).
599
+ `<name> — <description>` line each; a description starts with when to load the
600
+ skill. `juno skill <name>` prints one skill's body, and `juno skill <a> <b> …`
601
+ several, each under a `===== <name> =====` line. `--json` prints the instance's
602
+ JSON instead (an array for several names). An unknown name fails the call with
603
+ nothing printed. The skills are the in-app assistant's, rendered for a reader
604
+ outside Juno: where the assistant reads a tool name (`edit_state_layers`), you
605
+ read the command in front of it (`juno state edit-layers`), or what to do in a
606
+ working copy instead (`npm run deploy` for `submit_code`).
426
607
 
427
- ### `juno agent-skill install`
608
+ ### `juno permissions`
428
609
 
429
610
  ```bash
430
- juno agent-skill install # .claude/skills/juno/SKILL.md
431
- juno agent-skill install --dir ~/.claude/skills/juno # for every project
432
- juno agent-skill install --allow-change # + every change command
433
- juno agent-skill install --working-copy # + the npm scripts (run it in a working copy)
434
- juno agent-skill install --agents-md >> AGENTS.md # for a harness that reads AGENTS.md
611
+ juno permissions # every read command
612
+ juno permissions --allow-change # + every change command
613
+ juno permissions --working-copy # + the npm scripts (run it in a working copy)
435
614
  ```
436
615
 
437
- Writes the skill that teaches a coding agent to use Juno — `SKILL.md` in the
438
- Agent Skills format, rendered by the instance (`GET /api/agent-skill`) — and
439
- prints a Claude Code permission block on stdout:
616
+ There is nothing to install for a coding agent: `juno --help` is the primer,
617
+ served live by the instance, and `juno skill` the topic skills. A line in your
618
+ `AGENTS.md` or `CLAUDE.md` such as "Juno home automation is operated with the
619
+ `juno` CLI; run `juno --help` first" is all an agent needs to find it.
620
+
621
+ `juno permissions` prints a Claude Code permission block on stdout, rendered by
622
+ the instance (`GET /api/agent-permissions`):
440
623
 
441
624
  ```json
442
625
  {"permissions":{"allow":["Bash(juno discover:*)","Bash(juno related:*)","Bash(juno state get:*)",…]}}
@@ -452,13 +635,8 @@ trust whatever that `package.json` says the script runs, so put them in the
452
635
  working copy's `.claude/settings.json`, never in user-wide settings. **An `act`
453
636
  command is never in the block**: it always prompts. Add one by hand if you want
454
637
  to, for example, let the agent press a test button. A command added in a later
455
- version prompts until you regenerate the block.
456
-
457
- The skill is short and stable; what changes per instance comes from `juno skill`
458
- and `--help` at run time. It records the version it was rendered at, and
459
- `juno skill`, `juno flow init` and `juno integration init` say on stderr when
460
- the instance serves a newer one. `--agents-md` prints the same text as an
461
- `AGENTS.md` section and writes nothing; `--json` prints the instance's answer.
638
+ version prompts until you regenerate the block. `--json` prints the instance's
639
+ answer.
462
640
 
463
641
  ## Choosing an instance
464
642
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junoflow/cli",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Develop Juno flows, integrations and apps, and operate a Juno instance from the command line",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,7 +40,7 @@
40
40
  "node": ">=20"
41
41
  },
42
42
  "dependencies": {
43
- "@junoflow/eslint-plugin": "0.15.0",
43
+ "@junoflow/eslint-plugin": "0.17.0",
44
44
  "@typescript-eslint/parser": "^8.0.0"
45
45
  },
46
46
  "devDependencies": {