asgard 0.2.0 → 0.3.1

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.
data/docs/dependencies.md CHANGED
@@ -26,15 +26,15 @@ Bare symbols run one after another in the order declared:
26
26
 
27
27
  ```ruby
28
28
  class Tasks
29
- desc "build", "Compile the project"
29
+ desc "Compile the project"
30
30
  def build = sh "rake build"
31
31
 
32
32
  depends_on :build
33
- desc "test", "Run the test suite"
33
+ desc "Run the test suite"
34
34
  def test = sh "rake test"
35
35
 
36
36
  depends_on :test
37
- desc "release", "Publish the gem"
37
+ desc "Publish the gem"
38
38
  def release = sh "bundle exec rake release"
39
39
  end
40
40
  ```
@@ -47,7 +47,7 @@ Multiple sequential dependencies in a single `depends_on` call run left to right
47
47
 
48
48
  ```ruby
49
49
  depends_on :clean, :build, :test
50
- desc "package", "Clean, build, and test"
50
+ desc "Clean, build, and test"
51
51
  def package = sh "rake package"
52
52
  ```
53
53
 
@@ -59,14 +59,14 @@ Wrap symbols in an array to declare they can run concurrently. Asgard waits for
59
59
 
60
60
  ```ruby
61
61
  class Tasks
62
- desc "lint", "Check code style"
62
+ desc "Check code style"
63
63
  def lint = sh "bundle exec rubocop"
64
64
 
65
- desc "typecheck", "Run type checks"
65
+ desc "Run type checks"
66
66
  def typecheck = sh "bundle exec srb tc"
67
67
 
68
68
  depends_on [:lint, :typecheck]
69
- desc "test", "Run tests (after lint and typecheck)"
69
+ desc "Run tests (after lint and typecheck)"
70
70
  def test = sh "bundle exec rake test"
71
71
  end
72
72
  ```
@@ -85,16 +85,16 @@ Mix bare symbols and arrays in a single `depends_on` call. Execution proceeds st
85
85
 
86
86
  ```ruby
87
87
  class Tasks
88
- desc "setup", "Install dependencies"; def setup = sh "bundle install"
89
- desc "lint", "Check code style"; def lint = sh "bundle exec rubocop"
90
- desc "build", "Compile assets"; def build = sh "rake assets:precompile"
91
- desc "test", "Run tests"; def test = sh "bundle exec rake test"
92
- desc "notify", "Post to Slack"; def notify = sh "curl $SLACK_WEBHOOK -d '{\"text\":\"done\"}'"
88
+ desc "Install dependencies"; def setup = sh "bundle install"
89
+ desc "Check code style"; def lint = sh "bundle exec rubocop"
90
+ desc "Compile assets"; def build = sh "rake assets:precompile"
91
+ desc "Run tests"; def test = sh "bundle exec rake test"
92
+ desc "Post to Slack"; def notify = sh "curl $SLACK_WEBHOOK -d '{\"text\":\"done\"}'"
93
93
 
94
94
  # setup first, then lint+build in parallel, then test, then notify
95
95
  depends_on :setup, [:lint, :build], :test, :notify
96
- desc "ci", "Full CI pipeline"
97
- def ci = sh "echo 'CI complete'"
96
+ desc "Full CI pipeline"
97
+ def ci = puts "CI complete"
98
98
  end
99
99
  ```
100
100
 
@@ -124,19 +124,19 @@ Each task runs at most once per `asgard` invocation. If multiple tasks declare t
124
124
 
125
125
  ```ruby
126
126
  class Tasks
127
- desc "setup", "Install gems"
127
+ desc "Install gems"
128
128
  def setup = sh "bundle install"
129
129
 
130
130
  depends_on :setup
131
- desc "test", "Run tests"
131
+ desc "Run tests"
132
132
  def test = sh "rake test"
133
133
 
134
134
  depends_on :setup
135
- desc "lint", "Check style"
135
+ desc "Check style"
136
136
  def lint = sh "rubocop"
137
137
 
138
138
  depends_on [:test, :lint]
139
- desc "ci", "Test and lint (setup runs once)"
139
+ desc "Test and lint (setup runs once)"
140
140
  def ci = puts "done"
141
141
  end
142
142
  ```
