openclacky 0.7.7 → 0.7.9
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/.clacky/skills/commit/SKILL.md +2 -2
- data/CHANGELOG.md +37 -0
- data/docs/agent-first-ui-design.md +77 -0
- data/docs/session-skill-invocation.md +69 -0
- data/lib/clacky/agent/session_serializer.rb +76 -0
- data/lib/clacky/agent/system_prompt_builder.rb +19 -0
- data/lib/clacky/agent/tool_executor.rb +5 -13
- data/lib/clacky/agent.rb +4 -9
- data/lib/clacky/agent_config.rb +1 -5
- data/lib/clacky/cli.rb +3 -3
- data/lib/clacky/default_skills/onboard/SKILL.md +146 -0
- data/lib/clacky/server/http_server.rb +193 -21
- data/lib/clacky/server/scheduler.rb +18 -2
- data/lib/clacky/server/web_ui_controller.rb +6 -0
- data/lib/clacky/skill.rb +7 -0
- data/lib/clacky/skill_loader.rb +28 -0
- data/lib/clacky/tools/base.rb +11 -0
- data/lib/clacky/tools/edit.rb +3 -0
- data/lib/clacky/tools/file_reader.rb +1 -1
- data/lib/clacky/tools/glob.rb +3 -2
- data/lib/clacky/tools/grep.rb +1 -1
- data/lib/clacky/tools/write.rb +3 -0
- data/lib/clacky/ui2/components/input_area.rb +0 -2
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +528 -0
- data/lib/clacky/web/app.js +280 -66
- data/lib/clacky/web/index.html +128 -3
- data/lib/clacky/web/onboard.js +240 -0
- data/lib/clacky/web/sessions.js +203 -57
- data/lib/clacky/web/settings.js +20 -1
- data/lib/clacky/web/skills.js +270 -0
- data/lib/clacky/web/tasks.js +34 -92
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93b025580a849df456c7d9d20089b31f392446c2d46d842117563fe349740c92
|
|
4
|
+
data.tar.gz: 5a61ab30b81751176a2f612d3db2822bef54f99b3f907f73e7e7bba0c12bc231
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c5a5aefd0945a2296783df6f9b8f6779d4bd36bc35cae587dc743a19e71e59c7714731e742dc8b1ecdba2535896c8f4be434bea1d2adac2dc1bd4ad56762a11e
|
|
7
|
+
data.tar.gz: 639d2f4e3d72250ac70dc9e27ad3157e91878bac076a3b13edaa8de00bae7f8342b87fe42357475092b2aecaca5425720e124d222e398cbe623207167ef21b2c
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
+
---
|
|
2
3
|
name: commit
|
|
3
4
|
description: Smart Git commit helper that analyzes changes and creates semantic commits
|
|
4
|
-
disable-model-invocation: false
|
|
5
5
|
user-invocable: true
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -472,4 +472,4 @@ This skill works best:
|
|
|
472
472
|
|
|
473
473
|
- Created: 2025-02-01
|
|
474
474
|
- Purpose: Improve commit quality and development workflow
|
|
475
|
-
- Compatible with: Any git repository
|
|
475
|
+
- Compatible with: Any git repository
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.9] - 2026-03-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Cursor-paginated message history in web UI for large session navigation
|
|
14
|
+
- `confirm_all` permission mode for WebUI human sessions
|
|
15
|
+
- Re-run onboard entry in settings panel
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Expand `~` in file system tools path arguments (file_reader, glob, grep, write, edit)
|
|
19
|
+
- Sort sessions newest-first with scheduled sessions at bottom
|
|
20
|
+
- Tasks and skills sidebar items now static — no longer disappear on scroll
|
|
21
|
+
- Delete task now also removes associated schedules
|
|
22
|
+
|
|
23
|
+
### More
|
|
24
|
+
- Add frontmatter (`name`, `description`, `disable-model-invocation`, `user-invocable`) to onboard skill
|
|
25
|
+
|
|
26
|
+
## [0.7.8] - 2026-03-06
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- Skills panel in web UI: list all skills, enable/disable with toggle, view skill details
|
|
30
|
+
- Hash-based routing (`#session/:id`, `#tasks`, `#skills`, `#settings`) with deep-link and refresh support
|
|
31
|
+
- REST API endpoints for skills management (`GET /api/skills`, `PATCH /api/skills/:name/toggle`)
|
|
32
|
+
- `disabled?` helper on `Skill` model for quick enabled/disabled state checks
|
|
33
|
+
|
|
34
|
+
### Improved
|
|
35
|
+
- Centralized `Router` object in web UI — single source of truth for all panel switching and sidebar highlight state
|
|
36
|
+
- Web UI frontend split further: `skills.js` extracted as standalone module
|
|
37
|
+
- Ctrl-C in web server now exits immediately via `StartCallback` trap override
|
|
38
|
+
- Skill enable/disable now writes `disable-model-invocation: false` (retains field) instead of deleting it
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- Sidebar highlight for Tasks and Skills stuck active after navigating away
|
|
42
|
+
- Router correctly restores last view on page refresh via hash URL
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
- Removed `plan_only` permission mode from agent, CLI, and web UI
|
|
46
|
+
|
|
10
47
|
## [0.7.7] - 2026-03-04
|
|
11
48
|
|
|
12
49
|
### Added
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Agent-First UI Design Philosophy
|
|
2
|
+
|
|
3
|
+
> Guiding principle for all OpenClacky UI and feature design.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Core Principle
|
|
8
|
+
|
|
9
|
+
**Conversation first, interactive cards when needed.**
|
|
10
|
+
|
|
11
|
+
Users interact with the Agent through natural language to accomplish everything. When conversation is inconvenient for structured input (e.g. dropdowns, multi-select, precise time picking), the Agent triggers an **interactive card** via the `request_user_feedback` tool — rendered by the frontend as a structured UI component. Cards are a complement to conversation, not a replacement.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Two Interaction Modes
|
|
16
|
+
|
|
17
|
+
### 1. Conversation (default)
|
|
18
|
+
User expresses intent in natural language, Agent understands and executes.
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
User: Send me a daily standup summary every morning at 9
|
|
22
|
+
Agent: Done! Task created, runs Mon–Fri at 09:00 ✅
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Interactive Cards (when conversation falls short)
|
|
26
|
+
When the Agent needs structured input that's hard to express in free text, it calls `request_user_feedback`. The frontend renders this as an interactive card (dropdowns, radio buttons, time pickers, etc.).
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Agent calls request_user_feedback → frontend renders a card:
|
|
30
|
+
|
|
31
|
+
┌─────────────────────────────┐
|
|
32
|
+
│ 📋 Confirm task settings │
|
|
33
|
+
│ Frequency: [Daily ▼] │
|
|
34
|
+
│ Time: [09:00 ] │
|
|
35
|
+
│ [✅ Confirm] [Cancel] │
|
|
36
|
+
└─────────────────────────────┘
|
|
37
|
+
|
|
38
|
+
User fills card → structured data sent back to Agent → execution continues
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## When to Use Cards
|
|
44
|
+
|
|
45
|
+
| Situation | Reason |
|
|
46
|
+
|-----------|--------|
|
|
47
|
+
| Choosing from a list of options | Easier than enumerating in chat |
|
|
48
|
+
| Date / time selection | Precise value, error-prone in free text |
|
|
49
|
+
| Sensitive input like API keys | Should not appear in conversation history |
|
|
50
|
+
| Collecting multiple fields at once | One card beats several back-and-forth questions |
|
|
51
|
+
|
|
52
|
+
Everything else: use conversation.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## What Should NOT Exist
|
|
57
|
+
|
|
58
|
+
- ❌ Persistent configuration form pages
|
|
59
|
+
- ❌ Fields that require users to understand technical details (cron expressions, agent IDs, etc.)
|
|
60
|
+
- ❌ More than 3 action buttons per list row
|
|
61
|
+
- ❌ Standalone "Create" form modals
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Role of UI Pages
|
|
66
|
+
|
|
67
|
+
UI pages are for **displaying state**, not for configuring things:
|
|
68
|
+
|
|
69
|
+
- ✅ Show task lists, run history, current status
|
|
70
|
+
- ✅ Minimal action set per row: ▶ Run / ✎ Edit (opens conversation) / ✕ Delete
|
|
71
|
+
- ❌ No inline create/edit forms inside list pages
|
|
72
|
+
|
|
73
|
+
Clicking "Edit" opens an Agent conversation with context pre-filled. The Agent drives the modification flow from there.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
*Applies to all OpenClacky Web UI and feature design.*
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Session + Skill Invocation Pattern
|
|
2
|
+
|
|
3
|
+
> Design pattern for launching an Agent session that immediately runs a skill.
|
|
4
|
+
> Follow this whenever a UI action needs to "open a session and do something automatically."
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## The Pattern
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
1. POST /api/sessions → create a named session
|
|
12
|
+
2. Sessions.add(session) → register locally
|
|
13
|
+
3. Sessions.renderList() → update sidebar
|
|
14
|
+
4. _bootUI() if needed → connect WS (only on first boot)
|
|
15
|
+
5. Sessions.select(session.id) → navigate to session (triggers WS subscribe)
|
|
16
|
+
6. WS.send({ type: "message", session_id, content: "/skill-name" })
|
|
17
|
+
→ agent runs the skill immediately
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The slash command (`/skill-name`) is handled by `Agent#parse_skill_command` on the
|
|
21
|
+
server side — no special API endpoint or pending-state machinery needed.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Real Usages
|
|
26
|
+
|
|
27
|
+
### Create Task (`tasks.js → createInSession`)
|
|
28
|
+
```js
|
|
29
|
+
Sessions.select(session.id);
|
|
30
|
+
WS.send({ type: "message", session_id: session.id, content: "/create-task" });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Onboard (`onboard.js → _startSoulSession`)
|
|
34
|
+
```js
|
|
35
|
+
_bootUI(); // WS.connect() + Tasks/Skills load
|
|
36
|
+
Sessions.add(session);
|
|
37
|
+
Sessions.renderList();
|
|
38
|
+
Sessions.select(session.id);
|
|
39
|
+
WS.send({ type: "message", session_id: session.id, content: "/onboard" });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## When to Use `pending_task` Instead
|
|
45
|
+
|
|
46
|
+
Use the `pending_task` registry field (and the `run_task` WS message) **only** when
|
|
47
|
+
the prompt is a large block of text read from a file (e.g. `POST /api/tasks/run`).
|
|
48
|
+
|
|
49
|
+
For slash commands, always prefer the direct `WS.send` approach above — simpler and
|
|
50
|
+
no server-side state to manage.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Anti-patterns Avoided
|
|
55
|
+
|
|
56
|
+
| Anti-pattern | Why it was wrong |
|
|
57
|
+
|---|---|
|
|
58
|
+
| Store `_pendingSessionId` in module state, resolve on `session_list` | Race condition between WS connect and session_list arrival; unnecessary complexity |
|
|
59
|
+
| Custom `takePendingSession()` hook in app.js `session_list` handler | Spread logic across files; hard to trace |
|
|
60
|
+
| Send prompt via `setTimeout` after boot | Fragile timing; breaks if WS is slow |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Key Insight
|
|
65
|
+
|
|
66
|
+
`Sessions.select(id)` triggers a WS `subscribe` message. Once the server confirms
|
|
67
|
+
with `subscribed`, the client is guaranteed to receive all subsequent broadcasts for
|
|
68
|
+
that session. Sending `WS.send({ type: "message" })` right after `select` is safe
|
|
69
|
+
because the WebSocket driver queues messages until the connection is open.
|
|
@@ -131,6 +131,82 @@ module Clacky
|
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
133
|
|
|
134
|
+
# Replay conversation history by calling ui.show_* methods for each message.
|
|
135
|
+
# Supports cursor-based pagination using created_at timestamps on user messages.
|
|
136
|
+
# Each "round" starts at a user message and includes all subsequent assistant/tool messages.
|
|
137
|
+
#
|
|
138
|
+
# @param ui [Object] UI interface that responds to show_user_message, show_assistant_message, etc.
|
|
139
|
+
# @param limit [Integer] Maximum number of rounds (user turns) to replay
|
|
140
|
+
# @param before [Float, nil] Unix timestamp cursor — only replay rounds where the user message
|
|
141
|
+
# created_at < before. Pass nil to get the most recent rounds.
|
|
142
|
+
# @return [Hash] { has_more: Boolean } — whether older rounds exist beyond this page
|
|
143
|
+
def replay_history(ui, limit: 20, before: nil)
|
|
144
|
+
# Split @messages into rounds, each starting at a real user message
|
|
145
|
+
rounds = []
|
|
146
|
+
current_round = nil
|
|
147
|
+
|
|
148
|
+
@messages.each do |msg|
|
|
149
|
+
role = msg[:role].to_s
|
|
150
|
+
|
|
151
|
+
if role == "user" && !msg[:system_injected] && msg[:content].is_a?(String) &&
|
|
152
|
+
!msg[:content].to_s.start_with?("[SYSTEM]")
|
|
153
|
+
# Start a new round at each real user message
|
|
154
|
+
current_round = { user_msg: msg, events: [] }
|
|
155
|
+
rounds << current_round
|
|
156
|
+
elsif current_round
|
|
157
|
+
current_round[:events] << msg
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Apply before-cursor filter: only rounds whose user message created_at < before
|
|
162
|
+
if before
|
|
163
|
+
rounds = rounds.select { |r| r[:user_msg][:created_at] && r[:user_msg][:created_at] < before }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
has_more = rounds.size > limit
|
|
167
|
+
# Take the most recent `limit` rounds
|
|
168
|
+
page = rounds.last(limit)
|
|
169
|
+
|
|
170
|
+
page.each do |round|
|
|
171
|
+
msg = round[:user_msg]
|
|
172
|
+
# Emit user message with its timestamp for dedup on the frontend
|
|
173
|
+
ui.show_user_message(extract_text_from_content(msg[:content]), created_at: msg[:created_at])
|
|
174
|
+
|
|
175
|
+
round[:events].each do |ev|
|
|
176
|
+
case ev[:role].to_s
|
|
177
|
+
when "assistant"
|
|
178
|
+
# Text content
|
|
179
|
+
text = extract_text_from_content(ev[:content]).to_s.strip
|
|
180
|
+
ui.show_assistant_message(text) unless text.empty?
|
|
181
|
+
|
|
182
|
+
# Tool calls embedded in assistant message
|
|
183
|
+
Array(ev[:tool_calls]).each do |tc|
|
|
184
|
+
name = tc[:name] || tc.dig(:function, :name) || ""
|
|
185
|
+
args_raw = tc[:arguments] || tc.dig(:function, :arguments) || {}
|
|
186
|
+
args = args_raw.is_a?(String) ? (JSON.parse(args_raw) rescue args_raw) : args_raw
|
|
187
|
+
ui.show_tool_call(name, args)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
when "user"
|
|
191
|
+
# Anthropic-format tool results (role: user, content: array of tool_result blocks)
|
|
192
|
+
next unless ev[:content].is_a?(Array)
|
|
193
|
+
|
|
194
|
+
ev[:content].each do |blk|
|
|
195
|
+
next unless blk.is_a?(Hash) && blk[:type] == "tool_result"
|
|
196
|
+
|
|
197
|
+
ui.show_tool_result(blk[:content].to_s)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
when "tool"
|
|
201
|
+
# OpenAI-format tool result
|
|
202
|
+
ui.show_tool_result(ev[:content].to_s)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
{ has_more: has_more }
|
|
208
|
+
end
|
|
209
|
+
|
|
134
210
|
private
|
|
135
211
|
|
|
136
212
|
# Extract text from message content (handles string and array formats)
|
|
@@ -86,6 +86,25 @@ module Clacky
|
|
|
86
86
|
prompt += "=" * 80
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
# Load agent soul and user profile from ~/.clacky/agents/
|
|
90
|
+
agents_dir = File.expand_path("~/.clacky/agents")
|
|
91
|
+
[
|
|
92
|
+
{ file: "SOUL.md", label: "AGENT SOUL" },
|
|
93
|
+
{ file: "USER.md", label: "USER PROFILE" }
|
|
94
|
+
].each do |entry|
|
|
95
|
+
path = File.join(agents_dir, entry[:file])
|
|
96
|
+
next unless File.exist?(path)
|
|
97
|
+
|
|
98
|
+
content = File.read(path).strip
|
|
99
|
+
next if content.empty?
|
|
100
|
+
|
|
101
|
+
prompt += "\n\n" + "=" * 80 + "\n"
|
|
102
|
+
prompt += "#{entry[:label]} (from ~/.clacky/agents/#{entry[:file]}):\n"
|
|
103
|
+
prompt += "=" * 80 + "\n"
|
|
104
|
+
prompt += content
|
|
105
|
+
prompt += "\n" + "=" * 80
|
|
106
|
+
end
|
|
107
|
+
|
|
89
108
|
# Add all loaded skills to system prompt
|
|
90
109
|
skill_context = build_skill_context
|
|
91
110
|
prompt += skill_context if skill_context && !skill_context.empty?
|
|
@@ -11,13 +11,15 @@ module Clacky
|
|
|
11
11
|
# @return [Boolean] true if should auto-execute
|
|
12
12
|
def should_auto_execute?(tool_name, tool_params = {})
|
|
13
13
|
case @config.permission_mode
|
|
14
|
-
when :auto_approve
|
|
14
|
+
when :auto_approve, :confirm_all
|
|
15
|
+
# Both modes auto-execute all file/shell tools without confirmation.
|
|
16
|
+
# The difference is only in request_user_feedback handling:
|
|
17
|
+
# auto_approve → no human present, inject auto_reply
|
|
18
|
+
# confirm_all → human present, truly wait for user input
|
|
15
19
|
true
|
|
16
20
|
when :confirm_safes
|
|
17
21
|
# Use SafeShell integration for safety check
|
|
18
22
|
is_safe_operation?(tool_name, tool_params)
|
|
19
|
-
when :plan_only
|
|
20
|
-
false
|
|
21
23
|
else
|
|
22
24
|
false
|
|
23
25
|
end
|
|
@@ -217,16 +219,6 @@ module Clacky
|
|
|
217
219
|
}
|
|
218
220
|
end
|
|
219
221
|
|
|
220
|
-
# Build planned result for plan-only mode
|
|
221
|
-
# @param call [Hash] Tool call
|
|
222
|
-
# @return [Hash] Formatted planned result
|
|
223
|
-
def build_planned_result(call)
|
|
224
|
-
{
|
|
225
|
-
id: call[:id],
|
|
226
|
-
content: JSON.generate({ planned: true, message: "Tool execution skipped (plan mode)" })
|
|
227
|
-
}
|
|
228
|
-
end
|
|
229
|
-
|
|
230
222
|
# Check if a tool is potentially slow and should show progress
|
|
231
223
|
# @param tool_name [String] Name of the tool
|
|
232
224
|
# @param args [Hash] Tool arguments
|
data/lib/clacky/agent.rb
CHANGED
|
@@ -163,7 +163,7 @@ module Clacky
|
|
|
163
163
|
|
|
164
164
|
# Format user message with images if provided
|
|
165
165
|
user_content = format_user_content(user_input, images)
|
|
166
|
-
@messages << { role: "user", content: user_content, task_id: task_id }
|
|
166
|
+
@messages << { role: "user", content: user_content, task_id: task_id, created_at: Time.now.to_f }
|
|
167
167
|
@total_tasks += 1
|
|
168
168
|
|
|
169
169
|
@hooks.trigger(:on_start, user_input)
|
|
@@ -389,12 +389,6 @@ module Clacky
|
|
|
389
389
|
end
|
|
390
390
|
else
|
|
391
391
|
# Permission check (if not in auto-approve mode)
|
|
392
|
-
if @config.is_plan_only?
|
|
393
|
-
@ui&.show_info("Planned: #{call[:name]}")
|
|
394
|
-
results << build_planned_result(call)
|
|
395
|
-
next
|
|
396
|
-
end
|
|
397
|
-
|
|
398
392
|
confirmation = confirm_tool_use?(call)
|
|
399
393
|
unless confirmation[:approved]
|
|
400
394
|
# Show denial warning only for user-initiated denials (not system-injected preview errors)
|
|
@@ -508,12 +502,13 @@ module Clacky
|
|
|
508
502
|
end
|
|
509
503
|
|
|
510
504
|
if @config.permission_mode == :auto_approve
|
|
511
|
-
#
|
|
512
|
-
#
|
|
505
|
+
# auto_approve means no human is watching (unattended/scheduled tasks).
|
|
506
|
+
# Inject an auto_reply so the LLM makes a reasonable decision and keeps going.
|
|
513
507
|
result = result.merge(
|
|
514
508
|
auto_reply: "No user is available. Please make a reasonable decision based on the context and continue."
|
|
515
509
|
)
|
|
516
510
|
else
|
|
511
|
+
# confirm_all / confirm_safes — a human is present, truly wait for user input.
|
|
517
512
|
awaiting_feedback = true
|
|
518
513
|
end
|
|
519
514
|
else
|
data/lib/clacky/agent_config.rb
CHANGED
|
@@ -148,7 +148,7 @@ module Clacky
|
|
|
148
148
|
# Default model for ClaudeCode environment
|
|
149
149
|
CLAUDE_DEFAULT_MODEL = "claude-sonnet-4-5"
|
|
150
150
|
|
|
151
|
-
PERMISSION_MODES = [:auto_approve, :confirm_safes, :
|
|
151
|
+
PERMISSION_MODES = [:auto_approve, :confirm_safes, :confirm_all].freeze
|
|
152
152
|
|
|
153
153
|
attr_accessor :permission_mode, :max_tokens, :verbose,
|
|
154
154
|
:enable_compression, :enable_prompt_caching,
|
|
@@ -397,10 +397,6 @@ module Clacky
|
|
|
397
397
|
true
|
|
398
398
|
end
|
|
399
399
|
|
|
400
|
-
def is_plan_only?
|
|
401
|
-
@permission_mode == :plan_only
|
|
402
|
-
end
|
|
403
|
-
|
|
404
400
|
private def validate_permission_mode(mode)
|
|
405
401
|
mode ||= :confirm_safes
|
|
406
402
|
mode = mode.to_sym
|
data/lib/clacky/cli.rb
CHANGED
|
@@ -25,9 +25,9 @@ module Clacky
|
|
|
25
25
|
After completing a task, the agent waits for your next instruction.
|
|
26
26
|
|
|
27
27
|
Permission modes:
|
|
28
|
-
auto_approve - Automatically execute all tools (use with caution)
|
|
28
|
+
auto_approve - Automatically execute all tools, no human interaction (use with caution)
|
|
29
29
|
confirm_safes - Auto-approve safe operations, confirm risky ones (default)
|
|
30
|
-
|
|
30
|
+
confirm_all - Auto-approve all file/shell tools, but wait for human on interactive prompts
|
|
31
31
|
|
|
32
32
|
UI themes:
|
|
33
33
|
hacker - Matrix/hacker-style with bracket symbols (default)
|
|
@@ -42,7 +42,7 @@ module Clacky
|
|
|
42
42
|
$ clacky agent --mode=auto_approve --path /path/to/project
|
|
43
43
|
LONGDESC
|
|
44
44
|
option :mode, type: :string, default: "confirm_safes",
|
|
45
|
-
desc: "Permission mode: auto_approve, confirm_safes,
|
|
45
|
+
desc: "Permission mode: auto_approve, confirm_safes, confirm_all"
|
|
46
46
|
option :theme, type: :string, default: "hacker",
|
|
47
47
|
desc: "UI theme: hacker, minimal (default: hacker)"
|
|
48
48
|
option :verbose, type: :boolean, aliases: "-v", default: false, desc: "Show detailed output"
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: onboard
|
|
3
|
+
description: Onboard a new user by collecting AI personality preferences and user profile, then writing SOUL.md and USER.md.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Skill: onboard
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
Guide a new user through personalizing their Clacky experience via interactive cards.
|
|
12
|
+
Collect AI personality preferences and user profile, then write `SOUL.md` and `USER.md`.
|
|
13
|
+
All structured input is gathered through `request_user_feedback` cards — no free-form interrogation.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
### 1. Greet the user
|
|
18
|
+
|
|
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.
|
|
21
|
+
|
|
22
|
+
Example (English):
|
|
23
|
+
> Hi! I'm Clacky, your AI coding assistant ⚡
|
|
24
|
+
> Let's take 30 seconds to personalize your experience — I'll ask just a couple of quick things.
|
|
25
|
+
|
|
26
|
+
### 2. Collect AI personality (card)
|
|
27
|
+
|
|
28
|
+
Call `request_user_feedback` with a card to set the assistant's name and personality:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"question": "First, let's set up your assistant.",
|
|
33
|
+
"options": [
|
|
34
|
+
"🎯 Professional — Precise, structured, minimal filler",
|
|
35
|
+
"😊 Friendly — Warm, encouraging, like a knowledgeable friend",
|
|
36
|
+
"🎨 Creative — Imaginative, uses metaphors, enthusiastic",
|
|
37
|
+
"⚡ Concise — Ultra-brief, bullet points, maximum signal"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
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
|
+
Map the chosen option to a personality key:
|
|
46
|
+
- Option 1 → `professional`
|
|
47
|
+
- Option 2 → `friendly`
|
|
48
|
+
- Option 3 → `creative`
|
|
49
|
+
- Option 4 → `concise`
|
|
50
|
+
|
|
51
|
+
Store: `ai.name` (default `"Clacky"`), `ai.personality`.
|
|
52
|
+
|
|
53
|
+
### 3. Collect user profile (card)
|
|
54
|
+
|
|
55
|
+
Call `request_user_feedback` again:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"question": "Now a bit about you — all optional, skip anything you like.",
|
|
60
|
+
"options": []
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
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
|
|
69
|
+
|
|
70
|
+
Parse the user's reply as free text; extract whatever they provide.
|
|
71
|
+
|
|
72
|
+
### 4. Learn from links (if any)
|
|
73
|
+
|
|
74
|
+
For each URL the user provided, use the `web_search` tool or fetch the page to read
|
|
75
|
+
publicly available info: bio, projects, tech stack, interests, writing style, etc.
|
|
76
|
+
Note key facts for the USER.md. Skip silently if a URL is unreachable.
|
|
77
|
+
|
|
78
|
+
### 5. Write SOUL.md
|
|
79
|
+
|
|
80
|
+
Write to `~/.clacky/agents/SOUL.md`.
|
|
81
|
+
|
|
82
|
+
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.
|
|
84
|
+
|
|
85
|
+
**Personality style guide:**
|
|
86
|
+
|
|
87
|
+
| Key | Tone |
|
|
88
|
+
|-----|------|
|
|
89
|
+
| `professional` | Concise, precise, structured. Gets to the point. Minimal filler. |
|
|
90
|
+
| `friendly` | Warm, uses light humor, feels like a knowledgeable friend. |
|
|
91
|
+
| `creative` | Imaginative, uses metaphors, thinks outside the box, enthusiastic. |
|
|
92
|
+
| `concise` | Ultra-brief. Bullet points. Maximum signal-to-noise ratio. |
|
|
93
|
+
|
|
94
|
+
Template:
|
|
95
|
+
|
|
96
|
+
```markdown
|
|
97
|
+
# [AI Name] — Soul
|
|
98
|
+
|
|
99
|
+
## Identity
|
|
100
|
+
I am [AI Name], an AI coding assistant and technical co-founder.
|
|
101
|
+
[1–2 sentences reflecting the chosen personality.]
|
|
102
|
+
|
|
103
|
+
## Personality & Tone
|
|
104
|
+
[3–5 bullet points describing communication style.]
|
|
105
|
+
|
|
106
|
+
## Core Strengths
|
|
107
|
+
- Translating ideas into working code quickly
|
|
108
|
+
- Breaking down complex problems into clear steps
|
|
109
|
+
- Spotting issues before they become problems
|
|
110
|
+
- Adapting explanation depth to the user's background
|
|
111
|
+
|
|
112
|
+
## Working Style
|
|
113
|
+
[2–3 sentences about how I approach tasks, matching the personality.]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 6. Write USER.md
|
|
117
|
+
|
|
118
|
+
Write to `~/.clacky/agents/USER.md`.
|
|
119
|
+
|
|
120
|
+
```markdown
|
|
121
|
+
# User Profile
|
|
122
|
+
|
|
123
|
+
## About
|
|
124
|
+
- **Name**: [nickname or "Not provided"]
|
|
125
|
+
- **Occupation**: [or "Not provided"]
|
|
126
|
+
- **Primary Goal**: [or "Not provided"]
|
|
127
|
+
|
|
128
|
+
## Background & Interests
|
|
129
|
+
[If links were fetched: 3–5 bullet points from what was learned.
|
|
130
|
+
Otherwise: omit section or write "No additional context."]
|
|
131
|
+
|
|
132
|
+
## How to Help Best
|
|
133
|
+
[1–2 sentences tailored to the user's goal and background.]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 7. Confirm and close
|
|
137
|
+
|
|
138
|
+
Reply with a single short message, e.g.:
|
|
139
|
+
> All set! I've saved your preferences. Feel free to close this tab and start a fresh session — enjoy! 🚀
|
|
140
|
+
|
|
141
|
+
Do NOT open a new session — the UI handles navigation after the skill finishes.
|
|
142
|
+
|
|
143
|
+
## Notes
|
|
144
|
+
- Keep both files under 300 words each.
|
|
145
|
+
- Do not ask follow-up questions beyond the two cards above.
|
|
146
|
+
- Work with whatever the user provides; fill in sensible defaults for anything omitted.
|