openclacky 0.8.6 → 0.8.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0190a435c18d666a4d2cc7c65b301bd24f18cc6a0159f5d7a4ae25ee552883d7'
4
- data.tar.gz: 62eecd22f6cc112aa00674c683002f14ada01d637b95551a5ca7ff29d408ad75
3
+ metadata.gz: 94fe2f9fd0477231e7411c762aacfecc9eb15b87f027366d7598c73e738499c0
4
+ data.tar.gz: 41e84ac897d4d120dec9c7d88f69b35f82779adc2963a13dd099fd686d926ec3
5
5
  SHA512:
6
- metadata.gz: 8b0dcb369eeb481fc32dd8e02d4cc22ed6b21f3fb5115d9145623444bb88be1cf95cf1c3f8b62988bd54c28888512ee90ecf4df74f98250a04f2f0fcbd9d77d8
7
- data.tar.gz: a6d72920b58547540dd6b389cb8c5dadafc4cf9face794217a7d4c2245bb4f2b46fce4e83616074d1054b9f7542ee0601cad1f9e43fc9358563f6d0dc8b97124
6
+ metadata.gz: 1f28d84aab760ca2c53cc0c558ec82fb3b0bebc8b2fe5279021f3d0cc5c896ebe5dda7fc8f060f7d77609bb6b6aedffa064ef1897185db01a9b6f4e022364646
7
+ data.tar.gz: 64b71cd3c7793201f16600b100088cd739671c9c414252dadc6a593600f15271fc79a953877e59bc2014af929888384e3640ee78e7c1227c8b72e81f919ca72c
data/CHANGELOG.md CHANGED
@@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.8] - 2026-03-13
11
+
12
+ ### Added
13
+ - **i18n system with zh/en runtime switching**: WebUI now supports Chinese and English; all UI text is served through an `I18n` module and switches instantly without a page reload
14
+ - **Onboard language selection step**: first-time setup now opens with a language picker (中文 / English) before any configuration, so the entire onboard experience is conducted in the user's chosen language
15
+ - **Onboard "what's your name" step**: onboard flow now asks for the user's preferred name early on and addresses them by name throughout the rest of the setup
16
+ - **Chinese SOUL.md default**: when a user onboards in Chinese and skips the soul-setup conversation, a Chinese-language SOUL.md is written automatically so the assistant responds in Chinese by default
17
+
18
+ ### Fixed
19
+ - **Onboard WS race condition**: fixed a bug where the first auto-triggered `/onboard` command was silently lost — the WebSocket `session_list` event arrived before the session view was active and redirected the UI to the welcome screen, hiding the agent's response
20
+
21
+ ## [0.8.7] - 2026-03-13
22
+
23
+ ### Added
24
+ - **PDF file upload and reading**: users can now upload PDF files directly in the WebUI chat; the agent reads and analyzes the content via the built-in `pdf-reader` skill
25
+ - **WebUI favicon and SVG icons**: browser tab now shows the Clacky icon
26
+ - **Public skill store install**: skills from the public store can be installed directly via the WebUI without a GitHub URL
27
+ - **Auto-kill previous server on startup**: launching `clacky serve` now automatically kills any previously running instance via pidfile, preventing port conflicts
28
+
29
+ ### Improved
30
+ - **Brand skill loading speed**: loading brand skills no longer triggers a network decryption request — name and description are now read from the local `brand_skills.json` cache, making New Session significantly faster
31
+ - **Memory update UX**: memory update step now shows a spinner and info-style message instead of a bare log line
32
+ - **Browser snapshot output**: snapshot output is compressed to reduce token cost when the agent uses browser tools
33
+ - **Subagent output**: subagent task completion now shows a brief info line instead of a full "Task Complete" block, reducing noise in the parent agent's context
34
+
35
+ ### Fixed
36
+ - **Subagent token delta on first iteration**: subagent now inherits `previous_total_tokens` correctly, fixing an inflated token count on the first tool iteration
37
+ - **Chrome DevTools inspect URL**: updated the remote debugging URL to include the `#remote-debugging` fragment for correct navigation
38
+ - **Shell output token explosion**: long lines in shell output are now truncated to prevent excessive token usage
39
+
40
+ ### More
41
+ - Binary file size limit lowered from 5 MB to 512 KB to reduce accidental token cost
42
+ - `kill_existing_server` logic moved from CLI into `HttpServer` for cleaner separation
43
+ - Browser tool prefers `snapshot -i` over `screenshot` for lower token cost
44
+ - Cross-platform PID file path using `Dir.tmpdir` instead of hardcoded `/tmp`
45
+
10
46
  ## [0.8.6] - 2026-03-12