@@ -152,10 +152,10 @@ Asgard validates the full dependency graph using [Dagwood](https://rubygems.org/
152
152
  ```ruby
153
153
  class Tasks
154
154
  depends_on :b
155
- desc "a", "Task A"; def a = puts "a"
155
+ desc "Task A"; def a = puts "a"
156
156
 
157
157
  depends_on :a
158
- desc "b", "Task B"; def b = puts "b"
158
+ desc "Task B"; def b = puts "b"
159
159
  end
160
160
  ```
161
161
 
@@ -175,14 +175,14 @@ No backtrace is shown — just a single diagnostic line.
175
175
  ```ruby
176
176
  # build.loki
177
177
  class Tasks
178
- desc "build", "Compile"
178
+ desc "Compile"
179
179
  def build = sh "rake build"
180
180
  end
181
181
 
182
182
  # test.loki
183
183
  class Tasks
184
184
  depends_on :build # build.loki must be loaded first
185
- desc "test", "Test"
185
+ desc "Test"
186
186
  def test = sh "rake test"
187
187
  end
188
188
  ```
@@ -197,14 +197,14 @@ When `--auto-load` is used, `*.loki` files are loaded alphabetically, so `build.
197
197
 
198
198
  ```ruby
199
199
  class DBCommands < Tasks
200
- desc "migrate", "Run migrations"
200
+ desc "Run migrations"
201
201
  def migrate = sh "rails db:migrate"
202
202
 
203
- desc "seed", "Load seed data"
203
+ desc "Load seed data"
204
204
  def seed = sh "rails db:seed"
205
205
 
206
206
  depends_on :migrate, :seed
207
- desc "reset", "Migrate then seed"
207
+ desc "Migrate then seed"
208
208
  def reset = puts "Done."
209
209
  end
210
210
 
data/docs/environment.md CHANGED
@@ -12,8 +12,8 @@ Call `dotenv` inside the class body (not inside a task method) to load the defau
12
12
  class Tasks
13
13
  dotenv # loads .env from the current working directory
14
14
 
15
- desc "check", "Print the app name from .env"
16
- def check = sh "echo $APP_NAME"
15
+ desc "Print the app name from .env"
16
+ def check = puts env(:app_name)
17
17
  end
18
18
  ```
19
19
 
@@ -46,17 +46,14 @@ end
46
46
  `dotenv` is a **class-level** call — it executes at Ruby class-load time, not when a task is invoked. This means:
47
47
 
48
48
  1. Variables are available in `ENV` before any task method runs.
49
- 2. They are also available to lambda variables declared with `var` that reference `ENV`.
50
- 3. They are available during `depends_on` dependency resolution.
49
+ 2. They are available during `depends_on` dependency resolution.
51
50
 
52
51
  ```ruby
53
52
  class Tasks
54
53
  dotenv
55
54
 
56
- var :database_url, -> { ENV.fetch("DATABASE_URL") }
57
-
58
- desc "migrate", "Run migrations"
59
- def migrate = sh "DATABASE_URL=#{database_url} rails db:migrate"
55
+ desc "Run migrations"
56
+ def migrate = sh "DATABASE_URL=#{env(:database_url)} rails db:migrate"
60
57
  end
61
58
  ```
62
59
 
@@ -79,20 +76,39 @@ This makes it safe to commit a `.env.local` line to your `.loki` without requiri
79
76
 
80
77
  ---
81
78
 
82
- ## Environment Variables vs. `var`
79
+ ## Environment Variables vs. Class Variables
83
80
 
84
- Use `dotenv` to bring external configuration into `ENV`, and `var` to define task-internal computed values:
81
+ Use `dotenv` to bring external configuration into `ENV`. Use `@@` class variables for fixed values declared in the task file, and read from `ENV` directly in task bodies or helper methods when the value comes from the environment:
85
82
 
86
83
  ```ruby
87
84
  class Tasks
88
85
  dotenv
89
86
 
90
- # Read from ENV (set by dotenv or the shell)
91
- var :app_name, -> { ENV.fetch("APP_NAME", "myapp") }
92
- var :port, -> { ENV.fetch("PORT", "3000").to_i }
87
+ @@app_name ||= "myapp".freeze
88
+
89
+ desc "Start the server"
90
+ def start
91
+ port = ENV.fetch("PORT", "3000").to_i
92
+ sh "puma -b tcp://0.0.0.0:#{port} -w #{ENV.fetch('WORKERS', '2')}"
93
+ end
94
+ end
95
+ ```
96
+
97
+ For `ENV` values used in multiple tasks, define a private helper method:
98
+
99
+ ```ruby
100
+ class Tasks
101
+ dotenv
93
102
 
94
- desc "start", "Start the server"
103
+ desc "Start the server"
95
104
  def start = sh "puma -p #{port}"
105
+
106
+ desc "Show config"
107
+ def config = puts "#{@@app_name} on port #{port}"
108
+
109
+ private
110
+
111
+ def port = ENV.fetch("PORT", "3000").to_i
96
112
  end
97
113
  ```
98
114
 
data/docs/examples.md CHANGED
@@ -27,7 +27,7 @@ Alternatively, copy individual example files into your own project's directory.
27
27
 
28
28
  The most comprehensive example — demonstrates every Thor DSL feature available in Asgard:
29
29
 
30
- - `var` with a static value and a lazy lambda
30
+ - `@@` class variables for shared configuration values
31
31
  - `dotenv` (commented out, ready to activate)
32
32
  - `class_option` with `:boolean` and `:string` types, including `enum`
33
33
  - `default_task` — sets the default command when `asgard` is run with no arguments
@@ -56,7 +56,7 @@ asgard pipeline
56
56
 
57
57
  ---
58
58
 
59
- ## `server_subcommands.loki`
59
+ ## Server Subcommands
60
60
 
61
61
  **Path:** `examples/server_subcommands.loki`
62
62
 
@@ -80,7 +80,7 @@ The `ServerCommands` class inherits from `Tasks`, giving it access to `sh`, `dep
80
80
 
81
81
  ---
82
82
 
83
- ## `db_subcommands.loki`
83
+ ## DB Subcommands
84
84
 
85
85
  **Path:** `examples/db_subcommands.loki`
86
86
 
@@ -50,8 +50,8 @@ Open `.loki` in your editor and add a task:
50
50
 
51
51
  ```ruby
52
52
  class Tasks
53
- desc "hello", "Say hello to the world"
54
- def hello = sh 'echo "Hello, World!"'
53
+ desc "Say hello to the world"
54
+ def hello = puts "Hello, World!"
55
55
  end
56
56
  ```
57
57
 
@@ -64,7 +64,6 @@ end
64
64
 
65
65
  ```bash
66
66
  asgard hello
67
- # echo "Hello, World!"
68
67
  # Hello, World!
69
68
  ```
70
69
 
@@ -90,7 +89,7 @@ Positional parameters are declared directly in the method signature. Document th
90
89
  class Tasks
91
90
  desc "greet NAME", "Greet someone by name"
92
91
  def greet(name = "World")
93
- sh "echo 'Hello, #{name}!'"
92
+ puts "Hello, #{name}!"
94
93
  end
95
94
  end
96
95
  ```
@@ -115,7 +114,7 @@ class Tasks
115
114
  option :shout, aliases: "-s", type: :boolean, desc: "Uppercase the greeting"
116
115
  def greet(name = "World")
117
116
  msg = options[:shout] ? "HELLO, #{name.upcase}!" : "Hello, #{name}!"
118
- sh "echo '#{msg}'"
117
+ puts msg
119
118
  end
120
119
  end
121
120
  ```
@@ -163,7 +162,7 @@ Inside a task body, use the `debug?` and `verbose?` predicates:
163
162
 
164
163
  ```ruby
165
164
  def hello
166
- sh "echo 'building...'"
165
+ puts "building..."
167
166
  sh "make --debug" if debug?
168
167
  end
169
168
  ```
data/docs/helpers.md CHANGED
@@ -10,13 +10,13 @@ Methods declared after `private` are callable from any task in the same class bu
10
10
 
11
11
  ```ruby
12
12
  class Tasks
13
- desc "build", "Compile and package"
13
+ desc "Compile and package"
14
14
  def build
15
15
  compile("src")
16
16
  package(app_version)
17
17
  end
18
18
 
19
- desc "release", "Build and publish to RubyGems"
19
+ desc "Build and publish to RubyGems"
20
20
  def release
21
21
  build
22
22
  sh "gem push pkg/myapp-#{app_version}.gem"
@@ -49,13 +49,13 @@ Thor's `no_commands` block marks public methods as excluded from CLI discovery.
49
49
 
50
50
  ```ruby
51
51
  class Tasks
52
- desc "build", "Compile the project"
52
+ desc "Compile the project"
53
53
  def build
54
54
  puts "Revision: #{current_sha}"
55
55
  sh "rake build"
56
56
  end
57
57
 
58
- desc "deploy", "Deploy to production"
58
+ desc "Deploy to production"
59
59
  def deploy
60
60
  puts "Deploying revision #{current_sha}..."
61
61
  sh "cap production deploy"
@@ -73,7 +73,89 @@ class Tasks
73
73
  end
74
74
  ```
75
75
 
76
- `var`-declared variables are also implemented using `no_commands` internally, which is why they appear as callable methods but not as CLI commands.
76
+ ---
77
+
78
+ ## The `helper` DSL Method
79
+
80
+ `helper` is an Asgard DSL method that defines a helper available in **both class context and instance context** with a single declaration. It is the right tool when a value needs to be used inside a `header` or `footer` call (which execute at class load time) and also inside task instance methods.
81
+
82
+ ### The problem it solves
83
+
84
+ Thor task methods run as instance methods. Class-level DSL calls like `header` and `footer` run as class methods. A plain `def` only creates an instance method, so it cannot be called inside `header`. Conversely, `def self.name` only creates a class method, so it cannot be called inside a task body without `self.class.name`.
85
+
86
+ The manual workaround is verbose:
87
+
88
+ ```ruby
89
+ class Tasks
90
+ @@project ||= "myapp".freeze
91
+
92
+ # class method for header/footer
93
+ def self.version
94
+ @@version ||= File.read("lib/myapp/version.rb").match(/VERSION\s*=\s*"([^"]+)"/)[1].freeze
95
+ end
96
+
97
+ # private instance method delegating to the class method
98
+ no_commands do
99
+ private def version = self.class.version
100
+ end
101
+
102
+ header "#{@@project} v#{version}"
103
+
104
+ desc "Show version"
105
+ def show_version = puts version
106
+ end
107
+ ```
108
+
109
+ `helper` replaces those six lines with one:
110
+
111
+ ```ruby
112
+ class Tasks
113
+ @@project ||= "myapp".freeze
114
+
115
+ helper(:version) {
116
+ @@version ||= File.read("lib/myapp/version.rb").match(/VERSION\s*=\s*"([^"]+)"/)[1].freeze
117
+ }
118
+
119
+ header "#{@@project} v#{version}"
120
+
121
+ desc "Show version"
122
+ def show_version = puts version
123
+ end
124
+ ```
125
+
126
+ ### Arguments
127
+
128
+ `helper` supports any argument signature valid in a Ruby method definition: positional, keyword, default values, and blocks.
129
+
130
+ ```ruby
131
+ # No arguments
132
+ helper(:project_root) { loki_up.parent.to_s }
133
+
134
+ # Positional arguments
135
+ helper(:gem_path) { |name| "lib/#{name}/version.rb" }
136
+
137
+ # Positional with default
138
+ helper(:tag_prefix) { |sep = "-"| "#{@@project}#{sep}" }
139
+
140
+ # Positional and keyword arguments
141
+ helper(:format_version) { |name, version, prefix: "v", separator: "-"|
142
+ "#{prefix}#{name}#{separator}#{version}"
143
+ }
144
+ ```
145
+
146
+ Wrong argument counts or unknown keyword names raise the same `ArgumentError` Ruby raises for any method call — no special error handling needed.
147
+
148
+ ### Visibility
149
+
150
+ `helper`-defined methods are:
151
+
152
+ - **Excluded from `asgard help`** — they never appear as commands
153
+ - **Blocked from CLI invocation** — cannot be called directly from the command line
154
+ - **Private on the instance side** — not accessible from outside the class
155
+
156
+ ### When to use `helper`
157
+
158
+ Use `helper` when the value or computation must be available in both a class-level DSL call (`header`, `footer`, a `@@var` initializer) and inside task instance methods. For helpers that are only needed inside task bodies, a plain `private` method is simpler.
77
159
 
78
160
  ---
79
161
 
@@ -93,6 +175,8 @@ For most helpers, `private` is the right choice. Use `no_commands` when the help
93
175
 
94
176
  ## Sharing Helpers Across Files
95
177
 
178
+ ### Within a project
179
+
96
180
  Extract shared helpers into a plain Ruby module and load it from `.loki` using `require_relative`:
97
181
 
98
182
  ```ruby
