asgard 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d904144543326c15257e4e61584c46fa9b0ab4f1ba522680daa930950361e6a
4
- data.tar.gz: fea08260a5db1fdde268eaf4ca930b1e52a07a39f12eaf2ce20e31736319cdff
3
+ metadata.gz: b6045b69a572bacdc07c1ae149f2978000fb690c180cf536512caf4714345317
4
+ data.tar.gz: defa5899f4e66143fe3118d835c60f128534075d2ce4a41652a0bbf54f61e790
5
5
  SHA512:
6
- metadata.gz: 9f75f50ba53970998d047e21078b120090ba6c232ac698b0ba705b84a3de542bfef313690e11d2e09989101045323c7cd6549bf51826b316ccf1beeb96ada50d
7
- data.tar.gz: 497bee99076f476b8fe680962f59b369898381470b27fd59e0f7e4dc7636e620004660cbcf3ceb10a2678e65ddf78a2a7e24e6571362fc7ad3e7929eeb61a614
6
+ metadata.gz: 8d27eb2c214be342a64561c8f2287cd05aa48a30e411676dbd69b20f36ec9b706456d7c805c3bfa3a4b64897b8f79831a0a55b96c38b77e3981de551c8737750
7
+ data.tar.gz: 6bbd01f8c90195fc9b7428dbe7b56721a70d9ffe4b881853497df84c68db7d96400cb279ab9006ff3b6841ab85bd5f54d6838f3d887469331dbe04873bd5dca5
@@ -0,0 +1,52 @@
1
+ name: Deploy Documentation to GitHub Pages
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ - develop
7
+ paths:
8
+ - "docs/**"
9
+ - "mkdocs.yml"
10
+ - ".github/workflows/deploy-github-pages.yml"
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: write
15
+ pages: write
16
+ id-token: write
17
+
18
+ jobs:
19
+ deploy:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Checkout code
23
+ uses: actions/checkout@v4
24
+ with:
25
+ fetch-depth: 0
26
+
27
+ - name: Setup Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: 3.x
31
+
32
+ - name: Install dependencies
33
+ run: |
34
+ pip install mkdocs
35
+ pip install mkdocs-material
36
+ pip install mkdocs-macros-plugin
37
+ pip install mike
38
+
39
+ - name: Configure Git
40
+ run: |
41
+ git config --local user.email "action@github.com"
42
+ git config --local user.name "GitHub Action"
43
+
44
+ - name: Build MkDocs site
45
+ run: mkdocs build
46
+
47
+ - name: Deploy to GitHub Pages
48
+ uses: peaceiris/actions-gh-pages@v4
49
+ with:
50
+ github_token: ${{ secrets.GITHUB_TOKEN }}
51
+ publish_dir: ./site
52
+ keep_files: true
data/.loki CHANGED
@@ -3,33 +3,31 @@
3
3
  # Task is pre-defined by the gem — just reopen it to add tasks.
4
4
 
5
5
  class Tasks
6
- var :gem_name, "asgard"
7
- var :version, -> { Asgard::VERSION }
6
+ @@gem_name ||= "asgard".freeze
8
7
 
9
- desc "test", "Run the test suite"
8
+ desc "Run the test suite"
10
9
  def test
11
10
  sh "bundle exec rake test"
12
11
  end
13
12
 
14
- depends_on :test
15
- desc "quality", "Run tests then flog"
13
+ desc "Run all quality gates (tests, RuboCop, Flog)"
16
14
  def quality
17
- sh "flog lib/"
15
+ sh "bundle exec rake quality"
18
16
  end
19
17
 
20
- desc "build", "Build the gem package"
18
+ desc "Build the gem package"
21
19
  def build
22
20
  sh "bundle exec rake build"
23
21
  end
24
22
 
25
23
  depends_on :test
26
- desc "install", "Build and install gem locally"
24
+ desc "Build and install gem locally"
27
25
  def install
28
26
  sh "bundle exec rake install"
29
27
  end
30
28
 