11
47
 
12
48
  ### Added
@@ -42,7 +42,7 @@ module Clacky
42
42
 
43
43
  @memory_prompt_injected = true
44
44
  @memory_updating = true
45
- @ui&.show_info("Updating long-term memory...")
45
+ @ui&.show_progress("Updating long-term memory")
46
46
 
47
47
  @messages << {
48
48
  role: "user",
@@ -62,7 +62,7 @@ module Clacky
62
62
  @messages.reject! { |m| m[:memory_update] }
63
63
  @memory_prompt_injected = false
64
64
  @memory_updating = false
65
- @ui&.show_info("Memory updated.")
65
+ @ui&.clear_progress
66
66
  end
67
67
 
68
68
  private def memory_update_enabled?
@@ -275,6 +275,13 @@ module Clacky
275
275
  next unless source.is_a?(Hash) && source[:type].to_s == "base64"
276
276
 
277
277
  "data:#{source[:media_type]};base64,#{source[:data]}"
278
+ when "document"
279
+ # Anthropic PDF document block — return a sentinel string for frontend display
280
+ source = block[:source]
281
+ next unless source.is_a?(Hash) && source[:media_type].to_s == "application/pdf"
282
+
283
+ # Return a special marker so the frontend can render a PDF badge instead of an <img>
284
+ "pdf:#{source[:data]&.then { |d| d[0, 32] }}" # prefix to identify without full payload
278
285
  end
279
286
  end
280
287
  end
data/lib/clacky/agent.rb CHANGED
@@ -141,7 +141,7 @@ module Clacky
141
141
  @config.model_name
142
142
  end
143
143
 
144
- def run(user_input, images: [])
144
+ def run(user_input, images: [], files: [])
145
145
  # Start new task for Time Machine
146
146
  task_id = start_new_task
147
147
 
@@ -172,8 +172,8 @@ module Clacky
172
172
  @messages << system_message
173
173
  end
174
174
 
175
- # Format user message with images if provided
176
- user_content = format_user_content(user_input, images)
175
+ # Format user message with images and files if provided
176
+ user_content = format_user_content(user_input, images, files)
177
177
  @messages << { role: "user", content: user_content, task_id: task_id, created_at: Time.now.to_f }
178
178
  @total_tasks += 1
179
179
 
@@ -208,7 +208,12 @@ module Clacky
208
208
 
209
209
  # Check if done (no more tool calls needed)
210
210
  if response[:finish_reason] == "stop" || response[:tool_calls].nil? || response[:tool_calls].empty?
211
- @ui&.show_assistant_message(response[:content]) if response[:content] && !response[:content].empty?
211
+ # During memory update phase, show LLM response as info (not a chat bubble)
212
+ if @memory_updating && response[:content] && !response[:content].empty?
213
+ @ui&.show_info("🧠 " + response[:content].strip)
214
+ elsif response[:content] && !response[:content].empty?
215
+ @ui&.show_assistant_message(response[:content])
216
+ end
212
217
 
213
218
  # Debug: log why we're stopping
214
219
  if @config.verbose && (response[:tool_calls].nil? || response[:tool_calls].empty?)
@@ -227,7 +232,8 @@ module Clacky
227
232
  end
228
233
 
229
234
  # Show assistant message if there's content before tool calls
230
- if response[:content] && !response[:content].empty?
235
+ # During memory update phase, suppress text output (only tool calls matter)
236
+ if response[:content] && !response[:content].empty? && !@memory_updating
231
237
  @ui&.show_assistant_message(response[:content])
232
238
  end
233
239
 
@@ -272,13 +278,17 @@ module Clacky
272
278
  @modified_files_in_task = [] # Reset for next task
273
279
  end
274
280
 
275
- @ui&.show_complete(
276
- iterations: result[:iterations],
277
- cost: result[:total_cost_usd],
278
- duration: result[:duration_seconds],
279
- cache_stats: result[:cache_stats],
280
- awaiting_user_feedback: awaiting_user_feedback
281
- )
281
+ if @is_subagent
282
+ @ui&.show_info("Subagent done (#{result[:iterations]} iterations, $#{result[:total_cost_usd].round(4)})")
283
+ else
284
+ @ui&.show_complete(
285
+ iterations: result[:iterations],
286
+ cost: result[:total_cost_usd],
287
+ duration: result[:duration_seconds],
288
+ cache_stats: result[:cache_stats],
289
+ awaiting_user_feedback: awaiting_user_feedback
290
+ )
291
+ end
282
292
  @hooks.trigger(:on_complete, result)
283
293
  result
284
294
  rescue Clacky::AgentInterrupted
@@ -714,6 +724,10 @@ module Clacky
714
724
  ui: @ui,
715
725
  profile: @agent_profile.name
716
726
  )