@@ -117,10 +201,10 @@ require_relative "shared/helpers"
117
201
  class Tasks
118
202
  include BuildHelpers
119
203
 
120
- desc "build", "Compile the project"
204
+ desc "Compile the project"
121
205
  def build = compile("src")
122
206
 
123
- desc "package", "Create distribution archive"
207
+ desc "Create distribution archive"
124
208
  def package = sh "tar czf #{dist_path(app_version)} bin/"
125
209
  end
126
210
  ```
@@ -130,6 +214,30 @@ Because `include` in the class body makes the module methods available as instan
130
214
  !!! tip
131
215
  Helpers in a shared module can call `sh`, `shebang`, and other Asgard DSL methods because those are included in `Tasks` (via `Asgard::Base` and `Asgard::Shell`) and are available in `self` when the module method is invoked.
132
216
 
217
+ ### Across projects with `import_up`
218
+
219
+ If helpers are defined as tasks in a shared `.loki` file, use `import_up` to load them from any sub-project without knowing the absolute path:
220
+
221
+ ```
222
+ ~/sandbox/
223
+ shared_helpers.loki ← defines helper tasks available to all sub-projects
224
+ projectA/
225
+ .loki
226
+ projectB/
227
+ .loki
228
+ ```
229
+
230
+ ```ruby
231
+ # projectA/.loki (and identically in projectB/.loki)
232
+ import_up "shared_helpers.loki"
233
+
234
+ class Tasks
235
+ # shared tasks are already defined in Tasks by here
236
+ end
237
+ ```
238
+
239
+ `import_up` walks up from `Dir.pwd` until it finds `shared_helpers.loki`, then loads it. It returns `false` without raising if the file is not found, making it safe to use in projects where the shared file may not always be present.
240
+
133
241
  ---
134
242
 
135
243
  ## Helper Methods in Subcommands
@@ -138,10 +246,10 @@ Subcommand classes that inherit from `Tasks` also inherit all private helpers an
138
246
 
139
247
  ```ruby
