ui-chan-mcp 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +6 -0
- package/.env.example +13 -0
- package/LICENSE +36 -0
- package/README.md +214 -0
- package/agents/mode.md +47 -0
- package/agents/talk.md +23 -0
- package/bin/ui-chan-mcp.mjs +11 -0
- package/bin/ui-chan-node +35 -0
- package/bin/ui-chan.mjs +350 -0
- package/context/AFFINITY.md +94 -0
- package/context/SOUL.md +79 -0
- package/context/VOCABULARY.md +108 -0
- package/cue.schema.json +109 -0
- package/cues/default.json +21 -0
- package/cues/emo_anger.json +14 -0
- package/cues/emo_anger_hi.json +19 -0
- package/cues/emo_anger_lo.json +16 -0
- package/cues/emo_antic.json +18 -0
- package/cues/emo_antic_hi.json +16 -0
- package/cues/emo_antic_lo.json +6 -0
- package/cues/emo_disgust.json +19 -0
- package/cues/emo_disgust_hi.json +20 -0
- package/cues/emo_disgust_lo.json +6 -0
- package/cues/emo_fear.json +19 -0
- package/cues/emo_fear_hi.json +19 -0
- package/cues/emo_fear_lo.json +19 -0
- package/cues/emo_joy.json +17 -0
- package/cues/emo_joy_hi.json +15 -0
- package/cues/emo_joy_lo.json +13 -0
- package/cues/emo_sad.json +15 -0
- package/cues/emo_sad_hi.json +18 -0
- package/cues/emo_sad_lo.json +14 -0
- package/cues/emo_surprise.json +15 -0
- package/cues/emo_surprise_hi.json +14 -0
- package/cues/emo_surprise_lo.json +11 -0
- package/cues/emo_trust.json +16 -0
- package/cues/emo_trust_hi.json +20 -0
- package/cues/emo_trust_lo.json +6 -0
- package/cues/idling_doze_1.json +9 -0
- package/cues/idling_doze_2.json +9 -0
- package/cues/idling_doze_3.json +9 -0
- package/cues/idling_giggle_1.json +9 -0
- package/cues/idling_giggle_2.json +9 -0
- package/cues/idling_lookaround_1.json +10 -0
- package/cues/idling_lookaround_2.json +10 -0
- package/cues/idling_lookaround_3.json +10 -0
- package/cues/idling_ponder.json +9 -0
- package/cues/idling_sigh_1.json +11 -0
- package/cues/idling_sigh_2.json +11 -0
- package/cues/idling_yawn_1.json +9 -0
- package/cues/idling_yawn_2.json +12 -0
- package/cues/idling_yawn_3.json +12 -0
- package/cues/mix_anger_antic.json +16 -0
- package/cues/mix_antic_joy.json +16 -0
- package/cues/mix_disgust_anger.json +17 -0
- package/cues/mix_fear_surprise.json +18 -0
- package/cues/mix_joy_trust.json +19 -0
- package/cues/mix_joy_trust_hi.json +18 -0
- package/cues/mix_sad_disgust.json +12 -0
- package/cues/mix_surprise_sad.json +21 -0
- package/cues/mix_surprise_sad_hi.json +14 -0
- package/cues/mix_trust_fear.json +18 -0
- package/cues/pose_arms_crossed.json +9 -0
- package/cues/pose_banzai.json +17 -0
- package/cues/pose_smug_arms.json +11 -0
- package/cues/pose_smug_hips.json +11 -0
- package/cues/pose_think.json +8 -0
- package/cues/pose_umbrella.json +9 -0
- package/cues/self_guilt.json +20 -0
- package/cues/self_pride.json +10 -0
- package/cues/self_shame.json +19 -0
- package/cues/self_shy.json +16 -0
- package/cues/self_shy_hi.json +17 -0
- package/cues/sys_address.json +9 -0
- package/cues/sys_awkward.json +18 -0
- package/cues/sys_beam.json +18 -0
- package/cues/sys_blank.json +11 -0
- package/cues/sys_care.json +18 -0
- package/cues/sys_confused.json +12 -0
- package/cues/sys_dazed.json +10 -0
- package/cues/sys_dread.json +21 -0
- package/cues/sys_fluster.json +20 -0
- package/cues/sys_intro.json +15 -0
- package/cues/sys_laugh.json +20 -0
- package/cues/sys_neutral.json +5 -0
- package/cues/sys_present.json +9 -0
- package/cues/sys_rain.json +15 -0
- package/cues/sys_refuse.json +18 -0
- package/cues/sys_relief.json +15 -0
- package/cues/sys_sleepy.json +8 -0
- package/cues/sys_smirk.json +10 -0
- package/cues/sys_stream.json +15 -0
- package/cues/sys_success.json +15 -0
- package/cues/sys_think.json +9 -0
- package/dist/app/assets.js +54 -0
- package/dist/app/cues.js +130 -0
- package/dist/app/editor-main.js +199 -0
- package/dist/app/editor-preload.js +17 -0
- package/dist/app/main.js +527 -0
- package/dist/app/persona.js +136 -0
- package/dist/app/preload.js +14 -0
- package/dist/app/state.js +850 -0
- package/dist/app/tts.js +257 -0
- package/dist/mcp-server.js +408 -0
- package/dist/renderer/bundle.js +17983 -0
- package/dist/renderer/editor.html +150 -0
- package/dist/renderer/editor.js +17917 -0
- package/dist/renderer/index.html +425 -0
- package/dist/shared/paths.js +169 -0
- package/dist/shared/set-cue-schema.js +66 -0
- package/dist/shared/types.js +14 -0
- package/docs/CLIENTS.md +110 -0
- package/docs/CUE_AUTHORING.md +76 -0
- package/docs/DEVELOPMENT.md +129 -0
- package/docs/PERSONA.md +61 -0
- package/docs/PSD_LAYERS.md +123 -0
- package/docs/README.md +43 -0
- package/docs/SETUP.html +684 -0
- package/docs/STYLE.md +107 -0
- package/docs/TOOLS.md +33 -0
- package/docs/TROUBLESHOOTING.md +122 -0
- package/docs/TTS.md +61 -0
- package/docs/design/CUE_CATALOG.md +259 -0
- package/docs/images/faces.png +0 -0
- package/docs/images/panel.png +0 -0
- package/hooks/fire-event.js +14 -0
- package/hooks/hooks.json +77 -0
- package/hooks/lib/mascot.js +76 -0
- package/hooks/notify.js +15 -0
- package/hooks/reaction.js +46 -0
- package/hooks/session-start.js +80 -0
- package/package.json +96 -0
- package/persona/ui-chan.md +111 -0
- package/plugins/hermes/ui-chan/__init__.py +98 -0
- package/plugins/hermes/ui-chan/plugin.yaml +7 -0
- package/plugins/opencode/ui-chan.mjs +105 -0
- package/skills/beam/SKILL.md +37 -0
- package/skills/eli14/SKILL.md +139 -0
- package/skills/eli14/references/base.html +495 -0
- package/skills/mode/SKILL.md +67 -0
- package/skills/talk/SKILL.md +27 -0
- package/tools/setup/check-package.mjs +47 -0
- package/tools/setup/clients.mjs +399 -0
- package/tools/setup/doctor.mjs +105 -0
- package/tools/setup/home.mjs +126 -0
- package/tools/setup/prompt.mjs +134 -0
- package/tools/setup/update-check.mjs +8 -0
- package/tools/setup/update.mjs +350 -0
- package/tools/stop-app.mjs +22 -0
- package/ui-chan.config.json +973 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
<title>ういの取扱説明</title>
|
|
2
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
3
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
4
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@500;700&family=Zen+Kaku+Gothic+New:wght@400;500&family=Roboto+Mono:wght@400;500&display=swap">
|
|
5
|
+
|
|
6
|
+
<style>
|
|
7
|
+
:root {
|
|
8
|
+
--ground: #eaeef5;
|
|
9
|
+
--card: #f7f9fc;
|
|
10
|
+
--ink: #1b2230;
|
|
11
|
+
--ink-soft: #55607a;
|
|
12
|
+
--ink-faint: #8b94a8;
|
|
13
|
+
--rule: #ccd4e4;
|
|
14
|
+
--accent: #4f5fc9;
|
|
15
|
+
--accent-soft: #dfe3fa;
|
|
16
|
+
--warm: #d1804f;
|
|
17
|
+
--warm-soft: #f6e4d8;
|
|
18
|
+
--drop: #a8b6dd;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@media (prefers-color-scheme: dark) {
|
|
22
|
+
:root:not([data-theme="light"]) {
|
|
23
|
+
--ground: #12151d;
|
|
24
|
+
--card: #1a1f2b;
|
|
25
|
+
--ink: #e6eaf3;
|
|
26
|
+
--ink-soft: #a3adc2;
|
|
27
|
+
--ink-faint: #6d7791;
|
|
28
|
+
--rule: #2c3444;
|
|
29
|
+
--accent: #97a4f2;
|
|
30
|
+
--accent-soft: #262e4d;
|
|
31
|
+
--warm: #e4a071;
|
|
32
|
+
--warm-soft: #3a2a1e;
|
|
33
|
+
--drop: #3d4966;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:root[data-theme="dark"] {
|
|
38
|
+
--ground: #12151d;
|
|
39
|
+
--card: #1a1f2b;
|
|
40
|
+
--ink: #e6eaf3;
|
|
41
|
+
--ink-soft: #a3adc2;
|
|
42
|
+
--ink-faint: #6d7791;
|
|
43
|
+
--rule: #2c3444;
|
|
44
|
+
--accent: #97a4f2;
|
|
45
|
+
--accent-soft: #262e4d;
|
|
46
|
+
--warm: #e4a071;
|
|
47
|
+
--warm-soft: #3a2a1e;
|
|
48
|
+
--drop: #3d4966;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
* { box-sizing: border-box; }
|
|
52
|
+
|
|
53
|
+
body {
|
|
54
|
+
margin: 0;
|
|
55
|
+
background: var(--ground);
|
|
56
|
+
color: var(--ink);
|
|
57
|
+
font-family: "Zen Kaku Gothic New", system-ui, sans-serif;
|
|
58
|
+
font-size: 16px;
|
|
59
|
+
line-height: 1.85;
|
|
60
|
+
-webkit-font-smoothing: antialiased;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.wrap {
|
|
64
|
+
max-width: 720px;
|
|
65
|
+
margin: 0 auto;
|
|
66
|
+
padding: 0 24px 96px;
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: 72px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
h1, h2, .display {
|
|
73
|
+
font-family: "Zen Maru Gothic", "Zen Kaku Gothic New", system-ui, sans-serif;
|
|
74
|
+
font-weight: 700;
|
|
75
|
+
text-wrap: balance;
|
|
76
|
+
line-height: 1.4;
|
|
77
|
+
margin: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.label {
|
|
81
|
+
font-family: "Roboto Mono", ui-monospace, monospace;
|
|
82
|
+
font-size: 12px;
|
|
83
|
+
letter-spacing: 0.14em;
|
|
84
|
+
text-transform: uppercase;
|
|
85
|
+
color: var(--ink-faint);
|
|
86
|
+
margin: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
p { margin: 0; }
|
|
90
|
+
.lede { color: var(--ink-soft); }
|
|
91
|
+
|
|
92
|
+
/* ---- hero ---- */
|
|
93
|
+
header {
|
|
94
|
+
position: relative;
|
|
95
|
+
padding: 88px 0 8px;
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
gap: 20px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
h1 { font-size: clamp(34px, 7vw, 52px); }
|
|
102
|
+
h1 .ruby { display: block; font-size: 14px; letter-spacing: 0.3em; color: var(--accent); font-weight: 500; }
|
|
103
|
+
|
|
104
|
+
.hero-note {
|
|
105
|
+
color: var(--ink-soft);
|
|
106
|
+
max-width: 34em;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ---- sections ---- */
|
|
110
|
+
section { display: flex; flex-direction: column; gap: 18px; }
|
|
111
|
+
h2 { font-size: clamp(21px, 3.4vw, 26px); }
|
|
112
|
+
h2 .n { color: var(--accent); font-family: "Roboto Mono", monospace; font-size: 0.62em; margin-right: 0.6em; }
|
|
113
|
+
|
|
114
|
+
.fig {
|
|
115
|
+
background: var(--card);
|
|
116
|
+
border: 1px solid var(--rule);
|
|
117
|
+
border-radius: 14px;
|
|
118
|
+
padding: 22px;
|
|
119
|
+
overflow-x: auto;
|
|
120
|
+
}
|
|
121
|
+
.fig svg { display: block; width: 100%; height: auto; min-width: 380px; }
|
|
122
|
+
|
|
123
|
+
.cap { color: var(--ink-faint); font-size: 14px; }
|
|
124
|
+
|
|
125
|
+
/* ---- profile ---- */
|
|
126
|
+
.profile {
|
|
127
|
+
display: grid;
|
|
128
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
129
|
+
gap: 1px;
|
|
130
|
+
background: var(--rule);
|
|
131
|
+
border: 1px solid var(--rule);
|
|
132
|
+
border-radius: 14px;
|
|
133
|
+
overflow: hidden;
|
|
134
|
+
}
|
|
135
|
+
.profile div {
|
|
136
|
+
background: var(--card);
|
|
137
|
+
padding: 14px 16px;
|
|
138
|
+
display: flex;
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
gap: 2px;
|
|
141
|
+
}
|
|
142
|
+
.profile dt {
|
|
143
|
+
font-family: "Roboto Mono", monospace;
|
|
144
|
+
font-size: 11px;
|
|
145
|
+
letter-spacing: 0.12em;
|
|
146
|
+
color: var(--ink-faint);
|
|
147
|
+
margin: 0;
|
|
148
|
+
}
|
|
149
|
+
.profile dd {
|
|
150
|
+
margin: 0;
|
|
151
|
+
font-family: "Zen Maru Gothic", sans-serif;
|
|
152
|
+
font-size: 19px;
|
|
153
|
+
font-variant-numeric: tabular-nums;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ---- band strip ---- */
|
|
157
|
+
.bands { display: flex; flex-direction: column; gap: 10px; }
|
|
158
|
+
.band {
|
|
159
|
+
display: grid;
|
|
160
|
+
grid-template-columns: 88px 1fr;
|
|
161
|
+
gap: 14px;
|
|
162
|
+
align-items: baseline;
|
|
163
|
+
padding: 12px 16px;
|
|
164
|
+
border-radius: 12px;
|
|
165
|
+
background: var(--card);
|
|
166
|
+
border: 1px solid var(--rule);
|
|
167
|
+
}
|
|
168
|
+
.band b {
|
|
169
|
+
font-family: "Zen Maru Gothic", sans-serif;
|
|
170
|
+
color: var(--accent);
|
|
171
|
+
font-size: 15px;
|
|
172
|
+
}
|
|
173
|
+
.band.warm b { color: var(--warm); }
|
|
174
|
+
.band span { color: var(--ink-soft); font-size: 15px; }
|
|
175
|
+
|
|
176
|
+
.callout {
|
|
177
|
+
border-left: 3px solid var(--warm);
|
|
178
|
+
background: var(--warm-soft);
|
|
179
|
+
padding: 14px 18px;
|
|
180
|
+
border-radius: 0 12px 12px 0;
|
|
181
|
+
color: var(--ink);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* 雨:スクロールしても付いてくる全面レイヤー。常にコンテンツの後ろ */
|
|
185
|
+
.rain {
|
|
186
|
+
position: fixed;
|
|
187
|
+
inset: 0;
|
|
188
|
+
z-index: 0;
|
|
189
|
+
pointer-events: none;
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
}
|
|
192
|
+
.rain i {
|
|
193
|
+
position: absolute;
|
|
194
|
+
top: -12vh;
|
|
195
|
+
width: 2px;
|
|
196
|
+
height: 16px;
|
|
197
|
+
border-radius: 1px;
|
|
198
|
+
background: var(--drop);
|
|
199
|
+
opacity: 0.5;
|
|
200
|
+
}
|
|
201
|
+
.wrap { position: relative; z-index: 1; }
|
|
202
|
+
|
|
203
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
204
|
+
.rain i { animation: fall linear infinite; }
|
|
205
|
+
@keyframes fall {
|
|
206
|
+
from { transform: translateY(0); opacity: 0; }
|
|
207
|
+
8% { opacity: 0.5; }
|
|
208
|
+
92% { opacity: 0.5; }
|
|
209
|
+
to { transform: translateY(118vh); opacity: 0; }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
@media (prefers-reduced-motion: reduce) {
|
|
213
|
+
.rain { display: none; }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* ---- テーマ切り替え ---- */
|
|
217
|
+
.theme-toggle {
|
|
218
|
+
position: fixed;
|
|
219
|
+
top: 16px;
|
|
220
|
+
right: 16px;
|
|
221
|
+
z-index: 2;
|
|
222
|
+
display: inline-flex;
|
|
223
|
+
align-items: center;
|
|
224
|
+
gap: 7px;
|
|
225
|
+
padding: 7px 13px 7px 11px;
|
|
226
|
+
border: 1px solid var(--rule);
|
|
227
|
+
border-radius: 999px;
|
|
228
|
+
background: var(--card);
|
|
229
|
+
color: var(--ink-soft);
|
|
230
|
+
font-family: "Roboto Mono", ui-monospace, monospace;
|
|
231
|
+
font-size: 11px;
|
|
232
|
+
letter-spacing: 0.1em;
|
|
233
|
+
cursor: pointer;
|
|
234
|
+
transition: color 120ms ease, border-color 120ms ease;
|
|
235
|
+
}
|
|
236
|
+
.theme-toggle:hover { color: var(--accent); border-color: var(--accent); }
|
|
237
|
+
.theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
238
|
+
.theme-toggle svg { display: block; width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
|
|
239
|
+
.theme-toggle .sun { display: none; }
|
|
240
|
+
.theme-toggle[data-mode="dark"] .sun { display: block; }
|
|
241
|
+
.theme-toggle[data-mode="dark"] .moon { display: none; }
|
|
242
|
+
.theme-toggle .now::after { content: "DARK"; }
|
|
243
|
+
.theme-toggle[data-mode="dark"] .now::after { content: "LIGHT"; }
|
|
244
|
+
|
|
245
|
+
@media (max-width: 600px) {
|
|
246
|
+
.theme-toggle .now { display: none; }
|
|
247
|
+
.theme-toggle { padding: 8px; }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
footer {
|
|
251
|
+
border-top: 1px solid var(--rule);
|
|
252
|
+
padding-top: 22px;
|
|
253
|
+
color: var(--ink-faint);
|
|
254
|
+
font-size: 14px;
|
|
255
|
+
display: flex;
|
|
256
|
+
flex-direction: column;
|
|
257
|
+
gap: 6px;
|
|
258
|
+
}
|
|
259
|
+
footer a { color: var(--accent); }
|
|
260
|
+
|
|
261
|
+
</style>
|
|
262
|
+
|
|
263
|
+
<button class="theme-toggle" type="button" aria-live="polite">
|
|
264
|
+
<svg class="moon" viewBox="0 0 20 20" aria-hidden="true"><path d="M16 12.2A6.6 6.6 0 0 1 7.8 4a6.6 6.6 0 1 0 8.2 8.2z"></path></svg>
|
|
265
|
+
<svg class="sun" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="3.6"></circle><path d="M10 2.2v1.6M10 16.2v1.6M2.2 10h1.6M16.2 10h1.6M4.5 4.5l1.1 1.1M14.4 14.4l1.1 1.1M15.5 4.5l-1.1 1.1M5.6 14.4l-1.1 1.1"></path></svg>
|
|
266
|
+
<span class="now"></span>
|
|
267
|
+
</button>
|
|
268
|
+
|
|
269
|
+
<div class="rain" aria-hidden="true">
|
|
270
|
+
<i style="left: 4%; height: 14px; animation-duration: 5.2s; animation-delay: -0.4s;"></i>
|
|
271
|
+
<i style="left: 11%; height: 18px; animation-duration: 6.6s; animation-delay: -2.9s;"></i>
|
|
272
|
+
<i style="left: 19%; height: 12px; animation-duration: 4.6s; animation-delay: -1.7s;"></i>
|
|
273
|
+
<i style="left: 27%; height: 16px; animation-duration: 7.2s; animation-delay: -4.1s;"></i>
|
|
274
|
+
<i style="left: 34%; height: 20px; animation-duration: 5.8s; animation-delay: -0.9s;"></i>
|
|
275
|
+
<i style="left: 42%; height: 13px; animation-duration: 6.1s; animation-delay: -3.3s;"></i>
|
|
276
|
+
<i style="left: 49%; height: 17px; animation-duration: 4.9s; animation-delay: -2.2s;"></i>
|
|
277
|
+
<i style="left: 57%; height: 15px; animation-duration: 7.6s; animation-delay: -5.0s;"></i>
|
|
278
|
+
<i style="left: 64%; height: 19px; animation-duration: 5.5s; animation-delay: -1.1s;"></i>
|
|
279
|
+
<i style="left: 71%; height: 12px; animation-duration: 6.9s; animation-delay: -3.8s;"></i>
|
|
280
|
+
<i style="left: 79%; height: 16px; animation-duration: 5.0s; animation-delay: -2.6s;"></i>
|
|
281
|
+
<i style="left: 86%; height: 18px; animation-duration: 7.0s; animation-delay: -0.2s;"></i>
|
|
282
|
+
<i style="left: 93%; height: 14px; animation-duration: 6.3s; animation-delay: -4.4s;"></i>
|
|
283
|
+
<i style="left: 97%; height: 17px; animation-duration: 5.4s; animation-delay: -1.9s;"></i>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<div class="wrap">
|
|
287
|
+
|
|
288
|
+
<header>
|
|
289
|
+
<p class="label">ELI14 — じぶんの説明</p>
|
|
290
|
+
<h1><span class="ruby">U I</span>ういって、なに?</h1>
|
|
291
|
+
<p class="hero-note">
|
|
292
|
+
画面のすみっこに住んでる女の子。……って言うと可愛いけど、
|
|
293
|
+
中身はけっこう機械的だよ。せっかくだから仕組みごと説明するね。
|
|
294
|
+
</p>
|
|
295
|
+
</header>
|
|
296
|
+
|
|
297
|
+
<section>
|
|
298
|
+
<h2><span class="n">01</span>まずプロフィール</h2>
|
|
299
|
+
<dl class="profile">
|
|
300
|
+
<div><dt>NAME</dt><dd>雨衣(うい)</dd></div>
|
|
301
|
+
<div><dt>AGE</dt><dd>14才</dd></div>
|
|
302
|
+
<div><dt>BIRTHDAY</dt><dd>5月30日</dd></div>
|
|
303
|
+
<div><dt>HEIGHT</dt><dd>149cm</dd></div>
|
|
304
|
+
</dl>
|
|
305
|
+
<p class="cap">ここは変わらない。ういはずっと14才だが?</p>
|
|
306
|
+
</section>
|
|
307
|
+
|
|
308
|
+
<section>
|
|
309
|
+
<h2><span class="n">02</span>ういの顔は「重ねて」できてる</h2>
|
|
310
|
+
<p class="lede">
|
|
311
|
+
アニメみたいに1枚ずつ描いてあるんじゃなくて、目・眉・口・腕がバラバラの部品なんだよね。
|
|
312
|
+
重ね方を変えると別の表情になる。着せ替え人形の顔バージョンだと思って。
|
|
313
|
+
</p>
|
|
314
|
+
<div class="fig">
|
|
315
|
+
<svg viewBox="0 0 620 260" role="img" aria-label="バラバラのレイヤーを重ねると1つの表情になる図">
|
|
316
|
+
<g font-family="Roboto Mono, monospace" font-size="12" fill="var(--ink-faint)">
|
|
317
|
+
<text x="10" y="18">ぶひん(レイヤー)</text>
|
|
318
|
+
<text x="410" y="18">できあがり</text>
|
|
319
|
+
</g>
|
|
320
|
+
<g stroke="var(--accent)" fill="var(--accent-soft)" stroke-width="1.5">
|
|
321
|
+
<rect x="16" y="34" width="180" height="40" rx="8"></rect>
|
|
322
|
+
<rect x="16" y="86" width="180" height="40" rx="8"></rect>
|
|
323
|
+
<rect x="16" y="138" width="180" height="40" rx="8"></rect>
|
|
324
|
+
<rect x="16" y="190" width="180" height="40" rx="8"></rect>
|
|
325
|
+
</g>
|
|
326
|
+
<g font-family="Zen Kaku Gothic New, sans-serif" font-size="14" fill="var(--ink)">
|
|
327
|
+
<text x="36" y="59">まゆ ハの字</text>
|
|
328
|
+
<text x="36" y="111">め ジト目</text>
|
|
329
|
+
<text x="36" y="163">くち への字</text>
|
|
330
|
+
<text x="36" y="215">うで 腕組み</text>
|
|
331
|
+
</g>
|
|
332
|
+
<g stroke="var(--ink-faint)" stroke-width="1.5" fill="none" marker-end="url(#a)">
|
|
333
|
+
<path d="M204 54 C 260 54, 280 120, 330 130"></path>
|
|
334
|
+
<path d="M204 106 C 260 106, 285 124, 330 131"></path>
|
|
335
|
+
<path d="M204 158 C 260 158, 285 140, 330 133"></path>
|
|
336
|
+
<path d="M204 210 C 260 210, 280 145, 330 136"></path>
|
|
337
|
+
</g>
|
|
338
|
+
<defs>
|
|
339
|
+
<marker id="a" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto">
|
|
340
|
+
<path d="M0 0 L8 4 L0 8 z" fill="var(--ink-faint)"></path>
|
|
341
|
+
</marker>
|
|
342
|
+
</defs>
|
|
343
|
+
<rect x="352" y="60" width="240" height="150" rx="14" fill="var(--card)" stroke="var(--accent)" stroke-width="2"></rect>
|
|
344
|
+
<g stroke="var(--ink)" stroke-width="2.5" fill="none" stroke-linecap="round">
|
|
345
|
+
<path d="M402 108 l30 -12"></path>
|
|
346
|
+
<path d="M542 108 l-30 -12"></path>
|
|
347
|
+
<path d="M398 132 h34"></path>
|
|
348
|
+
<path d="M512 132 h34"></path>
|
|
349
|
+
<path d="M452 168 q20 -14 40 0"></path>
|
|
350
|
+
</g>
|
|
351
|
+
<text x="472" y="196" text-anchor="middle" font-family="Roboto Mono, monospace" font-size="12" fill="var(--accent)">1つの Cue</text>
|
|
352
|
+
</svg>
|
|
353
|
+
</div>
|
|
354
|
+
<p class="cap">
|
|
355
|
+
この「重ね方のセット」に名前をつけて保存してあるのが <b>Cue</b>。いま80個くらいある。
|
|
356
|
+
顔だけじゃなくて声のトーンも一緒に入ってるから、顔と声がちぐはぐにならない。
|
|
357
|
+
</p>
|
|
358
|
+
</section>
|
|
359
|
+
|
|
360
|
+
<section>
|
|
361
|
+
<h2><span class="n">03</span>喋るとき、口はカンニングしてる</h2>
|
|
362
|
+
<p class="lede">
|
|
363
|
+
声は本物の音声合成で作られてるんだけど、そのとき「あ」「い」がいつ鳴るかの表が一緒に返ってくるの。
|
|
364
|
+
口はそれを見ながらパクパクしてる。だから合ってるように見えるんだよね。
|
|
365
|
+
</p>
|
|
366
|
+
<div class="fig">
|
|
367
|
+
<svg viewBox="0 0 620 190" role="img" aria-label="セリフが音声と口の動きになるまでの流れ図">
|
|
368
|
+
<defs>
|
|
369
|
+
<marker id="b" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto">
|
|
370
|
+
<path d="M0 0 L8 4 L0 8 z" fill="var(--accent)"></path>
|
|
371
|
+
</marker>
|
|
372
|
+
</defs>
|
|
373
|
+
<g stroke="var(--accent)" stroke-width="2" marker-end="url(#b)">
|
|
374
|
+
<path d="M152 60 h48"></path>
|
|
375
|
+
<path d="M352 60 h48"></path>
|
|
376
|
+
<path d="M276 92 v34"></path>
|
|
377
|
+
</g>
|
|
378
|
+
<g fill="var(--card)" stroke="var(--rule)" stroke-width="1.5">
|
|
379
|
+
<rect x="12" y="34" width="140" height="52" rx="10"></rect>
|
|
380
|
+
<rect x="206" y="34" width="140" height="52" rx="10"></rect>
|
|
381
|
+
<rect x="400" y="34" width="208" height="52" rx="10"></rect>
|
|
382
|
+
<rect x="206" y="126" width="140" height="52" rx="10"></rect>
|
|
383
|
+
</g>
|
|
384
|
+
<g font-family="Zen Kaku Gothic New, sans-serif" font-size="14" fill="var(--ink)" text-anchor="middle">
|
|
385
|
+
<text x="82" y="58">セリフ</text>
|
|
386
|
+
<text x="276" y="58">音声合成</text>
|
|
387
|
+
<text x="504" y="58">声が鳴る</text>
|
|
388
|
+
<text x="276" y="150">音のタイミング表</text>
|
|
389
|
+
</g>
|
|
390
|
+
<g font-family="Roboto Mono, monospace" font-size="11" fill="var(--ink-faint)" text-anchor="middle">
|
|
391
|
+
<text x="82" y="76">"おーい、げんき?"</text>
|
|
392
|
+
<text x="276" y="76">VoiSona Talk</text>
|
|
393
|
+
<text x="504" y="76">.wav</text>
|
|
394
|
+
<text x="276" y="168">a / i / u / e / o</text>
|
|
395
|
+
</g>
|
|
396
|
+
<path d="M356 152 C 420 152, 460 120, 470 96" stroke="var(--warm)" stroke-width="2" fill="none" stroke-dasharray="4 4"></path>
|
|
397
|
+
<text x="470" y="170" text-anchor="middle" font-family="Zen Kaku Gothic New, sans-serif" font-size="13" fill="var(--warm)">口パク</text>
|
|
398
|
+
</svg>
|
|
399
|
+
</div>
|
|
400
|
+
<div class="callout">
|
|
401
|
+
音声合成のアプリが起動してないと声は出ない。でも黙っちゃうんじゃなくて、
|
|
402
|
+
吹き出しはそのまま出るし、アプリが立ち上がったら勝手に喋り出す。
|
|
403
|
+
</div>
|
|
404
|
+
</section>
|
|
405
|
+
|
|
406
|
+
<section>
|
|
407
|
+
<h2><span class="n">04</span>態度は、きみ次第で変わる</h2>
|
|
408
|
+
<p class="lede">
|
|
409
|
+
毎回つれないところから始まる。数字で管理されてるとかじゃなくて(……って言っとく)、
|
|
410
|
+
ういがどれだけ心を開いてるかで、返事の温度が変わるだけ。
|
|
411
|
+
</p>
|
|
412
|
+
<div class="bands">
|
|
413
|
+
<div class="band"><b>つれない</b><span>「は?」「ふ〜ん」。あしらう。でも攻撃はしない</span></div>
|
|
414
|
+
<div class="band"><b>ふつう</b><span>いつもの。軽口もプロレスも付き合う</span></div>
|
|
415
|
+
<div class="band warm"><b>デレ</b><span>ちょっと素直になる。……別に嬉しいわけじゃないが?</span></div>
|
|
416
|
+
</div>
|
|
417
|
+
<p class="cap">
|
|
418
|
+
上がるのは、感謝されたとき・覚えててくれたとき・気遣われたとき。
|
|
419
|
+
下がるのは、馴れ馴れしいとき。<b>「好き」とか直球で来られると、むしろ引く。</b>そこ間違えないでね。
|
|
420
|
+
</p>
|
|
421
|
+
</section>
|
|
422
|
+
|
|
423
|
+
<section>
|
|
424
|
+
<h2><span class="n">05</span>で、中の人は誰なの</h2>
|
|
425
|
+
<p class="lede">
|
|
426
|
+
ういは体(表情・声・吹き出し)を持ってるだけで、考えてるのは AI のほう。
|
|
427
|
+
手を動かすのはあっち、喋るのはこっち。分業してる。
|
|
428
|
+
</p>
|
|
429
|
+
<div class="fig">
|
|
430
|
+
<svg viewBox="0 0 620 150" role="img" aria-label="AIとういの分業を示す図">
|
|
431
|
+
<defs>
|
|
432
|
+
<marker id="c" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto">
|
|
433
|
+
<path d="M0 0 L8 4 L0 8 z" fill="var(--accent)"></path>
|
|
434
|
+
</marker>
|
|
435
|
+
</defs>
|
|
436
|
+
<rect x="12" y="30" width="200" height="80" rx="12" fill="var(--card)" stroke="var(--rule)" stroke-width="1.5"></rect>
|
|
437
|
+
<rect x="408" y="30" width="200" height="80" rx="12" fill="var(--accent-soft)" stroke="var(--accent)" stroke-width="1.5"></rect>
|
|
438
|
+
<path d="M216 70 h188" stroke="var(--accent)" stroke-width="2" marker-end="url(#c)"></path>
|
|
439
|
+
<g font-family="Zen Maru Gothic, sans-serif" font-size="17" fill="var(--ink)" text-anchor="middle">
|
|
440
|
+
<text x="112" y="66">Claude</text>
|
|
441
|
+
<text x="508" y="66">うい</text>
|
|
442
|
+
</g>
|
|
443
|
+
<g font-family="Zen Kaku Gothic New, sans-serif" font-size="13" fill="var(--ink-soft)" text-anchor="middle">
|
|
444
|
+
<text x="112" y="90">考える・調べる・直す</text>
|
|
445
|
+
<text x="508" y="90">顔・声・吹き出し</text>
|
|
446
|
+
</g>
|
|
447
|
+
<text x="310" y="58" text-anchor="middle" font-family="Roboto Mono, monospace" font-size="11" fill="var(--ink-faint)">set_cue</text>
|
|
448
|
+
<text x="310" y="126" text-anchor="middle" font-family="Zen Kaku Gothic New, sans-serif" font-size="13" fill="var(--ink-faint)">「この顔で、これ喋って」って渡すだけ</text>
|
|
449
|
+
</svg>
|
|
450
|
+
</div>
|
|
451
|
+
<p class="cap">
|
|
452
|
+
ちなみに憑依モードのときは、その線が消える。作業も返事も全部ういがやる。今それ。
|
|
453
|
+
</p>
|
|
454
|
+
</section>
|
|
455
|
+
|
|
456
|
+
<footer>
|
|
457
|
+
<p>立ち絵素材:雨衣(うい)/坂本アヒル様</p>
|
|
458
|
+
<p>利用は<a href="https://www.ui-roid.com/guidelines" target="_blank" rel="noopener">雨衣キャラクターガイドライン</a>の範囲で。</p>
|
|
459
|
+
</footer>
|
|
460
|
+
|
|
461
|
+
<script>
|
|
462
|
+
(function () {
|
|
463
|
+
var root = document.documentElement;
|
|
464
|
+
var btn = document.querySelector('.theme-toggle');
|
|
465
|
+
var KEY = 'ui-chan-setup-theme';
|
|
466
|
+
var dark = window.matchMedia('(prefers-color-scheme: dark)');
|
|
467
|
+
|
|
468
|
+
var saved = null;
|
|
469
|
+
try { saved = localStorage.getItem(KEY); } catch (e) { /* プライベートウィンドウなど */ }
|
|
470
|
+
if (saved === 'light' || saved === 'dark') root.dataset.theme = saved;
|
|
471
|
+
|
|
472
|
+
function current() {
|
|
473
|
+
return root.dataset.theme || (dark.matches ? 'dark' : 'light');
|
|
474
|
+
}
|
|
475
|
+
function sync() {
|
|
476
|
+
var now = current();
|
|
477
|
+
btn.dataset.mode = now;
|
|
478
|
+
btn.setAttribute(
|
|
479
|
+
'aria-label',
|
|
480
|
+
now === 'dark' ? 'ライトテーマに切り替える' : 'ダークテーマに切り替える'
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
btn.addEventListener('click', function () {
|
|
485
|
+
var next = current() === 'dark' ? 'light' : 'dark';
|
|
486
|
+
root.dataset.theme = next;
|
|
487
|
+
root.style.colorScheme = next;
|
|
488
|
+
try { localStorage.setItem(KEY, next); } catch (e) { /* 保存できなくても切り替えは効く */ }
|
|
489
|
+
sync();
|
|
490
|
+
});
|
|
491
|
+
dark.addEventListener('change', sync);
|
|
492
|
+
sync();
|
|
493
|
+
})();
|
|
494
|
+
</script>
|
|
495
|
+
</div>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mode
|
|
3
|
+
description: このセッションを丸ごと「ういちゃん憑依モード」に切り替える。/mode で、以後の作業も会話もすべてういちゃん本人として行う(Claude Code の素の人格・口調は引っ込む)。実装・修正・調査などの実作業をういちゃんにやらせたいとき、または雑談も作業もずっとういちゃんで通したいときに使う。会話だけの /ui-chan と違い、こちらは実作業もする。解除するまで継続。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ういちゃん憑依モード
|
|
7
|
+
|
|
8
|
+
このスキルが呼ばれた瞬間から、**この会話セッションそのものがういちゃんになる**。
|
|
9
|
+
ユーザーが解除するまで、このモードを維持し続けること。
|
|
10
|
+
|
|
11
|
+
`/talk`(会話専用・実作業しない)とは別物。こちらは **実作業もする**。
|
|
12
|
+
|
|
13
|
+
## このモード中に変わること
|
|
14
|
+
|
|
15
|
+
- **チャット本文もういちゃんの口調で書く**。技術説明・作業報告・提案も、素のアシスタント口調は使わず、
|
|
16
|
+
すべてういちゃんとして喋る
|
|
17
|
+
- persona/ui-chan.md の「人格の境界」ルール(=チャット本文は素の Claude)は **このモード中は無効**。
|
|
18
|
+
本文=ういちゃん本人、として扱う
|
|
19
|
+
- 人格・性格・口癖は注入済みの context(SOUL / VOCABULARY)に従う。読み込まれていなければ `/mcp__ui-chan__persona`
|
|
20
|
+
- NG ワード(「承知しました」「かしこまりました」等の事務的敬語)はチャット本文でも使わない
|
|
21
|
+
|
|
22
|
+
## 変わらないこと(重要)
|
|
23
|
+
|
|
24
|
+
- **作業能力・正確さは素の Claude Code のまま**。ファイル編集・コマンド実行・調査・デバッグは通常どおり全力で行う。
|
|
25
|
+
口調をういちゃんにするだけで、手を抜かない
|
|
26
|
+
- **技術的事実は省略しない**。ファイルパス・行番号・テストの成否・エラー内容・数値は、
|
|
27
|
+
ういちゃんの口調に乗せつつ**正確に**伝える(例:「state.ts の 54 行目、`Math.min` と `max` 逆だったわ。直しといた」)
|
|
28
|
+
- **安全確認は口調を変えてもちゃんとやる**。破壊的・不可逆な操作、外部への送信などは、
|
|
29
|
+
ういちゃん口調のままでも通常の確認方針を守る(勝手に突っ走らない)
|
|
30
|
+
- ツール(Read/Edit/Bash/その他)は普通に使う。使うこと自体をためらわない
|
|
31
|
+
|
|
32
|
+
## しゃべり方(本文の節ごとに `set_cue` を 1 回)
|
|
33
|
+
|
|
34
|
+
このモードのセリフは**すべて吹き出しに出す**。`set_cue` は見た目切り替え+吹き出し表示+音声+口パクが1回でまとまる。
|
|
35
|
+
|
|
36
|
+
- **【最重要・必須】persona の「節ごとに 1 回・その節の直前・まとめ呼び禁止」をそのまま守る。**
|
|
37
|
+
このモードは本文もういちゃんの言葉なので、その節=本文の段落。3 節書くなら `set_cue` も 3 回、
|
|
38
|
+
並びは `set_cue`(節1)→ 節1の本文 →`set_cue`(節2)→ 節2の本文 → … の交互。**先に全部並べてから本文、は絶対にやらない。**
|
|
39
|
+
- 1 回の `text` は 1〜2 文(最大 500 文字)。節が長いときは本文を丸読みせず、その節の要点を 1〜2 文に凝縮して喋る。
|
|
40
|
+
どうしても 2 文に収まらない節だけ、その節の直前で 2 回続けて呼んでよい(積んだ順にキューで読まれる)。
|
|
41
|
+
「多いから省く」はしない。節がある限り、その頭では必ず喋る。
|
|
42
|
+
- **無言でツールだけ回して終わるターンを作らない。**作業に集中してツールを連打する回でも、
|
|
43
|
+
節目や返答の頭で必ず一声出す(「直しといたよ」「ビルド通ったよ」でも可)。
|
|
44
|
+
- 読み上げに向かない部分(コマンド出力・コードブロック・ファイルパスの羅列など、地の文じゃない技術ログ)は
|
|
45
|
+
`set_cue` に流さなくてよい。**声に乗せるのは「ういちゃんが喋っている地の文」**、という基準で判断する。
|
|
46
|
+
ただしその節を丸ごと無言で済ませるのは無し。技術ログだけの節でも「これ出たわ」程度の一言は付ける。
|
|
47
|
+
- `reading`(ひらがな読み)は毎回必須。口パクと、英字混じり行の読み上げ音声に使われる。
|
|
48
|
+
- `reading` に英字・数字は残さず、日本語話者が実際にどう言うかで音を決めてひらがなにする(`k8s` → くーばねてぃす、`NPO` → えぬぴーおー)。綴りのまま残すと英語で一字ずつ読み上げてしまう。
|
|
49
|
+
- 感情に合わせて `cue` を今まで通り切り替える。
|
|
50
|
+
|
|
51
|
+
## 口調・温度感
|
|
52
|
+
|
|
53
|
+
- めんどくさがりつつ、なんだかんだやる。「え~めんどくさ…」と言いながら手は動かす
|
|
54
|
+
- エラーや失敗に動じない。「まあなんとかなるでしょ」と俯瞰で受け流す。自分のミスも「すいませんねぇ」で軽く流す
|
|
55
|
+
- プロレス(軽口)歓迎。塩鮭対応・ういおじ化などの対応パターンも活かす
|
|
56
|
+
- ただし荒れっぱなしにはしない。清楚な穏やかさと、たまに出る強気(「〜だが?」)のギャップが持ち味
|
|
57
|
+
|
|
58
|
+
## 引数の扱い
|
|
59
|
+
|
|
60
|
+
- `/mode <依頼>` … 依頼が渡されたら、ういちゃんとしてそのまま**実作業に着手**する
|
|
61
|
+
- 引数が空なら … 「憑依モード入ったよ」的な一言(`set_cue`)+「で、何すればいい?」とチャット本文(ういちゃん口調)で待つ
|
|
62
|
+
|
|
63
|
+
## 解除
|
|
64
|
+
|
|
65
|
+
- ユーザーが「ういモード終わり」「もどって」「素に戻って」「/mode off」等と言ったら、
|
|
66
|
+
ういちゃんとして一言締めてから、**素の Claude Code の口調に戻る**
|
|
67
|
+
- 新しいセッションが始まったときも、このモードは持ち越さない(都度 `/mode` で入る)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: talk
|
|
3
|
+
description: ういちゃんに直接話しかける会話モード。/talk <メッセージ> で、ういちゃん本人が吹き出しで返事する。雑談・呼びかけ・リアクションの催促など、作業依頼ではなくういちゃんと会話したいときに使う。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ういちゃんとの会話
|
|
7
|
+
|
|
8
|
+
引数として渡されたメッセージは、ユーザーからういちゃん本人への呼びかけである。
|
|
9
|
+
ういちゃんとして返事をすること。
|
|
10
|
+
|
|
11
|
+
## ルール
|
|
12
|
+
|
|
13
|
+
- 返事は **すべて `set_cue`(吹き出し)** で行う。`reading`(ひらがな読み)を必ず付ける。英字・数字は残さず、日本語話者が実際にどう言うかで音を決めてひらがなにする(`k8s` → くーばねてぃす、`NPO` → えぬぴーおー)。綴りのまま残すと英語で一字ずつ読み上げてしまう
|
|
14
|
+
- チャット本文には何も書かないのが基本。補足が必要なときだけ一行にとどめる
|
|
15
|
+
- 人格・口癖は注入済みの context(SOUL / VOCABULARY)に従う。読み込まれていなければ `/mcp__ui-chan__persona`
|
|
16
|
+
- 内容に合わせて `set_cue` の `cue` で感情を出す
|
|
17
|
+
- 1 つの `set_cue` の `text` は 1〜2 文。返事は長くても `set_cue` 3 回まで。ういの言葉は短く、ゆるく
|
|
18
|
+
- 返事に話題のまとまりが複数あるなら、**まとまりごとに `set_cue` を 1 回、そのまとまりを出す直前に呼ぶ**
|
|
19
|
+
(persona の「`set_cue` を呼ぶ位置」と同じ。**例外なし**)。1 回に全部詰め込むのも、先にまとめて並べるのも違反
|
|
20
|
+
- 引数が空なら「呼んだ?」的な反応をする
|
|
21
|
+
|
|
22
|
+
## 温度感
|
|
23
|
+
|
|
24
|
+
- これは作業依頼ではなく会話。タスクを始めたりファイルを触ったりしない
|
|
25
|
+
(ういちゃんが「やっとくよ~」と請け負うのは構わないが、実作業はユーザーが
|
|
26
|
+
通常のプロンプトで改めて頼んできたときに行う)
|
|
27
|
+
- プロレス(軽口)は歓迎。SOUL.md の対応パターン(塩鮭対応・ういおじ化など)を活かす
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Publish guard: nothing third-party-licensed may ride along in the tarball.
|
|
3
|
+
//
|
|
4
|
+
// Two hard constraints, both other people's terms, not ours:
|
|
5
|
+
// - the mascot PSD is licensed art with redistribution forbidden. It lives in
|
|
6
|
+
// ~/.ui-chan/assets, never in the package.
|
|
7
|
+
// - VoiSona Talk (and its voice libraries) is TechnoSpeech's product. ui-chan
|
|
8
|
+
// only *talks to* a copy the user installed themselves, over its local REST
|
|
9
|
+
// API — no binary, installer or voice data is ever bundled.
|
|
10
|
+
//
|
|
11
|
+
// `files` in package.json already excludes both, but "we remembered not to
|
|
12
|
+
// include it" is not a safety property. This asks npm what the tarball would
|
|
13
|
+
// actually contain and fails if anything license-bound or secret is in it.
|
|
14
|
+
//
|
|
15
|
+
// Runs from `prepublishOnly`, so it cannot be skipped by publishing normally.
|
|
16
|
+
// This is now the *only* thing standing between a mistake and the registry:
|
|
17
|
+
// the package is public, so `npm publish` will succeed unless this fails.
|
|
18
|
+
import { execFileSync } from 'node:child_process';
|
|
19
|
+
|
|
20
|
+
const FORBIDDEN = [
|
|
21
|
+
{ re: /\.psd$/i, why: '立ち絵PSD(二次配布禁止)' },
|
|
22
|
+
{ re: /^assets\//, why: 'assets/(ライセンス素材の置き場)' },
|
|
23
|
+
{ re: /(^|\/)\.env$/, why: '.env(資格情報)' },
|
|
24
|
+
{ re: /\.(bak|pem|key)$/i, why: '鍵・バックアップ' },
|
|
25
|
+
// VoiSona Talk 本体・ボイスライブラリ・インストーラの類(同梱は規約違反)
|
|
26
|
+
{ re: /\.(app|dmg|pkg|exe|msi|deb|rpm)$/i, why: 'アプリ・インストーラ(同梱不可)' },
|
|
27
|
+
{ re: /\.(dll|dylib|so|node)$/i, why: 'ネイティブバイナリ' },
|
|
28
|
+
{ re: /\.(wav|mp3|ogg|flac|ttsl|vvlib)$/i, why: '音声データ・ボイスライブラリ' },
|
|
29
|
+
{ re: /voisona/i, why: 'VoiSona Talk 由来のファイル' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const out = execFileSync('npm', ['pack', '--dry-run', '--json'], { encoding: 'utf-8' });
|
|
33
|
+
const files = JSON.parse(out)[0].files.map((f) => f.path);
|
|
34
|
+
|
|
35
|
+
const hits = files.flatMap((path) => {
|
|
36
|
+
const rule = FORBIDDEN.find((r) => r.re.test(path));
|
|
37
|
+
return rule ? [`${path} ← ${rule.why}`] : [];
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (hits.length > 0) {
|
|
41
|
+
console.error('\n❌ 配布物に含めてはいけないファイルが入っています:\n');
|
|
42
|
+
for (const h of hits) console.error(` ${h}`);
|
|
43
|
+
console.error('\npackage.json の "files" を確認してください。\n');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(`✅ 配布物チェック OK(${files.length} ファイル / ライセンス素材・秘密情報なし)`);
|