openclacky 0.9.30 → 0.9.32

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/lib/clacky/agent/llm_caller.rb +5 -5
  4. data/lib/clacky/agent/memory_updater.rb +1 -1
  5. data/lib/clacky/agent/session_serializer.rb +2 -1
  6. data/lib/clacky/agent/skill_auto_creator.rb +119 -0
  7. data/lib/clacky/agent/skill_evolution.rb +46 -0
  8. data/lib/clacky/agent/skill_manager.rb +8 -0
  9. data/lib/clacky/agent/skill_reflector.rb +97 -0
  10. data/lib/clacky/agent.rb +38 -12
  11. data/lib/clacky/agent_config.rb +10 -1
  12. data/lib/clacky/brand_config.rb +23 -0
  13. data/lib/clacky/cli.rb +1 -1
  14. data/lib/clacky/default_skills/onboard/SKILL.md +15 -7
  15. data/lib/clacky/default_skills/personal-website/publish.rb +1 -1
  16. data/lib/clacky/default_skills/skill-creator/SKILL.md +46 -0
  17. data/lib/clacky/json_ui_controller.rb +0 -4
  18. data/lib/clacky/message_history.rb +0 -12
  19. data/lib/clacky/plain_ui_controller.rb +19 -1
  20. data/lib/clacky/platform_http_client.rb +2 -4
  21. data/lib/clacky/providers.rb +12 -1
  22. data/lib/clacky/server/channel/channel_ui_controller.rb +0 -2
  23. data/lib/clacky/server/http_server.rb +13 -1
  24. data/lib/clacky/server/web_ui_controller.rb +55 -29
  25. data/lib/clacky/tools/shell.rb +91 -170
  26. data/lib/clacky/ui2/ui_controller.rb +100 -93
  27. data/lib/clacky/ui_interface.rb +0 -1
  28. data/lib/clacky/utils/arguments_parser.rb +5 -2
  29. data/lib/clacky/utils/limit_stack.rb +81 -13
  30. data/lib/clacky/version.rb +1 -1
  31. data/lib/clacky/web/app.css +247 -51
  32. data/lib/clacky/web/app.js +11 -3
  33. data/lib/clacky/web/brand.js +21 -3
  34. data/lib/clacky/web/creator.js +13 -2
  35. data/lib/clacky/web/i18n.js +41 -15
  36. data/lib/clacky/web/index.html +38 -20
  37. data/lib/clacky/web/sessions.js +256 -57
  38. data/lib/clacky/web/settings.js +32 -0
  39. data/lib/clacky/web/skills.js +61 -1
  40. metadata +4 -1