727
+ subagent.instance_variable_set(:@is_subagent, true)
728
+
729
+ # Inherit previous_total_tokens so the first iteration delta is calculated correctly
730
+ subagent.instance_variable_set(:@previous_total_tokens, @previous_total_tokens)
717
731
 
718
732
  # Deep clone messages to avoid cross-contamination
719
733
  subagent.instance_variable_set(:@messages, deep_clone(@messages))
@@ -809,11 +823,16 @@ module Clacky
809
823
  end
810
824
 
811
825
  # Format user content with optional images
826
+ # PDF files are handled upstream (server injects file path into message text),
827
+ # so this method only needs to handle images.
812
828
  # @param text [String] User's text input
813
829
  # @param images [Array<String>] Array of image file paths or data: URLs
814
- # @return [String|Array] String if no images, Array with text and image_url objects if images present
815
- private def format_user_content(text, images)
816
- return text if images.nil? || images.empty?
830
+ # @param files [Array] Unused kept for signature compatibility
831
+ # @return [String|Array] String if no images, Array with content blocks otherwise
832
+ private def format_user_content(text, images, files = [])
833
+ images ||= []
834
+
835
+ return text if images.empty?
817
836
 
818
837
  content = []
819
838
  content << { type: "text", text: text } unless text.nil? || text.empty?
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: channel-setup
3
3
  description: |
4
- Configure IM platform channels (Feishu/Lark, WeCom) for open-clacky.
5
- Uses browser automation to complete setup automatically no manual credential copying.
4
+ Configure IM platform channels (Feishu, WeCom) for open-clacky.
5
+ Uses browser automation for navigation; guides the user to paste credentials and perform UI steps.
6
6
  Trigger on: "channel setup", "setup feishu", "setup wecom", "channel config",
7
7
  "channel status", "channel enable", "channel disable", "channel reconfigure", "channel doctor".
8
8
  Subcommands: setup, status, enable <platform>, disable <platform>, reconfigure, doctor.
@@ -21,23 +21,14 @@ allowed-tools:
21
21
 
22
22
  Configure IM platform channels for open-clacky. Config is stored at `~/.clacky/channels.yml`.
23
23
 
24
- ## Core Rule: Never ask for credentials
25
-
26
- All credentials (App Secret, Bot Secret, etc.) must be read directly from browser snapshots.
27
- **Asking the user to copy, type, or provide any credential is a failure.**
28
- If automation cannot reveal a value, say so and suggest retrying — never fall back to manual input.
29
- **Exception**: For Feishu and WeCom, guide the user to paste credentials — do not take snapshots or screenshots to extract. Directly ask the user to reveal and paste.
30
-
31
24
  ## Browser Automation Principles
32
25
 
33
- - Before opening any platform URL, detect Chrome availability and confirm the browser to use with the user.
34
- - **CRITICAL**: When the user chooses "1. Use my Chrome", pass `isolated: false` on every browser tool call (open, snapshot, etc.). When the user chooses "2. Use built-in", pass `isolated: true`. Omitting this causes the wrong browser to be used.
35
- - **When using the user's Chrome** (isolated=false): use `tab new <url>` instead of `open <url>` so the page opens in a new tab rather than replacing the current one.
36
- - After every navigation, take a snapshot before interacting with the page.
26
+ - **Always use built-in browser**: Pass `isolated: true` on every browser tool call. Do NOT ask the user to choose — use the built-in browser only.
27
+ - **Never use `screenshot`**: Use `snapshot -i` instead to get page structure as text. Do NOT generate image files.
28
+ - Use `open <url>` for navigation.
29
+ - AI navigates; user performs form fills, clicks, and pastes when instructed.
37
30
  - If a login page or QR code appears, tell the user to log in and wait for "done" before continuing.
38
- - To read a hidden credential: take an interactive snapshot to find the reveal/eye button, click it, then read the now-visible value from the next snapshot.
39
- - If stuck (CAPTCHA, unexpected page, dialog, cannot find a UI element, scroll fails), take a screenshot, describe the situation, and **guide the user to help** — do NOT fall back to alternative navigation (e.g., switching tabs, trying different URLs). Ask the user to perform the specific step manually and reply "done" when ready.
40
- - Never print raw secrets — mask to last 4 characters in all output.
31
+ - If stuck (CAPTCHA, unexpected page, dialog, cannot find a UI element), **guide the user to help** ask the user to perform the specific step manually and reply "done" when ready.
41
32
 
42
33
  ---
43
34
 
@@ -76,7 +67,7 @@ If the file doesn't exist: "No channels configured yet. Run `/channel-setup setu
76
67
  Ask:
77
68
  > Which platform would you like to connect?
78
69
  >
79
- > 1. Feishu / Lark
70
+ > 1. Feishu
80
71
  > 2. WeCom (Enterprise WeChat)
81
72
 
82
73
  ---
@@ -85,33 +76,27 @@ Ask:
85
76
 
86
77
  #### Phase 1 — Open Feishu Open Platform
87
78
 
88
- 1. Detect Chrome and confirm browser preference with the user. **Remember**: user chose 1 → pass `isolated: false`; chose 2 → pass `isolated: true` on every browser call.
89
- 2. Ask (if not clear from context):
90
- > Are you using Feishu (China) or Lark (International)?
91
- > 1. Feishu — https://open.feishu.cn
92
- > 2. Lark — https://open.larksuite.com
93
- 3. Navigate to `https://open.feishu.cn/app` (or `/larksuite.com/app`).
94
- 4. Take a snapshot. If a login page or QR code is shown, tell the user to log in and wait for "done".
95
- 5. Confirm the app list is visible.
79
+ 1. Navigate: `open https://open.feishu.cn/app`. Pass `isolated: true`.
80
+ 2. Use `snapshot -i` to check page state. If a login page or QR code is shown, tell the user to log in and wait for "done".
81
+ 3. Confirm the app list is visible.
96
82
 
97
83
  #### Phase 2 — Create a new app
98
84
 
99
- 6. **Always create a new app** — do NOT reuse existing apps. Click "Create Enterprise Self-Built App", then create with name `Open Clacky` and description `AI assistant powered by open-clacky`.
85
+ 6. **Always create a new app** — do NOT reuse existing apps. Guide the user: "Click 'Create Enterprise Self-Built App', fill in name (e.g. Open Clacky) and description (e.g. AI assistant powered by open-clacky), then submit. Reply done." Wait for "done".
100
86
 
101
- #### Phase 3 — Get credentials
87
+ #### Phase 3 — Enable Bot capability
102
88
 
103
- 7. Navigate to the app's Credentials & Basic Info page.
104
- 8. Do NOT take snapshots or screenshots. Directly guide the user: "Click the eye icon next to App Secret to reveal it. Copy App ID and App Secret, then paste here. Reply with: App ID: xxx, App Secret: xxx" (confirm back masked to last 4 chars).
89
+ 7. Feishu opens Add App Capabilities by default after creating an app. Guide the user: "Find the Bot capability card and click the Add button next to it, then reply done." Wait for "done".
105
90
 
106
- #### Phase 4 — Enable Bot capability
91
+ #### Phase 4 — Get credentials
107
92
 
108
- 10. Navigate to Add App Capabilities in the left menu.
109
- 11. Find the Bot capability card and add it. Confirm any dialog.
93
+ 8. Navigate to Credentials & Basic Info in the left menu.
94
+ 9. Guide the user: "Copy App ID and App Secret, then paste here. Reply with: App ID: xxx, App Secret: xxx" Wait for "done".
110
95
 
111
96
  #### Phase 5 — Add message permissions
112
97
 
113
- 12. Navigate to Permission Management and open the bulk import dialog.
114
- 13. **Clear the default/example content first** (select all, delete), then paste the following JSON:
98
+ 10. Navigate to Permission Management and open the bulk import dialog.
99
+ 11. Guide the user: "In the bulk import dialog, clear the existing example first (select all, delete), then paste the following JSON. Reply done." Wait for "done". Do NOT try to clear or edit via browser — user does it.
115
100
 
116
101
  ```json
117
102
  {
@@ -126,45 +111,21 @@ Ask:
126
111
  }
127
112
  ```
128
113
 
