recess-cli 3.0.0 → 3.2.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 +7 -1
- package/dist/args.js +1 -0
- package/dist/auth.js +8 -3
- package/dist/cli.js +615 -48
- package/dist/command-schema.js +20 -9
- package/dist/commands/apps.js +10 -1
- package/dist/commands/goal-curriculum.js +187 -0
- package/dist/help.js +56 -7
- package/package.json +1 -1
- package/skill/recess-cli/SKILL.md +3 -2
- package/skill/recess-cli/agents/version.json +2 -2
package/dist/command-schema.js
CHANGED
|
@@ -2,6 +2,8 @@ import { CliError } from "./errors.js";
|
|
|
2
2
|
export const AGENT_CONTEXT_SCHEMA_VERSION = "4";
|
|
3
3
|
const BOOLEAN_FLAGS = new Set([
|
|
4
4
|
"allow-possible-duplicate",
|
|
5
|
+
"allow-state-modification",
|
|
6
|
+
"allow-assigned-edits",
|
|
5
7
|
"all-references",
|
|
6
8
|
"analyzed",
|
|
7
9
|
"apply",
|
|
@@ -27,6 +29,7 @@ const BOOLEAN_FLAGS = new Set([
|
|
|
27
29
|
"mirrored",
|
|
28
30
|
"no-collision",
|
|
29
31
|
"no-invite",
|
|
32
|
+
"open",
|
|
30
33
|
"refresh",
|
|
31
34
|
"resend",
|
|
32
35
|
"restore",
|
|
@@ -139,6 +142,15 @@ const AUTHENTICATED_COMMAND_PREFIXES = [
|
|
|
139
142
|
"village render",
|
|
140
143
|
];
|
|
141
144
|
const FAMILY_AI_COMMANDS = new Set([
|
|
145
|
+
"apps assign",
|
|
146
|
+
"apps list",
|
|
147
|
+
"apps preview",
|
|
148
|
+
"apps publish",
|
|
149
|
+
"apps pull",
|
|
150
|
+
"apps scaffold",
|
|
151
|
+
"apps standards",
|
|
152
|
+
"apps status",
|
|
153
|
+
"apps validate",
|
|
142
154
|
"chat-logs by-todo",
|
|
143
155
|
"chat-logs get",
|
|
144
156
|
"chat-logs list",
|
|
@@ -161,6 +173,7 @@ const FAMILY_AI_COMMANDS = new Set([
|
|
|
161
173
|
"goals files read",
|
|
162
174
|
"goals files write",
|
|
163
175
|
"goals list",
|
|
176
|
+
"goals move-module",
|
|
164
177
|
"goals pdf upload",
|
|
165
178
|
"goals queue get",
|
|
166
179
|
"goals queue set",
|
|
@@ -179,6 +192,10 @@ const FAMILY_AI_COMMANDS = new Set([
|
|
|
179
192
|
"students xp-history",
|
|
180
193
|
"todos create",
|
|
181
194
|
"todos edit",
|
|
195
|
+
"tutor-templates assign",
|
|
196
|
+
"tutor-templates get",
|
|
197
|
+
"tutor-templates list",
|
|
198
|
+
"tutor-templates unassign",
|
|
182
199
|
]);
|
|
183
200
|
const STAFF_COMMANDS = new Set([
|
|
184
201
|
"cohorts schedule get",
|
|
@@ -191,15 +208,6 @@ const STAFF_COMMANDS = new Set([
|
|
|
191
208
|
"memories history",
|
|
192
209
|
"memories revision",
|
|
193
210
|
"memories restore",
|
|
194
|
-
"apps assign",
|
|
195
|
-
"apps list",
|
|
196
|
-
"apps preview",
|
|
197
|
-
"apps publish",
|
|
198
|
-
"apps pull",
|
|
199
|
-
"apps scaffold",
|
|
200
|
-
"apps standards",
|
|
201
|
-
"apps status",
|
|
202
|
-
"apps validate",
|
|
203
211
|
"cohorts email",
|
|
204
212
|
"cohorts end",
|
|
205
213
|
"cohorts get",
|
|
@@ -291,6 +299,9 @@ const STAFF_COMMANDS = new Set([
|
|
|
291
299
|
"todos delete",
|
|
292
300
|
"todos restore",
|
|
293
301
|
"todos generate-applet",
|
|
302
|
+
"tutor-templates create",
|
|
303
|
+
"tutor-templates delete",
|
|
304
|
+
"tutor-templates update",
|
|
294
305
|
"users get",
|
|
295
306
|
"users tier list-tiers",
|
|
296
307
|
"village events",
|
package/dist/commands/apps.js
CHANGED
|
@@ -358,13 +358,22 @@ async function submit(ctx, dir, dryRun) {
|
|
|
358
358
|
const manifest = await readManifest(dir);
|
|
359
359
|
const link = await readAppLink(dir);
|
|
360
360
|
const name = flagString(ctx.parsed, "name") ?? manifest.title ?? path.basename(dir);
|
|
361
|
+
// --open ships the folder's frame as written: Studio skips the locked-scaffold check and the
|
|
362
|
+
// guide's own shell, styles and screens are what the kid gets. Plain apps already own every file.
|
|
363
|
+
const open = hasFlag(ctx.parsed, "open");
|
|
364
|
+
if (open && !manifest.template) {
|
|
365
|
+
throw new CliError("invalid_arguments", "--open is for template apps (manifest.template); a plain app already ships every file as written.");
|
|
366
|
+
}
|
|
367
|
+
const template = manifest.template
|
|
368
|
+
? { ...manifest.template, ...(open ? { scaffold: false } : {}) }
|
|
369
|
+
: undefined;
|
|
361
370
|
const body = {
|
|
362
371
|
...(link?.projectId ? { projectId: link.projectId } : {}),
|
|
363
372
|
name,
|
|
364
373
|
...(manifest.description ? { description: manifest.description } : {}),
|
|
365
374
|
files,
|
|
366
375
|
contract,
|
|
367
|
-
...(
|
|
376
|
+
...(template ? { template } : {}),
|
|
368
377
|
...(manifest.primitives?.length ? { primitives: manifest.primitives } : {}),
|
|
369
378
|
dryRun,
|
|
370
379
|
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { unwrap } from "../api.js";
|
|
5
|
+
import { flagString } from "../args.js";
|
|
6
|
+
import { CliError } from "../errors.js";
|
|
7
|
+
import { flagInteger, positional } from "./shared.js";
|
|
8
|
+
/** Selection files only supply explicit IDs; they never expand a filter into a mutation. */
|
|
9
|
+
export async function readAssignmentSelection(filePath) {
|
|
10
|
+
const absolutePath = path.resolve(filePath);
|
|
11
|
+
const stat = await fs.stat(absolutePath);
|
|
12
|
+
if (!stat.isFile() || stat.size > 1024 * 1024) {
|
|
13
|
+
throw new CliError("invalid_arguments", "The assignment selection must be a regular JSON file no larger than 1 MiB.");
|
|
14
|
+
}
|
|
15
|
+
const bytes = await fs.readFile(absolutePath);
|
|
16
|
+
let document;
|
|
17
|
+
try {
|
|
18
|
+
document = JSON.parse(bytes.toString("utf8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new CliError("invalid_arguments", "The assignment selection must contain valid JSON.");
|
|
22
|
+
}
|
|
23
|
+
if (!document ||
|
|
24
|
+
typeof document !== "object" ||
|
|
25
|
+
Array.isArray(document) ||
|
|
26
|
+
Object.keys(document).length !== 1 ||
|
|
27
|
+
!("todoIds" in document) ||
|
|
28
|
+
!Array.isArray(document.todoIds) ||
|
|
29
|
+
!document.todoIds.every((id) => typeof id === "string")) {
|
|
30
|
+
throw new CliError("invalid_arguments", 'The selection file must contain exactly {"todoIds":["assignment-uuid", ...]}.');
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
todoIds: document.todoIds,
|
|
34
|
+
file: {
|
|
35
|
+
absolutePath,
|
|
36
|
+
sizeBytes: bytes.byteLength,
|
|
37
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function readResolutions(filePath) {
|
|
42
|
+
const absolutePath = path.resolve(filePath);
|
|
43
|
+
let bytes;
|
|
44
|
+
try {
|
|
45
|
+
const stat = await fs.stat(absolutePath);
|
|
46
|
+
if (!stat.isFile() || stat.size > 1024 * 1024) {
|
|
47
|
+
throw new CliError("invalid_arguments", "The resolution file must be a regular JSON file no larger than 1 MiB.");
|
|
48
|
+
}
|
|
49
|
+
bytes = await fs.readFile(absolutePath);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error.code === "ENOENT") {
|
|
53
|
+
throw new CliError("invalid_arguments", `File not found: ${absolutePath}`);
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
let document;
|
|
58
|
+
try {
|
|
59
|
+
document = JSON.parse(bytes.toString("utf8"));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw new CliError("invalid_arguments", "The resolution file must contain valid JSON.");
|
|
63
|
+
}
|
|
64
|
+
if (!document || typeof document !== "object" || Array.isArray(document)) {
|
|
65
|
+
throw new CliError("invalid_arguments", "The resolution file must contain a JSON object.");
|
|
66
|
+
}
|
|
67
|
+
// The backend validates document version, choices, conflict IDs, and whether
|
|
68
|
+
// each choice is supported by the current three-way comparison.
|
|
69
|
+
return {
|
|
70
|
+
document: document,
|
|
71
|
+
file: {
|
|
72
|
+
absolutePath,
|
|
73
|
+
sizeBytes: bytes.byteLength,
|
|
74
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export async function runGoalCurriculumCommand({ parsed, api, resolveTemplateId, previewBoundWrite, recoverExecutedPreview, }) {
|
|
79
|
+
const verb = positional(parsed, 2, "curriculum operation");
|
|
80
|
+
const [id, session] = await Promise.all([
|
|
81
|
+
resolveTemplateId(positional(parsed, 3, "template ID or slug")),
|
|
82
|
+
api.client.GET("/auth/admin-cli/session/").then(unwrap),
|
|
83
|
+
]);
|
|
84
|
+
if (session.user.role !== "ADMIN" || session.cliScope !== "full_admin") {
|
|
85
|
+
throw new CliError("forbidden", "Curriculum updates require a full-admin session.");
|
|
86
|
+
}
|
|
87
|
+
const environment = {
|
|
88
|
+
actorId: session.user.id,
|
|
89
|
+
apiOrigin: api.config.apiOrigin,
|
|
90
|
+
};
|
|
91
|
+
if (verb === "status" || verb === "retry") {
|
|
92
|
+
const jobId = flagString(parsed, "job", { required: true });
|
|
93
|
+
if (verb === "status") {
|
|
94
|
+
return unwrap(await api.client.GET("/ai/goal-templates/{id}/curriculum/jobs/{jobId}", {
|
|
95
|
+
params: { path: { id, jobId } },
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
const requestPreview = {
|
|
99
|
+
action: "resume the previously approved curriculum job",
|
|
100
|
+
target: { templateId: id, jobId, ...environment },
|
|
101
|
+
request: {},
|
|
102
|
+
};
|
|
103
|
+
const receipt = await recoverExecutedPreview(requestPreview);
|
|
104
|
+
const preview = receipt ?? {
|
|
105
|
+
...requestPreview,
|
|
106
|
+
details: {
|
|
107
|
+
job: unwrap(await api.client.GET("/ai/goal-templates/{id}/curriculum/jobs/{jobId}", {
|
|
108
|
+
params: { path: { id, jobId } },
|
|
109
|
+
})),
|
|
110
|
+
note: "Resumes this job's stored scope and resolutions. Completed goals are not replayed; stale goals require a new preview.",
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return previewBoundWrite(parsed, preview, async () => unwrap(await api.client.POST("/ai/goal-templates/{id}/curriculum/jobs/{jobId}/retry", {
|
|
114
|
+
params: { path: { id, jobId } },
|
|
115
|
+
body: {},
|
|
116
|
+
})));
|
|
117
|
+
}
|
|
118
|
+
const goalIds = flagString(parsed, "goals", { required: true })
|
|
119
|
+
.split(",")
|
|
120
|
+
.map((value) => value.trim());
|
|
121
|
+
if (goalIds.some((value) => !value) ||
|
|
122
|
+
new Set(goalIds).size !== goalIds.length) {
|
|
123
|
+
throw new CliError("invalid_arguments", "--goals must list distinct, nonempty goal IDs.");
|
|
124
|
+
}
|
|
125
|
+
if (verb === "backlog") {
|
|
126
|
+
const dueBefore = flagString(parsed, "due-before");
|
|
127
|
+
return unwrap(await api.client.GET("/ai/goal-templates/{id}/curriculum/backlog", {
|
|
128
|
+
params: {
|
|
129
|
+
path: { id },
|
|
130
|
+
query: { goalIds: goalIds.join(","), dueBefore },
|
|
131
|
+
},
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
const targetVersion = flagInteger(parsed, "version", {
|
|
135
|
+
required: true,
|
|
136
|
+
min: 1,
|
|
137
|
+
});
|
|
138
|
+
const filePath = flagString(parsed, "resolutions-file", {
|
|
139
|
+
required: verb === "validate-resolutions",
|
|
140
|
+
});
|
|
141
|
+
const resolutionFile = filePath ? await readResolutions(filePath) : undefined;
|
|
142
|
+
const body = {
|
|
143
|
+
goalIds,
|
|
144
|
+
targetVersion,
|
|
145
|
+
resolutions: resolutionFile?.document ?? { version: 1, resolutions: [] },
|
|
146
|
+
};
|
|
147
|
+
const requestPreview = {
|
|
148
|
+
action: verb === "reverse"
|
|
149
|
+
? "merge a prior template curriculum version into the selected student goals"
|
|
150
|
+
: "merge the selected template curriculum version into the selected student goals",
|
|
151
|
+
target: { templateId: id, goalIds, ...environment },
|
|
152
|
+
request: { ...body, resolutionFile: resolutionFile?.file ?? null },
|
|
153
|
+
};
|
|
154
|
+
const receipt = verb === "apply" || verb === "reverse"
|
|
155
|
+
? await recoverExecutedPreview(requestPreview)
|
|
156
|
+
: null;
|
|
157
|
+
const comparison = receipt
|
|
158
|
+
? receipt.details?.comparison
|
|
159
|
+
: unwrap(await api.client.POST("/ai/goal-templates/{id}/curriculum/preview", {
|
|
160
|
+
params: { path: { id } },
|
|
161
|
+
body,
|
|
162
|
+
}));
|
|
163
|
+
if (verb === "preview" || verb === "validate-resolutions") {
|
|
164
|
+
return { ...comparison, resolutionFile: resolutionFile?.file ?? null };
|
|
165
|
+
}
|
|
166
|
+
if (verb !== "apply" && verb !== "reverse") {
|
|
167
|
+
throw new CliError("invalid_arguments", "Use curriculum backlog|preview|validate-resolutions|apply|status|retry|reverse.");
|
|
168
|
+
}
|
|
169
|
+
const preview = receipt ?? {
|
|
170
|
+
...requestPreview,
|
|
171
|
+
details: {
|
|
172
|
+
comparison,
|
|
173
|
+
note: "Only the exact selected goals are included. Unresolved conflicts block their goal. Assignments and earned history remain protected; reverse updates preserve changes made since the earlier version.",
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
return previewBoundWrite(parsed, preview, async () => unwrap(await api.client.POST("/ai/goal-templates/{id}/curriculum/apply", {
|
|
177
|
+
params: { path: { id } },
|
|
178
|
+
body: {
|
|
179
|
+
...body,
|
|
180
|
+
fingerprint: comparison.fingerprint,
|
|
181
|
+
operationKey: flagString(parsed, "operation-key", {
|
|
182
|
+
required: true,
|
|
183
|
+
}),
|
|
184
|
+
},
|
|
185
|
+
})));
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=goal-curriculum.js.map
|
package/dist/help.js
CHANGED
|
@@ -28,8 +28,8 @@ Usage:
|
|
|
28
28
|
recess [--json] jobs prune [--older-than-days 30]
|
|
29
29
|
recess [--json] feedback list [--limit 20]
|
|
30
30
|
recess [--json] feedback submit <text> [--confirm]
|
|
31
|
-
recess [--json] auth login [--client-id ID] [--callback-port 8765]
|
|
32
|
-
recess [--json] auth request [--label TEXT]
|
|
31
|
+
recess [--json] auth login [--client-id ID] [--callback-port 8765] [--duration 12h|30d]
|
|
32
|
+
recess [--json] auth request [--label TEXT] [--duration 12h|30d]
|
|
33
33
|
recess [--json] auth poll [--timeout 300]
|
|
34
34
|
recess [--json] auth status|logout
|
|
35
35
|
recess [--json] users search <name-or-id> [--limit 10]
|
|
@@ -78,8 +78,8 @@ Usage:
|
|
|
78
78
|
recess [--json] apps standards <query>
|
|
79
79
|
recess [--json] apps list
|
|
80
80
|
recess [--json] apps pull <project-id> [dir]
|
|
81
|
-
recess [--json] apps validate [dir] [--name TEXT] [--no-wait]
|
|
82
|
-
recess [--json] apps publish [dir] [--name TEXT] [--assign <kid-id>] [--due YYYY-MM-DD] [--no-wait]
|
|
81
|
+
recess [--json] apps validate [dir] [--name TEXT] [--open] [--no-wait]
|
|
82
|
+
recess [--json] apps publish [dir] [--name TEXT] [--open] [--assign <kid-id>] [--due YYYY-MM-DD] [--no-wait]
|
|
83
83
|
recess [--json] apps assign [dir] --student <kid-id> [--due YYYY-MM-DD]
|
|
84
84
|
recess [--json] apps status <build-id>
|
|
85
85
|
recess [--json] applications list [--status SUBMITTED|CLAIMED|ENROLLED|CLOSED]
|
|
@@ -339,6 +339,29 @@ Usage:
|
|
|
339
339
|
[--kind SIMPLE|BLUEPRINT] [--starter-only] [--include-deleted]
|
|
340
340
|
[--limit 20] [--cursor <id>]
|
|
341
341
|
recess [--json] goal-templates get <template-id|slug> [--spec-only]
|
|
342
|
+
recess [--json] goal-templates policy <template-id|slug>
|
|
343
|
+
recess [--json] goal-templates set-policy <template-id|slug> --recipe KEY
|
|
344
|
+
[--target-modules N] [--required-modules N|all] --expected-version N
|
|
345
|
+
[--confirm --approval-token TOKEN]
|
|
346
|
+
recess [--json] goal-templates migrate-policy <template-id|slug> --students ID,ID
|
|
347
|
+
[--confirm --approval-token TOKEN]
|
|
348
|
+
recess [--json] goal-templates student-policy <template-id|slug> --goal ID
|
|
349
|
+
--file <path/edit.json> [--confirm --approval-token TOKEN]
|
|
350
|
+
recess [--json] goal-templates retire-assignments <template-id|slug> --goal ID
|
|
351
|
+
[--todos ID,ID | --todos-file <path/selection.json>]
|
|
352
|
+
--retirement-reason TEXT [--confirm --approval-token TOKEN]
|
|
353
|
+
recess [--json] goal-templates curriculum backlog <template-id|slug>
|
|
354
|
+
--goals ID,ID [--due-before YYYY-MM-DD]
|
|
355
|
+
recess [--json] goal-templates curriculum preview <template-id|slug>
|
|
356
|
+
--goals ID,ID --version N [--resolutions-file <path/resolutions.json>]
|
|
357
|
+
recess [--json] goal-templates curriculum validate-resolutions <template-id|slug>
|
|
358
|
+
--goals ID,ID --version N --resolutions-file <path/resolutions.json>
|
|
359
|
+
recess [--json] goal-templates curriculum apply|reverse <template-id|slug>
|
|
360
|
+
--goals ID,ID --version N [--resolutions-file <path/resolutions.json>]
|
|
361
|
+
[--confirm --approval-token TOKEN]
|
|
362
|
+
recess [--json] goal-templates curriculum status <template-id|slug> --job ID
|
|
363
|
+
recess [--json] goal-templates curriculum retry <template-id|slug> --job ID
|
|
364
|
+
[--confirm --approval-token TOKEN]
|
|
342
365
|
recess [--json] goal-templates versions <template-id> [--version N]
|
|
343
366
|
recess [--json] goal-templates validate-spec --file <path/template.json>
|
|
344
367
|
recess [--json] goal-templates create --file <path/template.json>
|
|
@@ -382,6 +405,8 @@ Usage:
|
|
|
382
405
|
[--confirm --approval-token TOKEN]
|
|
383
406
|
recess [--json] goals unarchive <goal-id> --student <kid-id>
|
|
384
407
|
[--confirm --approval-token TOKEN]
|
|
408
|
+
recess [--json] goals move-module <module-ref> --student <kid-id> --goal <goal-id>
|
|
409
|
+
(--to-end | --position <n>) [--confirm --approval-token TOKEN]
|
|
385
410
|
recess [--json] goals queue get <goal-id> --student <kid-id>
|
|
386
411
|
recess [--json] goals queue set <goal-id> --student <kid-id>
|
|
387
412
|
--entries-file <path.json> --delta TEXT [--replace-description-pointer]
|
|
@@ -412,6 +437,19 @@ Usage:
|
|
|
412
437
|
recess [--json] rocky get --student <kid-id>
|
|
413
438
|
recess [--json] rocky set --student <kid-id> --patch-file <path/patch.json>
|
|
414
439
|
--expected-updated-at ISO|none [--confirm]
|
|
440
|
+
recess [--json] tutor-templates list [--q TEXT] [--subject TEXT] [--grade N]
|
|
441
|
+
recess [--json] tutor-templates get <slug>
|
|
442
|
+
recess [--json] tutor-templates assign <slug> [--student <kid-id>] [--confirm]
|
|
443
|
+
recess [--json] tutor-templates unassign <slug> [--student <kid-id>] [--confirm]
|
|
444
|
+
recess [--json] tutor-templates create --slug SLUG --title TEXT --summary TEXT
|
|
445
|
+
--subject TEXT --cadence TEXT --body-file <path.md> [--lessons-dir <dir>]
|
|
446
|
+
[--emoji TEXT] [--grade-min N] [--grade-max N]
|
|
447
|
+
[--visibility PUBLIC|STAFF] [--confirm]
|
|
448
|
+
recess [--json] tutor-templates update <slug> [--title TEXT] [--summary TEXT]
|
|
449
|
+
[--subject TEXT] [--cadence TEXT] [--body-file <path.md>]
|
|
450
|
+
[--lessons-dir <dir>] [--emoji TEXT] [--grade-min N] [--grade-max N]
|
|
451
|
+
[--visibility PUBLIC|STAFF] [--confirm]
|
|
452
|
+
recess [--json] tutor-templates delete <slug> [--confirm]
|
|
415
453
|
recess [--json] goals files list --student <goal-owner-id> --goal <goal-id>
|
|
416
454
|
recess [--json] goals files read --student <goal-owner-id> --goal <goal-id> --path P
|
|
417
455
|
recess [--json] goals files init --student <goal-owner-id> --draft <draft-slug>
|
|
@@ -420,11 +458,13 @@ Usage:
|
|
|
420
458
|
(--goal <goal-id> | --draft <draft-slug>)
|
|
421
459
|
--output-dir <local-dir>
|
|
422
460
|
recess [--json] goals files push --source-dir <local-checkout>
|
|
423
|
-
[--message TEXT] [--
|
|
461
|
+
[--message TEXT] [--allow-state-modification] [--allow-assigned-edits]
|
|
462
|
+
[--confirm --approval-token TOKEN]
|
|
424
463
|
recess [--json] goals files write --student <goal-owner-id>
|
|
425
464
|
(--goal <goal-id> | --draft <draft-slug>)
|
|
426
465
|
(--source-dir <local-dir> | --source-file <local-file> --path P)
|
|
427
|
-
[--message TEXT] [--
|
|
466
|
+
[--message TEXT] [--allow-state-modification] [--allow-assigned-edits]
|
|
467
|
+
[--confirm --approval-token TOKEN]
|
|
428
468
|
recess [--json] goals pdf upload --student <goal-owner-id>
|
|
429
469
|
(--goal <goal-id> | --draft <draft-slug>) --source-file <local.pdf>
|
|
430
470
|
[--path uploads/name.pdf] [--message TEXT]
|
|
@@ -436,6 +476,14 @@ bundled into this npm package. Load os-v2-goal-template-builder and its
|
|
|
436
476
|
references/deterministic-workflow-setup.md BEFORE authoring a template; that is
|
|
437
477
|
the same guidance the recess.gg/ai agent follows, so there is exactly one
|
|
438
478
|
standard. Responses cache under ~/.recess-cli/skills-cache/ (--refresh re-fetches).
|
|
479
|
+
Goal file writes/pushes warn and refuse curriculum edits with unfinished assignments
|
|
480
|
+
unless --allow-assigned-edits is supplied. This can change lessons while a student
|
|
481
|
+
is working; assignments are not retired/completed and student answers are not merged.
|
|
482
|
+
The flag requires a fresh preview and confirmation; revision checks still apply.
|
|
483
|
+
Goal file writes/pushes require --allow-state-modification to modify state or
|
|
484
|
+
student-work folders, or replace/delete an existing resources/artifacts file.
|
|
485
|
+
Review the protected paths in the warning; the flag does not preserve answers
|
|
486
|
+
automatically or bypass assignment/revision checks. Adding it needs a new preview.
|
|
439
487
|
For goal workspace and PDF commands, --student names the goal owner. A
|
|
440
488
|
full_admin session may use a KID or ADMIN user id, including its own ADMIN id;
|
|
441
489
|
family_ai sessions remain limited to managed KID profiles.
|
|
@@ -528,7 +576,8 @@ cannot call /admin; KID sessions cannot call any non-Village API command. For a
|
|
|
528
576
|
cloud agent that has no local browser to open, "auth request" prints an approval URL; whoever
|
|
529
577
|
opens it and approves in a signed-in web session grants THEIR OWN scope, so a guardian approving
|
|
530
578
|
mints a family-scoped session and only an admin can mint a full-admin one. "auth poll" then
|
|
531
|
-
collects the
|
|
579
|
+
collects the session. Sessions default to 12h; admins can request 30 days with --duration 30d
|
|
580
|
+
on either login or request. Only admins can approve 30-day requests. Kids cannot approve. When it lapses, run "auth request" again for a
|
|
532
581
|
fresh link. Both paths yield the same session.
|
|
533
582
|
|
|
534
583
|
Skill notes: this CLI's own agent skill ships inside the npm package AND is served
|
package/package.json
CHANGED
|
@@ -41,7 +41,8 @@ For machine-readable discovery filtered to the current session's locally decoded
|
|
|
41
41
|
- Headless (no browser): `auth request`, then a human approves the printed URL while signed in to
|
|
42
42
|
Recess, then `auth poll`. The session takes on the **approver's** scope — a guardian approving
|
|
43
43
|
grants family-only access, not staff access. Kids cannot approve.
|
|
44
|
-
- Sessions
|
|
44
|
+
- Sessions default to 12 hours. Admins can use `auth login --duration 30d` locally or
|
|
45
|
+
`auth request --duration 30d` remotely; only an admin can approve a 30-day request. `auth status` and `doctor` recheck the live user role and permissions;
|
|
45
46
|
discovery stays local and may reflect the minted scope until the next login.
|
|
46
47
|
- `auth logout` clears the stored session.
|
|
47
48
|
- The default API is production. If `RECESS_CLI_API_ORIGIN` is set, state the non-default origin before acting.
|
|
@@ -90,7 +91,7 @@ bundles/authorized URLs. `/mcp/legacy` temporarily retains `recess_exec({command
|
|
|
90
91
|
|
|
91
92
|
## Building Studio apps (`recess apps`)
|
|
92
93
|
|
|
93
|
-
Guides build Recess Studio apps in their own editor and publish them with the CLI; Studio never
|
|
94
|
+
Guides and guardians build Recess Studio apps in their own editor and publish them with the CLI; Studio never
|
|
94
95
|
prompts a model for them.
|
|
95
96
|
|
|
96
97
|
- `recess --json apps init <dir> --reason "..."` writes the locked scaffold plus `AGENTS.md` (the
|