@aptol/openclaw-persona 1.0.0 → 1.2.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.
Files changed (2) hide show
  1. package/bin/create.mjs +369 -76
  2. package/package.json +1 -1
package/bin/create.mjs CHANGED
@@ -51,11 +51,87 @@ const MODULE_MAP = {
51
51
  '선제 대화 (Proactive Chat)': 'proactive-chat.md',
52
52
  };
53
53
 
54
+ const DISCORD_POLICIES = {
55
+ '모든 서버에서 멘션(@봇) 시 반응 (추천, 설정 불필요)': 'mention',
56
+ '모든 서버에서 항상 반응': 'all',
57
+ '특정 서버/채널만 허용 (ID 직접 입력)': 'allowlist',
58
+ };
59
+
60
+ const AI_MODELS = {
61
+ 'Claude Opus 4 (최고 성능, 비용 높음)': 'anthropic/claude-opus-4-6',
62
+ 'Claude Sonnet 4 (균형, 추천)': 'anthropic/claude-sonnet-4-20250514',
63
+ 'Claude Sonnet 4.5 (고성능 + 빠름)': 'anthropic/claude-sonnet-4-5-20250514',
64
+ };
65
+
66
+ async function askServerChannels() {
67
+ const servers = [];
68
+ let addMore = true;
69
+
70
+ while (addMore) {
71
+ const { guildId } = await inquirer.prompt([{
72
+ type: 'input',
73
+ name: 'guildId',
74
+ message: '서버 ID:',
75
+ validate: (v) => /^\d+$/.test(v.trim()) ? true : '숫자만 입력해주세요.',
76
+ }]);
77
+
78
+ const { requireMention } = await inquirer.prompt([{
79
+ type: 'list',
80
+ name: 'requireMention',
81
+ message: `이 서버에서 멘션 필요?`,
82
+ choices: [
83
+ { name: '아니오 — 모든 메시지에 반응', value: false },
84
+ { name: '예 — 멘션(@봇)할 때만 반응', value: true },
85
+ ],
86
+ }]);
87
+
88
+ const channels = [];
89
+ let addChannels = true;
90
+
91
+ while (addChannels) {
92
+ const { channelId } = await inquirer.prompt([{
93
+ type: 'input',
94
+ name: 'channelId',
95
+ message: '채널 ID (비워두면 서버 전체):',
96
+ default: '',
97
+ }]);
98
+
99
+ if (channelId.trim()) {
100
+ channels.push(channelId.trim());
101
+ const { more } = await inquirer.prompt([{
102
+ type: 'confirm',
103
+ name: 'more',
104
+ message: '채널 더 추가?',
105
+ default: false,
106
+ }]);
107
+ addChannels = more;
108
+ } else {
109
+ addChannels = false;
110
+ }
111
+ }
112
+
113
+ servers.push({ guildId: guildId.trim(), requireMention, channels });
114
+
115
+ const { more } = await inquirer.prompt([{
116
+ type: 'confirm',
117
+ name: 'more',
118
+ message: '서버 더 추가?',
119
+ default: false,
120
+ }]);
121
+ addMore = more;
122
+ }
123
+
124
+ return servers;
125
+ }
126
+
54
127
  async function main() {
55
128
  console.log('\n🎭 OpenClaw Persona Creator\n');
56
129
  console.log('나만의 AI 캐릭터를 만들어보세요!\n');
57
130
 
58
- const answers = await inquirer.prompt([
131
+ // ─── 1. 캐릭터 기본 정보 ───
132
+ console.log('━━━ 1/5: 캐릭터 정보 ━━━\n');
133
+
134
+ const charAnswers = await inquirer.prompt([
59
135
  {
60
136
  type: 'input',
61
137
  name: 'name',
@@ -98,10 +174,106 @@ async function main() {
98
174
  message: '만든 사람 이름:',
99
175
  validate: (v) => v.trim() ? true : '이름을 입력해주세요.',
100
176
  },
177
+ {
178
+ type: 'list',
179
+ name: 'presetKey',
180
+ message: '프리셋 베이스:',
181
+ choices: Object.keys(PRESET_NAMES),
182
+ },
183
+ {
184
+ type: 'checkbox',
185
+ name: 'moduleKeys',
186
+ message: '모듈 선택 (스페이스바로 선택):',
187
+ choices: Object.keys(MODULE_MAP),
188
+ },
189
+ ]);
190
+
191
+ // ─── 2. AI 모델 설정 ───
192
+ console.log('\n━━━ 2/5: AI 모델 설정 ━━━\n');
193
+
194
+ const modelAnswers = await inquirer.prompt([
195
+ {
196
+ type: 'list',
197
+ name: 'modelKey',
198
+ message: 'AI 모델 선택:',
199
+ choices: Object.keys(AI_MODELS),
200
+ },
201
+ ]);
202
+
203
+ // ─── 3. API 키 설정 ───
204
+ console.log('\n━━━ 3/5: API 키 설정 ━━━\n');
205
+
206
+ const authModeAnswers = await inquirer.prompt([{
207
+ type: 'list',
208
+ name: 'anthropicAuth',
209
+ message: 'Anthropic 인증 방식:',
210
+ choices: [
211
+ { name: 'API 키 직접 입력 (sk-ant-...)', value: 'token' },
212
+ { name: 'Claude Max/Pro 구독 연결 (OAuth, 브라우저 인증)', value: 'oauth' },
213
+ { name: '나중에 설정', value: 'skip' },
214
+ ],
215
+ }]);
216
+
217
+ let anthropicKey = '';
218
+ let anthropicOAuth = false;
219
+
220
+ if (authModeAnswers.anthropicAuth === 'token') {
221
+ const { key } = await inquirer.prompt([{
222
+ type: 'password',
223
+ name: 'key',
224
+ message: 'Anthropic API 키 (sk-ant-...):',
225
+ mask: '*',
226
+ }]);
227
+ anthropicKey = key.trim();
228
+ } else if (authModeAnswers.anthropicAuth === 'oauth') {
229
+ anthropicOAuth = true;
230
+ console.log('\n📌 구독 연결은 캐릭터 생성 후 아래 명령어로 완료하세요:');
231
+ console.log(' $env:OPENCLAW_HOME = "<출력 디렉토리>"');
232
+ console.log(' openclaw auth login\n');
233
+ console.log(' 브라우저가 열리면 Claude 계정으로 로그인하세요.\n');
234
+ }
235
+
236
+ console.log('💡 빈칸으로 넘기면 나중에 openclaw.json에서 수동 설정 가능\n');
237
+
238
+ const apiAnswers = await inquirer.prompt([
239
+ {
240
+ type: 'password',
241
+ name: 'openaiKey',
242
+ message: 'OpenAI API 키 (메모리 검색용, sk-...):',
243
+ mask: '*',
244
+ default: '',
245
+ },
246
+ {
247
+ type: 'password',
248
+ name: 'braveKey',
249
+ message: 'Brave Search API 키 (웹 검색용):',
250
+ mask: '*',
251
+ default: '',
252
+ },
253
+ {
254
+ type: 'password',
255
+ name: 'elevenlabsKey',
256
+ message: 'ElevenLabs API 키 (음성 TTS용, 선택):',
257
+ mask: '*',
258
+ default: '',
259
+ },
260
+ {
261
+ type: 'password',
262
+ name: 'deepgramKey',
263
+ message: 'Deepgram API 키 (음성 STT용, 선택):',
264
+ mask: '*',
265
+ default: '',
266
+ },
267
+ ]);
268
+
269
+ // ─── 4. 디스코드 설정 ───
270
+ console.log('\n━━━ 4/5: 디스코드 설정 ━━━\n');
271
+
272
+ const discordAnswers = await inquirer.prompt([
101
273
  {
102
274
  type: 'input',
103
275
  name: 'discordId',
104
- message: 'Discord 사용자 ID (숫자):',
276
+ message: 'Discord 사용자 ID (숫자, 주인):',
105
277
  validate: (v) => {
106
278
  if (!v.trim()) return '디스코드 ID를 입력해주세요.';
107
279
  if (!/^\d+$/.test(v.trim())) return '숫자만 입력해주세요.';
@@ -109,89 +281,89 @@ async function main() {
109
281
  },
110
282
  },
111
283
  {
112
- type: 'input',
284
+ type: 'password',
113
285
  name: 'discordToken',
114
- message: 'Discord 봇 토큰 (나중에 설정하려면 빈칸):',
286
+ message: 'Discord 봇 토큰:',
287
+ mask: '*',
115
288
  default: '',
116
289
  },
117
290
  {
118
291
  type: 'list',
119
- name: 'presetKey',
120
- message: '프리셋 베이스:',
121
- choices: Object.keys(PRESET_NAMES),
122
- },
123
- {
124
- type: 'checkbox',
125
- name: 'moduleKeys',
126
- message: '모듈 선택 (스페이스바로 선택):',
127
- choices: Object.keys(MODULE_MAP),
292
+ name: 'discordPolicyKey',
293
+ message: '디스코드 서버 정책:',
294
+ choices: Object.keys(DISCORD_POLICIES),
128
295
  },
296
+ ]);
297
+
298
+ const discordPolicy = DISCORD_POLICIES[discordAnswers.discordPolicyKey];
299
+
300
+ let serverConfigs = [];
301
+ if (discordPolicy === 'allowlist') {
302
+ console.log('\n📋 활동할 서버와 채널을 설정합니다.\n');
303
+ serverConfigs = await askServerChannels();
304
+ }
305
+
306
+ // ─── 5. 출력 설정 ───
307
+ console.log('\n━━━ 5/5: 출력 설정 ━━━\n');
308
+
309
+ const outputAnswers = await inquirer.prompt([
129
310
  {
130
311
  type: 'input',
131
312
  name: 'outputDir',
132
313
  message: '출력 디렉토리:',
133
- default: (ans) => `./output/${ans.name}`,
314
+ default: `./output/${charAnswers.name.trim()}`,
315
+ },
316
+ {
317
+ type: 'input',
318
+ name: 'gatewayPort',
319
+ message: '게이트웨이 포트 (기본 18789, 다른 봇 있으면 변경):',
320
+ default: '18789',
321
+ validate: (v) => /^\d+$/.test(v.trim()) ? true : '숫자만 입력해주세요.',
134
322
  },
135
323
  ]);
136
324
 
325
+ // ─── Build ───
326
+
137
327
  const vars = {
138
- name: answers.name.trim(),
139
- gender: answers.gender,
140
- speechStyle: SPEECH_STYLES[answers.speechStyleKey],
141
- personality: answers.personality.trim(),
142
- likes: answers.likes.trim(),
143
- dislikes: answers.dislikes.trim(),
144
- creator: answers.creator.trim(),
328
+ name: charAnswers.name.trim(),
329
+ gender: charAnswers.gender,
330
+ speechStyle: SPEECH_STYLES[charAnswers.speechStyleKey],
331
+ personality: charAnswers.personality.trim(),
332
+ likes: charAnswers.likes.trim(),
333
+ dislikes: charAnswers.dislikes.trim(),
334
+ creator: charAnswers.creator.trim(),
145
335
  };
146
336
 
147
- const preset = PRESET_NAMES[answers.presetKey];
148
- const outputDir = answers.outputDir;
337
+ const preset = PRESET_NAMES[charAnswers.presetKey];
338
+ const outputDir = outputAnswers.outputDir;
149
339
 
150
340
  // Create output directories
151
341
  mkdirSync(join(outputDir, 'memory'), { recursive: true });
152
342
  mkdirSync(join(outputDir, 'modules'), { recursive: true });
153
343
 
154
- // Generate files
344
+ // Generate character files
155
345
  if (preset) {
156
- // Preset-based: copy preset files with variable replacement
157
346
  const presetDir = join(ROOT, 'presets', preset);
158
- const soulContent = replaceVars(readTemplate(join(presetDir, 'SOUL.md')), vars);
159
- const agentsContent = readTemplate(join(presetDir, 'AGENTS.md'));
160
- const identityContent = replaceVars(readTemplate(join(presetDir, 'IDENTITY.md')), vars);
161
-
162
- writeOutput(outputDir, 'SOUL.md', soulContent);
163
- writeOutput(outputDir, 'AGENTS.md', agentsContent);
164
- writeOutput(outputDir, 'IDENTITY.md', identityContent);
347
+ writeOutput(outputDir, 'SOUL.md', replaceVars(readTemplate(join(presetDir, 'SOUL.md')), vars));
348
+ writeOutput(outputDir, 'AGENTS.md', readTemplate(join(presetDir, 'AGENTS.md')));
349
+ writeOutput(outputDir, 'IDENTITY.md', replaceVars(readTemplate(join(presetDir, 'IDENTITY.md')), vars));
165
350
  } else {
166
- // From scratch: use templates
167
- const soulContent = replaceVars(readTemplate(join(ROOT, 'templates', 'SOUL.template.md')), vars);
168
- const agentsContent = readTemplate(join(ROOT, 'templates', 'AGENTS.template.md'));
169
- const identityContent = replaceVars(readTemplate(join(ROOT, 'templates', 'IDENTITY.template.md')), vars);
170
-
171
- writeOutput(outputDir, 'SOUL.md', soulContent);
172
- writeOutput(outputDir, 'AGENTS.md', agentsContent);
173
- writeOutput(outputDir, 'IDENTITY.md', identityContent);
351
+ writeOutput(outputDir, 'SOUL.md', replaceVars(readTemplate(join(ROOT, 'templates', 'SOUL.template.md')), vars));
352
+ writeOutput(outputDir, 'AGENTS.md', readTemplate(join(ROOT, 'templates', 'AGENTS.template.md')));
353
+ writeOutput(outputDir, 'IDENTITY.md', replaceVars(readTemplate(join(ROOT, 'templates', 'IDENTITY.template.md')), vars));
174
354
  }
175
355
 
176
- // USER.md
177
- const userContent = replaceVars(readTemplate(join(ROOT, 'templates', 'USER.template.md')), vars);
178
- writeOutput(outputDir, 'USER.md', userContent);
179
-
180
- // MEMORY.md
181
- const memoryContent = replaceVars(readTemplate(join(ROOT, 'templates', 'MEMORY.template.md')), vars);
182
- writeOutput(outputDir, 'MEMORY.md', memoryContent);
356
+ writeOutput(outputDir, 'USER.md', replaceVars(readTemplate(join(ROOT, 'templates', 'USER.template.md')), vars));
357
+ writeOutput(outputDir, 'MEMORY.md', replaceVars(readTemplate(join(ROOT, 'templates', 'MEMORY.template.md')), vars));
183
358
 
184
359
  // Copy selected modules
185
360
  let hasProactiveChat = false;
186
- for (const moduleKey of answers.moduleKeys) {
361
+ for (const moduleKey of charAnswers.moduleKeys) {
187
362
  const moduleFile = MODULE_MAP[moduleKey];
188
- const src = join(ROOT, 'modules', moduleFile);
189
- const dest = join(outputDir, 'modules', moduleFile);
190
- copyFileSync(src, dest);
363
+ copyFileSync(join(ROOT, 'modules', moduleFile), join(outputDir, 'modules', moduleFile));
191
364
  if (moduleFile === 'proactive-chat.md') hasProactiveChat = true;
192
365
  }
193
366
 
194
- // If proactive-chat module selected, create HEARTBEAT.md
195
367
  if (hasProactiveChat) {
196
368
  writeOutput(outputDir, 'HEARTBEAT.md', `# HEARTBEAT.md
197
369
 
@@ -224,38 +396,159 @@ async function main() {
224
396
  `);
225
397
  }
226
398
 
227
- // Copy and customize openclaw config
399
+ // ─── Build openclaw.json ───
400
+
228
401
  const config = JSON.parse(readFileSync(join(ROOT, 'config', 'openclaw.template.json'), 'utf-8'));
229
- config.agents.defaults.workspace = outputDir;
230
- const discordId = answers.discordId.trim();
231
- const discordToken = answers.discordToken.trim();
232
- config.channels.discord.token = discordToken || '__DISCORD_BOT_TOKEN__';
402
+ const discordId = discordAnswers.discordId.trim();
403
+ const port = parseInt(outputAnswers.gatewayPort.trim());
404
+
405
+ // Model
406
+ config.agents.defaults.model.primary = AI_MODELS[modelAnswers.modelKey];
407
+
408
+ // Anthropic auth
409
+ if (anthropicOAuth) {
410
+ config.auth.profiles['anthropic:default'] = {
411
+ provider: 'anthropic',
412
+ mode: 'oauth',
413
+ };
414
+ } else if (anthropicKey) {
415
+ config.auth.profiles['anthropic:default'].token = anthropicKey;
416
+ }
417
+
418
+ // OpenAI (memory search)
419
+ if (apiAnswers.openaiKey.trim()) {
420
+ config.agents.defaults.memorySearch.remote = { apiKey: apiAnswers.openaiKey.trim() };
421
+ }
422
+
423
+ // Brave Search
424
+ if (apiAnswers.braveKey.trim()) {
425
+ if (!config.plugins) config.plugins = { entries: {} };
426
+ config.plugins.entries = config.plugins.entries || {};
427
+ config.plugins.entries.brave = {
428
+ enabled: true,
429
+ config: { webSearch: { apiKey: apiAnswers.braveKey.trim() } },
430
+ };
431
+ if (!config.tools) config.tools = {};
432
+ config.tools.web = { search: { provider: 'brave' } };
433
+ }
434
+
435
+ // ElevenLabs
436
+ if (apiAnswers.elevenlabsKey.trim()) {
437
+ if (!config.env) config.env = {};
438
+ if (!config.env.vars) config.env.vars = {};
439
+ config.env.vars.ELEVENLABS_API_KEY = apiAnswers.elevenlabsKey.trim();
440
+ }
441
+
442
+ // Deepgram
443
+ if (apiAnswers.deepgramKey.trim()) {
444
+ if (!config.env) config.env = {};
445
+ if (!config.env.vars) config.env.vars = {};
446
+ config.env.vars.DEEPGRAM_API_KEY = apiAnswers.deepgramKey.trim();
447
+ config.env.vars.DEEPGRAM_MODEL = 'nova-3';
448
+ config.env.vars.DEEPGRAM_LANGUAGE = 'ko';
449
+ }
450
+
451
+ // Discord
452
+ config.channels.discord.token = discordAnswers.discordToken.trim() || '';
233
453
  config.channels.discord.dmPolicy = 'allowlist';
234
454
  config.channels.discord.allowFrom = [discordId];
235
- config.commands = { ownerAllowFrom: [discordId] };
236
- writeOutput(outputDir, 'openclaw.json', JSON.stringify(config, null, 2) + '\n');
455
+ config.commands = {
456
+ native: 'auto',
457
+ nativeSkills: 'auto',
458
+ debug: true,
459
+ restart: true,
460
+ ownerAllowFrom: [discordId],
461
+ ownerDisplay: 'raw',
462
+ };
237
463
 
238
- // Create empty .gitkeep in memory folder
464
+ // Discord server policy
465
+ if (discordPolicy === 'mention') {
466
+ config.channels.discord.groupPolicy = 'mention';
467
+ } else if (discordPolicy === 'all') {
468
+ config.channels.discord.groupPolicy = 'all';
469
+ } else {
470
+ config.channels.discord.groupPolicy = 'allowlist';
471
+ const guilds = {};
472
+ for (const server of serverConfigs) {
473
+ const guild = { requireMention: server.requireMention, channels: {} };
474
+ for (const chId of server.channels) {
475
+ guild.channels[chId] = { allow: true, requireMention: server.requireMention, enabled: true };
476
+ }
477
+ guilds[server.guildId] = guild;
478
+ }
479
+ config.channels.discord.guilds = guilds;
480
+ }
481
+
482
+ // Gateway
483
+ config.gateway.port = port;
484
+
485
+ // Discord plugin
486
+ if (!config.plugins) config.plugins = { entries: {} };
487
+ config.plugins.entries.discord = { enabled: true, config: {} };
488
+
489
+ // Streaming
490
+ config.channels.discord.streaming = 'partial';
491
+ config.channels.discord.historyLimit = 16;
492
+ config.channels.discord.intents = { presence: true };
493
+ config.channels.discord.actions = {
494
+ reactions: true, stickers: true, emojiUploads: true, stickerUploads: true,
495
+ polls: true, permissions: true, messages: true, threads: true, pins: true,
496
+ search: true, memberInfo: true, roleInfo: true, roles: true, channelInfo: true,
497
+ voiceStatus: true, events: true, moderation: true, channels: true, presence: true,
498
+ };
499
+
500
+ writeOutput(outputDir, 'openclaw.json', JSON.stringify(config, null, 2) + '\n');
239
501
  writeFileSync(join(outputDir, 'memory', '.gitkeep'), '', 'utf-8');
240
502
 
241
- console.log(`\n✅ ${vars.name} 생성 완료! openclaw start로 시작하세요.`);
503
+ // ─── Summary ───
504
+
505
+ const configured = [];
506
+ const notConfigured = [];
507
+
508
+ if (anthropicKey) configured.push('Anthropic (API 키)');
509
+ else if (anthropicOAuth) configured.push('Anthropic (OAuth 구독 — openclaw auth login 필요)');
510
+ else notConfigured.push('Anthropic 인증');
511
+
512
+ if (apiAnswers.openaiKey.trim()) configured.push('OpenAI (메모리 검색)');
513
+ else notConfigured.push('OpenAI API 키 (메모리 검색)');
514
+
515
+ if (apiAnswers.braveKey.trim()) configured.push('Brave Search (웹 검색)');
516
+ else notConfigured.push('Brave Search API 키 (웹 검색)');
517
+
518
+ if (apiAnswers.elevenlabsKey.trim()) configured.push('ElevenLabs (음성 TTS)');
519
+ if (apiAnswers.deepgramKey.trim()) configured.push('Deepgram (음성 STT)');
520
+
521
+ if (discordAnswers.discordToken.trim()) configured.push('Discord 봇 토큰');
522
+ else notConfigured.push('Discord 봇 토큰');
523
+
524
+ console.log(`\n✅ ${vars.name} 생성 완료!`);
242
525
  console.log(`📁 위치: ${outputDir}`);
243
- console.log('\n생성된 파일:');
244
- console.log(' - SOUL.md (캐릭터 영혼)');
245
- console.log(' - AGENTS.md (행동 규칙)');
246
- console.log(' - IDENTITY.md (정체성)');
247
- console.log(' - USER.md (주인 정보)');
248
- console.log(' - MEMORY.md (장기 기억)');
249
- console.log(' - openclaw.json (설정)');
250
- console.log(' - memory/ (일별 기억 폴더)');
251
- if (answers.moduleKeys.length > 0) {
252
- console.log(' - modules/ (선택한 시스템 모듈)');
526
+
527
+ console.log('\n📋 생성된 파일:');
528
+ console.log(' SOUL.md / AGENTS.md / IDENTITY.md / USER.md / MEMORY.md / openclaw.json');
529
+ if (charAnswers.moduleKeys.length > 0) console.log(' modules/ (선택한 시스템 모듈)');
530
+ if (hasProactiveChat) console.log(' HEARTBEAT.md (선제 대화)');
531
+
532
+ console.log('\n🔑 API 설정 현황:');
533
+ if (configured.length > 0) console.log(' ' + configured.join(', '));
534
+ if (notConfigured.length > 0) console.log(' ❌ 미설정: ' + notConfigured.join(', '));
535
+
536
+ console.log(`\n🤖 모델: ${AI_MODELS[modelAnswers.modelKey]}`);
537
+
538
+ if (discordPolicy === 'mention') console.log('🏠 서버 정책: 멘션(@봇) 시에만 반응');
539
+ else if (discordPolicy === 'all') console.log('🏠 서버 정책: 모든 서버에서 항상 반응');
540
+ else console.log(`🏠 서버 정책: ${serverConfigs.length}개 서버 허용목록`);
541
+
542
+ console.log(`🌐 게이트웨이 포트: ${port}`);
543
+
544
+ console.log('\n🚀 구동 방법:');
545
+ console.log(` $env:OPENCLAW_HOME = "${outputDir}"`);
546
+ console.log(' openclaw gateway run');
547
+
548
+ if (notConfigured.length > 0) {
549
+ console.log('\n⚠️ 미설정 항목은 openclaw.json에서 직접 수정하세요.');
253
550
  }
254
- console.log('\n다음 단계:');
255
- console.log(' 1. openclaw.json에서 API 키와 Discord 토큰을 설정하세요');
256
- console.log(' 2. USER.md에 주인 정보를 추가하세요');
257
- console.log(' 3. SOUL.md를 원하는 대로 커스텀하세요');
258
- console.log(' 4. openclaw start로 시작!\n');
551
+ console.log('');
259
552
  }
260
553
 
261
554
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aptol/openclaw-persona",
3
- "version": "1.0.0",
3
+ "version": "1.2.1",
4
4
  "description": "Create your own AI persona for OpenClaw — interactive CLI character creator with presets, modules, and full customization",
5
5
  "type": "module",
6
6
  "bin": {