31
29
  depends_on :quality
32
- desc "release", "Release to RubyGems"
30
+ desc "Release to RubyGems"
33
31
  def release
34
32
  sh "bundle exec rake release"
35
33
  end
data/.rubocop.yml ADDED
@@ -0,0 +1,157 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+ TargetRubyVersion: 3.2
5
+ Exclude:
6
+ - 'examples/**/*'
7
+ - 'vendor/**/*'
8
+
9
+ # ── Style: disabled cops ───────────────────────────────────────────────────
10
+ Style/StringLiterals:
11
+ Enabled: false
12
+
13
+ Style/StringLiteralsInInterpolation:
14
+ Enabled: false
15
+
16
+ Style/Documentation:
17
+ Enabled: false
18
+
19
+ Style/IfUnlessModifier:
20
+ Enabled: false
21
+
22
+ Style/RescueModifier:
23
+ Enabled: false
24
+
25
+ Style/TrivialAccessors:
26
+ Enabled: false
27
+
28
+ Style/MultilineTernaryOperator:
29
+ Enabled: false
30
+
31
+ Style/SafeNavigation:
32
+ Enabled: false
33
+
34
+ Style/ClassAndModuleChildren:
35
+ Enabled: false
36
+
37
+ Style/RescueStandardError:
38
+ Enabled: false
39
+
40
+ # Both % and format/sprintf are acceptable
41
+ Style/FormatString:
42
+ Enabled: false
43
+
44
+ # String concatenation and interpolation are both acceptable
45
+ Style/StringConcatenation:
46
+ Enabled: false
47
+
48
+ # ── Layout ─────────────────────────────────────────────────────────────────
49
+ Layout/LineLength:
50
+ Max: 140
51
+
52
+ Layout/ExtraSpacing:
53
+ Enabled: false
54
+
55
+ Layout/HashAlignment:
56
+ Enabled: false
57
+
58
+ Layout/FirstHashElementIndentation:
59
+ Enabled: false
60
+
61
+ Layout/EmptyLineAfterGuardClause:
62
+ Enabled: false
63
+
64
+ # ── Naming ─────────────────────────────────────────────────────────────────
65
+ # Single-char params (e, t) are acceptable throughout
66
+ Naming/MethodParameterName:
67
+ Enabled: false
68
+
69
+ Naming/VariableNumber:
70
+ Exclude:
71
+ - 'test/**/*'
72
+
73
+ Naming/RescuedExceptionsVariableName:
74
+ Enabled: false
75
+
76
+ Naming/AccessorMethodName:
77
+ Enabled: false
78
+
79
+ Naming/PredicatePrefix:
80
+ Enabled: false
81
+
82
+ Naming/PredicateMethod:
83
+ AllowedMethods:
84
+ - import
85
+ Exclude:
86
+ - 'test/**/*'
87
+
88
+ # ── Lint ───────────────────────────────────────────────────────────────────
89
+ Lint/EmptyBlock:
90
+ Exclude:
91
+ - 'test/**/*'
92
+
93
+ Lint/UnusedMethodArgument:
94
+ Enabled: false
95
+
96
+ Lint/ConstantDefinitionInBlock:
97
+ Exclude:
98
+ - 'Rakefile'
99
+ - 'test/**/*'
100
+
101
+ # ── Gemspec ────────────────────────────────────────────────────────────────
102
+ Gemspec/DevelopmentDependencies:
103
+ EnforcedStyle: Gemfile
104
+
105
+ Gemspec/RequiredRubyVersion:
106
+ Enabled: false
107
+
108
+ Gemspec/OrderedDependencies:
109
+ Enabled: false
110
+
111
+ # ── Metrics ────────────────────────────────────────────────────────────────
112
+ # Flog is the primary complexity gate — these thresholds catch only
113
+ # egregious outliers without false-positiving every dispatch method.
114
+
115
+ Metrics/MethodLength:
116
+ Max: 35
117
+ CountAsOne:
118
+ - heredoc
119
+ - array
120
+ - hash
121
+ Exclude:
122
+ - 'test/**/*'
123
+
124
+ Metrics/AbcSize:
125
+ Max: 40
126
+ Exclude:
127
+ - 'test/**/*'
128
+
129
+ Metrics/ClassLength:
130
+ Max: 300
131
+ Exclude:
132
+ - 'test/**/*'
133
+
134
+ Metrics/ModuleLength:
135
+ Max: 200
136
+ Exclude:
137
+ - 'test/**/*'
138
+
139
+ Metrics/CyclomaticComplexity:
140
+ Max: 20
141
+ Exclude:
142
+ - 'test/**/*'
143
+
144
+ Metrics/PerceivedComplexity:
145
+ Max: 20
146
+ Exclude:
147
+ - 'test/**/*'
148
+
149
+ Metrics/ParameterLists:
150
+ Enabled: false
151
+
152
+ Metrics/BlockLength:
153
+ Max: 60
154
+ Exclude:
155
+ - 'Rakefile'
156
+ - '*.gemspec'
157
+ - 'test/**/*'
data/CHANGELOG.md CHANGED
@@ -5,7 +5,95 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.3.0] - Unreleased
9
+
10
+ ### Added
11
+
12
+ - **`loki_up(name = ".loki")` Kernel method** — searches `Dir.pwd` and each ancestor directory for a file with the given name; returns the absolute path of the first match or `nil`. Available everywhere in Ruby (task bodies, `.loki` files, top-level code) as a `module_function` on `Kernel`.
13
+ - **`import(path)` Kernel method** — loads a `.loki` file (or a glob of `.loki` files) with `require`-like idempotency via `$LOADED_FEATURES`. Accepts a `String` or `Pathname`. Relative paths are resolved relative to the caller's file (like `require_relative`). Glob patterns (`*.loki`, `**/*.loki`) expand via `Dir.glob` and load all matches. Returns `true` if any file was newly loaded, `false` if all were already loaded or no glob matches were found. Raises `ArgumentError` if the path does not end with `.loki`.
14
+ - **`import_up(name = ".loki")` Kernel method** — combines `loki_up` and `import`. For exact names, finds the first ancestor directory containing that file and loads it. For glob names, finds the first ancestor directory containing any matching files and loads them all — stopping at that level rather than aggregating across multiple ancestors. Returns `false` if nothing is found.
15
+ - **`debug?` and `verbose?` Kernel module functions** — thin wrappers around `$DEBUG` and `$VERBOSE`, available everywhere in Ruby as `module_function` on `Kernel`. Set via `--debug` / `--verbose` CLI flags or directly via `$DEBUG` / `$VERBOSE`.
16
+ - **`env(name, default = nil)` Kernel method** — fetches a system environment variable by symbol or string name, upcasing the key automatically. `env(:port, "3000")` returns `"3000"` when `PORT` is unset; `env(:api_key)` raises `KeyError` when `API_KEY` is missing and no default is provided. Accepts both `env(:port)` and `env("PORT")` forms. Cleaner than `ENV['PORT']` in task bodies.
17
+ - **Verbose/debug feedback for `import` and `import_up`** — when `verbose?` is true, each file loaded is printed to stderr. When `debug?` is true, already-loaded files are also reported (with an "already loaded" suffix), and `import_up` reports when a file is not found.
18
+ - **RuboCop lint gate** — RuboCop is now a first-class quality gate alongside tests and Flog. Added `rubocop` to the Gemfile, a `.rubocop.yml` tuned for this codebase (Ruby 3.2 target, relaxed `Metrics` thresholds consistent with Flog as the primary complexity gate, `examples/` excluded), and `rake rubocop` / `rake rubocop_fix` tasks backed by a `tmp/rubocop_cache` directory for fast re-runs.
19
+ - **Expanded `rake quality` task** — `quality` now runs three independent gates (tests + coverage, RuboCop, Flog) and prints a formatted pass/fail summary table after all gates complete, so every failure is visible in a single run rather than stopping at the first.
20
+ - **`rake flog_check` task** — replaces the bare `flog lib/` call with a structured task that enforces per-method thresholds (warn ≥20, fail ≥50), lists warnings and failures in separate sections, and exits non-zero only when the failure threshold is breached.
21
+ - **Single-argument `desc` shorthand** — `desc` now accepts one string (the description) with the usage string omitted. The usage defaults to the method name, eliminating the redundant first argument for the common case:
22
+ ```ruby
23
+ desc "Run the test suite" # usage defaults to "test"
24
+ def test = sh "bundle exec rake test"
25
+ ```
26
+ The two-argument form (`desc "usage", "description"`) still works unchanged and is still required when the usage string differs from the method name (e.g. `desc "build NAME", "Build an artifact"`).
27
+ - **`default_task` override warning** — `Asgard::Base` now overrides Thor's `default_task` to warn to stderr when a second call would silently replace the first. The warning includes the task name, file, and line number for both the original declaration and the override, making accidental cross-file clobbering visible immediately.
28
+ - **`examples/env_usage.loki` and `examples/.env`** — demonstrate the `env()` Kernel helper, including the default-fallback form (`env(:log_level, "info")`) for variables absent from the environment. Uses `loki_up(".env")` so `dotenv` locates the `.env` file correctly regardless of which directory `asgard` is invoked from.
29
+ - **`examples/subdir/`** — three-file demo showing `import` and `import_up` across directory boundaries: `subdir/.loki` imports `import_up_demo.loki` by name; `import_up_demo.loki` calls `import_up "env_usage.loki"` to locate and load a file from an ancestor directory without a hardcoded path.
30
+ - **`status` task in `kitchen_sink.loki`** — demonstrates `debug?` and `verbose?` predicates with conditional output; shows `--debug` printing `$DEBUG`, `$VERBOSE`, and the full options hash.
31
+ - **Computed value methods in `kitchen_sink.loki`** — three private methods (`version`, `sha`, `branch`) demonstrating the idiomatic Ruby replacement for the removed `var` DSL, including memoization via `@ivar ||=`.
32
+
33
+ ### Removed
34
+
35
+ - **`var` DSL method** — removed in favour of native Ruby class variables. Use `@@name ||= "value".freeze` in the class body. Class variables are visible in all task instance methods and in subcommand subclasses, making them the correct tool for shared configuration in a Thor-based task runner. Breaking change for projects using `var`.
36
+ - **`import` DSL method** — `import(mod)` was a one-line alias for Ruby's built-in `include`. Callers can use `include` directly.
37
+ - **`--auto-load` CLI flag** — sibling `*.loki` loading is now entirely user-controlled: place `import "*.loki"` (or any glob or explicit path) at the top of your `.loki` file to load additional task files. Breaking change for projects that relied on `--auto-load`.
38
+ - **`Asgard.load_loki(dir)`** — replaced by `import` with glob support. Callers can use `import(File.join(dir, "*.loki"))` directly.
39
+ - **`debug?` / `verbose?` private methods on `Tasks`** — removed as redundant. The identical `module_function` versions on `Kernel` are available everywhere, including inside task bodies.
40
+
41
+ ### Refactored
42
+
43
+ - **`validate_deps!` decomposed into focused private helpers** — the method was performing four unrelated validations in one body (orphaned `depends_on` check, undefined dep name check, dep arity check, cycle detection). Each concern is now a dedicated `_`-prefixed private method (`_check_orphaned_deps!`, `_check_undefined_deps!`, `_check_dep_arities!`, `_build_and_sort_graph`). `validate_deps!` is now a sequencer of ~8 lines. Flog score dropped from 87.3 to 24.4.
44
+ - **`invoke_command` dispatch helpers made private with descriptive names** — the dispatch hook was decomposed into focused helpers. Those helpers (`acquire_run_token`, `run_deps_for`, `run_dep_group`, `signal_done`, `run_dep`) are now declared `private` on `Asgard::Base` rather than wrapped in `no_commands`. The `_` prefix was dropped — the underscore convention is reserved for gem-owned methods on `Tasks`; `private` is sufficient to exclude instance methods from Thor's command registry. `invoke_command` itself stays in `no_commands` so Thor does not warn about an undescribed public method.
45
+
46
+ ### Fixed
47
+
48
+ - **Multiple parallel dep failures now all surfaced** — when two or more parallel deps raised, only the first exception was re-raised; the rest were silently discarded. All errors are now printed to stderr via `warn` before a general `Asgard::Error` is raised. When only one dep fails, its exception is re-raised directly as before.
49
+ - **Subcommand deps not validated at startup** — `run!` only called `Tasks.validate_deps!`, so circular dependencies and undefined dep names in subcommand groups were silently ignored. `run!` now snapshots `Asgard::Base.subclasses` before loading task files and validates every newly defined subclass alongside `Tasks`.
50
+ - **Parallel dep thread orphaned on exception** — when a parallel dep group contained one fast-failing task and one slow task, the join loop re-raised the first thread's exception and abandoned the remaining threads. The join loop now collects all thread exceptions before re-raising, ensuring every thread completes before execution exits the group.
51
+ - **Dep with required arguments gave a cryptic runtime error** — `depends_on :build` where `build(name)` has required parameters caused `Thor::InvocationError` at task invocation time with no indication of where the problem was. `validate_deps!` now checks arity via `instance_method.parameters` and raises `Asgard::Error` at startup with the task name and argument count.
52
+ - **Orphaned `depends_on` silently discarded** — a `depends_on` declaration at the end of a class body with no following `desc`/`def` left `@_pending_deps` non-empty but was ignored by `validate_deps!` due to an early `return if _deps.empty?` guard. `validate_deps!` now checks `@_pending_deps` before that guard and raises `Asgard::Error` naming the orphaned dependencies.
53
+ - **Single-arg `desc` options silently dropped** — when `desc "description", hide: true` was used, Ruby routed the options hash to the `description` positional parameter. The override now detects a `Hash` in the description position and treats it as options.
54
+ - **Single-arg `desc` stolen by `no_commands` blocks** — if a `no_commands` block appeared between a single-arg `desc` and its method, `method_added` consumed `@_pending_single_desc` for the interstitial helper and leaked a stale `@usage` onto the next real command. The fix: `@_pending_single_desc` is only consumed when `no_commands?` is false.
55
+ - **Parallel dep race condition** — when two parallel tasks shared a common dependency, the second thread could start before the shared dep finished. `_ran_tasks` (a single Set) has been replaced with `_running` / `_done` Sets and a per-task `ConditionVariable`. Threads that arrive at an already-running dep now wait for its completion.
56
+ - **`depends_on` silently dropped before `no_commands` blocks** — the `method_added` guard now checks `@usage` instead of the `no_commands?` counter, so `no_commands` helpers placed between `depends_on` and `def` no longer silently steal the dependency.
57
+ - **`shebang` ignored its `silent:` keyword argument** — the parameter was accepted but never referenced; the script body is now echoed to stdout unless `silent: true` is passed, matching the behavior of `sh`.
58
+
59
+ ### Changed
60
+
61
+ - **`validate_deps!` detects undefined dependency names** — `depends_on :nonexistent` previously passed validation silently and produced no error at runtime. `validate_deps!` now raises `Asgard::Error` listing every dep name that does not correspond to a defined task.
62
+ - **`default_task` behaviour documented** — `docs/tasks.md` now notes that running `asgard` with no arguments displays the help message when `default_task` is not set.
63
+ - **`loki_up` scope clarified in docs** — `docs/task-files.md` and `docs/api.md` now make explicit that `loki_up` locates any file by name, not just `.loki` files, with examples for `.env` and `VERSION`. The `dotenv loki_up(".env") || ".env"` pattern is shown as the canonical way to load a `.env` file from any subdirectory.
64
+ - **`examples/.loki`** — updated to use explicit `import "*.loki"` (sibling files) and `import "subdir/import_demo.loki"` (subdirectory file), with comments explaining `import`, `import_up`, and `loki_up`.
65
+
66
+ ## [0.2.0] - 2026-05-29
67
+
68
+ ### Changed
69
+
70
+ - `*.loki` files are no longer auto-loaded by default. Pass `--auto-load` to `asgard` to load all `*.loki` files from the project root alphabetically before `.loki`. This is a breaking change for projects using the multi-file layout.
71
+ - Added `--auto-load` as a built-in CLI flag in `Tasks`, visible in `asgard help`
72
+
73
+ ## [0.1.2] - 2026-05-29
74
+
75
+ ### Added
76
+
77
+ - `--version` built-in CLI flag — prints `Asgard::VERSION` and exits; implemented as a `_`-prefixed method in `Tasks` per the gem-owned naming convention
78
+ - `--debug` and `--verbose` built-in `class_option` declarations on `Tasks` — set `$DEBUG`/`$VERBOSE` before any task runs via the `invoke_command` hook in `Asgard::Base`
79
+ - `debug?` and `verbose?` private predicate helpers on `Tasks` — thin wrappers around `$DEBUG` and `$VERBOSE` for use inside task bodies
80
+ - `_` prefix convention for gem-owned methods in `Tasks` — built-in methods use `_` prefix to distinguish them from user-defined tasks
81
+ - `run!` guards against direct invocation of `_`-prefixed commands with a clean error message and exit 1
82
+ - `examples/` directory with working `.loki` files:
83
+ - `kitchen_sink.loki` — demonstrates the full Thor DSL (all option types, `long_desc`, `class_option`, `default_task`, `map`, `depends_on`, `var`, `no_commands`, `private`)
84
+ - `server_subcommands.loki` — subcommand group for server management
85
+ - `db_subcommands.loki` — subcommand group for database management with `depends_on` chaining
86
+ - README sections: Helper methods, Subcommands, Thor wrapper callout
87
+
88
+ ### Fixed
89
+
90
+ - Replaced `warn`/`exit 1` with `abort` throughout `run!` — `Kernel#warn` is silenced when `$VERBOSE = nil`, which is the default in Ruby 4.0; `abort` writes to `$stderr` regardless
91
+
92
+ ### Changed
93
+
94
+ - `--debug` and `--verbose` promoted from mapped tasks to `class_option` — they now work as modifiers alongside other commands (e.g. `asgard build --debug`) rather than as standalone commands
95
+ - Removed all references to `just` task runner and `recipe` terminology; Asgard uses "task" throughout
96
+ - `depends_on` parameter renamed from `*recipes` to `*tasks` for consistency
9
97
 
