@wji.ai/cli 0.4.0 → 0.5.1
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 +63 -2
- package/dist/commands/backreel.js +260 -0
- package/dist/commands/canvas.js +588 -88
- package/dist/commands/class.js +395 -0
- package/dist/commands/npc.js +413 -0
- package/dist/commands/workspace.js +109 -24
- package/dist/core/members.js +54 -0
- package/dist/core/npc.js +108 -0
- package/dist/core/output.js +4 -2
- package/dist/index.js +8 -0
- package/package.json +1 -1
- package/skill/ads/SKILL.md +176 -0
- package/skill/ads/reference/ai-consistency.md +109 -0
- package/skill/ads/reference/brief-template.md +103 -0
- package/skill/ads/reference/compliance.md +66 -0
- package/skill/ads/reference/formats-image.md +108 -0
- package/skill/ads/reference/formats-video.md +121 -0
- package/skill/ads/reference/models.md +90 -0
- package/skill/ads/reference/product-photography.md +119 -0
- package/skill/ads/reference/styles.md +70 -0
- package/skill/short-drama/SKILL.md +90 -8
- package/skill/short-drama/reference/action-bullet-time.md +47 -0
- package/skill/short-drama/reference/action-car-chase.md +51 -0
- package/skill/short-drama/reference/action-gun-fu.md +52 -0
- package/skill/short-drama/reference/action-historical-realist.md +50 -0
- package/skill/short-drama/reference/action-hk-kungfu.md +54 -0
- package/skill/short-drama/reference/action-street-brawl.md +49 -0
- package/skill/short-drama/reference/action-superhero.md +50 -0
- package/skill/short-drama/reference/action-wuxia-fantasy.md +51 -0
- package/skill/short-drama/reference/action.md +94 -0
- package/skill/short-drama/reference/cinematography-costume-dark.md +117 -0
- package/skill/short-drama/reference/cinematography-costume-idol.md +126 -0
- package/skill/short-drama/reference/cinematography-low-key.md +115 -0
- package/skill/short-drama/reference/cinematography-period-nostalgia.md +106 -0
- package/skill/short-drama/reference/cinematography-scifi-hard.md +63 -0
- package/skill/short-drama/reference/cinematography-scifi-soft.md +66 -0
- package/skill/short-drama/reference/cinematography-sweet-romance.md +175 -0
- package/skill/short-drama/reference/cinematography.md +83 -0
- package/skill/short-drama/reference/composition-art-first.md +74 -0
- package/skill/short-drama/reference/craft-shots.md +3 -1
- package/skill/short-drama/reference/craft-story.md +4 -1
- package/skill/short-drama/reference/format.md +94 -0
- package/skill/short-drama/reference/grammar-comedy.md +105 -0
- package/skill/short-drama/reference/grammar-faceslap.md +67 -0
- package/skill/short-drama/reference/grammar.md +103 -0
- package/skill/short-drama/reference/operations-cli.md +1 -1
- package/skill/short-drama/reference/schema.md +34 -3
- package/skill/short-drama/reference/stage-art.md +1 -1
- package/skill/short-drama/reference/stage-outline.md +21 -0
- package/skill/short-drama/reference/stage-script.md +10 -0
- package/skill/short-drama/reference/stage-storyboard.md +15 -0
- package/skill/short-drama/reference/story-angst.md +117 -0
- package/skill/short-drama/reference/story-comedy.md +136 -0
- package/skill/short-drama/reference/story-cultivation.md +109 -0
- package/skill/short-drama/reference/story-family.md +119 -0
- package/skill/short-drama/reference/story-horror.md +124 -0
- package/skill/short-drama/reference/story-mystery.md +127 -0
- package/skill/short-drama/reference/story-palace.md +103 -0
- package/skill/short-drama/reference/story-revenge.md +124 -0
- package/skill/short-drama/reference/story-scifi.md +112 -0
- package/skill/short-drama/reference/story-sweet.md +128 -0
- package/skill/short-drama/reference/story.md +127 -0
- package/skill/short-drama/scripts/selftest.mjs +809 -1
- package/skill/short-drama/scripts/short-drama.mjs +1608 -16
- package/skill/social-media/SKILL.md +152 -0
- package/skill/social-media/reference/content-craft.md +124 -0
- package/skill/social-media/reference/plan-template.md +171 -0
- package/skill/social-media/reference/platforms-china.md +94 -0
- package/skill/social-media/reference/platforms-global.md +122 -0
- package/skill/social-media/reference/ratio-map.md +100 -0
- package/skill/social-media/reference/repurpose.md +119 -0
- package/skill/wji/SKILL.md +66 -2
- package/skill/wji/reference/commands.md +76 -15
- package/skill/wji-classroom/SKILL.md +161 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Platform specs — global (Instagram, TikTok, YouTube, Facebook, X, LinkedIn, Pinterest, Threads, Snapchat)
|
|
2
|
+
|
|
3
|
+
One table per platform, one row per surface: the ratio and pixel size to deliver, the duration
|
|
4
|
+
and file-size caps, the safe zone where UI covers the picture, the text limits, and what has
|
|
5
|
+
changed recently with the date. Copy the row into the post spec sheet (plan-template.md) before
|
|
6
|
+
generating anything; the generation ratio and crop box for each row is in ratio-map.md, and the
|
|
7
|
+
Chinese platforms are in platforms-china.md. Figures marked *(verify)* were inconsistent between
|
|
8
|
+
sources in the 2025–2026 research; "not confirmed" means the research produced no figure — never
|
|
9
|
+
fill it from memory, say so and let the user check the platform's current help page.
|
|
10
|
+
|
|
11
|
+
## The 9:16 safe zone (1080×1920) — one diagram for every vertical surface
|
|
12
|
+
|
|
13
|
+
Vertical surfaces all hide the top band behind the status bar and the bottom band behind the
|
|
14
|
+
caption, the sound tag and the action rail on the right. Measured bands, in pixels of a
|
|
15
|
+
1080×1920 frame:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
y 0 ┌────────────────────────────┐
|
|
19
|
+
│ ▒ top band: Meta 14 % = 269 px (Stories / Reels, unified Mar 2026)
|
|
20
|
+
│ TikTok 108 · IG Reels 220 · Snapchat 150
|
|
21
|
+
y 270 ├────────────────────────────┤ ▒ right rail (likes / comments / share):
|
|
22
|
+
│ │ TikTok 120 px · Meta 6 % = 65 px
|
|
23
|
+
│ SAFE — faces, headline, │ ▒ left band: TikTok 60 px · Meta 65 px
|
|
24
|
+
│ subtitles, product, │
|
|
25
|
+
│ x 120 → 960 │
|
|
26
|
+
y 1248 ├────────────────────────────┤
|
|
27
|
+
│ ▒ bottom band: Meta 20–35 % = 384–672 px (caption + sound + CTA)
|
|
28
|
+
│ TikTok 320 · IG Reels 420 · Snapchat 330
|
|
29
|
+
y 1920 └────────────────────────────┘
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The conservative union — text and faces inside **x 120–960, y 270–1248** — is safe on every
|
|
33
|
+
platform in this file at once, so a single 9:16 master can be posted everywhere. Per-platform
|
|
34
|
+
bands are looser; use them only when a variant is made for one platform.
|
|
35
|
+
|
|
36
|
+
## Instagram
|
|
37
|
+
|
|
38
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
39
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
40
|
+
| Feed image | 4:5 1080×1350 (standard); 3:4 1080×1440 (shows uncropped in the grid); 1:1 1080×1080; 1.91:1 1080×566 | — | not confirmed | grid tile is 3:4 since Jan 2025: a 4:5 loses ≈3 % per side, 1:1 ≈12 % per side, 1.91:1 keeps only the centre | caption 2,200; **≤5 hashtags since Dec 2025** | 4:5 fills the most feed height; 1080×1440 may fail via API upload |
|
|
41
|
+
| Feed video | 4:5 or 9:16 | 3 s–60 min | not confirmed | as Reels | caption 2,200; ≤5 hashtags | every feed video is shown as a Reel |
|
|
42
|
+
| Reels | 9:16 1080×1920 | 3 min in-app (Jan 2025); up to 15 min upload *(verify)* | not confirmed | 220 px top, 420 px bottom (IG figure); Meta unified 14 % / 20–35 % / 6 % from Mar 2026 | caption 2,200; ≤5 hashtags | cover frame is cropped to 3:4 in the grid — keep the cover's title in the centre |
|
|
43
|
+
| Stories | 9:16 1080×1920 | 60 s per card | not confirmed | 14 % top and bottom clear (269 px) | sticker text, link sticker | tap-through: one idea per card |
|
|
44
|
+
| Carousel | one ratio for every slide (4:5 recommended) | — | not confirmed | first slide sets the crop for all | caption 2,200; ≤5 hashtags | ≤20 slides; last slide carries the CTA |
|
|
45
|
+
| Profile grid | 3:4 tiles since Jan 2025 | — | — | design covers for the 3:4 tile | — | previews of 9:16 Reels and 1:1 posts are centre-cropped |
|
|
46
|
+
|
|
47
|
+
## TikTok
|
|
48
|
+
|
|
49
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
50
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
51
|
+
| Video | 9:16 1080×1920 | ≤60 min upload, 10 min in-app | not confirmed | 108 px top · 320 px bottom · 60 px left · 120 px right | caption 4,000 | product or key message inside 3 s; start in motion |
|
|
52
|
+
| Photo mode | 9:16 (1:1 and 4:5 accepted) | — | not confirmed | as video | caption 4,000 | ≤35 images; swipes like a carousel with music |
|
|
53
|
+
| Cover | first frame or a chosen frame, 9:16 | — | — | profile-grid tile crop not confirmed — keep title text in the centre 60 % of the frame | cover text | face + ≤4 words; the same template across a series |
|
|
54
|
+
|
|
55
|
+
## YouTube
|
|
56
|
+
|
|
57
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
58
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
59
|
+
| Long-form | 16:9 1920×1080 or 3840×2160 | 15 min unverified account, 12 h verified | not confirmed | end-screen area in the last 5–20 s | title 100 (≈60–70 shown), description 5,000 | chapters from timestamps in the description |
|
|
60
|
+
| Shorts | 9:16 or 1:1 | ≤3 min (Oct 2024) | not confirmed | pixel bands not confirmed — keep captions above the bottom UI; use the 9:16 diagram | title 100 | a Short is any vertical video ≤3 min with #Shorts optional |
|
|
61
|
+
| Thumbnail | 16:9 1280×720 | — | ≤2 MB | duration badge sits bottom-right; keep that corner clear | — | face + ≤4 words, high contrast; see content-craft.md |
|
|
62
|
+
| Posts (formerly Community tab) | image ratio not confirmed | — | not confirmed | — | not confirmed | renamed from "Community" to "Posts"; polls, images, text |
|
|
63
|
+
| End screens | inside the 16:9 frame | last 5–20 s of videos ≥25 s | — | elements occupy the frame — no important picture behind them | — | ≤4 elements (video, playlist, subscribe, link) |
|
|
64
|
+
|
|
65
|
+
## Facebook
|
|
66
|
+
|
|
67
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
68
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
69
|
+
| Feed image | 4:5 1080×1350 or 1:1 1080×1080 | — | not confirmed | — | post text not confirmed | 4:5 fills the most feed height |
|
|
70
|
+
| Feed video | 4:5 or 9:16 | 1 s–240 min | not confirmed | Meta 9:16 bands | not confirmed | **all Facebook video is published as Reels since Jun 2025** |
|
|
71
|
+
| Reels | 9:16 1080×1920 | 3–90 s | not confirmed | Meta unified: 14 % top, 20–35 % bottom, 6 % sides (Mar 2026) | caption not confirmed | same master as Instagram Reels |
|
|
72
|
+
| Stories | 9:16 1080×1920 | 1–60 s | not confirmed | Meta unified bands | sticker text | — |
|
|
73
|
+
| Link preview | 1.91:1 1200×630 (OG image) | — | not confirmed | — | title and description from the page's OG tags | crop from 16:9 — see ratio-map.md |
|
|
74
|
+
|
|
75
|
+
## X
|
|
76
|
+
|
|
77
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
78
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
79
|
+
| Image post | 16:9 1600×900 or 1:1 | — | not confirmed | timeline preview crops tall images to 16:9-ish — keep the subject centred | 280 chars (Premium longer) | up to 4 images; the first sets the layout |
|
|
80
|
+
| Video | 16:9 or 1:1, ≤1920×1200 | 2 min 20 s (Premium up to 4 h) | 512 MB | — | 280 chars | autoplays muted — burned subtitles |
|
|
81
|
+
|
|
82
|
+
## LinkedIn
|
|
83
|
+
|
|
84
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
85
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
86
|
+
| Image post | 1080×1080 or 4:5 1080×1350 | — | not confirmed | — | 3,000 chars ("see more" after ≈3 lines) | first two lines carry the hook |
|
|
87
|
+
| Link preview | 1.91:1 1200×627 | — | not confirmed | — | OG title / description | crop from 16:9 — see ratio-map.md |
|
|
88
|
+
| Video | 16:9, 1:1 or 9:16 | ≤10 min organic | 5 GB | — | 3,000 chars | muted autoplay — burned subtitles; 3 hashtags |
|
|
89
|
+
| Document / carousel | PDF pages, 1:1 or 4:5 | — | not confirmed | — | — | one idea per page, last page CTA |
|
|
90
|
+
|
|
91
|
+
## Pinterest
|
|
92
|
+
|
|
93
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
94
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
95
|
+
| Standard Pin | 2:3 1000×1500 | — | not confirmed | — | title 100; description 500–800 *(verify)* | Idea Pins merged into standard Pins in 2023 |
|
|
96
|
+
| Video Pin | 9:16 (2:3 accepted) | 4 s–15 min | not confirmed | bottom band hides behind the title on the feed card | title 100 | first frame is the cover unless a cover is chosen |
|
|
97
|
+
|
|
98
|
+
## Threads
|
|
99
|
+
|
|
100
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
101
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
102
|
+
| Post | 4:5 best; 9:16, 1:1, 16:9 accepted | video ≤5 min | 500 MB | — | 500 chars | text-first feed: the first line is the hook |
|
|
103
|
+
|
|
104
|
+
## Snapchat
|
|
105
|
+
|
|
106
|
+
| Surface | Ratio · px | Duration | File size | Safe zone | Text limits | Notes |
|
|
107
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
108
|
+
| Story / Spotlight | 9:16 1080×1920 | 3–180 s | not confirmed | 150 px top · 330 px bottom | caption not confirmed | full-screen only; no letterboxing |
|
|
109
|
+
|
|
110
|
+
## Recent changes to keep in mind (dated)
|
|
111
|
+
|
|
112
|
+
| When | Platform | Change | Effect on delivery |
|
|
113
|
+
| --- | --- | --- | --- |
|
|
114
|
+
| Oct 2024 | YouTube | Shorts up to 3 min | drama teasers up to 3 min qualify as Shorts |
|
|
115
|
+
| Jan 2025 | Instagram | profile grid tiles 3:4; Reels 3 min in-app | covers designed for 3:4; 4:5 posts lose ≈3 % per side in the grid |
|
|
116
|
+
| 2025 | YouTube | Community tab renamed Posts | say "Posts" in plans |
|
|
117
|
+
| Jun 2025 | Facebook | every video post is a Reel | there is no separate feed-video spec; use the Reels row |
|
|
118
|
+
| Dec 2025 | Instagram | hashtags capped at 5 per post | captions with more are rejected or trimmed; pick 5 |
|
|
119
|
+
| Mar 2026 | Meta | one unified safe zone for Stories and Reels: 14 % top, 20–35 % bottom, 6 % sides | one 9:16 master serves IG and FB Reels and Stories |
|
|
120
|
+
|
|
121
|
+
Where a row says "not confirmed", the platform may well have a published figure; the research
|
|
122
|
+
did not verify it, so the plan must not state one.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Ratio map — placement size → generation ratio, model, crop box
|
|
2
|
+
|
|
3
|
+
Every image placement in platforms-global.md and platforms-china.md, mapped to the ratio a WJI
|
|
4
|
+
image model actually generates (taken from the model seeds — the model's `ratio` list is the
|
|
5
|
+
law, not the platform's wish) and, where the placement is not a native ratio, to the nearest
|
|
6
|
+
native ratio plus the crop box that reaches the exact pixels. Then the same for video, where
|
|
7
|
+
the rule is different: **video is regenerated at the target ratio from the same references,
|
|
8
|
+
never cropped**. Read this after choosing the surfaces and before writing the generation plan
|
|
9
|
+
in plan-template.md.
|
|
10
|
+
|
|
11
|
+
## Native image ratios per model (from the seeds)
|
|
12
|
+
|
|
13
|
+
| Model (`model`) | Ratios | Resolution | Refs |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| nano-banana, nano-banana-pro, nano-banana-2 | auto 1:1 2:3 3:2 3:4 4:3 **4:5** 5:4 9:16 16:9 21:9 9:21 | pro / 2: 1K–4K | 14 (pro / 2), 4 (nano-banana) |
|
|
16
|
+
| nano-banana-2-lite | same minus 9:21 | | |
|
|
17
|
+
| gpt-image-2 | auto 1:1 4:3 3:4 16:9 9:16 | 1K / 2K / 4K | 16 |
|
|
18
|
+
| gpt-image-2.5 (sunburst / flare) | gpt-image-2 + **21:9** | 1K / 2K / 4K | 16 |
|
|
19
|
+
| bytedance-seedream-4.5 | 1:1 4:3 3:4 16:9 9:16 | 2K / 4K | |
|
|
20
|
+
| bytedance-seedream-5-lite / 5-pro | seedream-4.5 + 3:2 **2:3** 21:9 | size select; 5-pro layered PNG | |
|
|
21
|
+
| flux-2-flex | 1:1 16:9 3:2 2:3 **4:5** 5:4 9:16 3:4 4:3 | | |
|
|
22
|
+
| marketing-studio-image | auto 1:1 3:2 2:3 4:3 3:4 16:9 9:16 21:9 | 1k / 2k / 4k | 8 |
|
|
23
|
+
| runninghub-midjourney v7 / v8.1 / v8.2 | 1:1 4:3 3:2 16:9 3:4 2:3 9:16 | | |
|
|
24
|
+
|
|
25
|
+
No model generates 1.91:1, 6:7, 16:10, 2.35:1 or any banner size. Those are crops.
|
|
26
|
+
|
|
27
|
+
**Resolution rule.** Pick the resolution whose generated short side is at least the target's short
|
|
28
|
+
side after cover-fit, then downscale; never upscale a 1K frame to a 1242-px-wide note. 1K frames
|
|
29
|
+
(≈1024 on the long side) are too small for every placement in this file except 200×200 and
|
|
30
|
+
400×400; generate at 2K for everything else and at 4K when the crop keeps less than 60 % of the
|
|
31
|
+
frame.
|
|
32
|
+
|
|
33
|
+
## Image placements
|
|
34
|
+
|
|
35
|
+
| Placement (file) | Target px | Ratio | Native models | If not native: generate → crop |
|
|
36
|
+
| --- | --- | --- | --- | --- |
|
|
37
|
+
| IG feed / FB feed / Threads | 1080×1350 | 4:5 | nano-banana, nano-banana-pro, nano-banana-2, flux-2-flex | 3:4 → cover-fit to 1080 wide (1080×1440) → crop 45 px top and 45 px bottom |
|
|
38
|
+
| IG feed grid-native, 小红书 图文, 抖音 图文, 小红书 cover | 1080×1440 / 1242×1660 | 3:4 | all models in the table above | 1242×1660 is 4 px off exact 3:4 — cover-fit to height 1660 (1245 wide) → crop 1–2 px each side |
|
|
39
|
+
| IG Reels / Stories / TikTok / Shorts / 抖音 / 快手 covers, Snapchat, 视频号 9:16 | 1080×1920 | 9:16 | all | — |
|
|
40
|
+
| IG 1:1, FB 1:1, LinkedIn post, 视频号 1:1, 公众号 secondary cover, X square | 1080×1080 / 1200×1200 / 400×400 | 1:1 | all | — |
|
|
41
|
+
| YouTube thumbnail, X, B站 video frame, 微博 1000×562 | 1280×720 / 1600×900 / 1920×1080 / 1000×562 | 16:9 | all | 1000×562 is 16:9 to the pixel — resize only; keep the title inside the middle 400 px |
|
|
42
|
+
| Pinterest Pin | 1000×1500 | 2:3 | nano-banana family, seedream-5-lite / 5-pro, flux-2-flex, marketing-studio-image, midjourney | 3:4 → cover-fit to height 1500 (1125 wide) → crop 62 px left, 63 px right |
|
|
43
|
+
| 视频号 4:3, B站 动态 | 1080×810 | 4:3 | all | — |
|
|
44
|
+
| IG landscape | 1080×566 | 1.91:1 | none | 16:9 → cover-fit to 1080 wide (1080×608) → crop 21 px top and bottom |
|
|
45
|
+
| FB / LinkedIn link preview, Google landscape | 1200×630 / 1200×627 / 1200×628 | 1.91:1 | none | 16:9 → cover-fit to 1200 wide (1200×675) → crop 22–24 px top and bottom (45 / 48 / 47 px in total) |
|
|
46
|
+
| 视频号 feed card | 1080×1260 | 6:7 | none | 3:4 → 1080×1440 → crop 90 px top and 90 px bottom (keeps full width); or 1:1 → cover-fit to height 1260 (1260 wide) → crop 90 px each side (keeps full height) |
|
|
47
|
+
| B站 cover | 1146×717 | ≈16:10 | none | **3:2** (closest) → cover-fit to 1146 wide (1146×764) → crop 23 px top, 24 px bottom; or 16:9 (from the master) → cover-fit to height 717 (1275 wide) → crop 64–65 px each side |
|
|
48
|
+
| B站 cover large | 1920×1200 | 16:10 | none | 3:2 → 1920×1280 → crop 40 px top and bottom; or 16:9 → 2133×1200 → crop 106–107 px each side |
|
|
49
|
+
| 公众号 cover | 900×383 | 2.35:1 | none | 21:9 (gpt-image-2.5, nano-banana family, seedream-5-lite / 5-pro, marketing-studio-image) → cover-fit to 900 wide (900×386) → crop 1 px top, 2 px bottom; the 1:1 share crop is the centre 383×383 (x 258–641) — compose for that square |
|
|
50
|
+
| 微博 头条文章 alt | 900×500 | 1.8:1 | none | 16:9 → cover-fit to 900 wide (900×506) → crop 3 px top and bottom |
|
|
51
|
+
| 公众号 secondary | 200×200 (export 400×400) | 1:1 | all | generate 1:1 at 1K, downscale to 400, deliver 400×400 |
|
|
52
|
+
|
|
53
|
+
"Cover-fit" means: scale the generated frame uniformly until it covers the target box, then cut
|
|
54
|
+
what falls outside, anchored as described under "Crop anchoring". One generated frame per ratio,
|
|
55
|
+
never one per pixel size: a single 16:9 frame serves the YouTube thumbnail, X, 微博 and every
|
|
56
|
+
1.91:1 preview.
|
|
57
|
+
|
|
58
|
+
## Video placements
|
|
59
|
+
|
|
60
|
+
| Placement | Ratio | Models that generate it natively (`ratio`) | Duration range (`duration`) |
|
|
61
|
+
| --- | --- | --- | --- |
|
|
62
|
+
| Reels, Stories, TikTok, Shorts, 抖音, 快手, 小红书 video, 视频号 9:16, Snapchat, Pinterest video | 9:16 | minimax-h3 (+ -ref), seedance-2.5 (+ -ref), seedance-2.0 family, wan-3.0 (+ -ref, prime), veo-3.1, kling-3, happy-horse | h3 5–15 s · seedance-2.5 4–30 s · seedance-2.0 4–15 s · wan-3.0 2–30 s · veo-3.1 4–8 s · kling-3 3–15 s · happy-horse 3–15 s |
|
|
63
|
+
| YouTube long-form, B站, X, LinkedIn, FB in-stream | 16:9 | all of the above | as above |
|
|
64
|
+
| IG / FB / LinkedIn square, 视频号 1:1, Shorts 1:1, X 1:1 | 1:1 | minimax-h3, seedance-2.5, seedance-2.0, wan-3.0, kling-3, happy-horse (not veo-3.1) | as above |
|
|
65
|
+
| 视频号 4:3 | 4:3 | minimax-h3, seedance-2.5, seedance-2.0, wan-3.0, happy-horse | as above |
|
|
66
|
+
| 小红书 3:4 video, 视频号 tall card, 抖音 图文-style video | 3:4 | minimax-h3, seedance-2.5, seedance-2.0, wan-3.0, happy-horse | as above |
|
|
67
|
+
| Cinematic banner, hero loop | 21:9 | minimax-h3, seedance-2.5, seedance-2.0 | as above |
|
|
68
|
+
| 视频号 6:7, Pinterest 2:3, 1.91:1 previews | — | none | deliver the nearest accepted ratio instead: 视频号 takes 3:4 or 1:1 (0.33–3.0 accepted), Pinterest takes 9:16, link previews take a 16:9 still |
|
|
69
|
+
|
|
70
|
+
Resolutions: minimax-h3 480P / 768P / 2K / 4K; seedance-2.5 480p / 720p / 1080p; wan-3.0 480P /
|
|
71
|
+
720P / 1080P; seedance-2.0 480p / 720p. Publish 1080p or the model's top tier below it; the
|
|
72
|
+
platform re-encodes anyway.
|
|
73
|
+
|
|
74
|
+
**The video rule.** A 16:9 master cropped to 9:16 loses two thirds of the frame and lands the
|
|
75
|
+
faces under the caption band; a 9:16 master padded to 16:9 is a phone video with bars. So every
|
|
76
|
+
ratio in the placement list is a **new generation**: the same character / scene / product
|
|
77
|
+
references (`@image1` …), the same segment prompt with the framing sentence rewritten for the
|
|
78
|
+
new frame ("vertical, subject centred, head at the upper third, room built in depth"), the same
|
|
79
|
+
`duration`. Cost is one clip per ratio per segment — plan it in repurpose.md, quote it, then
|
|
80
|
+
generate. The only cut permitted on a video is trimming its length.
|
|
81
|
+
|
|
82
|
+
## Crop anchoring
|
|
83
|
+
|
|
84
|
+
A crop box has an anchor: the point that must stay inside. Decide it per frame before cutting.
|
|
85
|
+
|
|
86
|
+
| Frame content | Anchor | Then |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| A face (poster, teaser cover, testimonial still) | the face box; eyes at ≈1/3 from the top of the target | crop from the bottom first, then the top; never cut the chin or the hairline |
|
|
89
|
+
| Two faces | the midpoint between them, both boxes inside | if both cannot fit, regenerate the frame tighter rather than cut one out |
|
|
90
|
+
| Product / packshot | the product's centre; the whole product plus its shadow inside | crop the sides symmetrically; on 1.91:1 previews keep the label readable at 600 px wide |
|
|
91
|
+
| Headline zone (key visual with text) | the text block plus its margin | text is rendered into the picture only on the final ratio — on crops, generate without text and overlay after |
|
|
92
|
+
| Scene / landscape | the horizon or the main light | side crops are free; top / bottom crops keep the horizon off the edge |
|
|
93
|
+
|
|
94
|
+
After the crop, re-check the target's safe zone (platforms-global.md / platforms-china.md):
|
|
95
|
+
a 6:7 视频号 cover is shared to 朋友圈 as its centre 1:1, so the face has to sit inside that
|
|
96
|
+
square; a 900×383 公众号 cover is read as its centre 383×383 in chats; a 4:5 IG post loses ≈3 %
|
|
97
|
+
per side in the 3:4 grid; a 16:9 thumbnail keeps the bottom-right corner clear for the duration
|
|
98
|
+
badge and a B站 cover the bottom-left. When the anchor cannot survive the crop, the answer is a
|
|
99
|
+
new generation at the closer native ratio (3:4 instead of 1:1 for 6:7, 3:2 instead of 16:9 for
|
|
100
|
+
1146×717), not a smaller face.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Repurposing — one master → every placement; episode → teaser; ad → placements
|
|
2
|
+
|
|
3
|
+
How one piece of source material becomes a full set of platform deliverables without cropping
|
|
4
|
+
video: which surfaces need which variant, what is regenerated and what is cropped, in which
|
|
5
|
+
order, and the two recipes that use it most — a 短剧 episode turned into vertical teasers and a
|
|
6
|
+
poster set, and an ad master turned into its placements. Sizes come from platforms-global.md
|
|
7
|
+
and platforms-china.md, the generation ratios and crop boxes from ratio-map.md, the hook and
|
|
8
|
+
caption rules from content-craft.md; the result is written into the spec sheet of
|
|
9
|
+
plan-template.md.
|
|
10
|
+
|
|
11
|
+
## 1. Master → placement matrix
|
|
12
|
+
|
|
13
|
+
Two masters exist: a **16:9 master** (long-form, TVC, 中剧 / movie, B站, YouTube) or a
|
|
14
|
+
**9:16 master** (短剧, Reels-first ads). Given one, the matrix says what each surface needs.
|
|
15
|
+
|
|
16
|
+
| Surface | From a 16:9 master | From a 9:16 master | Kind |
|
|
17
|
+
| --- | --- | --- | --- |
|
|
18
|
+
| YouTube long-form, B站, X, LinkedIn 16:9 | the master, trimmed | regenerate 16:9 (wides can carry two people; keep the vertical's cut points) | video |
|
|
19
|
+
| Reels, TikTok, Shorts, 抖音, 快手, 小红书 video, 视频号 9:16, Snapchat, Pinterest video | **regenerate 9:16** from the same references, reframed on faces | the master, trimmed to the surface's cap | video |
|
|
20
|
+
| IG / FB / LinkedIn square, 视频号 1:1 | regenerate 1:1 | regenerate 1:1 | video |
|
|
21
|
+
| 视频号 6:7 card, 小红书 3:4 video | regenerate 3:4 (视频号 accepts it; 6:7 is not generatable) | regenerate 3:4 | video |
|
|
22
|
+
| YouTube thumbnail, X image, 微博 1000×562 | a keyframe of the master, 16:9 | regenerate a 16:9 still from the same references | image |
|
|
23
|
+
| IG feed 4:5, Threads | regenerate 4:5 still (nano-banana family / flux-2-flex) | regenerate 4:5 | image |
|
|
24
|
+
| 小红书 图文 / cover 3:4, IG grid 3:4 | regenerate 3:4 still | regenerate 3:4 | image |
|
|
25
|
+
| Pinterest 2:3 | regenerate 2:3 still | regenerate 2:3 | image |
|
|
26
|
+
| Link previews 1.91:1, 微博 cover, 公众号 900×383, B站 1146×717, 视频号 6:7 cover | crop from the 16:9 / 21:9 / 3:2 / 3:4 still per ratio-map.md | same, from a regenerated still | image crop |
|
|
27
|
+
| Stories cards | regenerate 9:16 stills or reuse the 9:16 clips ≤ 60 s | trim the master into ≤ 60-s cards | either |
|
|
28
|
+
|
|
29
|
+
**Regenerate vs crop.** Video: always regenerate (ratio-map.md, the video rule) — same
|
|
30
|
+
`@imageN` references, same `duration`, framing sentence rewritten for the new frame. Image:
|
|
31
|
+
regenerate at every native ratio (4:5, 3:4, 2:3, 1:1, 16:9, 21:9); crop only for the ratios no
|
|
32
|
+
model has (1.91:1, 6:7, 16:10, 2.35:1), and crop from the nearest native, not from the master.
|
|
33
|
+
|
|
34
|
+
**Order of work.** (1) Spec sheet rows for every surface. (2) Stills first: one generation per
|
|
35
|
+
native ratio, from the same references; approve them — they are the covers and the reference
|
|
36
|
+
frames for the clips. (3) Crops from the approved stills. (4) Video regenerations, one ratio at
|
|
37
|
+
a time, primary platform first; quote before each batch. (5) Overlays and subtitles at the final
|
|
38
|
+
ratio. (6) Captions, hashtags, slots. (7) The deliverables checklist (section 4) filled in.
|
|
39
|
+
|
|
40
|
+
## 2. Drama episode → teaser + poster set
|
|
41
|
+
|
|
42
|
+
Source: an episode's storyboard (segments, cuts, the 爽点 and the cliff) and the character /
|
|
43
|
+
scene sheets it was generated from. The teaser is not the episode's first 30 seconds and not a
|
|
44
|
+
crop of the episode; it is **new 9:16 segments** built from the same sheets.
|
|
45
|
+
|
|
46
|
+
### The 4-beat teaser (15–45 s)
|
|
47
|
+
|
|
48
|
+
| Beat | Time | Content | Source in the storyboard |
|
|
49
|
+
| --- | --- | --- | --- |
|
|
50
|
+
| Hook | 0–3 s | the most shocking line or action from the *middle* of the episode, cut in cold, burned-in text of the line | the 爽点 cut or the turn's reveal cut |
|
|
51
|
+
| Context | 3–15 s | who wants what, in two cuts and one line — enough to make the hook hurt | the episode's establish / evidence cuts |
|
|
52
|
+
| Escalation | 15–35 s | the pressure rising toward the payoff: the decision cut, the reaction cut | the cuts before the 爽点 / cliff |
|
|
53
|
+
| Cut before payoff | last 2–5 s | freeze one beat before the answer + 「主页看全集 / full episode in bio」 card in the same template as the cover | the cliff cut, ending early |
|
|
54
|
+
|
|
55
|
+
- **2–3 variants** that differ only in the hook beat (a line, a result-first, a question — see
|
|
56
|
+
content-craft.md); beats 2–4 are shared, so only the hook segment is regenerated per variant.
|
|
57
|
+
- Generated as **9:16 segments** with the same character and scene sheets as references
|
|
58
|
+
(`@image1` character, `@image2` scene …), segment length inside the video model's range
|
|
59
|
+
(ratio-map.md), 3–5 segments for a 30-s teaser. Prompts reframe on faces: medium / close /
|
|
60
|
+
extreme-close, space built in depth (foreground shoulder, counter edge, room behind) — a wide
|
|
61
|
+
on a phone says nothing.
|
|
62
|
+
- Subtitles burned in, inside the safe zone; the hook line is on screen from frame 1.
|
|
63
|
+
- Sound: the episode's voice track for the lines, one Seed Audio sting under the cut-before-payoff.
|
|
64
|
+
- A 16:9 teaser for YouTube / B站 is a further regeneration of the same beats, not a pad.
|
|
65
|
+
|
|
66
|
+
### The poster / cover set
|
|
67
|
+
|
|
68
|
+
From the episode's keyframes or a regenerated hero still (the two leads in the 爽点 pose, the
|
|
69
|
+
scene sheet behind), one generation per native ratio, then the crops; title-safe zones per
|
|
70
|
+
surface:
|
|
71
|
+
|
|
72
|
+
| Surface | Ratio · px | Generate | Title-safe zone |
|
|
73
|
+
| --- | --- | --- | --- |
|
|
74
|
+
| 小红书 cover / 图文 | 3:4 1242×1660 | native 3:4, text inside the picture (nano-banana-pro / seedream-5-pro) | middle 70 % — the tile is half a screen |
|
|
75
|
+
| Instagram feed | 4:5 1080×1350 | native 4:5 | avoid the outer 3 % (grid crop) |
|
|
76
|
+
| Pinterest | 2:3 1000×1500 | native 2:3 | lower third for the title band |
|
|
77
|
+
| YouTube thumbnail | 16:9 1280×720 | native 16:9, face + ≤ 4 words | keep the bottom-right clear |
|
|
78
|
+
| B站 cover | 1146×717 | 3:2 → crop 23 / 24 px | keep the bottom-left clear |
|
|
79
|
+
| 视频号 cover | 6:7 1080×1260 | 3:4 → crop 90 / 90 px | inside the centre 1:1 square (朋友圈 crop) |
|
|
80
|
+
| Reels / TikTok / 抖音 cover | 9:16 1080×1920 | the teaser's first frame or a native 9:16 still | centre 60 % (grid tile crop) |
|
|
81
|
+
|
|
82
|
+
One title template for the whole series: 剧名 + 第N集 + one-line hook, same position on every
|
|
83
|
+
cover.
|
|
84
|
+
|
|
85
|
+
## 3. Ad master → placements
|
|
86
|
+
|
|
87
|
+
Source: the ad brief (product references, CTA, legal lines, disclosure need), the key visual
|
|
88
|
+
(KV) and the master clip (16:9 or 9:16) from the ads skill.
|
|
89
|
+
|
|
90
|
+
- **KV crops per size**: the KV is regenerated at 4:5, 1:1, 3:4, 2:3, 16:9 and 21:9 with the
|
|
91
|
+
packshot as `@image1` and "keep the label, shape and colour exactly"; the 1.91:1 previews,
|
|
92
|
+
900×383, 1146×717 and 6:7 are crops of those (ratio-map.md), anchored on the product centre.
|
|
93
|
+
Legal text and small print are overlaid after the crop, never generated.
|
|
94
|
+
- **Vertical / square video regenerations**: 9:16 and 1:1 (and 4:3 / 3:4 for 视频号 and 小红书)
|
|
95
|
+
from the same product stills and the same shot list; product in frame by 3 s; CTA card in the
|
|
96
|
+
last 3 s inside the safe zone; 15–30 s for Reels / Stories ads, ≤ 6 s for a bumper-style cut.
|
|
97
|
+
- **Per-platform copy** from the brief's CTA and legal lines: the CTA verb stays the platform's
|
|
98
|
+
button (shop now, learn more, 立即购买), the legal line goes into the caption or the end card,
|
|
99
|
+
the hashtags follow content-craft.md (IG ≤ 5, TikTok 3–5, LinkedIn 3, 小红书 keyword + 话题).
|
|
100
|
+
- **Disclosure toggles**: YouTube "altered or synthetic" for realistic people / voices; Meta
|
|
101
|
+
"AI info" (automatic for Meta-tool ads, mandatory disclosure for political / social ads);
|
|
102
|
+
TikTok "contains AI-generated content"; China 「AI生成」 on every platform (since 2025-09-01).
|
|
103
|
+
Record the toggle state per placement in the spec sheet.
|
|
104
|
+
- Sound-off first: the ad reads with captions alone; the audio track is a bonus.
|
|
105
|
+
|
|
106
|
+
## 4. Deliverables checklist
|
|
107
|
+
|
|
108
|
+
One row per file. Empty cells are work not yet done.
|
|
109
|
+
|
|
110
|
+
| File | Platform · surface | Ratio | px | Duration | Cover | Caption | Hashtags | Posting slot | Disclosure | Status |
|
|
111
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
112
|
+
| teaser-A-9x16.mp4 | 抖音 video | 9:16 | 1080×1920 | 28 s | cover-9x16-A.jpg | ✓ | 4 话题 | Wed 20:00 CST | 「AI生成」 on | generated |
|
|
113
|
+
| teaser-A-9x16.mp4 | Instagram Reels | 9:16 | 1080×1920 | 28 s | cover-9x16-A.jpg | ✓ | 5 | Tue 12:00 local | AI label per policy | scheduled |
|
|
114
|
+
| poster-3x4.jpg | 小红书 图文 cover | 3:4 | 1242×1660 | — | itself | ✓ | keyword + 5 话题 | Thu 12:30 CST | 「AI生成」 on | to crop |
|
|
115
|
+
| thumb-16x9.jpg | YouTube thumbnail | 16:9 | 1280×720 | — | itself | — | — | with the video | — | pending |
|
|
116
|
+
| cover-bili.jpg | B站 封面 | ≈16:10 | 1146×717 | — | itself | — | — | with the video | 「AI生成」 on | pending |
|
|
117
|
+
|
|
118
|
+
Status values: planned · quoted · generated · cropped · overlaid · scheduled · posted. A row is
|
|
119
|
+
complete when every cell is filled and the file's pixel size equals the platform row.
|
package/skill/wji/SKILL.md
CHANGED
|
@@ -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
|
|
@@ -332,6 +332,19 @@ A workspace's InfiniteCanvas app can hold several canvases (`wji canvas list`);
|
|
|
332
332
|
`wji canvas create <name>` adds an empty one — e.g. one canvas per episode or
|
|
333
333
|
per character set.
|
|
334
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
|
+
|
|
335
348
|
**The link.** The user opens a canvas in the browser and presses the link
|
|
336
349
|
button in its top bar. That stores the open workspace, canvas and **the nodes
|
|
337
350
|
they have selected** on their account for 5 minutes (the tab renews it while
|
|
@@ -414,6 +427,55 @@ wji canvas rm <nodeId…> # destructive — see Rules
|
|
|
414
427
|
|
|
415
428
|
See `reference/canvas-recipes.md` for end-to-end examples.
|
|
416
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
|
+
|
|
417
479
|
## Everything else
|
|
418
480
|
|
|
419
481
|
```bash
|
|
@@ -431,7 +493,9 @@ wji org list && wji org use <orgId>
|
|
|
431
493
|
These commands delete, revoke or overwrite, and none can be undone from the
|
|
432
494
|
CLI: `ws rm`, `ws app rm`, `ws member rm`, `fs rm`, `dataset rm`,
|
|
433
495
|
`asset rm`, `history rm`, `preset rm`, `skill rm`, `skill update --dir`,
|
|
434
|
-
`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`.
|
|
435
499
|
They prompt `[y/N]` in a terminal, but when you run them there is no
|
|
436
500
|
terminal, so they refuse unless you pass `--yes`. **The user's reply in the
|
|
437
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
|
|
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,14 +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:{}}`) |
|
|
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 |
|
|
165
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) |
|
|
166
173
|
| `canvas tags [-c] [-a]` | the canvas's color tags: color, label, node count (`-a` also unused, unlabelled colors) |
|
|
167
174
|
| `canvas get <nodeId> [-c]` | same load; one node with references (`@image1 ← id`) and frame slots |
|
|
168
175
|
| `canvas edges [-c] [--node]` | the `canvas/edge` children |
|
|
169
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` |
|
|
170
|
-
| `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` |
|
|
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`) |
|
|
171
178
|
| `canvas connect <from> <to> [-c]` | `POST /fsnode/create` (`canvas/edge`); a first/last-frame video model gets `videoFrameSlot` start, then end |
|
|
172
179
|
| `canvas disconnect <from> <to> [-c]` | `DELETE /fsnode/list/:edgeId` |
|
|
173
180
|
| `canvas rm <nodeIds...> [-c] [-y]` | deletes the nodes' edges, then the nodes; refuses nodes still generating |
|
|
@@ -209,6 +216,71 @@ viewport, url}` on toggle (`takeover: true`), on selection change and every
|
|
|
209
216
|
60s. A non-takeover `PUT` from a tab that no longer owns the link gets
|
|
210
217
|
`409 canvas-link-gone`, which switches that tab's button off.
|
|
211
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
|
+
|
|
212
284
|
## gen
|
|
213
285
|
|
|
214
286
|
Shared flags: `-m/--model` (required), `-p/--prompt`, `-n/--n`, `-w/--workspace`,
|
|
@@ -357,17 +429,6 @@ Crew packs are local or remote folders (`crew.json`, `agents/<seat>.md`, `skills
|
|
|
357
429
|
| `crew uninstall <name> [--for targets] [-y]` | removes exactly the recorded paths; reminds you to clean `openclaw.json` by hand |
|
|
358
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 |
|
|
359
431
|
|
|
360
|
-
## crew
|
|
361
|
-
|
|
362
|
-
Crew packs are local/remote folders (`crew.json`, `agents/<seat>.md`, `skills/<name>/SKILL.md`, `AGENTS.md`) — no WJI API involved.
|
|
363
|
-
|
|
364
|
-
| Command | Behaviour |
|
|
365
|
-
| --- | --- |
|
|
366
|
-
| `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` |
|
|
367
|
-
| `crew status [name]` | reads `~/.wji/crews/installed.json`; per path ✓ / ↗ symlink / ✗ missing |
|
|
368
|
-
| `crew uninstall <name> [--for targets] [-y]` | removes exactly the recorded paths; reminds you to clean `openclaw.json` by hand |
|
|
369
|
-
| `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 |
|
|
370
|
-
|
|
371
432
|
Import rules: the zip must contain `SKILL.md` at its root (or one folder deep — a
|
|
372
433
|
single common root folder is stripped). `SKILL.md` frontmatter `name:` is
|
|
373
434
|
required; `description:` is read too. Visibility is `private` (default) or
|