openclacky 1.4.1 → 1.5.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/lib/clacky/agent/llm_caller.rb +3 -3
  4. data/lib/clacky/agent/session_serializer.rb +39 -5
  5. data/lib/clacky/brand_config.rb +49 -7
  6. data/lib/clacky/client.rb +22 -16
  7. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +135 -84
  8. data/lib/clacky/default_extensions/ext-studio/ext.yml +2 -4
  9. data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +522 -0
  10. data/lib/clacky/extension/api_extension.rb +5 -6
  11. data/lib/clacky/extension/loader.rb +16 -2
  12. data/lib/clacky/extension/scaffold/templates/full/api/handler.rb.erb +16 -0
  13. data/lib/clacky/mcp/http_transport.rb +1 -1
  14. data/lib/clacky/providers.rb +109 -1
  15. data/lib/clacky/server/http_server.rb +115 -19
  16. data/lib/clacky/server/session_registry.rb +9 -0
  17. data/lib/clacky/utils/model_pricing.rb +36 -0
  18. data/lib/clacky/version.rb +1 -1
  19. data/lib/clacky/web/app.css +321 -32
  20. data/lib/clacky/web/app.js +29 -6
  21. data/lib/clacky/web/components/onboard.js +7 -3
  22. data/lib/clacky/web/features/extensions/store.js +54 -2
  23. data/lib/clacky/web/features/extensions/view.js +40 -5
  24. data/lib/clacky/web/features/new-session/store.js +8 -4
  25. data/lib/clacky/web/features/version/view.js +5 -1
  26. data/lib/clacky/web/i18n.js +52 -18
  27. data/lib/clacky/web/index.html +51 -5
  28. data/lib/clacky/web/sessions.js +40 -11
  29. data/lib/clacky/web/settings.js +22 -3
  30. data/lib/clacky/web/theme.js +27 -58
  31. data/scripts/build/src/uninstall.sh.cc +1 -1
  32. metadata +2 -5
  33. data/lib/clacky/default_extensions/ext-studio/skills/ext-debug/SKILL.md +0 -71
  34. data/lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md +0 -73
  35. data/lib/clacky/default_extensions/ext-studio/skills/ext-scaffold/SKILL.md +0 -65
  36. data/lib/clacky/default_skills/extend-openclacky/SKILL.md +0 -106
@@ -1,12 +1,12 @@
1
1
  // theme.js — Theme switcher module
2
2
  //
3
- // Behavior:
4
- // Default follows the OS preference (prefers-color-scheme).
5
- // • User can manually override via the 🌓 header button — persisted
6
- // to localStorage. Once overridden, the explicit choice wins.
7
- // Choosing the theme that happens to match the current OS setting
8
- // clears the override, restoring "auto-follow-system" mode.
9
- // Responds to live OS theme changes when no manual override is set.
3
+ // Single storage key "clacky-theme" stores one of: light | dark | dim | warm
4
+ // All four values are first-class themes — no separate "bg-theme" dimension.
5
+ //
6
+ // Usage:
7
+ // Theme.init() — call once on page load
8
+ // Theme.apply("dark"|…) — set any theme explicitly
9
+ // Theme.current() — returns effective data-theme value
10
10
 