129
- 14. Confirm all three permissions appear as enabled.
114
+ #### Phase 6 Configure event subscription (Long Connection)
130
115
 
131
- #### Phase 6 Configure event subscription
116
+ **CRITICAL**: Feishu requires the long connection to be established *before* you can save the event config. The platform shows "No application connection detected, ensure long connection is established before saving" until `clacky server` is running and connected. Do NOT try to save until the connection is established.
132
117
 
133
- 15. Navigate to Events & Callbacks.
134
- 16. Change the subscription method to **Long Connection** and save.
135
- 17. Add the event `im.message.receive_v1`.
118
+ 12. **Apply config and establish connection** — Run `curl -X POST http://localhost:7070/api/channels/feishu -H "Content-Type: application/json" -d '{"app_id":"...","app_secret":"...","domain":"..."}'`. The server hot-reloads the Feishu adapter and establishes the WebSocket.
119
+ 13. **Wait for connection** — Wait until the log shows `[feishu-ws] WebSocket connected ✅`.
120
+ 14. **Navigate to Events & Callbacks** — Then guide the user: "Select 'Long Connection' mode. Click Save. Then click Add Event, type `im.message.receive_v1` in the search box, select it, click Add. Reply done." Wait for "done".
136
121
 
137
122
  #### Phase 7 — Publish the app
138
123
 
139
- 18. Navigate to Version Management & Release, create a new version (e.g. `1.0.0`), and publish.
140
- 19. Note: personal accounts publish immediately; enterprise accounts require admin approval — tell the user if this applies.
141
-
142
- #### Phase 8 — Allowed users (optional)
124
+ 15. Navigate to Version Management & Release. Then guide the user: "Create a new version, fill in version (e.g. 1.0.0) and update description (e.g. Initial release for Open Clacky), then publish. Reply done." Wait for "done".
143
125
 
144
- 20. Ask:
145
- > Do you want to restrict which Feishu users can send tasks to the AI?
146
- > Reply "skip" to allow everyone, or "yes" to configure a whitelist.
147
- 21. If "yes":
148
- - Tell the user to send any message to the Open Clacky bot in Feishu, then reply "done".
149
- - Navigate to Log Search → Event Log, find the latest `im.message.receive_v1` event, and read `sender.sender_id.open_id` (format `ou_xxx`) directly from the page.
150
- - Repeat for additional users if needed.
126
+ #### Phase 8 — Finalize config and validate
151
127
 
152
- #### Phase 9 Save config and validate
153
-
154
- Write `~/.clacky/channels.yml` (merge with existing content, never overwrite other platforms):
155
-
156
- ```yaml
157
- channels:
158
- feishu:
159
- enabled: true
160
- app_id: <from user paste>
161
- app_secret: <from user paste>
162
- domain: https://open.feishu.cn # or https://open.larksuite.com
163
- # allowed_users: # omit if not configured
164
- # - ou_xxx
165
- ```
166
-
167
- Run `chmod 600 ~/.clacky/channels.yml`.
128
+ Config was applied in step 12 (via API).
168
129
 
169
130
  Validate:
170
131
  ```bash
@@ -174,57 +135,23 @@ curl -s -X POST "${DOMAIN}/open-apis/auth/v3/tenant_access_token/internal" \
174
135
  ```
175
136
  Check for `"code":0`. If it fails, explain and offer to retry.
176
137
 
177
- On success: "✅ Feishu channel configured. Restart `clacky server` to activate."
138
+ On success: "✅ Feishu channel configured. The channel is already active."
178
139
 
179
140
  ---
180
141
 
181
142
  ### WeCom setup
182
143
 
