openclacky 0.6.1 ā 0.6.3
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/README.md +39 -88
- data/homebrew/README.md +96 -0
- data/homebrew/openclacky.rb +24 -0
- data/lib/clacky/agent.rb +557 -122
- data/lib/clacky/cli.rb +431 -3
- data/lib/clacky/default_skills/skill-add/SKILL.md +66 -0
- data/lib/clacky/skill.rb +236 -0
- data/lib/clacky/skill_loader.rb +320 -0
- data/lib/clacky/tools/file_reader.rb +245 -9
- data/lib/clacky/tools/grep.rb +9 -14
- data/lib/clacky/tools/safe_shell.rb +53 -17
- data/lib/clacky/tools/shell.rb +109 -5
- data/lib/clacky/tools/web_fetch.rb +81 -18
- data/lib/clacky/ui2/components/command_suggestions.rb +273 -0
- data/lib/clacky/ui2/components/inline_input.rb +34 -15
- data/lib/clacky/ui2/components/input_area.rb +279 -141
- data/lib/clacky/ui2/layout_manager.rb +147 -67
- data/lib/clacky/ui2/line_editor.rb +142 -2
- data/lib/clacky/ui2/themes/hacker_theme.rb +3 -3
- data/lib/clacky/ui2/themes/minimal_theme.rb +3 -3
- data/lib/clacky/ui2/ui_controller.rb +80 -29
- data/lib/clacky/ui2.rb +0 -1
- data/lib/clacky/utils/arguments_parser.rb +7 -2
- data/lib/clacky/utils/file_ignore_helper.rb +10 -12
- data/lib/clacky/utils/file_processor.rb +201 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky.rb +2 -0
- data/scripts/install.sh +249 -0
- data/scripts/uninstall.sh +146 -0
- metadata +10 -2
- data/lib/clacky/ui2/components/output_area.rb +0 -112
data/lib/clacky/cli.rb
CHANGED
|
@@ -32,6 +32,10 @@ module Clacky
|
|
|
32
32
|
confirm_edits - Auto-approve read-only tools, confirm edits
|
|
33
33
|
plan_only - Generate plan without executing
|
|
34
34
|
|
|
35
|
+
UI themes:
|
|
36
|
+
hacker - Matrix/hacker-style with bracket symbols (default)
|
|
37
|
+
minimal - Clean, simple symbols
|
|
38
|
+
|
|
35
39
|
Session management:
|
|
36
40
|
-c, --continue - Continue the most recent session for this directory
|
|
37
41
|
-l, --list - List recent sessions
|
|
@@ -42,6 +46,8 @@ module Clacky
|
|
|
42
46
|
LONGDESC
|
|
43
47
|
option :mode, type: :string, default: "confirm_safes",
|
|
44
48
|
desc: "Permission mode: auto_approve, confirm_safes, confirm_edits, plan_only"
|
|
49
|
+
option :theme, type: :string, default: "hacker",
|
|
50
|
+
desc: "UI theme: hacker, minimal (default: hacker)"
|
|
45
51
|
option :verbose, type: :boolean, aliases: "-v", default: false, desc: "Show detailed output"
|
|
46
52
|
option :path, type: :string, desc: "Project directory path (defaults to current directory)"
|
|
47
53
|
option :continue, type: :boolean, aliases: "-c", desc: "Continue most recent session"
|
|
@@ -127,6 +133,80 @@ module Clacky
|
|
|
127
133
|
end
|
|
128
134
|
end
|
|
129
135
|
|
|
136
|
+
desc "new PROJECT_NAME", "Create a new Rails project from the official template"
|
|
137
|
+
long_desc <<-LONGDESC
|
|
138
|
+
Create a new Rails project from the official template.
|
|
139
|
+
|
|
140
|
+
This command will:
|
|
141
|
+
1. Clone the template from git@github.com:clacky-ai/rails-template-7x-starter.git
|
|
142
|
+
2. Change into the project directory
|
|
143
|
+
3. Run bin/setup to install dependencies and configure the project
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
$ clacky new my_rails_app
|
|
147
|
+
LONGDESC
|
|
148
|
+
def new(project_name = nil)
|
|
149
|
+
unless project_name
|
|
150
|
+
say "Error: Project name is required.", :red
|
|
151
|
+
say "Usage: clacky new <project_name>", :yellow
|
|
152
|
+
exit 1
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Validate project name
|
|
156
|
+
unless project_name.match?(/^[a-zA-Z][a-zA-Z0-9_-]*$/)
|
|
157
|
+
say "Error: Invalid project name. Use only letters, numbers, underscores, and hyphens.", :red
|
|
158
|
+
exit 1
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
template_repo = "git@github.com:clacky-ai/rails-template-7x-starter.git"
|
|
162
|
+
current_dir = Dir.pwd
|
|
163
|
+
target_dir = File.join(current_dir, project_name)
|
|
164
|
+
|
|
165
|
+
# Check if target directory already exists
|
|
166
|
+
if Dir.exist?(target_dir)
|
|
167
|
+
say "Error: Directory '#{project_name}' already exists.", :red
|
|
168
|
+
exit 1
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
say "Creating new Rails project: #{project_name}", :green
|
|
172
|
+
|
|
173
|
+
# Clone the template repository
|
|
174
|
+
say "\nš¦ Cloning template repository...", :cyan
|
|
175
|
+
clone_command = "git clone #{template_repo} #{project_name}"
|
|
176
|
+
|
|
177
|
+
clone_result = system(clone_command)
|
|
178
|
+
|
|
179
|
+
unless clone_result
|
|
180
|
+
say "\nā Failed to clone repository. Please check your git configuration and network connection.", :red
|
|
181
|
+
exit 1
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
say "ā Repository cloned successfully", :green
|
|
185
|
+
|
|
186
|
+
# Run bin/setup
|
|
187
|
+
say "\nāļø Running bin/setup...", :cyan
|
|
188
|
+
|
|
189
|
+
Dir.chdir(target_dir)
|
|
190
|
+
|
|
191
|
+
setup_command = "./bin/setup"
|
|
192
|
+
|
|
193
|
+
setup_result = system(setup_command)
|
|
194
|
+
|
|
195
|
+
Dir.chdir(current_dir)
|
|
196
|
+
|
|
197
|
+
unless setup_result
|
|
198
|
+
say "\nā Failed to run bin/setup. Please check the setup script for errors.", :red
|
|
199
|
+
say "You can try running it manually:", :yellow
|
|
200
|
+
say " cd #{project_name} && ./bin/setup", :cyan
|
|
201
|
+
exit 1
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
say "\nā
Project '#{project_name}' created successfully!", :green
|
|
205
|
+
say "\nNext steps:", :green
|
|
206
|
+
say " cd #{project_name}", :cyan
|
|
207
|
+
say " clacky agent", :cyan
|
|
208
|
+
end
|
|
209
|
+
|
|
130
210
|
desc "price", "Show pricing information for AI models"
|
|
131
211
|
def price
|
|
132
212
|
say "\nš° Model Pricing Information\n\n", :green
|
|
@@ -171,6 +251,338 @@ module Clacky
|
|
|
171
251
|
say "https://www.anthropic.com/pricing\n\n", :blue
|
|
172
252
|
end
|
|
173
253
|
|
|
254
|
+
desc "skills", "Manage and list skills"
|
|
255
|
+
long_desc <<-LONGDESC
|
|
256
|
+
Manage and list skills that extend Claude's capabilities.
|
|
257
|
+
|
|
258
|
+
Skills are reusable prompts with YAML frontmatter that define
|
|
259
|
+
when and how Claude should use them. Skills can be invoked
|
|
260
|
+
directly with /skill-name or loaded automatically based on context.
|
|
261
|
+
|
|
262
|
+
Skill locations (in priority order):
|
|
263
|
+
- .clacky/skills/ (project, highest priority)
|
|
264
|
+
- ~/.clacky/skills/ (user global)
|
|
265
|
+
- .claude/skills/ (project, compatibility)
|
|
266
|
+
- ~/.claude/skills/ (user global, compatibility)
|
|
267
|
+
|
|
268
|
+
Subcommands:
|
|
269
|
+
list - List all available skills
|
|
270
|
+
show <name> - Show details of a specific skill
|
|
271
|
+
|
|
272
|
+
Examples:
|
|
273
|
+
$ clacky skills list
|
|
274
|
+
$ clacky skills show explain-code
|
|
275
|
+
LONGDESC
|
|
276
|
+
subcommand_option_names = []
|
|
277
|
+
|
|
278
|
+
# Main skills command - delegates to subcommands or shows help
|
|
279
|
+
def skills(*args)
|
|
280
|
+
if args.empty?
|
|
281
|
+
invoke :help, ["skills"]
|
|
282
|
+
else
|
|
283
|
+
subcommand = args.shift
|
|
284
|
+
case subcommand
|
|
285
|
+
when "list"
|
|
286
|
+
skills_list
|
|
287
|
+
when "show"
|
|
288
|
+
skills_show(args.first)
|
|
289
|
+
when "create"
|
|
290
|
+
# Parse options for create
|
|
291
|
+
name = args.first
|
|
292
|
+
opts = {}
|
|
293
|
+
i = 1
|
|
294
|
+
while i < args.length
|
|
295
|
+
if args[i] == "--description"
|
|
296
|
+
opts[:description] = args[i + 1]
|
|
297
|
+
i += 2
|
|
298
|
+
elsif args[i] == "--content"
|
|
299
|
+
opts[:content] = args[i + 1]
|
|
300
|
+
i += 2
|
|
301
|
+
elsif args[i] == "--project"
|
|
302
|
+
opts[:project] = true
|
|
303
|
+
i += 1
|
|
304
|
+
else
|
|
305
|
+
i += 1
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
skills_create_with_opts(name, opts)
|
|
309
|
+
when "delete"
|
|
310
|
+
skills_delete(args.first)
|
|
311
|
+
else
|
|
312
|
+
say "Unknown skill subcommand: #{subcommand}", :red
|
|
313
|
+
invoke :help, ["skills"]
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
desc "skills list", "List all available skills"
|
|
319
|
+
long_desc <<-LONGDESC
|
|
320
|
+
List all available skills from all configured locations:
|
|
321
|
+
- Project skills (.clacky/skills/)
|
|
322
|
+
- Global skills (~/.clacky/skills/)
|
|
323
|
+
- Compatible skills (.claude/skills/, ~/.claude/skills/)
|
|
324
|
+
|
|
325
|
+
Each skill shows:
|
|
326
|
+
- Name and slash command
|
|
327
|
+
- Description
|
|
328
|
+
- Whether it can be auto-invoked by Claude
|
|
329
|
+
- Whether it supports user invocation
|
|
330
|
+
LONGDESC
|
|
331
|
+
def skills_list
|
|
332
|
+
loader = Clacky::SkillLoader.new(Dir.pwd)
|
|
333
|
+
all_skills = loader.load_all
|
|
334
|
+
|
|
335
|
+
if all_skills.empty?
|
|
336
|
+
say "\nš No skills found.\n", :yellow
|
|
337
|
+
say "\nCreate your first skill:", :cyan
|
|
338
|
+
say " ~/.clacky/skills/<skill-name>/SKILL.md", :white
|
|
339
|
+
say " or .clacky/skills/<skill-name>/SKILL.md\n", :white
|
|
340
|
+
return
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
say "\nš Available Skills (#{all_skills.size})\n\n", :green
|
|
344
|
+
|
|
345
|
+
all_skills.each do |skill|
|
|
346
|
+
# Build status indicators
|
|
347
|
+
indicators = []
|
|
348
|
+
indicators << "š¤" if skill.model_invocation_allowed?
|
|
349
|
+
indicators << "š¤" if skill.user_invocable?
|
|
350
|
+
indicators << "š" if skill.forked_context?
|
|
351
|
+
|
|
352
|
+
say " /#{skill.identifier}", :cyan
|
|
353
|
+
say " #{indicators.join(' ')}" unless indicators.empty?
|
|
354
|
+
say "\n"
|
|
355
|
+
|
|
356
|
+
# Show description (truncated if too long)
|
|
357
|
+
desc = skill.context_description
|
|
358
|
+
if desc.length > 60
|
|
359
|
+
desc = desc[0..57] + "..."
|
|
360
|
+
end
|
|
361
|
+
say " #{desc}\n", :white
|
|
362
|
+
|
|
363
|
+
# Show location with priority indicator
|
|
364
|
+
location = case loader.loaded_from[skill.identifier]
|
|
365
|
+
when :default
|
|
366
|
+
"built-in"
|
|
367
|
+
when :project_clacky
|
|
368
|
+
"project .clacky"
|
|
369
|
+
when :project_claude
|
|
370
|
+
"project .claude (compat)"
|
|
371
|
+
when :global_clacky
|
|
372
|
+
"global .clacky"
|
|
373
|
+
when :global_claude
|
|
374
|
+
"global .claude (compat)"
|
|
375
|
+
else
|
|
376
|
+
"unknown"
|
|
377
|
+
end
|
|
378
|
+
say " [#{location}]\n", :yellow
|
|
379
|
+
|
|
380
|
+
say "\n"
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Show errors if any
|
|
384
|
+
if loader.errors.any?
|
|
385
|
+
say "\nā ļø Warnings:\n", :yellow
|
|
386
|
+
loader.errors.each do |error|
|
|
387
|
+
say " - #{error}\n", :red
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
desc "skills show NAME", "Show details of a specific skill"
|
|
393
|
+
long_desc <<-LONGDESC
|
|
394
|
+
Show the full content and metadata of a specific skill.
|
|
395
|
+
|
|
396
|
+
NAME is the skill name (without the leading /).
|
|
397
|
+
|
|
398
|
+
Examples:
|
|
399
|
+
$ clacky skills show explain-code
|
|
400
|
+
LONGDESC
|
|
401
|
+
def skills_show(name = nil)
|
|
402
|
+
unless name
|
|
403
|
+
say "Error: Skill name required.\n", :red
|
|
404
|
+
say "Usage: clacky skills show <name>\n", :yellow
|
|
405
|
+
exit 1
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
loader = Clacky::SkillLoader.new(Dir.pwd)
|
|
409
|
+
all_skills = loader.load_all
|
|
410
|
+
|
|
411
|
+
# Try to find the skill
|
|
412
|
+
skill = all_skills.find { |s| s.identifier == name }
|
|
413
|
+
|
|
414
|
+
unless skill
|
|
415
|
+
# Try prefix matching
|
|
416
|
+
matching = all_skills.select { |s| s.identifier.start_with?(name) }
|
|
417
|
+
if matching.size == 1
|
|
418
|
+
skill = matching.first
|
|
419
|
+
else
|
|
420
|
+
say "\nā Skill '#{name}' not found.\n", :red
|
|
421
|
+
say "\nAvailable skills:\n", :yellow
|
|
422
|
+
all_skills.each { |s| say " /#{s.identifier}\n", :cyan }
|
|
423
|
+
exit 1
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# Display skill details
|
|
428
|
+
say "\nš Skill: /#{skill.identifier}\n\n", :green
|
|
429
|
+
|
|
430
|
+
say "Description:\n", :yellow
|
|
431
|
+
say " #{skill.context_description}\n\n", :white
|
|
432
|
+
|
|
433
|
+
say "Status:\n", :yellow
|
|
434
|
+
say " Auto-invokable: #{skill.model_invocation_allowed? ? 'Yes' : 'No'}\n", :white
|
|
435
|
+
say " User-invokable: #{skill.user_invocable? ? 'Yes' : 'No'}\n", :white
|
|
436
|
+
say " Forked context: #{skill.forked_context? ? 'Yes' : 'No'}\n", :white
|
|
437
|
+
|
|
438
|
+
if skill.allowed_tools
|
|
439
|
+
say " Allowed tools: #{skill.allowed_tools.join(', ')}\n", :white
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
say "\nLocation: #{skill.source_path}\n\n", :yellow
|
|
443
|
+
|
|
444
|
+
say "Content:\n", :yellow
|
|
445
|
+
say "-" * 60 + "\n", :white
|
|
446
|
+
say skill.content, :white
|
|
447
|
+
say "\n" + "-" * 60 + "\n", :white
|
|
448
|
+
|
|
449
|
+
# Show supporting files if any
|
|
450
|
+
if skill.has_supporting_files?
|
|
451
|
+
say "\nSupporting files:\n", :yellow
|
|
452
|
+
skill.supporting_files.each do |file|
|
|
453
|
+
say " - #{file.relative_path_from(Pathname.new(Dir.pwd))}\n", :cyan
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
desc "skills create NAME", "Create a new skill"
|
|
459
|
+
long_desc <<-LONGDESC
|
|
460
|
+
Create a new skill in the global skills directory.
|
|
461
|
+
|
|
462
|
+
NAME is the skill name (lowercase letters, numbers, and hyphens only).
|
|
463
|
+
|
|
464
|
+
This creates a new directory at ~/.clacky/skills/NAME/SKILL.md
|
|
465
|
+
with a template skill file.
|
|
466
|
+
|
|
467
|
+
Options:
|
|
468
|
+
--description Set the skill description
|
|
469
|
+
--content Set the skill content (use - for stdin)
|
|
470
|
+
--project Create in project .clacky/skills/ instead
|
|
471
|
+
|
|
472
|
+
Examples:
|
|
473
|
+
$ clacky skills create explain-code --description "Explain code with diagrams"
|
|
474
|
+
$ clacky skills create deploy --description "Deploy application" --project
|
|
475
|
+
LONGDESC
|
|
476
|
+
option :description, type: :string, desc: "Skill description"
|
|
477
|
+
option :content, type: :string, desc: "Skill content (use - for stdin)"
|
|
478
|
+
option :project, type: :boolean, desc: "Create in project directory"
|
|
479
|
+
def skills_create(name = nil)
|
|
480
|
+
unless name
|
|
481
|
+
say "Error: Skill name required.\n", :red
|
|
482
|
+
say "Usage: clacky skills create <name>\n", :yellow
|
|
483
|
+
exit 1
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# Validate name
|
|
487
|
+
unless name.match?(/^[a-z0-9][a-z0-9-]*$/)
|
|
488
|
+
say "Error: Invalid skill name '#{name}'.\n", :red
|
|
489
|
+
say "Use lowercase letters, numbers, and hyphens only.\n", :yellow
|
|
490
|
+
exit 1
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# Get description
|
|
494
|
+
description = options[:description] || ask("Skill description: ").to_s
|
|
495
|
+
|
|
496
|
+
# Get content
|
|
497
|
+
if options[:content] == "-"
|
|
498
|
+
say "Enter skill content (end with Ctrl+D):\n", :yellow
|
|
499
|
+
content = STDIN.read
|
|
500
|
+
elsif options[:content]
|
|
501
|
+
content = options[:content]
|
|
502
|
+
else
|
|
503
|
+
content = "Describe the skill here..."
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# Determine location
|
|
507
|
+
location = options[:project] ? :project : :global
|
|
508
|
+
|
|
509
|
+
# Create the skill
|
|
510
|
+
loader = Clacky::SkillLoader.new(Dir.pwd)
|
|
511
|
+
skill = loader.create_skill(name, content, description, location: location)
|
|
512
|
+
|
|
513
|
+
skill_path = skill.directory
|
|
514
|
+
say "\nā
Skill created at: #{skill_path}\n", :green
|
|
515
|
+
say "\nYou can invoke it with: /#{name}\n", :cyan
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Helper method for skills command dispatcher
|
|
519
|
+
no_commands do
|
|
520
|
+
def skills_create_with_opts(name, opts = {})
|
|
521
|
+
unless name
|
|
522
|
+
say "Error: Skill name required.\n", :red
|
|
523
|
+
say "Usage: clacky skills create <name>\n", :yellow
|
|
524
|
+
exit 1
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# Validate name
|
|
528
|
+
unless name.match?(/^[a-z0-9][a-z0-9-]*$/)
|
|
529
|
+
say "Error: Invalid skill name '#{name}'.\n", :red
|
|
530
|
+
say "Use lowercase letters, numbers, and hyphens only.\n", :yellow
|
|
531
|
+
exit 1
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
description = opts[:description] || ask("Skill description: ").to_s
|
|
535
|
+
content = opts[:content] || "Describe the skill here..."
|
|
536
|
+
location = opts[:project] ? :project : :global
|
|
537
|
+
|
|
538
|
+
loader = Clacky::SkillLoader.new(Dir.pwd)
|
|
539
|
+
skill = loader.create_skill(name, content, description, location: location)
|
|
540
|
+
|
|
541
|
+
skill_path = skill.directory
|
|
542
|
+
say "\nā
Skill created at: #{skill_path}\n", :green
|
|
543
|
+
say "\nYou can invoke it with: /#{name}\n", :cyan
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
desc "skills delete NAME", "Delete a skill"
|
|
548
|
+
long_desc <<-LONGDESC
|
|
549
|
+
Delete a skill by name.
|
|
550
|
+
|
|
551
|
+
NAME is the skill name (without the leading /).
|
|
552
|
+
|
|
553
|
+
Examples:
|
|
554
|
+
$ clacky skills delete explain-code
|
|
555
|
+
LONGDESC
|
|
556
|
+
def skills_delete(name = nil)
|
|
557
|
+
unless name
|
|
558
|
+
say "Error: Skill name required.\n", :red
|
|
559
|
+
say "Usage: clacky skills delete <name>\n", :yellow
|
|
560
|
+
exit 1
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
loader = Clacky::SkillLoader.new(Dir.pwd)
|
|
564
|
+
all_skills = loader.load_all
|
|
565
|
+
|
|
566
|
+
# Find the skill
|
|
567
|
+
skill = all_skills.find { |s| s.identifier == name }
|
|
568
|
+
|
|
569
|
+
unless skill
|
|
570
|
+
say "Error: Skill '#{name}' not found.\n", :red
|
|
571
|
+
exit 1
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# Confirm deletion
|
|
575
|
+
prompt = TTY::Prompt.new
|
|
576
|
+
unless prompt.yes?("Delete skill '/#{name}' at #{skill.directory}?")
|
|
577
|
+
say "Cancelled.\n", :yellow
|
|
578
|
+
exit 0
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Delete the skill
|
|
582
|
+
loader.delete_skill(name)
|
|
583
|
+
say "\nā
Skill '/#{name}' deleted.\n", :green
|
|
584
|
+
end
|
|
585
|
+
|
|
174
586
|
no_commands do
|
|
175
587
|
def build_agent_config(config)
|
|
176
588
|
AgentConfig.new(
|
|
@@ -239,7 +651,8 @@ module Clacky
|
|
|
239
651
|
end
|
|
240
652
|
|
|
241
653
|
def load_session_by_number(client, agent_config, session_manager, working_dir, identifier)
|
|
242
|
-
|
|
654
|
+
# Get a larger list to search through (for ID prefix matching)
|
|
655
|
+
sessions = session_manager.list(current_dir: working_dir, limit: 100)
|
|
243
656
|
|
|
244
657
|
if sessions.empty?
|
|
245
658
|
say "No sessions found.", :yellow
|
|
@@ -249,7 +662,8 @@ module Clacky
|
|
|
249
662
|
session_data = nil
|
|
250
663
|
|
|
251
664
|
# Check if identifier is a number (index-based)
|
|
252
|
-
|
|
665
|
+
# Heuristic: If it's a small number (1-99), treat as index; otherwise treat as session ID prefix
|
|
666
|
+
if identifier.match?(/^\d+$/) && identifier.to_i <= 99
|
|
253
667
|
index = identifier.to_i - 1
|
|
254
668
|
if index < 0 || index >= sessions.size
|
|
255
669
|
say "Invalid session number. Use -l to list available sessions.", :red
|
|
@@ -300,16 +714,28 @@ module Clacky
|
|
|
300
714
|
|
|
301
715
|
# Run agent with UI2 split-screen interface
|
|
302
716
|
def run_agent_with_ui2(agent, working_dir, agent_config, initial_message = nil, session_manager = nil, client = nil, is_session_load: false)
|
|
717
|
+
# Validate theme
|
|
718
|
+
theme_name = options[:theme] || "hacker"
|
|
719
|
+
available_themes = UI2::ThemeManager.available_themes.map(&:to_s)
|
|
720
|
+
unless available_themes.include?(theme_name)
|
|
721
|
+
say "Error: Unknown theme '#{theme_name}'. Available themes: #{available_themes.join(', ')}", :red
|
|
722
|
+
exit 1
|
|
723
|
+
end
|
|
724
|
+
|
|
303
725
|
# Create UI2 controller with configuration
|
|
304
726
|
ui_controller = UI2::UIController.new(
|
|
305
727
|
working_dir: working_dir,
|
|
306
728
|
mode: agent_config.permission_mode.to_s,
|
|
307
|
-
model: agent_config.model
|
|
729
|
+
model: agent_config.model,
|
|
730
|
+
theme: theme_name
|
|
308
731
|
)
|
|
309
732
|
|
|
310
733
|
# Inject UI into agent
|
|
311
734
|
agent.instance_variable_set(:@ui, ui_controller)
|
|
312
735
|
|
|
736
|
+
# Set skill loader for command suggestions
|
|
737
|
+
ui_controller.set_skill_loader(agent.skill_loader)
|
|
738
|
+
|
|
313
739
|
# Track agent thread state
|
|
314
740
|
agent_thread = nil
|
|
315
741
|
|
|
@@ -408,6 +834,8 @@ module Clacky
|
|
|
408
834
|
if is_session_load
|
|
409
835
|
recent_user_messages = agent.get_recent_user_messages(limit: 5)
|
|
410
836
|
ui_controller.initialize_and_show_banner(recent_user_messages: recent_user_messages)
|
|
837
|
+
# Update session bar with restored agent stats
|
|
838
|
+
ui_controller.update_sessionbar(tasks: agent.total_tasks, cost: agent.total_cost)
|
|
411
839
|
else
|
|
412
840
|
ui_controller.initialize_and_show_banner
|
|
413
841
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-add
|
|
3
|
+
description: Guide for creating new SKILL.md files
|
|
4
|
+
disable-model-invocation: false
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Skill Creation Guide
|
|
9
|
+
|
|
10
|
+
## SKILL.md Structure
|
|
11
|
+
|
|
12
|
+
### 1. Front Matter (Required)
|
|
13
|
+
```yaml
|
|
14
|
+
---
|
|
15
|
+
name: skill-name
|
|
16
|
+
description: Brief one-line description
|
|
17
|
+
disable-model-invocation: false
|
|
18
|
+
user-invocable: true
|
|
19
|
+
---
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. Main Content
|
|
23
|
+
```markdown
|
|
24
|
+
# Skill Title
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
How to invoke: "command description" or `/skill-name`
|
|
28
|
+
|
|
29
|
+
## Process Steps
|
|
30
|
+
|
|
31
|
+
### 1. First Step
|
|
32
|
+
What to do
|
|
33
|
+
|
|
34
|
+
### 2. Next Step
|
|
35
|
+
Continue the task
|
|
36
|
+
|
|
37
|
+
## Commands Used
|
|
38
|
+
```bash
|
|
39
|
+
# Key commands
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Notes
|
|
43
|
+
- Important points
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## File Location
|
|
47
|
+
`.clacky/skills/{skill-name}/SKILL.md`
|
|
48
|
+
|
|
49
|
+
## Minimal Example
|
|
50
|
+
```markdown
|
|
51
|
+
---
|
|
52
|
+
name: hello
|
|
53
|
+
description: Simple greeting
|
|
54
|
+
disable-model-invocation: false
|
|
55
|
+
user-invocable: true
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
# Hello Skill
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
Say "hello" or `/hello`
|
|
62
|
+
|
|
63
|
+
## Process Steps
|
|
64
|
+
### 1. Greet user
|
|
65
|
+
### 2. Offer help
|
|
66
|
+
```
|