openclacky 1.0.2 → 1.0.4

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/benchmark/fixtures/sample_project/Gemfile +3 -0
  4. data/benchmark/fixtures/sample_project/lib/api_handler.rb +32 -0
  5. data/benchmark/fixtures/sample_project/lib/order_calculator.rb +23 -0
  6. data/benchmark/fixtures/sample_project/lib/user_renderer.rb +20 -0
  7. data/benchmark/fixtures/sample_project/spec/order_calculator_spec.rb +20 -0
  8. data/benchmark/results/EVALUATION_REPORT.md +165 -0
  9. data/benchmark/results/baseline_20260511_174424.json +128 -0
  10. data/benchmark/results/report_20260511_175256.json +271 -0
  11. data/benchmark/results/report_20260511_175444.json +271 -0
  12. data/benchmark/results/treatment_20260511_175103.json +130 -0
  13. data/benchmark/runner.rb +441 -0
  14. data/docs/proposals/2026-05-11-system-prompt-alignment.md +325 -0
  15. data/docs/proposals/2026-05-12-memory-mechanism-optimization.md +89 -0
  16. data/lib/clacky/agent/cost_tracker.rb +8 -2
  17. data/lib/clacky/agent/llm_caller.rb +218 -0
  18. data/lib/clacky/agent/memory_updater.rb +41 -30
  19. data/lib/clacky/agent/message_compressor.rb +15 -4
  20. data/lib/clacky/agent/message_compressor_helper.rb +41 -2
  21. data/lib/clacky/agent/skill_manager.rb +5 -2
  22. data/lib/clacky/agent/skill_reflector.rb +10 -1
  23. data/lib/clacky/agent/tool_registry.rb +109 -0
  24. data/lib/clacky/agent.rb +20 -0
  25. data/lib/clacky/agent_config.rb +17 -0
  26. data/lib/clacky/cli.rb +65 -0
  27. data/lib/clacky/client.rb +15 -0
  28. data/lib/clacky/default_agents/base_prompt.md +20 -20
  29. data/lib/clacky/default_agents/coding/system_prompt.md +51 -1
  30. data/lib/clacky/default_skills/channel-setup/SKILL.md +113 -5
  31. data/lib/clacky/default_skills/channel-setup/import_lark_skills.rb +97 -0
  32. data/lib/clacky/default_skills/onboard/SKILL.md +1 -1
  33. data/lib/clacky/default_skills/persist-memory/SKILL.md +59 -0
  34. data/lib/clacky/providers.rb +48 -6
  35. data/lib/clacky/server/channel/adapters/weixin/adapter.rb +7 -0
  36. data/lib/clacky/server/channel/channel_manager.rb +91 -0
  37. data/lib/clacky/server/discover.rb +77 -0
  38. data/lib/clacky/server/epipe_safe_io.rb +105 -0
  39. data/lib/clacky/server/http_server.rb +121 -41
  40. data/lib/clacky/server/server_master.rb +6 -0
  41. data/lib/clacky/skill.rb +30 -0
  42. data/lib/clacky/utils/file_processor.rb +71 -0
  43. data/lib/clacky/version.rb +1 -1
  44. data/lib/clacky/web/app.css +58 -22
  45. data/lib/clacky/web/i18n.js +4 -2
  46. data/lib/clacky/web/sessions.js +29 -17
  47. metadata +33 -2
