appydave-tools 0.71.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff2949280923d4d700c79c676aff2969cc95a0808fe857bee5a20c14f33e8524
4
- data.tar.gz: 4d8616d22c94aae3316c310ed07da76cf5739d39c10771b43f09362d77eff70b
3
+ metadata.gz: 53447fe92cfd604234875affe143a083bc07b267ccf92ad11782e6640e440fa6
4
+ data.tar.gz: 74ed454547b2f4a3a5d859ca1bc41228b7efc340dc31c8f3a174b6ff4e1774dd
5
5
  SHA512:
6
- metadata.gz: 16b926b801da0246092980d859729ce9c408a92644881877e54ccf1fc8a2ef43141ba0b78cc32fb31c0c3d2eaa41d53d224b4552fd63f49a798c52ad5a70a3d6
7
- data.tar.gz: 5a21ef6d3d0629ca756527210b9e1f208a01aa752f0c93229d1cd3f7bd1c7116ab72cc83357c069984976061f15b2ffe17fd0cbbf784cc60d4fb5b3f1a073c4a
6
+ metadata.gz: 61ab02e5529f5a95be3aeeb85674e2f92d9db9d13571aafefc6569cec9f30f967a5d79b48885ad2f636816b50380c39ecdaf67d05b3d403c25ce06517efb8761
7
+ data.tar.gz: db15735eb6b14e5060a9f533231067222402edff360e5034e997c03483b8755692751ce54e95628eea2893e2f649c75a7f9af6e625c7c1be661c8f392d2908e9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
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
+
8
+ ## [0.71.1](https://github.com/appydave/appydave-tools/compare/v0.71.0...v0.71.1) (2025-12-15)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * resolve jump get/remove returning 'No locations found' by making Search#get return results array consistent with search/list ([8ea39b5](https://github.com/appydave/appydave-tools/commit/8ea39b5fdd9bb9fe55c22f9cb86c8b66782c0f09))
14
+
1
15
  # [0.71.0](https://github.com/appydave/appydave-tools/compare/v0.70.0...v0.71.0) (2025-12-14)
2
16
 
3
17
 
data/docs/backlog.md CHANGED
@@ -129,7 +129,7 @@ When `get agent-workflow-build` failed, it suggested: "Did you mean: test-minima
129
129
  - Personal skill at `~/.claude/skills/jump/SKILL.md`
130
130
  - Description includes brand names (appydave, voz, supportsignal, joy, aitldr, kiros), products (flivideo, storyline, klueless, dam), and trigger keywords
131
131
  - Primary mode: Jump CLI commands (`jump search`, `jump get`, `jump list`)
132
- - Fallback mode: Direct JSON read from `/ad/brains/brand-david/data-systems/collections/jump/current.json`
132
+ - Fallback mode: Direct JSON read from `/ad/brains/brand-dave/data-systems/collections/jump/current.json`
133
133
  - Tool restrictions: `allowed-tools: Read, Bash, Grep, Glob`
134
134
  - Includes natural language examples and key location reference table
135
135
 
@@ -245,7 +245,7 @@ module Appydave
245
245
 
246
246
  unless target
247
247
  output.puts 'Usage: jump generate <target> [--output <file>] [--output-dir <dir>]'
248
- output.puts 'Targets: aliases, help, all'
248
+ output.puts 'Targets: aliases, help, ah-help, all'
249
249
  return EXIT_INVALID_INPUT
250
250
  end
251
251
 
@@ -542,16 +542,18 @@ module Appydave
542
542
  Targets:
543
543
  aliases Generate shell alias file (aliases-jump.zsh)
544
544
  help Generate help content for fzf (jump-help.txt)
545
- all Generate both files
545
+ ah-help Generate content for aliases-help.zsh (ah function format)
546
+ all Generate both aliases and help files
546
547
 
547
548
  Options:
548
- --output <file> Write to specific file (for aliases, help)
549
+ --output <file> Write to specific file (for aliases, help, ah-help)
549
550
  --output-dir <dir> Write to directory (for all)
550
551
 
551
552
  Examples:
552
553
  jump generate aliases
553
554
  jump generate aliases --output ~/.oh-my-zsh/custom/aliases-jump.zsh
554
555
  jump generate help --output ~/.oh-my-zsh/custom/data/jump-help.txt
556
+ jump generate ah-help --output ~/.oh-my-zsh/custom/aliases-help.zsh
555
557
  jump generate all --output-dir ~/.oh-my-zsh/custom/
556
558
  HELP
557
559
  end
@@ -6,7 +6,19 @@ module Appydave
6
6
  module Commands
7
7
  # Generate command creates shell aliases and help content
8
8
  class Generate < Base
9
- VALID_TARGETS = %w[aliases help all].freeze
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
 
@@ -28,7 +40,9 @@ module Appydave
28
40
  )
29
41
  end
30
42
 
31
- send("generate_#{target}")
43
+ # Convert hyphenated targets to underscored method names
44
+ method_name = "generate_#{target.tr('-', '_')}"
45
+ send(method_name)
32
46
  end
33
47
 
34
48
  private
@@ -43,6 +57,11 @@ module Appydave
43
57
  write_or_return(content, output_path, 'jump-help.txt')
44
58
  end
45
59
 
60
+ def generate_ah_help
61
+ content = build_ah_help_content
62
+ write_or_return(content, output_path, 'aliases-help.zsh')
63
+ end
64
+
46
65
  def generate_all
47
66
  aliases_content = build_aliases_content
48
67
  help_content = build_help_content
