devex 0.3.5

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 (72) hide show
  1. checksums.yaml +7 -0
  2. data/.obsidian/app.json +6 -0
  3. data/.obsidian/appearance.json +4 -0
  4. data/.obsidian/community-plugins.json +5 -0
  5. data/.obsidian/core-plugins.json +33 -0
  6. data/.obsidian/plugins/obsidian-minimal-settings/data.json +34 -0
  7. data/.obsidian/plugins/obsidian-minimal-settings/main.js +8 -0
  8. data/.obsidian/plugins/obsidian-minimal-settings/manifest.json +11 -0
  9. data/.obsidian/plugins/obsidian-style-settings/data.json +15 -0
  10. data/.obsidian/plugins/obsidian-style-settings/main.js +165 -0
  11. data/.obsidian/plugins/obsidian-style-settings/manifest.json +10 -0
  12. data/.obsidian/plugins/obsidian-style-settings/styles.css +243 -0
  13. data/.obsidian/plugins/table-editor-obsidian/data.json +6 -0
  14. data/.obsidian/plugins/table-editor-obsidian/main.js +236 -0
  15. data/.obsidian/plugins/table-editor-obsidian/manifest.json +17 -0
  16. data/.obsidian/plugins/table-editor-obsidian/styles.css +78 -0
  17. data/.obsidian/themes/AnuPpuccin/manifest.json +7 -0
  18. data/.obsidian/themes/AnuPpuccin/theme.css +9080 -0
  19. data/.obsidian/themes/Minimal/manifest.json +8 -0
  20. data/.obsidian/themes/Minimal/theme.css +2251 -0
  21. data/.rubocop.yml +231 -0
  22. data/CHANGELOG.md +97 -0
  23. data/LICENSE +21 -0
  24. data/README.md +314 -0
  25. data/Rakefile +13 -0
  26. data/devex-logo.jpg +0 -0
  27. data/docs/developing-tools.md +1000 -0
  28. data/docs/ref/agent-mode.md +46 -0
  29. data/docs/ref/cli-interface.md +60 -0
  30. data/docs/ref/configuration.md +46 -0
  31. data/docs/ref/design-philosophy.md +17 -0
  32. data/docs/ref/error-handling.md +38 -0
  33. data/docs/ref/io-handling.md +88 -0
  34. data/docs/ref/signals.md +141 -0
  35. data/docs/ref/temporal-software-theory.md +790 -0
  36. data/exe/dx +52 -0
  37. data/lib/devex/builtins/.index.rb +10 -0
  38. data/lib/devex/builtins/debug.rb +43 -0
  39. data/lib/devex/builtins/format.rb +44 -0
  40. data/lib/devex/builtins/gem.rb +77 -0
  41. data/lib/devex/builtins/lint.rb +61 -0
  42. data/lib/devex/builtins/test.rb +76 -0
  43. data/lib/devex/builtins/version.rb +156 -0
  44. data/lib/devex/cli.rb +340 -0
  45. data/lib/devex/context.rb +433 -0
  46. data/lib/devex/core/configuration.rb +136 -0
  47. data/lib/devex/core.rb +79 -0
  48. data/lib/devex/dirs.rb +210 -0
  49. data/lib/devex/dsl.rb +100 -0
  50. data/lib/devex/exec/controller.rb +245 -0
  51. data/lib/devex/exec/result.rb +229 -0
  52. data/lib/devex/exec.rb +662 -0
  53. data/lib/devex/loader.rb +136 -0
  54. data/lib/devex/output.rb +257 -0
  55. data/lib/devex/project_paths.rb +309 -0
  56. data/lib/devex/support/ansi.rb +437 -0
  57. data/lib/devex/support/core_ext.rb +560 -0
  58. data/lib/devex/support/global.rb +68 -0
  59. data/lib/devex/support/path.rb +357 -0
  60. data/lib/devex/support.rb +71 -0
  61. data/lib/devex/template_helpers.rb +136 -0
  62. data/lib/devex/templates/debug.erb +24 -0
  63. data/lib/devex/tool.rb +374 -0
  64. data/lib/devex/version.rb +5 -0
  65. data/lib/devex/working_dir.rb +99 -0
  66. data/lib/devex.rb +158 -0
  67. data/ruby-project-template/.gitignore +0 -0
  68. data/ruby-project-template/Gemfile +0 -0
  69. data/ruby-project-template/README.md +0 -0
  70. data/ruby-project-template/docs/README.md +0 -0
  71. data/sig/devex.rbs +4 -0
  72. metadata +122 -0
