openclacky 1.3.6 → 1.3.7

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/lib/clacky/agent.rb +8 -1
  4. data/lib/clacky/agent_profile.rb +121 -53
  5. data/lib/clacky/brand_config.rb +309 -6
  6. data/lib/clacky/cli.rb +4 -57
  7. data/lib/clacky/default_extensions/coding/ext.yml +18 -0
  8. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +95 -0
  9. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +254 -0
  10. data/lib/clacky/default_extensions/ext-studio/ext.yml +32 -0
  11. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +1269 -0
  12. data/lib/clacky/default_extensions/ext-studio/skills/ext-debug/SKILL.md +71 -0
  13. data/lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md +74 -0
  14. data/lib/clacky/default_extensions/ext-studio/skills/ext-scaffold/SKILL.md +65 -0
  15. data/lib/clacky/default_extensions/general/ext.yml +18 -0
  16. data/lib/clacky/default_extensions/git/ext.yml +12 -0
  17. data/lib/clacky/{default_agents/_panels/git/panel.js → default_extensions/git/panels/git/view.js} +1 -2
  18. data/lib/clacky/default_extensions/meeting/{handler.rb → api/handler.rb} +78 -26
  19. data/lib/clacky/default_extensions/meeting/ext.yml +15 -0
  20. data/lib/clacky/default_extensions/meeting/{meeting.js → panels/meeting/view.js} +266 -89
  21. data/lib/clacky/default_extensions/meeting/skills/meeting-summarizer/SKILL.md +2 -1
  22. data/lib/clacky/default_extensions/time_machine/ext.yml +12 -0
  23. data/lib/clacky/{default_agents/_panels/time_machine/panel.js → default_extensions/time_machine/panels/time_machine/view.js} +58 -47
  24. data/lib/clacky/default_skills/channel-manager/discord_setup.rb +1 -1
  25. data/lib/clacky/default_skills/extend-openclacky/SKILL.md +83 -18
  26. data/lib/clacky/{api_extension.rb → extension/api_extension.rb} +27 -11
  27. data/lib/clacky/extension/api_loader.rb +136 -0
  28. data/lib/clacky/extension/cli_commands.rb +226 -0
  29. data/lib/clacky/{server/api_extension_dispatcher.rb → extension/dispatcher.rb} +22 -9
  30. data/lib/clacky/extension/hook_loader.rb +77 -0
  31. data/lib/clacky/extension/loader.rb +483 -0
  32. data/lib/clacky/extension/packager.rb +226 -0
  33. data/lib/clacky/{patch_loader.rb → extension/patch_loader.rb} +45 -0
  34. data/lib/clacky/extension/scaffold/template_renderer.rb +64 -0
  35. data/lib/clacky/extension/scaffold/templates/full/README.md.erb +48 -0
  36. data/lib/clacky/extension/scaffold/templates/full/agents/designer.md +5 -0
  37. data/lib/clacky/extension/scaffold/templates/full/api/handler.rb.erb +16 -0
  38. data/lib/clacky/extension/scaffold/templates/full/channels/noop.rb.erb +40 -0
  39. data/lib/clacky/extension/scaffold/templates/full/ext.yml.erb +41 -0
  40. data/lib/clacky/extension/scaffold/templates/full/hooks/audit.rb +9 -0
  41. data/lib/clacky/extension/scaffold/templates/full/panels/dashboard/view.js.erb +194 -0
  42. data/lib/clacky/extension/scaffold/templates/full/patches/audit.rb +15 -0
  43. data/lib/clacky/extension/scaffold/templates/full/skills/__slug__-skill/SKILL.md.erb +8 -0
  44. data/lib/clacky/extension/scaffold/templates/hello/api/handler.rb.erb +9 -0
  45. data/lib/clacky/extension/scaffold/templates/hello/ext.yml.erb +12 -0
  46. data/lib/clacky/extension/scaffold/templates/hello/panels/hello/view.js.erb +31 -0
  47. data/lib/clacky/extension/scaffold.rb +55 -0
  48. data/lib/clacky/extension/verifier.rb +196 -0
  49. data/lib/clacky/identity.rb +70 -0
  50. data/lib/clacky/locales/en.rb +19 -1
  51. data/lib/clacky/locales/zh.rb +19 -1
  52. data/lib/clacky/platform_http_client.rb +18 -15
  53. data/lib/clacky/server/channel/extension_adapter_loader.rb +32 -0
  54. data/lib/clacky/server/channel.rb +6 -0
  55. data/lib/clacky/server/http_server.rb +313 -144
  56. data/lib/clacky/server/session_registry.rb +9 -2
  57. data/lib/clacky/skill_loader.rb +28 -1
  58. data/lib/clacky/ui2/ui_controller.rb +9 -6
  59. data/lib/clacky/version.rb +1 -1
  60. data/lib/clacky/web/app.css +748 -573
  61. data/lib/clacky/web/app.js +63 -15
  62. data/lib/clacky/web/auth.js +2 -0
  63. data/lib/clacky/web/components/notify.js +2 -0
  64. data/lib/clacky/web/components/sidebar.js +1 -5
  65. data/lib/clacky/web/core/ext.js +316 -79
  66. data/lib/clacky/web/features/backup/store.js +1 -0
  67. data/lib/clacky/web/features/backup/view.js +0 -1
  68. data/lib/clacky/web/features/brand/view.js +13 -5
  69. data/lib/clacky/web/features/extensions/store.js +184 -0
  70. data/lib/clacky/web/features/extensions/view.js +389 -0
  71. data/lib/clacky/web/features/new-session/store.js +186 -0
  72. data/lib/clacky/web/features/new-session/view.js +319 -0
  73. data/lib/clacky/web/features/share/view.js +63 -14
  74. data/lib/clacky/web/features/skills/store.js +3 -1
  75. data/lib/clacky/web/features/skills/view.js +1 -1
  76. data/lib/clacky/web/features/tasks/view.js +12 -0
  77. data/lib/clacky/web/features/trash/store.js +24 -6
  78. data/lib/clacky/web/features/trash/view.js +46 -9
  79. data/lib/clacky/web/features/workspace/store.js +2 -0
  80. data/lib/clacky/web/features/workspace/view.js +0 -1
  81. data/lib/clacky/web/i18n.js +118 -106
  82. data/lib/clacky/web/index.html +138 -188
  83. data/lib/clacky/web/sessions.js +64 -269
  84. data/lib/clacky/web/settings.js +1 -1
  85. data/lib/clacky/web/skills.js +2 -0
  86. data/lib/clacky/web/theme.js +2 -0
  87. data/lib/clacky/web/ws-dispatcher.js +1 -0
  88. data/lib/clacky/web/ws.js +2 -0
  89. data/lib/clacky.rb +21 -10
  90. metadata +55 -24
  91. data/benchmark/runner.rb +0 -441
  92. data/lib/clacky/api_extension_loader.rb +0 -168
  93. data/lib/clacky/default_agents/SOUL.md +0 -3
  94. data/lib/clacky/default_agents/USER.md +0 -1
  95. data/lib/clacky/default_agents/coding/profile.yml +0 -5
  96. data/lib/clacky/default_agents/coding/webui/.gitkeep +0 -0
  97. data/lib/clacky/default_agents/general/profile.yml +0 -2
  98. data/lib/clacky/default_extensions/meeting/meta.yml +0 -3
  99. data/lib/clacky/web/features/creator/store.js +0 -81
  100. data/lib/clacky/web/features/creator/view.js +0 -380
  101. /data/lib/clacky/{default_agents → default_extensions/coding/agents}/coding/system_prompt.md +0 -0
  102. /data/lib/clacky/{default_agents → default_extensions/general/agents}/general/system_prompt.md +0 -0
  103. /data/lib/clacky/{default_agents/base_prompt.md → prompts/base.md} +0 -0
