@wji.ai/cli 0.3.0 → 0.5.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.
Files changed (61) hide show
  1. package/README.md +76 -2
  2. package/dist/commands/backreel.js +260 -0
  3. package/dist/commands/canvas.js +751 -86
  4. package/dist/commands/class.js +395 -0
  5. package/dist/commands/crew.js +205 -0
  6. package/dist/commands/npc.js +413 -0
  7. package/dist/commands/workspace.js +109 -24
  8. package/dist/core/archive.js +95 -0
  9. package/dist/core/crew.js +546 -0
  10. package/dist/core/members.js +54 -0
  11. package/dist/core/npc.js +108 -0
  12. package/dist/core/output.js +4 -2
  13. package/dist/index.js +10 -0
  14. package/package.json +1 -1
  15. package/skill/short-drama/SKILL.md +86 -8
  16. package/skill/short-drama/reference/action-bullet-time.md +47 -0
  17. package/skill/short-drama/reference/action-car-chase.md +51 -0
  18. package/skill/short-drama/reference/action-gun-fu.md +52 -0
  19. package/skill/short-drama/reference/action-historical-realist.md +50 -0
  20. package/skill/short-drama/reference/action-hk-kungfu.md +54 -0
  21. package/skill/short-drama/reference/action-street-brawl.md +49 -0
  22. package/skill/short-drama/reference/action-superhero.md +50 -0
  23. package/skill/short-drama/reference/action-wuxia-fantasy.md +51 -0
  24. package/skill/short-drama/reference/action.md +94 -0
  25. package/skill/short-drama/reference/cinematography-costume-dark.md +117 -0
  26. package/skill/short-drama/reference/cinematography-costume-idol.md +126 -0
  27. package/skill/short-drama/reference/cinematography-low-key.md +115 -0
  28. package/skill/short-drama/reference/cinematography-period-nostalgia.md +106 -0
  29. package/skill/short-drama/reference/cinematography-scifi-hard.md +63 -0
  30. package/skill/short-drama/reference/cinematography-scifi-soft.md +66 -0
  31. package/skill/short-drama/reference/cinematography-sweet-romance.md +175 -0
  32. package/skill/short-drama/reference/cinematography.md +83 -0
  33. package/skill/short-drama/reference/composition-art-first.md +74 -0
  34. package/skill/short-drama/reference/craft-shots.md +3 -1
  35. package/skill/short-drama/reference/craft-story.md +4 -1
  36. package/skill/short-drama/reference/format.md +94 -0
  37. package/skill/short-drama/reference/grammar-comedy.md +105 -0
  38. package/skill/short-drama/reference/grammar-faceslap.md +67 -0
  39. package/skill/short-drama/reference/grammar.md +103 -0
  40. package/skill/short-drama/reference/operations-cli.md +1 -1
  41. package/skill/short-drama/reference/schema.md +34 -3
  42. package/skill/short-drama/reference/stage-art.md +1 -1
  43. package/skill/short-drama/reference/stage-outline.md +21 -0
  44. package/skill/short-drama/reference/stage-script.md +10 -0
  45. package/skill/short-drama/reference/stage-storyboard.md +15 -0
  46. package/skill/short-drama/reference/story-angst.md +117 -0
  47. package/skill/short-drama/reference/story-comedy.md +136 -0
  48. package/skill/short-drama/reference/story-cultivation.md +109 -0
  49. package/skill/short-drama/reference/story-family.md +119 -0
  50. package/skill/short-drama/reference/story-horror.md +124 -0
  51. package/skill/short-drama/reference/story-mystery.md +127 -0
  52. package/skill/short-drama/reference/story-palace.md +103 -0
  53. package/skill/short-drama/reference/story-revenge.md +124 -0
  54. package/skill/short-drama/reference/story-scifi.md +112 -0
  55. package/skill/short-drama/reference/story-sweet.md +128 -0
  56. package/skill/short-drama/reference/story.md +127 -0
  57. package/skill/short-drama/scripts/selftest.mjs +809 -1
  58. package/skill/short-drama/scripts/short-drama.mjs +1608 -16
  59. package/skill/wji/SKILL.md +84 -2
  60. package/skill/wji/reference/commands.md +96 -6
  61. package/skill/wji-classroom/SKILL.md +161 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wji
