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.
@@ -0,0 +1,129 @@
1
+ # Environment Variables
2
+
3
+ Asgard provides the `dotenv` class method to load `.env` files into the process environment before tasks run. It is a thin wrapper around the [dotenv gem](https://github.com/bkeepers/dotenv).
4
+
5
+ ---
6
+
7
+ ## Basic Usage
8
+
9
+ Call `dotenv` inside the class body (not inside a task method) to load the default `.env` file:
10
+
11
+ ```ruby
12
+ class Tasks
13
+ dotenv # loads .env from the current working directory
14
+
15
+ desc "Print the app name from .env"
16
+ def check = puts env(:app_name)
17
+ end
18
+ ```
19
+
20
+ ### Load a Named File
21
+
22
+ Pass a file path string to load a specific file:
23
+
24
+ ```ruby
25
+ class Tasks
26
+ dotenv ".env.local" # load a local override
27
+ dotenv ".env.staging" # load staging-specific vars
28
+ end
29
+ ```
30
+
31
+ ### Multiple Calls
32
+
33
+ Call `dotenv` multiple times to load several files. Each call merges the loaded variables into `ENV`. Later calls do not overwrite variables already set by earlier calls (standard dotenv behavior):
34
+
35
+ ```ruby
36
+ class Tasks
37
+ dotenv # loads .env (base config)
38
+ dotenv ".env.local" # loads .env.local (local overrides)
39
+ end
40
+ ```
41
+
42
+ ---
43
+
44
+ ## When `dotenv` Runs
45
+
46
+ `dotenv` is a **class-level** call — it executes at Ruby class-load time, not when a task is invoked. This means:
47
+
48
+ 1. Variables are available in `ENV` before any task method runs.
49
+ 2. They are available during `depends_on` dependency resolution.
50
+
51
+ ```ruby
52
+ class Tasks
53
+ dotenv
54
+
55
+ desc "Run migrations"
56
+ def migrate = sh "DATABASE_URL=#{env(:database_url)} rails db:migrate"
57
+ end
58
+ ```
59
+
60
+ !!! warning
61
+ If `.env` does not exist, `dotenv` silently does nothing — it checks `File.exist?` before loading. There is no error for a missing file.
62
+
63
+ ---
64
+
65
+ ## File Not Found
66
+
67
+ Asgard calls `Dotenv.load(path)` only when `File.exist?(path)` is true. If the file is absent, the call is a no-op:
68
+
69
+ ```ruby
70
+ class Tasks
71
+ dotenv ".env.local" # silently skipped if .env.local does not exist
72
+ end
73
+ ```
74
+
75
+ This makes it safe to commit a `.env.local` line to your `.loki` without requiring every developer to create the file.
76
+
77
+ ---
78
+
79
+ ## Environment Variables vs. Class Variables
80
+
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:
82
+
83
+ ```ruby
84
+ class Tasks
85
+ dotenv
86
+
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
102
+
103
+ desc "Start the server"
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
112
+ end
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Dotenv File Format
118
+
119
+ Standard dotenv file syntax applies:
120
+
121
+ ```bash
122
+ # .env
123
+ APP_NAME=myapp
124
+ DATABASE_URL=postgres://localhost/myapp_development
125
+ REDIS_URL=redis://localhost:6379/0
126
+ PORT=3000
127
+ ```
128
+
129
+ Multi-line values and quotes are supported per the dotenv gem's own documentation.
data/docs/examples.md ADDED
@@ -0,0 +1,140 @@
1
+ # Examples
2
+
3
+ The `examples/` directory in the Asgard repository contains complete, working `.loki` files that demonstrate every feature of the gem. You can use them as a standalone Asgard project or as copy-paste references.
4
+
5
+ ---
6
+
7
+ ## Using the Examples Directory
8
+
9
+ The `examples/` directory contains its own `.loki` root marker (the gem's project-level `.loki`), which means you can run `asgard` from inside the `examples/` directory and all example files will be loaded:
10
+
11
+ ```bash
12
+ git clone https://github.com/MadBomber/asgard.git
13
+ cd asgard/examples
14
+ asgard help
15
+ ```
16
+
17
+ Alternatively, copy individual example files into your own project's directory.
18
+
19
+ !!! note
20
+ Some examples (notably `concurrent.loki`) produce visible interleaved output to demonstrate real thread concurrency. They are designed to be run, not just read.
21
+
22
+ ---
23
+
24
+ ## `kitchen_sink.loki`
25
+
26
+ **Path:** `examples/kitchen_sink.loki`
27
+
28
+ The most comprehensive example — demonstrates every Thor DSL feature available in Asgard:
29
+
30
+ - `@@` class variables for shared configuration values
31
+ - `dotenv` (commented out, ready to activate)
32
+ - `class_option` with `:boolean` and `:string` types, including `enum`
33
+ - `default_task` — sets the default command when `asgard` is run with no arguments
34
+ - `map` — short aliases for multiple tasks
35
+ - A basic task with no parameters
36
+ - A task with a positional parameter and default
37
+ - A task with `option` (the `method_option` alias)
38
+ - All five `method_option` types: `:string`, `:boolean`, `:numeric`, `:array`, `:hash`
39
+ - `required` option, `enum` validation, and `banner` customization
40
+ - `long_desc` with `\x5` line-break trick for formatted examples in help text
41
+ - Sequential `depends_on` (`:analyze` before `:spec`)
42
+ - Parallel `depends_on` (`[:analyze, :typecheck]` run concurrently)
43
+ - Mixed sequential + parallel `depends_on` (`:check`, `[:compile, :spec]`, `:pack`)
44
+ - `no_commands` block for a public helper excluded from CLI
45
+ - `private` methods for internal helpers
46
+
47
+ ```bash
48
+ asgard help # see all tasks
49
+ asgard greet # default task
50
+ asgard hello Alice
51
+ asgard compile --jobs 4 --tags debug release --defines VERSION:2 MODE:fast
52
+ asgard deploy --strategy rolling
53
+ asgard report --format html --since 2024-01-01
54
+ asgard pipeline
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Server Subcommands
60
+
61
+ **Path:** `examples/server_subcommands.loki`
62
+
63
+ Demonstrates Thor subcommands with a server management group. Covers:
64
+
65
+ - Defining a subcommand class (`ServerCommands < Tasks`)
66
+ - Registering it with `subcommand "server", ServerCommands`
67
+ - Per-command options (`--daemon`, `--workers`, `--log`, `--force`, `--wait`)
68
+ - `depends_on` inside a subcommand group (`:stop` and `:start` before `:restart`)
69
+
70
+ ```bash
71
+ asgard server help
72
+ asgard server start
73
+ asgard server start 4000 --workers 4 --daemon
74
+ asgard server stop --force
75
+ asgard server status
76
+ asgard server restart 4000
77
+ ```
78
+
79
+ The `ServerCommands` class inherits from `Tasks`, giving it access to `sh`, `depends_on`, and the built-in `--debug`/`--verbose` flags.
80
+
81
+ ---
82
+
83
+ ## DB Subcommands
84
+
85
+ **Path:** `examples/db_subcommands.loki`
86
+
87
+ Demonstrates subcommands with more complex `depends_on` chaining within the group. Covers:
88
+
89
+ - `DBCommands < Tasks` with migrate, rollback, seed, reset, console, and status commands
90
+ - Multi-step `depends_on` chain: `rollback → migrate → seed → reset`
91
+ - `long_desc` with formatted examples inside a subcommand class
92
+ - `enum` validation on subcommand options
93
+ - Optional positional parameters (`migrate [VERSION]`, `rollback [STEPS]`, `seed [FILE]`)
94
+
95
+ ```bash
96
+ asgard db help
97
+ asgard db migrate
98
+ asgard db migrate 20240101120000 --dry-run
99
+ asgard db rollback
100
+ asgard db rollback 3
101
+ asgard db seed --env staging
102
+ asgard db reset # rollback → migrate → seed → reset
103
+ asgard db console --env staging
104
+ asgard db status
105
+ ```
106
+
107
+ ---
108
+
109
+ ## `concurrent.loki`
110
+
111
+ **Path:** `examples/concurrent.loki`
112
+
113
+ A focused demonstration of true concurrent execution via parallel `depends_on` groups:
114
+
115
+ - Three worker tasks (`worker_a`, `worker_b`, `worker_c`) each print a character repeatedly with random sleep delays
116
+ - All three run in parallel threads when `asgard finish` is invoked
117
+ - The interleaved output proves that real concurrency is occurring (not sequential batching)
118
+ - Uses `$stdout.sync = true` to ensure thread-safe immediate output flushing
119
+
120
+ ```bash
121
+ asgard finish
122
+ # starting demo of concurrent task execution ...
123
+ # ABCBACBACBABCBACBACB (order varies every run)
124
+ # fini - the end of concurrent task demo
125
+ ```
126
+
127
+ Execution order: `start` → `worker_a ∥ worker_b ∥ worker_c` → `finish`
128
+
129
+ This example is also useful as a test harness for verifying that parallel execution is working correctly on a given system.
130
+
131
+ ---
132
+
133
+ ## Summary
134
+
135
+ | File | Primary Focus |
136
+ |---|---|
137
+ | `kitchen_sink.loki` | Comprehensive Thor DSL reference — options, aliases, long_desc, depends_on |
138
+ | `server_subcommands.loki` | Subcommand groups, per-command options, depends_on in subcommands |
139
+ | `db_subcommands.loki` | Multi-step depends_on chains, enum validation, long_desc in subcommands |
140
+ | `concurrent.loki` | Parallel task execution, thread concurrency demonstration |
@@ -0,0 +1,179 @@
1
+ # Getting Started
2
+
3
+ This guide walks you through installing Asgard, creating your first `.loki` task file, and running tasks from the command line.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ === "RubyGems"
10
+
11
+ ```bash
12
+ gem install asgard
13
+ ```
14
+
15
+ === "Bundler"
16
+
17
+ ```bash
18
+ bundle add asgard
19
+ ```
20
+
21
+ Or add it manually to your `Gemfile`:
22
+
23
+ ```ruby
24
+ gem "asgard", "~> 0.1"
25
+ ```
26
+
27
+ then run `bundle install`.
28
+
29
+ ---
30
+
31
+ ## Verify the Installation
32
+
33
+ ```bash
34
+ asgard --version
35
+ # 0.1.2
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Create Your First Task File
41
+
42
+ Every Asgard project needs a `.loki` file at its root. This hidden file is both the project root marker (Asgard searches upward from CWD to find it) and the entry point for your tasks.
43
+
44
+ ```bash
45
+ # Create the root marker in your project directory
46
+ touch .loki
47
+ ```
48
+
49
+ Open `.loki` in your editor and add a task:
50
+
51
+ ```ruby
52
+ class Tasks
53
+ desc "Say hello to the world"
54
+ def hello = puts "Hello, World!"
55
+ end
56
+ ```
57
+
58
+ !!! note
59
+ The `Tasks` class is pre-defined by the gem as `class Tasks < Asgard::Base`. You just reopen it — no `require` or superclass declaration needed.
60
+
61
+ ---
62
+
63
+ ## Run Your Task
64
+
65
+ ```bash
66
+ asgard hello
67
+ # Hello, World!
68
+ ```
69
+
70
+ See all available tasks:
71
+
72
+ ```bash
73
+ asgard help
74
+ ```
75
+
76
+ See help for a specific task:
77
+
78
+ ```bash
79
+ asgard help hello
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Add a Parameter
85
+
86
+ Positional parameters are declared directly in the method signature. Document them in the `desc` usage string:
87
+
88
+ ```ruby
89
+ class Tasks
90
+ desc "greet NAME", "Greet someone by name"
91
+ def greet(name = "World")
92
+ puts "Hello, #{name}!"
93
+ end
94
+ end
95
+ ```
96
+
97
+ ```bash
98
+ asgard greet
99
+ # Hello, World!
100
+
101
+ asgard greet Alice
102
+ # Hello, Alice!
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Add an Option
108
+
109
+ Use `method_option` (alias: `option`) to declare named flags:
110
+
111
+ ```ruby
112
+ class Tasks
113
+ desc "greet NAME", "Greet someone by name"
114
+ option :shout, aliases: "-s", type: :boolean, desc: "Uppercase the greeting"
115
+ def greet(name = "World")
116
+ msg = options[:shout] ? "HELLO, #{name.upcase}!" : "Hello, #{name}!"
117
+ puts msg
118
+ end
119
+ end
120
+ ```
121
+
122
+ ```bash
123
+ asgard greet Alice --shout
124
+ # HELLO, ALICE!
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Multi-Loki Structure
130
+
131
+ A large Asgard project might look like this:
132
+
133
+ ```
134
+ myproject/
135
+ .loki ← root marker and entry point (may be empty or contain tasks)
136
+ build.loki ← build-related and library dependency-related tasks
137
+ deploy.loki ← deployment tasks
138
+ qa.loki ← test and lint tasks
139
+ ```
140
+
141
+ Each `*.loki` file reopens `class Tasks`. To load them, pass `--auto-load` to the `asgard` command — they are loaded alphabetically before `.loki`. See [Task Files](task-files.md) for full details.
142
+
143
+ ---
144
+
145
+ ## Built-in Flags
146
+
147
+ Every task automatically has three flags available, defined as `class_option` on `Tasks`:
148
+
149
+ | Flag | Description |
150
+ |---|---|
151
+ | `--version` | Print the Asgard version and exit |
152
+ | `--debug` | Set `$DEBUG = true` before the task runs |
153
+ | `--verbose` | Set `$VERBOSE = true` before the task runs |
154
+
155
+ ```bash
156
+ asgard --version
157
+ asgard hello --debug
158
+ asgard hello --verbose
159
+ ```
160
+
161
+ Inside a task body, use the `debug?` and `verbose?` predicates:
162
+
163
+ ```ruby
164
+ def hello
165
+ puts "building..."
166
+ sh "make --debug" if debug?
167
+ end
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Next Steps
173
+
174
+ - [Defining Tasks](tasks.md) — parameters, options, aliases, long_desc
175
+ - [Dependencies](dependencies.md) — sequential, parallel, and mixed dependency graphs
176
+ - [Variables](variables.md) — share values across tasks
177
+ - [Shell Helpers](shell.md) — `sh`, `shebang`, and polyglot scripts
178
+ - [Subcommands](subcommands.md) — group related tasks under a namespace
179
+ - [Examples](examples.md) — working `.loki` files for every feature
data/docs/helpers.md ADDED
@@ -0,0 +1,178 @@
1
+ # Helper Methods
2
+
3
+ Not every method needs to be a CLI command. Asgard (via Thor) provides two mechanisms to define callable helper methods that are excluded from `asgard help` and cannot be invoked directly from the command line.
4
+
5
+ ---
6
+
7
+ ## Private Methods
8
+
9
+ Methods declared after `private` are callable from any task in the same class but are invisible to Thor's command dispatcher. They will not appear in `--help` output and cannot be called from the CLI:
10
+
11
+ ```ruby
12
+ class Tasks
13
+ desc "Compile and package"
14
+ def build
15
+ compile("src")
16
+ package(app_version)
17
+ end
18
+
19
+ desc "Build and publish to RubyGems"
20
+ def release
21
+ build
22
+ sh "gem push pkg/myapp-#{app_version}.gem"
23
+ end
24
+
25
+ private
26
+
27
+ def compile(dir)
28
+ sh "gcc -O2 -o bin/myapp #{dir}/*.c"
29
+ end
30
+
31
+ def package(ver)
32
+ sh "tar czf pkg/myapp-#{ver}.tar.gz bin/"
33
+ end
34
+
35
+ def app_version
36
+ `git describe --tags`.strip
37
+ end
38
+ end
39
+ ```
40
+
41
+ !!! note
42
+ In Ruby, `private` applies to all methods defined after it in the same class body. You can group all helpers at the bottom of the class after a single `private` declaration.
43
+
44
+ ---
45
+
46
+ ## The `no_commands` Block
47
+
48
+ Thor's `no_commands` block marks public methods as excluded from CLI discovery. Unlike `private`, these methods are still publicly accessible from Ruby code (e.g., from a subclass or a module). They are useful for methods that must be public for technical reasons but should not appear as commands:
49
+
50
+ ```ruby
51
+ class Tasks
52
+ desc "Compile the project"
53
+ def build
54
+ puts "Revision: #{current_sha}"
55
+ sh "rake build"
56
+ end
57
+
58
+ desc "Deploy to production"
59
+ def deploy
60
+ puts "Deploying revision #{current_sha}..."
61
+ sh "cap production deploy"
62
+ end
63
+
64
+ no_commands do
65
+ def current_sha
66
+ `git rev-parse --short HEAD`.strip
67
+ end
68
+
69
+ def timestamp
70
+ Time.now.strftime("%Y%m%d-%H%M%S")
71
+ end
72
+ end
73
+ end
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Choosing Between `private` and `no_commands`
79
+
80
+ | | `private` | `no_commands` |
81
+ |---|---|---|
82
+ | Hidden from `--help` | Yes | Yes |
83
+ | Blocked from CLI | Yes | Yes |
84
+ | Accessible from subclass | No | Yes |
85
+ | Accessible from module include | No | Yes |
86
+ | Ruby idiom | Familiar | Thor-specific |
87
+
88
+ For most helpers, `private` is the right choice. Use `no_commands` when the helper must remain technically public (e.g., it will be inherited by a subcommand class).
89
+
90
+ ---
91
+
92
+ ## Sharing Helpers Across Files
93
+
94
+ ### Within a project
95
+
96
+ Extract shared helpers into a plain Ruby module and load it from `.loki` using `require_relative`:
97
+
98
+ ```ruby
99
+ # shared/helpers.rb
100
+ module BuildHelpers
101
+ private
102
+
103
+ def compile(dir)
104
+ sh "gcc -O2 -o bin/myapp #{dir}/*.c"
105
+ end
106
+
107
+ def dist_path(ver)
108
+ "pkg/myapp-#{ver}.tar.gz"
109
+ end
110
+ end
111
+ ```
112
+
113
+ ```ruby
114
+ # .loki
115
+ require_relative "shared/helpers"
116
+
117
+ class Tasks
118
+ include BuildHelpers
119
+
120
+ desc "Compile the project"
121
+ def build = compile("src")
122
+
123
+ desc "Create distribution archive"
124
+ def package = sh "tar czf #{dist_path(app_version)} bin/"
125
+ end
126
+ ```
127
+
128
+ Because `include` in the class body makes the module methods available as instance methods, and they are declared `private` inside the module, they remain invisible to Thor.
129
+
130
+ !!! tip
131
+ 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
+
133
+ ### Across projects with `import_up`
134
+
135
+ 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:
136
+
137
+ ```
138
+ ~/sandbox/
139
+ shared_helpers.loki ← defines helper tasks available to all sub-projects
140
+ projectA/
141
+ .loki
142
+ projectB/
143
+ .loki
144
+ ```
145
+
146
+ ```ruby
147
+ # projectA/.loki (and identically in projectB/.loki)
148
+ import_up "shared_helpers.loki"
149
+
150
+ class Tasks
151
+ # shared tasks are already defined in Tasks by here
152
+ end
153
+ ```
154
+
155
+ `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.
156
+
157
+ ---
158
+
159
+ ## Helper Methods in Subcommands
160
+
161
+ Subcommand classes that inherit from `Tasks` also inherit all private helpers and `no_commands` methods defined on `Tasks`. You can also define helpers local to the subcommand class:
162
+
163
+ ```ruby
164
+ class DeployCommands < Tasks
165
+ desc "Deploy to staging"
166
+ def staging = deploy_to("staging")
167
+
168
+ desc "Deploy to production"
169
+ def production = deploy_to("production")
170
+
171
+ private
172
+
173
+ def deploy_to(env)
174
+ sh "cap #{env} deploy REV=#{current_sha}"
175
+ end
176
+ # current_sha is inherited from Tasks if defined there
177
+ end
178
+ ```
data/docs/index.md ADDED
@@ -0,0 +1,85 @@
1
+ # Asgard
2
+
3
+ <table>
4
+ <tr>
5
+ <td width="40%" align="center" valign="top">
6
+ <img src="assets/images/asgard.jpg" alt="Asgard" width="300"><br>
7
+ <em>"Loki collects the tricks.<br>Thor of Asgard runs them."</em>
8
+ </td>
9
+ <td width="60%" valign="top">
10
+ <strong>Key Features</strong>
11
+ <ul>
12
+ <li><strong>Thor-Powered CLI</strong> — every Thor DSL feature available inside <code>.loki</code> task files</li>
13
+ <li><strong>Task Dependencies</strong> — sequential, parallel, and mixed dependency graphs via <code>depends_on</code></li>
14
+ <li><strong>Concurrent Execution</strong> — parallel task groups run in native Ruby threads</li>
15
+ <li><strong>Subcommands</strong> — group related tasks under a named namespace</li>
16
+ <li><strong>Variables</strong> — shared configuration via Ruby class variables (<code>@@name</code>), visible across all tasks and subcommands</li>
17
+ <li><strong>Shell Helpers</strong> — <code>sh</code> for any shell command or heredoc; <code>shebang</code> for polyglot scripts</li>
18
+ <li><strong>Dotenv Support</strong> — load <code>.env</code> files into the environment with <code>dotenv</code></li>
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 via <code>import</code></li>
21
+ <li><strong>Built-in Flags</strong> — <code>--version</code>, <code>--debug</code>, and <code>--verbose</code> available on every task</li>
22
+ </ul>
23
+ </td>
24
+ </tr>
25
+ </table>
26
+
27
+ Asgard is a [Thor](https://github.com/rails/thor)-based task runner for Ruby projects. Define tasks in `.loki` files, declare dependencies between them, and let Asgard handle ordering and concurrent execution. Anything Thor can do — subcommands, typed options, argument validation — is available inside a `.loki` file.
28
+
29
+ ---
30
+
31
+ ## Quick Start
32
+
33
+ ```bash
34
+ # Install
35
+ gem install asgard
36
+
37
+ # Create your project root marker
38
+ touch .loki
39
+
40
+ # Add your first task
41
+ cat >> .loki << 'EOF'
42
+ class Tasks
43
+ desc "Say hello"
44
+ def hello = puts "Hello from Asgard!"
45
+ end
46
+ EOF
47
+
48
+ # Run it
49
+ asgard hello
50
+ ```
51
+
52
+ ---
53
+
54
+ ## How It Works
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 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
+
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
+
60
+ ---
61
+
62
+ ## Documentation
63
+
64
+ | Section | Description |
65
+ |---|---|
66
+ | [Getting Started](getting-started.md) | Install, create your first `.loki`, run your first task |
67
+ | [Defining Tasks](tasks.md) | Parameters, options, long_desc, aliases, default_task |
68
+ | [Dependencies](dependencies.md) | Sequential, parallel, and mixed dependency graphs |
69
+ | [Variables](variables.md) | Static and lazy-evaluated task variables |
70
+ | [Helper Methods](helpers.md) | Private helpers and the `no_commands` block |
71
+ | [Options & Flags](options.md) | class_option, built-in flags, debug? and verbose? |
72
+ | [Subcommands](subcommands.md) | Grouping tasks under a namespace |
73
+ | [Shell Helpers](shell.md) | `sh`, `shebang`, and supported interpreters |
74
+ | [Environment](environment.md) | Loading `.env` files with `dotenv` |
75
+ | [Task Files](task-files.md) | `.loki` root marker, `--auto-load`, multi-file layout |
76
+ | [API Reference](api.md) | Module methods, DSL methods, error classes |
77
+ | [Examples](examples.md) | Working `.loki` files for every feature |
78
+ | [Changelog](changelog.md) | Release history |
79
+
80
+ ---
81
+
82
+ ## Requirements
83
+
84
+ - Ruby >= 3.2.0
85
+ - Dependencies: [thor](https://github.com/rails/thor) `~> 1.0`, [dagwood](https://rubygems.org/gems/dagwood) `~> 1.0`, [dotenv](https://github.com/bkeepers/dotenv) `~> 3.0`