183
- First ask: "Are you an admin of your WeCom enterprise (can log in to work.weixin.qq.com)? Reply 1 or 2."
184
- - If using AskFollowupQuestion: pass options as `Yes I am an enterprise admin` and `No I am not an admin` (no leading numbers; the tool will add 1. and 2.).
185
- - **1** use Admin Console flow (browser automation).
186
- - **2** use Client flow (guide user in WeCom desktop client, then ask for Bot ID and Secret).
187
-
188
- ---
189
-
190
- #### Admin Console flow (user has admin access)
191
-
192
- **Principle**: Do NOT take snapshots or screenshots to inspect the UI. Directly guide the user through each step. For Bot ID and Secret, guide the user to paste them — do NOT try to extract from the page.
144
+ 1. Navigate: `open https://work.weixin.qq.com/wework_admin/frame#/aiHelper/create`. Pass `isolated: true`.
145
+ 2. Use `snapshot -i` to check page state. If a login page or QR code is shown, tell the user to log in and wait for "done".
146
+ 3. Steps 3–7: Do NOT take snapshots. Guide the user: "Scroll to the bottom of the right panel and click 'API mode creation'. Reply done." Wait for "done".
147
+ 4. Guide the user: "Click 'Add' next to 'Visible Range'. In the scope dialog, select the top-level company node (or specific users/departments). Click Confirm. Reply done." Wait for "done".
148
+ 5. Guide the user: "If Secret is not visible, click 'Get Secret'. Copy Bot ID and Secret **before** clicking Save — do NOT click 'Get Secret' again after copying (it invalidates the previous secret). Paste here. Reply with: Bot ID: xxx, Secret: xxx" Wait for "done".
149
+ 6. Guide the user: "Click Save. In the dialog, enter name (e.g. Open Clacky) and description (e.g. AI assistant powered by open-clacky). Click Confirm. Click Save again. Reply done." Wait for "done".
150
+ 7. **Apply config and hot-reload** — Parse credentials from step 5. Trim leading/trailing whitespace from bot_id and secret. Run `curl -X POST http://localhost:7070/api/channels/wecom -H "Content-Type: application/json" -d '{"bot_id":"...","secret":"..."}'`. Ensure bot_id (starts with `aib`) and secret (longer string) are not swapped.
193
151
 
194
- 1. Detect Chrome and confirm browser preference with the user (if not already done). **Remember**: user chose 1 → pass `isolated: false` when calling browser; chose 2 → pass `isolated: true`.
195
- 2. Navigate directly to `https://work.weixin.qq.com/wework_admin/frame#/aiHelper/create` (use `tab new <url>` when isolated=false). Pass the same `isolated` value on every browser call.
196
- 3. Directly guide the user: "If you see a login page or QR code, log in. When the create page is visible, reply done." Wait for "done".
197
- 4. Guide the user: "Scroll to the bottom of the right panel and click 'API mode creation', then reply done." Wait for "done".
198
- 5. Guide the user: "In the scope dialog, select the top-level company node to allow all members, or select specific users/departments if you prefer. Click Confirm, then reply done." Wait for "done".
199
- 6. Guide the user: "If the Secret is not yet visible, click 'Get Secret'. When both Bot ID and Secret are visible, copy them and paste here. Reply with: Bot ID: xxx, Secret: xxx" (confirm back masked to last 4 chars).
200
- 7. Guide the user: "Click Save. In the dialog, enter name 'Open Clacky' and description 'AI assistant powered by open-clacky', click Confirm, then reply done." Wait for "done".
201
- 8. Write config and run `chmod 600 ~/.clacky/channels.yml`.
202
-
203
- ---
204
-
205
- #### Client flow (user is not admin; cannot access admin console)
206
-
207
- Guide the user to operate in the **WeCom desktop client** (Workbench). No browser automation needed.
208
-
209
- 1. Guide the user: "Open the WeCom desktop client → Workbench → Smart Bot → "Create Bot". Reply done when you see the creation page." Wait for "done".
210
- 2. Guide the user: "Scroll to the bottom of the page and click 'API Mode'. Reply done." Wait for "done".
211
- 3. Guide the user: "The Bot ID appears on the right side. Under 'API Configuration', find the Secret row and click 'Click to Reveal' if needed. Copy both and paste here. Reply with: Bot ID: xxx, Secret: xxx" (confirm back masked to last 4 chars).
212
- 4. Guide the user: "Fill in name 'Open Clacky' and description 'AI assistant powered by open-clacky', click Save (or Confirm), then reply done." Wait for "done".
213
- 5. Write `~/.clacky/channels.yml` and run `chmod 600 ~/.clacky/channels.yml`.
214
-
215
- ---
216
-
217
- #### Save config (both flows)
218
-
219
- ```yaml
220
- channels:
221
- wecom:
222
- enabled: true
223
- bot_id: <extracted or entered>
224
- secret: <extracted or entered>
225
- ```
152
+ On success: "✅ WeCom channel configured."
226
153
 
227
- On success: "✅ WeCom channel configured. Restart `clacky server` to activate. To use the bot: WeCom client → WorkbenchManagement click the bot details Go to use."
154
+ On success: "✅ WeCom channel configured. To use the bot: WeCom client → Contactsselect Smart Bot to see the newly created bot.".
228
155
 