140
248
  class DeployCommands < Tasks
141
- desc "staging", "Deploy to staging"
249
+ desc "Deploy to staging"
142
250
  def staging = deploy_to("staging")
143
251
 
144
- desc "production", "Deploy to production"
252
+ desc "Deploy to production"
145
253
  def production = deploy_to("production")
146
254
 
147
255
  private
data/docs/index.md CHANGED
@@ -13,11 +13,11 @@
13
13
  <li><strong>Task Dependencies</strong> — sequential, parallel, and mixed dependency graphs via <code>depends_on</code></li>
14
14
  <li><strong>Concurrent Execution</strong> — parallel task groups run in native Ruby threads</li>
15
15
  <li><strong>Subcommands</strong> — group related tasks under a named namespace</li>
16
- <li><strong>Variables</strong> — static values and lazy-evaluated lambdas via <code>var</code></li>
16
+ <li><strong>Variables</strong> — shared configuration via Ruby class variables (<code>@@name</code>), visible across all tasks and subcommands</li>
17
17
  <li><strong>Shell Helpers</strong> — <code>sh</code> for any shell command or heredoc; <code>shebang</code> for polyglot scripts</li>
18
18
  <li><strong>Dotenv Support</strong> — load <code>.env</code> files into the environment with <code>dotenv</code></li>