data/.rubocop.yml ADDED
@@ -0,0 +1,231 @@
1
+ # Archema RuboCop Configuration
2
+ #
3
+ # Layout/alignment settings come from tablecop.
4
+ # This file contains archema-specific settings.
5
+
6
+ plugins:
7
+ - rubocop-minitest
8
+ - rubocop-tablecop
9
+
10
+ AllCops:
11
+ TargetRubyVersion: 3.3
12
+ NewCops: enable
13
+ SuggestExtensions: false
14
+ Exclude:
15
+ - "tmp/**/*"
16
+ - "vendor/**/*"
17
+ - "coverage/**/*"
18
+ - "examples/**/*"
19
+ - "docs/msc/**/*" # Experimental analysis scripts, not production code
20
+
21
+ # ─────────────────────────────────────────────────────────────────────────────
22
+ # Style
23
+ # ─────────────────────────────────────────────────────────────────────────────
24
+
25
+ # Allow both single and double quotes - use double by convention,
26
+ # single when semantically meaningful (e.g., JSON-style string keys)
27
+ Style/StringLiterals:
28
+ Enabled: false
29
+
30
+ # Prefer explicit bracket syntax for symbol arrays
31
+ Style/SymbolArray:
32
+ EnforcedStyle: brackets
33
+
34
+ # Prefer %w[] for word arrays
35
+ Style/WordArray:
36
+ EnforcedStyle: percent
37
+
38
+ # Require class/module documentation (except for internal modules)
39
+ Style/Documentation:
40
+ Enabled: true
41
+ AllowedConstants:
42
+ - ClassMethods # Internal mixin modules don't need separate docs
43
+ - DSL # DSL namespace modules documented at parent level
44
+ Exclude:
45
+ - "lib/archema/schema/differ.rb" # Operation classes are internal data structures
46
+ - "lib/archema/shared_types.rb" # Module reopening, documented in main file
47
+ - "lib/archema/stores.rb" # Module reopening, documented in main file
48
+ - "test/**/*" # Test classes don't need documentation
49
+
50
+ # Allow multi-line block chains when readable
51
+ Style/MultilineBlockChain:
52
+ Enabled: false
53
+
54
+ # DANGEROUS: Autocorrect converts !!value to !value.nil? which has
55
+ # different semantics for boolean values (!!false → false, !false.nil? → true)
56
+ Style/DoubleNegation:
57
+ Enabled: false
58
+
59
+ # DANGEROUS: Changes semantics with mixed key types.
60
+ # reject { |k, _| keys.include?(k) } handles symbol/string keys correctly,
61
+ # but hash.except(*keys) treats :key and "key" as different.
62
+ Style/HashExcept:
63
+ Enabled: false
64
+
65
+ # DANGEROUS: Converts `Module % [args]` to `format(Module, args)` which breaks
66
+ # custom % methods (like ANSI % ["text", :style] for nested color spans).
67
+ Style/FormatString:
68
+ Enabled: false
69
+
70
+ # ─────────────────────────────────────────────────────────────────────────────
71
+ # Layout
72
+ # ─────────────────────────────────────────────────────────────────────────────
73
+
74
+ Layout/LineLength:
75
+ Max: 140
76
+
77
+ # Allow RBS inline type comments like #: Type
78
+ Layout/LeadingCommentSpace:
79
+ AllowRBSInlineAnnotation: true
80
+
81
+ # ─────────────────────────────────────────────────────────────────────────────
82
+ # Naming
83
+ # ─────────────────────────────────────────────────────────────────────────────
84
+
85
+ # DISABLED: Renames @data_layer_instance to @data_layer, but tests
86
+ # use instance_variable_set(:@data_layer_instance, nil) to reset state.
87
+ Naming/MemoizedInstanceVariableName:
88
+ Enabled: false
89
+
90
+ # DISABLED: Too pedantic about underscores in numbers (last_5, chain_from_11, 1_0_0)
91
+ Naming/VariableNumber:
92
+ Enabled: false
93
+
94
+ # Allow common short parameter names
95
+ Naming/MethodParameterName:
96
+ AllowedNames:
97
+ - a
98
+ - b
99
+ - c
100
+ - e # exception, element, entry
101
+ - i # index
102
+ - j # secondary index
103
+ - k # key
104
+ - v # value
105
+ - h # hash
106
+ - n # number, count
107
+ - x # coordinate, generic
108
+ - y # coordinate
109
+ - id # identifier
110
+ - io # IO object
111
+ - op # operation
112
+ - db # database
113
+ - fn # function
114
+ - s1 # string comparison (levenshtein)
115
+ - s2 # string comparison (levenshtein)
116
+ - sc # sub_constraint in composition constraints
117
+ - ds # dataset (Sequel)
118
+ - to # destination (from:, to:)
119
+ - of # element type (array of:)
120
+ - _ # explicitly unused
121
+
122
+ # Allow has_* prefix for methods that genuinely "have" something (not just predicates)
123
+ # has_default?, has_role?, etc. are clearer than default?, role? in context
124
+ Naming/PredicatePrefix:
125
+ AllowedMethods:
126
+ - has_default?
127
+ - has_role?
128
+ - has_managed_data?
129
+ - has_cardinality_constraints?
130
+ - has_temporal_defaults?
131
+ - has_irreversible_operations?
132
+ - has_one # DSL method: has_one :profile, Profile
133
+ - has_many # DSL method: has_many :posts, Post
134
+
135
+ # ─────────────────────────────────────────────────────────────────────────────
136
+ # Lint
137
+ # ─────────────────────────────────────────────────────────────────────────────
138
+
139
+ Lint/UselessAssignment:
140
+ Enabled: true
141
+
142
+ # DISABLED: Forces combining case branches that happen to have the same result,
143
+ # but often they represent distinct semantic intents. With tablecop alignment,
144
+ # separate branches read as visual lookup tables which is clearer.
145
+ Lint/DuplicateBranch:
146
+ Enabled: false
147
+
148
+ Lint/UnusedMethodArgument:
149
+ AllowUnusedKeywordArguments: true
150
+ IgnoreEmptyMethods: true
151
+
152
+ Lint/MissingSuper:
153
+ Enabled: false
154
+
155
+ # DANGEROUS: Removes `require "set"` as "redundant" in Ruby 3.2+, but code
156
+ # using Set.new will fail if Set isn't explicitly required.
157
+ Lint/RedundantRequireStatement:
158
+ Enabled: false
159
+
160
+ # ─────────────────────────────────────────────────────────────────────────────
161
+ # Metrics - Lenient thresholds for real-world code
162
+ # ─────────────────────────────────────────────────────────────────────────────
163
+
164
+ Metrics/MethodLength:
165
+ Max: 40
166
+ CountAsOne:
167
+ - array
168
+ - hash
169
+ - heredoc
170
+ Exclude:
171
+ - "lib/chiridion/engine/inline_rbs_loader.rb"
172
+
173
+ Metrics/AbcSize:
174
+ Max: 50
175
+ Exclude:
176
+ - "lib/chiridion/engine/inline_rbs_loader.rb"
177
+
178
+ Metrics/CyclomaticComplexity:
179
+ Max: 20
180
+
181
+ Metrics/PerceivedComplexity:
182
+ Max: 15
183
+ Exclude:
184
+ - "lib/chiridion/engine/inline_rbs_loader.rb"
185
+
186
+ Metrics/ClassLength:
187
+ Max: 300
188
+ CountAsOne:
189
+ - array
190
+ - hash
191
+ - heredoc
192
+
193
+ Metrics/BlockLength:
194
+ Max: 40
195
+ CountAsOne:
196
+ - array
197
+ - hash
198
+ - heredoc
199
+ Exclude:
200
+ - "test/**/*"
201
+ - "spec/**/*"
202
+ - "features/**/*"
203
+ - "lib/chiridion/engine/inline_rbs_loader.rb"
204
+ AllowedMethods:
205
+ - describe
206
+ - context
207
+ - define
208
+ - configure
209
+ - namespace
210
+
211
+ Metrics/ParameterLists:
212
+ Max: 15
213
+
214
+ # ─────────────────────────────────────────────────────────────────────────────
215
+ # Minitest
216
+ # ─────────────────────────────────────────────────────────────────────────────
217
+
218
+ Minitest/EmptyLineBeforeAssertionMethods:
219
+ Enabled: false
220
+
221
+ Minitest/MultipleAssertions:
222
+ Max: 8
223
+
224
+ # DANGEROUS: Converts `assert obj.foo?` to `assert_predicate obj, :foo?`
225
+ # which breaks refinements since assert_predicate uses send() internally
226
+ # and refinements are lexically scoped (don't work with send).
227
+ Minitest/AssertPredicate:
228
+ Enabled: false
229
+
230
+ Minitest/RefutePredicate:
231
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,97 @@
1
+ # Changelog
2
+
3
+ All notable changes to devex will be documented in this file.
4
+
5
+ ## [0.3.5] - 2025-12-29
6
+
7
+ ### Fixed
8
+ - **Version detection fallback**: `dx version` now searches effective working directory when no project markers found, handling pre-git-init projects
9
+ - **`--dx-from-dir` flag**: Now correctly affects project root discovery (was being ignored)
10
+ - **Subtool helper methods**: Nested tools (`tool "name" do...end`) can now access helper methods defined in parent file (fixes `dx version bump` which was broken)
11
+
12
+ ## [0.3.4] - 2025-12-13
13
+
14
+ ### Added
15
+ - `prj.linter` convention in ProjectPaths - finds .standard.yml or .rubocop.yml
16
+ - `Path#rm`, `Path#delete`, `Path#unlink` - delete a file
17
+ - `Path#rm_rf` - delete directory recursively
18
+
19
+ ### Changed
20
+ - Built-ins now fully trust ProjectPaths fail-fast behavior - no more rescue/re-error patterns
21
+ - `dx lint` and `dx format` use `prj.linter` instead of manual detection
22
+ - `dx test` uses `prj.test`, `dx gem` uses `prj.gemspec` - all with clean fail-fast
23
+
24
+ ## [0.3.3] - 2025-12-13
25
+
26
+ ### Fixed
27
+ - **Built-in tools now run from project root** - `dx test`, `dx lint`, `dx format`, `dx gem` now properly discover project root via `Devex::Dirs.project_dir` and run commands from there, regardless of current directory
28
+ - Built-ins now use `ProjectPaths` (`prj.test`, `prj.gemspec`, etc.) for conventional path discovery instead of manual File.exist? checks
29
+
30
+ ## [0.3.2] - 2025-12-13
31
+
32
+ ### Fixed
33
+ - ExecutionContext now includes Devex::Exec automatically, so nested tools (inside `tool "name" do ... end` blocks) have access to `cmd`, `capture`, etc. without explicit include
34
+ - Moved exec.rb require before tool.rb to ensure proper load order
35
+
36
+ ## [0.3.1] - 2025-12-13
37
+
38
+ ### Added
39
+ - `--no-verbose` flag to reset verbosity level (useful in subtools)
40
+ - `--no-quiet` flag to unset quiet mode
41
+ - `default:` option for flags to specify non-nil defaults
42
+
43
+ ### Changed
44
+ - Built-in tools (`test`, `lint`, `format`, `gem`) now use `Devex::Exec` module with `cmd` pattern
45
+ - `lint.rb` uses `capture()` with `.stdout_lines` for git diff (demonstrates capture API)
46
+ - `gem.rb` uses `.then { }` chaining for sequential build/install operations
47
+ - Documentation updated with comprehensive Exec examples showing result objects, chaining, capture
48
+
49
+ ### Fixed
50
+ - Documentation examples now consistently use `cmd`/`cmd?` inside `def run` blocks
51
+
52
+ ## [0.3.0] - 2025-12-13
53
+
54
+ ### Added
55
+ - **Built-in `dx test`**: Auto-detects minitest or RSpec, runs with coverage flag
56
+ - **Built-in `dx lint`**: Auto-detects RuboCop or StandardRB, with `--fix` and `--diff` options
57
+ - **Built-in `dx format`**: Auto-formats code (equivalent to `dx lint --fix`)
58
+ - **Built-in `dx gem`**: Subcommands for `build`, `install`, and `clean`
59
+ - **`cmd` and `cmd?` aliases**: Use these in tools to avoid `def run` collision with `Devex::Exec.run`
60
+ - **Reserved flag validation**: Tools that define flags conflicting with global flags (`-v`, `-f`, `-q`, etc.) now fail fast with a helpful error message
61
+
62
+ ### Fixed
63
+ - Boolean flags now default to `false` instead of `nil`, fixing method access in tools
64
+ - Fixed missing requires for `Devex::Exec`, `Devex::ProjectPaths`, and `Devex::WorkingDir` modules
65
+ - Tools can now use `run`, `capture`, `spawn`, and other Exec methods without errors
66
+
67
+ ## [0.2.0] - 2025-12-13
68
+
69
+ ### Added
70
+ - **Environment wrapper chain**: `[dotenv] [mise exec --] [bundle exec] command`
71
+ - `mise` auto-detected from `.mise.toml` or `.tool-versions`
72
+ - `bundle exec` auto-detected from `Gemfile` for gem commands
73
+ - `dotenv` requires explicit opt-in (`dotenv: true`)
74
+ - **Auto-require for project lib/**: Tools can `require "myproject/foo"` without `require_relative`
75
+ - **String case transforms**: `snake_case`, `kebab_case`, `camel_case`, `pascal_case`, `title_case`, `scream_case`, `up_case`, `down_case` with aliases
76
+ - `--dx-from-dir` flag for operating on projects remotely
77
+ - `.dx-use-local` delegation for version consistency
78
+ - `prj.hooks` and `prj.templates` path conventions
79
+
80
+ ### Changed
81
+ - ADRs updated from Draft to Accepted status
82
+
83
+ ## [0.1.0] - 2025-12-13
84
+
85
+ ### Added
86
+ - Initial release
87
+ - CLI framework with tool routing and nested subcommands
88
+ - Help system (`dx help`, `dx tool --help`, `dx tool help`)
89
+ - DSL for defining tools: `desc`, `long_desc`, `flag`, `required_arg`, `optional_arg`, `remaining_args`, `tool`
90
+ - Agent mode detection (non-tty, CI, explicit env var)
91
+ - Environment detection (development, test, staging, production)
92
+ - Output helpers with color support
93
+ - Built-in `dx version` command with bump support
94
+ - Support library: Path class, ANSI colors, core extensions
95
+ - Directory context: `Dirs`, `ProjectPaths`, `WorkingDir`
96
+ - Command execution: `run`, `capture`, `spawn`, `shell`, `ruby`, `tool`
97
+ - Result monad with `then`, `map`, `exit_on_failure!`
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Joseph A Wecker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,314 @@
1
+ ![Devex](devex-logo.jpg)
2
+
3
+ # Devex
4
+
5
+ A lightweight, zero-heavy-dependency Ruby CLI providing a unified `dx` command for common development tasks. Projects can extend with local tasks. Clean-room implementation inspired by toys-core patterns.
6
+
7
+ ## Vision
8
+
9
+ - **Single entry point**: `dx` command for all dev tasks
10
+ - **Zero-dependency core**: Support library uses only Ruby stdlib
11
+ - **Project-local tools**: Override or extend built-ins with `tools/*.rb`
12
+ - **Agent-aware**: Automatically detects AI agent invocation and adapts output
13
+ - **Environment-aware**: Rails-style environment detection (dev/test/staging/prod)
14
+ - **Command execution**: Clean subprocess management with environment orchestration
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ gem install devex
20
+ ```
21
+
22
+ Or add to your Gemfile:
23
+
24
+ ```ruby
25
+ gem "devex"
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ```bash
31
+ # Show available commands
32
+ dx help
33
+
34
+ # Show/manage version
35
+ dx version
36
+ dx version bump patch
37
+ dx version set 2.0.0
38
+
39
+ # With JSON output (auto-detected in agent mode)
40
+ dx version --format=json
41
+ ```
42
+
43
+ ## Project-Local Tools
44
+
45
+ Create a `tools/` directory in your project root with Ruby files:
46
+
47
+ ```ruby
48
+ # tools/build.rb
49
+ desc "Build and test the project"
50
+
51
+ long_desc <<~DESC
52
+ Runs the full build pipeline: install dependencies, run tests,
53
+ and optionally lint the code.
54
+ DESC
55
+
56
+ flag :skip_lint, "-s", "--skip-lint", desc: "Skip linting step"
57
+ flag :coverage, "-c", "--coverage", desc: "Run with code coverage"
58
+
59
+ include Devex::Exec
60
+
61
+ def run
62
+ # Use `cmd` instead of `run` to avoid conflict with `def run`
63
+ cmd("bundle", "install").exit_on_failure!
64
+
65
+ # Access flags as methods (boolean flags default to false)
66
+ env = coverage ? { "COVERAGE" => "1" } : {}
67
+ cmd("bundle", "exec", "rake", "test", env: env).exit_on_failure!
68
+
69
+ unless skip_lint
70
+ cmd("bundle", "exec", "rubocop").exit_on_failure!
71
+ end
72
+
73
+ # Check global verbose flag
74
+ puts "Build complete!" if verbose?
75
+ end
76
+ ```
77
+
78
+ Then run:
79
+ ```bash
80
+ dx build # Full build
81
+ dx build --skip-lint # Skip linting
82
+ dx build --coverage # With coverage
83
+ dx -v build # Verbose output
84
+ ```
85
+
86
+ ### Nested Tools
87
+
88
+ ```ruby
89
+ # tools/db.rb
90
+ desc "Database operations"
91
+
92
+ tool "migrate" do
93
+ desc "Run migrations"
94
+ def run
95
+ # ...
96
+ end
97
+ end
98
+
99
+ tool "seed" do
100
+ desc "Seed the database"
101
+ def run
102
+ # ...
103
+ end
104
+ end
105
+ ```
106
+
107
+ Access as: `dx db migrate`, `dx db seed`
108
+
109
+ ## Command Execution
110
+
111
+ Tools have access to smart command execution that automatically handles your environment.
112
+ Use `cmd` (not `run`) inside tools to avoid shadowing the `def run` entry point:
113
+
114
+ ```ruby
115
+ # tools/deploy.rb
116
+ desc "Deploy the application"
117
+
118
+ flag :skip_tests, "--skip-tests", desc: "Skip test suite"
119
+ flag :dry_run, "-n", "--dry-run", desc: "Show what would be done"
120
+
121
+ include Devex::Exec
122
+
123
+ def run
124
+ # ─── Basic execution with exit_on_failure! ───
125
+ cmd("bundle", "install").exit_on_failure!
126
+
127
+ # ─── Boolean check: cmd? returns true/false ───
128
+ unless skip_tests || cmd?("which", "rspec")
129
+ cmd("rake", "test").exit_on_failure!
130
+ end
131
+
132
+ # ─── Capture output into result object ───
133
+ result = capture("git", "rev-parse", "--short", "HEAD")
134
+ commit = result.stdout.strip
135
+ puts "Deploying commit #{commit}..."
136
+
137
+ # ─── Chain sequential operations with .then ───
138
+ cmd("docker", "build", "-t", "myapp:#{commit}", ".")
139
+ .then { cmd("docker", "push", "myapp:#{commit}") }
140
+ .exit_on_failure!
141
+
142
+ # ─── Transform captured output with .map ───
143
+ tag = capture("git", "describe", "--tags", "--abbrev=0")
144
+ .map { |stdout| stdout.strip }
145
+
146
+ # ─── Result object has rich info ───
147
+ result = cmd("kubectl", "apply", "-f", "k8s/")
148
+ if result.failed?
149
+ $stderr.puts "Deploy failed (exit #{result.exit_code})"
150
+ $stderr.puts result.stderr if result.stderr
151
+ exit 1
152
+ end
153
+
154
+ puts "Deployed #{tag || commit} successfully!" if verbose?
155
+ end
156
+ ```
157
+
158
+ ### Execution Methods
159
+
160
+ | Method | Purpose | Returns |
161
+ |--------|---------|---------|
162
+ | `cmd(*args)` | Run command, stream output | `Result` |
163
+ | `cmd?(*args)` | Test if command succeeds | `Boolean` |
164
+ | `capture(*args)` | Run command, capture output | `Result` with `.stdout`, `.stderr` |
165
+ | `shell(string)` | Run shell command (pipes, globs) | `Result` |
166
+ | `shell?(string)` | Test if shell command succeeds | `Boolean` |
167
+ | `spawn(*args)` | Start background process | `Controller` |
168
+
169
+ ### Result Object
170
+
171
+ ```ruby
172
+ result = cmd("make", "test")
173
+
174
+ result.success? # => true if exit code is 0
175
+ result.failed? # => true if non-zero exit
176
+ result.exit_code # => Integer exit code
177
+ result.stdout # => captured stdout (if using capture)
178
+ result.stderr # => captured stderr
179
+ result.stdout_lines # => stdout split into lines
180
+ result.duration # => execution time in seconds
181
+
182
+ # Chaining
183
+ result.exit_on_failure! # Exit process if failed
184
+ result.then { cmd("next") } # Chain if successful
185
+ result.map { |out| out.strip } # Transform stdout
186
+ ```
187
+
188
+ ### Environment Wrappers
189
+
190
+ Commands are automatically wrapped based on your project:
191
+
192
+ | Wrapper | When Applied |
193
+ |---------|--------------|
194
+ | `mise exec --` | Auto if `.mise.toml` or `.tool-versions` exists |
195
+ | `bundle exec` | Auto if `Gemfile` exists and command looks like a gem |
196
+ | `dotenv` | Explicit opt-in only (`dotenv: true`) |
197
+
198
+ Control wrappers explicitly:
199
+
200
+ ```ruby
201
+ cmd "rspec" # auto-detect mise + bundle
202
+ cmd "rspec", mise: false # skip mise wrapping
203
+ cmd "rspec", bundle: false # skip bundle exec
204
+ cmd "echo", "hi", raw: true # skip all wrappers
205
+ cmd "rails", "s", dotenv: true # enable dotenv loading
206
+ ```
207
+
208
+ ## Configuration
209
+
210
+ Create `.dx.yml` in your project root:
211
+
212
+ ```yaml
213
+ # Custom tools directory (default: tools)
214
+ tools_dir: dev/tools
215
+ ```
216
+
217
+ ## Global Options
218
+
219
+ ```bash
220
+ dx --help # Show help with global options
221
+ dx --dx-version # Show devex gem version
222
+ dx -f json version # Output in JSON format
223
+ dx --format=yaml version # Output in YAML format
224
+ dx -v version # Verbose mode
225
+ dx -q version # Quiet mode
226
+ dx --no-color version # Disable colors
227
+ dx --color=always version # Force colors
228
+ ```
229
+
230
+ ## Environment Variables
231
+
232
+ - `DX_ENV` / `DEVEX_ENV` - Set environment (development, test, staging, production)
233
+ - `DX_AGENT_MODE=1` - Force agent mode (structured output, no colors)
234
+ - `DX_INTERACTIVE=1` - Force interactive mode
235
+ - `NO_COLOR=1` - Disable colored output
236
+ - `FORCE_COLOR=1` - Force colored output
237
+
238
+ ## Debug Flags
239
+
240
+ Hidden flags for testing and bug reproduction (not shown in `--help`):
241
+
242
+ ```bash
243
+ dx --dx-agent-mode version # Force agent mode
244
+ dx --dx-no-agent-mode version # Force interactive mode
245
+ dx --dx-env=production version # Force environment
246
+ dx --dx-terminal version # Force terminal detection
247
+ ```
248
+
249
+ ## Built-in Commands
250
+
251
+ **Testing & Quality**
252
+ - `dx test` - Run tests (auto-detects minitest/RSpec)
253
+ - `dx lint` - Run linter (auto-detects RuboCop/StandardRB)
254
+ - `dx lint --fix` - Auto-fix linter issues
255
+ - `dx format` - Auto-format code (alias for `dx lint --fix`)
256
+
257
+ **Version Management**
258
+ - `dx version` - Show project version
259
+ - `dx version bump <major|minor|patch>` - Bump semantic version
260
+ - `dx version set <version>` - Set explicit version
261
+
262
+ **Gem Packaging**
263
+ - `dx gem build` - Build the gem
264
+ - `dx gem install` - Build and install locally
265
+ - `dx gem clean` - Remove built gem files
266
+
267
+ More built-ins planned: `types`, `pre-commit`, `init`
268
+
269
+ ## Building Custom CLIs with Devex::Core
270
+
271
+ Devex exposes its CLI framework for building your own command-line tools:
272
+
273
+ ```ruby
274
+ require "devex/core"
275
+
276
+ config = Devex::Core::Configuration.new(
277
+ executable_name: "mycli",
278
+ flag_prefix: "mycli", # --mycli-version, --mycli-agent-mode
279
+ project_markers: %w[.mycli.yml .git Gemfile],
280
+ env_prefix: "MYCLI" # MYCLI_AGENT_MODE, MYCLI_ENV
281
+ )
282
+
283
+ cli = Devex::Core::CLI.new(config: config)
284
+ cli.load_tools("/path/to/tools")
285
+ exit cli.run(ARGV)
286
+ ```
287
+
288
+ This gives you:
289
+ - Tool routing with nested subcommands
290
+ - Automatic help generation
291
+ - Agent mode detection (adapts output for AI agents)
292
+ - Environment detection (dev/test/staging/prod)
293
+ - Command execution with environment wrappers
294
+ - Project path conventions
295
+ - Zero-dependency support library (Path, ANSI, CoreExt)
296
+
297
+ See [docs/developing-tools.md](docs/developing-tools.md) for the full API.
298
+
299
+ ## Development
300
+
301
+ ```bash
302
+ bundle install
303
+ bundle exec rake test
304
+ bundle exec exe/dx --help
305
+ ```
306
+
307
+ ## Documentation
308
+
309
+ - **[Developing Tools](docs/developing-tools.md)** - How to create tools, available interfaces, best practices
310
+ - **[CHANGELOG](CHANGELOG.md)** - Version history and release notes
311
+
312
+ ## License
313
+
314
+ MIT - see [LICENSE](LICENSE)
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ t.warning = false
11
+ end
12
+
13
+ task default: :test
data/devex-logo.jpg ADDED
Binary file