@@ -17,6 +17,8 @@ const Brand = (() => {
17
17
  let _testMode = false;
18
18
  // Whether the current license is bound to a user (creator mode).
19
19
  let _userLicensed = false;
20
+ // Whether this installation has an activated brand license (any kind).
21
+ let _branded = false;
20
22
 
21
23
  // Check brand status. Returns true if activation is needed
22
24
  // (caller should defer normal UI boot until activation is done or skipped).
@@ -27,6 +29,7 @@ const Brand = (() => {
27
29
 
28
30
  _testMode = !!data.test_mode;
29
31
  _userLicensed = !!data.user_licensed;
32
+ _branded = !!data.branded;
30
33
 
31
34
  if (!data.branded) return false;
32
35
 
@@ -242,6 +245,13 @@ const Brand = (() => {
242
245
  // Also update browser tab color on mobile
243
246
  const metaTheme = document.querySelector("meta[name='theme-color']");
244
247
  if (metaTheme) metaTheme.setAttribute("content", info.theme_color);
248
+ } else {
249
+ // No brand theme — remove any previously applied overrides to restore defaults
250
+ const root = document.documentElement;
251
+ root.style.removeProperty("--color-accent-primary");
252
+ root.style.removeProperty("--color-accent-hover");
253
+ root.style.removeProperty("--color-button-primary");
254
+ root.style.removeProperty("--color-button-primary-hover");
245
255
  }
246
256
 
247
257
  // header-brand already has onclick="Router.navigate('chat')" in HTML, no extra link needed
@@ -268,6 +278,13 @@ const Brand = (() => {
268
278
  };
269
279
  img.src = info.logo_url;
270
280
  }
281
+ } else {
282
+ // No logo configured — hide logo image and remove has-logo class
283
+ if (logoImg) {
284
+ logoImg.style.display = "none";
285
+ logoImg.src = "";
286
+ }
287
+ if (brandWrap) brandWrap.classList.remove("has-logo");
271
288
  }
272
289
 
273
290
  // Always show brand name text; hide it only when no brand name is set
@@ -277,8 +294,9 @@ const Brand = (() => {
277
294
  logoText.textContent = name;
278
295
  logoText.style.display = "";
279
296
  } else {
280
- // No brand name at all hide the text span
281
- logoText.style.display = "none";
297
+ // No brand configuredshow the default "OpenClacky" name
298
+ logoText.textContent = "OpenClacky";
299
+ logoText.style.display = "";
282
300
  }
283
301
  }
284
302
  }).catch(() => {
@@ -348,5 +366,5 @@ const Brand = (() => {
348
366
  _brandInfoFetching = null;
349
367
  }
350
368
 
351
- return { check, applyBrandName: _applyBrandName, applyHeaderLogo: _applyHeaderLogo, clearBrandCache: _clearBrandCache, goToLicenseInput: _goToLicenseInput, get userLicensed() { return _userLicensed; } };
369
+ return { check, applyBrandName: _applyBrandName, applyHeaderLogo: _applyHeaderLogo, clearBrandCache: _clearBrandCache, goToLicenseInput: _goToLicenseInput, get userLicensed() { return _userLicensed; }, get branded() { return _branded; } };
352
370
  })();
@@ -399,11 +399,14 @@ const Creator = (() => {
399
399
  // ── Public API ────────────────────────────────────────────────────────
400
400
 
401
401
  return {
402
- /** Show/hide the creator section (both divider and item) based on Brand.userLicensed. */
402
+ /** Show/hide the creator section.
403
+ * Hidden for brand consumer users (branded=true, userLicensed=false).
404
+ * Visible for creators (userLicensed=true) and unbranded users. */
403
405
  updateSidebarVisibility() {
404
406
  const section = document.getElementById("creator-section");
405
407
  if (!section) return;
406
- section.style.display = Brand.userLicensed ? "" : "none";
408
+ const isBrandConsumer = Brand.branded && !Brand.userLicensed;
409
+ section.style.display = isBrandConsumer ? "none" : "";
407
410
  },
408
411
 
409
412
  /** Called by Router when the creator panel becomes active. */
@@ -412,6 +415,14 @@ const Creator = (() => {
412
415
  _domWired = true;
413
416
  _wireNewSkillEntry();
414
417
  }
418
+ // Show promo banner and cloud lock for non-licensed users
419
+ const licensed = Brand.userLicensed;
420
+ const banner = document.getElementById("creator-promo-banner");
421
+ const lock = document.getElementById("creator-cloud-lock");
422
+ const list = document.getElementById("creator-cloud-list");
423
+ if (banner) banner.style.display = licensed ? "none" : "";
424
+ if (lock) lock.style.display = licensed ? "none" : "";
425
+ if (list) list.style.display = licensed ? "" : "none";
415
426
  _load();
416
427
  },
417
428
  };
@@ -41,6 +41,7 @@ const I18n = (() => {
41
41
  "chat.image_expired": "Expired",
42
42
  "chat.done": "Done — {{n}} iteration(s), ${{cost}}",
43
43
  "chat.interrupted": "Interrupted.",
44
+ "chat.feedback_hint": "Or type your own answer below ↓",
44
45
  "chat.newMessageHint": "New messages ↓",
45
46
 
46
47
  // ── Session list ──
@@ -147,7 +148,10 @@ const I18n = (() => {
147
148
  "creator.shadow.label": "Local override",
148
149
  "creator.shadow.tooltip": "Local copy shadows a same-named brand skill",
149
150
 
150
- "creator.newSkill.label": "Create a new skill with AI",
151
+ "creator.newSkill.label": "Create a new skill with /skill-creator",
152
+ "creator.promo.text": "Publish your skills & build your own brand on OpenClacky.",
153
+ "creator.promo.link": "Learn more →",
154
+ "creator.cloud.locked": "Brand license required to publish cloud skills.",
151
155
 
152
156
  "skills.btn.new": "New Skill",
153
157
  "skills.btn.create": "Create",
@@ -157,6 +161,7 @@ const I18n = (() => {
157
161
  "skills.tab.my": "My Skills",
158
162
  "skills.tab.brand": "Brand Skills",
159
163
  "skills.empty": "No skills loaded.",
164
+ "skills.empty.createBtn": "Create a new skill with /skill-creator",
160
165
  "skills.noSkills": "No skills",
161
166
  "skills.count": "{{n}} skill(s)",
162
167
  "skills.loading": "Loading…",
@@ -279,7 +284,11 @@ const I18n = (() => {
279
284
  "settings.brand.label.supportQr": "Tech Support",
280
285
  "settings.brand.label.qrHint": "Scan with your phone camera",
281
286
  "settings.brand.btn.change": "Change License Key",
287
+ "settings.brand.btn.unbind": "Unbind License",
282
288
  "settings.brand.confirmRebind": "Warning: all previously installed brand skills will be deleted and cannot be used. Continue?",
289
+ "settings.brand.confirmUnbind": "Are you sure you want to unbind this license? All brand skills will be deleted and this device will no longer have access to branded features.",
290
+ "settings.brand.unbindSuccess": "License unbound successfully.",
291
+ "settings.brand.unbindFailed": "Failed to unbind license. Please try again.",
283
292
  "settings.brand.badge.active": "Active",
284
293
  "settings.brand.badge.warning": "Expiring Soon",
285
294
  "settings.brand.badge.expired": "Expired",
@@ -370,6 +379,7 @@ const I18n = (() => {
370
379
  "chat.image_expired": "已过期",
371
380
  "chat.done": "完成 — {{n}} 步,${{cost}}",
372
381
  "chat.interrupted": "已中断。",
382
+ "chat.feedback_hint": "或在下方输入框自由作答 ↓",
373
383
  "chat.newMessageHint": "有新消息 ↓",
374
384
 
375
385
  // ── Session list ──
@@ -476,7 +486,10 @@ const I18n = (() => {
476
486
  "creator.shadow.label": "本地优先",
477
487
  "creator.shadow.tooltip": "本地版本覆盖了同名的品牌加密技能",
478
488
 
479
- "creator.newSkill.label": "用 AI 创建新技能",
489
+ "creator.newSkill.label": "用 /skill-creator 创建新技能",
490
+ "creator.promo.text": "发布你的 Skill,在 OpenClacky 上打造自己的品牌。",
491
+ "creator.promo.link": "了解更多 →",
492
+ "creator.cloud.locked": "需要品牌授权才能发布云端 Skill。",
480
493
 
481
494
  "skills.btn.new": "新建技能",
482
495
  "skills.btn.create": "创建",
@@ -486,6 +499,7 @@ const I18n = (() => {
486
499
  "skills.tab.my": "我的技能",
487
500
  "skills.tab.brand": "品牌技能",
488
501
  "skills.empty": "暂无技能。",
502
+ "skills.empty.createBtn": "用 /skill-creator 创建新技能",
489
503
  "skills.noSkills": "无技能",
490
504
  "skills.count": "{{n}} 个技能",
491
505
  "skills.loading": "加载中…",
@@ -608,7 +622,11 @@ const I18n = (() => {
608
622
  "settings.brand.label.supportQr": "技术支持",
609
623
  "settings.brand.label.qrHint": "使用手机扫描二维码",
610
624
  "settings.brand.btn.change": "更换授权码",
625
+ "settings.brand.btn.unbind": "解绑授权",
611
626
  "settings.brand.confirmRebind": "警告:所有已安装的历史品牌技能将被删除,无法继续使用。确认继续?",
627
+ "settings.brand.confirmUnbind": "确定要解绑此授权吗?所有品牌技能将被删除,本设备将无法再访问品牌功能。",
628
+ "settings.brand.unbindSuccess": "授权解绑成功。",
629
+ "settings.brand.unbindFailed": "解绑授权失败,请重试。",
612
630
  "settings.brand.badge.active": "已激活",
613
631
  "settings.brand.badge.warning": "即将过期",
614
632
  "settings.brand.badge.expired": "已过期",
@@ -770,24 +788,32 @@ const THINKING_VERBS = {
770
788
  "Reasoning"
771
789
  ],
772
790
  zh: [
791
+ "思考中", // 5x weight (appears 5 times for higher probability)
773
792
  "思考中",
793
+ "思考中",
794
+ "思考中",
795
+ "思考中",
796
+ "琢磨中", // 2x weight
774
797
  "琢磨中",
775
- "推敲中",
776
- "酝酿中",
777
- "沉思中",
778
- "冥想中",
798
+ "思忖中",
779
799
  "盘算中",
780
- "权衡中",
781
- "构思中",
782
- "揣摩中",
783
- "运算中",
800
+ "酝酿中",
801
+ "捋一捋",
802
+ "理理头绪",
803
+ "掂量掂量",
804
+ "寻思中",
805
+ "琢磨琢磨",
806
+ "想想办法",
807
+ "推演中",
808
+ "解析中",
809
+ "拆解中",
810
+ "组装中",
811
+ "梳理中",
812
+ "验证中",
813
+ "演算中",
784
814
  "分析中",
785
- "综合中",
786
- "理解中",
787
- "头脑风暴中",
788
815
  "推理中",
789
- "整理思绪中",
790
- "组织语言中"
816
+ "构思中"
791
817
  ]
792
818
  };
793
819
 
@@ -334,6 +334,15 @@
334
334
  <p class="skills-page-subtitle" data-i18n="creator.subtitle">Manage and publish your skills to the platform</p>
335
335
  </div>
336
336
 
337
+ <!-- Promo banner: shown only to non-licensed users -->
338
+ <div id="creator-promo-banner" style="display:none">
339
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
340
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
341
+ </svg>
342
+ <span data-i18n="creator.promo.text">Publish your skills &amp; build your own brand on OpenClacky.</span>
343
+ <a href="https://www.openclacky.com" target="_blank" data-i18n="creator.promo.link">Learn more →</a>
344
+ </div>
345
+
337
346
  <!-- Section 1: Cloud skills (published) -->
338
347
  <div class="creator-section-block" id="creator-cloud-block">
339
348
  <div class="creator-section-header">
@@ -341,6 +350,13 @@
341
350
  <span class="creator-section-hint" data-i18n="creator.section.cloudHint">Published to the platform</span>
342
351
  </div>
343
352
  <div id="creator-cloud-list" class="creator-list"></div>
353
+ <!-- Lock overlay: shown only to non-licensed users -->
354
+ <div id="creator-cloud-lock" style="display:none">
355
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
356
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
357
+ </svg>
358
+ <span data-i18n="creator.cloud.locked">Brand license required to publish cloud skills.</span>
359
+ </div>
344
360
  </div>
345
361
 
346
362
  <!-- Section 2: Local skills (ready to publish) -->
@@ -361,7 +377,7 @@
361
377
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
362
378
  <path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"/><path d="M12 8v8"/><path d="M8 12h8"/>
363
379
  </svg>
364
- <span data-i18n="creator.newSkill.label">Create a new skill with AI</span>
380
+ <span data-i18n="creator.newSkill.label">Create a new skill with /skill-creator</span>
365
381
  <svg class="creator-new-arrow" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
366
382
  <path d="M5 12h14"/><path d="M12 5l7 7-7 7"/>
367
383
  </svg>
@@ -397,22 +413,33 @@
397
413
  </div>
398
414
  </div>
399
415
 
400
- <!-- Tab bar -->
416
+ <!-- Tab bar with controls -->
401
417
  <div id="skills-tabs">
402
- <button class="skills-tab active" data-tab="my-skills" id="tab-my-skills" data-i18n="skills.tab.my">My Skills</button>
403
- <button class="skills-tab" data-tab="brand-skills" id="tab-brand-skills" data-i18n="skills.tab.brand">Brand Skills</button>
404
- </div>
405
-
406
- <!-- Tab: My Skills -->
407
- <div id="skills-tab-my" class="skills-tab-content">
408
- <!-- Filter bar -->
409
- <div class="skills-filter-bar">
418
+ <div class="skills-tabs-left">
419
+ <button class="skills-tab active" data-tab="my-skills" id="tab-my-skills" data-i18n="skills.tab.my">My Skills</button>
420
+ <button class="skills-tab" data-tab="brand-skills" id="tab-brand-skills" data-i18n="skills.tab.brand">Brand Skills</button>
421
+ </div>
422
+ <div class="skills-tabs-controls">
423
+ <!-- Show system skills toggle (only visible in My Skills tab) -->
410
424
  <label class="skills-filter-toggle" id="label-show-system">
411
425
  <input type="checkbox" id="chk-show-system-skills">
412
426
  <span class="skills-filter-toggle-track"></span>
413
427
  <span class="skills-filter-label" data-i18n="skills.filter.showSystem">Show system skills</span>
414
428
  </label>
429
+ <!-- Refresh brand skills button (only visible in Brand Skills tab) -->
430
+ <button id="btn-refresh-brand-skills" class="btn-brand-skills-refresh" title="Refresh from cloud" style="display:none">
431
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-sm">
432
+ <path d="M21 2v6h-6"/>
433
+ <path d="M3 12a9 9 0 0 1 15-6.7L21 8"/>
434
+ <path d="M3 22v-6h6"/>
435
+ <path d="M21 12a9 9 0 0 1-15 6.7L3 16"/>
436
+ </svg> <span data-i18n="skills.btn.refresh">Refresh</span>
437
+ </button>
415
438
  </div>
439
+ </div>
440
+
441
+ <!-- Tab: My Skills -->
442
+ <div id="skills-tab-my" class="skills-tab-content">
416
443
  <!-- Inline import bar (toggled by Import button) -->
417
444
  <div id="skill-import-bar" class="skill-import-bar" style="display:none">
418
445
  <div class="skill-import-bar-inner">
@@ -442,16 +469,6 @@
442
469
 
443
470
  <!-- Tab: Brand Skills (only visible when license is activated) -->
444
471
  <div id="skills-tab-brand" class="skills-tab-content" style="display:none">
445
- <div id="brand-skills-header">
446
- <button id="btn-refresh-brand-skills" class="btn-brand-skills-refresh" title="Refresh from cloud">
447
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-sm">
448
- <path d="M21 2v6h-6"/>
449
- <path d="M3 12a9 9 0 0 1 15-6.7L21 8"/>
450
- <path d="M3 22v-6h6"/>
451
- <path d="M21 12a9 9 0 0 1-15 6.7L3 16"/>
452
- </svg> <span data-i18n="skills.btn.refresh">Refresh</span>
453
- </button>
454
- </div>
455
472
  <div id="brand-skills-warning" class="brand-skills-warning" style="display:none"></div>
456
473
  <div id="brand-skills-list"></div>
457
474
  </div>
@@ -553,6 +570,7 @@
553
570
  </div>
554
571
  <div class="brand-status-actions">
555
572
  <button id="btn-rebind-license" class="btn-settings-action" data-i18n="settings.brand.btn.change">Change License Key</button>
573
+ <button id="btn-unbind-license" class="btn-settings-action btn-settings-action-danger" data-i18n="settings.brand.btn.unbind">Unbind License</button>
556
574
  </div>
557
575
  </div>
558
576
  <!-- Support contact (shown when support_contact is available) -->