openclacky 1.4.1 → 1.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/lib/clacky/agent/llm_caller.rb +3 -3
- data/lib/clacky/agent/session_serializer.rb +39 -5
- data/lib/clacky/brand_config.rb +49 -7
- data/lib/clacky/client.rb +22 -16
- data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +135 -84
- data/lib/clacky/default_extensions/ext-studio/ext.yml +2 -4
- data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +522 -0
- data/lib/clacky/extension/api_extension.rb +5 -6
- data/lib/clacky/extension/loader.rb +16 -2
- data/lib/clacky/extension/scaffold/templates/full/api/handler.rb.erb +16 -0
- data/lib/clacky/mcp/http_transport.rb +1 -1
- data/lib/clacky/providers.rb +109 -1
- data/lib/clacky/server/http_server.rb +112 -20
- data/lib/clacky/server/session_registry.rb +9 -0
- data/lib/clacky/utils/model_pricing.rb +36 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +321 -32
- data/lib/clacky/web/app.js +29 -6
- data/lib/clacky/web/components/onboard.js +7 -3
- data/lib/clacky/web/features/extensions/store.js +54 -2
- data/lib/clacky/web/features/extensions/view.js +40 -5
- data/lib/clacky/web/features/new-session/store.js +8 -4
- data/lib/clacky/web/features/version/view.js +5 -1
- data/lib/clacky/web/i18n.js +52 -18
- data/lib/clacky/web/index.html +51 -5
- data/lib/clacky/web/sessions.js +40 -11
- data/lib/clacky/web/settings.js +22 -3
- data/lib/clacky/web/theme.js +27 -58
- metadata +2 -5
- data/lib/clacky/default_extensions/ext-studio/skills/ext-debug/SKILL.md +0 -71
- data/lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md +0 -73
- data/lib/clacky/default_extensions/ext-studio/skills/ext-scaffold/SKILL.md +0 -65
- data/lib/clacky/default_skills/extend-openclacky/SKILL.md +0 -106
data/lib/clacky/web/theme.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// theme.js — Theme switcher module
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
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
|
-
|
|
27
|
+
_syncBgCards();
|
|
24
28
|
window.dispatchEvent(new CustomEvent("clacky-theme-change", { detail: { theme } }));
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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);
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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,
|
|
76
|
+
return { init, apply, applyBg, current };
|
|
108
77
|
})();
|
|
109
78
|
|
|
110
79
|
// Initialize theme on page load
|
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
|
+
version: 1.5.0
|
|
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-
|
|
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.
|