@@ -31,7 +31,7 @@ function escapeHtml(str) {
31
31
  // Single source of truth for panel visibility and URL hash.
32
32
  //
33
33
  // Views:
34
- // welcome → /#
34
+ // welcome → /#new (new-session landing page)
35
35
  // session/{id} → /#session/{id}
36
36
  // tasks → /#tasks
37
37
  // skills → /#skills
@@ -54,11 +54,12 @@ const PANELS = [
54
54
  "skills-panel",
55
55
  "channels-panel",
56
56
  "mcp-panel",
57
+ "extensions-panel",
57
58
  "trash-panel",
58
59
  "profile-panel",
59
60
  "billing-panel",
60
61
  "settings-panel",
61
- "creator-panel",
62
+ "ext-slot-main-workspace",
62
63
  ];
63
64
 
64
65
  const Router = (() => {
@@ -75,7 +76,12 @@ const Router = (() => {
75
76
  }
76
77
 
77
78
  // Update the URL hash without triggering a hashchange handler loop.
79
+ // Only arm the skip-flag if the hash is actually going to change — otherwise
80
+ // the browser fires no hashchange event and the flag would linger and
81
+ // silently swallow the next real hashchange.
78
82
  function _setHash(hash) {
83
+ const target = hash ? "#" + hash : "";
84
+ if (location.hash === target || (target === "" && location.hash === "")) return;
79
85
  _skipNextHashChange = true;
80
86
  location.hash = hash;
81
87
  }
@@ -84,10 +90,14 @@ const Router = (() => {
84
90
  function _parseHash(hash) {
85
91
  const h = (hash || "").replace(/^#\/?/, "");
86
92
  if (!h) return { view: "welcome", params: {} };
93
+ if (h === "new") return { view: "welcome", params: {} };
87
94
  if (h === "tasks") return { view: "tasks", params: {} };
88
95
  if (h === "skills") return { view: "skills", params: {} };
89
96
  if (h === "channels") return { view: "channels", params: {} };
90
97
  if (h === "mcp") return { view: "mcp", params: {} };
98
+ if (h === "extensions") return { view: "extensions", params: {} };
99
+ const mExtDetail = h.match(/^extensions\/(.+)$/);
100
+ if (mExtDetail) return { view: "extensions", params: { detailId: mExtDetail[1] } };
91
101
  if (h === "trash") return { view: "trash", params: {} };
92
102
  if (h === "profile") return { view: "profile", params: {} };
93
103
  // Legacy: #memories redirects to #profile (memories are now merged into
@@ -95,7 +105,8 @@ const Router = (() => {
95
105
  if (h === "memories") return { view: "profile", params: {} };
96
106
  if (h === "billing") return { view: "billing", params: {} };
97
107
  if (h === "settings") return { view: "settings", params: {} };
98
- if (h === "creator") return { view: "creator", params: {} };
108
+ const mExt = h.match(/^ext\/(.+)$/);
109
+ if (mExt) return { view: "ext-workspace", params: { id: mExt[1] } };
99
110
  const m = h.match(/^session\/(.+)$/);
100
111
  if (m) return { view: "session", params: { id: m[1] } };
101
112
  return { view: "welcome", params: {} };
@@ -109,10 +120,10 @@ const Router = (() => {
109
120
  skills: "skills-sidebar-item",
110
121
  channels: "channels-sidebar-item",
111
122
  mcp: "mcp-sidebar-item",
123
+ extensions: "extensions-sidebar-item",
112
124
  trash: "trash-sidebar-item",
113
125
  profile: "profile-sidebar-item",
114
126
  billing: "billing-sidebar-item",
115
- creator: "creator-sidebar-item",
116
127
  };
117
128
 
118
129
  // Remove active highlight from all Router-managed sidebar items.
@@ -121,6 +132,10 @@ const Router = (() => {
121
132
  const el = $(id);
122
133
  if (el) el.classList.remove("active");
123
134
  });
135
+ // Extension workspace nav items tag themselves with data-ext-workspace;
136
+ // the Router owns their active highlight the same way as built-in items.
137
+ document.querySelectorAll("[data-ext-workspace].active")
138
+ .forEach(el => el.classList.remove("active"));
124
139
  }
125
140
 
126
141
  // Core: apply a view change. Called both from navigate() and hashchange.
@@ -238,6 +253,18 @@ const Router = (() => {
238
253
  Sessions.renderList();
239
254
  break;
240
255
 
256
+ case "extensions":
257
+ $("extensions-panel").style.display = "flex";
258
+ if (params.detailId) {
259
+ _setHash(`extensions/${params.detailId}`);
260
+ Extensions.onPanelShow({ detailId: params.detailId });
261
+ } else {
262
+ _setHash("extensions");
263
+ Extensions.onPanelShow();
264
+ }
265
+ Sessions.renderList();
266
+ break;
267
+
241
268
  case "trash":
242
269
  _setHash("trash");
243
270
  $("trash-panel").style.display = "flex";
@@ -259,13 +286,6 @@ const Router = (() => {
259
286
  Sessions.renderList();
260
287
  break;
261
288
 
262
- case "creator":
263
- _setHash("creator");
264
- $("creator-panel").style.display = "flex";
265
- Creator.onPanelShow();
266
- Sessions.renderList();
267
- break;
268
-
269
289
  case "settings":
270
290
  _setHash("settings");
271
291
  $("settings-panel").style.display = "";
@@ -284,10 +304,26 @@ const Router = (() => {
284
304
  $("onboard-panel").style.display = "flex";
285
305
  break;
286
306
 
307
+ case "ext-workspace": {
308
+ const ws = window.Clacky && Clacky.ext && Clacky.ext._getWorkspace(params.id);
309
+ if (!ws) { await _apply("welcome"); return; }
310
+ _setHash(`ext/${params.id}`);
311
+ const container = $("ext-slot-main-workspace");
312
+ container.replaceChildren();
313
+ container.style.display = "block";
314
+ try { ws.render(container, {}); }
315
+ catch (err) { console.error("ext workspace render failed:", err); }
316
+ Sessions.renderList();
317
+ break;
318
+ }
319
+
287
320
  default: // "welcome"
288
- _setHash("");
321
+ _setHash("new");
289
322
  $("welcome").style.display = "";
290
323
  Sessions.renderList();
324
+ if (window.NewSessionView && NewSessionView.onPanelShow) {
325
+ NewSessionView.onPanelShow();
326
+ }
291
327
  break;
292
328
  }
293
329
 
@@ -296,6 +332,9 @@ const Router = (() => {
296
332
  _clearSidebarActive();
297
333
  const activeItem = SIDEBAR_ITEMS[view];
298
334
  if (activeItem) $(activeItem)?.classList.add("active");
335
+ if (view === "ext-workspace" && params.id) {
336
+ document.querySelector(`[data-ext-workspace="${params.id}"]`)?.classList.add("active");
337
+ }
299
338
  }
300
339
 
301
340
  // Listen for browser back/forward (or manual hash edits).
@@ -308,6 +347,14 @@ const Router = (() => {
308
347
  _apply(view, params).catch(err => console.error("Router._apply failed:", err));
309
348
  });
310
349
 
350
+ // Extensions request navigation via a CustomEvent so ext.js has no direct
351
+ // dependency on Router. Detail is { view, params } — same shape as navigate().
352
+ document.addEventListener("clacky:ext:navigate", (e) => {
353
+ const { view, params } = e.detail || {};
354
+ if (!view) return;
355
+ _apply(view, params || {}).catch(err => console.error("Router._apply failed:", err));
356
+ });
357
+
311
358
  return {
312
359
  get current() { return _current; },
313
360
  get params() { return _params; },
@@ -328,6 +375,8 @@ const Router = (() => {
328
375
  };
329
376
  })();
330
377
 
378
+ Clacky.Router = Router;
379
+
331
380
  // ── Modal utility ─────────────────────────────────────────────────────────
332
381
  const Modal = (() => {
333
382
  /** Show a yes/no confirmation dialog. Returns a Promise<boolean>. */
@@ -484,6 +533,8 @@ const Modal = (() => {
484
533
  return { confirm, confirmOnce, prompt, rename };
485
534
  })();
486
535
 
536
+ Clacky.Modal = Modal;
537
+
487
538
  // ── Toast helper ──────────────────────────────────────────────────────────
488
539
  // Non-blocking notification stack. Replaces alert() for success/error/info
489
540
  // feedback. Supports an optional action button (e.g. "Go check").
@@ -700,9 +751,6 @@ window.bootAfterBrand = async function() {
700
751
  // key_setup blocks boot entirely; onboard.js calls _bootUI() when done.
701
752
  if (needsOnboard && phase === "key_setup") return;
702
753
 
703
- // Show creator sidebar entry if user_licensed
704
- Creator.updateSidebarVisibility();
705
-
706
754
  // Initialize skill autocomplete
707
755
  SkillAC.init();
708
756
 
@@ -148,3 +148,5 @@ const Auth = (() => {
148
148
  get passed() { return _authPassed; },
149
149
  };
150
150
  })();
151
+
152
+ Clacky.Auth = Auth;
@@ -146,6 +146,8 @@ const Notify = (() => {
146
146
  return { init, toggle, enabled, setEnabled, onTaskFinished };
147
147
  })();
148
148
 
149
+ Clacky.Notify = Notify;
150
+
149
151
  // Initialize on load (button binding + initial icon state).
150
152
  if (document.readyState === "loading") {
151
153
  document.addEventListener("DOMContentLoaded", () => Notify.init());
@@ -23,16 +23,12 @@ const Sidebar = (() => {
23
23
  document.getElementById("skills-sidebar-item").addEventListener("click", () => Router.navigate("skills"));
24
24
  document.getElementById("channels-sidebar-item").addEventListener("click", () => Router.navigate("channels"));
25
25
  document.getElementById("mcp-sidebar-item").addEventListener("click", () => Router.navigate("mcp"));
26
+ document.getElementById("extensions-sidebar-item").addEventListener("click", () => Router.navigate("extensions"));
26
27
  document.getElementById("trash-sidebar-item").addEventListener("click", () => Router.navigate("trash"));
27
28
  document.getElementById("profile-sidebar-item").addEventListener("click", () => Router.navigate("profile"));
28
29
  document.getElementById("billing-sidebar-item").addEventListener("click", () => Router.navigate("billing"));
29
30
 
30
31
  // memories-sidebar-item is retained as a hidden legacy placeholder — no click handler.
31
-
32
- // creator-sidebar-item is conditionally rendered (only when user_licensed).
33
- // This ?. is a legitimate business guard, not defensive padding — the
34
- // element genuinely may not exist in the DOM for unlicensed users.
35
- document.getElementById("creator-sidebar-item")?.addEventListener("click", () => Router.navigate("creator"));
36
32
  }
37
33
 
38
34
  return { init };