recess-cli 1.9.2 → 2.1.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/dist/help.js ADDED
@@ -0,0 +1,376 @@
1
+ export const HELP = `recess — safe Recess administration and family AI tools
2
+
3
+ Usage:
4
+ recess [--json] --version
5
+ recess [--json] agent-context
6
+ recess [--json] setup [--skill-only]
7
+ recess [--json] doctor
8
+ recess [--json] profile list
9
+ recess [--json] profile show <name>
10
+ recess [--json] profile save <name> [--api-origin URL] [--web-origin URL]
11
+ [--oauth-client-id ID]
12
+ recess [--json] profile use <name>
13
+ recess [--json] profile delete <name>
14
+ recess [--json] jobs list [--limit 20]
15
+ recess [--json] jobs get <operation-key>
16
+ recess [--json] jobs prune [--older-than-days 30]
17
+ recess [--json] feedback list [--limit 20]
18
+ recess [--json] feedback submit <text> [--confirm]
19
+ recess [--json] auth login [--client-id ID] [--callback-port 8765]
20
+ recess [--json] auth request [--label TEXT]
21
+ recess [--json] auth poll [--timeout 300]
22
+ recess [--json] auth status|logout
23
+ recess [--json] users search <name-or-id> [--limit 10]
24
+ recess [--json] users get <user-id>
25
+ recess [--json] users tier list-tiers
26
+ recess [--json] users tier get <kid-id>
27
+ recess [--json] users tier preview <kid-id> --tier social|academics|lite|complete|platform
28
+ [--slots N]
29
+ recess [--json] users tier set <kid-id> --tier social|academics|lite|complete|platform
30
+ [--slots N] --expected-updated-at <iso> [--allow-strand] [--confirm]
31
+ recess [--json] guides create --email <email> --first-name TEXT
32
+ [--last-name TEXT] [--no-invite] [--confirm]
33
+ recess [--json] guides invite --user <user-id> [--confirm]
34
+ recess [--json] guardians invite --family <family-id> --email <email>
35
+ --first-name TEXT [--last-name TEXT] [--no-invite] [--confirm]
36
+ recess [--json] students upload-map-scores --student <kid-id>
37
+ --file </path/to/map-report.pdf> [--confirm]
38
+ recess [--json] students list
39
+ recess [--json] students today --student <kid-id> [--date YYYY-MM-DD]
40
+ recess [--json] students schedule --student <kid-id> [--days 14]
41
+ recess [--json] students xp-history --student <kid-id>
42
+ [--range week|month|quarter|year]
43
+ recess [--json] enrollments list --user <user-id>
44
+ recess [--json] subscriptions list --family <family-id> [--kid <kid-id>]
45
+ recess [--json] invoices list --subscription <subscription-id>
46
+ recess [--json] billing pause --subscription <id> [--until ISO_DATE] [--confirm]
47
+ recess [--json] billing resume --subscription <id> [--confirm]
48
+ recess [--json] invoices refund --invoice <id> --line-item <id>
49
+ --method refund|credit|tokens [--full | --amount-cents N]
50
+ [--who-pays guide|recess] [--reason TEXT] [--confirm]
51
+ recess [--json] applications list [--status SUBMITTED|CLAIMED|ENROLLED|CLOSED]
52
+ [--status-scope active|closed] [--disposition READY_TO_ENROLL|TRIAL|PENDING_FUNDING|NO]
53
+ [--dispositioned true|false] [--quality QUALIFIED|NEEDS_NURTURE|UNKNOWN]
54
+ [--source PUBLIC|STAFF] [--limit 100] [--cursor <id>]
55
+ recess [--json] applications get <application-id>
56
+ recess [--json] applications quotes <application-id>
57
+ recess [--json] applications meetings <application-id>
58
+ recess [--json] applications call-notes <application-id>
59
+ recess [--json] applications create --data-file <application.json> [--confirm]
60
+ recess [--json] applications disposition <application-id>
61
+ --data-file <disposition.json> [--confirm]
62
+ recess [--json] applications qualify <application-id>
63
+ --quality QUALIFIED|NEEDS_NURTURE|UNKNOWN --reason TEXT [--confirm]
64
+ recess [--json] applications update-contact <application-id>
65
+ --data-file <contact.json> [--confirm]
66
+ recess [--json] applications claim-email|claim-link <application-id>
67
+ --intent-key <uuid> [--supersede] [--confirm]
68
+ recess [--json] applications revoke-claim <application-id> [--confirm]
69
+ recess [--json] applications link-family <application-id> --family <family-id> [--confirm]
70
+ recess [--json] applications close <application-id> --reason TEXT [--confirm]
71
+ recess [--json] applications add-call-note <application-id>
72
+ --data-file <call-note.json> [--confirm]
73
+ recess [--json] applications edit-call-note <application-id>
74
+ --note-id <note-id> --data-file <comment.json> [--confirm]
75
+ recess [--json] applications enroll <application-id> --quote <quote-id>
76
+ --school <institution-slug> --kid "<quoteLineId>:<firstName>[:<age>]" (repeat per line)
77
+ [--family <family-id>] [--unassign <kid-id>] (repeat) [--note TEXT] [--confirm]
78
+ recess [--json] quotes get <quote-id>
79
+ recess [--json] quotes preview --data-file <quote.json>
80
+ recess [--json] quotes create --data-file <quote.json> [--confirm]
81
+ recess [--json] quotes update <quote-id> --data-file <quote.json> [--confirm]
82
+ recess [--json] quotes send <quote-id> [--expected-revision <sha>] [--confirm]
83
+ recess [--json] quotes accept <quote-id> [--confirm]
84
+ recess [--json] quotes decline <quote-id> [--note TEXT] [--confirm]
85
+ recess [--json] cohorts search <query>
86
+ recess [--json] enrollments create --user <kid-id> --cohort <id>
87
+ [--first-charge-at ISO_DATETIME] [--send-email] [--force]
88
+ [--confirm --approval-token TOKEN]
89
+ recess [--json] enrollments register-cohort --enrollment <id>
90
+ --user <id> --cohort <id> [--confirm]
91
+ recess [--json] enrollments unregister-cohort --user <id>
92
+ --cohort <id> [--confirm]
93
+ recess [--json] enrollments get-for-subscription --subscription <id>
94
+ recess [--json] billing extend-trial --subscription <id>
95
+ --trial-end ISO_DATE [--confirm]
96
+ recess [--json] billing cancel-subscription --subscription <id>
97
+ [--immediate] [--reason TEXT] [--restore] [--confirm]
98
+ recess [--json] payout payruns list [--status A,B] [--schedule <id>]
99
+ recess [--json] payout recipients list [--search <name>] [--user <id>] [--id <id>]
100
+ [--limit 20] [--cursor <id>]
101
+ recess [--json] payout invoices list [--payrun <id>] [--recipient <account-id>]
102
+ [--user <id>] [--status A,B] (at least one filter)
103
+ recess [--json] payout invoices get <invoice-id>
104
+ recess [--json] payout invoices set-status <invoice-id>
105
+ --status IN_REVIEW|OPEN|PAID|CANCELED [--send-email] [--confirm]
106
+ recess [--json] payout items add --invoice <id> --amount-cents N
107
+ --description TEXT [--date YYYY-MM-DD] [--confirm]
108
+ recess [--json] payout items edit <item-id> [--amount-cents N]
109
+ [--description TEXT] [--date YYYY-MM-DD] [--confirm]
110
+ recess [--json] payout items delete <item-id> [--confirm]
111
+ recess [--json] cohorts get <cohort-id> [--events-tab ACTIVE|ENDED|CANCELED|ARCHIVED]
112
+ recess [--json] cohorts parent-emails <cohort-id>
113
+ recess [--json] cohorts end <cohort-id> [--cancel-subscriptions] [--confirm]
114
+ recess [--json] cohorts pause-billing <cohort-id> --weeks 1..6 [--confirm]
115
+ recess [--json] cohorts resume-billing <cohort-id> [--confirm]
116
+ recess [--json] cohorts email <cohort-id> --target ALL_PARENTS|ALL_PARENTS_GUIDES
117
+ --content TEXT [--confirm]
118
+ recess [--json] events get <event-id>
119
+ recess [--json] events take-attendance <event-id> --attended <id,id,...>
120
+ [--absent <id,id,...>] [--excused <id,id,...>] [--confirm]
121
+ recess [--json] events cancel <event-id> --reason TEXT [--confirm]
122
+ recess [--json] events set-status <event-id> --status ACTIVE|ENDED|CANCELED [--confirm]
123
+ recess [--json] events reschedule --cohort <id> --event <event-id>
124
+ --starts-at "YYYY-MM-DDTHH:MM" [--timezone <iana>] [--length-mins N] [--confirm]
125
+ recess [--json] events add --cohort <id> --starts-at "YYYY-MM-DDTHH:MM"
126
+ [--timezone <iana>] [--length-mins N] [--confirm]
127
+ recess [--json] registrations approve --registration <id> [--confirm]
128
+ recess [--json] registrations deny --cohort <id> --user <id> [--confirm]
129
+ recess [--json] request get </path?query=value>
130
+ recess [--json] onboarding status <family-id>
131
+ recess [--json] onboarding queue [--school <institution-slug>]
132
+ recess [--json] onboarding kids [--time-period-days N] [--cohort <id>]
133
+ [--limit N] [--stage-filter all|scheduled|oriented|course|converted|lost]
134
+ recess [--json] onboarding timeline <family-id>
135
+ recess [--json] onboarding readiness <family-id>
136
+ recess [--json] onboarding active-tutors <family-id>
137
+ recess [--json] onboarding meetings <family-id>
138
+ recess [--json] onboarding cohort-options <family-id> [--kid <kid-id>]
139
+ recess [--json] onboarding comms [--school <institution-slug>]
140
+ [--status RESERVED|SENT|FAILED] [--kind <kind>] [--limit 50] [--cursor <cursor>]
141
+ recess [--json] onboarding lifecycle-prompts
142
+ recess [--json] onboarding orientation-sessions
143
+ recess [--json] onboarding ixl-preview <family-id> --kid <kid-id>
144
+ recess [--json] onboarding intake-session <family-id>
145
+ recess [--json] onboarding intake-session-create <family-id> [--confirm]
146
+ recess [--json] onboarding set-stage <family-id>
147
+ --stage LEGACY|PROVISIONED|PARENT_CONFIRMED|CLEARED_FOR_COHORT|COMPLETE [--confirm]
148
+ recess [--json] onboarding set-account-state <family-id>
149
+ --state ACTIVE|PENDING_PAYMENT|PAUSED|BOOTED [--note TEXT] [--confirm]
150
+ recess [--json] onboarding set-primary-tutor <family-id>
151
+ [--tutor <user-id> | --clear] [--confirm]
152
+ recess [--json] onboarding attest <family-id>
153
+ --condition app_downloaded|tutor_met|goals_loaded|ma_diagnostic [--revoke]
154
+ [--note TEXT] [--confirm]
155
+ recess [--json] onboarding clear-for-cohort <family-id> [--confirm]
156
+ recess [--json] onboarding register-cohort <family-id>
157
+ --kid <kid-id> --cohort <event-series-id> [--confirm]
158
+ recess [--json] onboarding set-kid-grade <family-id>
159
+ --kid <kid-id> [--grade 1..12 | --clear] [--confirm]
160
+ recess [--json] onboarding seed-feed <family-id> --kid <kid-id> [--confirm]
161
+ recess [--json] onboarding generate-summaries <family-id> [--confirm]
162
+ recess [--json] onboarding provision-math-academy <family-id>
163
+ --kid <kid-id> [--grade 1..12] [--confirm]
164
+ recess [--json] onboarding provision-ixl <family-id>
165
+ --kid <kid-id> [--credentials-file <credentials.json>] [--confirm]
166
+ recess [--json] onboarding remove-ixl <family-id> --kid <kid-id> [--confirm]
167
+ recess [--json] onboarding ixl-sync [--apply] [--confirm]
168
+ recess [--json] onboarding send-comms --subject <family:id|invite:id>
169
+ --kind <kind> [--confirm]
170
+ recess [--json] onboarding send-welcome <family-id> [--resend] [--confirm]
171
+ recess [--json] onboarding send-payment-email <family-id> [--resend] [--confirm]
172
+ recess [--json] onboarding send-recommendations <family-id>
173
+ --courses <course-id,course-id> [--resend] [--confirm]
174
+ recess [--json] onboarding set-lifecycle-prompt <kind>
175
+ [--enabled true|false] [--delay-days N] [--window-days N]
176
+ [--snooze-days N] [--max-prompts N] [--confirm]
177
+ recess [--json] onboarding override-lifecycle-prompt <kind> <family-id>
178
+ --action delay|skip|clear [--days N] [--confirm]
179
+ recess [--json] onboarding orientation-create --starts-at <iso>
180
+ [--ends-at <iso>] [--capacity N] [--confirm]
181
+ recess [--json] onboarding orientation-set-status <session-id>
182
+ --status CANCELED|COMPLETED [--confirm]
183
+ recess [--json] onboarding orientation-attendance <session-id>
184
+ --family <family-id> --attended true|false [--confirm]
185
+ recess [--json] onboarding meeting-reschedule <meeting-id>
186
+ --starts-at <iso> [--note TEXT] [--confirm]
187
+ recess [--json] onboarding meeting-cancel <meeting-id> --reason TEXT [--confirm]
188
+ recess [--json] onboarding set-intake <family-id> --session <id>
189
+ --data <json> [--expected-updated-at <iso>] [--confirm]
190
+ recess [--json] onboarding extract <family-id> --session <id>
191
+ (--transcript-file <path> | --granola <ref>) [--confirm]
192
+ recess [--json] village models list [--world village-1] [--query TEXT] [--archived]
193
+ recess [--json] village models upload --file </path/model.glb>
194
+ [--world village-1] [--name TEXT] [--id ID] [--description TEXT] [--tags A,B]
195
+ [--visual-only] [--confirm]
196
+ recess [--json] village models publish|archive <model-id> [--world village-1] [--confirm]
197
+ recess [--json] village models place <model-id> --x N --z N
198
+ [--y N] [--rotation 0..3] [--mirrored] [--no-collision] [--batch ID]
199
+ [--world village-1] [--confirm]
200
+ recess [--json] village models move <placement-id> --x N --z N
201
+ [--y N] [--rotation 0..3] [--mirrored] [--world village-1] [--confirm]
202
+ recess [--json] village models remove <placement-id> [--world village-1] [--confirm]
203
+ recess [--json] village render --min-x N --min-z N --max-x N --max-z N
204
+ [--world village-1]
205
+ recess [--json] village library search [query] [--query TEXT] [--limit 20]
206
+ [--world home-USER_ID]
207
+ recess [--json] village library get <model-id> [--world home-USER_ID]
208
+ recess [--json] village objects list [--min-x -20] [--min-z -20]
209
+ [--max-x 20] [--max-z 20] [--world home-USER_ID]
210
+ recess [--json] village objects get <object-id> [--world home-USER_ID]
211
+ recess [--json] village build cmd '<command-json>' [--world home-USER_ID] [--confirm]
212
+ recess [--json] village build cmd --file <command.json> [--world home-USER_ID] [--confirm]
213
+ recess [--json] village worlds export <world-id> [--out <bundle.json>] [--confirm]
214
+ recess [--json] village worlds import <world-id> --file <bundle.json> [--confirm]
215
+ recess [--json] village worlds promote <mirror-or-archive-id> [--confirm]
216
+ recess [--json] store-items list [--search TEXT]
217
+ [--status ACTIVE|INACTIVE|COMING_SOON] [--item-type TYPE]
218
+ [--page 0] [--limit 20] [--sort-by name|price|createdAt|updatedAt|order]
219
+ [--sort-order asc|desc]
220
+ recess [--json] store-items set-status <village-store-item-id>
221
+ --status ACTIVE|INACTIVE|COMING_SOON [--confirm]
222
+ recess [--json] content-library search <query> [--limit 8]
223
+ recess [--json] content-library status <gem-id-or-url>
224
+ recess [--json] content-library set-stage <gem-id-or-url...> [--file <path>]
225
+ --stage review|polishing|live|archived [--wait] [--timeout 900] [--confirm]
226
+ recess [--json] content-library submit <url...> [--file <path>]
227
+ [--stage review|polish] [--title TEXT] [--summary TEXT]
228
+ [--lane web-toys|mechanics|explorables|data-stories|sims|maps-scale|sound-art|puzzles|wonder|idea-games]
229
+ [--wait] [--timeout 900] [--confirm]
230
+ recess [--json] skills guardian list [--query TEXT] [--category TEXT]
231
+ recess [--json] skills guardian get <skill-name>
232
+ [--reference NAME | --all-references] [--refresh]
233
+ recess [--json] skills admin list [--query TEXT] [--category TEXT]
234
+ recess [--json] skills admin get <skill-name>
235
+ [--reference NAME | --all-references]
236
+ [--refresh]
237
+ recess [--json] goal-templates list [--query TEXT] [--category TEXT]
238
+ [--kind SIMPLE|BLUEPRINT] [--starter-only] [--include-deleted]
239
+ [--limit 20] [--cursor <id>]
240
+ recess [--json] goal-templates get <template-id|slug> [--spec-only]
241
+ recess [--json] goal-templates versions <template-id> [--version N]
242
+ recess [--json] goal-templates validate-spec --file <path/template.json>
243
+ recess [--json] goal-templates create --file <path/template.json> [--confirm]
244
+ recess [--json] goal-templates patch-spec <template-id|slug> --expected-version N
245
+ --patches-file <path/patches.json> [--confirm --approval-token TOKEN]
246
+ [--confirm-destructive-changes --destructive-change-token TOKEN]
247
+ recess [--json] goal-templates set-metadata <template-id> --expected-version N
248
+ [--title TEXT] [--description TEXT] [--emoji X] [--category TEXT] [--tags A,B]
249
+ [--sort-order N] [--is-starter true|false]
250
+ [--setup-audience KID_FRIENDLY|PARENT_SETUP] [--kind SIMPLE|BLUEPRINT]
251
+ [--agent-instructions-file <path>]
252
+ [--output-template-file <path>] [--confirm]
253
+ recess [--json] goal-templates delete <template-id> --expected-version N [--confirm]
254
+ recess [--json] goal-templates snapshot-files <template-id> [--path P]
255
+ recess [--json] goal-templates capture-snapshot <template-id|slug>
256
+ (--source-goal <goal-id> | --source-draft <draft-slug> --student <goal-owner-id>
257
+ | --source-dir <local-checkout>)
258
+ [--dry-run] [--confirm --approval-token TOKEN]
259
+ recess [--json] goal-templates apply <template-id> --answers-file <path>
260
+ [--dry-run] [--confirm --approval-token TOKEN]
261
+ recess [--json] goal-templates apply-starter <template-id> --student <kid-id>
262
+ [--answers-file <path>] [--confirm]
263
+ recess [--json] goals list --student <kid-id>
264
+ recess [--json] goals create [--student <kid-id>] --title TEXT
265
+ (--description TEXT | --description-file <path>) [--target-date <iso>]
266
+ [--schedule TEXT] [(--draft <draft-slug> | --source-dir <local-checkout>)
267
+ (--enable-applet-follow-ups | --disable-applet-follow-ups)]
268
+ [--confirm --approval-token TOKEN]
269
+ recess [--json] goals edit <goal-id> --student <kid-id> --patch-file <path/patch.json>
270
+ --delta TEXT [--confirm --approval-token TOKEN]
271
+ recess [--json] goals delete <goal-id> --student <kid-id>
272
+ [--confirm --approval-token TOKEN]
273
+ recess [--json] goals queue get <goal-id> --student <kid-id>
274
+ recess [--json] goals queue set <goal-id> --student <kid-id>
275
+ --entries-file <path.json> --delta TEXT [--replace-description-pointer]
276
+ [--confirm --approval-token TOKEN]
277
+ recess [--json] todos create --student <kid-id> --title TEXT
278
+ [--due-date YYYY-MM-DD] [--estimated-minutes N] [--url URL] [--confirm]
279
+ recess [--json] todos edit <todo-id> --patch-file <path/patch.json>
280
+ [--confirm --approval-token TOKEN]
281
+ recess [--json] todos delete <todo-id>
282
+ [--confirm --approval-token TOKEN]
283
+ recess [--json] todos generate-applet <todo-id> --student <kid-id>
284
+ [--due-date YYYY-MM-DD] [--confirm --approval-token TOKEN]
285
+ recess [--json] memories context --student <kid-id>
286
+ recess [--json] memories log --student <kid-id> --date YYYY-MM-DD
287
+ recess [--json] rocky get --student <kid-id>
288
+ recess [--json] rocky set --student <kid-id> --patch-file <path/patch.json>
289
+ --expected-updated-at ISO|none [--confirm]
290
+ recess [--json] goals files list --student <goal-owner-id> --goal <goal-id>
291
+ recess [--json] goals files read --student <goal-owner-id> --goal <goal-id> --path P
292
+ recess [--json] goals files init --student <goal-owner-id> --draft <draft-slug>
293
+ --output-dir <local-dir>
294
+ recess [--json] goals files checkout --student <goal-owner-id>
295
+ (--goal <goal-id> | --draft <draft-slug>)
296
+ --output-dir <local-dir>
297
+ recess [--json] goals files push --source-dir <local-checkout>
298
+ [--message TEXT] [--confirm --approval-token TOKEN]
299
+ recess [--json] goals files write --student <goal-owner-id>
300
+ (--goal <goal-id> | --draft <draft-slug>)
301
+ (--source-dir <local-dir> | --source-file <local-file> --path P)
302
+ [--message TEXT] [--confirm --approval-token TOKEN]
303
+ recess [--json] goals pdf upload --student <goal-owner-id>
304
+ (--goal <goal-id> | --draft <draft-slug>) --source-file <local.pdf>
305
+ [--path uploads/name.pdf] [--message TEXT]
306
+ [--confirm --approval-token TOKEN]
307
+
308
+ Authoring notes: "skills" serves the in-product tutor skills (the private
309
+ packages/skills workspace package) read-only over your admin session — they are never
310
+ bundled into this npm package. Load os-v2-goal-template-builder and its
311
+ references/deterministic-workflow-setup.md BEFORE authoring a template; that is
312
+ the same guidance the recess.gg/ai agent follows, so there is exactly one
313
+ standard. Responses cache under ~/.recess-cli/skills-cache/ (--refresh re-fetches).
314
+ For goal workspace and PDF commands, --student names the goal owner. A
315
+ full_admin session may use a KID or ADMIN user id, including its own ADMIN id;
316
+ family_ai sessions remain limited to managed KID profiles.
317
+ "goals create --source-dir" materializes a clean, fully pushed draft checkout as
318
+ a personal module-backed goal and retargets that checkout to the resulting live
319
+ goal. It does not create or apply a reusable template. Its preview is bound to
320
+ the exact workspace revision, hash, module inventory, and applet follow-up choice.
321
+ Every template created here is setupMode DETERMINISTIC_WORKFLOW and CANNOT be
322
+ converted back, so "validate-spec" against the same file until it passes, then
323
+ "create". "create" runs a real server-side validation before its gate, so the
324
+ preview shows the handler/goalShape/step keys the SERVER resolved. "apply" runs
325
+ the backend's own dryRun before the gate and previews the per-student outcome.
326
+ "set-metadata" and "delete" require --expected-version (from "get"); a stale one
327
+ 409s STALE_WRITE and writes nothing. The spec is unreachable from "set-metadata"
328
+ by design — an existing spec is edited only through the guarded /ai patch path.
329
+
330
+ Onboarding notes: "status" and "intake-session" are reads — "intake-session"
331
+ looks up the current IN_PROGRESS session without creating one (prints a "none
332
+ yet" result when absent). "intake-session-create" is the explicit write that
333
+ mints a blank session, so it is gated behind --confirm. "set-stage" reads the
334
+ current stage first and warns when a move re-locks progress; CLEARED_FOR_COHORT
335
+ unlocks cohort registration. "set-account-state" PAUSED/BOOTED lock the family out of paid
336
+ capabilities (--note is analytics-only). "set-intake --data" takes JSON (agents
337
+ drive it with --json; humans rarely will); the preview is offline. Its
338
+ optimistic-concurrency token resolves only on --confirm: pass
339
+ --expected-updated-at <iso> (from an intake-session read's updatedAt) for strict
340
+ CAS that 409s if a parent autosaved since; omit it and the CLI fetches the
341
+ current token at confirm time and warns that post-preview edits are unprotected.
342
+ "extract" is LLM-bound and can take ~30s; a 503 means Granola is not configured
343
+ server-side.
344
+
345
+ Class-ops notes: "events cancel" notifies families (chat + parent email blast +
346
+ credit notes + Slack); "events set-status --status CANCELED" is a silent status
347
+ change. Reschedule times are cohort-local wall-clock (zoneless).
348
+
349
+ Payout notes: amounts are integer cents. "payout items add" without --date
350
+ defaults the item date to the penultimate day of the invoice's cycle (its
351
+ endDate minus one day) and shows the computed date in the preview.
352
+
353
+ Auth notes: "auth login" runs the browser loopback flow for ADMIN, GUIDE, a GUARDIAN with
354
+ access:ai, or a KID using only Village home building. Guardian sessions are family-scoped and
355
+ cannot call /admin; KID sessions cannot call any non-Village API command. For a headless
356
+ cloud agent, the ADMIN-only "auth request" prints an approval URL to hand a Recess admin; after they
357
+ approve it in a browser, "auth poll" collects the 12h session. When it lapses, run
358
+ "auth request" again for a fresh link. Both paths yield the same session.
359
+
360
+ Skill notes: this CLI's own agent skill ships inside the npm package AND is served
361
+ by the server, so wording/Gotcha updates arrive without an npm release. "setup"
362
+ (or "setup --skill-only") installs the bundled copy, then upgrades it from the
363
+ server when the served bundle's minCliVersion allows — an older binary keeps the
364
+ bundled copy, and an unreachable server is not an error. "doctor" reports whether
365
+ a newer skill exists and names the command; it never writes.
366
+
367
+ Writes preview and exit 2 unless --confirm is supplied after explicit human approval.
368
+ Every preview includes an operationKey; confirmed writes must echo it with
369
+ --operation-key so an interrupted invocation can be retried without duplicating the write.
370
+ Environment overrides: RECESS_CLI_API_ORIGIN, RECESS_CLI_WEB_ORIGIN,
371
+ RECESS_CLI_OAUTH_CLIENT_ID, RECESS_CLI_COOKIE, RECESS_CLI_CONFIG,
372
+ RECESS_CLI_PROFILE, RECESS_CLI_FEEDBACK_ENDPOINT.
373
+
374
+ Every command that calls the Recess API, except auth commands, requires
375
+ --reason TEXT: a non-empty human-readable purpose of at most 1024 characters.`;
376
+ //# sourceMappingURL=help.js.map
package/dist/http.js ADDED
@@ -0,0 +1,25 @@
1
+ import { CliError } from "./errors.js";
2
+ export const RECESS_CLIENT_HEADER = "x-recess-client";
3
+ export const RECESS_CLIENT_CLI = "cli";
4
+ export const RECESS_CLIENT_CLI_UI = "cli-ui";
5
+ export const RECESS_REASON_HEADER = "x-recess-reason";
6
+ export function requireCliRequestReason(value) {
7
+ const reason = value?.trim();
8
+ if (!reason) {
9
+ throw new CliError("invalid_arguments", "Missing required --reason.");
10
+ }
11
+ if (reason.length > 1024) {
12
+ throw new CliError("invalid_arguments", "--reason must be 1024 characters or fewer.");
13
+ }
14
+ return reason;
15
+ }
16
+ export function markCliRequest(headers, reason, client = RECESS_CLIENT_CLI) {
17
+ headers.set(RECESS_CLIENT_HEADER, client);
18
+ if (reason)
19
+ headers.set(RECESS_REASON_HEADER, reason);
20
+ return headers;
21
+ }
22
+ export function cliRequestHeaders(init, reason, client = RECESS_CLIENT_CLI) {
23
+ return markCliRequest(new Headers(init), reason, client);
24
+ }
25
+ //# sourceMappingURL=http.js.map
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { createInterface } from "node:readline/promises";
3
3
  import { parseArgs } from "./args.js";
