appydave-tools 0.72.0 → 0.73.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 +7 -0
- 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: 53447fe92cfd604234875affe143a083bc07b267ccf92ad11782e6640e440fa6
|
|
4
|
+
data.tar.gz: 74ed454547b2f4a3a5d859ca1bc41228b7efc340dc31c8f3a174b6ff4e1774dd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 61ab02e5529f5a95be3aeeb85674e2f92d9db9d13571aafefc6569cec9f30f967a5d79b48885ad2f636816b50380c39ecdaf67d05b3d403c25ce06517efb8761
|
|
7
|
+
data.tar.gz: db15735eb6b14e5060a9f533231067222402edff360e5034e997c03483b8755692751ce54e95628eea2893e2f649c75a7f9af6e625c7c1be661c8f392d2908e9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [0.72.0](https://github.com/appydave/appydave-tools/compare/v0.71.1...v0.72.0) (2025-12-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add ah-help generate target for aliases-help.zsh format ([874fde9](https://github.com/appydave/appydave-tools/commit/874fde982a2b66ba7d9ec8f8078d79fcde7db4f6))
|
|
7
|
+
|
|
1
8
|
## [0.71.1](https://github.com/appydave/appydave-tools/compare/v0.71.0...v0.71.1) (2025-12-15)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -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