19
19
  <li><strong>Auto-Discovery</strong> — <code>.loki</code> root marker searched from CWD upward through parent directories</li>
20
- <li><strong>Multi-File Tasks</strong> — split tasks across <code>*.loki</code> files, loaded on demand with <code>--auto-load</code></li>
20
+ <li><strong>Multi-File Tasks</strong> — split tasks across <code>*.loki</code> files loaded via <code>import</code></li>
21
21
  <li><strong>Built-in Flags</strong> — <code>--version</code>, <code>--debug</code>, and <code>--verbose</code> available on every task</li>
22
22
  </ul>
23
23
  </td>
@@ -40,8 +40,8 @@ touch .loki
40
40
  # Add your first task
41
41
  cat >> .loki << 'EOF'
42
42
  class Tasks
43
- desc "hello", "Say hello"
44
- def hello = sh 'echo "Hello from Asgard!"'
43
+ desc "Say hello"
44
+ def hello = puts "Hello from Asgard!"
45
45
  end
46
46
  EOF
47
47
 
@@ -53,9 +53,9 @@ asgard hello
53
53
 
54
54
  ## How It Works
55
55
 
56
- Asgard searches upward from your current directory for a `.loki` file. That file marks the project root. Additional `*.loki` files in the same directory can be loaded by passing `--auto-load` to the `asgard` command. All task files reopen `class Tasks`, which is pre-defined by the gem as a subclass of `Asgard::Base` (itself a Thor subclass).
56
+ Asgard searches upward from your current directory for a `.loki` file. That file marks the project root. Additional `*.loki` files in the same directory can be loaded via `import "*.loki"` at the top of `.loki`. All task files reopen `class Tasks`, which is pre-defined by the gem as a subclass of `Asgard::Base` (itself a Thor subclass).
57
57
 
