pindo 5.2.4 → 5.4.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pindo/base/aeshelper.rb +23 -2
  3. data/lib/pindo/base/pindocontext.rb +476 -0
  4. data/lib/pindo/client/pgyer_feishu_oauth_cli.rb +343 -80
  5. data/lib/pindo/client/pgyerclient.rb +30 -20
  6. data/lib/pindo/command/android/autobuild.rb +58 -22
  7. data/lib/pindo/command/android/build.rb +27 -16
  8. data/lib/pindo/command/android/debug.rb +25 -15
  9. data/lib/pindo/command/dev/debug.rb +2 -51
  10. data/lib/pindo/command/dev/feishu.rb +19 -2
  11. data/lib/pindo/command/ios/adhoc.rb +2 -1
  12. data/lib/pindo/command/ios/autobuild.rb +39 -9
  13. data/lib/pindo/command/ios/build.rb +7 -0
  14. data/lib/pindo/command/ios/debug.rb +2 -132
  15. data/lib/pindo/command/lib/lint.rb +24 -1
  16. data/lib/pindo/command/setup.rb +24 -4
  17. data/lib/pindo/command/unity/apk.rb +20 -0
  18. data/lib/pindo/command/unity/ipa.rb +27 -3
  19. data/lib/pindo/command/unity/web.rb +15 -10
  20. data/lib/pindo/command/web/autobuild.rb +5 -0
  21. data/lib/pindo/command.rb +58 -1
  22. data/lib/pindo/module/android/android_build_config_helper.rb +427 -0
  23. data/lib/pindo/module/android/apk_helper.rb +23 -25
  24. data/lib/pindo/module/android/base_helper.rb +572 -0
  25. data/lib/pindo/module/android/build_helper.rb +8 -318
  26. data/lib/pindo/module/android/gp_compliance_helper.rb +668 -0
  27. data/lib/pindo/module/android/gradle_helper.rb +746 -3
  28. data/lib/pindo/module/appselect.rb +18 -5
  29. data/lib/pindo/module/build/buildhelper.rb +120 -29
  30. data/lib/pindo/module/build/unityhelper.rb +674 -18
  31. data/lib/pindo/module/build/versionhelper.rb +146 -0
  32. data/lib/pindo/module/cert/certhelper.rb +33 -2
  33. data/lib/pindo/module/cert/xcodecerthelper.rb +3 -1
  34. data/lib/pindo/module/pgyer/pgyerhelper.rb +114 -31
  35. data/lib/pindo/module/xcode/xcodebuildconfig.rb +232 -0
  36. data/lib/pindo/module/xcode/xcodebuildhelper.rb +0 -1
  37. data/lib/pindo/version.rb +356 -86
  38. data/lib/pindo.rb +72 -3
  39. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 235be197b7b9f5717b54477a778d7b27387ce4b12d72f3d4cf4bccb7f8267df3
4
- data.tar.gz: 619ee059cd180a87cd699417f7a0b3021ee88223ca82565dad183edcbb2df5e7
3
+ metadata.gz: 56fcfdba92a710c2c7ab85226d30f305cbb1fca5d3fccb57d5c62e6593c8afbb
4
+ data.tar.gz: a2d7a5c80ca642869ce4f79ef9dca35383b7426c5cf15077937137db899b4eab
5
5
  SHA512:
6
- metadata.gz: 317f3cc6730620555bfe942315f75261cb91c52706820d65aa81ce76d1abb2ab0fd4861fe0392ed085a3567d4c3307cfd64bd64f3ded724efd776c9011ee3bcd
7
- data.tar.gz: fd961b915306b88886c1ae987774da7fc535e55c54f9599221ce5e342ab14182177ea555895de3a9d2277864f28de78935075ee31b99552c7fbb4ab7384a7d2b
6
+ metadata.gz: 54cef915058ddb2d765e136181c13b1e1e6e1376316e8b443a19e54bf03da4afb7763564348c7fe04099cb87476e3547664016dd479936423243360036a9fbe4
7
+ data.tar.gz: 86a5729f1929db20b938254ae541c1310fce8dba85d7d0088966f197aad668723c11270775bbdcd8651ca86609d357e69fac7dfd8e6d1459133d58fe9357bccd
@@ -8,7 +8,7 @@ module Pindo
8
8
  module AESHelper
9
9
 
10
10
 
11
- def self.fetch_password(keychain_name:nil)
11
+ def self.fetch_password(keychain_name:nil, test_file:nil)
12
12
  # password = ENV["MATCH_PASSWORD"]
13
13
 
14
14
  server_name = ["match", keychain_name].join("_")
@@ -18,9 +18,30 @@ module Pindo
18
18
  password = item.password if item
19
19
 
20
20
  unless password
21
+ puts "\e[33m[DEBUG] Keychain中未找到密码,需要用户输入: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
21
22
  password = FastlaneCore::Helper.ask_password(message: "请输入证书仓库的加密密码: ", confirm: true)
22
- Security::InternetPassword.add(server_name, "", password)
23
+ # 尝试添加密码到Keychain,如果已存在则先删除再添加
24
+ begin
25
+ # 先检查是否已存在,如果存在则删除
26
+ existing_item = Security::InternetPassword.find(server: server_name)
27
+ if existing_item
28
+ # 重定向stderr到/dev/null来隐藏Keychain的详细输出
29
+ system("security delete-internet-password -s '#{server_name}' 2>/dev/null")
30
+ puts "\e[33m[DEBUG] 删除Keychain中的旧密码项: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
31
+ end
32
+
33
+ # 添加新密码,重定向stderr到/dev/null来隐藏Keychain的详细输出
34
+ system("security add-internet-password -s '#{server_name}' -w '#{password}' 2>/dev/null")
35
+ puts "\e[32m[DEBUG] 密码已保存到Keychain: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
36
+ rescue => e
37
+ # 忽略Keychain错误,继续使用密码
38
+ # 错误信息可能包含 "already exists" 等,但不影响功能
39
+ puts "\e[31m[DEBUG] Keychain操作错误: #{e.message}\e[0m" if ENV['PINDO_DEBUG']
40
+ end
41
+ else
42
+ puts "\e[32m[DEBUG] 从Keychain获取密码成功: #{server_name}\e[0m" if ENV['PINDO_DEBUG']
23
43
  end
44
+
24
45
  return password
25
46
  end
26
47
 
