appydave-tools 0.72.0 → 0.74.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 +14 -0
- data/docs/templates/settings.example.json +2 -1
- data/lib/appydave/tools/configuration/models/settings_config.rb +6 -0
- data/lib/appydave/tools/jump/cli.rb +76 -3
- data/lib/appydave/tools/jump/commands/generate.rb +176 -12
- data/lib/appydave/tools/version.rb +1 -1
- data/package.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44ca2e425677ffd01f35cbad749a7cf026321e30e8133b2db75b08d3062407e6
|
|
4
|
+
data.tar.gz: e6555b956b2d404689761382faecf83458eefdb9453fbdfbc7fdb1406f37b585
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88a430612add3c650c890797874ad17332bbd8c4507bfa2e847e310cbec6b659f719fdfa7db47f2a672e22080ad070cc026fb1fbf66b028b740babac7261502a
|
|
7
|
+
data.tar.gz: ab498bebd3971c75d7bc222af0c1c5405568c78e2d6fbec47a9d64818f78df8504c720cbd8c25a3ac48356e2e7ba586520f345074d157799d974e789b8a48c8b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [0.73.0](https://github.com/appydave/appydave-tools/compare/v0.72.0...v0.73.0) (2025-12-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add smart grouping and auto-backup to jump generate aliases ([aedc533](https://github.com/appydave/appydave-tools/commit/aedc533f9a9fd158de806830f7c495dd4d1b011b))
|
|
7
|
+
|
|
8
|
+
# [0.72.0](https://github.com/appydave/appydave-tools/compare/v0.71.1...v0.72.0) (2025-12-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add ah-help generate target for aliases-help.zsh format ([874fde9](https://github.com/appydave/appydave-tools/commit/874fde982a2b66ba7d9ec8f8078d79fcde7db4f6))
|
|
14
|
+
|
|
1
15
|
## [0.71.1](https://github.com/appydave/appydave-tools/compare/v0.71.0...v0.71.1) (2025-12-15)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
"video-projects-root": "/Users/YOUR_USERNAME/dev/video-projects",
|
|
3
3
|
"ecamm-recording-folder": "/Users/YOUR_USERNAME/ecamm",
|
|
4
4
|
"download-folder": "/Users/YOUR_USERNAME/Downloads",
|
|
5
|
-
"download-image-folder": "/Users/YOUR_USERNAME/Downloads/images"
|
|
5
|
+
"download-image-folder": "/Users/YOUR_USERNAME/Downloads/images",
|
|
6
|
+
"aliases-output-path": "~/.oh-my-zsh/custom/aliases-jump.zsh"
|
|
6
7
|
}
|
|
@@ -32,6 +32,12 @@ module Appydave
|
|
|
32
32
|
get('download-image-folder') || download_folder
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# Default output path for jump generate aliases command
|
|
36
|
+
# Used by auto-regenerate after CRUD operations
|
|
37
|
+
def aliases_output_path
|
|
38
|
+
get('aliases-output-path')
|
|
39
|
+
end
|
|
40
|
+
|
|
35
41
|
def current_user
|
|
36
42
|
get('current_user')
|
|
37
43
|
end
|
|
@@ -158,6 +158,7 @@ module Appydave
|
|
|
158
158
|
|
|
159
159
|
def run_add(args)
|
|
160
160
|
format = format_option(args)
|
|
161
|
+
no_generate = args.delete('--no-generate')
|
|
161
162
|
attrs = parse_location_args(args)
|
|
162
163
|
|
|
163
164
|
if attrs[:key].nil?
|
|
@@ -169,11 +170,19 @@ module Appydave
|
|
|
169
170
|
result = cmd.run
|
|
170
171
|
|
|
171
172
|
format_output(result, format)
|
|
173
|
+
|
|
174
|
+
# Auto-regenerate aliases after successful add
|
|
175
|
+
if result[:success] && !no_generate
|
|
176
|
+
regenerate_result = auto_regenerate_aliases
|
|
177
|
+
output_regenerate_result(regenerate_result) if regenerate_result
|
|
178
|
+
end
|
|
179
|
+
|
|
172
180
|
exit_code_for(result)
|
|
173
181
|
end
|
|
174
182
|
|
|
175
183
|
def run_update(args)
|
|
176
184
|
format = format_option(args)
|
|
185
|
+
no_generate = args.delete('--no-generate')
|
|
177
186
|
key = args.shift
|
|
178
187
|
attrs = parse_location_args(args)
|
|
179
188
|
|
|
@@ -186,11 +195,19 @@ module Appydave
|
|
|
186
195
|
result = cmd.run
|
|
187
196
|
|
|
188
197
|
format_output(result, format)
|
|
198
|
+
|
|
199
|
+
# Auto-regenerate aliases after successful update
|
|
200
|
+
if result[:success] && !no_generate
|
|
201
|
+
regenerate_result = auto_regenerate_aliases
|
|
202
|
+
output_regenerate_result(regenerate_result) if regenerate_result
|
|
203
|
+
end
|
|
204
|
+
|
|
189
205
|
exit_code_for(result)
|
|
190
206
|
end
|
|
191
207
|
|
|
192
208
|
def run_remove(args)
|
|
193
209
|
format = format_option(args)
|
|
210
|
+
no_generate = args.delete('--no-generate')
|
|
194
211
|
force = args.delete('--force')
|
|
195
212
|
key = args.first
|
|
196
213
|
|
|
@@ -203,6 +220,13 @@ module Appydave
|
|
|
203
220
|
result = cmd.run
|
|
204
221
|
|
|
205
222
|
format_output(result, format)
|
|
223
|
+
|
|
224
|
+
# Auto-regenerate aliases after successful remove
|
|
225
|
+
if result[:success] && !no_generate
|
|
226
|
+
regenerate_result = auto_regenerate_aliases
|
|
227
|
+
output_regenerate_result(regenerate_result) if regenerate_result
|
|
228
|
+
end
|
|
229
|
+
|
|
206
230
|
exit_code_for(result)
|
|
207
231
|
end
|
|
208
232
|
|
|
@@ -331,6 +355,36 @@ module Appydave
|
|
|
331
355
|
value
|
|
332
356
|
end
|
|
333
357
|
|
|
358
|
+
# Auto-regenerate helpers
|
|
359
|
+
|
|
360
|
+
def auto_regenerate_aliases
|
|
361
|
+
output_path = aliases_output_path
|
|
362
|
+
return nil unless output_path
|
|
363
|
+
|
|
364
|
+
cmd = Commands::Generate.new(
|
|
365
|
+
load_config,
|
|
366
|
+
'aliases',
|
|
367
|
+
output_path: output_path,
|
|
368
|
+
path_validator: path_validator
|
|
369
|
+
)
|
|
370
|
+
cmd.run
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def aliases_output_path
|
|
374
|
+
Configuration::Config.configure
|
|
375
|
+
Configuration::Config.settings.aliases_output_path
|
|
376
|
+
rescue StandardError
|
|
377
|
+
nil
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def output_regenerate_result(result)
|
|
381
|
+
return unless result[:success]
|
|
382
|
+
|
|
383
|
+
output.puts ''
|
|
384
|
+
output.puts "Backed up: #{result[:backup]}" if result[:backup]
|
|
385
|
+
output.puts "Regenerated: #{result[:path]} (#{result[:lines]} lines)"
|
|
386
|
+
end
|
|
387
|
+
|
|
334
388
|
# Help display methods
|
|
335
389
|
|
|
336
390
|
def show_version
|
|
@@ -451,10 +505,16 @@ module Appydave
|
|
|
451
505
|
--type, -t <type> Location type (tool, gem, brand, etc.)
|
|
452
506
|
--tags <t1,t2> Comma-separated tags
|
|
453
507
|
--description, -d Human description
|
|
508
|
+
--no-generate Skip auto-regenerating aliases file
|
|
509
|
+
|
|
510
|
+
Auto-Regeneration:
|
|
511
|
+
After a successful add, the aliases file is automatically regenerated
|
|
512
|
+
if 'aliases-output-path' is set in settings.json.
|
|
454
513
|
|
|
455
514
|
Examples:
|
|
456
515
|
jump add --key my-project --path ~/dev/my-project
|
|
457
|
-
jump add -k ad-tools -p ~/dev/ad/appydave-tools --brand appydave --type tool
|
|
516
|
+
jump add -k ad-tools -p ~/dev/ad/appydave-tools --brand appydave --type tool
|
|
517
|
+
jump add --key temp-proj --path ~/tmp/proj --no-generate
|
|
458
518
|
HELP
|
|
459
519
|
end
|
|
460
520
|
|
|
@@ -472,10 +532,16 @@ module Appydave
|
|
|
472
532
|
--type, -t <type> New type
|
|
473
533
|
--tags <t1,t2> New tags (replaces existing)
|
|
474
534
|
--description, -d New description
|
|
535
|
+
--no-generate Skip auto-regenerating aliases file
|
|
536
|
+
|
|
537
|
+
Auto-Regeneration:
|
|
538
|
+
After a successful update, the aliases file is automatically regenerated
|
|
539
|
+
if 'aliases-output-path' is set in settings.json.
|
|
475
540
|
|
|
476
541
|
Examples:
|
|
477
542
|
jump update my-project --description "Updated description"
|
|
478
543
|
jump update ad-tools --tags ruby,cli,youtube
|
|
544
|
+
jump update my-project --path ~/new/path --no-generate
|
|
479
545
|
HELP
|
|
480
546
|
end
|
|
481
547
|
|
|
@@ -483,12 +549,19 @@ module Appydave
|
|
|
483
549
|
output.puts <<~HELP
|
|
484
550
|
jump remove - Remove a location
|
|
485
551
|
|
|
486
|
-
Usage: jump remove <key> --force
|
|
552
|
+
Usage: jump remove <key> --force [--no-generate]
|
|
553
|
+
|
|
554
|
+
Options:
|
|
555
|
+
--force Required to confirm deletion
|
|
556
|
+
--no-generate Skip auto-regenerating aliases file
|
|
487
557
|
|
|
488
|
-
|
|
558
|
+
Auto-Regeneration:
|
|
559
|
+
After a successful remove, the aliases file is automatically regenerated
|
|
560
|
+
if 'aliases-output-path' is set in settings.json.
|
|
489
561
|
|
|
490
562
|
Examples:
|
|
491
563
|
jump remove old-project --force
|
|
564
|
+
jump remove temp-project --force --no-generate
|
|
492
565
|
HELP
|
|
493
566
|
end
|
|
494
567
|
|
|
@@ -7,6 +7,18 @@ module Appydave
|
|
|
7
7
|
# Generate command creates shell aliases and help content
|
|
8
8
|
class Generate < Base
|
|
9
9
|
VALID_TARGETS = %w[aliases help ah-help all].freeze
|
|
10
|
+
MAX_BACKUPS = 10
|
|
11
|
+
|
|
12
|
+
TITLE_MAPPINGS = {
|
|
13
|
+
'appydave' => 'Appy Dave',
|
|
14
|
+
'supportsignal' => 'SupportSignal',
|
|
15
|
+
'beauty-and-joy' => 'Beauty & Joy',
|
|
16
|
+
'david-cruwys' => 'David Cruwys',
|
|
17
|
+
'guy-monroe' => 'Guy Monroe',
|
|
18
|
+
'voz' => 'VOZ',
|
|
19
|
+
'kiros' => 'Kiros',
|
|
20
|
+
'aitldr' => 'AITLDR'
|
|
21
|
+
}.freeze
|
|
10
22
|
|
|
11
23
|
attr_reader :target, :output_path, :output_dir
|
|
12
24
|
|
|
@@ -82,20 +94,25 @@ module Appydave
|
|
|
82
94
|
lines << "# Generated by: jump generate aliases (#{Time.now.strftime('%Y-%m-%d %H:%M')})"
|
|
83
95
|
lines << '# Source: ~/.config/appydave/locations.json'
|
|
84
96
|
lines << ''
|
|
85
|
-
lines << '# Usage: source this file in your .zshrc'
|
|
86
|
-
lines << '# source ~/.oh-my-zsh/custom/aliases-jump.zsh'
|
|
87
|
-
lines << ''
|
|
88
97
|
|
|
89
|
-
#
|
|
90
|
-
|
|
98
|
+
# Build grouped sections using smart logic
|
|
99
|
+
sections = build_smart_alias_sections
|
|
100
|
+
|
|
101
|
+
sections.each do |section|
|
|
102
|
+
# Add major section separator if needed
|
|
103
|
+
if section[:major]
|
|
104
|
+
lines << '# ========================================'
|
|
105
|
+
lines << "# #{section[:title]}"
|
|
106
|
+
lines << '# ========================================'
|
|
107
|
+
else
|
|
108
|
+
lines << "# #{section[:title]}"
|
|
109
|
+
end
|
|
91
110
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
path = path_validator.expand(loc.path)
|
|
96
|
-
desc = loc.description ? " # #{loc.description}" : ''
|
|
97
|
-
lines << "alias #{loc.jump}=\"cd '#{path}'\"#{desc}"
|
|
111
|
+
section[:locations].each do |loc|
|
|
112
|
+
path = format_display_path(loc.path)
|
|
113
|
+
lines << "alias #{loc.jump}=\"cd #{path}\""
|
|
98
114
|
end
|
|
115
|
+
|
|
99
116
|
lines << ''
|
|
100
117
|
end
|
|
101
118
|
|
|
@@ -230,15 +247,132 @@ module Appydave
|
|
|
230
247
|
end
|
|
231
248
|
end
|
|
232
249
|
|
|
250
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
251
|
+
def build_smart_alias_sections
|
|
252
|
+
sections = []
|
|
253
|
+
locations = config.locations.dup
|
|
254
|
+
|
|
255
|
+
# 1. JUMPS - Base directories (monorepo/config without brand/client)
|
|
256
|
+
jumps = locations.select { |l| %w[monorepo config].include?(l.type) && l.brand.nil? && l.client.nil? }
|
|
257
|
+
add_section(sections, 'JUMPS', jumps) unless jumps.empty?
|
|
258
|
+
locations -= jumps
|
|
259
|
+
|
|
260
|
+
# 2. Brain / Second Brain (only non-client brains)
|
|
261
|
+
brains = locations.select { |l| l.type == 'brain' && l.client.nil? }
|
|
262
|
+
add_section(sections, 'Brain / Second Brain', brains) unless brains.empty?
|
|
263
|
+
locations -= brains
|
|
264
|
+
|
|
265
|
+
# 3. Appy Dave (including david-cruwys ecosystem docs, excluding video, archive, and subprojects)
|
|
266
|
+
appydave_all = locations.select do |l|
|
|
267
|
+
%w[appydave david-cruwys].include?(l.brand) && !%w[video archive brain].include?(l.type)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Extract subgroups
|
|
271
|
+
flivideo = appydave_all.select { |l| l.key.start_with?('flivideo') }
|
|
272
|
+
storyline = appydave_all.select { |l| l.key.start_with?('storyline') }
|
|
273
|
+
awb = appydave_all.select { |l| l.key.start_with?('agent-workflow', 'awb') }
|
|
274
|
+
klueless = appydave_all.select { |l| l.key == 'klueless' }
|
|
275
|
+
|
|
276
|
+
appydave_main = appydave_all - flivideo - storyline - awb - klueless
|
|
277
|
+
|
|
278
|
+
add_section(sections, 'Appy Dave', appydave_main) unless appydave_main.empty?
|
|
279
|
+
add_section(sections, 'KlueLess', klueless) unless klueless.empty?
|
|
280
|
+
add_section(sections, 'FliVideo', flivideo) unless flivideo.empty?
|
|
281
|
+
add_section(sections, 'Storyline App', storyline) unless storyline.empty?
|
|
282
|
+
add_section(sections, 'Agent Workflow Builder', awb) unless awb.empty?
|
|
283
|
+
|
|
284
|
+
locations -= appydave_all
|
|
285
|
+
|
|
286
|
+
# 4. CLIENT WORK (major section)
|
|
287
|
+
clients = locations.select { |l| l.client && l.type != 'video' }
|
|
288
|
+
unless clients.empty?
|
|
289
|
+
# Add major separator
|
|
290
|
+
sections << { title: 'CLIENT WORK', major: true, locations: [] }
|
|
291
|
+
|
|
292
|
+
# Group by client
|
|
293
|
+
clients.group_by(&:client).sort.each do |client_name, client_locs|
|
|
294
|
+
title = format_title(client_name, 'Client')
|
|
295
|
+
add_section(sections, title, client_locs)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
locations -= clients
|
|
299
|
+
|
|
300
|
+
# 5. VIDEO PROJECTS (major section)
|
|
301
|
+
videos = locations.select { |l| l.type == 'video' }
|
|
302
|
+
unless videos.empty?
|
|
303
|
+
sections << { title: 'VIDEO PROJECTS', major: true, locations: [] }
|
|
304
|
+
|
|
305
|
+
# Split into brand and client videos
|
|
306
|
+
brand_videos = videos.select(&:brand)
|
|
307
|
+
client_videos = videos.select(&:client)
|
|
308
|
+
other_videos = videos - brand_videos - client_videos
|
|
309
|
+
|
|
310
|
+
add_section(sections, 'Brand Video Projects', brand_videos) unless brand_videos.empty?
|
|
311
|
+
add_section(sections, 'Client Video Projects', client_videos) unless client_videos.empty?
|
|
312
|
+
add_section(sections, 'Video Projects', other_videos) unless other_videos.empty?
|
|
313
|
+
end
|
|
314
|
+
locations -= videos
|
|
315
|
+
|
|
316
|
+
# 6. Beauty & Joy (non-video)
|
|
317
|
+
joy = locations.select { |l| l.brand == 'beauty-and-joy' }
|
|
318
|
+
add_section(sections, 'Beauty & Joy', joy, major: true) unless joy.empty?
|
|
319
|
+
locations -= joy
|
|
320
|
+
|
|
321
|
+
# 7. Archive
|
|
322
|
+
archives = locations.select { |l| l.type == 'archive' }
|
|
323
|
+
add_section(sections, 'Archive', archives) unless archives.empty?
|
|
324
|
+
locations -= archives
|
|
325
|
+
|
|
326
|
+
# 8. GEMs
|
|
327
|
+
gems = locations.select { |l| l.type == 'gem' }
|
|
328
|
+
add_section(sections, 'GEMs', gems) unless gems.empty?
|
|
329
|
+
locations -= gems
|
|
330
|
+
|
|
331
|
+
# 9. Reference / 3rd Party
|
|
332
|
+
refs = locations.select { |l| l.type == 'reference' }
|
|
333
|
+
add_section(sections, 'Reference / 3rd Party', refs) unless refs.empty?
|
|
334
|
+
locations -= refs
|
|
335
|
+
|
|
336
|
+
# 10. Experiments
|
|
337
|
+
experiments = locations.select { |l| l.type == 'experiments' }
|
|
338
|
+
add_section(sections, 'Experiments', experiments) unless experiments.empty?
|
|
339
|
+
locations -= experiments
|
|
340
|
+
|
|
341
|
+
# 11. Other (anything remaining)
|
|
342
|
+
add_section(sections, 'Other', locations) unless locations.empty?
|
|
343
|
+
|
|
344
|
+
sections.reject { |s| s[:locations].empty? && !s[:major] }
|
|
345
|
+
end
|
|
346
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
347
|
+
|
|
348
|
+
def add_section(sections, title, locations, major: false)
|
|
349
|
+
return if locations.empty?
|
|
350
|
+
|
|
351
|
+
sections << {
|
|
352
|
+
title: title,
|
|
353
|
+
major: major,
|
|
354
|
+
locations: locations.sort_by(&:jump)
|
|
355
|
+
}
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def format_title(name, suffix = nil)
|
|
359
|
+
title = TITLE_MAPPINGS[name] || name.split('-').map(&:capitalize).join(' ')
|
|
360
|
+
suffix ? "#{title} #{suffix}" : title
|
|
361
|
+
end
|
|
362
|
+
|
|
233
363
|
def write_or_return(content, path, default_filename)
|
|
234
364
|
if path
|
|
365
|
+
backup_result = backup_existing_file(path)
|
|
235
366
|
FileUtils.mkdir_p(File.dirname(path))
|
|
236
367
|
File.write(path, content)
|
|
237
|
-
|
|
368
|
+
|
|
369
|
+
result = success_result(
|
|
238
370
|
message: "Generated #{default_filename}",
|
|
239
371
|
path: path,
|
|
240
372
|
lines: content.lines.count
|
|
241
373
|
)
|
|
374
|
+
result[:backup] = backup_result if backup_result
|
|
375
|
+
result
|
|
242
376
|
else
|
|
243
377
|
success_result(
|
|
244
378
|
content: content,
|
|
@@ -246,6 +380,36 @@ module Appydave
|
|
|
246
380
|
)
|
|
247
381
|
end
|
|
248
382
|
end
|
|
383
|
+
|
|
384
|
+
def backup_existing_file(path)
|
|
385
|
+
return nil unless File.exist?(path)
|
|
386
|
+
|
|
387
|
+
# Create backup directory
|
|
388
|
+
dir = File.dirname(path)
|
|
389
|
+
filename = File.basename(path, '.*')
|
|
390
|
+
backup_dir = File.join(dir, 'backups', filename)
|
|
391
|
+
FileUtils.mkdir_p(backup_dir)
|
|
392
|
+
|
|
393
|
+
# Generate timestamped backup filename
|
|
394
|
+
timestamp = Time.now.strftime('%Y-%m-%d-%H%M%S')
|
|
395
|
+
ext = File.extname(path)
|
|
396
|
+
backup_path = File.join(backup_dir, "#{timestamp}#{ext}")
|
|
397
|
+
|
|
398
|
+
# Copy existing file to backup
|
|
399
|
+
FileUtils.cp(path, backup_path)
|
|
400
|
+
|
|
401
|
+
# Clean up old backups (keep last N)
|
|
402
|
+
cleanup_old_backups(backup_dir, ext)
|
|
403
|
+
|
|
404
|
+
backup_path
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def cleanup_old_backups(backup_dir, ext)
|
|
408
|
+
backups = Dir.glob(File.join(backup_dir, "*#{ext}")).sort.reverse
|
|
409
|
+
return if backups.length <= MAX_BACKUPS
|
|
410
|
+
|
|
411
|
+
backups[MAX_BACKUPS..].each { |f| File.delete(f) }
|
|
412
|
+
end
|
|
249
413
|
end
|
|
250
414
|
end
|
|
251
415
|
end
|
data/package.json
CHANGED