10
98
  ## [0.1.1] - 2026-05-28
11
99
  ### Added
@@ -52,5 +140,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
52
140
  - 100% test coverage enforced via SimpleCov (95% minimum threshold)
53
141
  - Quality task in `.loki` runs flog after tests
54
142
 
55
- [Unreleased]: https://github.com/MadBomber/asgard/compare/v0.1.0...HEAD
143
+ [0.3.0]: https://github.com/MadBomber/asgard/compare/v0.2.2...HEAD
144
+ [0.2.0]: https://github.com/MadBomber/asgard/compare/v0.1.2...v0.2.0
145
+ [0.1.2]: https://github.com/MadBomber/asgard/compare/v0.1.1...v0.1.2
146
+ [0.1.1]: https://github.com/MadBomber/asgard/compare/v0.1.0...v0.1.1
56
147
  [0.1.0]: https://github.com/MadBomber/asgard/releases/tag/v0.1.0
data/CLAUDE.md CHANGED
@@ -29,11 +29,10 @@ Single test: `ruby -Ilib:test test/test_asgard.rb`
29
29
 
30
30
  `bin/asgard` → `Asgard.run!(ARGV)` (`lib/asgard.rb`):
31
31
  1. Walk CWD + ancestors for `.loki` (marker only, not a task file)
