appydave-tools 0.69.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 +11 -0
- data/AGENTS.md +43 -0
- data/CHANGELOG.md +24 -0
- data/CLAUDE.md +96 -3
- data/README.md +15 -0
- data/bin/dam +39 -7
- 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 -68
- 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/dam/batch-s3-listing-requirements.md +780 -0
- data/docs/guides/tools/video-file-namer.md +400 -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 +220 -138
- data/lib/appydave/tools/dam/s3_operations.rb +112 -60
- 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 +53 -4
data/exe/jump
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
5
|
+
|
|
6
|
+
require 'appydave/tools'
|
|
7
|
+
|
|
8
|
+
# Set $PROGRAM_NAME to the bin/jump.rb file so the guard passes
|
|
9
|
+
$PROGRAM_NAME = File.expand_path('../bin/jump.rb', __dir__)
|
|
10
|
+
|
|
11
|
+
load $PROGRAM_NAME
|
data/exe/zsh_history
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
|
5
|
+
|
|
6
|
+
require 'appydave/tools'
|
|
7
|
+
|
|
8
|
+
# Set $PROGRAM_NAME to the bin file so the guard passes
|
|
9
|
+
$PROGRAM_NAME = File.expand_path('../bin/zsh_history.rb', __dir__)
|
|
10
|
+
|
|
11
|
+
load $PROGRAM_NAME
|
|
@@ -8,7 +8,7 @@ OpenAI.configure do |config|
|
|
|
8
8
|
tools_enabled = ENV.fetch('TOOLS_ENABLED', 'false')
|
|
9
9
|
|
|
10
10
|
if tools_enabled == 'true'
|
|
11
|
-
puts 'Tools are enabled, OpenAI will allow net connections'
|
|
11
|
+
# puts 'Tools are enabled, OpenAI will allow net connections'
|
|
12
12
|
config.access_token = ENV.fetch('OPENAI_ACCESS_TOKEN')
|
|
13
13
|
config.organization_id = ENV.fetch('OPENAI_ORGANIZATION_ID', nil)
|
|
14
14
|
end
|
|
@@ -37,6 +37,34 @@ module Appydave
|
|
|
37
37
|
|
|
38
38
|
format('%<size>.1f %<unit>s', size: bytes.to_f / (1024**exp), unit: units[exp])
|
|
39
39
|
end
|
|
40
|
+
|
|
41
|
+
# Format time as relative age (e.g., "3d", "2w", "1mo")
|
|
42
|
+
# @param time [Time, nil] Time to format
|
|
43
|
+
# @return [String] Relative age string
|
|
44
|
+
def format_age(time)
|
|
45
|
+
return 'N/A' if time.nil?
|
|
46
|
+
|
|
47
|
+
seconds = Time.now - time
|
|
48
|
+
return 'just now' if seconds < 60
|
|
49
|
+
|
|
50
|
+
minutes = seconds / 60
|
|
51
|
+
return "#{minutes.round}m" if minutes < 60
|
|
52
|
+
|
|
53
|
+
hours = minutes / 60
|
|
54
|
+
return "#{hours.round}h" if hours < 24
|
|
55
|
+
|
|
56
|
+
days = hours / 24
|
|
57
|
+
return "#{days.round}d" if days < 7
|
|
58
|
+
|
|
59
|
+
weeks = days / 7
|
|
60
|
+
return "#{weeks.round}w" if weeks < 4
|
|
61
|
+
|
|
62
|
+
months = days / 30
|
|
63
|
+
return "#{months.round}mo" if months < 12
|
|
64
|
+
|
|
65
|
+
years = days / 365
|
|
66
|
+
"#{years.round}y"
|
|
67
|
+
end
|
|
40
68
|
end
|
|
41
69
|
end
|
|
42
70
|
end
|
|
@@ -11,7 +11,7 @@ module Appydave
|
|
|
11
11
|
class ProjectListing
|
|
12
12
|
# List all brands with summary table
|
|
13
13
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
14
|
-
def self.list_brands_with_counts(detailed: false)
|
|
14
|
+
def self.list_brands_with_counts(detailed: false, s3: false)
|
|
15
15
|
brands = Config.available_brands
|
|
16
16
|
|
|
17
17
|
if brands.empty?
|
|
@@ -19,64 +19,113 @@ module Appydave
|
|
|
19
19
|
return
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
# Gather brand data
|
|
23
|
-
brand_data = brands.map { |brand| collect_brand_data(brand, detailed: detailed) }
|
|
22
|
+
# Gather brand data (skip S3 if not requested)
|
|
23
|
+
brand_data = brands.map { |brand| collect_brand_data(brand, detailed: detailed, s3: s3) }
|
|
24
24
|
|
|
25
25
|
if detailed
|
|
26
26
|
# Detailed view with additional columns
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
if s3
|
|
28
|
+
header = 'BRAND KEY PROJECTS SIZE LAST MODIFIED ' \
|
|
29
|
+
'GIT S3 SYNC PATH SSD BACKUP ' \
|
|
30
|
+
'WORKFLOW ACTIVE'
|
|
31
|
+
puts header
|
|
32
|
+
puts '-' * 200
|
|
33
|
+
else
|
|
34
|
+
header = 'BRAND KEY PROJECTS SIZE LAST MODIFIED ' \
|
|
35
|
+
'GIT PATH SSD BACKUP ' \
|
|
36
|
+
'WORKFLOW ACTIVE'
|
|
37
|
+
puts header
|
|
38
|
+
puts '-' * 189
|
|
39
|
+
end
|
|
32
40
|
|
|
33
41
|
brand_data.each do |data|
|
|
34
42
|
brand_display = "#{data[:shortcut]} - #{data[:name]}"
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
if s3
|
|
45
|
+
puts format(
|
|
46
|
+
'%-30s %-12s %10d %12s %20s %-15s %-10s %-35s %-30s %-10s %6d',
|
|
47
|
+
brand_display,
|
|
48
|
+
data[:key],
|
|
49
|
+
data[:count],
|
|
50
|
+
format_size(data[:size]),
|
|
51
|
+
format_date(data[:modified]),
|
|
52
|
+
data[:git_status],
|
|
53
|
+
data[:s3_sync],
|
|
54
|
+
shorten_path(data[:path]),
|
|
55
|
+
data[:ssd_backup] || 'N/A',
|
|
56
|
+
data[:workflow] || 'N/A',
|
|
57
|
+
data[:active_count] || 0
|
|
58
|
+
)
|
|
59
|
+
else
|
|
60
|
+
puts format(
|
|
61
|
+
'%-30s %-12s %10d %12s %20s %-15s %-35s %-30s %-10s %6d',
|
|
62
|
+
brand_display,
|
|
63
|
+
data[:key],
|
|
64
|
+
data[:count],
|
|
65
|
+
format_size(data[:size]),
|
|
66
|
+
format_date(data[:modified]),
|
|
67
|
+
data[:git_status],
|
|
68
|
+
shorten_path(data[:path]),
|
|
69
|
+
data[:ssd_backup] || 'N/A',
|
|
70
|
+
data[:workflow] || 'N/A',
|
|
71
|
+
data[:active_count] || 0
|
|
72
|
+
)
|
|
73
|
+
end
|
|
50
74
|
end
|
|
51
75
|
else
|
|
52
76
|
# Default view - use same format for header and data
|
|
53
77
|
# rubocop:disable Style/RedundantFormat
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
if s3
|
|
79
|
+
puts format(
|
|
80
|
+
'%-30s %-15s %10s %12s %20s %-15s %-10s',
|
|
81
|
+
'BRAND',
|
|
82
|
+
'KEY',
|
|
83
|
+
'PROJECTS',
|
|
84
|
+
'SIZE',
|
|
85
|
+
'LAST MODIFIED',
|
|
86
|
+
'GIT',
|
|
87
|
+
'S3 SYNC'
|
|
88
|
+
)
|
|
89
|
+
puts '-' * 133
|
|
90
|
+
else
|
|
91
|
+
puts format(
|
|
92
|
+
'%-30s %-15s %10s %12s %20s %-15s',
|
|
93
|
+
'BRAND',
|
|
94
|
+
'KEY',
|
|
95
|
+
'PROJECTS',
|
|
96
|
+
'SIZE',
|
|
97
|
+
'LAST MODIFIED',
|
|
98
|
+
'GIT'
|
|
99
|
+
)
|
|
100
|
+
puts '-' * 122
|
|
101
|
+
end
|
|
64
102
|
# rubocop:enable Style/RedundantFormat
|
|
65
|
-
puts '-' * 133
|
|
66
103
|
|
|
67
104
|
brand_data.each do |data|
|
|
68
105
|
brand_display = "#{data[:shortcut]} - #{data[:name]}"
|
|
69
106
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
if s3
|
|
108
|
+
puts format(
|
|
109
|
+
'%-30s %-15s %10d %12s %20s %-15s %-10s',
|
|
110
|
+
brand_display,
|
|
111
|
+
data[:key],
|
|
112
|
+
data[:count],
|
|
113
|
+
format_size(data[:size]),
|
|
114
|
+
format_date(data[:modified]),
|
|
115
|
+
data[:git_status],
|
|
116
|
+
data[:s3_sync]
|
|
117
|
+
)
|
|
118
|
+
else
|
|
119
|
+
puts format(
|
|
120
|
+
'%-30s %-15s %10d %12s %20s %-15s',
|
|
121
|
+
brand_display,
|
|
122
|
+
data[:key],
|
|
123
|
+
data[:count],
|
|
124
|
+
format_size(data[:size]),
|
|
125
|
+
format_date(data[:modified]),
|
|
126
|
+
data[:git_status]
|
|
127
|
+
)
|
|
128
|
+
end
|
|
80
129
|
end
|
|
81
130
|
end
|
|
82
131
|
|
|
@@ -93,7 +142,7 @@ module Appydave
|
|
|
93
142
|
|
|
94
143
|
# List all projects for a specific brand (Mode 3)
|
|
95
144
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
96
|
-
def self.list_brand_projects(brand_arg, detailed: false)
|
|
145
|
+
def self.list_brand_projects(brand_arg, detailed: false, s3: false)
|
|
97
146
|
# ProjectResolver expects the original brand key/shortcut, not the expanded v-* version
|
|
98
147
|
projects = ProjectResolver.list_projects(brand_arg)
|
|
99
148
|
|
|
@@ -114,13 +163,13 @@ module Appydave
|
|
|
114
163
|
return
|
|
115
164
|
end
|
|
116
165
|
|
|
117
|
-
# Gather project data
|
|
166
|
+
# Gather project data (skip S3 if not requested)
|
|
118
167
|
brand_path = Config.brand_path(brand_arg)
|
|
119
168
|
brand_info = Appydave::Tools::Configuration::Config.brands.get_brand(brand_arg)
|
|
120
169
|
is_git_repo = Dir.exist?(File.join(brand_path, '.git'))
|
|
121
170
|
|
|
122
171
|
project_data = projects.map do |project|
|
|
123
|
-
collect_project_data(brand_arg, brand_path, brand_info, project, is_git_repo, detailed: detailed)
|
|
172
|
+
collect_project_data(brand_arg, brand_path, brand_info, project, is_git_repo, detailed: detailed, s3: s3)
|
|
124
173
|
end
|
|
125
174
|
|
|
126
175
|
# Print common header
|
|
@@ -132,67 +181,119 @@ module Appydave
|
|
|
132
181
|
if detailed
|
|
133
182
|
# Detailed view with additional columns - use same format for header and data
|
|
134
183
|
# rubocop:disable Style/RedundantFormat
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
184
|
+
if s3
|
|
185
|
+
puts format(
|
|
186
|
+
'%-45s %12s %15s %-15s %-12s %-65s %-18s %-18s %-30s %-15s %-15s',
|
|
187
|
+
'PROJECT',
|
|
188
|
+
'SIZE',
|
|
189
|
+
'AGE',
|
|
190
|
+
'GIT',
|
|
191
|
+
'S3',
|
|
192
|
+
'PATH',
|
|
193
|
+
'HEAVY FILES',
|
|
194
|
+
'LIGHT FILES',
|
|
195
|
+
'SSD BACKUP',
|
|
196
|
+
'S3 ↑ UPLOAD',
|
|
197
|
+
'S3 ↓ DOWNLOAD'
|
|
198
|
+
)
|
|
199
|
+
puts '-' * 280
|
|
200
|
+
else
|
|
201
|
+
puts format(
|
|
202
|
+
'%-45s %12s %15s %-15s %-65s %-18s %-18s %-30s',
|
|
203
|
+
'PROJECT',
|
|
204
|
+
'SIZE',
|
|
205
|
+
'AGE',
|
|
206
|
+
'GIT',
|
|
207
|
+
'PATH',
|
|
208
|
+
'HEAVY FILES',
|
|
209
|
+
'LIGHT FILES',
|
|
210
|
+
'SSD BACKUP'
|
|
211
|
+
)
|
|
212
|
+
puts '-' * 239
|
|
213
|
+
end
|
|
149
214
|
# rubocop:enable Style/RedundantFormat
|
|
150
|
-
puts '-' * 280
|
|
151
215
|
|
|
152
216
|
project_data.each do |data|
|
|
153
217
|
age_display = data[:stale] ? "#{data[:age]} ⚠️" : data[:age]
|
|
154
|
-
s3_upload = data[:s3_last_upload] ? format_age(data[:s3_last_upload]) : 'N/A'
|
|
155
|
-
s3_download = data[:s3_last_download] ? format_age(data[:s3_last_download]) : 'N/A'
|
|
156
218
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
data[:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
219
|
+
if s3
|
|
220
|
+
s3_upload = data[:s3_last_upload] ? FileHelper.format_age(data[:s3_last_upload]) : 'N/A'
|
|
221
|
+
s3_download = data[:s3_last_download] ? FileHelper.format_age(data[:s3_last_download]) : 'N/A'
|
|
222
|
+
|
|
223
|
+
puts format(
|
|
224
|
+
'%-45s %12s %15s %-15s %-12s %-65s %-18s %-18s %-30s %-15s %-15s',
|
|
225
|
+
data[:name],
|
|
226
|
+
format_size(data[:size]),
|
|
227
|
+
age_display,
|
|
228
|
+
data[:git_status],
|
|
229
|
+
data[:s3_sync],
|
|
230
|
+
shorten_path(data[:path]),
|
|
231
|
+
data[:heavy_files] || 'N/A',
|
|
232
|
+
data[:light_files] || 'N/A',
|
|
233
|
+
data[:ssd_backup] || 'N/A',
|
|
234
|
+
s3_upload,
|
|
235
|
+
s3_download
|
|
236
|
+
)
|
|
237
|
+
else
|
|
238
|
+
puts format(
|
|
239
|
+
'%-45s %12s %15s %-15s %-65s %-18s %-18s %-30s',
|
|
240
|
+
data[:name],
|
|
241
|
+
format_size(data[:size]),
|
|
242
|
+
age_display,
|
|
243
|
+
data[:git_status],
|
|
244
|
+
shorten_path(data[:path]),
|
|
245
|
+
data[:heavy_files] || 'N/A',
|
|
246
|
+
data[:light_files] || 'N/A',
|
|
247
|
+
data[:ssd_backup] || 'N/A'
|
|
248
|
+
)
|
|
249
|
+
end
|
|
171
250
|
end
|
|
172
251
|
else
|
|
173
252
|
# Default view - use same format for header and data
|
|
174
253
|
# rubocop:disable Style/RedundantFormat
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
254
|
+
if s3
|
|
255
|
+
puts format(
|
|
256
|
+
'%-45s %12s %15s %-15s %-12s',
|
|
257
|
+
'PROJECT',
|
|
258
|
+
'SIZE',
|
|
259
|
+
'AGE',
|
|
260
|
+
'GIT',
|
|
261
|
+
'S3'
|
|
262
|
+
)
|
|
263
|
+
puts '-' * 130
|
|
264
|
+
else
|
|
265
|
+
puts format(
|
|
266
|
+
'%-45s %12s %15s %-15s',
|
|
267
|
+
'PROJECT',
|
|
268
|
+
'SIZE',
|
|
269
|
+
'AGE',
|
|
270
|
+
'GIT'
|
|
271
|
+
)
|
|
272
|
+
puts '-' * 117
|
|
273
|
+
end
|
|
183
274
|
# rubocop:enable Style/RedundantFormat
|
|
184
|
-
puts '-' * 130
|
|
185
275
|
|
|
186
276
|
project_data.each do |data|
|
|
187
277
|
age_display = data[:stale] ? "#{data[:age]} ⚠️" : data[:age]
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
278
|
+
|
|
279
|
+
if s3
|
|
280
|
+
puts format(
|
|
281
|
+
'%-45s %12s %15s %-15s %-12s',
|
|
282
|
+
data[:name],
|
|
283
|
+
format_size(data[:size]),
|
|
284
|
+
age_display,
|
|
285
|
+
data[:git_status],
|
|
286
|
+
data[:s3_sync]
|
|
287
|
+
)
|
|
288
|
+
else
|
|
289
|
+
puts format(
|
|
290
|
+
'%-45s %12s %15s %-15s',
|
|
291
|
+
data[:name],
|
|
292
|
+
format_size(data[:size]),
|
|
293
|
+
age_display,
|
|
294
|
+
data[:git_status]
|
|
295
|
+
)
|
|
296
|
+
end
|
|
196
297
|
end
|
|
197
298
|
end
|
|
198
299
|
|
|
@@ -237,7 +338,7 @@ module Appydave
|
|
|
237
338
|
path: project_path,
|
|
238
339
|
size: size,
|
|
239
340
|
modified: modified,
|
|
240
|
-
age: format_age(modified),
|
|
341
|
+
age: FileHelper.format_age(modified),
|
|
241
342
|
stale: stale?(modified)
|
|
242
343
|
}
|
|
243
344
|
end
|
|
@@ -313,7 +414,7 @@ module Appydave
|
|
|
313
414
|
|
|
314
415
|
# Collect brand data for display
|
|
315
416
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
316
|
-
def self.collect_brand_data(brand, detailed: false)
|
|
417
|
+
def self.collect_brand_data(brand, detailed: false, s3: false)
|
|
317
418
|
Appydave::Tools::Configuration::Config.configure
|
|
318
419
|
brand_info = Appydave::Tools::Configuration::Config.brands.get_brand(brand)
|
|
319
420
|
brand_path = Config.brand_path(brand)
|
|
@@ -331,8 +432,8 @@ module Appydave
|
|
|
331
432
|
# Get git status
|
|
332
433
|
git_status = calculate_git_status(brand_path)
|
|
333
434
|
|
|
334
|
-
# Get S3 sync status (count of projects with s3-staging)
|
|
335
|
-
s3_sync_status = calculate_s3_sync_status(brand, projects)
|
|
435
|
+
# Get S3 sync status (count of projects with s3-staging) - only if requested
|
|
436
|
+
s3_sync_status = s3 ? calculate_s3_sync_status(brand, projects) : 'N/A'
|
|
336
437
|
|
|
337
438
|
result = {
|
|
338
439
|
shortcut: shortcut || key,
|
|
@@ -449,7 +550,7 @@ module Appydave
|
|
|
449
550
|
|
|
450
551
|
# Collect project data for display
|
|
451
552
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/ParameterLists
|
|
452
|
-
def self.collect_project_data(brand_arg, brand_path, brand_info, project, is_git_repo, detailed: false)
|
|
553
|
+
def self.collect_project_data(brand_arg, brand_path, brand_info, project, is_git_repo, detailed: false, s3: false)
|
|
453
554
|
project_path = Config.project_path(brand_arg, project)
|
|
454
555
|
size = FileHelper.calculate_directory_size(project_path)
|
|
455
556
|
modified = File.mtime(project_path)
|
|
@@ -461,15 +562,15 @@ module Appydave
|
|
|
461
562
|
'N/A'
|
|
462
563
|
end
|
|
463
564
|
|
|
464
|
-
# Calculate 3-state S3 sync status
|
|
465
|
-
s3_sync = calculate_project_s3_sync_status(brand_arg, brand_info, project)
|
|
565
|
+
# Calculate 3-state S3 sync status - only if requested (performance optimization)
|
|
566
|
+
s3_sync = s3 ? calculate_project_s3_sync_status(brand_arg, brand_info, project) : 'N/A'
|
|
466
567
|
|
|
467
568
|
result = {
|
|
468
569
|
name: project,
|
|
469
570
|
path: project_path,
|
|
470
571
|
size: size,
|
|
471
572
|
modified: modified,
|
|
472
|
-
age: format_age(modified),
|
|
573
|
+
age: FileHelper.format_age(modified),
|
|
473
574
|
stale: stale?(modified),
|
|
474
575
|
git_status: git_status,
|
|
475
576
|
s3_sync: s3_sync
|
|
@@ -500,16 +601,23 @@ module Appydave
|
|
|
500
601
|
File.exist?(ssd_project_path) ? shorten_path(ssd_project_path) : nil
|
|
501
602
|
end
|
|
502
603
|
|
|
503
|
-
# S3 timestamps (last upload/download)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
604
|
+
# S3 timestamps (last upload/download) - only if requested (performance optimization)
|
|
605
|
+
if s3
|
|
606
|
+
s3_timestamps = calculate_s3_timestamps(brand_arg, brand_info, project)
|
|
607
|
+
result.merge!(
|
|
608
|
+
heavy_files: "#{heavy_count} (#{format_size(heavy_size)})",
|
|
609
|
+
light_files: "#{light_count} (#{format_size(light_size)})",
|
|
610
|
+
ssd_backup: ssd_path,
|
|
611
|
+
s3_last_upload: s3_timestamps[:last_upload],
|
|
612
|
+
s3_last_download: s3_timestamps[:last_download]
|
|
613
|
+
)
|
|
614
|
+
else
|
|
615
|
+
result.merge!(
|
|
616
|
+
heavy_files: "#{heavy_count} (#{format_size(heavy_size)})",
|
|
617
|
+
light_files: "#{light_count} (#{format_size(light_size)})",
|
|
618
|
+
ssd_backup: ssd_path
|
|
619
|
+
)
|
|
620
|
+
end
|
|
513
621
|
end
|
|
514
622
|
|
|
515
623
|
result
|
|
@@ -544,32 +652,6 @@ module Appydave
|
|
|
544
652
|
time.strftime('%Y-%m-%d %H:%M')
|
|
545
653
|
end
|
|
546
654
|
|
|
547
|
-
# Format age as relative time (e.g., "3 days", "2 weeks")
|
|
548
|
-
def self.format_age(time)
|
|
549
|
-
return 'N/A' if time.nil?
|
|
550
|
-
|
|
551
|
-
seconds = Time.now - time
|
|
552
|
-
return 'just now' if seconds < 60
|
|
553
|
-
|
|
554
|
-
minutes = seconds / 60
|
|
555
|
-
return "#{minutes.round}m" if minutes < 60
|
|
556
|
-
|
|
557
|
-
hours = minutes / 60
|
|
558
|
-
return "#{hours.round}h" if hours < 24
|
|
559
|
-
|
|
560
|
-
days = hours / 24
|
|
561
|
-
return "#{days.round}d" if days < 7
|
|
562
|
-
|
|
563
|
-
weeks = days / 7
|
|
564
|
-
return "#{weeks.round}w" if weeks < 4
|
|
565
|
-
|
|
566
|
-
months = days / 30
|
|
567
|
-
return "#{months.round}mo" if months < 12
|
|
568
|
-
|
|
569
|
-
years = days / 365
|
|
570
|
-
"#{years.round}y"
|
|
571
|
-
end
|
|
572
|
-
|
|
573
655
|
# Check if project is stale (>90 days old)
|
|
574
656
|
def self.stale?(time)
|
|
575
657
|
return false if time.nil?
|