recess-cli 2.6.1 → 2.8.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.
@@ -2,6 +2,7 @@ import { unwrap } from "../api.js";
2
2
  import { flagString, hasFlag } from "../args.js";
3
3
  import { apiError, CliError } from "../errors.js";
4
4
  const NONE = "—";
5
+ const WEEKDAYS = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"];
5
6
  async function fetchTemplatesAndRooms(ctx) {
6
7
  const [templatesResponse, rooms] = await Promise.all([
7
8
  ctx.api.client.GET("/recess/event-templates-editable/"),
@@ -17,6 +18,17 @@ function schedule(template) {
17
18
  return template.singleEventDate.slice(0, 10);
18
19
  return template.rrule || NONE;
19
20
  }
21
+ function templateDays(template) {
22
+ if (template.singleEventDate)
23
+ return null;
24
+ const match = /BYDAY=([A-Z,]+)/.exec(template.rrule);
25
+ if (!match)
26
+ return null;
27
+ const days = match[1]
28
+ .split(",")
29
+ .filter((day) => WEEKDAYS.includes(day));
30
+ return days.length > 0 ? days : null;
31
+ }
20
32
  function renderColumns(rows) {
21
33
  const widths = rows[0].map((_, column) => Math.max(...rows.map((row) => row[column].length)));
22
34
  return rows
@@ -45,7 +57,7 @@ async function listEvents(ctx) {
45
57
  };
46
58
  });
47
59
  const unlinkedRooms = rooms
48
- .filter((room) => !room.linkedTemplateId)
60
+ .filter((room) => room.bookings.length === 0)
49
61
  .map((room) => ({ id: room.id, name: room.name }));
50
62
  if (hasFlag(ctx.parsed, "json")) {
51
63
  return { templates: templateRows, unlinkedRooms };
@@ -97,14 +109,22 @@ async function linkEvent(ctx) {
97
109
  throw new CliError("not_found", `No editable event template found for ${templateId}. Run \`recess village events\` to list them.`);
98
110
  }
99
111
  let room;
112
+ let informationalBookings = [];
100
113
  if (roomId) {
101
114
  room = rooms.find((row) => row.id === roomId);
102
115
  if (!room) {
103
116
  throw new CliError("not_found", `No Village Town Center zone found for ${roomId}. Run \`recess village events\` to list linkable zones.`);
104
117
  }
105
- if (room.linkedTemplateId && room.linkedTemplateId !== templateId) {
106
- throw new CliError("invalid_arguments", `Zone ${room.id} (${room.name}) is already linked to template ${room.linkedTemplateId} (${room.linkedTemplateName ?? "unnamed"}). Unlink that template first.`);
118
+ const otherBookings = room.bookings.filter((booking) => booking.templateId !== templateId);
119
+ const days = templateDays(template);
120
+ const conflict = days
121
+ ? otherBookings.find((booking) => booking.days.some((day) => days.includes(day)))
122
+ : undefined;
123
+ if (conflict) {
124
+ throw new CliError("invalid_arguments", `Zone ${room.id} (${room.name}) is already linked to template ${conflict.templateId} (${conflict.name}). Unlink that template first.`);
107
125
  }
126
+ if (!days)
127
+ informationalBookings = otherBookings;
108
128
  }
109
129
  const previousRoom = template.villageRoomId
110
130
  ? rooms.find((row) => row.id === template.villageRoomId)
@@ -123,6 +143,9 @@ async function linkEvent(ctx) {
123
143
  currentVillageRoomId: template.villageRoomId,
124
144
  },
125
145
  request: body,
146
+ ...(informationalBookings.length > 0 && {
147
+ details: { bookings: informationalBookings },
148
+ }),
126
149
  }, async () => {
127
150
  const result = await api.client.PATCH("/recess/event-templates/{id}/", {
128
151
  params: { path: { id: template.id } },
package/dist/engine.js ADDED
@@ -0,0 +1,6 @@
1
+ export { executeRecessCommand, } from "./cli.js";
2
+ export { buildCommandSchema, findCommandSchema, RECESS_MCP_SCOPES, } from "./command-schema.js";
3
+ export { RecessAdminApi } from "./api.js";
4
+ export { CliError } from "./errors.js";
5
+ export { APP_BUNDLE_EDITABLE_FILES } from "./commands/apps.js";
6
+ //# sourceMappingURL=engine.js.map
package/dist/errors.js CHANGED
@@ -10,9 +10,18 @@ export class CliError extends Error {
10
10
  }
11
11
  }
12
12
  export function apiError(status, body) {
13
- const message = typeof body === "object" && body && "message" in body
14
- ? String(body.message)
15
- : `Recess API request failed with status ${status}`;
16
- return new CliError("api_error", message, 1, { status, body });
13
+ const message = typeof body === "object" && body && "error" in body
14
+ ? String(body.error)
15
+ : typeof body === "object" && body && "message" in body
16
+ ? String(body.message)
17
+ : `Recess API request failed with status ${status}`;
18
+ const code = status === 404
19
+ ? "not_found"
20
+ : status === 409
21
+ ? "conflict"
22
+ : status === 403
23
+ ? "forbidden"
24
+ : "api_error";
25
+ return new CliError(code, message, 1, { status, body });
17
26
  }
18
27
  //# sourceMappingURL=errors.js.map
package/dist/help.js CHANGED
@@ -1,7 +1,17 @@
1
1
  export const HELP = `recess — safe Recess administration and family AI tools
2
2
 
3
3
  Usage:
4
+ recess [--json] mastery graphs [--domain <slug>]
5
+ recess [--json] mastery get <graph-id> [--kind seed|draft|release]
6
+ recess [--json] mastery edit <graph-id> [--kind seed|draft|release]
7
+ [--file <graph.json> | --instruction TEXT] [--node <node-id>] [--confirm]
8
+ recess [--json] mastery publish <draft-id> --expected-active-release <release-id-or-none> [--confirm]
9
+ recess [--json] mastery links --graph <release-id> [--node <node-id>] [--cursor <link-id>]
10
+ recess [--json] mastery link <content-id> --type applet|goal --graph <release-id>
11
+ [--node <node-id>] [--confirm]
12
+ recess [--json] mastery unlink <link-id> [--confirm]
4
13
  recess [--json] --version
14
+ recess [--json] help [noun] [verb]
5
15
  recess [--json] agent-context
6
16
  recess [--json] setup [--skill-only]
7
17
  recess [--json] doctor
@@ -39,10 +49,11 @@ Usage:
39
49
  --first-name TEXT [--last-name TEXT] [--no-invite] [--confirm]
40
50
  recess [--json] students upload-map-scores --student <kid-id>
41
51
  --file </path/to/map-report.pdf> [--confirm]
42
- recess [--json] students list
52
+ recess [--json] students list [--scope mine|family]
43
53
  recess [--json] students today --student <kid-id> [--date YYYY-MM-DD]
44
54
  recess [--json] students schedule --student <kid-id> [--days 14]
45
- recess [--json] students todos --student <kid-id> [--limit 30] [--analyzed]
55
+ recess [--json] students todos --student <id-or-name> [--date <today-or-YYYY-MM-DD>]
56
+ [--limit 30] [--cursor <todo-id>] [--analyzed]
46
57
  recess [--json] students analysis --todo <todo-id>
47
58
  recess [--json] students xp-history --student <kid-id>
48
59
  [--range week|month|quarter|year]
@@ -55,6 +66,7 @@ Usage:
55
66
  --method refund|credit|tokens [--full | --amount-cents N]
56
67
  [--who-pays guide|recess] [--reason TEXT] [--confirm]
57
68
  recess [--json] apps init <dir> [--for-todo <todo-id>] [--force]
69
+ recess [--json] apps scaffold [--for-todo <todo-id>]
58
70
  recess [--json] apps preview [dir] [--port N]
59
71
  recess [--json] apps primitives list
60
72
  recess [--json] apps primitives publish <file.js> --name <kebab-name> [--description TEXT]
@@ -301,12 +313,12 @@ Usage:
301
313
  --status ACTIVE|INACTIVE|COMING_SOON [--confirm]
302
314
  recess [--json] content-library search <query> [--limit 8]
303
315
  recess [--json] content-library status <gem-id-or-url>
304
- recess [--json] content-library set-stage <gem-id-or-url...> [--file <path>]
316
+ recess [--json] content-library set-stage [<gem-id-or-url>...] [--file <path>]
305
317
  --stage review|polishing|live|archived [--wait] [--timeout 900] [--confirm]
306
- recess [--json] content-library submit <url...> [--file <path>]
318
+ recess [--json] content-library submit [<url>...] [--file <path>]
307
319
  [--stage review|polish] [--title TEXT] [--summary TEXT]
308
- [--lane web-toys|mechanics|explorables|data-stories|sims|maps-scale|sound-art|puzzles|wonder|idea-games]
309
- [--wait] [--timeout 900] [--confirm]
320
+ [--lane web-toys|mechanics|explorables|data-stories|sims|maps-scale|sound-art|puzzles|wonder|idea-games|makers|drills]
321
+ [--allow-possible-duplicate] [--wait] [--timeout 900] [--confirm]
310
322
  recess [--json] skills guardian list [--query TEXT] [--category TEXT]
311
323
  recess [--json] skills guardian get <skill-name>
312
324
  [--reference NAME | --all-references] [--refresh]
@@ -353,6 +365,8 @@ Usage:
353
365
  --delta TEXT [--confirm --approval-token TOKEN]
354
366
  recess [--json] goals delete <goal-id> --student <kid-id>
355
367
  [--confirm --approval-token TOKEN]
368
+ recess [--json] goals restore <goal-id> --student <kid-id>
369
+ [--confirm --approval-token TOKEN]
356
370
  recess [--json] goals complete <goal-id> [--confirm]
357
371
  recess [--json] goals undo-completion <goal-id> [--confirm]
358
372
  recess [--json] goals archive <goal-id> --student <kid-id>
@@ -371,6 +385,9 @@ Usage:
371
385
  [--confirm --approval-token TOKEN]
372
386
  recess [--json] todos delete <todo-id>
373
387
  [--confirm --approval-token TOKEN]
388
+ recess [--json] todos restore <todo-id>
389
+ [--confirm --approval-token TOKEN]
390
+ recess [--json] todos generate-learning-analysis <todo-id> [--confirm]
374
391
  recess [--json] todos generate-applet <todo-id> --student <kid-id>
375
392
  [--due-date YYYY-MM-DD] [--confirm --approval-token TOKEN]
376
393
  recess [--json] memories context --student <kid-id>
@@ -0,0 +1,19 @@
1
+ // A mirror of `apps/web-server/src/services/os-v2/upload-names.ts` — the one
2
+ // rule for what a goal workspace stores an upload under (PROD-983). Read that
3
+ // module for why the rule is what it is; change it there first, then here.
4
+ //
5
+ // A copy, not an import, because `recess-cli` is published to npm from this
6
+ // directory's own `tsc` output and every workspace package it could import the
7
+ // rule from is private, so a `workspace:*` dependency would leave
8
+ // `npm i -g recess-cli` unable to resolve it. `skills-cache.ts` and
9
+ // `skill-update.ts` mirror web-server code for the same reason.
10
+ /**
11
+ * The name a workspace stores an upload under: safe for a Mesa path, stable,
12
+ * idempotent, and total — never empty, never `.` or `..`, never a path
13
+ * separator, never a dotfile.
14
+ */
15
+ export function sanitizeWorkspaceUploadName(original) {
16
+ const cleaned = original.replace(/^\.+/, "").replace(/[^a-zA-Z0-9._-]/g, "_");
17
+ return cleaned || "file";
18
+ }
19
+ //# sourceMappingURL=upload-names.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recess-cli",
3
- "version": "2.6.1",
3
+ "version": "2.8.0",
4
4
  "description": "Safe Recess administration and family AI tools from the command line.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -9,6 +9,13 @@
9
9
  "directory": "apps/admin-cli"
10
10
  },
11
11
  "type": "module",
12
+ "exports": {
13
+ "./engine": {
14
+ "development": "./src/engine.ts",
15
+ "types": "./src/engine.ts",
16
+ "default": "./dist/engine.js"
17
+ }
18
+ },
12
19
  "bin": {
13
20
  "recess": "dist/index.js"
14
21
  },
@@ -50,7 +50,7 @@ Never print or paste session cookies, device codes, or config-file contents.
50
50
 
51
51
  ## Agent contract
52
52
 
53
- Always pass `--json`. Stdout contains one object:
53
+ With the local binary, always pass `--json`. Stdout contains one object:
54
54
 
55
55
  - Success: `{"ok":true,"data":{...}}`, exit 0.
56
56
  - Failure: `{"ok":false,"error":{"code":"...","message":"...","details":{...}}}`, exit 1.
@@ -70,6 +70,21 @@ Every write is two-step:
70
70
 
71
71
  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>`.
72
72
 
73
+ ### Hosted MCP transport
74
+
75
+ When this skill is reached through the hosted Recess connector, use its only tool,
76
+ `recess_exec({argv,input?})`. `argv` starts with the command noun, omits both `recess` and `--json`,
77
+ and still carries `--reason`. The CLI envelope is returned in MCP `structuredContent`; a
78
+ `confirmation_required` envelope is expected control flow, while other failures are tool errors.
79
+ Start with `argv:["agent-context"]` or `argv:["help","apps"]` for the remote-only catalog. OAuth
80
+ already supplies the live user and scopes: never ask for cookies, profiles, local paths, or
81
+ `--deliver`.
82
+
83
+ Hosted writes stage the exact argv and structured payload on the server. Preview without
84
+ `--confirm`, show the human `error.details.preview`, then confirm with the same command path plus
85
+ `--confirm --operation-key <key>` and **omit `input`**. The server executes the stored bytes; changed
86
+ source, flags, project/version, or operation keys require a fresh preview.
87
+
73
88
  ## Building Studio apps (`recess apps`)
74
89
 
75
90
  Guides build Recess Studio apps in their own editor and publish them with the CLI; Studio never
@@ -91,6 +106,21 @@ prompts a model for them.
91
106
  - `recess --json apps standards "<words or notation>"` finds the CCSS standard for `manifest.json`
92
107
  (`"grade 4 adding fractions"` → `4.NF.B.3a`…); guides rarely know the codes, look them up.
93
108
 
109
+ Hosted authoring keeps those same contracts but carries no folder:
110
+
111
+ - `apps scaffold [--for-todo <id>]` returns authoring instructions, the five editable starter
112
+ files, fork guidance, and an optional learner brief. It performs no write.
113
+ - Generate `input.appBundle` with exactly `skill.js`, `model.js`, `model.css`,
114
+ `params.schema.json`, and `params.json`, plus `contract` and `manifest`. Preview and confirm
115
+ `apps validate`; keep the returned project/version/build IDs and poll `apps status <build-id>`.
116
+ - Fix findings in the bundle. For an existing project, first `apps pull <project-id>` and preserve
117
+ its `baseVersion`; a stale version is a conflict, never an overwrite.
118
+ - `apps preview <project-id> --version <n>` returns the short-lived capability URL.
119
+ Preview/confirm `apps publish <project-id> --version <n>`, then poll its build. Publication sends
120
+ no source and reviews exactly that validated version.
121
+ - After publication, preview/confirm `apps assign <project-id> --student <id> [--due YYYY-MM-DD]`.
122
+ Assignment is intentionally separate from hosted publish.
123
+
94
124
  Two ways in. If the guide names a kid or a todo, pull the analysis first and build against it;
95
125
  otherwise build from their description:
96
126
 
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "2.5.0",
3
- "minCliVersion": "2.4.0"
2
+ "version": "2.6.0",
3
+ "minCliVersion": "2.6.1"
4
4
  }