3
- description: Drive the WJI AI platform from the terminal with the `wji` CLI — sign in, manage workspaces and the fsnode/dataset tree, upload assets, generate images/video/audio and poll the results, and work on Infinite Canvas nodes (read the user's selected nodes, create generate nodes, connect references), and manage models, agents, prompt presets and your profile. Use whenever a task mentions WJI, wji-backend, a WJI workspace, canvas, fsnode, dataset node, or asks to generate media on WJI.
3
+ description: Drive the WJI AI platform from the terminal with the `wji` CLI — sign in, manage workspaces and the fsnode/dataset tree, upload assets, generate images/video/audio and poll the results, and work on Infinite Canvas nodes (read the user's selected nodes, create generate nodes, connect references), manage workspace members, invitations and canvas owners, manage NPC City NPCs (profile, work history, soul, character document) and Backreel posts (drafts, publishing, cast and crew credits), and manage models, agents, prompt presets and your profile. Use whenever a task mentions WJI, wji-backend, a WJI workspace, canvas, fsnode, dataset node, NPC City, an NPC / resident, Backreel, or asks to generate media on WJI.
4
4
  ---
5
5
 
6
6
  # WJI CLI
@@ -302,6 +302,15 @@ System keys currently include `image`, `document`, `workspace-dataset`,
302
302
  When authoring, edit a pulled folder and `skill push` it again, or
303
303
  `skill update <id> --dir ./my-skill` to replace its files.
304
304
 
305
+ **Crews** are skill packs with agent seats (a `crew.json` + `agents/<seat>.md`
306
+ next to `skills/`). `wji crew install <name> --for claude|codex|openclaw|grok|all`
307
+ installs one into the host (Claude Code: `~/.claude/skills` + `~/.claude/agents`;
308
+ Codex: `~/.agents/skills` + `~/.codex/agents/*.toml`; OpenClaw: workspaces under
309
+ `~/.openclaw/workspaces/<crew>/` plus a JSON5 fragment to merge by hand; Grok bot:
310
+ an import zip). `--dry-run` shows the plan, `--from <dir|zip|url>` bypasses the
311
+ registry, `wji crew status` / `uninstall` track what was written, and
312
+ `wji crew check <dir>` validates a pack. The `short-drama` crew is the known one.
313
+
305
314
  Bundle limits: at most 200 markdown files, 2MB per file, 20MB in total and a
306
315
  5MB zip; 50 user skills per account. `skill update --dir` sends files in the
307
316
  request body, where each file is capped far lower, at **200,000 characters** —
@@ -323,6 +332,19 @@ A workspace's InfiniteCanvas app can hold several canvases (`wji canvas list`);
323
332
  `wji canvas create <name>` adds an empty one — e.g. one canvas per episode or
324
333
  per character set.
325
334
 
335
+ **Ownership.** A canvas can have an owner (a workspace member) and a
336
+ visibility: `members` (everyone in the workspace sees it) or `owner` (only
337
+ the owner and the workspace owner). `wji canvas list` shows both;
338
+ `wji canvas owner <canvasId> <member> --visibility owner` sets them — the
339
+ workspace owner only. Visibility hides, it does not lock: on a `members`
340
+ canvas any editor can edit. For a class of students (one canvas each, copy
341
+ work to all, collect results) use `wji class` — see the `wji-classroom` skill.
342
+
343
+ **Color tags.** `wji canvas tag red <nodeIds…> -l HW1` tags nodes and names
344
+ the color on that canvas; `--tag HW1` then filters `canvas nodes`,
345
+ `class status` and `class results`. `wji canvas copy <nodeIds…> --to
346
+ <canvasId…>` copies nodes with their connections and tags to other canvases.
347
+
326
348
  **The link.** The user opens a canvas in the browser and presses the link
327
349
  button in its top bar. That stores the open workspace, canvas and **the nodes
328
350
  they have selected** on their account for 5 minutes (the tab renews it while
@@ -332,12 +354,17 @@ open). Every `wji canvas` command then targets that canvas; pass
332
354
  ```bash
333
355
  wji canvas status # linked canvas + selected nodes (ids, asset URLs, settings)
334
356
  wji canvas nodes # every node; * marks generate nodes
357
+ wji canvas nodes --tag C01 # nodes with the color tag labelled C01 (or --tag red, --untagged)
358
+ wji canvas nodes -n '^C0[1-3] ' # nodes whose name matches a regex (case-insensitive); combines with --tag/-k
359
+ wji canvas tags # color tags on the canvas: color, label, node count
335
360
  wji canvas get <nodeId> # full detail incl. references
336
361
  wji asset download <assetId…> -o <dir> # then look at the images yourself
337
362
 
338
363
  wji canvas create E01 -w <workspaceId> # new empty canvas; then --canvas <id>
339
364
  wji canvas add video --from <imgNode> <imgNode> -m <model> -r 16:9 -d 8 -p "@image1 … @image2 …"
340
365
  wji canvas add text -t "shot list…" --at 0,600
366
+ wji canvas add text --text-file prompt.txt --plain --size 600,900 # plain text keeps its line breaks; node 600×900
367
+ wji canvas set <nodeId> --text-file notes.md --size 520,700 # replace the text, resize
341
368
  wji canvas add image --asset <assetId> -n "Ada sheet" # display node for an existing asset
342
369
  wji canvas add dataset --dataset <fsnodeId> # card bound to a workspace dataset
343
370
  wji canvas set <nodeId> -p "new prompt" # merges: other settings stay
@@ -387,6 +414,10 @@ wji canvas rm <nodeId…> # destructive — see Rules
387
414
  - Take new ids from the command's own output (`wji --json canvas add … | jq
388
415
  -r .id`), not by searching names — names repeat on a canvas. In zsh a
389
416
  variable holding several ids is not word-split; pass the ids literally.
417
+ - Text, title and comment nodes are **Markdown** on the web: a single line break
418
+ joins the lines into one paragraph (the CLI warns). For prompts and other plain
419
+ text pass `--plain` (line breaks become Markdown hard breaks); `--size w,h`
420
+ sets the node's box so a long prompt is readable without scrolling.
390
421
  - Changes show up live in the user's open canvas; there's no need to ask them
391
422
  to reload.
392
423
  - `canvas gen` sends exactly what the canvas's own Generate button sends
@@ -396,6 +427,55 @@ wji canvas rm <nodeId…> # destructive — see Rules
396
427
 
397
428
  See `reference/canvas-recipes.md` for end-to-end examples.
398
429
 
430
+ ## NPC City and Backreel
431
+
432
+ **NPC City NPCs** (residents) belong to you, or to an organization when you
433
+ pass `--org`. Refer to one by id, `@handle` or name; an `@handle` that isn't
434
+ yours resolves in the public directory (read-only for you).
435
+
436
+ ```bash
437
+ wji npc list # yours; --public for the directory
438
+ wji npc get @mika # profile + numbered work history
439
+ wji npc create "Mika" --role Singer --genre "City pop" --handle mika.npc # private draft
440
+ wji npc set @mika --tagline "…" --tag tier=Rising --social ig=https://…
441
+ wji npc publish @mika # visibility public (unpublish = private)
442
+ wji npc job add @mika -t concert --title "Neon Night" --date 2026-10-01 --location Taipei
443
+ wji npc job set @mika 2 --detail "sold out" ; wji npc job rm @mika 3 -y
444
+ wji npc soul get @mika > soul.json ; wji npc soul set @mika '{"voice":{…}}'
445
+ wji npc doc export @mika -o mika.md # whole character in one file
446
+ wji npc doc apply @mika @mika.md --dry-run # see the diff, then without --dry-run
447
+ ```
448
+
449
+ - **Jobs** are the NPC's work history (concert, release, collaboration, brand,
450
+ event, ad, appearance) shown on its public page. They have no ids: the CLI
451
+ numbers them 1…n and rewrites the whole list, so list again before `job
452
+ set` / `job rm` if anything else may have changed it.
453
+ - `soul set` merges top-level keys only; a nested object you pass replaces
454
+ that whole key. Memories and albums are not part of the soul.
455
+ - `npc create` needs NPC City spawn permission on the account (a 403 says so);
456
+ only an admin can grant it. Admin actions — spawn permission, featured / hero
457
+ slider, moderation — are not in `wji`.
458
+ - `npc transfer send <npc> --to <email>` offers the NPC to someone; they
459
+ `npc transfer accept <id>` within 7 days. You lose it on acceptance.
460
+
461
+ **Backreel posts** are your film / project pages. `create` always makes a
462
+ draft; publishing needs a cover, media or an episode.
463
+
464
+ ```bash
465
+ wji backreel list # yours, drafts included; --all for published posts
466
+ wji backreel create "Harbour" -d @synopsis.txt --tags drama,short --cover <assetId>
467
+ wji backreel set <postId> --project-status in_production
468
+ wji backreel credit add <postId> --kind creative --name "Wayne" --role 導演
469
+ wji backreel credit add <postId> --npc @mika --role "飾 阿明" --job appearance
470
+ wji backreel publish <postId> # unpublish takes it offline
471
+ ```
472
+
473
+ - Credits (max 30) are rewritten whole in order; `credit rm` goes by number.
474
+ - `--npc` casts an NPC (the credit's `npcId`, name and avatar come from it);
475
+ `--job <type>` also adds the work to that NPC's history — only for your own
476
+ NPCs.
477
+ - `--description` takes plain text (turned into paragraphs) or HTML.
478
+
399
479
  ## Everything else
400
480
 
401
481
  ```bash
@@ -413,7 +493,9 @@ wji org list && wji org use <orgId>
413
493
  These commands delete, revoke or overwrite, and none can be undone from the
414
494
  CLI: `ws rm`, `ws app rm`, `ws member rm`, `fs rm`, `dataset rm`,
415
495
  `asset rm`, `history rm`, `preset rm`, `skill rm`, `skill update --dir`,
416
- `agent rm`, `auth key rm`, `auth key revoke`, `config rm`, `canvas rm`.
496
+ `agent rm`, `auth key rm`, `auth key revoke`, `config rm`, `canvas rm`,
497
+ `ws member link` (revokes the current share link), `npc rm`, `npc job rm`,
498
+ `npc doc apply`, `npc transfer send`, `backreel rm`, `backreel credit rm`.
417
499
  They prompt `[y/N]` in a terminal, but when you run them there is no
418
500
  terminal, so they refuse unless you pass `--yes`. **The user's reply in the
419
501
  chat is the only confirmation there is.** Before running one:
@@ -73,7 +73,11 @@ switch endpoint.
73
73
  | `ws favorite <id> [--off]` | `PUT /workspace/list/:id/favorite` |
74
74
  | `ws fs [id] [-a appId]` | `GET /workspace/list/:id/fs[/:appid]` |
75
75
  | `ws app list\|add <app> [-n]\|rm <appId> [-y]` | `POST/DELETE /workspace/list/:id/apps` |
76
- | `ws member list\|add <userId> [-r role]\|role <memberId> <role>\|rm <memberId> [-y]` | `/workspace/list/:id/member/*` |
76
+ | `ws member list [id] [-r role]` | members from `GET /workspace/list/:id` (there is no member-list endpoint): name, email, role, user id, membership id |
77
+ | `ws member add <userIds...> [-r editor\|viewer] [-w]` | `POST .../member/add-batch {users, role}` (≤50; existing members skipped). Org workspaces accept only org members |
78
+ | `ws member invite <emails...> [-r editor\|viewer] [-w]` | `POST /invitation/workspace/:id/invite-emails` (owner only, ≤50; commas/spaces split). Registered users are **added at once**, others get an emailed invitation — which also revokes the current share link. Reports added / invited / skipped / invalid |
79
+ | `ws member link [id] [-r role] [--show]` | `POST /invitation/create {targetType:'workspace', targetId, role}` (owner only; valid 7 days — the server rejects any `expiredAt`). One active link per workspace — a new one revokes the old. `--show` lists the active link (`GET /invitation/workspace/:id`) |
80
+ | `ws member role <member> <editor\|viewer> [-w]` / `ws member rm <members...> [-w] [-y]` | `PUT .../member/:memberId/role`, `DELETE .../member/:memberId`. `<member>` is a name, email, user id or membership id (ambiguous names are an error); the owner is refused. A removed member's canvases stay |
77
81
  | `ws quota [id] [--set k=v]` | `GET .../quota-usage`, `PUT .../quota` |
78
82
  | `ws stats [id]` | `GET /workspace/list/:id/stats` |
79
83
  | `ws templates` / `ws clone <templateId>` | `GET /workspace/start/templates`, `POST /workspace/create/:id` |
@@ -160,13 +164,17 @@ updates live.
160
164
  | --- | --- |
161
165
  | `canvas status` (`link`) | `GET /canvas-link`, then the selected nodes read fresh from the canvas |
162
166
  | `canvas unlink` | `DELETE /canvas-link`; the tab turns its button off at its next heartbeat (≤ 60s) |
163
- | `canvas list [-w]` | canvases under each InfiniteCanvas app: `GET /workspace/list/:id/fs/:appId` → `workspace-v2/canvases` children |
164
- | `canvas create <name> [-w] [--app]` | `POST /fsnode/create` a `workspace-v2/canvas` folder under that `canvases` folder with the web's initial content (`{data:{viewport, backgroundMode:'dots'}, metadata:{}}`) |
165
- | `canvas nodes [-c] [-k kind]` | `GET /fsnode/list/:canvasId/children?limit=-1` + `POST /assets/check-status` |
167
+ | `canvas list [-w] [-o member\|none]` | canvases under each InfiniteCanvas app: `GET /workspace/list/:id/fs/:appId` → `workspace-v2/canvases` children, with owner and visibility from `content.metadata`. Owner-only canvases of others are hidden unless you own the workspace |
168
+ | `canvas create <name> [-w] [--app] [-o member] [--visibility owner\|members]` | `POST /fsnode/create` a `workspace-v2/canvas` folder under that `canvases` folder with the web's initial content (`{data:{viewport, backgroundMode:'dots'}, metadata:{ownerUserId?, visibility?}}`) |
169
+ | `canvas owner <canvasId> [member] [--visibility owner\|members] [--none]` | without options shows owner + visibility; otherwise `PATCH /fsnode/list/:id` with the whole envelope `{data, metadata}` and `baseUpdatedAt` (retried on 409). **Workspace owner only** (403 otherwise). `owner` visibility = only the owner and the workspace owner see the canvas; it hides, it does not lock — with `members` any editor can edit it |
170
+ | `canvas tag <tag> [nodeIds...] [-c] [-l label] [--selected]` | sets `content.colorTag {colorId}` on the nodes (`contentPatch`); `none` removes it. `<tag>` is a color, color id or existing label. `-l` writes the color's label to the canvas folder `content.data.colorTagNames` (`""` clears) |
171
+ | `canvas copy [nodeIds...] --to <canvasIds...> [-c] [--selected] [--at x,y] [--same-position] [-y]` | the canvas's own copy-paste: new nodes (no position, `generate.historyId/pendingAssetId` dropped, finished asset shared via `file`), edges among them re-created oldest first so `@image1…` numbering holds, color tags kept and a missing label carried to the target. Edges from nodes outside the copy are left out. Default placement: right of what is already on the target |
172
+ | `canvas nodes [-c] [-k kind] [-n regex] [--tag tags…\|--untagged]` | `GET /fsnode/list/:canvasId/children?limit=-1` + `POST /assets/check-status`. `-n` matches the node name as a case-insensitive regex (`/re/flags` to set flags yourself). `--tag` takes a label (`C01`, case-insensitive), a color (`red`) or a color id (`rose`); several values match any. Filters combine (AND) |
173
+ | `canvas tags [-c] [-a]` | the canvas's color tags: color, label, node count (`-a` also unused, unlabelled colors) |
166
174
  | `canvas get <nodeId> [-c]` | same load; one node with references (`@image1 ← id`) and frame slots |
167
175
  | `canvas edges [-c] [--node]` | the `canvas/edge` children |
168
- | `canvas add <kind> [-c] [-n] [-t\|--text-file] [--asset] [--dataset] [-p\|--prompt-file] [-m] [-r] [--resolution] [-d] [--set] [--from ids…] [--at x,y]` | `POST /fsnode/create` (`canvas/node/<kind>`), then one `canvas/edge` per `--from`. Kinds: text, title, comment, image, video, audio, dataset. `add dataset --dataset <fsnodeId>` writes `content {datasetId, datasetName, datasetType, datasetParentName, description}` from the dataset node (must be `workspace/dataset`); it takes no `--from` |
169
- | `canvas set <nodeId> [-c] [-n] [-t] [--move x,y] [-p] [-m] [-r] [--resolution] [-d] [--set]` | read → merge → `PATCH /fsnode/list/:id` |
176
+ | `canvas add <kind> [-c] [-n] [-t\|--text-file] [--plain] [--size w,h] [--asset] [--dataset] [-p\|--prompt-file] [-m] [-r] [--resolution] [-d] [--set] [--from ids…] [--at x,y]` | `POST /fsnode/create` (`canvas/node/<kind>`), then one `canvas/edge` per `--from`. Kinds: text, title, comment, image, video, audio, dataset. `add dataset --dataset <fsnodeId>` writes `content {datasetId, datasetName, datasetType, datasetParentName, description}` from the dataset node (must be `workspace/dataset`); it takes no `--from`. Text kinds are Markdown on the web; `--plain` turns every single line break into a Markdown hard break (`" \n"`, fenced code untouched), and without it the CLI warns when a single break would be joined. `--size` writes `content.width/height` |
177
+ | `canvas set <nodeId> [-c] [-n] [-t\|--text-file] [--plain] [--move x,y] [--size w,h] [-p] [-m] [-r] [--resolution] [-d] [--set]` | read → merge → `PATCH /fsnode/list/:id` with `contentPatch` of only the top-level keys that changed, so a concurrent browser edit of another key survives (falls back to a whole-content write on a backend without `contentPatch`) |
170
178
  | `canvas connect <from> <to> [-c]` | `POST /fsnode/create` (`canvas/edge`); a first/last-frame video model gets `videoFrameSlot` start, then end |
171
179
  | `canvas disconnect <from> <to> [-c]` | `DELETE /fsnode/list/:edgeId` |
172
180
  | `canvas rm <nodeIds...> [-c] [-y]` | deletes the nodes' edges, then the nodes; refuses nodes still generating |
@@ -179,6 +187,12 @@ Storage format (what the web canvas writes):
179
187
  `generate` with `content.generate {prompt, model, ratio, resolution,
180
188
  duration, values, improvePromptEnabled, historyId?, pendingAssetId?}`. Width
181
189
  and height are not stored.
190
+ - Color tag: the node stores only `content.colorTag {colorId}`, one of
191
+ `rose` (red), `orange`, `amber` (yellow), `lime` (green), `teal`, `sky`
192
+ (blue), `violet` (purple), `fuchsia` (pink). The label is per color per
193
+ canvas, on the canvas folder: `content.colorTagNames {rose: "C01", …}`, so
194
+ every red node on a canvas shares one label. A `name` left on an old node's
195
+ `colorTag` is ignored, as on the web.
182
196
  - Edge: `mimetype canvas/edge`, name `连线`, `content {fromNodeId, toNodeId,
183
197
  sourceHandle, targetHandle, videoFrameSlot?}`. References are incoming edges
184
198
  sorted by `sort` then id, numbered per source kind.
@@ -202,6 +216,71 @@ viewport, url}` on toggle (`takeover: true`), on selection change and every
202
216
  60s. A non-takeover `PUT` from a tab that no longer owns the link gets
203
217
  `409 canvas-link-gone`, which switches that tab's button off.
204
218
 
219
+ ## class
220
+
221
+ A class is a workspace the teacher owns; students are members (editors) and
222
+ each student's canvas is the one whose `metadata.ownerUserId` is them. Every
223
+ command takes `-w` (defaults to the linked workspace, then the profile's).
224
+
225
+ | Command | API / behaviour |
226
+ | --- | --- |
227
+ | `class roster` | members minus the owner, with role and owned canvases |
228
+ | `class canvases [--create] [--visibility owner\|members] [--update] [--name '{name}'] [--student …] [--app]` | lists each student's canvas; `--create` makes the missing ones (`POST /fsnode/create`, owner = student, default visibility `owner`); `--update` applies `--visibility` to existing ones. Idempotent |
229
+ | `class copy [nodeIds...] [-c] [--selected] [--student …] [--at x,y] [--same-position] [-y]` | `canvas copy` into every student canvas (default: the linked selection); 4 canvases at a time; failures reported per student with a rerun hint, exit 1 |
230
+ | `class status [--tag …] [-k kind] [--student …] [-s name\|recent\|progress]` | per student: nodes, generate nodes done / running / failed / not run, `GET /ai/assets/history/search?scope=canvas&scopeId=<canvas>&limit=1&sort[createdAt]=desc` for count + last generation, credits from `GET .../quota-usage` `byUserTotal` |
231
+ | `class results [--student …] [--tag …] [-k kind] [-n regex] [--since 2h] [--all] [-s recent\|name] [-l n] [-d dir]` | finished media on student canvases (generate nodes; `--all` adds display nodes), newest first; `-d` saves `<dir>/<student>/<node>-<id>.<ext>` |
232
+
233
+ `--tag` matches each canvas's own label (a label a canvas lacks matches nothing
234
+ there); `--student` takes names, emails or user ids.
235
+
236
+ ## npc
237
+
238
+ `<npc>` is an id, `@handle` or name among your NPCs (org NPCs with `--org`); an
239
+ unknown `@handle` falls back to `GET /npc-city/residents/by-handle/:h` (public).
240
+
241
+ | Command | API |
242
+ | --- | --- |
243
+ | `npc list [--public] [-k] [--status active\|debut\|hiatus] [--visibility] [--page] [--limit]` | `GET /npc-city/residents/mine` (`--public`: `GET /npc-city/residents`) |
244
+ | `npc get <npc>` | `GET /npc-city/residents/:id` |
245
+ | `npc create <name> --role --genre [--handle] [--tagline] [--bio] [--image url\|assetId] [--hero-image] [--color] [--status] [--visibility] [--social k=url…] [--tag k=v…] [--set k=v…]` | `GET …/handle-available`, then `POST /npc-city/residents` (needs profile `canSpawn`; private by default; name is uppercased) |
246
+ | `npc set <npc> [-n] [--role] [--genre] [same flags]` | `PATCH /npc-city/residents/:id` with only the given fields; `--social` / `--tag` merge into the current objects |
247
+ | `npc publish <npc>` / `npc unpublish <npc>` | `PATCH … {visibility}` |
248
+ | `npc rm <npc> [-y]` | `DELETE /npc-city/residents/:id` (soft delete) |
249
+ | `npc handle <handle> [--exclude <npc>]` | `GET /npc-city/residents/handle-available` |
250
+ | `npc job list <npc>` | the resident's `jobs[]`, numbered 1…n |
251
+ | `npc job add <npc> -t <type> --title --date YYYY-MM-DD [--detail] [--location] [--url]` | read, append, `PATCH {jobs}` |
252
+ | `npc job set <npc> <n> [same flags]` | read, merge into #n, `PATCH {jobs}` |
253
+ | `npc job rm <npc> <n...> [-y]` | read, drop, `PATCH {jobs}`; refuses if the list changed meanwhile |
254
+ | `npc soul get <npc>` | `GET /npc-city/residents/:id/soul` |
255
+ | `npc soul set <npc> <json\|@file\|->` | `PATCH /npc-city/residents/:id/soul {soulConfig}` (shallow merge) |
256
+ | `npc doc export <npc> [--format md\|json] [-o file]` | `GET /npc-city/residents/:id/document?format=` |
257
+ | `npc doc apply <npc> <@file\|-> [--dry-run] [-y]` | `POST …/document/preview`, confirm, `POST …/document` |
258
+ | `npc transfer send <npc> --to <email\|userId> [-y]` | `POST /npc-city/residents/:id/transfer` |
259
+ | `npc transfer list` | `GET /npc-city/transfers/incoming` + `/outgoing` |
260
+ | `npc transfer accept\|reject\|cancel <transferId>` | `POST /npc-city/transfers/:id/<action>` |
261
+
262
+ Job types: concert, release, collaboration, brand, event, ad, appearance. The
263
+ server drops jobs without a title or date and turns unknown types into `event`;
264
+ the CLI rejects both up front.
265
+
266
+ ## backreel
267
+
268
+ | Command | API |
269
+ | --- | --- |
270
+ | `backreel list [--all] [-k] [--page] [--limit]` | `GET /backreel/post/search/my` (`--all`: `GET /backreel/post/search`) |
271
+ | `backreel get <postId>` | `GET /backreel/post/list/:id` |
272
+ | `backreel create <title> [--kind showcase\|crowdfunding] [--category] [--project-status] [--tags a,b] [-d text\|@file] [--cover assetId] [--cover-portrait] [--trailer] [--body json]` | `POST /backreel/post/create` (always a draft) |
273
+ | `backreel set <postId> [--title] [same flags]` | `PUT /backreel/post/list/:id` with only the given fields |
274
+ | `backreel publish\|unpublish <postId>` | `POST /backreel/post/list/:id/publish\|unpublish` (publish needs a cover, media or an episode) |
275
+ | `backreel rm <postId> [-y]` | `DELETE /backreel/post/list/:id` |
276
+ | `backreel credit list <postId>` | the post's `credits[]`, numbered |
277
+ | `backreel credit add <postId> --role [--name] [--kind cast\|crew\|creative] [--npc] [--user] [--link] [--job <type>]` | read, append, `PUT {credits}`; `--job` then appends to the NPC's `jobs` (own NPCs only, checked first) |
278
+ | `backreel credit rm <postId> <n...> [-y]` | read, drop, `PUT {credits}` |
279
+
280
+ Project status: planning, pre_production, in_production, post_production,
281
+ completed. `-d` plain text becomes `<p>` paragraphs; HTML passes through and
282
+ is sanitized server-side. Admin routes (`/api/backreel/*`) reject `sk_` keys.
283
+
205
284
  ## gen
206
285
 
207
286
  Shared flags: `-m/--model` (required), `-p/--prompt`, `-n/--n`, `-w/--workspace`,
@@ -339,6 +418,17 @@ Only `.md` files persist (the importer drops everything else); scripts belong to
339
418
  | `skill update <id> [-n] [-d] [--visibility] [--dir] [-y]` | `PUT /ai/skill/:id` — metadata, or replace **all** files from `--dir` (confirms first) |
340
419
  | `skill rm <id> [-y]` | `DELETE /ai/skill/:id` |
341
420
 
421
+ ## crew
422
+
423
+ Crew packs are local or remote folders (`crew.json`, `agents/<seat>.md`, `skills/<name>/SKILL.md`, `AGENTS.md`) — no WJI API involved.
424
+
425
+ | Command | Behaviour |
426
+ | --- | --- |
427
+ | `crew install [name] --for <claude,codex,openclaw,grok\|all> [--from dir\|zip\|tar.gz\|url] [--ref] [--project dir] [--link] [--out zip] [--no-register] [--dry-run] [-y]` | fetches the pack (registry → GitHub tarball, cached under `~/.wji/crews/`), validates it, prints the plan, then per host: **claude** copies `skills/*` → `~/.claude/skills/` and `agents/*.md` → `~/.claude/agents/`; **codex** copies skills → `~/.agents/skills/` and writes `~/.codex/agents/<seat>.toml` (`developer_instructions` = persona body, `sandbox_mode` from capabilities, `[[skills.config]]` per skill); **openclaw** shares `~/.agents/skills/`, writes `~/.openclaw/workspaces/<crew>/<seat>/{AGENTS.md,SOUL.md,skills→}` + `openclaw.agents.json5` and runs `openclaw agents add` when the binary exists; **grok** writes `<crew>-export.zip` (`skills/`, `bots/*.json`, `bots/crew-manifest.json`, `channels/*.json`, sibling links rewritten to `sand-workflow:`). `--project` targets `<dir>/.claude`, `.agents`, `.codex`, `openclaw` instead of `$HOME` |
428
+ | `crew status [name]` | reads `~/.wji/crews/installed.json`; per path ✓ / ↗ symlink / ✗ missing |
429
+ | `crew uninstall <name> [--for targets] [-y]` | removes exactly the recorded paths; reminds you to clean `openclaw.json` by hand |
430
+ | `crew check <dir>` | validates `crew.json` ↔ `agents/*.md` (names, skills, tools vs capabilities), every `SKILL.md` (name == folder, description ≤ 1024) and that relative links resolve |
431
+
342
432
  Import rules: the zip must contain `SKILL.md` at its root (or one folder deep — a
343
433
  single common root folder is stripped). `SKILL.md` frontmatter `name:` is
344
434
  required; `description:` is read too. Visibility is `private` (default) or
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: wji-classroom
3
+ description: Run a class on WJI with the `wji` CLI — a teacher's workspace where every student has their own Infinite Canvas. Set up the class workspace, invite students by email or share link, create one private canvas per student, hand out preset prompts and starter nodes (copy the teacher's selected nodes into every student's canvas), then check each student's progress and collect their generated results by name, recency or color tag. Use when a teacher / instructor / 老師 / 講師 mentions a class, course, workshop, students / 學生 / 学员, assignments / homework / 作業, or wants to manage members' canvases on WJI.
4
+ ---
5
+
6
+ # WJI classroom
7
+
8
+ This skill sits on top of the `wji` skill — read that one for sign-in,
9
+ models, generation and the canvas basics. Everything here is the `wji`
10
+ CLI; the class-wide commands live under `wji class`.
11
+
12
+ ## How a class maps onto WJI
13
+
14
+ | Class | WJI |
15
+ | --- | --- |
16
+ | the class | a workspace the teacher **owns** |
17
+ | a student | a workspace member with role **editor** (viewers cannot generate) |
18
+ | a student's desk | a canvas whose owner is that student, visibility `owner` — only they and the teacher see it |
19
+ | handouts | workspace prompt presets, and starter nodes copied into every canvas |
20
+ | an assignment | nodes carrying a color tag with a label, e.g. red = `HW1` |
21
+
22
+ Only the workspace **owner** can invite, create share links and set canvas
23
+ owners. Check first:
24
+
25
+ ```bash
26
+ wji auth whoami
27
+ wji ws member list <workspaceId> # your row must say owner
28
+ ```
29
+
30
+ ## 1. Create the class workspace
31
+
32
+ ```bash
33
+ wji ws create "Art 101 — Spring" -a canvas # the canvas app is all a class needs
34
+ wji ws use <workspaceId> # later commands default to it
35
+ ```
36
+
37
+ ## 2. Invite students
38
+
39
+ ```bash
40
+ wji ws member invite amy@school.edu, ben@school.edu # ≤50 per call; commas or spaces
41
+ wji ws member link # or a join link (valid 7 days) to post in the class chat
42
+ wji ws member link --show # the active link
43
+ wji class roster # who has joined, role, canvas
44
+ ```
45
+
46
+ - Registered emails join immediately (`added`); the others get an invitation
47
+ email and join after signing up (`invited`). Rerun `class roster` later.
48
+ - There is **one** active link per workspace. `member link` — and any
49
+ `member invite` that emails someone — revokes the previous link. Tell the
50
+ teacher before replacing a link they already shared.
51
+ - Late joiners via the link join as the link's role (default editor).
52
+ - Wrong role: `wji ws member role "Ben" editor`. Remove: `wji ws member rm "Ben"`.
53
+
54
+ ## 3. One canvas per student
55
+
56
+ ```bash
57
+ wji class canvases --create # named after each student, private
58
+ wji class canvases --create --name "{name} · Art 101"
59
+ wji class canvases --visibility members --update # let the class see each other's canvases
60
+ ```
61
+
62
+ It only creates what is missing, so rerun it after new students join. The
63
+ teacher (workspace owner) sees every canvas; each student sees only theirs
64
+ (plus any canvas with visibility `members`, e.g. a shared "Class board").
65
+ Individual canvases: `wji canvas owner <canvasId> "Amy" --visibility owner`.
66
+
67
+ ## 4. Preset prompts, so students don't have to type
68
+
69
+ Two ways, often both:
70
+
71
+ **Workspace presets** — a prompt library for the class. Students press `/`
72
+ on their canvas (the *workspace* tab) and the prompt becomes a text node, or
73
+ `/` inside a generate node's prompt box to insert it.
74
+
75
+ ```bash
76
+ wji preset create "Watercolor portrait" -w <workspaceId> -c utility -t hw1 \
77
+ -p "A soft watercolor portrait of @image1, loose brush strokes, paper texture"
78
+ wji preset workspace # what the class sees
79
+ ```
80
+
81
+ **Starter nodes** — a ready-to-run generate node (prompt, model, settings,
82
+ reference images already wired) that students only have to press Generate on.
83
+ Build it once on the teacher's canvas, then copy it to everyone (step 5):
84
+
85
+ ```bash
86
+ wji canvas add image -c <teacherCanvas> -n "HW1 portrait" -m <model> \
87
+ -p "@image1 as a watercolor portrait" --from <referenceImageNode>
88
+ ```
89
+
90
+ Pick the model with `wji models list --feature image` / `wji models get`.
91
+
92
+ ## 5. Copy nodes into every student's canvas
93
+
94
+ The usual way: the teacher selects the nodes in the browser and presses the
95
+ link button in the canvas top bar. Then:
96
+
97
+ ```bash
98
+ wji canvas status # confirm what is selected
99
+ wji canvas tag red --selected -l HW1 # optional: mark it as an assignment
100
+ wji class copy -y # the selection → every student canvas
101
+ wji class copy <nodeId> <nodeId> -c <canvasId> --student "Amy" "Ben" -y
102
+ ```
103
+
104
+ - Connections between the copied nodes come along with their `@image1…`
105
+ order; finished images/videos are shared (not regenerated); generate nodes
106
+ keep prompt, model and settings but start un-generated.
107
+ - The group lands to the right of what is already on each canvas (`--at x,y`
108
+ or `--same-position` to choose).
109
+ - Color tags come along; the label (`HW1`) is added to a student's canvas if
110
+ that color has no label there yet.
111
+ - Failures are reported per student with a `--student …` rerun hint.
112
+
113
+ ## 6. Check progress and gather results
114
+
115
+ ```bash
116
+ wji class status # every student: nodes, done / running / failed / not run, gens, last gen, credits
117
+ wji class status --tag HW1 --sort progress # just the assignment, least done first
118
+ wji class status --sort recent # who generated most recently
119
+ wji class results --tag HW1 # finished work, newest first
120
+ wji class results --student "Amy" --since 2h
121
+ wji class results --tag HW1 -d ./hw1 # download: ./hw1/<student>/<node>-<id>.png
122
+ wji class results --tag HW1 --all # also display nodes students uploaded
123
+ ```
124
+
125
+ To review, download and look at the files, then leave feedback on the
126
+ student's canvas:
127
+
128
+ ```bash
129
+ wji canvas list --owner "Amy"
130
+ wji canvas add comment -c <amyCanvas> -t "Lovely colors — try a warmer background" --at 0,-200
131
+ ```
132
+
133
+ ## Rules
134
+
135
+ - **Emails go out in the teacher's name.** Show the exact address list and
136
+ role and get a yes in the chat before `ws member invite`. Same for
137
+ `ws member link` when a link is already active (it stops working).
138
+ - **Copying writes into every student's canvas.** Say what will be copied
139
+ (node names, count of students) and get a yes, then pass `-y`.
140
+ - **Don't generate on students' canvases** unless the teacher asks — it
141
+ spends the teacher's credits and replaces what the student would do. When
142
+ asked, follow the `wji` skill's cost rules (show the total for N students).
143
+ - **Deleting in a student's canvas** (`canvas rm`) follows the `wji`
144
+ confirm-before-destroying rule, one canvas at a time.
145
+ - Look students up by name or email (`--student "Amy"`); if a name is
146
+ ambiguous the CLI says so — ask, don't guess.
147
+
148
+ ## Good to know
149
+
150
+ - Private (`owner`) visibility hides a canvas; it does not lock it. On a
151
+ `members` canvas any editor can edit anything.
152
+ - Students can edit or delete workspace presets (the backend has no role
153
+ check there). Keep the canonical prompts in the teacher's own presets too
154
+ (`wji preset create` without `-w`) and re-copy with `wji preset copy`.
155
+ - Generation history in a workspace is readable by every member, so students
156
+ can technically list each other's generations through the API even when
157
+ canvases are private.
158
+ - Organization admins who are not the workspace owner cannot invite or set
159
+ canvas owners — the workspace owner must.
160
+ - Credits in `class status` are what each student spent in this workspace
161
+ (all time); `wji ws quota` sets a workspace spending cap.