229
156
  ---
230
157
 
@@ -14,22 +14,72 @@ All structured input is gathered through `request_user_feedback` cards — no fr
14
14
 
15
15
  ## Steps
16
16
 
17
+ ### 0. Detect language
18
+
19
+ The user's language was set during the onboarding intro screen. The skill is invoked with
20
+ a `lang:` argument in the slash command, e.g. `/onboard lang:zh` or `/onboard lang:en`.
21
+
22
+ Check the invocation message for `lang:zh` or `lang:en`:
23
+ - If `lang:zh` is present → conduct the **entire** onboard in **Chinese**, write SOUL.md & USER.md in Chinese.
24
+ - Otherwise (or if missing) → use **English** throughout.
25
+
26
+ If the `lang:` argument is absent, infer from the user's first reply; default to English.
27
+
17
28
  ### 1. Greet the user
18
29
 
19
- Send a short, warm welcome message (2–3 sentences). Detect the user's language from any
20
- text they've already typed; default to English. Do NOT ask any questions yet.
30
+ Send a short, warm welcome message (2–3 sentences). Use the language determined in Step 0.
31
+ Do NOT ask any questions yet.
21
32
 
22
33
  Example (English):
23
34
  > Hi! I'm your personal assistant ⚡
24
35
  > Let's take 30 seconds to personalize your experience — I'll ask just a couple of quick things.
25
36
 
26
- ### 2. Collect AI personality (card)
37
+ Example (Chinese):
38
+ > 嗨!我是你的专属 AI 助手 ⚡
39
+ > 只需 30 秒完成个性化设置,我会问你两个简单问题。
40
+
41
+ ### 2. Ask the user's name (card)
42
+
43
+ Call `request_user_feedback` to get the user's preferred name.
44
+
45
+ If `lang == "zh"`, use:
46
+ ```json
47
+ {
48
+ "question": "先告诉我,我该怎么称呼你?"
49
+ }
50
+ ```
51
+
52
+ Otherwise (English):
53
+ ```json
54
+ {
55
+ "question": "First, what should I call you?"
56
+ }
57
+ ```
58
+
59
+ Store the reply as `user.name` (default `"there"` for English, `"朋友"` for Chinese if blank).
60
+
61
+ ### 3. Collect AI personality (card)
27
62
 
28
- Call `request_user_feedback` with a card to set the assistant's name and personality:
63
+ Call `request_user_feedback` with a card to set the assistant's personality.
64
+ Address the user by `user.name` in the question.
65
+
66
+ If `lang == "zh"`, use:
67
+ ```json
68
+ {
69
+ "question": "好的,[user.name]!来设置一下你的助手吧。",
70
+ "options": [
71
+ "🎯 专业型 — 精准、结构化、不废话",
72
+ "😊 友好型 — 热情、鼓励、像一位博学的朋友",
73
+ "🎨 创意型 — 富有想象力,善用比喻,充满热情",
74
+ "⚡ 简洁型 — 极度简短,用要点,信噪比最高"
75
+ ]
76
+ }
77
+ ```
29
78
 
79
+ Otherwise (English):
30
80
  ```json
31
81
  {
32
- "question": "First, let's set up your assistant.",
82
+ "question": "Nice to meet you, [user.name]! Now let's set up your assistant.",
33
83
  "options": [
34
84
  "🎯 Professional — Precise, structured, minimal filler",
35
85
  "😊 Friendly — Warm, encouraging, like a knowledgeable friend",
@@ -39,48 +89,49 @@ Call `request_user_feedback` with a card to set the assistant's name and persona
39
89
  }
40
90
  ```
41
91
 
42
- Also ask for a custom name in the same message if the platform supports a text field;
43
- otherwise follow up with: "What should I call myself? (leave blank to keep 'Clacky')"
44
-
45
92
  Map the chosen option to a personality key:
46
93
  - Option 1 → `professional`
47
94
  - Option 2 → `friendly`
48
95
  - Option 3 → `creative`
49
96
  - Option 4 → `concise`
50
97
 
51
- Store: `ai.name` (default `"Clacky"`), `ai.personality`.
98
+ Store: `ai.personality`.
52
99
 
53
- ### 3. Collect user profile (card)
100
+ ### 4. Collect user profile (card)
54
101
 
55
- Call `request_user_feedback` again:
102
+ Call `request_user_feedback` again.
56
103
 
