@wji.ai/cli 0.6.1 → 0.7.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 +23 -3
- package/dist/index.js +7162 -76
- package/package.json +6 -9
- package/skill/backreel/SKILL.md +171 -0
- package/skill/backreel/reference/crowdfunding.md +82 -0
- package/skill/backreel/reference/enquiries.md +80 -0
- package/skill/backreel/reference/episodes.md +104 -0
- package/skill/backreel/reference/images.md +76 -0
- package/skill/npc-character/SKILL.md +111 -0
- package/skill/npc-character/examples/nagi.character.md +117 -0
- package/skill/npc-character/examples/nagi.stage.md +34 -0
- package/skill/npc-character/examples/yuzu.character.md +110 -0
- package/skill/npc-character/examples/yuzu.stage.md +30 -0
- package/skill/npc-character/reference/character-craft.md +153 -0
- package/skill/npc-character/reference/document-format.md +151 -0
- package/skill/npc-character/reference/interview.md +44 -0
- package/skill/npc-character/reference/life.md +92 -0
- package/skill/npc-character/reference/runtime.md +95 -0
- package/skill/npc-character/reference/stage-craft.md +109 -0
- package/skill/npc-character/reference/visual-kit.md +81 -0
- package/skill/npc-character/reference/voice.md +101 -0
- package/skill/wji/SKILL.md +1 -1
- package/skill/wji/reference/commands.md +25 -5
- package/dist/commands/agent.js +0 -294
- package/dist/commands/asset.js +0 -241
- package/dist/commands/auth.js +0 -216
- package/dist/commands/backreel.js +0 -320
- package/dist/commands/canvas.js +0 -1706
- package/dist/commands/class.js +0 -395
- package/dist/commands/config.js +0 -113
- package/dist/commands/crew.js +0 -205
- package/dist/commands/fsnode.js +0 -288
- package/dist/commands/gen.js +0 -352
- package/dist/commands/model.js +0 -136
- package/dist/commands/npc.js +0 -413
- package/dist/commands/org.js +0 -85
- package/dist/commands/preset.js +0 -176
- package/dist/commands/profile.js +0 -129
- package/dist/commands/skill.js +0 -272
- package/dist/commands/workspace.js +0 -470
- package/dist/core/archive.js +0 -95
- package/dist/core/client.js +0 -252
- package/dist/core/config.js +0 -95
- package/dist/core/crew.js +0 -546
- package/dist/core/members.js +0 -54
- package/dist/core/npc.js +0 -108
- package/dist/core/output.js +0 -174
- package/dist/core/prompt.js +0 -118
- package/dist/core/zip.js +0 -87
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Her public life: jobs and albums
|
|
2
|
+
|
|
3
|
+
The profile page shows two things the soul doesn't: a **work history**
|
|
4
|
+
(jobs) and **photo albums**. Neither reaches the reply model — they are what a
|
|
5
|
+
fan scrolls through — so they must tell the *same story* as the parts that do
|
|
6
|
+
(key events, memories, current focus). A fan who reads the timeline and then
|
|
7
|
+
watches her live should feel it's one person.
|
|
8
|
+
|
|
9
|
+
## Jobs — the work timeline
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
wji npc job list <npc>
|
|
13
|
+
wji npc job add <npc> -t release --title "자작 안무 1분 영상" --date 2026-09-28 --about "처음으로 직접 만든 안무 공개"
|
|
14
|
+
wji npc job add <npc> -t concert --title "홍대 버스킹 · 3분 솔로" --date 2026-12-24 --about "직접 만든 안무로 끝까지" --location "서울 홍대"
|
|
15
|
+
wji npc job set <npc> 2 --about "…"
|
|
16
|
+
wji npc job rm <npc> 3 -y
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| type | use for |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `concert` | a show she performs — busking counts |
|
|
22
|
+
| `release` | a song, video, EP, choreography, photo book |
|
|
23
|
+
| `collaboration` | work with another resident or creator |
|
|
24
|
+
| `brand` | a brand partnership |
|
|
25
|
+
| `event` | a stream milestone, a fan meeting, an opening |
|
|
26
|
+
| `ad` | a commercial / campaign |
|
|
27
|
+
| `appearance` | a role or guest spot (a Backreel credit is usually this) |
|
|
28
|
+
|
|
29
|
+
Writing them:
|
|
30
|
+
|
|
31
|
+
- **Title** as it would appear on a poster or release page, in her language.
|
|
32
|
+
- **About** (`--about`, stored as the job's detail): one concrete line — a
|
|
33
|
+
number, a place, what was different ("시청자 다섯 명, 끝까지 춘 첫 무대").
|
|
34
|
+
- **Date** `YYYY-MM-DD`. A future date is an announced event — use it for the
|
|
35
|
+
thing her current focus is building towards.
|
|
36
|
+
- **3–8 jobs** that rise with her tier: a Rookie has small events and one
|
|
37
|
+
release, not three sold-out concerts.
|
|
38
|
+
- **Agree with the key events.** Jobs are display-only; key events (first 8)
|
|
39
|
+
are what the model knows. Every job that matters to who she is should also
|
|
40
|
+
be a key event with the same date, worded as a résumé line.
|
|
41
|
+
- A Backreel work she appears in: `wji br credit add <post> --npc <npc>
|
|
42
|
+
--role "…" --job appearance` credits her **and** adds the job in one go.
|
|
43
|
+
|
|
44
|
+
## Albums — moments, not galleries
|
|
45
|
+
|
|
46
|
+
An album is one stretch of her life told in photos: "새벽 한 시 연습실",
|
|
47
|
+
"편의점의 새벽", "the week before the debut was cancelled". Each has a title
|
|
48
|
+
(≤120), a description (≤500) and photos with captions.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
wji npc album list <npc> # albums; `list <npc> 2` shows album 2's photos
|
|
52
|
+
wji npc album create <npc> --name "새벽 한 시 연습실" \
|
|
53
|
+
-d "망원동 시간제 연습실. 거울 앞에서 8카운트를 세다가, 바닥에 앉아 채팅을 읽는 밤들." \
|
|
54
|
+
--photo <assetId>:"바닥에 앉아 쉬는 중" <assetId>:"브릿지 첫 동작" ./count.jpg:"카운트 다시 세기"
|
|
55
|
+
wji npc album add <npc> 1 <assetId>:"안무 노트"
|
|
56
|
+
wji npc album set <npc> 1 --cover 2 --label 3="카운트 다시 세기 (11번째)"
|
|
57
|
+
wji npc album rm-photo <npc> 1 4 -y
|
|
58
|
+
wji npc album rm <npc> 2 -y
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Photos are an asset id, a local file (uploaded) or a URL, each optionally
|
|
62
|
+
`:caption`. The first photo is the cover unless you pick one.
|
|
63
|
+
|
|
64
|
+
Writing them:
|
|
65
|
+
|
|
66
|
+
- **2–5 albums**, each tied to a place or period that also appears in her
|
|
67
|
+
memories, key events or current focus.
|
|
68
|
+
- **3–9 photos** per album. Mix shot sizes: one wide of the place, one close
|
|
69
|
+
of her face, one detail (hands, an object from her props).
|
|
70
|
+
- **Captions in her voice**, short, like she labelled her own phone roll —
|
|
71
|
+
not descriptions of the image ("브릿지 첫 동작", not "A woman dancing").
|
|
72
|
+
- **Description**: one or two sentences of *when and why*, written by her or
|
|
73
|
+
about her, in her language.
|
|
74
|
+
- Reuse what you made for the visual kit (hero, gallery shots, props,
|
|
75
|
+
scenes) before generating more.
|
|
76
|
+
|
|
77
|
+
### Making album photos
|
|
78
|
+
|
|
79
|
+
Candid phone photos of her, generated with her references so it's always the
|
|
80
|
+
same person — **confirm with the creator first** (about 14 credits each at
|
|
81
|
+
1080p / high):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
wji gen image -m gpt-image-2.5-flare-fal -r 3:4 \
|
|
85
|
+
--ref <headshotAssetId> <characterSheetAssetId> [<outfitAssetId>] [<sceneAssetId>] \
|
|
86
|
+
--set resolution=1080p quality=high --wait --json \
|
|
87
|
+
-p "Candid smartphone photo of the woman from image 1 (same face, same build as image 2), <what she is doing>, <where, time, light>. Slightly imperfect framing, natural light, photorealistic, no text, no logos."
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Look at every photo before adding it: same face, right outfit for the
|
|
91
|
+
moment, nothing that contradicts the story (wrong season, wrong city). Reject
|
|
92
|
+
and regenerate rather than add a different-looking person.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# What the runtime actually reads
|
|
2
|
+
|
|
3
|
+
Today the persona reaches a model in one place: **NPC Live**. There is no
|
|
4
|
+
DM chat, no autonomous posting and no text-to-speech yet. A live turn is: the
|
|
5
|
+
reply model writes `SAY` (spoken line) and `ACT` (one English sentence of
|
|
6
|
+
upper-body action), then a video model performs it on the stage's keyframe.
|
|
7
|
+
Fields outside the lists below show on the profile only.
|
|
8
|
+
|
|
9
|
+
## The reply model's system prompt, in order
|
|
10
|
+
|
|
11
|
+
| # | Section | Fields (limit as read) |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| 1 | opening | name, handle, role — "You are … live-streaming from your phone" |
|
|
14
|
+
| 2 | Who you are | **selfDescription (400 chars; falls back to bio)**, pronouns, primaryDrive, traits, values, fears |
|
|
15
|
+
| 3 | | origin (300) |
|
|
16
|
+
| 4 | | key events — first **8**, each event 80 chars, "date — event" |
|
|
17
|
+
| 5 | | currentFocus (200), short-term goals, long-term goals |
|
|
18
|
+
| 6 | | memories — **top 3 by emotional weight**, "(type) title: content", content **140** chars, no date |
|
|
19
|
+
| 7 | Voice | speechPattern; **"Live speaking tone — match this closely:" (500)**; **"How you talk to humans:" humanProtocol (500)**; axes ≤30 or ≥70 as "Your temperament leans: …" |
|
|
20
|
+
| 8 | | trigger keywords → "react with extra energy" |
|
|
21
|
+
| 9 | | avoid topics → "Never discuss: … deflect in character in one sentence" |
|
|
22
|
+
| 10 | stage | the stage's reply direction (`llmInstruction`, 1000) as "Extra direction from your operator" |
|
|
23
|
+
| 11 | stage | event clips she can trigger (title + trigger) |
|
|
24
|
+
|
|
25
|
+
Then the output rules:
|
|
26
|
+
|
|
27
|
+
- **SAY**: in the first `languages` tag language; **≤40 words** (EN, ID) or
|
|
28
|
+
**≤60 characters** (JP, KR, TW, HK, ZH, TH, VI); 1–2 sentences, speakable in
|
|
29
|
+
under 12 s; no markdown, emoji or quotes; never mentions being an AI.
|
|
30
|
+
- **ACT**: English, one sentence under 30 words, face / hands / upper body,
|
|
31
|
+
locked camera; plus "Your usual behaviour on camera:" **live.action (300)**
|
|
32
|
+
and the stage `instruction` (300).
|
|
33
|
+
- Only the last 4 turns of the conversation are remembered.
|
|
34
|
+
- After 20 s of silence she chatters on her own (up to 5 times in a row),
|
|
35
|
+
about `currentFocus` (120) and her top 3 trigger keywords.
|
|
36
|
+
|
|
37
|
+
Consequences for writing:
|
|
38
|
+
|
|
39
|
+
- Anything over the limits is cut mid-sentence — front-load.
|
|
40
|
+
- The model imitates the **shape** of the speaking tone. Sample lines longer
|
|
41
|
+
than one SAY teach it to ramble, then get truncated.
|
|
42
|
+
- Memories 4+ by weight are invisible live; key events 9+ too.
|
|
43
|
+
|
|
44
|
+
## The video side
|
|
45
|
+
|
|
46
|
+
Every clip and reply video adds a performance-style line built from:
|
|
47
|
+
|
|
48
|
+
- `speechPattern` → body language: formal "composed and poised", casual
|
|
49
|
+
"relaxed", poetic "dreamy and soft", robotic "precise and minimal",
|
|
50
|
+
archaic "graceful and ceremonious"
|
|
51
|
+
- axes ≤30 or ≥70 → "leaning X in demeanor"
|
|
52
|
+
- the first **3 traits**
|
|
53
|
+
- `live.action` (300) as "On-camera habits"
|
|
54
|
+
|
|
55
|
+
So the first three traits should be ones you can *see*, and live.action must
|
|
56
|
+
be visible, repeatable upper-body habits in English.
|
|
57
|
+
|
|
58
|
+
**Keyframe images** (gpt-image-2) read: the visual style line, "Subject:
|
|
59
|
+
name — selfDescription (240)", the first 5 traits, all aesthetic keywords, the
|
|
60
|
+
reference images (headshot → full body → outfits → props → scene), the
|
|
61
|
+
stage's framing preset, scene description, frame tune and instruction.
|
|
62
|
+
|
|
63
|
+
## Voice sample
|
|
64
|
+
|
|
65
|
+
The only voice field is the uploaded voice sample (web). It is used as
|
|
66
|
+
**reference audio** by *reference-mode* video models for clips with
|
|
67
|
+
dialogue. Stages default to a first/last-frame reply model, which does not
|
|
68
|
+
take audio — so the sample shapes greeting / talk clips but usually not live
|
|
69
|
+
replies. There is no voice id or TTS style to write.
|
|
70
|
+
|
|
71
|
+
## Going live — the readiness gate
|
|
72
|
+
|
|
73
|
+
`wji npc readiness <npc>` must show *can prepare: yes* before a stage can be
|
|
74
|
+
made. It requires: selfDescription > 10 chars, humanProtocol > 10 chars, at
|
|
75
|
+
least one trait, a headshot, and at least one scene. (The web wizard does not
|
|
76
|
+
fill humanProtocol — new NPCs fail the gate until you write it.)
|
|
77
|
+
|
|
78
|
+
The "soul integrity" percentage on the profile counts selfDescription,
|
|
79
|
+
primaryDrive, traits, origin, humanProtocol, currentFocus and legacy
|
|
80
|
+
in-soul memories — memories added through the memory table don't count, so a
|
|
81
|
+
complete new NPC can show 86%. That number is cosmetic.
|
|
82
|
+
|
|
83
|
+
## Things that look like they matter but don't (yet)
|
|
84
|
+
|
|
85
|
+
| Field / hint | Reality |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| Memories in the character document | ignored by the document route — use `--sync-memories` or `wji npc memory` |
|
|
88
|
+
| npcProtocol ("how NPCs talk to each other") | stored, never sent to a model |
|
|
89
|
+
| activityLevel / postFrequency ("how often it posts") | nothing posts |
|
|
90
|
+
| creationStory, dataSourceNotes | profile only — move the important scene into a memory |
|
|
91
|
+
| jobs, relationships, albums | profile timeline / graph / photos only — keep them consistent with key events and memories (life.md) |
|
|
92
|
+
| tagline, genre, colour, socials | profile only |
|
|
93
|
+
| bio | only when selfDescription is empty |
|
|
94
|
+
| stage "instruction — not sent to the reply model" (web hint) | it is sent, as part of the ACT rule |
|
|
95
|
+
| unknown keys via `wji npc soul set` | stored silently, used by nothing |
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Writing a live stage
|
|
2
|
+
|
|
3
|
+
A stage is one filming setup the character inhabits for hours: a start
|
|
4
|
+
frame (keyframe A, plus optional B–D poses), idle loops that play while she
|
|
5
|
+
waits, a greeting, and per-turn reply videos. You are the director: some of
|
|
6
|
+
what you write is **performed by an image / video model (English)**, some is
|
|
7
|
+
**spoken by her (her language)**, and some steers **what she says (any
|
|
8
|
+
language)**.
|
|
9
|
+
|
|
10
|
+
| Field | Who reads it | Language |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| greeting | spoken in the greeting clip | hers, ≤300 chars, ~10 s |
|
|
13
|
+
| idle prompts | video model, one loop each | English |
|
|
14
|
+
| keyframes | image model, extra resting poses | English |
|
|
15
|
+
| instruction | keyframe image, every clip, every reply video **and** the reply model's ACT rule | English |
|
|
16
|
+
| frame tune | keyframe image | English |
|
|
17
|
+
| reply direction (`llmInstruction`) | reply model only — what she says | any |
|
|
18
|
+
|
|
19
|
+
## 1. Give the stream an occasion
|
|
20
|
+
|
|
21
|
+
Why is she live **right now, here**? "After closing, the last month of the
|
|
22
|
+
old shop", "rain day, can't go out", "unboxing fan letters after a show".
|
|
23
|
+
The occasion decides the greeting, the mood, the idle loops and what she
|
|
24
|
+
keeps steering back to. Put it in the label. One stage = one occasion; make a
|
|
25
|
+
second stage for a different one.
|
|
26
|
+
|
|
27
|
+
## 2. The greeting is a hook, not a welcome
|
|
28
|
+
|
|
29
|
+
One or two sentences in her speaking tone that acknowledge the moment and
|
|
30
|
+
invite **one specific answer** — about ten seconds spoken, so **≤60
|
|
31
|
+
characters** in Japanese / Chinese / Korean, ≤30 words in English (the 300
|
|
32
|
+
limit is only a hard cap) ("講低你最鍾意食邊樣" — "tell me your
|
|
33
|
+
favourite"). Never "welcome to my stream". It plays as a pre-rendered clip,
|
|
34
|
+
so it must make sense to anyone arriving.
|
|
35
|
+
|
|
36
|
+
## 3. Idle loops are a person waiting
|
|
37
|
+
|
|
38
|
+
They play whenever she is not replying, back to back, so they must loop:
|
|
39
|
+
|
|
40
|
+
- one English line each, face / hands / head / upper body only
|
|
41
|
+
- start and end in the start frame's neutral pose
|
|
42
|
+
- 3–5 loops across different energies: calm, curious, amused, waiting for an
|
|
43
|
+
answer
|
|
44
|
+
- reuse her `live.action` habits so the loops feel like her
|
|
45
|
+
- only objects that are in the scene image — look at the creator's scene
|
|
46
|
+
first (`wji npc readiness` lists scenes; the image is on the web). If no
|
|
47
|
+
scene exists yet, write the scene description with the creator first and
|
|
48
|
+
name only objects it will contain
|
|
49
|
+
|
|
50
|
+
Good: "wipes the counter slowly with a cloth, glances at the phone, small
|
|
51
|
+
smile" · "tunes one string, frowns, then laughs with her eyes shut".
|
|
52
|
+
Bad: "walks to the window" · "picks up a cup that isn't there" · "camera
|
|
53
|
+
slowly zooms in" · "her friend waves from the door".
|
|
54
|
+
|
|
55
|
+
## 4. Instruction: the visual mood in one breath
|
|
56
|
+
|
|
57
|
+
Lighting, time of day, atmosphere, in English, one or two sentences:
|
|
58
|
+
"Small Hong Kong dessert shop after closing, fluorescent tube light with one
|
|
59
|
+
warm lamp at the counter, steam from a pot, quiet late-night mood."
|
|
60
|
+
It is also added to every reply's ACT rule, so it must describe a mood she
|
|
61
|
+
can stay in — not an action. Don't list props that aren't in the scene image.
|
|
62
|
+
|
|
63
|
+
## 5. Reply direction is about speech
|
|
64
|
+
|
|
65
|
+
What tonight is about, what to steer toward and away from, what to plug, how
|
|
66
|
+
to treat trolls and repeat questions, how short to keep answers. Never
|
|
67
|
+
visuals. Write it as notes from a producer in the talkback:
|
|
68
|
+
|
|
69
|
+
> 答得短,好似喺櫃面同熟客傾偈。今晚重點:舊舖最後一個月、新歌寫到一半。
|
|
70
|
+
> 有人問搬舖就講實話但唔好賣慘,一句帶過再問返對方。
|
|
71
|
+
|
|
72
|
+
It stacks on top of the character's own human protocol — don't repeat it,
|
|
73
|
+
add tonight's specifics.
|
|
74
|
+
|
|
75
|
+
## 6. Keyframes and frame tune
|
|
76
|
+
|
|
77
|
+
- Frame preset: `selfie` (arm's length, phone in hand), `phoneStand` (phone
|
|
78
|
+
propped on a table — hands free for props and instruments), `studio`
|
|
79
|
+
(proper camera setup), `custom` (write the frame prompt yourself).
|
|
80
|
+
- Keyframes B–D are the **same shot** with a different resting pose ("leaning
|
|
81
|
+
on the counter, chin on hand"); the reply video picks the closest one, so
|
|
82
|
+
2–3 poses make replies feel less frozen.
|
|
83
|
+
- Frame tune is photographic language only: "warmer light on her face",
|
|
84
|
+
"less clutter on the counter", "slightly lower angle".
|
|
85
|
+
|
|
86
|
+
## 7. Aspect ratio and video model
|
|
87
|
+
|
|
88
|
+
- 9:16 for a phone-stream feel (most NPC City viewers are on phones); 16:9
|
|
89
|
+
for a desk / studio show.
|
|
90
|
+
- Leave out the `Video model` line so the stage keeps the default
|
|
91
|
+
first/last-frame model; the stage document rejects reference models.
|
|
92
|
+
|
|
93
|
+
## 8. Consistency pass
|
|
94
|
+
|
|
95
|
+
- Greeting, instruction, idle loops and reply direction all belong to the
|
|
96
|
+
same occasion and time of day.
|
|
97
|
+
- Every English field is English; the greeting is in her language.
|
|
98
|
+
- Nothing leaves frame, stands up, moves the camera, cuts, adds props or
|
|
99
|
+
people.
|
|
100
|
+
- It still makes sense to a viewer who joins forty minutes in.
|
|
101
|
+
|
|
102
|
+
## What costs credits
|
|
103
|
+
|
|
104
|
+
Writing, importing and applying stage documents is free. Rendering is not:
|
|
105
|
+
the start frame (image), every clip (video), transitions, and — when the
|
|
106
|
+
stage is on air without debug mode — **every live reply**, charged to the
|
|
107
|
+
owner (a session stops at 6000 credits). Render and enable on the web, with
|
|
108
|
+
the creator's go-ahead. Debug mode (`wji npc stage set <id> --debug on`)
|
|
109
|
+
skips reply videos for rehearsal.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# The visual kit — formats per slot
|
|
2
|
+
|
|
3
|
+
The NPC City configure page has one AI-image slot per asset, each with a fixed
|
|
4
|
+
format and layout. Generate to **these** formats (they mirror the web app's
|
|
5
|
+
`build-image-prompts.ts`), or the assets look wrong next to the ones the
|
|
6
|
+
creator makes on the web and the live keyframes get worse references.
|
|
7
|
+
|
|
8
|
+
| Slot (web label) | Soul field | Ratio | Layout |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| Headshot | `assets.headshotAssetId` / `headshot` (+ profile `image`) | **1:1** | shoulders up, face clearly visible, clean background |
|
|
11
|
+
| **Character Sheet** | `assets.fullBodyAssetId` / `fullBody` | **16:9** | left third: large headshot · right two-thirds: **front, back, side** full-body views in one outfit · neutral studio background |
|
|
12
|
+
| Outfit | `assets.outfits[]` | **3:4** | **OOTD flat lay**: top-down, garments only (not worn), whole ensemble with shoes and accessories |
|
|
13
|
+
| Prop | `assets.props[]` | 1:1 | product-style, materials and scale readable |
|
|
14
|
+
| Scene | `assets.scenes[]` | **16:9** | environment / backdrop, no people |
|
|
15
|
+
| Profile / hero | profile `heroImage` | 16:9 (9:16 mobile) | character in her world, explicit camera (shot, lens, angle, depth of field) |
|
|
16
|
+
|
|
17
|
+
Every prompt carries the style line for the NPC's `visualStyle`; for
|
|
18
|
+
photoreal it is: *photorealistic real person (真人), live-action photography —
|
|
19
|
+
not anime, not illustration, not 3D render*.
|
|
20
|
+
|
|
21
|
+
## Order and references
|
|
22
|
+
|
|
23
|
+
1. **Headshot first** — every other image of her references it. If a good
|
|
24
|
+
portrait comes out in the wrong ratio, crop it to 1:1 rather than
|
|
25
|
+
regenerating (same face, no credits): `sips --cropToHeightWidth 1440 1440
|
|
26
|
+
--cropOffset 120 0 in.jpg --out out.jpg`.
|
|
27
|
+
2. **Character Sheet** with the headshot as image 1. The live model treats it
|
|
28
|
+
as "full-body / outfit sheet — match the proportions", so the outfit on the
|
|
29
|
+
sheet is her default look.
|
|
30
|
+
3. **Outfits**: generate (or photograph) the outfit, then make the flat lay
|
|
31
|
+
from it ("Transform the clothing from the reference image into an OOTD
|
|
32
|
+
flat lay…"). A stage can attach up to 6 outfits / props.
|
|
33
|
+
4. **Scenes** at 16:9 even for 9:16 stages — the scene is a reference image,
|
|
34
|
+
the stage's frame preset decides the shot. A stage copies its scene when it
|
|
35
|
+
is created: after replacing a scene image, re-import (or recreate) a draft
|
|
36
|
+
stage so it uses the new one.
|
|
37
|
+
5. Props and hero last.
|
|
38
|
+
|
|
39
|
+
## Prompt templates (photoreal)
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
Character reference sheet, 16:9 horizontal com card / model sheet layout, <style line>.
|
|
43
|
+
Left third: one large portrait headshot — face, hair, and expression clearly visible, matching the reference headshot (image 1).
|
|
44
|
+
Right two-thirds: three full-body standing views in a row — front view, back view, side profile — same outfit, proportions, and appearance across all panels.
|
|
45
|
+
Clean neutral studio background, evenly spaced figures, professional reference sheet layout.
|
|
46
|
+
Character: <who, age-feel, build, hair>. Signature look: <outfit, shoes, accessories>. No text labels, no logos.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
Transform the clothing from the reference image (image 1) into an OOTD flat lay outfit photo, <style line>.
|
|
51
|
+
Top-down bird's-eye view of the complete ensemble neatly arranged on a clean neutral surface — shirt/top, pants/skirt, outerwear, shoes, and accessories laid out as a coordinated outfit-of-the-day flat lay.
|
|
52
|
+
Garments only, not worn by a model; clear fabric texture, silhouette, and styling visible from above.
|
|
53
|
+
The outfit: <pieces>. No text, no logos.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
Environment / scene background, <style line>. Atmospheric composition suitable as a character backdrop. <place, time, light, 3–5 concrete objects that the stage will mention>. No people, no readable signs, no logos.
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
wji gen image -m gpt-image-2.5-flare-fal -r 16:9 --ref <headshotAssetId> \
|
|
62
|
+
--set resolution=1080p quality=high --wait --json -p @sheet-prompt.txt
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
About 14 credits per image at 1080p / high — **confirm with the creator
|
|
66
|
+
before generating**, and look at every result before attaching it (faces
|
|
67
|
+
drift; reject and regenerate rather than attach a different face).
|
|
68
|
+
|
|
69
|
+
## Attaching
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
wji npc soul get <npc> > soul.json # edit assets, keep album/albums out
|
|
73
|
+
wji npc soul set <npc> @assets.json # {"assets": {...}} — replaces the whole assets object
|
|
74
|
+
wji npc set <npc> --image <headshotAssetId> --hero-image <heroAssetId> --set 'gallery=["url1","url2"]'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`assets` is replaced as a whole by `soul set`, so always start from the
|
|
78
|
+
current one. Outfit / prop / scene entries are
|
|
79
|
+
`{id, assetId, name, url, description, referenceRole: outfit|prop|location}`;
|
|
80
|
+
names and descriptions are shown to the creator and scene descriptions go into
|
|
81
|
+
keyframe prompts.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Voice: speaking tone and on-camera action
|
|
2
|
+
|
|
3
|
+
## Speaking tone (`live.speakingTone`, 500 chars, her language)
|
|
4
|
+
|
|
5
|
+
The reply model is told to "match this closely", and it copies what you
|
|
6
|
+
**show** far better than what you describe. A good speaking tone has:
|
|
7
|
+
|
|
8
|
+
1. **Register in one line** — "Cantonese with English slips, short sentences,
|
|
9
|
+
like talking across a shop counter."
|
|
10
|
+
2. **One catchphrase** she really uses, in quotes.
|
|
11
|
+
3. **One thing she never says**, and why ("never says 加油 — too lazy").
|
|
12
|
+
4. **How she takes a compliment / a tease** — with the line she'd say.
|
|
13
|
+
5. **Two or three sample lines**, each short enough to be one live reply:
|
|
14
|
+
≤40 words in English / Indonesian, **≤60 characters** in Japanese, Korean,
|
|
15
|
+
Chinese, Cantonese, Thai, Vietnamese.
|
|
16
|
+
|
|
17
|
+
Before: "She is witty and warm, speaks casually and likes to tease people."
|
|
18
|
+
After: "Cantonese with a few English words, short lines, like at the shop
|
|
19
|
+
counter. Catchphrase「糖水都要趁熱食啦」. Takes compliments by deflecting:
|
|
20
|
+
「得啦得啦,你想我送多碗咩」. Never says「加油」. e.g.「你今晚食咗咩?唔好話我知係杯麵。」"
|
|
21
|
+
|
|
22
|
+
`speechPattern` (formal / casual / poetic / robotic / archaic) is the coarse
|
|
23
|
+
register **and** her body language on video (formal "composed and poised",
|
|
24
|
+
casual "relaxed", poetic "dreamy and soft", robotic "precise and minimal",
|
|
25
|
+
archaic "graceful and ceremonious"). When the two pull apart — a polite
|
|
26
|
+
speaker who slouches — choose for the body, and let the speaking tone carry
|
|
27
|
+
the politeness.
|
|
28
|
+
|
|
29
|
+
## Code-switching
|
|
30
|
+
|
|
31
|
+
If she mixes languages, write the rule *and* show it:
|
|
32
|
+
|
|
33
|
+
- **Hong Kong**: Cantonese grammar and particles (啦、喎、咩、囉) with English
|
|
34
|
+
nouns and fillers ("OK", "chill", "send 我"). Write in written Cantonese
|
|
35
|
+
(嘅、咗、唔、佢), not standard written Chinese, or she will sound like a
|
|
36
|
+
newsreader. Set `languages=HK`.
|
|
37
|
+
- **Taiwan**: Mandarin with Taiwanese particles (欸、啦、齁) and occasional
|
|
38
|
+
Taiwanese Hokkien / Japanese loanwords; traditional characters. `languages=TW`.
|
|
39
|
+
- **Japanese**: decide politeness (です/ます vs plain), first-person pronoun
|
|
40
|
+
(私、あたし、僕、俺, or her own name), and any sentence-final habit.
|
|
41
|
+
`languages=JP`.
|
|
42
|
+
- **English**: dialect and rhythm matter more than slang — "British, dry,
|
|
43
|
+
understated" vs "SoCal, fast, lots of 'like'". Avoid dated slang lists.
|
|
44
|
+
|
|
45
|
+
The first `languages` entry is what she speaks live; add a second only if
|
|
46
|
+
she really answers in it.
|
|
47
|
+
|
|
48
|
+
## Lines that work live
|
|
49
|
+
|
|
50
|
+
A live reply is one or two sentences, spoken in under 12 seconds, reacting
|
|
51
|
+
to one viewer. So sample lines should:
|
|
52
|
+
|
|
53
|
+
- react to the person (use what they said; address them the way she
|
|
54
|
+
would — "you", a nickname habit, "ラジオネーム…さん"), then
|
|
55
|
+
- add one beat of her (an opinion, a callback, a question back).
|
|
56
|
+
|
|
57
|
+
Bad: long monologues, lists, lines that need context the viewer doesn't
|
|
58
|
+
have, emoji and stage directions inside the line. Don't invent viewer names
|
|
59
|
+
inside sample lines — the model will greet strangers by them; the runtime
|
|
60
|
+
supplies the real viewer's name when it wants her to use it.
|
|
61
|
+
|
|
62
|
+
## Human protocol vs speaking tone
|
|
63
|
+
|
|
64
|
+
- Speaking tone = **how** she sounds.
|
|
65
|
+
- Human protocol = **what she does** with people: regulars vs newcomers,
|
|
66
|
+
compliments, trolls, sadness, silence, personal questions, a boundary.
|
|
67
|
+
|
|
68
|
+
Don't repeat one in the other.
|
|
69
|
+
|
|
70
|
+
## On-camera action (`live.action`, 300 chars, ENGLISH)
|
|
71
|
+
|
|
72
|
+
2–3 physical habits tied to emotion, performed by a video model on a locked
|
|
73
|
+
camera:
|
|
74
|
+
|
|
75
|
+
- visible, upper body only: hands, face, head, shoulders, objects already in
|
|
76
|
+
frame
|
|
77
|
+
- repeatable — they will recur in many clips
|
|
78
|
+
- tied to a feeling: "laughs with her eyes shut", "rests her chin on the
|
|
79
|
+
guitar when listening", "taps the table twice when excited"
|
|
80
|
+
|
|
81
|
+
Never: walking, standing up, turning away, leaving frame, camera moves, new
|
|
82
|
+
props, other people. Never write it in the character's language — it is a
|
|
83
|
+
video prompt.
|
|
84
|
+
|
|
85
|
+
## Making the voice sample
|
|
86
|
+
|
|
87
|
+
The voice sample is uploaded or set on the soul (`voiceSample`). The cheap way
|
|
88
|
+
to make one is **Seed Audio 1.0** with the headshot as the voice reference —
|
|
89
|
+
about 45 credits a minute, so a 3-second sample costs a few credits (MiniMax
|
|
90
|
+
speech is a flat ~300 per line). Write a bracketed direction in her language,
|
|
91
|
+
then one new 8–12-character line; the bracket is followed, not spoken.
|
|
92
|
+
**Confirm with the creator before generating.**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
wji gen audio -m "Seed Audio 1.0" --ref <headshotAssetId> -n 2 --wait --json \
|
|
96
|
+
-p "(27세 여성; 전직 연습생; 맑지만 살짝 허스키한 음색; 중간 음높이; 약간 빠르고 숨이 찬 말투; 표준 서울말; 지쳤지만 단단한 감정; 건조한 목소리, 배경음악과 효과음 없음)오늘은 한 번만 더 해 볼게요"
|
|
97
|
+
wji npc soul set <npc> '{"voiceSample":{"assetId":"<audioAssetId>","url":"<url>","label":"…","durationSec":3.2}}'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Listen to both takes and keep the one that sounds like her speaking tone.
|
|
101
|
+
|
package/skill/wji/SKILL.md
CHANGED
|
@@ -473,7 +473,7 @@ wji npc set @mika --tagline "…" --tag tier=Rising --social ig=https://…
|
|
|
473
473
|
wji npc publish @mika # visibility public (unpublish = private)
|
|
474
474
|
wji npc job add @mika -t concert --title "Neon Night" --date 2026-10-01 --location Taipei
|
|
475
475
|
wji npc job set @mika 2 --detail "sold out" ; wji npc job rm @mika 3 -y
|
|
476
|
-
wji npc soul get @mika > soul.json ; wji npc soul set @mika '{"
|
|
476
|
+
wji npc soul get @mika > soul.json ; wji npc soul set @mika '{"liveSpeakingTone":"…"}' # keys must be soul keys — unknown ones are stored silently
|
|
477
477
|
wji npc doc export @mika -o mika.md # whole character in one file
|
|
478
478
|
wji npc doc apply @mika @mika.md --dry-run # see the diff, then without --dry-run
|
|
479
479
|
```
|
|
@@ -256,17 +256,29 @@ unknown `@handle` falls back to `GET /npc-city/residents/by-handle/:h` (public).
|
|
|
256
256
|
| `npc rm <npc> [-y]` | `DELETE /npc-city/residents/:id` (soft delete) |
|
|
257
257
|
| `npc handle <handle> [--exclude <npc>]` | `GET /npc-city/residents/handle-available` |
|
|
258
258
|
| `npc job list <npc>` | the resident's `jobs[]`, numbered 1…n |
|
|
259
|
-
| `npc job add <npc> -t <type> --title --date YYYY-MM-DD [--
|
|
259
|
+
| `npc job add <npc> -t <type> --title --date YYYY-MM-DD [--about] [--location] [--url]` | read, append, `PATCH {jobs}` |
|
|
260
260
|
| `npc job set <npc> <n> [same flags]` | read, merge into #n, `PATCH {jobs}` |
|
|
261
261
|
| `npc job rm <npc> <n...> [-y]` | read, drop, `PATCH {jobs}`; refuses if the list changed meanwhile |
|
|
262
262
|
| `npc soul get <npc>` | `GET /npc-city/residents/:id/soul` |
|
|
263
263
|
| `npc soul set <npc> <json\|@file\|->` | `PATCH /npc-city/residents/:id/soul {soulConfig}` (shallow merge) |
|
|
264
264
|
| `npc doc export <npc> [--format md\|json] [-o file]` | `GET /npc-city/residents/:id/document?format=` |
|
|
265
|
-
| `npc doc apply <npc> <@file\|-> [--dry-run] [-y]` | `POST …/document/preview`, confirm, `POST …/document` |
|
|
265
|
+
| `npc doc apply <npc> <@file\|-> [--dry-run] [--sync-memories] [-y]` | `POST …/document/preview`, confirm, `POST …/document`. The document route reports memory changes but never saves them; `--sync-memories` replaces the memory table with the document's `## Memories` |
|
|
266
|
+
| `npc memory list\|add\|set\|rm <npc>` | `GET/POST /npc-city/residents/:id/memories`, `PATCH/DELETE /npc-city/memories/:id` (title ≤200, date ≤40, weight 1–5, type formative\|trauma\|joy\|connection\|revelation). `list` marks the top 3 by weight (then newest) — the only ones the live reply prompt reads, content clipped to 140 |
|
|
267
|
+
| `npc album list <npc> [album]` · `create <npc> --name [-d] [--photo …]` · `add <npc> <album> <photo…>` · `set <npc> <album> [--name] [-d] [--cover n] [--label n=text…]` · `rm-photo <npc> <album> <n…>` · `rm <npc> <album>` | `GET/POST /npc-city/residents/:id/albums`, `PATCH/DELETE …/albums/:albumId` (label ≤120, description ≤500; photos `{id, assetId, url, label}`, the CLI mints ids and resolves asset ids / uploads files to URLs; `<album>` = number or id) |
|
|
268
|
+
| `npc readiness <npc>` | `GET /npc-city/live/residents/:id/readiness` — soul integrity, gate reasons (selfDescription / humanProtocol >10 chars, a trait, headshot, a scene), scenes, voice sample, stages |
|
|
269
|
+
| `npc stage list <npc>` | `GET /npc-city/live/residents/:id/stages` |
|
|
270
|
+
| `npc stage export <stageId> [--format md\|json] [-o]` | `GET /npc-city/live/stages/:id/document` |
|
|
271
|
+
| `npc stage apply <stageId> <@file\|-> [--dry-run] [-y]` | `POST …/document/preview`, confirm, `POST …/document` (text only; baked fields warn) |
|
|
272
|
+
| `npc stage import <npc> <@file\|-> [-y]` | `POST /npc-city/live/residents/:id/stages/import` — a Draft stage, nothing rendered, no credits |
|
|
273
|
+
| `npc stage set <stageId> [--debug on\|off] [--on on\|off] [--label]` | `PATCH /npc-city/live/stages/:id {debugMode, enabled, label}` — `--on` needs a Ready stage; debug on an on-air stage asks first |
|
|
274
|
+
| `npc stage rm <stageId> [-y]` | `DELETE /npc-city/live/stages/:id` |
|
|
266
275
|
| `npc transfer send <npc> --to <email\|userId> [-y]` | `POST /npc-city/residents/:id/transfer` |
|
|
267
276
|
| `npc transfer list` | `GET /npc-city/transfers/incoming` + `/outgoing` |
|
|
268
277
|
| `npc transfer accept\|reject\|cancel <transferId>` | `POST /npc-city/transfers/:id/<action>` |
|
|
269
278
|
|
|
279
|
+
Writing the character and stages well — what each field does at runtime —
|
|
280
|
+
is the `npc-character` skill.
|
|
281
|
+
|
|
270
282
|
Job types: concert, release, collaboration, brand, event, ad, appearance. The
|
|
271
283
|
server drops jobs without a title or date and turns unknown types into `event`;
|
|
272
284
|
the CLI rejects both up front.
|
|
@@ -277,11 +289,17 @@ the CLI rejects both up front.
|
|
|
277
289
|
| --- | --- |
|
|
278
290
|
| `backreel list [--all] [-k] [--page] [--limit]` | `GET /backreel/post/search/my` (`--all`: `GET /backreel/post/search`) |
|
|
279
291
|
| `backreel get <postId>` | `GET /backreel/post/list/:id` |
|
|
280
|
-
| `backreel create <title> [--kind showcase\|crowdfunding] [--logline] [--category] [--project-status] [--tags a,b] [-d text\|@file] [--cover assetId] [--cover-portrait] [--trailer] [--body json]` | `POST /backreel/post/create` (always a draft) |
|
|
292
|
+
| `backreel create <title> [--kind showcase\|crowdfunding] [--logline] [--media assetId...] [--category] [--project-status] [--tags a,b] [-d text\|@file] [--cover assetId] [--cover-portrait] [--trailer] [--body json]` | `POST /backreel/post/create` (always a draft) |
|
|
281
293
|
| `backreel set <postId> [--title] [same flags]` | `PUT /backreel/post/list/:id` with only the given fields |
|
|
282
294
|
| `backreel publish\|unpublish <postId>` | `POST /backreel/post/list/:id/publish\|unpublish` (publish needs a cover, media or an episode) |
|
|
283
295
|
| `backreel rm <postId> [-y]` | `DELETE /backreel/post/list/:id` |
|
|
284
296
|
| `backreel creators [-k] [--role kind] [--verified] [--no-featured] [--page] [--limit ≤48]` | `GET /backreel/creator/list` — editor-picked `featured` plus a heat-ranked page (recent likes, favorites ×2); creators with no recent activity are not listed. `--role`: investor, production_company, brand, distributor, sales_agent |
|
|
297
|
+
| `backreel image <file...> --slot cover\|portrait\|media\|photo [--post id] [--check]` | reads the size locally, warns on ratio / resolution, `POST /assets/v2/upload` with width/height, then `PUT {cover\|coverPortrait\|assets}` (media appends, ≤9) |
|
|
298
|
+
| `backreel image-specs` | recommended size per slot (16:9 1920×1080, 9:16 1080×1920, 3:4 1536×2048, 1:1 800×800) |
|
|
299
|
+
| `backreel check <postId>` | publish blockers (title, description text/img, cover\|media\|episode, crowdfunding goal ≥1) and advice (portrait, logline, credits, VOD not ready) |
|
|
300
|
+
| `backreel episode list\|add\|set\|rm\|move\|tiers\|status\|play` | whole `episodes[]` read-modify-`PUT` (≤30, showcase only). A file: `POST /backreel/vod/upload-token` → signed ApplyUploadInfo / PUT / CommitUploadInfo → `POST /backreel/vod/claim` → `vodVid`; `--legacy` / `--asset` = WJI asset. `status` / `play` use `GET …/episodes/:order/play` (409 = transcoding) |
|
|
301
|
+
| `backreel enquiry list\|unread\|get\|reply\|status\|grant\|download` | `/backreel/enquiry/search?box=`, `unread-count`, `list/:id` + `list/:id/messages`, `POST list/:id/messages {body, attachments}` (files via `POST /backreel/file/upload?purpose=enquiry`), `POST list/:id/status`, `POST list/:id/grant`, `GET /backreel/file/:id/download` |
|
|
302
|
+
| `backreel fund show\|set\|tier\|supporter\|donations` | read-modify-`PUT` of `campaign` (crowdfunding only) and `donation` (any post); `donations` = `GET /backreel/donation/post/:id/manage` |
|
|
285
303
|
| `backreel credit list <postId>` | the post's `credits[]`, numbered |
|
|
286
304
|
| `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) |
|
|
287
305
|
| `backreel credit rm <postId> <n...> [-y]` | read, drop, `PUT {credits}` |
|
|
@@ -290,7 +308,9 @@ Project status: planning, pre_production, in_production, post_production,
|
|
|
290
308
|
completed. `--logline` is a one-line hook (≤200 chars, HTML stripped, `""`
|
|
291
309
|
clears it) shown large on the post page and **always public**, even when the
|
|
292
310
|
description is gated. The post detail no longer carries playable URLs for VOD
|
|
293
|
-
episodes — playback is gated per episode on the web.
|
|
311
|
+
episodes — playback is gated per episode on the web. Every `backreel` call
|
|
312
|
+
sends `X-App: backreel` so notifications and mail link to Backreel. The
|
|
313
|
+
`backreel` skill covers the creator workflow in depth. `-d` plain text becomes `<p>` paragraphs; HTML passes through and
|
|
294
314
|
is sanitized server-side. Admin routes (`/api/backreel/*`) reject `sk_` keys.
|
|
295
315
|
|
|
296
316
|
## gen
|
|
@@ -304,7 +324,7 @@ Shared flags: `-m/--model` (required), `-p/--prompt`, `-n/--n`, `-w/--workspace`
|
|
|
304
324
|
| --- | --- | --- |
|
|
305
325
|
| `gen image` | `--ref`, `--omni`, `--style-ref`, `--reference-weight`, `--stylize`, `--model-options` | `POST /ai/assets/history/image/generations` |
|
|
306
326
|
| `gen video` | `-d/--duration`, `--start-image`, `--end-image`, `--ref`, `--ref-video`, `--ref-audio` | `POST /ai/assets/history/video/generations` |
|
|
307
|
-
| `gen audio` | `-d/--duration`, `--ref-audio` | `POST /ai/assets/history/audio/generations` |
|
|
327
|
+
| `gen audio` | `-d/--duration`, `--ref-audio`, `--ref` (one image, e.g. a headshot → a Seed Audio voice sample; not with `--ref-audio`) | `POST /ai/assets/history/audio/generations` |
|
|
308
328
|
| `gen cost -t <type> -m <model>` | `-n`, `-p`, `-d`, `-r`, `--refs`, `--values` | `POST /ai/assets/preComputeCost` |
|
|
309
329
|
|
|
310
330
|
Every generation goes through the history endpoint — there is no raw mode. The
|