58
- The full Thor DSL is available: `desc`, `method_option`, `class_option`, `long_desc`, `argument`, `default_task`, `map`, and `subcommand` all work exactly as documented in Thor — with Asgard's own `depends_on`, `var`, `sh`, `shebang`, and `dotenv` layered on top.
58
+ The full Thor DSL is available: `desc`, `method_option`, `class_option`, `long_desc`, `argument`, `default_task`, `map`, and `subcommand` all work exactly as documented in Thor — with Asgard's own `depends_on`, `sh`, `shebang`, and `dotenv` layered on top.
59
59
 
60
60
  ---
61
61
 
data/docs/options.md CHANGED
@@ -53,7 +53,7 @@ class Tasks
53
53
  enum: %w[development staging production],
54
54
  desc: "Target environment"
55
55
 
56
- desc "deploy", "Deploy the application"
56
+ desc "Deploy the application"
57
57
  def deploy
58
58
  if options[:dry_run]
59
59
  puts "Would deploy to #{options[:env]}"
@@ -62,7 +62,7 @@ class Tasks
62
62
  end
63
63
  end
64
64
 
65
- desc "migrate", "Run database migrations"
65
+ desc "Run database migrations"
66
66
  def migrate
67
67
  sh "rails db:migrate RAILS_ENV=#{options[:env]}"