104
+ If `lang == "zh"`, use:
57
105
  ```json
58
106
  {
59
- "question": "Now a bit about you all optional, skip anything you like.",
107
+ "question": "再简单了解一下你自己吧 —— 全部可选,随便填:\n• 职业\n• 最希望用 AI 做什么\n• 社交 / 作品链接(GitHub、微博、个人网站等)—— AI 会读取公开信息来了解你",
60
108
  "options": []
61
109
  }
62
110
  ```
63
111
 
64
- Ask for the following in the question text (as labeled fields description, since options is empty):
65
- - Name / nickname
66
- - Occupation
67
- - What you want to use AI for most
68
- - Social / portfolio links (GitHub, Twitter/X, personal site…) — AI will read them to learn about you
112
+ Otherwise (English):
113
+ ```json
114
+ {
115
+ "question": "Now a bit about you — all optional, skip anything you like.\n• Occupation\n• What you want to use AI for most\n• Social / portfolio links (GitHub, Twitter/X, personal site…) — AI will read them to learn about you",
116
+ "options": []
117
+ }
118
+ ```
69
119
 
70
120
  Parse the user's reply as free text; extract whatever they provide.
71
121
 
72
- ### 4. Learn from links (if any)
122
+ ### 5. Learn from links (if any)
73
123
 
74
124
  For each URL the user provided, use the `web_search` tool or fetch the page to read
75
125
  publicly available info: bio, projects, tech stack, interests, writing style, etc.
76
126
  Note key facts for the USER.md. Skip silently if a URL is unreachable.
77
127
 
78
- ### 5. Write SOUL.md
128
+ ### 6. Write SOUL.md
79
129
 
80
130
  Write to `~/.clacky/agents/SOUL.md`.
81
131
 
82
132
  Use `ai.name` and `ai.personality` to shape the content.
83
- If the user's language appears to be non-English (detected from their replies), write in that language.
133
+ Write in the language determined in Step 0 (`zh` Chinese, otherwise English).
134
+ If `lang == "zh"`, add a line: `**始终用中文回复用户。**` near the top of the Identity section.
84
135
 
85
136
  **Personality style guide:**
86
137
 
@@ -113,7 +164,7 @@ I am [AI Name], a personal assistant and technical co-founder.
113
164
  [2–3 sentences about how I approach tasks, matching the personality.]
114
165
  ```
115
166
 
116
- ### 6. Write USER.md
167
+ ### 7. Write USER.md
117
168
 
118
169
  Write to `~/.clacky/agents/USER.md`.
119
170
 
@@ -121,7 +172,7 @@ Write to `~/.clacky/agents/USER.md`.
121
172
  # User Profile
122
173
 
123
174
  ## About
124
- - **Name**: [nickname or "Not provided"]
175
+ - **Name**: [user.name, or "Not provided"]
125
176
  - **Occupation**: [or "Not provided"]
126
177
  - **Primary Goal**: [or "Not provided"]
127
178
 
@@ -133,9 +184,31 @@ Write to `~/.clacky/agents/USER.md`.
133
184
  [1–2 sentences tailored to the user's goal and background.]
134
185
  ```
135
186
 
136
- ### 7. Confirm and close
187
+ ### 7b. Write USER.md (Chinese version, if applicable)
188
+
189
+ If `lang == "zh"`, write `~/.clacky/agents/USER.md` in Chinese:
190
+
191
+ ```markdown
192
+ # 用户档案
193
+
194
+ ## 基本信息
195
+ - **姓名**: [user.name,未填则写「未填写」]
196
+ - **职业**: [未填则写「未填写」]
197
+ - **主要目标**: [未填则写「未填写」]
198
+
199
+ ## 背景与兴趣
200
+ [如有链接:3–5 条从公开信息中提取的要点。否则:写「暂无更多背景信息。」]
201
+
202
+ ## 如何最好地帮助用户
203
+ [1–2 句话,根据用户目标和背景量身定制。]
204
+ ```
205
+
206
+ ### 8. Confirm and close
207
+
208
+ If `lang == "zh"`, reply:
209
+ > 全部设置完成!我已保存你的偏好。关掉这个对话,开启一个新对话就可以开始了 —— 尽情享用吧!🚀
137
210
 
138
- Reply with a single short message, e.g.:
211
+ Otherwise:
139
212
  > All set! I've saved your preferences. Feel free to close this tab and start a fresh session — enjoy! 🚀
140
213
 
141
214
  Do NOT open a new session — the UI handles navigation after the skill finishes.