ace-support-core 0.29.3 → 0.29.8
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/.ace-defaults/README.md +16 -0
- data/.ace-defaults/project-root/AGENTS.md +17 -0
- data/.ace-defaults/project-root/CLAUDE.md +23 -0
- data/CHANGELOG.md +31 -0
- data/README.md +13 -3
- data/lib/ace/core/version.rb +1 -1
- metadata +6 -9
- data/exe/ace-framework +0 -13
- data/lib/ace/core/cli.rb +0 -192
- data/lib/ace/core/models/config_templates.rb +0 -87
- data/lib/ace/core/organisms/config_diff.rb +0 -187
- data/lib/ace/core/organisms/config_initializer.rb +0 -125
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42ad3d96ee72755834ba5c9c7ab09270262db526094e9377687c59b86b23dadc
|
|
4
|
+
data.tar.gz: 30276bee679ad5961e477a35ad055235be9da30a8de5aa9050f6ac613de209fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec3c10d1ceabf0d8d722c6e144185f7d522d5a2715391eb63e06c05c722134d8dabba023303d9572dfeb937cdd00b6c8ad1f6fd7f8a4e3f2545ed5f935228a15
|
|
7
|
+
data.tar.gz: f137cf374ad87dd9cdf690377208abe3455b856a4f76079a353060200635a02a1d834693c01e59a0fc6e1c809cee77259d6922844f533782e98c655ffdff68e3
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# .ace Configuration
|
|
2
|
+
|
|
3
|
+
This directory contains local ACE configuration for your project.
|
|
4
|
+
These starter files are intentionally generic so you can tailor them to your own codebase.
|
|
5
|
+
|
|
6
|
+
## First Steps
|
|
7
|
+
|
|
8
|
+
1. Review `.ace/bundle/presets/project.md` and replace TODO placeholders.
|
|
9
|
+
2. Optionally use `.ace/bundle/presets/project-base.md` for lightweight context loading.
|
|
10
|
+
3. Run `ace-bundle project` to verify the preset loads successfully.
|
|
11
|
+
4. Use `ace-task status` to inspect and manage your project task queue.
|
|
12
|
+
|
|
13
|
+
## Notes
|
|
14
|
+
|
|
15
|
+
- Re-run `ace-config init --force` to refresh files from gem defaults.
|
|
16
|
+
- Keep project-specific guidance in your local `.ace/` files.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
Read `CLAUDE.md` first.
|
|
4
|
+
|
|
5
|
+
## ACE CLI Command Integrity
|
|
6
|
+
|
|
7
|
+
Run `ace-*` commands directly. Do not pipe, redirect, or post-process `ace-*` output.
|
|
8
|
+
|
|
9
|
+
- Run `ace-*` commands directly.
|
|
10
|
+
- When an `ace-*` command prints a file path, read that file directly.
|
|
11
|
+
- Use `.ace-local/` for project-local ACE artifacts and `/tmp/` for disposable temporary files.
|
|
12
|
+
|
|
13
|
+
## Working Rules
|
|
14
|
+
|
|
15
|
+
- Keep repository-specific guidance in this file.
|
|
16
|
+
- Do not overwrite unrelated user-owned content when refreshing generated bootstrap files.
|
|
17
|
+
- Use `ace-test` for tests when ACE packages provide test targets.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Agent guidance for this repository.
|
|
4
|
+
|
|
5
|
+
## Command Types
|
|
6
|
+
|
|
7
|
+
- Slash commands such as `/as-task-work` run inside the chat tool.
|
|
8
|
+
- `ace-*` commands run in the terminal.
|
|
9
|
+
|
|
10
|
+
## ACE CLI Output Handling
|
|
11
|
+
|
|
12
|
+
- Run `ace-*` commands directly.
|
|
13
|
+
- Do not use pipes, redirects, or shell post-processors on `ace-*` commands.
|
|
14
|
+
- If an `ace-*` command prints a file path, read that file directly.
|
|
15
|
+
|
|
16
|
+
## Local Artifacts
|
|
17
|
+
|
|
18
|
+
- Use `.ace-local/` for project-local ACE artifacts.
|
|
19
|
+
- Use `/tmp/` for disposable temporary files.
|
|
20
|
+
|
|
21
|
+
## Testing
|
|
22
|
+
|
|
23
|
+
- Prefer `ace-test` and `ace-test-suite` over raw `bundle exec` test commands when those targets exist.
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.29.8] - 2026-04-13
|
|
11
|
+
|
|
12
|
+
### Technical
|
|
13
|
+
- **ace-support-core v0.29.8**: Added shipped project-root bootstrap templates for `.gitignore`, `AGENTS.md`, and `CLAUDE.md` so freshly initialized ACE projects get local-artifact hygiene and starter agent guidance.
|
|
14
|
+
|
|
15
|
+
## [0.29.7] - 2026-04-13
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **ace-support-core v0.29.7**: Standardized shared package tests to the fast-only layout and updated testing flow defaults.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Technical
|
|
23
|
+
- Relaxed fast-test version assertions to validate semantic-version shape instead of pinned placeholder values in package/config fixtures.
|
|
24
|
+
## [0.29.6] - 2026-04-11
|
|
25
|
+
|
|
26
|
+
### Technical
|
|
27
|
+
- Migrated package tests to `test/fast/` and `test/feat/`, replacing the legacy `test/integration/` layout while keeping deterministic-only coverage.
|
|
28
|
+
- Updated README testing guidance to the public `ace-test ace-support-core` / `feat` / `all` contract and clarified no package-owned `test/e2e/` surface.
|
|
29
|
+
|
|
30
|
+
## [0.29.5] - 2026-03-31
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Migrated config CLI ownership to `ace-support-config` by removing `ace-framework` packaging/runtime surfaces from `ace-support-core` and updating package docs/default guidance to reference `ace-config`.
|
|
34
|
+
|
|
35
|
+
## [0.29.4] - 2026-03-29
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- Added a shipped `.ace-defaults/README.md` scaffold so `ace-framework init` generates `.ace/README.md` for newly initialized projects.
|
|
39
|
+
- Updated bootstrap configuration guidance and examples to current `ace-task` CLI usage and documented generated `project`/`project-base` presets.
|
|
40
|
+
|
|
10
41
|
## [0.29.3] - 2026-03-29
|
|
11
42
|
|
|
12
43
|
### Technical
|
data/README.md
CHANGED
|
@@ -14,13 +14,23 @@
|
|
|
14
14
|
|
|
15
15
|
> Works with: Claude Code, Codex CLI, OpenCode, Gemini CLI, pi-agent, and more.
|
|
16
16
|
|
|
17
|
-
`ace-support-core` centralizes configuration loading, environment handling, and shared runtime behavior used by support libraries. It delegates config resolution through [ace-support-config](../ace-support-config) and exposes stable orchestration points that other `ace-support-*` gems build on.
|
|
17
|
+
`ace-support-core` centralizes configuration loading, environment handling, and shared runtime behavior used by support libraries. It delegates config resolution through [ace-support-config](../ace-support-config) and exposes stable orchestration points that other `ace-support-*` gems build on.
|
|
18
18
|
|
|
19
19
|
## How It Works
|
|
20
20
|
|
|
21
21
|
1. Provide a shared bootstrap path for config, env, and runtime context so every `ace-support-*` gem starts from the same foundation.
|
|
22
22
|
2. Delegate cascaded configuration resolution through [ace-support-config](../ace-support-config) so packages inherit project, user, and gem-level defaults automatically.
|
|
23
|
-
3.
|
|
23
|
+
3. Provide shared runtime/config primitives consumed by `ace-support-config`, which owns the `ace-config` CLI.
|
|
24
|
+
|
|
25
|
+
## Testing
|
|
26
|
+
|
|
27
|
+
This package uses the batch-2 deterministic testing contract:
|
|
28
|
+
|
|
29
|
+
- `ace-test ace-support-core` runs the default `fast` loop from `test/fast/`.
|
|
30
|
+
- `ace-test ace-support-core feat` runs deterministic feature coverage from `test/feat/`.
|
|
31
|
+
- `ace-test ace-support-core all` runs both deterministic layers.
|
|
32
|
+
|
|
33
|
+
`ace-support-core` does not define package-owned `test/e2e/` scenarios in this migration.
|
|
24
34
|
|
|
25
35
|
## Use Cases
|
|
26
36
|
|
|
@@ -31,4 +41,4 @@
|
|
|
31
41
|
**Keep support libraries composable** - build package-specific features on top of stable core contracts that provide predictable runtime behavior.
|
|
32
42
|
|
|
33
43
|
---
|
|
34
|
-
[Configuration overview](docs/config.md) | [`ace-
|
|
44
|
+
[Configuration overview](docs/config.md) | [`ace-config` usage](../ace-support-config/docs/usage.md) | Part of [ACE](https://github.com/cs3b/ace)
|
data/lib/ace/core/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-support-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.29.
|
|
4
|
+
version: 0.29.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-04-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-config
|
|
@@ -56,17 +56,18 @@ description: Foundational infrastructure gem providing configuration cascade res
|
|
|
56
56
|
gem following ace-support-* pattern for infrastructure components.
|
|
57
57
|
email:
|
|
58
58
|
- mc@cs3b.com
|
|
59
|
-
executables:
|
|
60
|
-
- ace-framework
|
|
59
|
+
executables: []
|
|
61
60
|
extensions: []
|
|
62
61
|
extra_rdoc_files: []
|
|
63
62
|
files:
|
|
63
|
+
- ".ace-defaults/README.md"
|
|
64
64
|
- ".ace-defaults/core/settings.yml"
|
|
65
|
+
- ".ace-defaults/project-root/AGENTS.md"
|
|
66
|
+
- ".ace-defaults/project-root/CLAUDE.md"
|
|
65
67
|
- CHANGELOG.md
|
|
66
68
|
- LICENSE
|
|
67
69
|
- README.md
|
|
68
70
|
- Rakefile
|
|
69
|
-
- exe/ace-framework
|
|
70
71
|
- lib/ace/core.rb
|
|
71
72
|
- lib/ace/core/atoms/command_executor.rb
|
|
72
73
|
- lib/ace/core/atoms/config_summary.rb
|
|
@@ -75,18 +76,14 @@ files:
|
|
|
75
76
|
- lib/ace/core/atoms/glob_expander.rb
|
|
76
77
|
- lib/ace/core/atoms/process_terminator.rb
|
|
77
78
|
- lib/ace/core/atoms/template_parser.rb
|
|
78
|
-
- lib/ace/core/cli.rb
|
|
79
79
|
- lib/ace/core/cli/config_summary_mixin.rb
|
|
80
80
|
- lib/ace/core/config_discovery.rb
|
|
81
81
|
- lib/ace/core/errors.rb
|
|
82
|
-
- lib/ace/core/models/config_templates.rb
|
|
83
82
|
- lib/ace/core/molecules/env_loader.rb
|
|
84
83
|
- lib/ace/core/molecules/file_aggregator.rb
|
|
85
84
|
- lib/ace/core/molecules/frontmatter_free_policy.rb
|
|
86
85
|
- lib/ace/core/molecules/output_formatter.rb
|
|
87
86
|
- lib/ace/core/molecules/prompt_cache_manager.rb
|
|
88
|
-
- lib/ace/core/organisms/config_diff.rb
|
|
89
|
-
- lib/ace/core/organisms/config_initializer.rb
|
|
90
87
|
- lib/ace/core/organisms/environment_manager.rb
|
|
91
88
|
- lib/ace/core/version.rb
|
|
92
89
|
homepage: https://github.com/cs3b/ace
|
data/exe/ace-framework
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "ace/core"
|
|
5
|
-
require "ace/core/cli"
|
|
6
|
-
|
|
7
|
-
# Start FrameworkCLI (separate from Ace::Core::CLI module which is ace-support-cli infrastructure)
|
|
8
|
-
begin
|
|
9
|
-
Ace::Core::FrameworkCLI.start(ARGV)
|
|
10
|
-
rescue Ace::Support::Cli::Error => e
|
|
11
|
-
warn e.message
|
|
12
|
-
exit(e.exit_code)
|
|
13
|
-
end
|
data/lib/ace/core/cli.rb
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "optparse"
|
|
4
|
-
require_relative "organisms/config_initializer"
|
|
5
|
-
require_relative "organisms/config_diff"
|
|
6
|
-
require_relative "models/config_templates"
|
|
7
|
-
|
|
8
|
-
module Ace
|
|
9
|
-
module Core
|
|
10
|
-
# Framework CLI for ace-framework binary.
|
|
11
|
-
# Uses a separate class name to avoid collision with Ace::Core::CLI module
|
|
12
|
-
# (which provides shared CLI infrastructure).
|
|
13
|
-
class FrameworkCLI
|
|
14
|
-
def self.start(argv)
|
|
15
|
-
new.run(argv)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def run(argv)
|
|
19
|
-
return show_help if argv.empty?
|
|
20
|
-
|
|
21
|
-
command = argv.shift
|
|
22
|
-
|
|
23
|
-
case command
|
|
24
|
-
when "init"
|
|
25
|
-
run_init(argv)
|
|
26
|
-
when "diff"
|
|
27
|
-
run_diff(argv)
|
|
28
|
-
when "list"
|
|
29
|
-
run_list(argv)
|
|
30
|
-
when "version", "--version"
|
|
31
|
-
show_version
|
|
32
|
-
when "help", "--help", "-h"
|
|
33
|
-
show_help
|
|
34
|
-
else
|
|
35
|
-
puts "Unknown command: #{command}"
|
|
36
|
-
puts ""
|
|
37
|
-
show_help
|
|
38
|
-
exit 1
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def run_init(argv)
|
|
45
|
-
options = {}
|
|
46
|
-
|
|
47
|
-
parser = OptionParser.new do |opts|
|
|
48
|
-
opts.banner = <<~BANNER.chomp
|
|
49
|
-
NAME
|
|
50
|
-
ace-framework init - Initialize configuration for ace-* gems
|
|
51
|
-
|
|
52
|
-
USAGE
|
|
53
|
-
ace-framework init [GEM] [OPTIONS]
|
|
54
|
-
|
|
55
|
-
OPTIONS
|
|
56
|
-
BANNER
|
|
57
|
-
opts.on("--force", "Overwrite existing files") { options[:force] = true }
|
|
58
|
-
opts.on("--dry-run", "Show what would be done") { options[:dry_run] = true }
|
|
59
|
-
opts.on("--global", "Use ~/.ace instead of ./.ace") { options[:global] = true }
|
|
60
|
-
opts.on("--verbose", "Show verbose output") { options[:verbose] = true }
|
|
61
|
-
opts.on("-h", "--help", "Show this help") {
|
|
62
|
-
puts opts
|
|
63
|
-
exit
|
|
64
|
-
}
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
parser.parse!(argv)
|
|
68
|
-
gem_name = argv.shift
|
|
69
|
-
|
|
70
|
-
initializer = ConfigInitializer.new(**options)
|
|
71
|
-
|
|
72
|
-
if gem_name
|
|
73
|
-
initializer.init_gem(gem_name)
|
|
74
|
-
else
|
|
75
|
-
initializer.init_all
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def run_diff(argv)
|
|
80
|
-
options = {}
|
|
81
|
-
|
|
82
|
-
parser = OptionParser.new do |opts|
|
|
83
|
-
opts.banner = <<~BANNER.chomp
|
|
84
|
-
NAME
|
|
85
|
-
ace-framework diff - Compare configs with examples
|
|
86
|
-
|
|
87
|
-
USAGE
|
|
88
|
-
ace-framework diff [GEM] [OPTIONS]
|
|
89
|
-
|
|
90
|
-
OPTIONS
|
|
91
|
-
BANNER
|
|
92
|
-
opts.on("--global", "Compare global configs") { options[:global] = true }
|
|
93
|
-
opts.on("--local", "Compare local configs (default)") { options[:local] = true }
|
|
94
|
-
opts.on("--file PATH", "Compare specific file") { |f| options[:file] = f }
|
|
95
|
-
opts.on("--one-line", "One-line summary per file") { options[:one_line] = true }
|
|
96
|
-
opts.on("-h", "--help", "Show this help") {
|
|
97
|
-
puts opts
|
|
98
|
-
exit
|
|
99
|
-
}
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
parser.parse!(argv)
|
|
103
|
-
gem_name = argv.shift
|
|
104
|
-
|
|
105
|
-
differ = ConfigDiff.new(**options)
|
|
106
|
-
|
|
107
|
-
if gem_name
|
|
108
|
-
differ.diff_gem(gem_name)
|
|
109
|
-
else
|
|
110
|
-
differ.run
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def run_list(argv)
|
|
115
|
-
verbose = false
|
|
116
|
-
|
|
117
|
-
parser = OptionParser.new do |opts|
|
|
118
|
-
opts.banner = <<~BANNER.chomp
|
|
119
|
-
NAME
|
|
120
|
-
ace-framework list - List available ace-* gems with example configs
|
|
121
|
-
|
|
122
|
-
USAGE
|
|
123
|
-
ace-framework list [OPTIONS]
|
|
124
|
-
|
|
125
|
-
OPTIONS
|
|
126
|
-
BANNER
|
|
127
|
-
opts.on("--verbose", "Show detailed information") { verbose = true }
|
|
128
|
-
opts.on("-h", "--help", "Show this help") {
|
|
129
|
-
puts opts
|
|
130
|
-
exit
|
|
131
|
-
}
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
parser.parse!(argv)
|
|
135
|
-
|
|
136
|
-
puts "Available ace-* gems with example configurations:\n\n"
|
|
137
|
-
|
|
138
|
-
if ConfigTemplates.all_gems.empty?
|
|
139
|
-
puts "No ace-* gems with example configurations found."
|
|
140
|
-
return
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
ConfigTemplates.all_gems.each do |gem_name|
|
|
144
|
-
info = ConfigTemplates.gem_info[gem_name]
|
|
145
|
-
source_label = case info[:source]
|
|
146
|
-
when :local then "[local]"
|
|
147
|
-
when :gem then "[gem]"
|
|
148
|
-
when :both then "[local+gem]"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
puts " #{gem_name} #{source_label}"
|
|
152
|
-
|
|
153
|
-
if verbose
|
|
154
|
-
puts " Path: #{info[:path]}"
|
|
155
|
-
puts " Gem: #{info[:gem_path]}" if info[:gem_path]
|
|
156
|
-
example_dir = ConfigTemplates.example_dir_for(gem_name)
|
|
157
|
-
if example_dir && File.exist?(example_dir)
|
|
158
|
-
example_files = Dir.glob("#{example_dir}/**/*").reject { |f| File.directory?(f) }
|
|
159
|
-
puts " Example files: #{example_files.size}"
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
puts "\nUse 'ace-framework init [GEM]' to initialize a specific gem's configuration"
|
|
165
|
-
puts "Use 'ace-framework init' to initialize all configurations"
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def show_version
|
|
169
|
-
puts "ace-framework #{Ace::Core::VERSION}"
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def show_help
|
|
173
|
-
puts <<~HELP
|
|
174
|
-
NAME
|
|
175
|
-
ace-framework - Configuration management for ace-* gems
|
|
176
|
-
|
|
177
|
-
USAGE
|
|
178
|
-
ace-framework COMMAND [OPTIONS]
|
|
179
|
-
|
|
180
|
-
COMMANDS
|
|
181
|
-
init [GEM] Initialize configuration for specific gem or all
|
|
182
|
-
diff [GEM] Compare configs with examples
|
|
183
|
-
list List available ace-* gems with example configs
|
|
184
|
-
version Show version
|
|
185
|
-
help Show this help
|
|
186
|
-
|
|
187
|
-
Run 'ace-framework COMMAND --help' for more information on a command.
|
|
188
|
-
HELP
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
end
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "pathname"
|
|
4
|
-
require "rubygems"
|
|
5
|
-
|
|
6
|
-
module Ace
|
|
7
|
-
module Core
|
|
8
|
-
class ConfigTemplates
|
|
9
|
-
class << self
|
|
10
|
-
def all_gems
|
|
11
|
-
gem_info.keys.sort
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def gem_exists?(gem_name)
|
|
15
|
-
gem_info.key?(gem_name)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def example_dir_for(gem_name)
|
|
19
|
-
info = gem_info[gem_name]
|
|
20
|
-
return nil unless info
|
|
21
|
-
|
|
22
|
-
# Prefer local path for development
|
|
23
|
-
path = (info[:source] == :gem) ? info[:path] : (info[:path] || info[:gem_path])
|
|
24
|
-
resolve_defaults_dir(path)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def gem_info
|
|
28
|
-
@gem_info ||= build_gem_info
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def build_gem_info
|
|
32
|
-
gems = {}
|
|
33
|
-
|
|
34
|
-
# 1. Look in the parent directory (monorepo/development)
|
|
35
|
-
parent_dir = File.expand_path("../../../../../../", __FILE__)
|
|
36
|
-
Dir.glob("#{parent_dir}/ace-*").each do |dir|
|
|
37
|
-
next unless File.directory?(dir)
|
|
38
|
-
gem_name = File.basename(dir)
|
|
39
|
-
if has_example_dir?(dir)
|
|
40
|
-
gems[gem_name] = {source: :local, path: dir}
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# 2. Look for installed RubyGems
|
|
45
|
-
begin
|
|
46
|
-
Gem::Specification.each do |spec|
|
|
47
|
-
next unless spec.name.start_with?("ace-")
|
|
48
|
-
|
|
49
|
-
gem_path = spec.gem_dir
|
|
50
|
-
if has_example_dir?(gem_path)
|
|
51
|
-
if gems.key?(spec.name)
|
|
52
|
-
gems[spec.name][:source] = :both
|
|
53
|
-
gems[spec.name][:gem_path] = gem_path
|
|
54
|
-
else
|
|
55
|
-
gems[spec.name] = {source: :gem, path: gem_path}
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
rescue
|
|
60
|
-
# If we can't access installed gems, just use local ones
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
gems
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def docs_file_for(gem_name)
|
|
67
|
-
info = gem_info[gem_name]
|
|
68
|
-
return nil unless info
|
|
69
|
-
|
|
70
|
-
# Prefer local path for development
|
|
71
|
-
path = (info[:source] == :gem) ? info[:path] : (info[:path] || info[:gem_path])
|
|
72
|
-
File.join(path, "docs", "config.md")
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
def resolve_defaults_dir(gem_path)
|
|
78
|
-
File.join(gem_path, ".ace-defaults")
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def has_example_dir?(gem_dir)
|
|
82
|
-
Dir.exist?(File.join(gem_dir, ".ace-defaults"))
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "open3"
|
|
4
|
-
require "pathname"
|
|
5
|
-
require_relative "../models/config_templates"
|
|
6
|
-
|
|
7
|
-
module Ace
|
|
8
|
-
module Core
|
|
9
|
-
class ConfigDiff
|
|
10
|
-
def initialize(global: false, file: nil, one_line: false)
|
|
11
|
-
@global = global
|
|
12
|
-
@file = file
|
|
13
|
-
@one_line = one_line
|
|
14
|
-
@diffs = []
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def run
|
|
18
|
-
if @file
|
|
19
|
-
diff_file(@file)
|
|
20
|
-
else
|
|
21
|
-
diff_all_configs
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
print_results
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def diff_gem(gem_name)
|
|
28
|
-
# Normalize gem name (support both "ace-bundle" and "bundle")
|
|
29
|
-
gem_name = gem_name.start_with?("ace-") ? gem_name : "ace-#{gem_name}"
|
|
30
|
-
|
|
31
|
-
unless ConfigTemplates.gem_exists?(gem_name)
|
|
32
|
-
puts "Error: Gem '#{gem_name}' not found or has no example configurations"
|
|
33
|
-
exit 1
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
diff_gem_configs(gem_name)
|
|
37
|
-
print_results
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def config_directory
|
|
43
|
-
@global ? File.expand_path("~/.ace") : ".ace"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def diff_all_configs
|
|
47
|
-
ConfigTemplates.all_gems.each do |gem_name|
|
|
48
|
-
diff_gem_configs(gem_name)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def diff_gem_configs(gem_name)
|
|
53
|
-
source_dir = ConfigTemplates.example_dir_for(gem_name)
|
|
54
|
-
|
|
55
|
-
return unless source_dir && File.exist?(source_dir)
|
|
56
|
-
|
|
57
|
-
Dir.glob("#{source_dir}/**/*").each do |source_file|
|
|
58
|
-
next if File.directory?(source_file)
|
|
59
|
-
|
|
60
|
-
relative_path = Pathname.new(source_file).relative_path_from(Pathname.new(source_dir))
|
|
61
|
-
target_file = File.join(config_directory, relative_path.to_s)
|
|
62
|
-
|
|
63
|
-
compare_files(source_file, target_file, gem_name)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def diff_file(file_path)
|
|
68
|
-
# Determine which gem this file belongs to
|
|
69
|
-
if file_path.start_with?(config_directory)
|
|
70
|
-
relative_path = Pathname.new(file_path).relative_path_from(Pathname.new(config_directory))
|
|
71
|
-
parts = relative_path.to_s.split(File::SEPARATOR)
|
|
72
|
-
|
|
73
|
-
if parts.any?
|
|
74
|
-
config_subdir = parts.first
|
|
75
|
-
gem_name = "ace-#{config_subdir}"
|
|
76
|
-
|
|
77
|
-
if ConfigTemplates.gem_exists?(gem_name)
|
|
78
|
-
source_dir = ConfigTemplates.example_dir_for(gem_name)
|
|
79
|
-
relative_file = parts[1..-1].join(File::SEPARATOR)
|
|
80
|
-
source_file = File.join(source_dir, relative_file)
|
|
81
|
-
|
|
82
|
-
if File.exist?(source_file)
|
|
83
|
-
compare_files(source_file, file_path, gem_name)
|
|
84
|
-
else
|
|
85
|
-
puts "No example file found for #{file_path}"
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
else
|
|
90
|
-
puts "File #{file_path} is not in a configuration directory"
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def compare_files(source_file, target_file, gem_name)
|
|
95
|
-
@diffs << if !File.exist?(target_file)
|
|
96
|
-
{
|
|
97
|
-
gem: gem_name,
|
|
98
|
-
file: target_file,
|
|
99
|
-
status: :missing,
|
|
100
|
-
source: source_file
|
|
101
|
-
}
|
|
102
|
-
elsif files_differ?(source_file, target_file)
|
|
103
|
-
{
|
|
104
|
-
gem: gem_name,
|
|
105
|
-
file: target_file,
|
|
106
|
-
status: :different,
|
|
107
|
-
source: source_file,
|
|
108
|
-
diff_output: get_diff_output(source_file, target_file)
|
|
109
|
-
}
|
|
110
|
-
else
|
|
111
|
-
{
|
|
112
|
-
gem: gem_name,
|
|
113
|
-
file: target_file,
|
|
114
|
-
status: :same,
|
|
115
|
-
source: source_file
|
|
116
|
-
}
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def files_differ?(file1, file2)
|
|
121
|
-
File.read(file1) != File.read(file2)
|
|
122
|
-
rescue
|
|
123
|
-
true
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def get_diff_output(source_file, target_file)
|
|
127
|
-
# Use system diff command
|
|
128
|
-
output, _status = Open3.capture2("diff", "-u", target_file, source_file)
|
|
129
|
-
output
|
|
130
|
-
rescue
|
|
131
|
-
"Unable to generate diff"
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def print_results
|
|
135
|
-
if @one_line
|
|
136
|
-
print_one_line_summary
|
|
137
|
-
else
|
|
138
|
-
print_detailed_diffs
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def print_one_line_summary
|
|
143
|
-
@diffs.each do |diff|
|
|
144
|
-
case diff[:status]
|
|
145
|
-
when :missing
|
|
146
|
-
puts "MISSING: #{diff[:file]}"
|
|
147
|
-
when :different
|
|
148
|
-
puts "CHANGED: #{diff[:file]}"
|
|
149
|
-
when :same
|
|
150
|
-
puts "SAME: #{diff[:file]}" if @verbose
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
puts "\nSummary:"
|
|
155
|
-
puts " Missing: #{@diffs.count { |d| d[:status] == :missing }}"
|
|
156
|
-
puts " Changed: #{@diffs.count { |d| d[:status] == :different }}"
|
|
157
|
-
puts " Same: #{@diffs.count { |d| d[:status] == :same }}"
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def print_detailed_diffs
|
|
161
|
-
missing = @diffs.select { |d| d[:status] == :missing }
|
|
162
|
-
changed = @diffs.select { |d| d[:status] == :different }
|
|
163
|
-
|
|
164
|
-
if missing.any?
|
|
165
|
-
puts "Missing configuration files:"
|
|
166
|
-
missing.each do |diff|
|
|
167
|
-
puts " #{diff[:file]}"
|
|
168
|
-
puts " -> Example: #{diff[:source]}"
|
|
169
|
-
end
|
|
170
|
-
puts
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
if changed.any?
|
|
174
|
-
puts "Changed configuration files:"
|
|
175
|
-
changed.each do |diff|
|
|
176
|
-
puts "\n#{diff[:file]}:"
|
|
177
|
-
puts diff[:diff_output]
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
if missing.empty? && changed.empty?
|
|
182
|
-
puts "All configuration files match the examples."
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
end
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "fileutils"
|
|
4
|
-
require "pathname"
|
|
5
|
-
require_relative "../models/config_templates"
|
|
6
|
-
|
|
7
|
-
module Ace
|
|
8
|
-
module Core
|
|
9
|
-
class ConfigInitializer
|
|
10
|
-
def initialize(force: false, dry_run: false, global: false, verbose: false)
|
|
11
|
-
@force = force
|
|
12
|
-
@dry_run = dry_run
|
|
13
|
-
@global = global
|
|
14
|
-
@verbose = verbose
|
|
15
|
-
@copied_files = []
|
|
16
|
-
@skipped_files = []
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def init_all
|
|
20
|
-
puts "Initializing all ace-* gem configurations..." if @verbose
|
|
21
|
-
|
|
22
|
-
ConfigTemplates.all_gems.each do |gem_name|
|
|
23
|
-
init_gem(gem_name)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
print_summary
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def init_gem(gem_name)
|
|
30
|
-
gem_name = normalize_gem_name(gem_name)
|
|
31
|
-
|
|
32
|
-
unless ConfigTemplates.gem_exists?(gem_name)
|
|
33
|
-
puts "Warning: No configuration found for #{gem_name}"
|
|
34
|
-
return
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
puts "\nInitializing #{gem_name}..." if @verbose
|
|
38
|
-
|
|
39
|
-
source_dir = ConfigTemplates.example_dir_for(gem_name)
|
|
40
|
-
target_dir = target_directory
|
|
41
|
-
|
|
42
|
-
unless File.exist?(source_dir)
|
|
43
|
-
puts "Warning: No .ace-defaults directory found for #{gem_name}"
|
|
44
|
-
return
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Show config docs on first run if config is missing
|
|
48
|
-
show_config_docs_if_needed(gem_name, target_dir)
|
|
49
|
-
|
|
50
|
-
# Copy files from .ace-defaults to .ace
|
|
51
|
-
copy_config_files(source_dir, target_dir, gem_name)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
private
|
|
55
|
-
|
|
56
|
-
def normalize_gem_name(name)
|
|
57
|
-
# Handle both "ace-core" and "core" formats
|
|
58
|
-
name.start_with?("ace-") ? name : "ace-#{name}"
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def target_directory
|
|
62
|
-
@global ? File.expand_path("~/.ace") : ".ace"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def show_config_docs_if_needed(gem_name, target_dir)
|
|
66
|
-
# Check if any config files exist for this gem
|
|
67
|
-
config_subdir = gem_name.sub("ace-", "")
|
|
68
|
-
|
|
69
|
-
# Look for any existing config files in the expected location
|
|
70
|
-
existing_configs = Dir.glob("#{target_dir}/#{config_subdir}/**/*").reject { |f| File.directory?(f) }
|
|
71
|
-
|
|
72
|
-
# Only show docs if this is the first time (no existing config)
|
|
73
|
-
if existing_configs.empty? && !@dry_run
|
|
74
|
-
docs_file = ConfigTemplates.docs_file_for(gem_name)
|
|
75
|
-
if File.exist?(docs_file)
|
|
76
|
-
puts "\n#{File.read(docs_file)}\n"
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def copy_config_files(source_dir, target_dir, gem_name)
|
|
82
|
-
Dir.glob("#{source_dir}/**/*").each do |source_file|
|
|
83
|
-
next if File.directory?(source_file)
|
|
84
|
-
|
|
85
|
-
# Calculate relative path from source_dir
|
|
86
|
-
relative_path = Pathname.new(source_file).relative_path_from(Pathname.new(source_dir))
|
|
87
|
-
|
|
88
|
-
# Build target path - files already include their subdirectory
|
|
89
|
-
target_file = File.join(target_dir, relative_path.to_s)
|
|
90
|
-
|
|
91
|
-
copy_file(source_file, target_file)
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def copy_file(source, target)
|
|
96
|
-
if File.exist?(target) && !@force
|
|
97
|
-
@skipped_files << target
|
|
98
|
-
puts " Skipped: #{target} (already exists)" if @verbose
|
|
99
|
-
return
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
if @dry_run
|
|
103
|
-
puts " Would copy: #{source} -> #{target}"
|
|
104
|
-
else
|
|
105
|
-
FileUtils.mkdir_p(File.dirname(target))
|
|
106
|
-
FileUtils.cp(source, target)
|
|
107
|
-
@copied_files << target
|
|
108
|
-
puts " Copied: #{target}" if @verbose
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def print_summary
|
|
113
|
-
return if @dry_run
|
|
114
|
-
|
|
115
|
-
puts "\nConfiguration initialization complete:"
|
|
116
|
-
puts " Files copied: #{@copied_files.size}"
|
|
117
|
-
puts " Files skipped: #{@skipped_files.size}"
|
|
118
|
-
|
|
119
|
-
if @skipped_files.any? && !@force
|
|
120
|
-
puts "\nUse --force to overwrite existing files"
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|