11
11
  const Theme = (() => {
12
12
  const STORAGE_KEY = "clacky-theme";
@@ -18,53 +18,27 @@ const Theme = (() => {
18
18
  ? "dark" : "light";
19
19
  }
20
20
 
21
+ function _effectiveTheme() {
22
+ return localStorage.getItem(STORAGE_KEY) || _systemTheme();
23
+ }
24
+
21
25
  function _applyAttr(theme) {
22
26
  document.documentElement.setAttribute(ATTR_NAME, theme);
23
- _updateToggleIcon(theme);
27
+ _syncBgCards();
24
28
  window.dispatchEvent(new CustomEvent("clacky-theme-change", { detail: { theme } }));
25
29
  }
26
30
 
27
- function _updateToggleIcon(theme) {
28
- const headerToggle = document.getElementById("theme-toggle-header");
29
- if (headerToggle) {
30
- // Icon shows what you'd switch TO, not what you are on.
31
- if (theme === "light") {
32
- // In light mode → show moon (click to go dark)
33
- headerToggle.title = I18n.t("header.theme.toDark");
34
- headerToggle.innerHTML = `<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">
35
- <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>
36
- </svg>`;
37
- } else {
38
- // In dark mode → show sun (click to go light)
39
- headerToggle.title = I18n.t("header.theme.toLight");
40
- headerToggle.innerHTML = `<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">
41
- <circle cx="12" cy="12" r="4"/>
42
- <path d="M12 2v2"/>
43
- <path d="M12 20v2"/>
44
- <path d="m4.93 4.93 1.41 1.41"/>
45
- <path d="m17.66 17.66 1.41 1.41"/>
46
- <path d="M2 12h2"/>
47
- <path d="M20 12h2"/>
48
- <path d="m6.34 17.66-1.41 1.41"/>
49
- <path d="m19.07 4.93-1.41 1.41"/>
50
- </svg>`;
51
- }
52
- }
53
-
54
- // Legacy settings toggle (kept for compatibility)
55
- const toggle = document.getElementById("theme-toggle");
56
- if (toggle) {
57
- const icon = theme === "light" ? "🌙" : "☀️";
58
- const label = theme === "light" ? "Dark" : "Light";
59
- toggle.innerHTML = `<span class="theme-icon">${icon}</span><span>${label}</span>`;
60
- }
31
+ // Update active state on all .settings-bg-theme-card buttons.
32
+ function _syncBgCards() {
33
+ const effective = current();
34
+ document.querySelectorAll(".settings-bg-theme-card").forEach(btn => {
35
+ btn.classList.toggle("active", btn.dataset.bgTheme === effective);
36
+ });
61
37
  }
62
38
 
63
39
  // ── Public API ───────────────────────────────────────────────────────
64
40
  function init() {
65
- const saved = localStorage.getItem(STORAGE_KEY);
66
- const effective = saved || _systemTheme();
67
- _applyAttr(effective);
41
+ _applyAttr(_effectiveTheme());
68
42
 
69
43
  // Live-follow OS changes when user has no manual override.
70
44
  if (window.matchMedia) {
@@ -75,36 +49,31 @@ const Theme = (() => {
75
49
  }
76
50
  };
77
51
  if (mq.addEventListener) mq.addEventListener("change", onChange);
78
- else if (mq.addListener) mq.addListener(onChange); // Safari < 14
52
+ else if (mq.addListener) mq.addListener(onChange); // Safari < 14
79
53
  }
80
-
81
- // Re-apply title text when language changes.
82
- document.addEventListener("langchange", () => _updateToggleIcon(current()));
83
54
  }
84
55
 
85
- // Explicit apply (used by toggle). Persists the choice unless it equals
86
- // the OS preference — in which case we clear the override so subsequent
87
- // OS theme changes once again propagate.
56
+ // Apply any theme (light | dark | dim | warm).
88
57
  function apply(theme) {
89
- _applyAttr(theme);
58
+ // If the chosen theme matches OS default, no need to persist
90
59
  if (theme === _systemTheme()) {
91
60
  localStorage.removeItem(STORAGE_KEY);
92
61
  } else {
93
62
  localStorage.setItem(STORAGE_KEY, theme);
94
63
  }
64
+ _applyAttr(theme);
95
65
  }
96
66
 
97
- function toggle() {
98
- const current = document.documentElement.getAttribute(ATTR_NAME) || _systemTheme();
99
- const next = current === "dark" ? "light" : "dark";
100
- apply(next);
67
+ // Keep applyBg as alias for backward compat (settings.js calls it)
68
+ function applyBg(theme) {
69
+ apply(theme);
101
70
  }
102
71
 
103
72
  function current() {
104
73
  return document.documentElement.getAttribute(ATTR_NAME) || _systemTheme();
105
74
  }
106
75
 
107
- return { init, toggle, apply, current };
76
+ return { init, apply, applyBg, current };
108
77
  })();
109
78
 
110
79
  // Initialize theme on page load
@@ -34,7 +34,7 @@ uninstall_gem() {
34
34
  command_exists gem || return 1
35
35
  if gem list -i openclacky >/dev/null 2>&1; then
36
36
  print_step "Uninstalling via RubyGems..."
37
- gem uninstall openclacky -x
37
+ gem uninstall openclacky -a -x
38
38
  else
39
39
  print_info "Gem 'openclacky' not found (already removed)"
40
40
  fi
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openclacky
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - windy
@@ -371,9 +371,7 @@ files:
371
371
  - lib/clacky/default_extensions/ext-studio/api/handler.rb
372
372
  - lib/clacky/default_extensions/ext-studio/ext.yml
373
373
  - lib/clacky/default_extensions/ext-studio/panels/studio/view.js
374
- - lib/clacky/default_extensions/ext-studio/skills/ext-debug/SKILL.md
375
- - lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md
376
- - lib/clacky/default_extensions/ext-studio/skills/ext-scaffold/SKILL.md
374
+ - lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md
377
375
  - lib/clacky/default_extensions/general/agents/general/avatar.png
378
376
  - lib/clacky/default_extensions/general/agents/general/system_prompt.md
379
377
  - lib/clacky/default_extensions/general/ext.yml
@@ -406,7 +404,6 @@ files:
406
404
  - lib/clacky/default_skills/cron-task-creator/SKILL.md
407
405
  - lib/clacky/default_skills/cron-task-creator/evals/evals.json
408
406
  - lib/clacky/default_skills/deploy/SKILL.md
409
- - lib/clacky/default_skills/extend-openclacky/SKILL.md
410
407
  - lib/clacky/default_skills/mcp-manager/SKILL.md
411
408
  - lib/clacky/default_skills/media-gen/SKILL.md
412
409
  - lib/clacky/default_skills/media-gen/scripts/video_seq.sh
@@ -1,71 +0,0 @@
1
- ---
2
- name: ext-debug
3
- description: Debug an OpenClacky extension that won't load, throws errors, or whose panel/api/skill/agent isn't showing up. Use when the user reports something broken with their extension, when `clacky ext verify` reports issues, or when a change didn't take effect. Reads structured verify errors and fixes manifest and file problems.
4
- ---
5
-
6
- # Extension Debug
7
-
8
- Diagnose and fix a broken extension. Your primary instrument is `clacky ext verify` —
9
- it's a compiler for extensions: every issue is structured with a `code`, `message`,
10
- the offending `file`, and a `hint` telling you how to fix it.
11
-
12
- ## Step 1 — Run verify
13
-
14
- ```
15
- clacky ext verify
16
- ```
17
-
18
- Read the output line by line. `[OK]` lines confirm resolved units. `[ERR]` blocks a
19
- load; `[WARN]` is advisory. Each issue looks like:
20
-
21
- ```
22
- [ERR] <ext> <unit> (<code>) — <message> [<file>]
23
- hint: <how to fix>
24
- ```
25
-
26
- ## Step 2 — Fix by error code
27
-
28
- - **`loader.error`** — a file the manifest points at is missing or the container
29
- couldn't be parsed. Check the `file` path exists and `ext.yml` is valid YAML. A skill
30
- needs `SKILL.md` under `skills/<id>/`; an agent needs its `prompt` file; a panel needs
31
- its `view` file; api needs `api/handler.rb`.
32
- - **`schema.unknown_contributes`** — a top-level key under `contributes:` isn't one of
33
- `panels api skills agents channels patches hooks`. Fix the spelling.
34
- - **`schema.unknown_field` / `schema.unknown_key`** — a unit has a field that isn't
35
- allowed for its type. Remove or rename it. Allowed fields:
36
- - panel: `id title title_zh description description_zh view order attach`
37
- - api: `id handler`
38
- - skill: `id dir protected`
39
- - agent: `id title title_zh description description_zh order prompt panels skills`
40
- - channel: `id platform adapter`
41
- - **`schema.bad_attach`** — a panel `attach:` entry isn't a valid token. Use agent ids
42
- or `"*"` for all.
43
- - **`ref.missing_panel`** — an agent references `panels: [id]` that no panel provides.
44
- Fix the id, or use `<ext_id>/<panel_id>` to reference a panel in another extension.
45
- - **`ref.missing_skill`** — an agent references `skills: [id]` that no skill provides.
46
- Fix the id or add the skill.
47
- - **`ref.missing_attach_agent`** — a panel's `attach:` names an agent that doesn't
48
- exist. Fix the agent id.
49
- - **`override`** (warning) — a unit in a higher layer is shadowing a lower one
50
- (`local > installed > builtin`). Usually intentional; confirm with the user if not.
51
-
52
- Fix one issue, re-run verify, repeat until clean.
53
-
54
- ## Step 3 — "It verifies but doesn't show up"
55
-
56
- If verify is clean but a change isn't visible:
57
- - **Hot reload is per-request.** After editing `view.js`, `handler.rb`, or a `SKILL.md`,
58
- the user must **reload the WebUI page** — there's no restart, but a stale tab won't
59
- update on its own. Editing `ext.yml` also applies on the next load.
60
- - **Panel not appearing?** Check the panel's `attach:` (or the agent that references it
61
- via `panels: [id]`). A panel with no `attach` and no referencing agent has nothing to
62
- mount onto.
63
- - **API 404?** Routes are relative to `/api/ext/<ext_id>/`. Confirm the handler subclasses
64
- `Clacky::ApiExtension` and the route pattern matches what `view.js` fetches.
65
- - **Skill not triggering?** The AI selects skills by their `description`. Make the
66
- description concrete about WHEN to use it.
67
-
68
- ## Step 4 — Confirm the fix
69
-
70
- End with a clean `clacky ext verify` and have the user reload to confirm the behavior
71
- actually works — don't declare success on "should work."
@@ -1,73 +0,0 @@
1
- ---
2
- name: ext-publish
3
- description: Pack and publish an OpenClacky extension to the marketplace, publish a new version, list published extensions, or unpublish one. Use when the user wants to ship, release, publish, update, or take down an extension. Handles packing, the license requirement, versioning, and the already-published case.
4
- ---
5
-
6
- # Extension Publish
7
-
8
- Take a finished local extension and ship it to the OpenClacky marketplace, then confirm
9
- it's live.
10
-
11
- ## Before publishing
12
-
13
- - The extension must live in the **local** layer (`~/.clacky/ext/local/<id>/`). Only
14
- local containers can be packed. Encrypted (`SKILL.md.enc`) containers are rejected.
15
- - Publishing requires an **activated user license** — it proves creator identity, and
16
- the platform attributes the extension to that account. If activation is missing, tell
17
- the user to activate first; don't try to work around it.
18
- - Run `clacky ext verify` one last time and confirm no errors before shipping.
19
-
20
- ## Publish (first time)
21
-
22
- ```
23
- clacky ext publish <id>
24
- ```
25
-
26
- This packs the local container into a zip and uploads it. On success you'll see:
27
-
28
- ```
29
- Published <id> v<version> → status=<status>
30
- ```
31
-
32
- Options:
33
- - `--status draft` — publish as a draft (not yet visible on the public marketplace).
34
- Omit or use `--status published` to go live.
35
- - `--changelog "..."` — release notes for this version.
36
-
37
- ## Publish a new version
38
-
39
- If the extension is already published, a plain `publish` fails with:
40
-
41
- ```
42
- Error: <id> already published. Re-run with --force to publish a new version.
43
- ```
44
-
45
- Re-run with `--force` (and ideally a `--changelog`) to publish a new version. The patch
46
- version auto-increments on the platform side.
47
-
48
- ```
49
- clacky ext publish <id> --force --changelog "Fixed the weather refresh bug"
50
- ```
51
-
52
- ## List your published extensions
53
-
54
- ```
55
- clacky ext published
56
- ```
57
-
58
- Shows each extension with its latest version, status, and unit summary.
59
-
60
- ## Unpublish
61
-
62
- ```
63
- clacky ext unpublish <id>
64
- ```
65
-
66
- Soft-deletes (takes down) one of your published extensions. Confirm with the user
67
- before doing this — it removes it from the marketplace.
68
-
69
- ## Wrap up
70
-
71
- After a successful publish, tell the user the version and status in plain terms, and
72
- mention they can run `clacky ext published` to see it in their list, or bump a new
73
- version anytime with `--force`.
@@ -1,65 +0,0 @@
1
- ---
2
- name: ext-scaffold
3
- description: Scaffold a new OpenClacky extension from an idea. Use when the user wants to create, start, or bootstrap a new extension, plugin, panel, agent, or skill container. Maps the idea to the right contributes types and generates a working skeleton in the local layer.
4
- ---
5
-
6
- # Extension Scaffold
7
-
8
- Turn a plain-language idea into a working extension skeleton, then read the generated
9
- files so you know what you're working with.
10
-
11
- ## Step 1 — Understand the idea
12
-
13
- Figure out what the extension should DO and which contributes types it needs. Ask one
14
- clarifying question only if it's genuinely ambiguous. Common mappings:
15
-
16
- - "Show me X in a side panel / add a button / dashboard" → **panel** (+ **api** if it
17
- needs a backend or to call an external service).
18
- - "A capability the AI can invoke" (summarize, translate, format) → **skill**.
19
- - "A specialized assistant with its own personality/tools" → **agent** (usually
20
- bundling its own panels/skills).
21
- - "Connect to Slack / an in-house IM" → **channel**.
22
-
23
- Keep it minimal. Most useful extensions are one panel + one handler, or one skill.
24
- Do NOT add `patches` or `hooks` unless the user explicitly asks — they run arbitrary
25
- Ruby and carry supply-chain risk.
26
-
27
- ## Step 2 — Generate the skeleton
28
-
29
- Pick a lowercase, hyphenated id derived from the idea (e.g. `weather-panel`).
30
-
31
- ```
32
- clacky ext new <id>
33
- ```
34
-
35
- This creates `~/.clacky/ext/local/<id>/` with a working hello panel + handler:
36
- - `ext.yml` — the manifest
37
- - `panels/hello/view.js` — a panel that pings the backend
38
- - `api/handler.rb` — a `Clacky::ApiExtension` subclass mounted at `/api/ext/<id>/`
39
-
40
- Use `--full` only if the user needs the kitchen-sink reference exercising all seven
41
- contributes types — it's a lot to read, so prefer the plain scaffold otherwise.
42
-
43
- ## Step 3 — Read what was generated
44
-
45
- Always read the generated `ext.yml`, `view.js`, and `handler.rb` before editing. This
46
- is your starting point; you'll reshape it to match the idea.
47
-
48
- ## Step 4 — Reshape to the idea
49
-
50
- Edit the files into real, working code:
51
- - Rename the panel id and `view.js` path to match the feature.
52
- - Update `ext.yml` `contributes:` — add `skills:`/`agents:` blocks if needed. A skill
53
- is a `SKILL.md` under `skills/<id>/`; an agent is a `system_prompt.md` that can
54
- reference `panels: [id]` and `skills: [id]`.
55
- - In `view.js`, reuse host CSS classes (`btn-primary`, `btn-secondary`, `form-input`,
56
- `form-textarea`, `form-label`) so the panel inherits the theme automatically.
57
- - In `handler.rb`, define routes relative to the `/api/ext/<id>/` mount.
58
-
59
- ## Step 5 — Confirm it loads
60
-
61
- Run `clacky ext verify` and confirm the new units resolve with no errors. Then tell
62
- the user to reload the WebUI page — panels and api changes are live on the next request,
63
- no restart needed.
64
-
65
- If verify reports problems, switch to debugging (the ext-debug skill covers this).
@@ -1,106 +0,0 @@
1
- ---
2
- name: extend-openclacky
3
- description: Customize, fix, override or extend openclacky itself — change a built-in tool's behavior, intercept/audit/block tool calls, plug in a new IM channel (Slack, in-house IM…), or add UI to the Web UI (panel, button, settings tab). Trigger on "patch openclacky", "block dangerous commands", "audit tool use", "add Slack channel", "extend the web ui", "改 openclacky 内置", "拦截工具调用", "扩展 web 界面". Do NOT trigger for ordinary feature work in the user's own project that doesn't touch openclacky.
4
- ---
5
-
6
- # Extending Openclacky
7
-
8
- Openclacky ships one unified extension mechanism — an **extension container**
9
- declared by a single `ext.yml`. It survives `gem update` and never requires
10
- editing the gem source.
11
-
12
- **Never tell the user to `bundle show openclacky` and edit the gem.**
13
-
14
- ## The one entry point
15
-
16
- Every extension lives in a container directory:
17
-
18
- ```
19
- ~/.clacky/ext/local/<id>/
20
- ext.yml # single manifest — declares everything the container contributes
21
- panels/… # WebUI panels (JS)
22
- api/handler.rb # HTTP API backend
23
- skills/… # AI skills
24
- agents/… # agent profiles + prompts
25
- channels/… # IM adapters
26
- patches/… # runtime method patches
27
- hooks/… # shell hooks
28
- ```
29
-
30
- Scaffold with:
31
- ```bash
32
- clacky ext new <id> # minimal hello-panel starter
33
- clacky ext new <id> --full # kitchen-sink example with every contributes type
34
- ```
35
-
36
- The ext.yml `contributes:` map declares which of these 7 types the container
37
- provides. A container may use one, several, or all.
38
-
39
- ## Pick what to add to `contributes:`
40
-
41
- | User wants to… | contributes: field |
42
- |---|---|
43
- | Add a **WebUI panel / button / settings tab / data visualisation** | `panels:` |
44
- | Add an **HTTP API backend** (routes under `/api/ext/<id>/…`) | `api:` (a single `handler.rb`) |
45
- | **Change behavior of a built-in method** in openclacky (e.g. `WebSearch#execute` timeout) | `patches:` |
46
- | **Audit / block / observe** tool calls (block `rm -rf /`, log every shell command) | `hooks:` |
47
- | Plug openclacky into a **new IM platform** (Slack, in-house IM, custom webhook) | `channels:` |
48
- | Add a **new AI skill** (SKILL.md) | `skills:` |
49
- | Bundle a **custom agent profile** with its own panels + skills | `agents:` |
50
-
51
- ## Authoritative documentation
52
-
53
- Read the relevant reference doc with `web_fetch` before writing code —
54
- don't guess field names, hook events, adapter methods, or the `Clacky.ext`
55
- WebUI contract.
56
-
57
- - Extension containers (ext.yml overview) → https://www.openclacky.com/docs/extend
58
- - Panels (WebUI) → https://www.openclacky.com/docs/extend-webui
59
- - API backends → https://www.openclacky.com/docs/extend-api
60
- - Patches → https://www.openclacky.com/docs/extend-patches
61
- - Shell Hooks → https://www.openclacky.com/docs/extend-shell-hooks
62
- - Channel Adapters → https://www.openclacky.com/docs/extend-channel-adapter
63
-
64
- ## WebUI host services live under `Clacky.*`
65
-
66
- The single public API surface for WebUI extensions is `window.Clacky`.
67
- All host services are exposed as properties on it — reach for them there,
68
- not through bare globals or `window.Xxx`:
69
-
70
- ```js
71
- Clacky.Sessions.on("switched", handler); // active session store
72
- Clacky.Router.go("session"); // top-level view routing
73
- Clacky.I18n.t("some.key"); // translations
74
- Clacky.Modal.confirm("Delete?"); // dialogs
75
- Clacky.Notify.info("Saved"); // toasts
76
- Clacky.Auth.passed; // auth state
77
- Clacky.Workspace.list(dir); // working-directory files
78
- Clacky.Skills.list(); // skill catalog
79
- Clacky.Backup.load(); // backup/restore state
80
- Clacky.WS.send({ type: "..." }); // send a WebSocket message to the agent
81
- ```
82
-
83
- Rules:
84
-
85
- - Prefer `Clacky.Xxx.method(...)` — this is the recommended, forward-stable form.
86
- - `window.Clacky.Xxx.method(...)` works too and is fine in defensive code.
87
- - **Never** write `window.Sessions` / `typeof window.Sessions` / `"Sessions" in window`
88
- — bare host names are `const` bindings, not `window` properties, so those checks
89
- return `undefined` / `false` even though the module is loaded.
90
- - The bare form (`Sessions.on(...)`) still works for backwards compatibility
91
- but is not the pattern to teach or generate.
92
-
93
- ## Execution playbook
94
-
95
- 1. **Identify** which `contributes:` fields the user's intent needs (use the table above; ask if genuinely ambiguous).
96
- 2. **Read the doc(s)** for those fields. The doc is the contract.
97
- 3. **Scaffold** with `clacky ext new <id>` (or `--full` if the user wants every type wired up as a reference).
98
- 4. **Edit** `ext.yml` to declare the fields, and fill in the referenced files (panel view.js, api handler.rb, patches/xxx.rb, etc.).
99
- 5. **Verify** with `clacky ext verify`. Surface any error/skip lines to the user verbatim.
100
- 6. **Reload** the WebUI page (for panel/api changes take effect on next request — no restart needed).
101
-
102
- ## When NOT to use this skill
103
-
104
- - The user is building features in their own application that just *use* openclacky — that's normal coding, no extension container needed.
105
- - The user wants a brand-new tool/skill for *their* project — use `.clacky/skills/` or `.clacky/tools/` in their project, not a gem-level container.
106
- - The change can be made via `clacky config set ...` — prefer config over patches.