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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 584067a790d262ecf04ab52f20557fc879db4cf968928199ae279438afddc0e9
4
- data.tar.gz: c2d31308bd43b6e93e66bbdcff15bcde169fe6cde278de0d03a793c4dc605e1c
3
+ metadata.gz: 44ca2e425677ffd01f35cbad749a7cf026321e30e8133b2db75b08d3062407e6
4
+ data.tar.gz: e6555b956b2d404689761382faecf83458eefdb9453fbdfbc7fdb1406f37b585
5
5
  SHA512:
6
- metadata.gz: 6f8d108fd12581e5e22d1d4125c85794f0df4e94ea8f626dddf1e26bdfcde423bccee1325e4548a5b55c0f33c9f87e7aaaa1d55ead202b8010caa766afdf90ef
7
- data.tar.gz: 95b5b25e5b1d6816f271a0a12cb6cfd57ce53652c23fc1fdb34e6eed31eefc5fc63bd5381ea812355184f410d7537f81329ff34fd312cc0b7345727c9dc561ca
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 --tags ruby,cli
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
- The --force flag is required to prevent accidental deletions.
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
- # Group by brand/client
90
- grouped = group_locations_for_aliases
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
- grouped.each do |group_name, locations|
93
- lines << "# #{group_name}"
94
- locations.each do |loc|
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
- success_result(
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.72.0'
5
+ VERSION = '0.74.0'
6
6
  end
7
7
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.72.0",
3
+ "version": "0.74.0",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.72.0
4
+ version: 0.74.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys