ace-support-config 0.11.2 → 0.16.3

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: 33ca9302021e76335a714f27321b3075dd172056154ed5d1769b6f7b9ea7d6c7
4
- data.tar.gz: e06462886d8d07fa3104367a96d129957b8554b8ac4419651f51c220dcd09167
3
+ metadata.gz: 292cd8b30cde0ff6e42674342eec91c5ced9ac82e54721c41889e2efce28c0b2
4
+ data.tar.gz: bc74b84b53654b33d26b737236066c9d4699cda31dce60a4950f68116cccf4b9
5
5
  SHA512:
6
- metadata.gz: 5f6377fe1c334cdf394a619c0548966317167789dd3214c87d7cbef778fe069d7e059af7db77d53fcc86aeb0acb445ef4eb644cbfb29e1f2006f73f74aa671ef
7
- data.tar.gz: d21e340eb555a7457aabcf55e7ea26120b54b9ed282f7227658810377274501e5fac8ce1b1d0519f8a9868330502a8d0dbae4e2b69afd8805f8b37ac033de699
6
+ metadata.gz: a0f53f807d5dd5660e9e66b429975a52f3b03d8aa3ff50a5bfe2bbfc064d268873affdd9e6c046942e93aaec4f6805803507d78083aea8ab9a8cd60a35b88181
7
+ data.tar.gz: 955386f488b292bd9eaadb0c0e30324628c0d7022f1bc86a1ac680296a067d720977262d194b5d5963b4098525c094e6c915361bb0abf7a43432b214f4e07c6f
data/CHANGELOG.md CHANGED
@@ -7,6 +7,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Fixed
11
+ - Corrected `ace-config-bootstrap-root-files` to sync `ace-support-core` and record from the sandbox root layout where the bootstrap files are actually created.
12
+
13
+ ## [0.16.2] - 2026-04-24
14
+
15
+ ### Technical
16
+ - Expanded bootstrap feature coverage so generated `AGENTS.md` and `CLAUDE.md` files must retain ACE provenance, customization, and refresh guidance.
17
+
18
+ ## [0.16.1] - 2026-04-24
19
+
20
+ ### Changed
21
+ - Clarified setup-readiness documentation so `ace-llm --list-providers` remains the provider discovery command while `ace-config doctor` is documented as the quick-start readiness check, including blocker versus warning guidance and `--no-probe` usage.
22
+
23
+ ## [0.16.0] - 2026-04-23
24
+
25
+ ### Changed
26
+ - Renamed `ace-config init` to `ace-config sync` and removed the old `init` command path.
27
+ - Updated quick-start setup guidance to sync only `ace-llm-providers-cli` config by default because other package config should come from packaged `.ace-defaults` unless project overrides are needed.
28
+
29
+ ## [0.15.0] - 2026-04-23
30
+
31
+ ### Changed
32
+ - Extended `ace-config doctor` with informational project `.ace` vs package `.ace-defaults` counts, provider skill projection sync warnings, and streamed fast-check progress before the final report.
33
+ - Updated doctor provider pings to order API targets before CLI targets, use 15-second API timeouts and 30-second CLI timeouts, and distinguish timeout failures from other provider errors.
34
+
35
+ ### Technical
36
+ - Added doctor coverage for config-default counts, skill sync drift, provider timeout selection, timeout row formatting, and progress ordering.
37
+
38
+ ## [0.14.1] - 2026-04-23
39
+
40
+ ### Changed
41
+ - Updated `ace-config doctor` live provider checks to probe deduped `_utility` plus `commit` role candidates, preserving alias labels alongside resolved provider/model names.
42
+ - Changed doctor provider-ping progress and summaries to show pass/fail/running glyphs, live TTY line updates, and explicit passed/total counts for full and partial success.
43
+
44
+ ### Technical
45
+ - Added doctor regression coverage for utility-plus-commit target selection, alias-preserving ping commands, append-only non-TTY progress, and partial-success summary output.
46
+
47
+ ## [0.14.0] - 2026-04-23
48
+
49
+ ### Changed
50
+ - Split `ace-config doctor` output into health checks that control exit status and a concise hygiene summary, with `--hygiene` for full alias/role drift details.
51
+ - Made provider health live by default again through concurrent `ace-llm TARGET "ping" --no-fallback` checks against deduped `_utility` and `commit` role candidates; `--no-probe` disables live pings.
52
+ - Streamed human `ace-config doctor` output so utility provider ping lines show alias labels, resolved model names, and pass counts as checks complete.
53
+
54
+ ### Technical
55
+ - Added doctor coverage for health-only exit status, hidden-by-default hygiene findings, expanded hygiene output, JSON health/hygiene counts, first-role base-provider distillation, and concurrent ping delegation.
56
+
57
+ ## [0.13.0] - 2026-04-22
58
+
59
+ ### Fixed
60
+ - Made `ace-config doctor` fast by default by replacing automatic live probes with structural role-default readiness checks; live probes now require `--probe` and target only resolved role-default providers.
61
+
62
+ ### Technical
63
+ - Added doctor regression coverage for non-live defaults, opt-in provider probes, blocker-gated probes, and role-default target validation.
64
+
65
+ ## [0.12.1] - 2026-04-22
66
+
67
+ ### Fixed
68
+ - Extended `SetupDoctor` stale-alias detection to validate `aliases.global` provider targets (`provider:model`) in addition to provider-local model aliases.
69
+ - Made `.ace-local` artifact-hygiene detection accept equivalent `.gitignore` forms (for example `/.ace-local/`, `.ace-local`, `.ace-local/**`) instead of requiring an exact literal line.
70
+
71
+ ### Technical
72
+ - Added fast-test coverage for stale global alias detection and semantic `.ace-local` ignore pattern acceptance.
73
+
74
+ ## [0.12.0] - 2026-04-22
75
+
76
+ ### Added
77
+ - Added `ace-config doctor` with text/JSON readiness output and `--no-probe` support for non-mutating setup diagnostics.
78
+
79
+ ### Changed
80
+ - Extended `ace-config` command routing and help output to include the new `doctor` workflow.
81
+
82
+ ### Technical
83
+ - Added `SetupDoctor` organism coverage for blocker/warn/skip classification and CLI contract behavior.
84
+
10
85
  ## [0.11.2] - 2026-04-13
11
86
 
12
87
  ### Fixed
data/README.md CHANGED
@@ -22,13 +22,16 @@
22
22
  This package now owns the `ace-config` executable for managing `.ace` configuration files and templates.
23
23
 
24
24
  ```bash
25
- ace-config init [GEM] [--force] [--dry-run] [--global] [--verbose]
25
+ ace-config sync [GEM] [--force] [--dry-run] [--global] [--verbose]
26
+ ace-config doctor [--json] [--hygiene] [--probe] [--no-probe] [--verbose] [--quiet] [--no-color]
26
27
  ace-config diff [GEM] [--global] [--local] [--file PATH] [--one-line]
27
28
  ace-config list [--verbose]
28
29
  ace-config version
29
30
  ace-config help
30
31
  ```
31
32
 
33
+ Use `ace-config doctor` after a fresh setup to check quick-start readiness without mutating the project. By default it prints each fast setup check as it completes, streams concurrent `ace-llm TARGET "ping" --no-fallback` results for deduped `_utility` and `commit` role candidates, and then prints a final doctor report. The report separates health warnings from informational config-default drift and hidden-by-default hygiene findings. Add `--hygiene` to print full hygiene details, `--json` for machine-readable output, or `--no-probe` to skip live provider pings.
34
+
32
35
  ## Testing
33
36
 
34
37
  `ace-support-config` uses the ACE deterministic test taxonomy:
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Showcase ace-config init bootstrapping project-root guidance files from a subdirectory
2
+ description: Showcase ace-config sync bootstrapping project-root guidance files from a subdirectory
3
3
  tags:
4
4
  - ace-config
5
5
  - ace-support-config
@@ -10,17 +10,19 @@ settings:
10
10
  height: 700
11
11
  format: gif
12
12
  scenes:
13
- - name: Initialize config from a nested working directory
13
+ - name: Sync config from a nested working directory
14
14
  commands:
15
- - type: mkdir -p repo/subdir && cd repo && git init
15
+ - type: mkdir -p subdir && git init
16
16
  sleep: 3s
17
- - type: cd subdir && ace-config init support-core
17
+ - type: cd subdir && ace-config sync ace-support-core
18
18
  sleep: 5s
19
19
  - name: Verify bootstrap files landed at the repository root
20
20
  commands:
21
- - type: cd ../ && find . -maxdepth 2 \\( -name '.gitignore' -o -name 'AGENTS.md' -o -name 'CLAUDE.md' \\) | sort
21
+ - type: cd ..
22
+ sleep: 1s
23
+ - type: ls .gitignore AGENTS.md CLAUDE.md
22
24
  sleep: 4s
23
- - type: printf '\n.gitignore contents:\n' && cat .gitignore
25
+ - type: sed -n '1,20p' AGENTS.md
24
26
  sleep: 4s
25
27
  setup:
26
28
  - sandbox
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Showcase ace-config CLI for listing, initializing, and diffing ace-* gem configurations
2
+ description: Showcase ace-config CLI for listing, syncing, and diffing ace-* gem configurations
3
3
  tags:
4
4
  - ace-config
5
5
  - ace-support-config
@@ -14,9 +14,9 @@ scenes:
14
14
  commands:
15
15
  - type: ace-config list
16
16
  sleep: 3s
17
- - name: Preview config initialization
17
+ - name: Preview config sync
18
18
  commands:
19
- - type: ace-config init --dry-run
19
+ - type: ace-config sync --dry-run
20
20
  sleep: 4s
21
21
  - name: Diff configs against defaults
22
22
  commands:
data/docs/usage.md CHANGED
@@ -3,8 +3,8 @@ doc-type: user
3
3
  title: ace-support-config Usage Guide
4
4
  purpose: Documentation for ace-support-config/docs/usage.md
5
5
  ace-docs:
6
- last-updated: '2026-03-31'
7
- last-checked: '2026-03-31'
6
+ last-updated: '2026-04-22'
7
+ last-checked: '2026-04-22'
8
8
  ---
9
9
 
10
10
  # ace-support-config Usage Guide
@@ -15,16 +15,48 @@ The `ace-support-config` gem provides a generic configuration cascade system tha
15
15
 
16
16
  ## ace-config Command
17
17
 
18
- `ace-support-config` ships the `ace-config` CLI for template discovery, initialization, and drift checks.
18
+ `ace-support-config` ships the `ace-config` CLI for template discovery, config sync, and drift checks.
19
19
 
20
20
  ```bash
21
- ace-config init [GEM] [--force] [--dry-run] [--global] [--verbose]
21
+ ace-config sync [GEM] [--force] [--dry-run] [--global] [--verbose]
22
+ ace-config doctor [--json] [--hygiene] [--probe] [--no-probe]
22
23
  ace-config diff [GEM] [--global] [--local] [--file PATH] [--one-line]
23
24
  ace-config list [--verbose]
24
25
  ace-config version
25
26
  ace-config help
26
27
  ```
27
28
 
29
+ ### Setup Readiness Doctor
30
+
31
+ Run `ace-config doctor` after `ace-config sync ace-llm-providers-cli` and provider setup to verify that the quick-start
32
+ path is ready without changing files. `ace-llm --list-providers` is still the provider discovery command; doctor is the
33
+ separate readiness command that tells you whether the documented setup can actually execute.
34
+
35
+ The doctor checks:
36
+
37
+ - health markers for generated local artifacts and `.ace-local/` gitignore readiness
38
+ - `ace-llm-providers-cli` availability and bundled gem/package readiness
39
+ - `ace-llm --list-providers` discovery for configured providers
40
+ - project `.ace` vs package `.ace-defaults` config-default mode counts
41
+ - usable core role defaults
42
+ - provider-native skill projection sync status from `ace-handbook status`
43
+ - a one-line hygiene summary for configured provider aliases and wider role/default drift
44
+ - concurrent `ace-llm TARGET "ping" --no-fallback` checks for deduped `_utility` and `commit` role candidates
45
+
46
+ Interpret results as setup blockers versus actionable warnings:
47
+
48
+ - Missing provider packages, unresolved provider/model configuration, or failed readiness requirements for the active
49
+ quick-start path are blockers.
50
+ - Missing credentials or missing local CLI account access are reported as setup readiness issues with next actions,
51
+ not as ACE install corruption.
52
+ - API-only users can still pass doctor when their configured API providers are ready, even if unrelated CLI providers
53
+ are inactive.
54
+
55
+ Use `--hygiene` to expand full hygiene findings and `--json` for structured output in automation. By default, doctor
56
+ prints fast health/info checks as they complete, runs concurrent `ace-llm TARGET "ping" --no-fallback` checks for
57
+ deduped `_utility` and `commit` role candidates, and then prints the final report. Use `--no-probe` to skip live
58
+ provider calls when you want a readiness check based only on local setup and configuration.
59
+
28
60
  ## Testing Contract
29
61
 
30
62
  `ace-support-config` follows the ACE deterministic package targets:
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "optparse"
4
- require_relative "organisms/config_initializer"
4
+ require_relative "organisms/config_synchronizer"
5
5
  require_relative "organisms/config_diff"
6
+ require_relative "organisms/setup_doctor"
6
7
  require_relative "models/config_templates"
7
8
 
8
9
  module Ace
@@ -19,12 +20,14 @@ module Ace
19
20
  command = argv.shift
20
21
 
21
22
  case command
22
- when "init"
23
- run_init(argv)
23
+ when "sync"
24
+ run_sync(argv)
24
25
  when "diff"
25
26
  run_diff(argv)
26
27
  when "list"
27
28
  run_list(argv)
29
+ when "doctor"
30
+ run_doctor(argv)
28
31
  when "version", "--version"
29
32
  show_version
30
33
  when "help", "--help", "-h"
@@ -39,16 +42,16 @@ module Ace
39
42
 
40
43
  private
41
44
 
42
- def run_init(argv)
45
+ def run_sync(argv)
43
46
  options = {}
44
47
 
45
48
  parser = OptionParser.new do |opts|
46
49
  opts.banner = <<~BANNER.chomp
47
50
  NAME
48
- ace-config init - Initialize configuration for ace-* gems
51
+ ace-config sync - Sync configuration for ace-* gems
49
52
 
50
53
  USAGE
51
- ace-config init [GEM] [OPTIONS]
54
+ ace-config sync [GEM] [OPTIONS]
52
55
 
53
56
  OPTIONS
54
57
  BANNER
@@ -65,12 +68,12 @@ module Ace
65
68
  parser.parse!(argv)
66
69
  gem_name = argv.shift
67
70
 
68
- initializer = Organisms::ConfigInitializer.new(**options)
71
+ synchronizer = Organisms::ConfigSynchronizer.new(**options)
69
72
 
70
73
  if gem_name
71
- initializer.init_gem(gem_name)
74
+ synchronizer.sync_gem(gem_name)
72
75
  else
73
- initializer.init_all
76
+ synchronizer.sync_all
74
77
  end
75
78
  end
76
79
 
@@ -160,8 +163,48 @@ module Ace
160
163
  end
161
164
  end
162
165
 
163
- puts "\nUse 'ace-config init [GEM]' to initialize a specific gem's configuration"
164
- puts "Use 'ace-config init' to initialize all configurations"
166
+ puts "\nUse 'ace-config sync [GEM]' to sync a specific gem's configuration"
167
+ puts "Use 'ace-config sync' to sync all configurations"
168
+ end
169
+
170
+ def run_doctor(argv)
171
+ options = {json: false, no_probe: false, probe: false, hygiene: false, verbose: false, no_color: false, quiet: false}
172
+
173
+ parser = OptionParser.new do |opts|
174
+ opts.banner = <<~BANNER.chomp
175
+ NAME
176
+ ace-config doctor - Check setup readiness for quick-start workflows
177
+
178
+ USAGE
179
+ ace-config doctor [OPTIONS]
180
+
181
+ OPTIONS
182
+ BANNER
183
+ opts.on("--json", "Output checks as JSON") { options[:json] = true }
184
+ opts.on("--hygiene", "Show full hygiene findings") { options[:hygiene] = true }
185
+ opts.on("-v", "--verbose", "Show full diagnostic detail") { options[:verbose] = true }
186
+ opts.on("-q", "--quiet", "Suppress output; use exit status only") { options[:quiet] = true }
187
+ opts.on("--no-color", "Disable colored output") { options[:no_color] = true }
188
+ opts.on("--probe", "Run live provider probes (default)") { options[:probe] = true }
189
+ opts.on("--no-probe", "Disable live provider probes") { options[:no_probe] = true }
190
+ opts.on("-h", "--help", "Show this help") do
191
+ puts opts
192
+ exit
193
+ end
194
+ end
195
+
196
+ parser.parse!(argv)
197
+
198
+ exit_code = Organisms::SetupDoctor.new.run(
199
+ json: options[:json],
200
+ no_probe: options[:no_probe],
201
+ probe: options[:probe],
202
+ hygiene: options[:hygiene],
203
+ verbose: options[:verbose],
204
+ colors: !options[:no_color],
205
+ quiet: options[:quiet]
206
+ )
207
+ exit(exit_code) if exit_code.positive?
165
208
  end
166
209
 
167
210
  def show_version
@@ -177,9 +220,10 @@ module Ace
177
220
  ace-config COMMAND [OPTIONS]
178
221
 
179
222
  COMMANDS
180
- init [GEM] Initialize configuration for specific gem or all
223
+ sync [GEM] Sync configuration for specific gem or all
181
224
  diff [GEM] Compare configs with examples
182
225
  list List available ace-* gems with example configs
226
+ doctor Check setup readiness for quick-start workflows
183
227
  version Show version
184
228
  help Show this help
185
229
 
@@ -0,0 +1,270 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Ace
6
+ module Support
7
+ module Config
8
+ module Molecules
9
+ class SetupDoctorReporter
10
+ COLORS = {
11
+ red: "\e[31m",
12
+ yellow: "\e[33m",
13
+ green: "\e[32m",
14
+ blue: "\e[34m",
15
+ cyan: "\e[36m",
16
+ reset: "\e[0m",
17
+ bold: "\e[1m"
18
+ }.freeze
19
+
20
+ ICONS = {
21
+ doctor: "🏥",
22
+ stats: "📊",
23
+ success: "✅",
24
+ error: "❌",
25
+ warning: "⚠️",
26
+ info: "ℹ️"
27
+ }.freeze
28
+
29
+ STATUS_GLYPHS = {
30
+ "pass" => "✓",
31
+ "warn" => "○",
32
+ "blocker" => "✗",
33
+ "skip" => "○",
34
+ "info" => "○"
35
+ }.freeze
36
+
37
+ STATUS_COLORS = {
38
+ "pass" => :green,
39
+ "warn" => :yellow,
40
+ "blocker" => :red,
41
+ "skip" => :yellow,
42
+ "info" => :cyan
43
+ }.freeze
44
+
45
+ def self.format_results(results, format: :terminal, hygiene: false, verbose: false, colors: true)
46
+ case format.to_sym
47
+ when :json
48
+ JSON.pretty_generate(results)
49
+ else
50
+ new(results, hygiene: hygiene, verbose: verbose, colors: colors).format_terminal
51
+ end
52
+ end
53
+
54
+ def initialize(results, hygiene:, verbose:, colors:)
55
+ @results = results
56
+ @hygiene = hygiene
57
+ @verbose = verbose
58
+ @colors = colors
59
+ end
60
+
61
+ def format_terminal
62
+ output = []
63
+ output << "\n#{colorize("#{ICONS[:doctor]} Setup Health Check", :bold)}"
64
+ output << "=" * 40
65
+ output.concat(format_overview)
66
+ output.concat(format_readiness)
67
+ output.concat(format_info)
68
+ output.concat(format_provider_pings)
69
+ output.concat(format_issues)
70
+ output << "=" * 40
71
+ output << format_summary_line
72
+ output << "\n#{colorize("Completed in #{format_duration(@results[:duration])}", :blue)}" if @results[:duration]
73
+ output << final_status_line
74
+ output.join("\n")
75
+ end
76
+
77
+ private
78
+
79
+ def format_overview
80
+ stats = @results[:stats] || {}
81
+ [
82
+ "",
83
+ colorize("#{ICONS[:stats]} Overview", :cyan),
84
+ "-" * 20,
85
+ " Health checks: #{stats[:health_checks] || health_checks.length}",
86
+ " Info checks: #{stats[:info_checks] || info_checks.length}",
87
+ " Utility providers checked: #{stats[:provider_targets] || provider_outcomes.length}",
88
+ " Hygiene findings: #{stats[:hygiene_findings] || 0}"
89
+ ]
90
+ end
91
+
92
+ def format_readiness
93
+ rows = health_checks.reject { |check| check[:id] == "probe-readiness" }
94
+ return [] if rows.empty?
95
+
96
+ output = ["", colorize("Readiness", :cyan), "-" * 20]
97
+ rows.each do |check|
98
+ output << " #{status_glyph(check[:status])} #{check[:message]}"
99
+ if check[:status] == "blocker" || check[:status] == "warn" || @verbose
100
+ Array(check[:details]).each { |detail| output << " - #{detail}" }
101
+ output << " Next: #{check[:next_action]}" if check[:next_action]
102
+ end
103
+ end
104
+ output
105
+ end
106
+
107
+ def format_info
108
+ return [] if info_checks.empty?
109
+
110
+ output = ["", colorize("Info", :cyan), "-" * 20]
111
+ info_checks.each do |check|
112
+ output << " #{status_glyph(check[:status])} #{check[:message]}"
113
+ if @verbose
114
+ Array(check[:details]).each { |detail| output << " - #{detail}" }
115
+ output << " Next: #{check[:next_action]}" if check[:next_action]
116
+ end
117
+ end
118
+ output
119
+ end
120
+
121
+ def format_provider_pings
122
+ check = health_checks.find { |row| row[:id] == "probe-readiness" }
123
+ return [] unless check
124
+
125
+ output = ["", colorize("Utility Provider Pings", :cyan), "-" * 20]
126
+ output << " #{status_glyph(check[:status])} #{check[:message]}"
127
+ details = provider_outcomes.any? ? provider_outcomes.map { |outcome| provider_outcome_line(outcome) } : Array(check[:details])
128
+ details.each { |detail| output << " #{detail}" }
129
+ output << " Next: #{check[:next_action]}" if check[:next_action]
130
+ output
131
+ end
132
+
133
+ def format_issues
134
+ blockers = health_checks.select { |check| check[:status] == "blocker" }
135
+ warnings = health_checks.select { |check| check[:status] == "warn" }
136
+ hygiene_warnings = hygiene_checks.reject { |check| check[:status] == "pass" }
137
+
138
+ if blockers.empty? && warnings.empty? && hygiene_warnings.empty?
139
+ return ["", colorize("#{ICONS[:success]} No issues found", :green)]
140
+ end
141
+
142
+ output = ["", colorize("Issues Found:", :yellow), "-" * 20]
143
+ output.concat(format_issue_group("#{ICONS[:error]} Blockers", blockers, :red, include_details: true))
144
+ output.concat(format_issue_group("#{ICONS[:warning]} Warnings", warnings, :yellow, include_details: true))
145
+ output.concat(format_hygiene_group(hygiene_warnings))
146
+ output
147
+ end
148
+
149
+ def format_issue_group(title, checks, color, include_details:)
150
+ return [] if checks.empty?
151
+
152
+ output = ["", colorize("#{title} (#{checks.length})", color)]
153
+ checks.each_with_index do |check, index|
154
+ output << "#{index + 1}. #{check[:message]}"
155
+ if include_details
156
+ Array(check[:details]).each { |detail| output << " - #{detail}" }
157
+ end
158
+ output << " Next: #{check[:next_action]}" if check[:next_action]
159
+ end
160
+ output
161
+ end
162
+
163
+ def format_hygiene_group(checks)
164
+ finding_count = @results.dig(:hygiene, :finding_count) || 0
165
+ return [] if finding_count.zero?
166
+
167
+ if !@hygiene && !@verbose
168
+ return [
169
+ "",
170
+ colorize("#{ICONS[:warning]} Hygiene", :yellow),
171
+ "1. Hygiene findings detected (#{finding_count}); rerun with --hygiene for details"
172
+ ]
173
+ end
174
+
175
+ output = ["", colorize("#{ICONS[:warning]} Hygiene (#{finding_count})", :yellow)]
176
+ issue_number = 1
177
+ checks.each do |check|
178
+ output << "#{issue_number}. #{check[:message]}"
179
+ Array(check[:details]).each { |detail| output << " - #{detail}" }
180
+ output << " Next: #{check[:next_action]}" if check[:next_action]
181
+ issue_number += 1
182
+ end
183
+ output
184
+ end
185
+
186
+ def format_summary_line
187
+ health = @results[:health] || {}
188
+ hygiene = @results[:hygiene] || {}
189
+ info = @results[:info] || {}
190
+ parts = []
191
+ parts << colorize("#{health[:blocker_count]} blockers", health[:blocker_count].to_i.positive? ? :red : :green)
192
+ parts << colorize("#{health[:warning_count]} warnings", health[:warning_count].to_i.positive? ? :yellow : :green)
193
+ parts << colorize("#{info[:count]} info", :cyan)
194
+ parts << colorize("#{hygiene[:finding_count]} hygiene findings", hygiene[:finding_count].to_i.positive? ? :yellow : :green)
195
+ parts.join(", ")
196
+ end
197
+
198
+ def final_status_line
199
+ if @results[:valid]
200
+ if @results.dig(:health, :warning_count).to_i.positive? || @results.dig(:hygiene, :finding_count).to_i.positive?
201
+ colorize("Setup check passed with warnings", :yellow)
202
+ else
203
+ colorize("Setup check passed", :green)
204
+ end
205
+ else
206
+ colorize("Setup check failed", :red)
207
+ end
208
+ end
209
+
210
+ def provider_outcome_line(outcome)
211
+ elapsed = outcome[:elapsed_ms] ? " in #{outcome[:elapsed_ms]}ms" : ""
212
+ glyph = outcome[:status] == "pass" ? status_glyph(outcome[:status]) : colorize("✗", :red)
213
+ suffix = if outcome[:status] == "pass"
214
+ elapsed
215
+ elsif outcome[:failure_type] == "timeout"
216
+ " timed out after #{outcome[:timeout_seconds]}s"
217
+ else
218
+ " failed"
219
+ end
220
+ "#{glyph} #{target_display(outcome)}#{suffix}"
221
+ end
222
+
223
+ def target_display(target)
224
+ label = target[:label].to_s
225
+ selector = target[:selector].to_s
226
+ label = selector if label.empty?
227
+ selector.empty? || selector == label ? label : "#{label} (#{selector})"
228
+ end
229
+
230
+ def status_glyph(status)
231
+ glyph = STATUS_GLYPHS.fetch(status.to_s, STATUS_GLYPHS["warn"])
232
+ colorize(glyph, STATUS_COLORS.fetch(status.to_s, :yellow))
233
+ end
234
+
235
+ def health_checks
236
+ @health_checks ||= checks.select { |check| check[:kind] == "health" }
237
+ end
238
+
239
+ def hygiene_checks
240
+ @hygiene_checks ||= checks.select { |check| check[:kind] == "hygiene" }
241
+ end
242
+
243
+ def info_checks
244
+ @info_checks ||= checks.select { |check| check[:kind] == "info" }
245
+ end
246
+
247
+ def provider_outcomes
248
+ @provider_outcomes ||= Array(health_checks.find { |row| row[:id] == "probe-readiness" }&.fetch(:outcomes, []))
249
+ end
250
+
251
+ def checks
252
+ @results[:checks] || []
253
+ end
254
+
255
+ def format_duration(duration)
256
+ return "0ms" unless duration
257
+
258
+ duration < 1 ? "#{(duration * 1000).round}ms" : "#{duration.round(2)}s"
259
+ end
260
+
261
+ def colorize(text, color)
262
+ return text unless @colors && COLORS[color]
263
+
264
+ "#{COLORS[color]}#{text}#{COLORS[:reset]}"
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
@@ -8,7 +8,7 @@ module Ace
8
8
  module Support
9
9
  module Config
10
10
  module Organisms
11
- class ConfigInitializer
11
+ class ConfigSynchronizer
12
12
  PROJECT_ROOT_DIR = "project-root"
13
13
  GITIGNORE_ACE_LOCAL_ENTRY = ".ace-local/"
14
14
 
@@ -21,17 +21,17 @@ module Ace
21
21
  @skipped_files = []
22
22
  end
23
23
 
24
- def init_all
25
- puts "Initializing all ace-* gem configurations..." if @verbose
24
+ def sync_all
25
+ puts "Syncing all ace-* gem configurations..." if @verbose
26
26
 
27
27
  Models::ConfigTemplates.all_gems.each do |gem_name|
28
- init_gem(gem_name)
28
+ sync_gem(gem_name)
29
29
  end
30
30
 
31
31
  print_summary
32
32
  end
33
33
 
34
- def init_gem(gem_name)
34
+ def sync_gem(gem_name)
35
35
  gem_name = normalize_gem_name(gem_name)
36
36
 
37
37
  unless Models::ConfigTemplates.gem_exists?(gem_name)
@@ -39,7 +39,7 @@ module Ace
39
39
  return
40
40
  end
41
41
 
42
- puts "\nInitializing #{gem_name}..." if @verbose
42
+ puts "\nSyncing #{gem_name}..." if @verbose
43
43
 
44
44
  source_dir = Models::ConfigTemplates.example_dir_for(gem_name)
45
45
  target_dir = target_directory
@@ -182,7 +182,7 @@ module Ace
182
182
  def print_summary
183
183
  return if @dry_run
184
184
 
185
- puts "\nConfiguration initialization complete:"
185
+ puts "\nConfiguration sync complete:"
186
186
  puts " Files copied: #{@copied_files.size}"
187
187
  puts " Files skipped: #{@skipped_files.size}"
188
188