68
68
  end
@@ -71,19 +71,53 @@ end
71
71
 
72
72
  Both `deploy` and `migrate` automatically accept `--dry-run` and `--env`.
73
73
 
74
+ ### Boolean class options and `no_negate`
75
+
76
+ Thor automatically generates `[--no-name]` and `[--skip-name]` help entries alongside every boolean `class_option`. When negation is meaningful — `--dry-run` paired with `--no-dry-run` — this is useful. When negation is meaningless, call `no_negate` immediately after the declaration to remove the extra variants from the help output:
77
+
78
+ ```ruby
79
+ class Tasks
80
+ class_option :color,
81
+ type: :boolean,
82
+ default: true,
83
+ desc: "Colorise output"
84
+ no_negate :color
85
+ end
86
+ ```
87
+
88
+ Help output before `no_negate`:
89
+
90
+ ```
91
+ [--color], [--no-color], [--skip-color] # Colorise output
92
+ ```
93
+
94
+ Help output after `no_negate`:
95
+
96
+ ```
97
+ [--color] # Colorise output
98
+ ```
99
+
100
+ `no_negate` accepts multiple option names in a single call:
101
+
102
+ ```ruby
103
+ no_negate :color, :version, :emoji
104
+ ```
105
+
106
+ It has no effect on runtime behaviour — `--no-color` still works on the CLI; only the help display is affected.
107
+
74
108
  ---
75
109
 
76
110
  ## Built-in Flags
77
111
 
78
- `Tasks` ships with three built-in class options and a version flag:
112
+ `Tasks` ships with three built-in `class_option` declarations — `--debug`, `--verbose`, and `--version` — all visible in the Options section of `asgard help`.
79
113
 
80
114
  ### `--version`
81
115
 
82
- Prints `Asgard::VERSION` and exits. Implemented as the `_version` method with the `_` prefix convention (gem-owned, blocked from direct CLI invocation):
116
+ A `class_option :version` of type `:boolean`. Prints `Asgard::VERSION` and exits. Handled by `Asgard.run!` before the `.loki` file is loaded, so it works even in a directory without a `.loki` file. `no_negate :version` suppresses the `[--no-version]` / `[--skip-version]` variants (see [Boolean class options and `no_negate`](#boolean-class-options-and-no_negate) above):
83
117
 
84
118
  ```bash
85
119
  asgard --version
86
- # 0.1.2
120
+ # 0.3.0
87
121
  ```
88
122
 
89
123
  ### `--debug`
@@ -173,8 +207,8 @@ asgard deploy production --verbose
173
207
  Methods whose names start with `_` are considered gem-owned in Asgard's naming convention. `run!` guards against invoking them directly from the CLI:
174
208
 
175
209
  ```bash
176
- asgard _version
177
- # asgard: unknown command '_version'
210
+ asgard _something
211
+ # asgard: unknown command '_something'
178
212
  ```
179
213
 
180
- If you define your own methods on `Tasks`, avoid the `_` prefix to prevent them from being silently blocked.
214
+ If you define your own methods on `Tasks`, avoid the `_` prefix to prevent them from being blocked. Built-in `class_option` declarations (like `--version`, `--debug`, `--verbose`) do not use the `_` prefix because they are options, not commands.