32
- 2. Glob + sort `*.loki` files from that dir, `load` each alphabetically
33
- 3. Load `.loki` itself last
34
- 4. `Tasks.validate_deps!` — build full dep graph, raise `CircularDependencyError` if cyclic
35
- 5. `Tasks._reset_ran!`clear execution tracking
36
- 6. `Tasks.start(argv)` — Thor dispatches the command
32
+ 2. Load `.loki` any sibling `*.loki` files are loaded only if `.loki` calls `import`
33
+ 3. `Tasks.validate_deps!` — build full dep graph, raise `CircularDependencyError` if cyclic
34
+ 4. `Tasks._reset_ran!` — clear execution tracking
35
+ 5. `Tasks.start(argv)`Thor dispatches the command
37
36
 
38
37
  ### Core Classes
39
38
 
@@ -71,8 +70,6 @@ Do not define `_`-prefixed methods in user `.loki` files — that namespace is r
71
70
  depends_on :a, [:b, :c], :d # stages: [[:a], [:b, :c], [:d]]
72
71
  ```
73
72
 
74
- **`var`** stores values or lambdas in `@_vars` and creates an instance method that evaluates the lambda once on first access.
75
-
76
73
  **`invoke_command`** (Thor dispatch hook):
77
74
  1. Atomically check `@_ran_tasks` Set (with `@_ran_mutex`); return early if already run
78
75
  2. Resolve `@_deps` stages → `_build_dep_graph` → `Dagwood::DependencyGraph#parallel_order`
