appydave-tools 0.70.0 → 0.71.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/.claude/commands/brainstorming-agent.md +227 -0
- data/.claude/commands/cli-test.md +251 -0
- data/.claude/commands/dev.md +234 -0
- data/.claude/commands/po.md +227 -0
- data/.claude/commands/progress.md +51 -0
- data/.claude/commands/uat.md +321 -0
- data/.rubocop.yml +9 -0
- data/AGENTS.md +43 -0
- data/CHANGELOG.md +12 -0
- data/CLAUDE.md +26 -3
- data/README.md +15 -0
- data/bin/dam +21 -1
- data/bin/jump.rb +29 -0
- data/bin/subtitle_processor.rb +54 -1
- data/bin/zsh_history.rb +846 -0
- data/docs/README.md +162 -69
- data/docs/architecture/cli/exe-bin-convention.md +434 -0
- data/docs/architecture/cli-patterns.md +631 -0
- data/docs/architecture/gpt-context/gpt-context-architecture.md +325 -0
- data/docs/architecture/gpt-context/gpt-context-implementation-guide.md +419 -0
- data/docs/architecture/gpt-context/gpt-context-vision.md +179 -0
- data/docs/architecture/testing/testing-patterns.md +762 -0
- data/docs/backlog.md +120 -0
- data/docs/cli-tests/FR-3-jump-location-tool.md +515 -0
- data/docs/specs/fr-002-gpt-context-help-system.md +265 -0
- data/docs/specs/fr-003-jump-location-tool.md +779 -0
- data/docs/specs/zsh-history-tool.md +820 -0
- data/docs/uat/FR-3-jump-location-tool.md +741 -0
- data/exe/jump +11 -0
- data/exe/{subtitle_manager → subtitle_processor} +1 -1
- data/exe/zsh_history +11 -0
- data/lib/appydave/tools/configuration/openai.rb +1 -1
- data/lib/appydave/tools/dam/file_helper.rb +28 -0
- data/lib/appydave/tools/dam/project_listing.rb +4 -30
- data/lib/appydave/tools/dam/s3_operations.rb +2 -1
- data/lib/appydave/tools/dam/ssd_status.rb +226 -0
- data/lib/appydave/tools/dam/status.rb +3 -51
- data/lib/appydave/tools/jump/cli.rb +561 -0
- data/lib/appydave/tools/jump/commands/add.rb +52 -0
- data/lib/appydave/tools/jump/commands/base.rb +43 -0
- data/lib/appydave/tools/jump/commands/generate.rb +153 -0
- data/lib/appydave/tools/jump/commands/remove.rb +58 -0
- data/lib/appydave/tools/jump/commands/report.rb +214 -0
- data/lib/appydave/tools/jump/commands/update.rb +42 -0
- data/lib/appydave/tools/jump/commands/validate.rb +54 -0
- data/lib/appydave/tools/jump/config.rb +233 -0
- data/lib/appydave/tools/jump/formatters/base.rb +48 -0
- data/lib/appydave/tools/jump/formatters/json_formatter.rb +19 -0
- data/lib/appydave/tools/jump/formatters/paths_formatter.rb +21 -0
- data/lib/appydave/tools/jump/formatters/table_formatter.rb +183 -0
- data/lib/appydave/tools/jump/location.rb +134 -0
- data/lib/appydave/tools/jump/path_validator.rb +47 -0
- data/lib/appydave/tools/jump/search.rb +230 -0
- data/lib/appydave/tools/subtitle_processor/transcript.rb +51 -0
- data/lib/appydave/tools/version.rb +1 -1
- data/lib/appydave/tools/zsh_history/command.rb +37 -0
- data/lib/appydave/tools/zsh_history/config.rb +235 -0
- data/lib/appydave/tools/zsh_history/filter.rb +184 -0
- data/lib/appydave/tools/zsh_history/formatter.rb +75 -0
- data/lib/appydave/tools/zsh_history/parser.rb +101 -0
- data/lib/appydave/tools.rb +25 -0
- data/package.json +1 -1
- metadata +51 -4
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Appydave
|
|
4
|
+
module Tools
|
|
5
|
+
module Jump
|
|
6
|
+
# CLI provides the command-line interface for the Jump tool
|
|
7
|
+
#
|
|
8
|
+
# Uses the Method Dispatch (Full) pattern for 10+ commands with
|
|
9
|
+
# hierarchical help system.
|
|
10
|
+
#
|
|
11
|
+
# @example Usage
|
|
12
|
+
# cli = CLI.new
|
|
13
|
+
# cli.run(['search', 'appydave', 'ruby'])
|
|
14
|
+
# cli.run(['add', '--key', 'my-project', '--path', '~/dev/project'])
|
|
15
|
+
class CLI
|
|
16
|
+
EXIT_SUCCESS = 0
|
|
17
|
+
EXIT_NOT_FOUND = 1
|
|
18
|
+
EXIT_INVALID_INPUT = 2
|
|
19
|
+
EXIT_CONFIG_ERROR = 3
|
|
20
|
+
EXIT_PATH_NOT_FOUND = 4
|
|
21
|
+
|
|
22
|
+
attr_reader :config, :path_validator, :output
|
|
23
|
+
|
|
24
|
+
def initialize(config: nil, path_validator: nil, output: $stdout)
|
|
25
|
+
@path_validator = path_validator || PathValidator.new
|
|
26
|
+
@output = output
|
|
27
|
+
@config = config
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
|
31
|
+
def run(args = ARGV)
|
|
32
|
+
command = args.shift
|
|
33
|
+
|
|
34
|
+
case command
|
|
35
|
+
when nil, '', '--help', '-h'
|
|
36
|
+
show_main_help
|
|
37
|
+
EXIT_SUCCESS
|
|
38
|
+
when '--version', '-v'
|
|
39
|
+
show_version
|
|
40
|
+
EXIT_SUCCESS
|
|
41
|
+
when 'help'
|
|
42
|
+
show_help(args)
|
|
43
|
+
EXIT_SUCCESS
|
|
44
|
+
when 'search'
|
|
45
|
+
run_search(args)
|
|
46
|
+
when 'get'
|
|
47
|
+
run_get(args)
|
|
48
|
+
when 'list'
|
|
49
|
+
run_list(args)
|
|
50
|
+
when 'add'
|
|
51
|
+
run_add(args)
|
|
52
|
+
when 'update'
|
|
53
|
+
run_update(args)
|
|
54
|
+
when 'remove'
|
|
55
|
+
run_remove(args)
|
|
56
|
+
when 'validate'
|
|
57
|
+
run_validate(args)
|
|
58
|
+
when 'report'
|
|
59
|
+
run_report(args)
|
|
60
|
+
when 'generate'
|
|
61
|
+
run_generate(args)
|
|
62
|
+
when 'info'
|
|
63
|
+
run_info(args)
|
|
64
|
+
else
|
|
65
|
+
output.puts "Unknown command: #{command}"
|
|
66
|
+
output.puts "Run 'jump help' for available commands."
|
|
67
|
+
EXIT_INVALID_INPUT
|
|
68
|
+
end
|
|
69
|
+
rescue StandardError => e
|
|
70
|
+
output.puts "Error: #{e.message}"
|
|
71
|
+
output.puts e.backtrace.first(3).join("\n") if ENV['DEBUG']
|
|
72
|
+
EXIT_CONFIG_ERROR
|
|
73
|
+
end
|
|
74
|
+
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def load_config
|
|
79
|
+
@config ||= Config.new
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def format_option(args)
|
|
83
|
+
format_index = args.index('--format') || args.index('-f')
|
|
84
|
+
return 'table' unless format_index
|
|
85
|
+
|
|
86
|
+
format = args[format_index + 1]
|
|
87
|
+
args.delete_at(format_index + 1)
|
|
88
|
+
args.delete_at(format_index)
|
|
89
|
+
format || 'table'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def format_output(result, format)
|
|
93
|
+
formatter = case format
|
|
94
|
+
when 'json'
|
|
95
|
+
Formatters::JsonFormatter.new(result)
|
|
96
|
+
when 'paths'
|
|
97
|
+
Formatters::PathsFormatter.new(result)
|
|
98
|
+
else
|
|
99
|
+
Formatters::TableFormatter.new(result)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
output.puts formatter.format
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def exit_code_for(result)
|
|
106
|
+
return EXIT_SUCCESS if result[:success]
|
|
107
|
+
|
|
108
|
+
case result[:code]
|
|
109
|
+
when 'NOT_FOUND'
|
|
110
|
+
EXIT_NOT_FOUND
|
|
111
|
+
when 'INVALID_INPUT', 'DUPLICATE_KEY', 'CONFIRMATION_REQUIRED'
|
|
112
|
+
EXIT_INVALID_INPUT
|
|
113
|
+
when 'PATH_NOT_FOUND'
|
|
114
|
+
EXIT_PATH_NOT_FOUND
|
|
115
|
+
else
|
|
116
|
+
EXIT_CONFIG_ERROR
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Command implementations
|
|
121
|
+
|
|
122
|
+
def run_search(args)
|
|
123
|
+
format = format_option(args)
|
|
124
|
+
query = args.join(' ')
|
|
125
|
+
|
|
126
|
+
search = Search.new(load_config)
|
|
127
|
+
result = search.search(query)
|
|
128
|
+
|
|
129
|
+
format_output(result, format)
|
|
130
|
+
exit_code_for(result)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def run_get(args)
|
|
134
|
+
format = format_option(args)
|
|
135
|
+
key = args.first
|
|
136
|
+
|
|
137
|
+
unless key
|
|
138
|
+
output.puts 'Usage: jump get <key>'
|
|
139
|
+
return EXIT_INVALID_INPUT
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
search = Search.new(load_config)
|
|
143
|
+
result = search.get(key)
|
|
144
|
+
|
|
145
|
+
format_output(result, format)
|
|
146
|
+
exit_code_for(result)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def run_list(args)
|
|
150
|
+
format = format_option(args)
|
|
151
|
+
|
|
152
|
+
search = Search.new(load_config)
|
|
153
|
+
result = search.list
|
|
154
|
+
|
|
155
|
+
format_output(result, format)
|
|
156
|
+
exit_code_for(result)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def run_add(args)
|
|
160
|
+
format = format_option(args)
|
|
161
|
+
attrs = parse_location_args(args)
|
|
162
|
+
|
|
163
|
+
if attrs[:key].nil?
|
|
164
|
+
output.puts 'Usage: jump add --key <key> --path <path> [options]'
|
|
165
|
+
return EXIT_INVALID_INPUT
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
cmd = Commands::Add.new(load_config, attrs, path_validator: path_validator)
|
|
169
|
+
result = cmd.run
|
|
170
|
+
|
|
171
|
+
format_output(result, format)
|
|
172
|
+
exit_code_for(result)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def run_update(args)
|
|
176
|
+
format = format_option(args)
|
|
177
|
+
key = args.shift
|
|
178
|
+
attrs = parse_location_args(args)
|
|
179
|
+
|
|
180
|
+
unless key
|
|
181
|
+
output.puts 'Usage: jump update <key> [options]'
|
|
182
|
+
return EXIT_INVALID_INPUT
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
cmd = Commands::Update.new(load_config, key, attrs, path_validator: path_validator)
|
|
186
|
+
result = cmd.run
|
|
187
|
+
|
|
188
|
+
format_output(result, format)
|
|
189
|
+
exit_code_for(result)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def run_remove(args)
|
|
193
|
+
format = format_option(args)
|
|
194
|
+
force = args.delete('--force')
|
|
195
|
+
key = args.first
|
|
196
|
+
|
|
197
|
+
unless key
|
|
198
|
+
output.puts 'Usage: jump remove <key> [--force]'
|
|
199
|
+
return EXIT_INVALID_INPUT
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
cmd = Commands::Remove.new(load_config, key, force: !force.nil?, path_validator: path_validator)
|
|
203
|
+
result = cmd.run
|
|
204
|
+
|
|
205
|
+
format_output(result, format)
|
|
206
|
+
exit_code_for(result)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def run_validate(args)
|
|
210
|
+
format = format_option(args)
|
|
211
|
+
key = args.first
|
|
212
|
+
|
|
213
|
+
cmd = Commands::Validate.new(load_config, key: key, path_validator: path_validator)
|
|
214
|
+
result = cmd.run
|
|
215
|
+
|
|
216
|
+
format_output(result, format)
|
|
217
|
+
exit_code_for(result)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def run_report(args)
|
|
221
|
+
format = format_option(args)
|
|
222
|
+
report_type = args.shift
|
|
223
|
+
filter = args.first
|
|
224
|
+
|
|
225
|
+
unless report_type
|
|
226
|
+
output.puts 'Usage: jump report <type> [filter]'
|
|
227
|
+
output.puts 'Types: categories, brands, clients, types, tags, by-brand, by-client, by-type, by-tag, summary'
|
|
228
|
+
return EXIT_INVALID_INPUT
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
cmd = Commands::Report.new(load_config, report_type, filter: filter, path_validator: path_validator)
|
|
232
|
+
result = cmd.run
|
|
233
|
+
|
|
234
|
+
format_output(result, format)
|
|
235
|
+
exit_code_for(result)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def run_generate(args)
|
|
239
|
+
format = format_option(args)
|
|
240
|
+
target = args.shift
|
|
241
|
+
|
|
242
|
+
# Parse output options
|
|
243
|
+
output_path = extract_option(args, '--output')
|
|
244
|
+
output_dir = extract_option(args, '--output-dir')
|
|
245
|
+
|
|
246
|
+
unless target
|
|
247
|
+
output.puts 'Usage: jump generate <target> [--output <file>] [--output-dir <dir>]'
|
|
248
|
+
output.puts 'Targets: aliases, help, all'
|
|
249
|
+
return EXIT_INVALID_INPUT
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
cmd = Commands::Generate.new(
|
|
253
|
+
load_config,
|
|
254
|
+
target,
|
|
255
|
+
output_path: output_path,
|
|
256
|
+
output_dir: output_dir,
|
|
257
|
+
path_validator: path_validator
|
|
258
|
+
)
|
|
259
|
+
result = cmd.run
|
|
260
|
+
|
|
261
|
+
# For generate, if content is present (stdout mode), show it directly
|
|
262
|
+
if result[:content] && !output_path && !output_dir
|
|
263
|
+
output.puts result[:content]
|
|
264
|
+
else
|
|
265
|
+
format_output(result, format)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
exit_code_for(result)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def run_info(args)
|
|
272
|
+
format = format_option(args)
|
|
273
|
+
result = {
|
|
274
|
+
success: true,
|
|
275
|
+
**load_config.info
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
format_output(result, format)
|
|
279
|
+
EXIT_SUCCESS
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Argument parsing helpers
|
|
283
|
+
|
|
284
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
285
|
+
def parse_location_args(args)
|
|
286
|
+
attrs = {}
|
|
287
|
+
|
|
288
|
+
i = 0
|
|
289
|
+
while i < args.length
|
|
290
|
+
case args[i]
|
|
291
|
+
when '--key', '-k'
|
|
292
|
+
attrs[:key] = args[i + 1]
|
|
293
|
+
i += 2
|
|
294
|
+
when '--path', '-p'
|
|
295
|
+
attrs[:path] = args[i + 1]
|
|
296
|
+
i += 2
|
|
297
|
+
when '--jump', '-j'
|
|
298
|
+
attrs[:jump] = args[i + 1]
|
|
299
|
+
i += 2
|
|
300
|
+
when '--brand', '-b'
|
|
301
|
+
attrs[:brand] = args[i + 1]
|
|
302
|
+
i += 2
|
|
303
|
+
when '--client', '-c'
|
|
304
|
+
attrs[:client] = args[i + 1]
|
|
305
|
+
i += 2
|
|
306
|
+
when '--type', '-t'
|
|
307
|
+
attrs[:type] = args[i + 1]
|
|
308
|
+
i += 2
|
|
309
|
+
when '--tags'
|
|
310
|
+
attrs[:tags] = args[i + 1]&.split(',')&.map(&:strip)
|
|
311
|
+
i += 2
|
|
312
|
+
when '--description', '-d'
|
|
313
|
+
attrs[:description] = args[i + 1]
|
|
314
|
+
i += 2
|
|
315
|
+
else
|
|
316
|
+
i += 1
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
attrs
|
|
321
|
+
end
|
|
322
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
323
|
+
|
|
324
|
+
def extract_option(args, flag)
|
|
325
|
+
index = args.index(flag)
|
|
326
|
+
return nil unless index
|
|
327
|
+
|
|
328
|
+
value = args[index + 1]
|
|
329
|
+
args.delete_at(index + 1)
|
|
330
|
+
args.delete_at(index)
|
|
331
|
+
value
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Help display methods
|
|
335
|
+
|
|
336
|
+
def show_version
|
|
337
|
+
output.puts "Jump Location Tool v#{Appydave::Tools::VERSION}"
|
|
338
|
+
output.puts 'Part of appydave-tools gem'
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# rubocop:disable Metrics/MethodLength
|
|
342
|
+
def show_main_help
|
|
343
|
+
output.puts <<~HELP
|
|
344
|
+
Jump - Development Folder Location Manager
|
|
345
|
+
|
|
346
|
+
Usage: jump <command> [options]
|
|
347
|
+
|
|
348
|
+
Search & Retrieval:
|
|
349
|
+
search <terms> Fuzzy search across all location metadata
|
|
350
|
+
get <key> Get location by exact key
|
|
351
|
+
list List all locations
|
|
352
|
+
|
|
353
|
+
CRUD Operations:
|
|
354
|
+
add Add a new location
|
|
355
|
+
update <key> Update an existing location
|
|
356
|
+
remove <key> Remove a location (requires --force)
|
|
357
|
+
|
|
358
|
+
Validation:
|
|
359
|
+
validate [key] Check if paths exist
|
|
360
|
+
|
|
361
|
+
Reports:
|
|
362
|
+
report <type> Generate reports (brands, clients, types, tags, etc.)
|
|
363
|
+
|
|
364
|
+
Generation:
|
|
365
|
+
generate <target> Generate shell aliases or help content
|
|
366
|
+
|
|
367
|
+
Info:
|
|
368
|
+
info Show configuration info
|
|
369
|
+
|
|
370
|
+
Options:
|
|
371
|
+
--format <fmt> Output format: table (default), json, paths
|
|
372
|
+
--help, -h Show this help
|
|
373
|
+
--version, -v Show version
|
|
374
|
+
|
|
375
|
+
Examples:
|
|
376
|
+
jump search appydave ruby
|
|
377
|
+
jump get ad-tools
|
|
378
|
+
jump add --key my-proj --path ~/dev/my-proj --brand appydave
|
|
379
|
+
jump report brands
|
|
380
|
+
jump generate aliases --output ~/.oh-my-zsh/custom/aliases-jump.zsh
|
|
381
|
+
|
|
382
|
+
Run 'jump help <command>' for detailed command help.
|
|
383
|
+
HELP
|
|
384
|
+
end
|
|
385
|
+
# rubocop:enable Metrics/MethodLength
|
|
386
|
+
|
|
387
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
388
|
+
def show_help(args)
|
|
389
|
+
topic = args.first
|
|
390
|
+
|
|
391
|
+
case topic
|
|
392
|
+
when 'search'
|
|
393
|
+
show_search_help
|
|
394
|
+
when 'add'
|
|
395
|
+
show_add_help
|
|
396
|
+
when 'update'
|
|
397
|
+
show_update_help
|
|
398
|
+
when 'remove'
|
|
399
|
+
show_remove_help
|
|
400
|
+
when 'validate'
|
|
401
|
+
show_validate_help
|
|
402
|
+
when 'report'
|
|
403
|
+
show_report_help
|
|
404
|
+
when 'generate'
|
|
405
|
+
show_generate_help
|
|
406
|
+
else
|
|
407
|
+
show_main_help
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
411
|
+
|
|
412
|
+
def show_search_help
|
|
413
|
+
output.puts <<~HELP
|
|
414
|
+
jump search - Fuzzy search locations
|
|
415
|
+
|
|
416
|
+
Usage: jump search <terms> [--format json|table|paths]
|
|
417
|
+
|
|
418
|
+
Searches across all location metadata:
|
|
419
|
+
- Key, path, brand, client, type, tags, description
|
|
420
|
+
|
|
421
|
+
Scoring:
|
|
422
|
+
- Exact key match: 100 points
|
|
423
|
+
- Key contains term: 50 points
|
|
424
|
+
- Brand/client alias: 40 points
|
|
425
|
+
- Tag match: 30 points
|
|
426
|
+
- Type match: 20 points
|
|
427
|
+
- Description contains: 10 points
|
|
428
|
+
- Path contains: 5 points
|
|
429
|
+
|
|
430
|
+
Examples:
|
|
431
|
+
jump search appydave
|
|
432
|
+
jump search ruby cli
|
|
433
|
+
jump search ss app --format json
|
|
434
|
+
HELP
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def show_add_help
|
|
438
|
+
output.puts <<~HELP
|
|
439
|
+
jump add - Add a new location
|
|
440
|
+
|
|
441
|
+
Usage: jump add --key <key> --path <path> [options]
|
|
442
|
+
|
|
443
|
+
Required:
|
|
444
|
+
--key, -k <key> Unique identifier (lowercase, alphanumeric, hyphens)
|
|
445
|
+
--path, -p <path> Directory path (must start with ~ or /)
|
|
446
|
+
|
|
447
|
+
Optional:
|
|
448
|
+
--jump, -j <alias> Shell alias (default: j + key)
|
|
449
|
+
--brand, -b <brand> Associated brand
|
|
450
|
+
--client, -c <client> Associated client
|
|
451
|
+
--type, -t <type> Location type (tool, gem, brand, etc.)
|
|
452
|
+
--tags <t1,t2> Comma-separated tags
|
|
453
|
+
--description, -d Human description
|
|
454
|
+
|
|
455
|
+
Examples:
|
|
456
|
+
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
|
|
458
|
+
HELP
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def show_update_help
|
|
462
|
+
output.puts <<~HELP
|
|
463
|
+
jump update - Update an existing location
|
|
464
|
+
|
|
465
|
+
Usage: jump update <key> [options]
|
|
466
|
+
|
|
467
|
+
Options:
|
|
468
|
+
--path, -p <path> New directory path
|
|
469
|
+
--jump, -j <alias> New shell alias
|
|
470
|
+
--brand, -b <brand> New brand
|
|
471
|
+
--client, -c <client> New client
|
|
472
|
+
--type, -t <type> New type
|
|
473
|
+
--tags <t1,t2> New tags (replaces existing)
|
|
474
|
+
--description, -d New description
|
|
475
|
+
|
|
476
|
+
Examples:
|
|
477
|
+
jump update my-project --description "Updated description"
|
|
478
|
+
jump update ad-tools --tags ruby,cli,youtube
|
|
479
|
+
HELP
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def show_remove_help
|
|
483
|
+
output.puts <<~HELP
|
|
484
|
+
jump remove - Remove a location
|
|
485
|
+
|
|
486
|
+
Usage: jump remove <key> --force
|
|
487
|
+
|
|
488
|
+
The --force flag is required to prevent accidental deletions.
|
|
489
|
+
|
|
490
|
+
Examples:
|
|
491
|
+
jump remove old-project --force
|
|
492
|
+
HELP
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
def show_validate_help
|
|
496
|
+
output.puts <<~HELP
|
|
497
|
+
jump validate - Check if paths exist
|
|
498
|
+
|
|
499
|
+
Usage: jump validate [key]
|
|
500
|
+
|
|
501
|
+
Without key: validates all locations
|
|
502
|
+
With key: validates specific location
|
|
503
|
+
|
|
504
|
+
Examples:
|
|
505
|
+
jump validate
|
|
506
|
+
jump validate ad-tools
|
|
507
|
+
jump validate --format json
|
|
508
|
+
HELP
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def show_report_help
|
|
512
|
+
output.puts <<~HELP
|
|
513
|
+
jump report - Generate reports
|
|
514
|
+
|
|
515
|
+
Usage: jump report <type> [filter]
|
|
516
|
+
|
|
517
|
+
Report Types:
|
|
518
|
+
categories List all category definitions
|
|
519
|
+
brands List brands with location counts
|
|
520
|
+
clients List clients with location counts
|
|
521
|
+
types List types with location counts
|
|
522
|
+
tags List tags with location counts
|
|
523
|
+
by-brand Group locations by brand
|
|
524
|
+
by-client Group locations by client
|
|
525
|
+
by-type Group locations by type
|
|
526
|
+
by-tag Group locations by tag
|
|
527
|
+
summary Overview of all data
|
|
528
|
+
|
|
529
|
+
Examples:
|
|
530
|
+
jump report brands
|
|
531
|
+
jump report by-brand appydave
|
|
532
|
+
jump report tags --format json
|
|
533
|
+
HELP
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def show_generate_help
|
|
537
|
+
output.puts <<~HELP
|
|
538
|
+
jump generate - Generate shell aliases or help content
|
|
539
|
+
|
|
540
|
+
Usage: jump generate <target> [options]
|
|
541
|
+
|
|
542
|
+
Targets:
|
|
543
|
+
aliases Generate shell alias file (aliases-jump.zsh)
|
|
544
|
+
help Generate help content for fzf (jump-help.txt)
|
|
545
|
+
all Generate both files
|
|
546
|
+
|
|
547
|
+
Options:
|
|
548
|
+
--output <file> Write to specific file (for aliases, help)
|
|
549
|
+
--output-dir <dir> Write to directory (for all)
|
|
550
|
+
|
|
551
|
+
Examples:
|
|
552
|
+
jump generate aliases
|
|
553
|
+
jump generate aliases --output ~/.oh-my-zsh/custom/aliases-jump.zsh
|
|
554
|
+
jump generate help --output ~/.oh-my-zsh/custom/data/jump-help.txt
|
|
555
|
+
jump generate all --output-dir ~/.oh-my-zsh/custom/
|
|
556
|
+
HELP
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Appydave
|
|
4
|
+
module Tools
|
|
5
|
+
module Jump
|
|
6
|
+
module Commands
|
|
7
|
+
# Add command creates a new location entry
|
|
8
|
+
class Add < Base
|
|
9
|
+
attr_reader :attrs
|
|
10
|
+
|
|
11
|
+
def initialize(config, attrs, path_validator: PathValidator.new, **options)
|
|
12
|
+
super(config, path_validator: path_validator, **options)
|
|
13
|
+
@attrs = attrs
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run
|
|
17
|
+
# Validate required fields
|
|
18
|
+
return error_result('Key is required', code: 'INVALID_INPUT') if attrs[:key].nil? || attrs[:key].empty?
|
|
19
|
+
return error_result('Path is required', code: 'INVALID_INPUT') if attrs[:path].nil? || attrs[:path].empty?
|
|
20
|
+
|
|
21
|
+
# Check for duplicate
|
|
22
|
+
return error_result("Location '#{attrs[:key]}' already exists", code: 'DUPLICATE_KEY') if config.key_exists?(attrs[:key])
|
|
23
|
+
|
|
24
|
+
# Validate path exists (optional warning)
|
|
25
|
+
unless path_validator.exists?(attrs[:path])
|
|
26
|
+
# Just warn, don't fail - path might be created later
|
|
27
|
+
@path_warning = "Warning: Path '#{attrs[:path]}' does not exist"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Create and validate location
|
|
31
|
+
location = Location.new(attrs)
|
|
32
|
+
errors = location.validate
|
|
33
|
+
return error_result("Invalid location: #{errors.join(', ')}", code: 'INVALID_INPUT') unless errors.empty?
|
|
34
|
+
|
|
35
|
+
# Add to config
|
|
36
|
+
config.add(location)
|
|
37
|
+
config.save
|
|
38
|
+
|
|
39
|
+
result = success_result(
|
|
40
|
+
message: "Location '#{attrs[:key]}' added successfully",
|
|
41
|
+
location: location.to_h
|
|
42
|
+
)
|
|
43
|
+
result[:warning] = @path_warning if @path_warning
|
|
44
|
+
result
|
|
45
|
+
rescue ArgumentError => e
|
|
46
|
+
error_result(e.message, code: 'INVALID_INPUT')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Appydave
|
|
4
|
+
module Tools
|
|
5
|
+
module Jump
|
|
6
|
+
module Commands
|
|
7
|
+
# Base command class for Jump CLI commands
|
|
8
|
+
class Base
|
|
9
|
+
attr_reader :config, :path_validator, :options
|
|
10
|
+
|
|
11
|
+
def initialize(config, path_validator: PathValidator.new, **options)
|
|
12
|
+
@config = config
|
|
13
|
+
@path_validator = path_validator
|
|
14
|
+
@options = options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Execute the command
|
|
18
|
+
#
|
|
19
|
+
# @return [Hash] Result hash with success status
|
|
20
|
+
def run
|
|
21
|
+
raise NotImplementedError, 'Subclasses must implement #run'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
def success_result(data = {})
|
|
27
|
+
{ success: true }.merge(data)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def error_result(message, code: 'ERROR', suggestion: nil)
|
|
31
|
+
result = {
|
|
32
|
+
success: false,
|
|
33
|
+
error: message,
|
|
34
|
+
code: code
|
|
35
|
+
}
|
|
36
|
+
result[:suggestion] = suggestion if suggestion
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|