@@ -1492,7 +1492,7 @@ const Sessions = (() => {
1492
1492
  : "";
1493
1493
 
1494
1494
  // Pin icon (always visible for pinned sessions)
1495
- const pinIcon = s.pinned ? `<span class="session-pin-icon">📌</span>` : "";
1495
+ const pinIcon = s.pinned ? `<span class="session-pin-icon"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(45deg);display:block"><line x1="12" y1="17" x2="12" y2="22"/><path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z"/></svg></span>` : "";
1496
1496
 
1497
1497
  // Status dot: only rendered for non-idle states. Idle is the default
1498
1498
  // state for 95% of sessions and doesn't deserve a persistent visual marker.
@@ -1505,7 +1505,7 @@ const Sessions = (() => {
1505
1505
  <div class="session-name">${dotHtml}<span class="session-name__text">${escapeHtml(displayName)}</span>${badgeHtml}${codingBadgeHtml}${pinIcon}</div>
1506
1506
  <div class="session-meta">${metaText}</div>
1507
1507
  </div>
1508
- <button class="session-actions-btn" title="Actions">⋯</button>`;
1508
+ <button class="session-actions-btn" title="Actions"><svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="2.5" cy="7" r="1.2" fill="currentColor"/><circle cx="7" cy="7" r="1.2" fill="currentColor"/><circle cx="11.5" cy="7" r="1.2" fill="currentColor"/></svg></button>`;
1509
1509
 
1510
1510
  // Use a click timer to distinguish single-click (select) from double-click (old rename behavior).
1511
1511
  let clickTimer = null;
@@ -1618,7 +1618,7 @@ const Sessions = (() => {
1618
1618
  // session when renderList() forces scroll-to-active.
1619
1619
  const sidebarList = document.getElementById("sidebar-list");
1620
1620
  const savedScrollTop = sidebarList ? sidebarList.scrollTop : 0;
1621
- Sessions.renderList();
1621
+ Sessions.renderList({ skipScrollToActive: true });
1622
1622
 
1623
1623
  try {
1624
1624
  // Cursor: oldest created_at in the current list, EXCLUDING pinned
@@ -1651,7 +1651,7 @@ const Sessions = (() => {
1651
1651
  console.error("loadMore error:", e);
1652
1652
  } finally {
1653
1653
  _loadingMore = false;
1654
- Sessions.renderList();
1654
+ Sessions.renderList({ skipScrollToActive: true });
1655
1655
  // Restore scroll position so the user stays where they were
1656
1656
  if (sidebarList) sidebarList.scrollTop = savedScrollTop;
1657
1657
  }
@@ -1826,7 +1826,7 @@ const Sessions = (() => {
1826
1826
 
1827
1827
  // ── Rendering ─────────────────────────────────────────────────────────
1828
1828
 
1829
- renderList() {
1829
+ renderList({ skipScrollToActive = false } = {}) {
1830
1830
  // Sort helper: pinned first, then newest-first by created_at
1831
1831
  const byPinnedAndTime = (a, b) => {
1832
1832
  // Pinned sessions always come first
@@ -1877,15 +1877,17 @@ const Sessions = (() => {
1877
1877
  if (_hasMore) list.appendChild(_makeLoadMoreBtn());
1878
1878
 
1879
1879
  // Scroll active session into view so the sidebar always shows the current session.
1880
- const activeEl = list.querySelector(".session-item.active");
1881
- if (activeEl) {
1882
- // If the active session is the very first item, scroll to top of the sidebar
1883
- // container so sticky headers / expanded panels don't obscure it.
1884
- if (activeEl === list.firstElementChild) {
1885
- const sidebarList = document.getElementById("sidebar-list");
1886
- if (sidebarList) sidebarList.scrollTop = 0;
1887
- } else {
1888
- activeEl.scrollIntoView({ block: "nearest" });
1880
+ if (!skipScrollToActive) {
1881
+ const activeEl = list.querySelector(".session-item.active");
1882
+ if (activeEl) {
1883
+ // If the active session is the very first item, scroll to top of the sidebar
1884
+ // container so sticky headers / expanded panels don't obscure it.
1885
+ if (activeEl === list.firstElementChild) {
1886
+ const sidebarList = document.getElementById("sidebar-list");
1887
+ if (sidebarList) sidebarList.scrollTop = 0;
1888
+ } else {
1889
+ activeEl.scrollIntoView({ block: "nearest" });
1890
+ }
1889
1891
  }
1890
1892
  }
1891
1893
  },
@@ -1961,17 +1963,27 @@ const Sessions = (() => {
1961
1963
  // Close any existing menu first
1962
1964
  Sessions._closeActionsMenu();
1963
1965
 
1966
+ // Lucide-style stroked icons to match the rest of the UI
1967
+ const iconPin = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="transform:rotate(45deg);display:block"><path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>`;
1968
+ const iconRename = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4z"/></svg>`;
1969
+ const iconTrash = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>`;
1970
+
1971
+ const pinLabel = session.pinned ? I18n.t("sessions.actions.unpin") : I18n.t("sessions.actions.pin");
1972
+
1964
1973
  const menu = document.createElement("div");
1965
1974
  menu.className = "session-actions-menu";
1966
1975
  menu.innerHTML = `
1967
1976
  <div class="session-actions-menu-item" data-action="pin">
1968
- ${session.pinned ? "📌 " + I18n.t("sessions.actions.unpin") : "📌 " + I18n.t("sessions.actions.pin")}
1977
+ <span class="session-actions-menu-icon">${iconPin}</span>
1978
+ <span class="session-actions-menu-label">${escapeHtml(pinLabel)}</span>
1969
1979
  </div>
1970
1980
  <div class="session-actions-menu-item" data-action="rename">
1971
- ✏️ ${I18n.t("sessions.actions.rename")}
1981
+ <span class="session-actions-menu-icon">${iconRename}</span>
1982
+ <span class="session-actions-menu-label">${escapeHtml(I18n.t("sessions.actions.rename"))}</span>
1972
1983
  </div>
1973
1984
  <div class="session-actions-menu-item session-actions-menu-item--danger" data-action="delete">
1974
- 🗑️ ${I18n.t("sessions.actions.delete")}
1985
+ <span class="session-actions-menu-icon">${iconTrash}</span>
1986
+ <span class="session-actions-menu-label">${escapeHtml(I18n.t("sessions.actions.delete"))}</span>
1975
1987
  </div>
1976
1988
  `;
1977
1989
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openclacky
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - windy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-07 00:00:00.000000000 Z
11
+ date: 2026-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -156,6 +156,20 @@ dependencies:
156
156
  - - ">="
157
157
  - !ruby/object:Gem::Version
158
158
  version: 0.1.0
159
+ - !ruby/object:Gem::Dependency
160
+ name: logger
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '1.4'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '1.4'
159
173
  - !ruby/object:Gem::Dependency
160
174
  name: websocket
161
175
  requirement: !ruby/object:Gem::Requirement
@@ -269,6 +283,17 @@ files:
269
283
  - LICENSE.txt
270
284
  - README.md
271
285
  - Rakefile
286
+ - benchmark/fixtures/sample_project/Gemfile
287
+ - benchmark/fixtures/sample_project/lib/api_handler.rb
288
+ - benchmark/fixtures/sample_project/lib/order_calculator.rb
289
+ - benchmark/fixtures/sample_project/lib/user_renderer.rb
290
+ - benchmark/fixtures/sample_project/spec/order_calculator_spec.rb
291
+ - benchmark/results/EVALUATION_REPORT.md
292
+ - benchmark/results/baseline_20260511_174424.json
293
+ - benchmark/results/report_20260511_175256.json
294
+ - benchmark/results/report_20260511_175444.json
295
+ - benchmark/results/treatment_20260511_175103.json
296
+ - benchmark/runner.rb
272
297
  - bin/clacky
273
298
  - bin/clarky
274
299
  - bin/openclacky
@@ -286,6 +311,8 @@ files:
286
311
  - docs/install-script-simplification.md
287
312
  - docs/memory-architecture.md
288
313
  - docs/openclacky_cloud_api_reference.md
314
+ - docs/proposals/2026-05-11-system-prompt-alignment.md
315
+ - docs/proposals/2026-05-12-memory-mechanism-optimization.md
289
316
  - docs/security-design.md
290
317
  - docs/session-management-redesign.md
291
318
  - docs/session-skill-invocation.md
@@ -338,6 +365,7 @@ files:
338
365
  - lib/clacky/default_skills/browser-setup/SKILL.md
339
366
  - lib/clacky/default_skills/channel-setup/SKILL.md
340
367
  - lib/clacky/default_skills/channel-setup/feishu_setup.rb
368
+ - lib/clacky/default_skills/channel-setup/import_lark_skills.rb
341
369
  - lib/clacky/default_skills/channel-setup/weixin_setup.rb
342
370
  - lib/clacky/default_skills/code-explorer/SKILL.md
343
371
  - lib/clacky/default_skills/cron-task-creator/SKILL.md
@@ -348,6 +376,7 @@ files:
348
376
  - lib/clacky/default_skills/onboard/SKILL.md
349
377
  - lib/clacky/default_skills/onboard/scripts/import_external_skills.rb
350
378
  - lib/clacky/default_skills/onboard/scripts/install_builtin_skills.rb
379
+ - lib/clacky/default_skills/persist-memory/SKILL.md
351
380
  - lib/clacky/default_skills/personal-website/SKILL.md
352
381
  - lib/clacky/default_skills/personal-website/publish.rb
353
382
  - lib/clacky/default_skills/product-help/SKILL.md
@@ -395,6 +424,8 @@ files:
395
424
  - lib/clacky/server/channel/channel_config.rb
396
425
  - lib/clacky/server/channel/channel_manager.rb
397
426
  - lib/clacky/server/channel/channel_ui_controller.rb
427
+ - lib/clacky/server/discover.rb
428
+ - lib/clacky/server/epipe_safe_io.rb
398
429
  - lib/clacky/server/http_server.rb
399
430
  - lib/clacky/server/scheduler.rb
400
431
  - lib/clacky/server/server_master.rb