@@ -91,6 +88,19 @@ Dagwood topologically sorts the DAG and returns parallel groups. The thread-safe
91
88
  - `sh(script, silent: false)` — single-line strings use `system(script)`; multi-line strings pipe through `bash -c`; exits with the command's status on failure
92
89
  - `shebang(interpreter, script)` — writes script to a tempfile and executes with the named interpreter (`:python3`, `:node`, `:ruby`, `:perl`, `:bash`, etc.)
93
90
 
91
+ ### Kernel Methods
92
+
93
+ Asgard adds the following `module_function` methods to `Kernel`, making them available everywhere in `.loki` files without any prefix or require:
94
+
95
+ | Method | Description |
96
+ |--------|-------------|
97
+ | `env(name, default = nil)` | Fetch a system environment variable by symbol or string; name is upcased automatically. Raises `KeyError` when missing and no default given. |
98
+ | `loki_up(name = ".loki")` | Walk CWD and ancestors for a file by name; returns absolute path or `nil`. |
99
+ | `import(path)` | Load a `.loki` file or glob of `.loki` files, idempotently. |
100
+ | `import_up(name = ".loki")` | Combine `loki_up` and `import` — find and load in one call. |
101
+ | `debug?` | Returns `$DEBUG`. |
102
+ | `verbose?` | Returns `$VERBOSE`. |
103
+
94
104
  ## Testing
95
105
 
96
106
  All tests are in `test/test_asgard.rb` (one file, ~11 named classes). SimpleCov minimum is 95%; the Rakefile configures this with a prelude that loads coverage before the library.
@@ -103,7 +113,7 @@ A `.loki` file is plain Ruby that reopens `Tasks`:
103
113
 
104
114
  ```ruby
105
115
  class Tasks
106
- var :gem_name, "asgard"
116
+ @@gem_name ||= "asgard".freeze
107
117
 
108
118
  desc "test", "Run tests"
109
119
  def test = sh "bundle exec rake test"
@@ -114,4 +124,4 @@ class Tasks
114
124
  end
115
125
  ```
116
126
 
117
- Multiple `*.loki` files in the same directory are all loaded (alphabetically). The bare `.loki` file serves only as the project root marker its content is loaded last.
127
+ Only `.loki` is loaded by default. The bare `.loki` file is the project root marker and always controls what else gets loaded — call `import "*.loki"` (or any glob/path) at the top to pull in sibling task files.