@@ -0,0 +1,476 @@
1
+ require 'singleton'
2
+ require 'digest'
3
+ require 'json'
4
+ require 'fileutils'
5
+
6
+ module Pindo
7
+ class PindoContext
8
+ include Singleton
9
+
10
+ attr_reader :current_command, :current_directory
11
+
12
+ def initialize
13
+ @current_command = nil # 根命令(第一个设置的命令)
14
+ @current_directory = nil # 项目路径
15
+ @memory_selections = {} # 三级结构的内存缓存
16
+ @file_cache_loaded = false # 标记文件缓存是否已加载
17
+ @cache_enabled = false # 默认禁用缓存
18
+ @command_group = nil # 命令组名称
19
+ ensure_cache_dir
20
+ end
21
+
22
+ # 设置当前上下文
23
+ def set_context(command, directory = nil, options = {})
24
+ # 如果已有命令在执行,这是嵌套调用,不需要做任何事
25
+ unless @current_command.nil?
26
+ puts "[PindoContext] 嵌套调用 #{command},使用已有上下文 (根命令: #{@current_command},命令组: #{@command_group})"
27
+ return
28
+ end
29
+
30
+ # 第一次设置,是顶层命令
31
+ @current_command = command # 保持原始命令名称
32
+ @current_directory = directory || Dir.pwd # 固定项目目录
33
+
34
+ # 从选项中获取缓存配置
35
+ @cache_enabled = options[:cache_enabled] || false
36
+ # 使用 get_command_group 来确定缓存组
37
+ @command_group = get_command_group(command)
38
+
39
+ puts "\n[PindoContext] ========== 设置顶层命令上下文 =========="
40
+ puts "[PindoContext] 命令: #{command}"
41
+ puts "[PindoContext] 目录: #{@current_directory}"
42
+ puts "[PindoContext] 缓存启用: #{@cache_enabled}"
43
+ puts "[PindoContext] 命令组: #{@command_group}"
44
+ puts "[PindoContext] options: #{options.inspect}"
45
+
46
+ # 仅在启用缓存时加载文件缓存
47
+ if @cache_enabled && !@file_cache_loaded
48
+ puts "[PindoContext] 准备加载文件缓存..."
49
+ load_file_cache_with_confirmation
50
+ @file_cache_loaded = true
51
+ else
52
+ if !@cache_enabled
53
+ puts "[PindoContext] 缓存未启用,跳过文件缓存加载"
54
+ elsif @file_cache_loaded
55
+ puts "[PindoContext] 文件缓存已加载,跳过重复加载"
56
+ end
57
+ end
58
+ puts "[PindoContext] ======================================\n"
59
+ end
60
+
61
+ # 重置上下文(顶层命令结束时调用)
62
+ def reset_context
63
+ # 仅在启用缓存时保存文件缓存
64
+ save_file_cache if @cache_enabled && @current_command
65
+
66
+ @current_command = nil
67
+ @current_directory = nil
68
+ @file_cache_loaded = false
69
+ @cache_enabled = false
70
+ @command_group = nil
71
+ # 暂不清理内存缓存,让同一进程内可以复用
72
+ end
73
+
74
+ # 设置用户选择(三级结构:项目路径 -> 根命令 -> 键值)
75
+ def set_selection(key, value)
76
+ # 构建三级键 - 使用统一的项目路径获取方法
77
+ project_path = get_project_path
78
+ root_command = @command_group || @current_command
79
+
80
+ # 如果没有有效的项目路径或命令,不保存
81
+ unless project_path && root_command
82
+ puts "[PindoContext] 警告: 无法保存选择,缺少项目路径或命令上下文"
83
+ return
84
+ end
85
+
86
+ # 初始化三级结构(总是保存到内存,不管是否启用缓存)
87
+ @memory_selections[project_path] ||= {}
88
+ @memory_selections[project_path][root_command] ||= {}
89
+ @memory_selections[project_path][root_command][key] = value
90
+
91
+ # 更新该命令组的最后修改时间
92
+ @memory_selections[project_path][root_command]['__last_modified__'] = Time.now.to_i
93
+
94
+ puts "[PindoContext] 保存到内存: [#{project_path}][#{root_command}][#{key}] = #{value.inspect}"
95
+
96
+ # 仅在启用缓存时保存到文件
97
+ if @cache_enabled
98
+ save_file_cache
99
+ puts "[PindoContext] 已保存到文件缓存"
100
+ else
101
+ puts "[PindoContext] 缓存未启用,仅保存到内存"
102
+ end
103
+ end
104
+
105
+ # 获取用户选择(三级结构访问)
106
+ def get_selection(key)
107
+ # 使用统一的项目路径获取方法
108
+ project_path = get_project_path
109
+ root_command = @command_group || @current_command
110
+
111
+ return nil unless project_path && root_command
112
+
113
+ # 从三级结构中获取值(总是从内存获取,不管是否启用缓存)
114
+ value = @memory_selections.dig(project_path, root_command, key)
115
+
116
+ if value
117
+ puts "[PindoContext] 从内存获取: [#{project_path}][#{root_command}][#{key}] = #{value.inspect}"
118
+ else
119
+ puts "[PindoContext] 内存中未找到: [#{project_path}][#{root_command}][#{key}]" if ENV['PINDO_DEBUG']
120
+ end
121
+
122
+ value
123
+ end
124
+
125
+ # 检查是否有缓存的选择
126
+ def has_selection?(key)
127
+ # 总是检查内存中是否有值,不管是否启用缓存
128
+ !get_selection(key).nil?
129
+ end
130
+
131
+ # 启用/禁用缓存
132
+ def enable_cache(enabled = true)
133
+ @cache_enabled = enabled
134
+ if enabled && !@file_cache_loaded && @current_command
135
+ load_file_cache_with_confirmation
136
+ @file_cache_loaded = true
137
+ end
138
+ end
139
+
140
+ # 检查缓存是否启用
141
+ def cache_enabled?
142
+ @cache_enabled
143
+ end
144
+
145
+ # 是否是嵌套调用
146
+ def nested_call?
147
+ !@current_command.nil?
148
+ end
149
+
150
+ # 是否是顶层命令(非嵌套)
151
+ def top_level_command?
152
+ @current_command.nil?
153
+ end
154
+
155
+ # 获取当前根命令
156
+ def root_command
157
+ @command_group || @current_command
158
+ end
159
+
160
+ # 获取当前项目路径(统一的获取方法,确保一致性)
161
+ def project_path
162
+ get_project_path
163
+ end
164
+
165
+ # 内部使用的项目路径获取方法
166
+ # 优先使用已设置的目录,如果没有则尝试获取Git仓库根目录
167
+ def get_project_path
168
+ # 如果已经设置了目录,直接返回
169
+ return @current_directory if @current_directory
170
+
171
+ # 尝试获取Git仓库根目录
172
+ begin
173
+ git_root = `git rev-parse --show-toplevel 2>/dev/null`.strip
174
+ return git_root if $?.success? && !git_root.empty?
175
+ rescue
176
+ # git命令失败
177
+ end
178
+
179
+ # 最后的后备:使用当前目录
180
+ Dir.pwd
181
+ end
182
+
183
+ # 清除当前上下文的缓存
184
+ def clear_current_cache
185
+ return unless @cache_enabled
186
+
187
+ proj_path = get_project_path
188
+ cmd = root_command
189
+ return unless proj_path && cmd
190
+
191
+ if @memory_selections[proj_path] && @memory_selections[proj_path][cmd]
192
+ @memory_selections[proj_path].delete(cmd)
193
+ @memory_selections.delete(proj_path) if @memory_selections[proj_path].empty?
194
+ save_file_cache
195
+ puts "[PindoContext] 清除缓存: [#{proj_path}][#{cmd}]" if ENV['PINDO_DEBUG']
196
+ end
197
+ end
198
+
199
+ # 选择键定义
200
+ module SelectionKey
201
+ TAG_DECISION = :tag_decision # 打Tag决定
202
+ PROJECT_NAME = :project_name # 项目名称
203
+ BUNDLE_ID = :bundle_id # Bundle ID
204
+ CERT_TYPE = :cert_type # 证书类型
205
+ end
206
+
207
+ private
208
+
209
+ # 确保缓存目录存在
210
+ def ensure_cache_dir
211
+ cache_dir = File.expand_path('~/.pindo/cache')
212
+ FileUtils.mkdir_p(cache_dir) unless File.exist?(cache_dir)
213
+ end
214
+
215
+ # 获取缓存文件路径
216
+ def cache_file_path
217
+ cache_dir = File.expand_path('~/.pindo/cache')
218
+ File.join(cache_dir, 'context_selections.json')
219
+ end
220
+
221
+ # 加载文件缓存并确认(三级结构)
222
+ def load_file_cache_with_confirmation
223
+ file_path = cache_file_path
224
+
225
+ puts "[PindoContext] 检查文件缓存: #{file_path}"
226
+
227
+ unless File.exist?(file_path)
228
+ puts "[PindoContext] 文件缓存不存在,跳过加载"
229
+ return
230
+ end
231
+
232
+ begin
233
+ file_content = File.read(file_path)
234
+ file_cache = JSON.parse(file_content)
235
+
236
+ # 清理过期缓存(超过7天)
237
+ file_cache = clean_expired_cache(file_cache)
238
+
239
+ # 获取当前项目和命令的缓存
240
+ project_path = get_project_path
241
+ root_command = @command_group || @current_command
242
+
243
+ puts "[PindoContext] 查找缓存: [#{project_path}][#{root_command}]"
244
+
245
+ # 检查三级结构中是否有当前上下文的缓存
246
+ cached_selections = file_cache.dig(project_path, root_command)
247
+
248
+ if cached_selections && !cached_selections.empty?
249
+ # 根据命令组显示更友好的描述
250
+ group_desc = case root_command
251
+ when 'ios:autobuild'
252
+ 'iOS 构建'
253
+ when 'and:autobuild', 'android:autobuild'
254
+ 'Android 构建'
255
+ when 'web:autobuild'
256
+ 'Web 构建'
257
+ else
258
+ @current_command # 其他命令显示原始命令名
259
+ end
260
+
261
+ puts "\n检测到之前的选择 (#{group_desc}):"
262
+ puts "────────────────────────────────────────"
263
+
264
+ cached_selections.each do |key, value|
265
+ # 跳过内部字段
266
+ next if key.to_s.start_with?('__')
267
+
268
+ case key.to_s
269
+ when 'bundle_id'
270
+ puts " Bundle ID: #{value}"
271
+ when 'project_name', 'app_key' # 兼容旧的 app_key
272
+ puts " 项目名称: #{value}"
273
+ when 'tag_decision'
274
+ if value.is_a?(Hash)
275
+ action_desc = value['description'] || value['action']
276
+ puts " Tag决定: #{action_desc}"
277
+ end
278
+ when 'cert_type'
279
+ puts " 证书类型: #{value}"
280
+ end
281
+ end
282
+ puts "────────────────────────────────────────"
283
+
284
+ # 询问用户是否使用缓存
285
+ require 'highline/import'
286
+ cli = HighLine.new
287
+ confirm = cli.agree("\n是否使用以上缓存的选择? (y/n) ")
288
+
289
+ if confirm
290
+ puts "使用缓存的选择\n"
291
+ # 恢复三级结构的文件缓存到内存缓存
292
+ file_cache.each do |proj_path, commands|
293
+ @memory_selections[proj_path] ||= {}
294
+ commands.each do |cmd, selections|
295
+ @memory_selections[proj_path][cmd] ||= {}
296
+ selections.each do |key, value|
297
+ symbol_key = key.to_sym
298
+ @memory_selections[proj_path][cmd][symbol_key] = value
299
+ end
300
+ end
301
+ end
302
+ else
303
+ puts "清除缓存,重新选择\n"
304
+ # 清除当前上下文的文件缓存(三级结构)
305
+ if file_cache[project_path] && file_cache[project_path][root_command]
306
+ file_cache[project_path].delete(root_command)
307
+ # 如果项目路径下没有任何命令缓存了,删除项目路径键
308
+ file_cache.delete(project_path) if file_cache[project_path].empty?
309
+ end
310
+ File.write(file_path, JSON.pretty_generate(file_cache))
311
+ # 不加载任何缓存到内存
312
+ end
313
+ else
314
+ # 加载整个文件缓存到内存(不需要确认)
315
+ file_cache.each do |proj_path, commands|
316
+ @memory_selections[proj_path] ||= {}
317
+ commands.each do |cmd, selections|
318
+ # 跳过当前上下文(已经是空的)
319
+ next if proj_path == project_path && cmd == root_command
320
+
321
+ @memory_selections[proj_path][cmd] ||= {}
322
+ selections.each do |key, value|
323
+ symbol_key = key.to_sym
324
+ @memory_selections[proj_path][cmd][symbol_key] = value
325
+ end
326
+ end
327
+ end
328
+ end
329
+
330
+ puts "[PindoContext] 处理文件缓存: #{file_path}" if ENV['PINDO_DEBUG']
331
+ rescue => e
332
+ puts "[PindoContext] 加载缓存失败: #{e.message}" if ENV['PINDO_DEBUG']
333
+ end
334
+ end
335
+
336
+ # 保存文件缓存(三级结构)
337
+ def save_file_cache
338
+ file_path = cache_file_path
339
+
340
+ begin
341
+ # 转换内存缓存为可序列化的格式(保持三级结构)
342
+ file_cache = {}
343
+ @memory_selections.each do |project_path, commands|
344
+ file_cache[project_path] ||= {}
345
+ commands.each do |command, selections|
346
+ file_cache[project_path][command] ||= {}
347
+ selections.each do |key, value|
348
+ # 将符号键转换为字符串
349
+ string_key = key.to_s
350
+ file_cache[project_path][command][string_key] = value
351
+ end
352
+ end
353
+ end
354
+
355
+ # 写入文件
356
+ File.write(file_path, JSON.pretty_generate(file_cache))
357
+
358
+ puts "[PindoContext] 保存文件缓存: #{file_path}" if ENV['PINDO_DEBUG']
359
+ rescue => e
360
+ puts "[PindoContext] 保存缓存失败: #{e.message}" if ENV['PINDO_DEBUG']
361
+ end
362
+ end
363
+
364
+ # 获取命令组名称(统一管理命令缓存组)
365
+ def get_command_group(command)
366
+ # 根据需求文档定义命令组,相关命令共享同一缓存
367
+ case command
368
+ # iOS 构建相关命令组
369
+ when 'unity:ipa'
370
+ 'ios:autobuild'
371
+
372
+ # Android 构建相关命令组
373
+ when 'unity:apk'
374
+ 'and:autobuild'
375
+
376
+ # Web 构建相关命令组
377
+ when 'unity:web'
378
+ 'web:autobuild'
379
+
380
+ else
381
+ command # 其他命令使用独立缓存(命令名本身作为组)
382
+ end
383
+ end
384
+
385
+ # 清理过期缓存(超过7天的缓存)
386
+ def clean_expired_cache(cache_data)
387
+ return {} unless cache_data.is_a?(Hash)
388
+
389
+ current_time = Time.now.to_i
390
+ seven_days_in_seconds = 7 * 24 * 60 * 60
391
+ cleaned_cache = {}
392
+ expired_commands = []
393
+ expired_projects = []
394
+
395
+ cache_data.each do |project_path, commands|
396
+ # 初始化项目路径
397
+ cleaned_commands = {}
398
+
399
+ commands.each do |command, selections|
400
+ # 获取最后修改时间
401
+ last_modified = selections['__last_modified__']
402
+
403
+ # 如果没有时间戳,视为过期
404
+ if last_modified.nil?
405
+ expired_commands << "[#{project_path}][#{command}] (无时间戳)"
406
+ puts "[PindoContext] 清理无时间戳缓存: [#{project_path}][#{command}]" if ENV['PINDO_DEBUG']
407
+ next
408
+ end
409
+
410
+ # 检查是否过期(超过7天)
411
+ if (current_time - last_modified.to_i) > seven_days_in_seconds
412
+ days_old = (current_time - last_modified.to_i) / (24 * 60 * 60)
413
+ expired_commands << "[#{project_path}][#{command}] (#{days_old}天前)"
414
+ puts "[PindoContext] 清理过期缓存 (#{days_old}天前): [#{project_path}][#{command}]" if ENV['PINDO_DEBUG']
415
+ next
416
+ end
417
+
418
+ # 保留未过期的缓存
419
+ cleaned_commands[command] = selections
420
+ end
421
+
422
+ # 如果该项目路径下还有有效的命令缓存,保留它
423
+ if cleaned_commands.any?
424
+ cleaned_cache[project_path] = cleaned_commands
425
+ else
426
+ # 整个项目路径下的所有命令都过期了,删除整个项目路径
427
+ expired_projects << project_path
428
+ puts "[PindoContext] 删除空项目路径: #{project_path}" if ENV['PINDO_DEBUG']
429
+ end
430
+ end
431
+
432
+ # 输出清理总结
433
+ if expired_commands.any? || expired_projects.any?
434
+ puts "[PindoContext] 缓存清理完成:"
435
+ if expired_commands.any?
436
+ puts " - 清理了 #{expired_commands.size} 个过期命令缓存"
437
+ expired_commands.each { |cmd| puts " • #{cmd}" } if ENV['PINDO_DEBUG']
438
+ end
439
+ if expired_projects.any?
440
+ puts " - 删除了 #{expired_projects.size} 个空项目路径"
441
+ expired_projects.each { |proj| puts " • #{proj}" } if ENV['PINDO_DEBUG']
442
+ end
443
+
444
+ # 立即保存清理后的缓存到文件
445
+ File.write(cache_file_path, JSON.pretty_generate(cleaned_cache))
446
+ end
447
+
448
+ cleaned_cache
449
+ end
450
+
451
+
452
+ public
453
+
454
+ # 调试信息
455
+ def debug_info
456
+ puts "\n=== PindoContext Debug Info ==="
457
+ puts "当前命令: #{@current_command || '无'}"
458
+ puts "命令组: #{@command_group || '无'}"
459
+ puts "设置的目录: #{@current_directory || '未设置'}"
460
+ puts "实际项目路径: #{get_project_path}"
461
+ puts "缓存启用: #{@cache_enabled}"
462
+
463
+ proj_path = get_project_path
464
+ root_cmd = @command_group || @current_command
465
+
466
+ if root_cmd && @memory_selections.dig(proj_path, root_cmd)
467
+ puts "当前缓存的选择:"
468
+ @memory_selections[proj_path][root_cmd].each do |k, v|
469
+ next if k.to_s.start_with?('__') # 跳过内部字段
470
+ puts " #{k}: #{v.inspect}"
471
+ end
472
+ end
473
+ puts "================================\n"
474
+ end
475
+ end
476
+ end