recess-cli 1.3.0 → 1.3.3
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 +17 -1
- package/dist/cli.js +146 -2
- package/package.json +1 -1
- package/skill/recess-cli/SKILL.md +26 -5
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ recess setup
|
|
|
13
13
|
|
|
14
14
|
`setup` installs the bundled skill for both Codex and Claude and then opens Recess SSO in your browser (skip the browser step with `--skill-only`; it is also skipped when a live session already exists). Restart your agent afterwards so it discovers the skill. `npx -y recess-cli setup` works too, but leaves no `recess` on your PATH — which is the command the installed skill tells the agent to run — so `setup` warns when it detects it is running from an npx cache.
|
|
15
15
|
|
|
16
|
-
Publishing rides the production deploy (`.github/workflows/admin-cli-publish.yml`): bump `version` in `apps/admin-cli/package.json` in a normal PR to `staging`, and it publishes when `staging` promotes to `production`. A production deploy that did not bump the version is a no-op — a `gate` job checks the version against npm first. The same workflow is still dispatchable by hand for out-of-band releases. pnpm packs the CLI so the workspace `catalog:` dependency becomes a real range; npm publishes that tarball through the
|
|
16
|
+
Publishing rides the production deploy (`.github/workflows/admin-cli-publish.yml`): bump `version` in `apps/admin-cli/package.json` in a normal PR to `staging`, and it publishes when `staging` promotes to `production`. A production deploy that did not bump the version is a no-op — a `gate` job checks the version against npm first. The same workflow is still dispatchable by hand for out-of-band releases. pnpm packs the CLI so the workspace `catalog:` dependency becomes a real range; npm then publishes that tarball through the workflow's OIDC trusted-publishing path. The registry-side publisher must be configured as described in `docs/codebase/admin-cli.md`.
|
|
17
17
|
|
|
18
18
|
## Install (from a checkout — CLI development)
|
|
19
19
|
|
|
@@ -92,10 +92,26 @@ human explicitly approves the exceptional `--allow-strand` override.
|
|
|
92
92
|
|
|
93
93
|
MAP uploads accept one PDF up to 15 MB. The preview includes the resolved path, byte count, and SHA-256 without contacting the API; the confirmed command sends the report to the existing tutor-dashboard extraction route.
|
|
94
94
|
|
|
95
|
+
## Store catalog and Village
|
|
96
|
+
|
|
97
|
+
Village store goods are main-Recess `StoreItem` rows, not Village-island database rows. List them
|
|
98
|
+
and preview an availability change with:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
recess --json store-items list --item-type VILLAGE_ITEM
|
|
102
|
+
recess --json store-items set-status <store-item-id> --status INACTIVE
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The status command resolves the exact `VILLAGE_ITEM` first and includes its name, catalog metadata,
|
|
106
|
+
current status, purchase count, and lack of feed-publication side effects in the approval preview;
|
|
107
|
+
it refuses non-Village item IDs. After approval, rerun the unchanged command with `--confirm`. The
|
|
108
|
+
separate `village models` commands edit the Village island's reusable models and placements.
|
|
109
|
+
|
|
95
110
|
## Authoring learning content
|
|
96
111
|
|
|
97
112
|
```bash
|
|
98
113
|
recess --json skills get os-v2-goal-template-builder --all-references
|
|
114
|
+
recess --json content-library search "fractions through visual puzzles" --limit 8
|
|
99
115
|
recess --json goal-templates validate-spec --file ./template.json # iterate; writes nothing
|
|
100
116
|
recess --json goal-templates create --file ./template.json # preview, exit 2
|
|
101
117
|
recess --json goal-templates create --file ./template.json --confirm
|
package/dist/cli.js
CHANGED
|
@@ -113,6 +113,13 @@ Usage:
|
|
|
113
113
|
recess [--json] village models remove <placement-id> [--world village-1] [--confirm]
|
|
114
114
|
recess [--json] village render --min-x N --min-z N --max-x N --max-z N
|
|
115
115
|
[--world village-1]
|
|
116
|
+
recess [--json] store-items list [--search TEXT]
|
|
117
|
+
[--status ACTIVE|INACTIVE|COMING_SOON] [--item-type TYPE]
|
|
118
|
+
[--page 0] [--limit 20] [--sort-by name|price|createdAt|updatedAt|order]
|
|
119
|
+
[--sort-order asc|desc]
|
|
120
|
+
recess [--json] store-items set-status <village-store-item-id>
|
|
121
|
+
--status ACTIVE|INACTIVE|COMING_SOON [--confirm]
|
|
122
|
+
recess [--json] content-library search <query> [--limit 8]
|
|
116
123
|
recess [--json] skills list [--query TEXT] [--category TEXT]
|
|
117
124
|
recess [--json] skills get <skill-name> [--reference NAME | --all-references]
|
|
118
125
|
[--refresh]
|
|
@@ -128,7 +135,7 @@ Usage:
|
|
|
128
135
|
recess [--json] goal-templates set-metadata <template-id> --expected-version N
|
|
129
136
|
[--title TEXT] [--description TEXT] [--emoji X] [--category TEXT] [--tags A,B]
|
|
130
137
|
[--sort-order N] [--kind SIMPLE|BLUEPRINT] [--agent-instructions-file <path>]
|
|
131
|
-
[--confirm]
|
|
138
|
+
[--output-template-file <path>] [--confirm]
|
|
132
139
|
recess [--json] goal-templates delete <template-id> --expected-version N [--confirm]
|
|
133
140
|
recess [--json] goal-templates snapshot-files <template-id> [--path P]
|
|
134
141
|
recess [--json] goal-templates capture-snapshot <template-id|slug>
|
|
@@ -455,6 +462,27 @@ const SCHOOL_TIER_IDS = SCHOOL_TIER_OPTIONS.map((tier) => tier.id);
|
|
|
455
462
|
const MAX_MAP_PDF_BYTES = 15 * 1024 * 1024;
|
|
456
463
|
const GOAL_TEMPLATE_KINDS = ["SIMPLE", "BLUEPRINT"];
|
|
457
464
|
const GOAL_TEMPLATE_SETUP_AUDIENCES = ["KID_FRIENDLY", "PARENT_SETUP"];
|
|
465
|
+
const STORE_ITEM_STATUSES = ["ACTIVE", "INACTIVE", "COMING_SOON"];
|
|
466
|
+
const STORE_ITEM_TYPES = [
|
|
467
|
+
"PROFILE_RING",
|
|
468
|
+
"ANIMATED_COVER",
|
|
469
|
+
"PROFILE_STICKER",
|
|
470
|
+
"CLASS_PASS",
|
|
471
|
+
"GUIDE_MENTORSHIP",
|
|
472
|
+
"TOOL_CREDIT",
|
|
473
|
+
"MERCH_ITEM",
|
|
474
|
+
"STEAM_GAME",
|
|
475
|
+
"KID_PROFILE_PICTURE",
|
|
476
|
+
"WORLD_AVATAR_ITEM",
|
|
477
|
+
"VILLAGE_ITEM",
|
|
478
|
+
];
|
|
479
|
+
const STORE_ITEM_SORT_FIELDS = [
|
|
480
|
+
"name",
|
|
481
|
+
"price",
|
|
482
|
+
"createdAt",
|
|
483
|
+
"updatedAt",
|
|
484
|
+
"order",
|
|
485
|
+
];
|
|
458
486
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
459
487
|
/**
|
|
460
488
|
* Read and parse a JSON file supplied by an authoring agent. Large payloads —
|
|
@@ -1056,6 +1084,103 @@ export async function runCommand(argv) {
|
|
|
1056
1084
|
}
|
|
1057
1085
|
throw new CliError("invalid_arguments", "Use village models list|upload|publish|archive|place|move|remove.");
|
|
1058
1086
|
}
|
|
1087
|
+
if (noun === "store-items") {
|
|
1088
|
+
if (verb === "list") {
|
|
1089
|
+
const search = flagString(parsed, "search");
|
|
1090
|
+
const rawStatus = flagString(parsed, "status");
|
|
1091
|
+
const rawItemType = flagString(parsed, "item-type");
|
|
1092
|
+
const page = flagNumber(parsed, "page") ?? 0;
|
|
1093
|
+
const limit = flagNumber(parsed, "limit") ?? 20;
|
|
1094
|
+
const rawSortBy = flagString(parsed, "sort-by");
|
|
1095
|
+
const rawSortOrder = flagString(parsed, "sort-order");
|
|
1096
|
+
if (!Number.isInteger(page) || page < 0) {
|
|
1097
|
+
throw new CliError("invalid_arguments", "--page must be a non-negative integer.");
|
|
1098
|
+
}
|
|
1099
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
1100
|
+
throw new CliError("invalid_arguments", "--limit must be an integer from 1 through 100.");
|
|
1101
|
+
}
|
|
1102
|
+
const status = rawStatus
|
|
1103
|
+
? assertChoice(rawStatus, STORE_ITEM_STATUSES, "--status")
|
|
1104
|
+
: undefined;
|
|
1105
|
+
const itemType = rawItemType
|
|
1106
|
+
? assertChoice(rawItemType, STORE_ITEM_TYPES, "--item-type")
|
|
1107
|
+
: undefined;
|
|
1108
|
+
const sortBy = rawSortBy
|
|
1109
|
+
? assertChoice(rawSortBy, STORE_ITEM_SORT_FIELDS, "--sort-by")
|
|
1110
|
+
: "order";
|
|
1111
|
+
const sortOrder = rawSortOrder
|
|
1112
|
+
? assertChoice(rawSortOrder, ["asc", "desc"], "--sort-order")
|
|
1113
|
+
: "asc";
|
|
1114
|
+
return unwrap(await api.client.GET("/admin/store-items/", {
|
|
1115
|
+
params: {
|
|
1116
|
+
query: {
|
|
1117
|
+
...(search ? { search } : {}),
|
|
1118
|
+
...(status ? { status } : {}),
|
|
1119
|
+
...(itemType ? { itemType } : {}),
|
|
1120
|
+
page,
|
|
1121
|
+
limit,
|
|
1122
|
+
sortBy,
|
|
1123
|
+
sortOrder,
|
|
1124
|
+
},
|
|
1125
|
+
},
|
|
1126
|
+
}));
|
|
1127
|
+
}
|
|
1128
|
+
if (verb === "set-status") {
|
|
1129
|
+
const storeItemId = positional(parsed, 2, "store item ID");
|
|
1130
|
+
const status = assertChoice(flagString(parsed, "status", { required: true }), STORE_ITEM_STATUSES, "--status");
|
|
1131
|
+
let page = 0;
|
|
1132
|
+
let storeItem;
|
|
1133
|
+
// Resolve the exact live row before the confirmation gate. Filtering at
|
|
1134
|
+
// the API makes this command incapable of mutating non-Village catalog
|
|
1135
|
+
// items, while the preview can name the row and its current state.
|
|
1136
|
+
do {
|
|
1137
|
+
const result = unwrap(await api.client.GET("/admin/store-items/", {
|
|
1138
|
+
params: {
|
|
1139
|
+
query: {
|
|
1140
|
+
itemType: "VILLAGE_ITEM",
|
|
1141
|
+
page,
|
|
1142
|
+
limit: 100,
|
|
1143
|
+
sortBy: "order",
|
|
1144
|
+
sortOrder: "asc",
|
|
1145
|
+
},
|
|
1146
|
+
},
|
|
1147
|
+
}));
|
|
1148
|
+
storeItem = result.items.find((item) => item.id === storeItemId);
|
|
1149
|
+
if (storeItem || page + 1 >= result.totalPages)
|
|
1150
|
+
break;
|
|
1151
|
+
page += 1;
|
|
1152
|
+
} while (true);
|
|
1153
|
+
if (!storeItem) {
|
|
1154
|
+
throw new CliError("not_found", `No Village store item found for ${storeItemId}.`);
|
|
1155
|
+
}
|
|
1156
|
+
const metadata = storeItem.metadata &&
|
|
1157
|
+
typeof storeItem.metadata === "object" &&
|
|
1158
|
+
!Array.isArray(storeItem.metadata)
|
|
1159
|
+
? storeItem.metadata
|
|
1160
|
+
: {};
|
|
1161
|
+
const body = { status };
|
|
1162
|
+
return writeCommand(parsed, {
|
|
1163
|
+
action: `set Village store item status to ${status}`,
|
|
1164
|
+
target: {
|
|
1165
|
+
storeItemId,
|
|
1166
|
+
name: storeItem.name,
|
|
1167
|
+
itemType: storeItem.itemType,
|
|
1168
|
+
villageKind: metadata.villageKind,
|
|
1169
|
+
catalogId: metadata.catalogId,
|
|
1170
|
+
},
|
|
1171
|
+
request: body,
|
|
1172
|
+
details: {
|
|
1173
|
+
currentStatus: storeItem.status,
|
|
1174
|
+
purchaseCount: storeItem._count.purchases,
|
|
1175
|
+
publishesFeedActivity: false,
|
|
1176
|
+
},
|
|
1177
|
+
}, async () => unwrap(await api.client.PATCH("/admin/store-items/{id}", {
|
|
1178
|
+
params: { path: { id: storeItemId } },
|
|
1179
|
+
body,
|
|
1180
|
+
})));
|
|
1181
|
+
}
|
|
1182
|
+
throw new CliError("invalid_arguments", "Use store-items list or store-items set-status.");
|
|
1183
|
+
}
|
|
1059
1184
|
if (noun === "users" && verb === "search") {
|
|
1060
1185
|
const query = parsed.positionals.slice(2).join(" ").trim();
|
|
1061
1186
|
if (!query)
|
|
@@ -2072,6 +2197,19 @@ export async function runCommand(argv) {
|
|
|
2072
2197
|
}
|
|
2073
2198
|
throw new CliError("invalid_arguments", "Use skills list|get.");
|
|
2074
2199
|
}
|
|
2200
|
+
if (noun === "content-library") {
|
|
2201
|
+
if (verb === "search") {
|
|
2202
|
+
const query = positional(parsed, 2, "search query");
|
|
2203
|
+
const limit = flagNumber(parsed, "limit") ?? 8;
|
|
2204
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 40) {
|
|
2205
|
+
throw new CliError("invalid_arguments", "--limit must be an integer between 1 and 40.");
|
|
2206
|
+
}
|
|
2207
|
+
return unwrap(await api.client.GET("/admin/content-library/search", {
|
|
2208
|
+
params: { query: { q: query, limit } },
|
|
2209
|
+
}));
|
|
2210
|
+
}
|
|
2211
|
+
throw new CliError("invalid_arguments", "Use content-library search.");
|
|
2212
|
+
}
|
|
2075
2213
|
if (noun === "goal-templates") {
|
|
2076
2214
|
if (verb === "list") {
|
|
2077
2215
|
const query = flagString(parsed, "query")?.toLowerCase();
|
|
@@ -2281,6 +2419,7 @@ export async function runCommand(argv) {
|
|
|
2281
2419
|
const id = await resolveGoalTemplateId(api, positional(parsed, 2, "template ID or slug"));
|
|
2282
2420
|
const expectedVersion = requiredExpectedVersion(parsed);
|
|
2283
2421
|
const agentInstructionsFile = flagString(parsed, "agent-instructions-file");
|
|
2422
|
+
const outputTemplateFile = flagString(parsed, "output-template-file");
|
|
2284
2423
|
const tags = flagString(parsed, "tags");
|
|
2285
2424
|
const kind = flagString(parsed, "kind");
|
|
2286
2425
|
const sortOrder = flagNumber(parsed, "sort-order");
|
|
@@ -2315,13 +2454,18 @@ export async function runCommand(argv) {
|
|
|
2315
2454
|
agentInstructions: await fs.readFile(path.resolve(agentInstructionsFile), "utf8"),
|
|
2316
2455
|
}
|
|
2317
2456
|
: {}),
|
|
2457
|
+
...(outputTemplateFile
|
|
2458
|
+
? {
|
|
2459
|
+
outputTemplate: await fs.readFile(path.resolve(outputTemplateFile), "utf8"),
|
|
2460
|
+
}
|
|
2461
|
+
: {}),
|
|
2318
2462
|
};
|
|
2319
2463
|
// `setupWorkflowSpec` is unreachable from this command by construction.
|
|
2320
2464
|
// The route still accepts one, but a wholesale spec replacement is the
|
|
2321
2465
|
// shape that caused the template incident; editing an existing spec goes
|
|
2322
2466
|
// through the guarded /ai patch path with its destructive-change token.
|
|
2323
2467
|
if (Object.keys(body).length === 1) {
|
|
2324
|
-
throw new CliError("invalid_arguments", "Pass at least one field to change (--title, --description, --emoji, --category, --tags, --sort-order, --kind, --agent-instructions-file).");
|
|
2468
|
+
throw new CliError("invalid_arguments", "Pass at least one field to change (--title, --description, --emoji, --category, --tags, --sort-order, --kind, --agent-instructions-file, --output-template-file).");
|
|
2325
2469
|
}
|
|
2326
2470
|
return writeCommand(parsed, {
|
|
2327
2471
|
action: "update goal template metadata (never its setupWorkflowSpec)",
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recess-cli
|
|
3
|
-
description: Safely perform Recess staff administration through the recess CLI. Use when a Recess admin asks Codex to find a kid, parent, family, enrollment, subscription, invoice, or cohort; inspect or change a school kid's tier and capability gates; upload a kid's MAP Growth report; pause or resume billing; refund or credit an invoice item; extend a trial; cancel or restore a subscription; register or unregister a cohort against an enrollment; switch or move kids from one cohort to another; manage guide payout invoices (biweekly pay-cycle line-item changes, invoice status moves, payout recipient lookups); run class operations (take attendance, cancel or reschedule a class session, add a one-off session, end a cohort, pause cohort billing, email a cohort's families, approve or deny pending registrations); process class-cancellation credits (the "Please credit these students accordingly" Slack message — credit every registered kid for a guide-canceled session); or author learning content — build, validate, publish, and loss-safely patch a deterministic GoalTemplate, apply a template to a kid or a roster, create a goal directly on a kid, write a Mesa draft or goal workspace, capture a template snapshot, and read Mesa workspace or snapshot files.
|
|
3
|
+
description: Safely perform Recess staff administration through the recess CLI. Use when a Recess admin asks Codex to find a kid, parent, family, enrollment, subscription, invoice, or cohort; edit the Recess Village map or manage store-catalog availability; search the curated Content Library; inspect or change a school kid's tier and capability gates; upload a kid's MAP Growth report; pause or resume billing; refund or credit an invoice item; extend a trial; cancel or restore a subscription; register or unregister a cohort against an enrollment; switch or move kids from one cohort to another; manage guide payout invoices (biweekly pay-cycle line-item changes, invoice status moves, payout recipient lookups); run class operations (take attendance, cancel or reschedule a class session, add a one-off session, end a cohort, pause cohort billing, email a cohort's families, approve or deny pending registrations); process class-cancellation credits (the "Please credit these students accordingly" Slack message — credit every registered kid for a guide-canceled session); or author learning content — build, validate, publish, and loss-safely patch a deterministic GoalTemplate, apply a template to a kid or a roster, create a goal directly on a kid, write a Mesa draft or goal workspace, capture a template snapshot, and read Mesa workspace or snapshot files.
|
|
4
4
|
# Bundle version. Bump on every substantive edit; the CLI reports it and `doctor`
|
|
5
5
|
# compares it against the served copy to tell an operator a refresh is available.
|
|
6
|
-
version: 1.4.
|
|
6
|
+
version: 1.4.4
|
|
7
7
|
# The lowest `recess` version this bundle is safe to install onto. Raise it ONLY
|
|
8
8
|
# when the bundle documents a command, flag, or changed semantic that an older
|
|
9
9
|
# binary does not have — an older CLI keeps its bundled copy instead of taking
|
|
10
10
|
# this one. Prose, formatting, and Gotcha edits must NOT raise it; that is the
|
|
11
11
|
# whole point of serving the bundle.
|
|
12
|
-
minCliVersion: 1.3.
|
|
12
|
+
minCliVersion: 1.3.3
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
# Recess CLI (`recess`)
|
|
@@ -209,10 +209,26 @@ recess --json onboarding attest <family-id> --condition app_downloaded|tutor_met
|
|
|
209
209
|
recess --json onboarding set-intake <family-id> --session <id> --data <json> [--expected-updated-at <iso>] [--confirm]
|
|
210
210
|
recess --json onboarding extract <family-id> --session <id> (--transcript-file <path> | --granola <ref>) [--confirm]
|
|
211
211
|
|
|
212
|
+
# Recess Village map administration
|
|
213
|
+
recess --json village render --min-x N --min-z N --max-x N --max-z N [--world village-1]
|
|
214
|
+
recess --json village models list [--world village-1] [--query TEXT] [--archived]
|
|
215
|
+
recess --json village models upload --file <model.glb> [--world village-1] [--name TEXT] [--confirm]
|
|
216
|
+
recess --json village models publish|archive <model-id> [--world village-1] [--confirm]
|
|
217
|
+
recess --json village models place <model-id> --x N --z N [--y N] [--rotation 0..3] [--world village-1] [--confirm]
|
|
218
|
+
recess --json village models move <placement-id> --x N --z N [--y N] [--rotation 0..3] [--world village-1] [--confirm]
|
|
219
|
+
recess --json village models remove <placement-id> [--world village-1] [--confirm]
|
|
220
|
+
|
|
221
|
+
# Main Recess store catalog (status writes are restricted to Village goods)
|
|
222
|
+
recess --json store-items list [--search TEXT] [--status ACTIVE|INACTIVE|COMING_SOON] [--item-type TYPE]
|
|
223
|
+
recess --json store-items set-status <village-store-item-id> --status ACTIVE|INACTIVE|COMING_SOON [--confirm]
|
|
224
|
+
|
|
212
225
|
# Tutor skills — the authoring know-how (served, never bundled)
|
|
213
226
|
recess --json skills list [--query TEXT] [--category TEXT]
|
|
214
227
|
recess --json skills get <skill-name> [--reference NAME | --all-references] [--refresh]
|
|
215
228
|
|
|
229
|
+
# Curated learning-resource research (read-only)
|
|
230
|
+
recess --json content-library search <query> [--limit 8]
|
|
231
|
+
|
|
216
232
|
# Learning content (reference/goal-authoring.md)
|
|
217
233
|
recess --json goal-templates list [--query TEXT] [--kind SIMPLE|BLUEPRINT] [--starter-only]
|
|
218
234
|
recess --json goal-templates get <id-or-slug> [--spec-only]
|
|
@@ -220,7 +236,7 @@ recess --json goal-templates versions <id-or-slug> [--version N]
|
|
|
220
236
|
recess --json goal-templates validate-spec --file <path/template.json>
|
|
221
237
|
recess --json goal-templates create --file <path/template.json> [--confirm]
|
|
222
238
|
recess --json goal-templates patch-spec <id-or-slug> --expected-version N --patches-file <path/patches.json> [--confirm] [--confirm-destructive-changes --destructive-change-token TOKEN]
|
|
223
|
-
recess --json goal-templates set-metadata <id-or-slug> --expected-version N [--title …] [--confirm]
|
|
239
|
+
recess --json goal-templates set-metadata <id-or-slug> --expected-version N [--title …] [--agent-instructions-file <path>] [--output-template-file <path>] [--confirm]
|
|
224
240
|
recess --json goal-templates delete <id-or-slug> --expected-version N [--confirm]
|
|
225
241
|
recess --json goal-templates snapshot-files <id-or-slug> [--path P]
|
|
226
242
|
recess --json goal-templates capture-snapshot <id-or-slug> (--source-goal <goal-id> | --source-draft <draft-slug> --student <kid-id>) [--dry-run] [--confirm]
|
|
@@ -318,7 +334,8 @@ the tool names do not:
|
|
|
318
334
|
| `search_web`, `fetch_webpage` | your own web search / page fetch |
|
|
319
335
|
| `spawn_subagents` | your own subagents. The five researcher prompts in `goal-creation`'s `subagent-tasks.md` (curriculum-map, platforms, resources, learning-paths, prerequisites) are usable almost verbatim — they are prompts, not tool calls |
|
|
320
336
|
| `validate_urls` | check the URLs yourself before baking them into a spec. Do not skip this: a rotted queue URL is invisible until a kid clicks it |
|
|
321
|
-
| `
|
|
337
|
+
| `search_gem_library` | `content-library search <query>` (same hybrid/vector + rerank funnel; no content or Pipeline writes) |
|
|
338
|
+
| `search_videos`, `search_books`, `recommend_videos` | your own search |
|
|
322
339
|
| `get_student_profile`, `read_memory`, `query_student_activity_sql` | **partial.** `users search`/`users get` give account context, `goals list` what is already authored, `mesa files` the workspace. There is **no** command for kid memory or activity SQL — for those, the separate `query-db` skill's read-only production access is the honest route, not a guess |
|
|
323
340
|
| `search_standards`, `navigate_standards` | no CLI command |
|
|
324
341
|
| `save_platform_research` | **no CLI command** — a researched platform profile can only be persisted from `recess.gg/ai`. Your research still informs the spec you write; it just does not get saved as a reusable profile |
|
|
@@ -479,3 +496,7 @@ Dated, newest last. Add an entry every time reality surprises you.
|
|
|
479
496
|
- 2026-08-04 — A `Makefile install-persistent` copy is NOT the npm package: it synthesizes its own `package.json`. If that manifest lacks `version`, or `skill/` is not copied alongside `dist/`, then `--version` reports `0.0.0` and the `minCliVersion` fence **fails closed** — every served skill upgrade is silently refused with `cli_too_old`. Both are now copied; if you add another packaged artifact the CLI reads at runtime, add it to that target too.
|
|
480
497
|
- 2026-08-04 — **`goal-templates patch-spec` performs a server preview even on a confirmed run.** The first POST is `dryRun:true`, never a write; it binds the current template version, before/after hashes, and protected removals. A destructive second POST is impossible without `--confirm-destructive-changes` and that fresh preview token. If the template or patch file changes, preview again and obtain new approval.
|
|
481
498
|
- 2026-08-05 — **A deterministic MODULE_BACKED spec is not the course content.** `goal-templates create` can create a valid BLUEPRINT while `snapshot` remains null; Goal Preview stays empty and apply correctly says there is no valid snapshot. Build the complete tree locally, `mesa files write --draft … --source-dir …`, then `goal-templates capture-snapshot --source-draft … --student …`. Both writes server-preview first. Mesa writes are fenced to the previewed repo change; capture is fenced to that source change and the template version. Direct live-goal `modules/`/`state/` writes are deliberately blocked because those paths have DB projections.
|
|
499
|
+
- 2026-08-05 — **The goal-audit GET cannot investigate an already-soft-deleted goal.** `request get /admin/browser/students/goals/<goal-id>/audit/` returns 404 because the handler's `ensureGoalAccess` calls `canManageGoal`, which requires `Goal.deletedAt: null` before it loads `GoalAuditLog`. The 404 is not evidence that the audit row is absent. Use the `query-db` skill's guarded read-only production query for deletion forensics.
|
|
500
|
+
- 2026-08-05 — **`set-metadata --output-template-file` edits an AI_CHAT template's `outputTemplate`** (the goal-description payload DailyTodoGeneration consumes for description-only goals). Like `--agent-instructions-file`, it reads a local file and the server bumps the version + freezes a `GoalTemplateVersion` row. Editing a template never rewrites goals already created from it — the description was copied at goal-creation time; re-apply or edit live goals separately.
|
|
501
|
+
- 2026-08-05 — **Content Library search now has a content/Pipeline-read-only CLI command.** `content-library search <query> [--limit 8]` calls the same hybrid/vector + rerank `/agent/search` funnel as Rocky's `search_gem_library`, returns the full fit/gist/coverage payload, and attributes the funnel's standard `offered` telemetry to `admin-cli`. It cannot inspect, ingest, file requests, curate, or edit the Pipeline; the permanent library token stays on the web server.
|
|
502
|
+
- 2026-08-07 — **Village store goods do not live in the Village database.** They are ordinary main-Recess `StoreItem` rows with `itemType=VILLAGE_ITEM`; use `store-items list --item-type VILLAGE_ITEM` and the confirmation-gated `store-items set-status`, while `village models …` remains the separate Village-island map/model boundary. `set-status` refuses non-Village IDs and its live preflight names the item, current status, purchase count, and feed consequence before approval. A checked-in seed still owns fresh-database defaults, so an operational status edit does not replace updating that seed when the product default itself changes.
|