4
4
  import { runCommand } from "./cli.js";
5
+ import { deliverJson } from "./delivery.js";
5
6
  import { CliError } from "./errors.js";
6
7
  async function withInteractiveAdmissionStage(args) {
7
8
  const parsed = parseArgs(args);
@@ -38,7 +39,8 @@ async function withInteractiveAdmissionStage(args) {
38
39
  }
39
40
  }
40
41
  const argv = await withInteractiveAdmissionStage(process.argv.slice(2));
41
- const json = argv.includes("--json");
42
+ const parsedArgv = parseArgs(argv);
43
+ const json = parsedArgv.flags.has("json");
42
44
  // The interactive console owns the terminal, so it runs before the JSON
43
45
  // envelope machinery rather than through it.
44
46
  if (argv[0] === "ui") {
@@ -57,11 +59,21 @@ if (argv[0] === "ui") {
57
59
  }
58
60
  try {
59
61
  const data = await runCommand(argv);
60
- if (typeof data === "object" && data && "help" in data && !json) {
62
+ const destination = parsedArgv.flags.get("deliver");
63
+ if (destination === true) {
64
+ throw new CliError("invalid_delivery", "--deliver requires a value.");
65
+ }
66
+ const envelope = { ok: true, data };
67
+ const delivery = await deliverJson(destination, envelope);
68
+ if (delivery.delivered) {
69
+ const acknowledgement = { ok: true, data: delivery };
70
+ process.stdout.write(`${json ? JSON.stringify(acknowledgement) : JSON.stringify(acknowledgement.data, null, 2)}\n`);
71
+ }
72
+ else if (typeof data === "object" && data && "help" in data && !json) {
61
73
  process.stdout.write(`${String(data.help)}\n`);
62
74
  }
63
75
  else if (json) {
64
- process.stdout.write(`${JSON.stringify({ ok: true, data })}\n`);
76
+ process.stdout.write(`${JSON.stringify(envelope)}\n`);
65
77
  }
66
78
  else {
67
79
  process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
package/dist/jobs.js ADDED
@@ -0,0 +1,80 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ function jobsPath() {
5
+ return (process.env.RECESS_CLI_JOBS_PATH ??
6
+ path.join(os.homedir(), ".recess-cli", "jobs.jsonl"));
7
+ }
8
+ async function readEvents() {
9
+ try {
10
+ return (await fs.readFile(jobsPath(), "utf8"))
11
+ .split("\n")
12
+ .filter(Boolean)
13
+ .flatMap((line) => {
14
+ try {
15
+ return [JSON.parse(line)];
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ });
21
+ }
22
+ catch (error) {
23
+ if (error.code === "ENOENT")
24
+ return [];
25
+ throw error;
26
+ }
27
+ }
28
+ export async function appendJobEvent(event) {
29
+ const file = jobsPath();
30
+ await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
31
+ let stored = event;
32
+ const serialized = JSON.stringify(stored);
33
+ if (serialized.length > 64 * 1024 && event.result !== undefined) {
34
+ stored = {
35
+ ...event,
36
+ result: {
37
+ omitted: true,
38
+ reason: "Result exceeded the 64 KiB local job-ledger limit.",
39
+ bytes: Buffer.byteLength(serialized),
40
+ },
41
+ };
42
+ }
43
+ await fs.appendFile(file, `${JSON.stringify(stored)}\n`, { mode: 0o600 });
44
+ await fs.chmod(file, 0o600);
45
+ }
46
+ function foldJobs(events) {
47
+ const jobs = new Map();
48
+ for (const event of events) {
49
+ const prior = jobs.get(event.jobId);
50
+ jobs.set(event.jobId, { ...event, events: (prior?.events ?? 0) + 1 });
51
+ }
52
+ return Array.from(jobs.values()).sort((left, right) => right.timestamp.localeCompare(left.timestamp));
53
+ }
54
+ export async function listJobs(limit = 20) {
55
+ const jobs = foldJobs(await readEvents());
56
+ return {
57
+ jobs: jobs.slice(0, limit),
58
+ truncated: jobs.length > limit,
59
+ total: jobs.length,
60
+ };
61
+ }
62
+ export async function getJob(jobId) {
63
+ const history = (await readEvents()).filter((event) => event.jobId === jobId);
64
+ return { job: foldJobs(history)[0] ?? null, history };
65
+ }
66
+ export async function pruneJobs(olderThanDays) {
67
+ const events = await readEvents();
68
+ const cutoff = Date.now() - olderThanDays * 24 * 60 * 60 * 1000;
69
+ const kept = events.filter((event) => Date.parse(event.timestamp) >= cutoff);
70
+ const file = jobsPath();
71
+ await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
72
+ const temporary = `${file}.${process.pid}.tmp`;
73
+ await fs.writeFile(temporary, kept.length > 0
74
+ ? `${kept.map((event) => JSON.stringify(event)).join("\n")}\n`
75
+ : "", { mode: 0o600 });
76
+ await fs.rename(temporary, file);
77
+ await fs.chmod(file, 0o600);
78
+ return { removed: events.length - kept.length, remaining: kept.length };
79
+ }
80
+ //# sourceMappingURL=jobs.js.map
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { cliRequestHeaders } from "./http.js";
3
4
  import { skillDestinations } from "./setup.js";
4
5
  /**
5
6
  * Compare dotted numeric versions. Deliberately hand-rolled rather than adding a
@@ -88,7 +89,7 @@ export async function updateSkillFromServer(input) {
88
89
  let bundle;
89
90
  try {
90
91
  const response = await fetch(new URL("/auth/admin-cli/skill/", input.apiOrigin), {
91
- headers: input.sessionCookie ? { cookie: input.sessionCookie } : {},
92
+ headers: cliRequestHeaders(input.sessionCookie ? { cookie: input.sessionCookie } : undefined, input.reason),
92
93
  signal: AbortSignal.timeout(10_000),
93
94
  });
94
95
  if (!response.ok) {
package/dist/ui/index.js CHANGED
@@ -3,6 +3,7 @@ import { render } from "ink";
3
3
  import { RecessAdminApi } from "../api.js";
4
4
  import { resolveConfig } from "../config.js";
5
5
  import { CliError } from "../errors.js";
6
+ import { RECESS_CLIENT_CLI_UI } from "../http.js";
6
7
  import { App } from "./app.js";
7
8
  /**
8
9
  * The interactive console. Kept off the JSON path on purpose: agents parse
@@ -14,7 +15,7 @@ export async function runUi() {
14
15
  if (!config.sessionCookie) {
15
16
  throw new CliError("auth_required", "No Recess CLI session found. Run `recess auth login`.");
16
17
  }
17
- const api = new RecessAdminApi(config);
18
+ const api = new RecessAdminApi(config, undefined, RECESS_CLIENT_CLI_UI);
18
19
  const instance = render(_jsx(App, { api: api }));
19
20
  await instance.waitUntilExit();
20
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recess-cli",
3
- "version": "1.9.2",
3
+ "version": "2.1.0",
4
4
  "description": "Safe Recess administration and family AI tools from the command line.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -48,8 +48,8 @@
48
48
  "typecheck": "tsgo --noEmit",
49
49
  "format": "oxfmt --ignore-path .oxfmtignore --check",
50
50
  "format:fix": "oxfmt --ignore-path .oxfmtignore",
51
- "lint": "oxlint --type-aware src/*.ts",
52
- "lint:fix": "oxlint --type-aware --fix src/*.ts",
51
+ "lint": "oxlint --type-aware src/*.ts src/commands/*.ts",
52
+ "lint:fix": "oxlint --type-aware --fix src/*.ts src/commands/*.ts",
53
53
  "test": "vitest run",
54
54
  "install-local": "pnpm run build && make install-local",
55
55
  "install-persistent": "pnpm run build && make install-persistent",
@@ -11,26 +11,30 @@ Use the installed `recess` command for Recess work. This small skill is shared b
11
11
 
12
12
  ```bash
13
13
  command -v recess
14
- recess --json doctor
14
+ recess --json doctor --reason "Verify CLI connectivity, identity, and scope"
15
15
  ```
16
16
 
17
17
  `doctor` reports the active API origin, authenticated user, and CLI scope. Do not assume a role from the request. Use the scope returned by the server.
18
18
 
19
+ If the reported scope is `village_home`, use only `recess village build`, `village library`, `village objects`, and `village render`. That session is intentionally unable to fetch the guardian or admin skill catalogs or call ordinary Recess API routes; Village applies the user's own-home and object-ownership checks to every build or read.
20
+
19
21
  Then load the guidance for the purpose of the work:
20
22
 
21
23
  ```bash
22
24
  # Parent or guardian work: kids, schedules, goals, todos, progress, memory, Rocky
23
- recess --json skills guardian get recess-family --all-references
25
+ recess --json skills guardian get recess-family --all-references --reason "Load family workflow guidance"
24
26
 
25
27
  # Research, author, validate, or apply a goal or reusable goal template
26
- recess --json skills guardian get recess-goal-authoring --all-references
28
+ recess --json skills guardian get recess-goal-authoring --all-references --reason "Load goal authoring guidance"
27
29
 
28
30
  # Staff-only school operations
29
- recess --json skills admin get recess-admin --all-references
31
+ recess --json skills admin get recess-admin --all-references --reason "Load staff operations guidance"
30
32
  ```
31
33
 
32
34
  Use `skills guardian list` or `skills admin list` when the needed skill is not obvious. Never substitute one audience for the other. A guardian session cannot fetch the admin catalog; an admin may fetch either catalog.
33
35
 
36
+ For machine-readable discovery, use `recess --json agent-context`; for a smaller syntax surface, use `recess --json help <noun> [verb]`. Unknown commands, flags, duplicate non-repeatable flags, missing flag values, and extra positional arguments fail explicitly.
37
+
34
38
  ## Authentication
35
39
 
36
40
  - Workstation: `recess --json auth login` opens Recess SSO.
@@ -49,23 +53,31 @@ Always pass `--json`. Stdout contains one object:
49
53
  - Failure: `{"ok":false,"error":{"code":"...","message":"...","details":{...}}}`, exit 1.
50
54
  - Write awaiting approval: `confirmation_required`, exit 2, with `details.preview` and `details.requiredFlag`.
51
55
 
56
+ Every command-driven Recess API call except `auth` requires `--reason "..."`: a specific,
57
+ human-readable purpose from 1 to 1024 characters. The server records it in the CLI request audit
58
+ ledger. Reuse the invocation's same reason across its preflight and execution requests. The
59
+ human-only `recess ui` console is exempt.
60
+
52
61
  Every write is two-step:
53
62
 
54
63
  1. Run without `--confirm`.
55
64
  2. Show the human the complete preview, including `preview.details`.
56
65
  3. Ask for explicit approval of that exact action.
57
- 4. Rerun the unchanged command with `--confirm`. If the preview includes an `approvalToken`, pass it unchanged.
66
+ 4. Rerun the unchanged command with `--confirm --operation-key <operationKey>`. If the preview includes an `approvalToken`, pass it unchanged too.
58
67
 
59
- Any changed target, payload, local file, server revision, amount, recipient, or consequence requires a new preview and new approval. Never infer approval from the original request.
68
+ Any changed target, payload, local file, server revision, amount, recipient, or consequence requires a new preview and new approval. Never infer approval from the original request. If a confirmed invocation is interrupted, retry the unchanged command with the same operation key; never mint a new one for an uncertain write. Inspect recovery state with `recess --json jobs get <operation-key>`.
60
69
 
61
70
  ## Shared constraints
62
71
 
63
- - Use high-level CLI commands. The raw escape hatch is GET-only: `recess --json request get /path`.
72
+ - Use high-level CLI commands. The raw escape hatch is GET-only:
73
+ `recess --json request get /path --reason "Inspect the exact API resource needed for this task"`.
64
74
  - Resolve names to exact IDs before writes. Stop on ambiguous matches.
65
75
  - Money uses integer cents.
66
76
  - Keep dates in the exact format required by command help.
67
77
  - Treat a 401 or `auth_required` as an authentication problem. A transport-level `fetch failed` is a network/sandbox problem, not proof that the session expired.
68
- - Run `recess --help` for syntax; load the audience skill for workflow meaning.
78
+ - Use `profile list|save|use` or global `--profile` for named environments. `--deliver file:<path>` atomically stores one JSON envelope locally; authenticated webhook delivery is intentionally unavailable.
79
+ - `feedback submit` records CLI friction under an operation key and optionally forwards it when an upstream endpoint is configured.
80
+ - Run scoped help for syntax; load the audience skill for workflow meaning.
69
81
 
70
82
  ## Updating this shared skill
71
83