@@ -75,20 +94,25 @@ module Appydave
75
94
  lines << "# Generated by: jump generate aliases (#{Time.now.strftime('%Y-%m-%d %H:%M')})"
76
95
  lines << '# Source: ~/.config/appydave/locations.json'
77
96
  lines << ''
78
- lines << '# Usage: source this file in your .zshrc'
79
- lines << '# source ~/.oh-my-zsh/custom/aliases-jump.zsh'
80
- lines << ''
81
97
 
82
- # Group by brand/client
83
- grouped = group_locations_for_aliases
98
+ # Build grouped sections using smart logic
99
+ sections = build_smart_alias_sections
84
100
 
85
- grouped.each do |group_name, locations|
86
- lines << "# #{group_name}"
87
- locations.each do |loc|
88
- path = path_validator.expand(loc.path)
89
- desc = loc.description ? " # #{loc.description}" : ''
90
- lines << "alias #{loc.jump}=\"cd '#{path}'\"#{desc}"
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]}"
91
109
  end
110
+
111
+ section[:locations].each do |loc|
112
+ path = format_display_path(loc.path)
113
+ lines << "alias #{loc.jump}=\"cd #{path}\""
114
+ end
115
+
92
116
  lines << ''
93
117
  end
94
118
 
@@ -115,6 +139,99 @@ module Appydave
115
139
  lines.join("\n")
116
140
  end
117
141
 
142
+ def build_ah_help_content
143
+ lines = []
144
+
145
+ # Group locations into logical sections
146
+ sections = group_locations_for_ah_help
147
+
148
+ sections.each do |section_name, locations|
149
+ lines << "## #{section_name}"
150
+
151
+ locations.each do |loc|
152
+ path = format_display_path(loc.path)
153
+ tags = build_ah_tags(loc)
154
+
155
+ # Format: alias (24 chars) + path + tags
156
+ alias_col = loc.jump.ljust(24)
157
+ path_with_tags = "#{path}#{' ' * [1, 48 - path.length].max}#{tags}"
158
+
159
+ lines << "#{alias_col}#{path_with_tags}"
160
+ end
161
+
162
+ lines << ''
163
+ end
164
+
165
+ lines.join("\n")
166
+ end
167
+
168
+ def format_display_path(path)
169
+ # Collapse home directory to ~
170
+ expanded = path_validator.expand(path)
171
+ home = Dir.home
172
+ expanded.start_with?(home) ? expanded.sub(home, '~') : expanded
173
+ end
174
+
175
+ def build_ah_tags(loc)
176
+ tags = [loc.type, loc.brand, loc.client].compact.reject(&:empty?)
177
+ tags.concat(loc.tags) if loc.tags
178
+ tags.uniq.map { |t| "##{t}" }.join(' ')
179
+ end
180
+
181
+ def group_locations_for_ah_help
182
+ sections = {
183
+ 'Base Directories' => [],
184
+ 'Brand Projects - AppyDave' => [],
185
+ 'Brand Projects - Other' => [],
186
+ 'Client Projects' => [],
187
+ 'Video Projects' => [],
188
+ 'Ruby Gems' => [],
189
+ 'Reference & Archives' => [],
190
+ 'Other' => []
191
+ }
192
+
193
+ config.locations.each do |loc|
194
+ section = determine_ah_section(loc)
195
+ sections[section] << loc
196
+ end
197
+
198
+ # Remove empty sections and sort locations within each
199
+ sections.reject { |_, locs| locs.empty? }.transform_values do |locs|
200
+ locs.sort_by(&:jump)
201
+ end
202
+ end
203
+
204
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
205
+ def determine_ah_section(loc)
206
+ type = loc.type&.downcase
207
+ brand = loc.brand&.downcase
208
+
209
+ case type
210
+ when 'monorepo', 'config'
211
+ return 'Base Directories' if loc.client.nil? && loc.brand.nil?
212
+ when 'video'
213
+ return 'Video Projects'
214
+ when 'gem'
215
+ return 'Ruby Gems'
216
+ when 'client'
217
+ return 'Client Projects'
218
+ when 'archive', 'reference'
219
+ return 'Reference & Archives'
220
+ end
221
+
222
+ return 'Client Projects' if loc.client
223
+
224
+ case brand
225
+ when 'appydave'
226
+ 'Brand Projects - AppyDave'
227
+ when 'beauty-and-joy', 'david-cruwys', 'aitldr'
228
+ 'Brand Projects - Other'
229
+ else
230
+ 'Other'
231
+ end
232
+ end
233
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
234
+
118
235
  def group_locations_for_aliases
119
236
  grouped = {}
120
237
 
@@ -130,15 +247,132 @@ module Appydave
130
247
  end
131
248
  end
132
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
+
133
363
  def write_or_return(content, path, default_filename)
134
364
  if path
365
+ backup_result = backup_existing_file(path)
135
366
  FileUtils.mkdir_p(File.dirname(path))
136
367
  File.write(path, content)
137
- success_result(
368
+
369
+ result = success_result(
138
370
  message: "Generated #{default_filename}",
139
371
  path: path,
140
372
  lines: content.lines.count
141
373
  )
374
+ result[:backup] = backup_result if backup_result
375
+ result
142
376
  else
143
377
  success_result(
144
378
  content: content,
@@ -146,6 +380,36 @@ module Appydave
146
380
  )
147
381
  end
148
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
149
413
  end
150
414
  end
151
415
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.71.1'
5
+ VERSION = '0.73.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.71.1",
3
+ "version": "0.73.0",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.71.1
4
+ version: 0.73.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-12-15 00:00:00.000000000 Z
11
+ date: 2025-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel