asgard 0.5.1 → 0.6.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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +179 -99
  3. data/README.md +141 -46
  4. data/bin/asgard +10 -0
  5. data/lib/asgard/base/dispatch.rb +257 -31
  6. data/lib/asgard/base/registry.rb +7 -3
  7. data/lib/asgard/base.rb +140 -9
  8. data/lib/asgard/declarations.rb +21 -0
  9. data/lib/asgard/doctor/task_sections.rb +11 -3
  10. data/lib/asgard/doctor.rb +12 -8
  11. data/lib/asgard/jobs/commands.rb +152 -0
  12. data/lib/asgard/jobs/declaration.rb +149 -0
  13. data/lib/asgard/jobs/launcher.rb +109 -0
  14. data/lib/asgard/jobs/state.rb +190 -0
  15. data/lib/asgard/jobs/stopper.rb +141 -0
  16. data/lib/asgard/jobs.rb +25 -0
  17. data/lib/asgard/kernel_methods.rb +19 -14
  18. data/lib/asgard/leading_flag.rb +10 -0
  19. data/lib/asgard/pid.rb +130 -0
  20. data/lib/asgard/schedule/commands.rb +39 -11
  21. data/lib/asgard/schedule/declaration.rb +139 -12
  22. data/lib/asgard/schedule/job_files.rb +24 -0
  23. data/lib/asgard/schedule/launchd.rb +55 -18
  24. data/lib/asgard/schedule/systemd.rb +69 -27
  25. data/lib/asgard/schedule/table.rb +34 -16
  26. data/lib/asgard/schedule.rb +49 -9
  27. data/lib/asgard/shell.rb +372 -44
  28. data/lib/asgard/state_dir.rb +84 -0
  29. data/lib/asgard/tasks.rb +8 -0
  30. data/lib/asgard/version.rb +1 -1
  31. data/lib/asgard/watch/changes.rb +26 -1
  32. data/lib/asgard/watch/commands.rb +52 -12
  33. data/lib/asgard/watch/daemon.rb +334 -43
  34. data/lib/asgard/watch/declaration.rb +135 -40
  35. data/lib/asgard/watch/dispatcher.rb +61 -10
  36. data/lib/asgard/watch/mailbox.rb +50 -0
  37. data/lib/asgard/watch/reporter.rb +50 -34
  38. data/lib/asgard/watch/spawn.rb +41 -16
  39. data/lib/asgard/watch/state.rb +93 -27
  40. data/lib/asgard/watch/worker.rb +44 -9
  41. data/lib/asgard/watch.rb +4 -11
  42. data/lib/asgard.rb +80 -14
  43. metadata +167 -186
  44. data/.envrc +0 -6
  45. data/.github/workflows/deploy-github-pages.yml +0 -52
  46. data/.loki +0 -39
  47. data/.reek.yml +0 -166
  48. data/.rubocop.yml +0 -163
  49. data/Archspec.rb +0 -24
  50. data/CLAUDE.md +0 -139
  51. data/COMMITS.md +0 -196
  52. data/bin/console +0 -11
  53. data/bin/setup +0 -8
  54. data/doc_tasks.loki +0 -21
  55. data/docs/api.md +0 -368
  56. data/docs/assets/css/custom.css +0 -93
  57. data/docs/assets/images/asgard.jpg +0 -0
  58. data/docs/changelog.md +0 -1
  59. data/docs/dependencies.md +0 -313
  60. data/docs/environment.md +0 -129
  61. data/docs/examples.md +0 -194
  62. data/docs/getting-started.md +0 -181
  63. data/docs/helpers.md +0 -262
  64. data/docs/index.md +0 -87
  65. data/docs/options.md +0 -224
  66. data/docs/schedule.md +0 -106
  67. data/docs/shell.md +0 -237
  68. data/docs/subcommands.md +0 -181
  69. data/docs/task-files.md +0 -407
  70. data/docs/tasks.md +0 -326
  71. data/docs/variables.md +0 -338
  72. data/docs/watch.md +0 -123
  73. data/examples/.env +0 -4
  74. data/examples/.envrc +0 -3
  75. data/examples/.loki +0 -24
  76. data/examples/bad.loki +0 -63
  77. data/examples/concurrent.loki +0 -58
  78. data/examples/confirm.loki +0 -48
  79. data/examples/db_subcommands.loki +0 -74
  80. data/examples/depends_on_block/bad/.loki +0 -51
  81. data/examples/depends_on_block/good/.loki +0 -83
  82. data/examples/do.loki +0 -45
  83. data/examples/env_usage.loki +0 -27
  84. data/examples/kitchen_sink.loki +0 -210
  85. data/examples/server_subcommands.loki +0 -65
  86. data/examples/subdir/.loki +0 -12
  87. data/examples/subdir/import_demo.loki +0 -14
  88. data/examples/subdir/import_up_demo.loki +0 -18
  89. data/examples/watcher_examples/.envrc +0 -3
  90. data/examples/watcher_examples/.loki +0 -54
  91. data/examples/watcher_examples/README.md +0 -112
  92. data/examples/watcher_examples/bin/hello +0 -3
  93. data/examples/watcher_examples/data/records.json +0 -1
  94. data/examples/watcher_examples/docs/drafts/ideas.md +0 -5
  95. data/examples/watcher_examples/docs/index.md +0 -4
  96. data/examples/watcher_examples/lib/greeter.rb +0 -7
  97. data/gem_tasks.loki +0 -51
  98. data/git.loki +0 -13
  99. data/mkdocs.yml +0 -166
  100. data/quality.loki +0 -266
  101. data/quality_rails.loki +0 -47
  102. data/xyzzy.loki +0 -12
data/mkdocs.yml DELETED
@@ -1,166 +0,0 @@
1
- # MkDocs Configuration for Asgard Documentation
2
- site_name: Asgard
3
- site_description: Thor-based Ruby task runner with dependency graphs and concurrent execution
4
- site_author: Dewayne VanHoozer
5
- site_url: https://madbomber.github.io/asgard
6
- copyright: Copyright © 2026 Dewayne VanHoozer
7
-
8
- # Repository information
9
- repo_name: madbomber/asgard
10
- repo_url: https://github.com/MadBomber/asgard
11
- edit_uri: edit/main/docs/
12
-
13
- # Configuration
14
- theme:
15
- name: material
16
-
17
- # Color scheme
18
- palette:
19
- - scheme: default
20
- primary: indigo
21
- accent: amber
22
- toggle:
23
- icon: material/brightness-7
24
- name: Switch to dark mode
25
-
26
- - scheme: slate
27
- primary: indigo
28
- accent: amber
29
- toggle:
30
- icon: material/brightness-4
31
- name: Switch to light mode
32
-
33
- # Typography
34
- font:
35
- text: Roboto
36
- code: Roboto Mono
37
-
38
- # Logo and icon
39
- icon:
40
- repo: fontawesome/brands/github
41
- logo: material/shield
42
-
43
- # Theme features
44
- features:
45
- - navigation.instant
46
- - navigation.tracking
47
- - navigation.tabs
48
- - navigation.tabs.sticky
49
- - navigation.path
50
- - navigation.indexes
51
- - navigation.top
52
- - navigation.footer
53
- - toc.follow
54
- - search.suggest
55
- - search.highlight
56
- - search.share
57
- - header.autohide
58
- - content.code.copy
59
- - content.code.annotate
60
- - content.tabs.link
61
- - content.tooltips
62
- - content.action.edit
63
- - content.action.view
64
-
65
- # Plugins
66
- plugins:
67
- - search:
68
- separator: '[\s\-,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])'
69
- - tags
70
-
71
- # Extensions
72
- markdown_extensions:
73
- - abbr
74
- - admonition
75
- - attr_list
76
- - def_list
77
- - footnotes
78
- - md_in_html
79
- - tables
80
- - toc:
81
- permalink: true
82
- title: On this page
83
-
84
- - pymdownx.arithmatex:
85
- generic: true
86
- - pymdownx.betterem:
87
- smart_enable: all
88
- - pymdownx.caret
89
- - pymdownx.critic
90
- - pymdownx.details
91
- - pymdownx.emoji:
92
- emoji_generator: !!python/name:material.extensions.emoji.to_svg
93
- emoji_index: !!python/name:material.extensions.emoji.twemoji
94
- - pymdownx.highlight:
95
- anchor_linenums: true
96
- line_spans: __span
97
- pygments_lang_class: true
98
- - pymdownx.inlinehilite
99
- - pymdownx.keys
100
- - pymdownx.magiclink:
101
- repo_url_shorthand: true
102
- user: madbomber
103
- repo: asgard
104
- normalize_issue_symbols: true
105
- - pymdownx.mark
106
- - pymdownx.smartsymbols
107
- - pymdownx.snippets:
108
- check_paths: true
109
- - pymdownx.superfences:
110
- custom_fences:
111
- - name: mermaid
112
- class: mermaid
113
- format: !!python/name:pymdownx.superfences.fence_code_format
114
- - pymdownx.tabbed:
115
- alternate_style: true
116
- - pymdownx.tasklist:
117
- custom_checkbox: true
118
- - pymdownx.tilde
119
-
120
- # Extra CSS
121
- extra_css:
122
- - assets/css/custom.css
123
-
124
- # Social media and extra configuration
125
- extra:
126
- social:
127
- - icon: fontawesome/brands/github
128
- link: https://github.com/MadBomber/asgard
129
- name: Asgard on GitHub
130
- - icon: fontawesome/solid/gem
131
- link: https://rubygems.org/gems/asgard
132
- name: Asgard on RubyGems
133
-
134
- analytics:
135
- feedback:
136
- title: Was this page helpful?
137
- ratings:
138
- - icon: material/emoticon-happy-outline
139
- name: This page was helpful
140
- data: 1
141
- note: Thanks for your feedback!
142
- - icon: material/emoticon-sad-outline
143
- name: This page could be improved
144
- data: 0
145
- note: Thanks for your feedback! Help us improve by creating an issue.
146
-
147
- # Navigation
148
- nav:
149
- - Home: index.md
150
- - Getting Started: getting-started.md
151
- - Tasks:
152
- - Defining Tasks: tasks.md
153
- - Dependencies: dependencies.md
154
- - Variables: variables.md
155
- - Helper Methods: helpers.md
156
- - CLI:
157
- - Options & Flags: options.md
158
- - Subcommands: subcommands.md
159
- - Shell Helpers: shell.md
160
- - Environment: environment.md
161
- - Scheduled Tasks: schedule.md
162
- - Watched Files: watch.md
163
- - Task Files: task-files.md
164
- - API Reference: api.md
165
- - Examples: examples.md
166
- - Changelog: changelog.md
data/quality.loki DELETED
@@ -1,266 +0,0 @@
1
- # frozen_string_literal: true
2
- # Quality gate tasks — imported by .loki
3
-
4
- require "shellwords"
5
-
6
- class Tasks
7
- # One process for every test/test_*.rb, so SimpleCov sees the whole suite.
8
- TEST_LOADER = %q(Dir["test/test_*.rb"].each { |f| require File.expand_path(f) }).shellescape.freeze
9
-
10
- desc "Run the test suite"
11
- def test_check
12
- output = `bundle exec ruby -Ilib:test -e #{TEST_LOADER} 2>&1`
13
- result = $?.success? ? :pass : :fail
14
- File.write("test_output.txt", output)
15
- summary = output.lines.reverse.find { |l| l =~ /\d+ runs,/ }&.strip || result.to_s
16
- puts "Tests: #{summary} (see test_output.txt)"
17
- result
18
- end
19
-
20
- desc "Run the test suite with verbose output"
21
- def test_verbose
22
- sh "bundle exec ruby -Ilib:test -e #{TEST_LOADER} -- -v"
23
- end
24
-
25
- desc "Run every *_check quality gate task in parallel"
26
- # A Proc defers resolution to validate_deps!, after every .loki file has
27
- # loaded — so a *_check task from another file (e.g. quality_rails.loki's
28
- # brakeman_check) is picked up too, with no need to redeclare anything here.
29
- depends_on -> { [all_commands.keys.grep(/_check\z/).sort.map(&:to_sym)] }
30
- def quality
31
- check_tasks = self.class._deps.fetch(:quality, []).flatten.map(&:to_s)
32
-
33
- results = check_tasks.to_h { |name| [quality_gate_label(name), dep_result(name)] }
34
-
35
- print_quality_summary(results)
36
- end
37
-
38
- desc "Check code style with RuboCop"
39
- def rubocop_check
40
- output = `RUBOCOP_CACHE_ROOT=tmp/rubocop_cache bundle exec rubocop 2>&1`
41
- result = $?.success? ? :pass : :fail
42
- File.write("rubocop_output.txt", output)
43
- summary = output.lines.find { |l| l =~ /files inspected/ }&.strip || result.to_s
44
- puts "RuboCop: #{summary} (see rubocop_output.txt)"
45
- result
46
- end
47
-
48
- desc "Auto-correct RuboCop offenses"
49
- def rubocop_fix
50
- sh "RUBOCOP_CACHE_ROOT=tmp/rubocop_cache bundle exec rubocop -a"
51
- end
52
-
53
- desc "Check code complexity with Flog (warn >=20, fail >=50)"
54
- def flog_check
55
- require "flog"
56
-
57
- method_warn = 20.0
58
- method_fail = 50.0
59
-
60
- flogger = Flog.new(all: true)
61
- flogger.flog(*Dir.glob("lib/**/*.rb"))
62
-
63
- warnings = []
64
- failures = []
65
-
66
- flogger.each_by_score do |method_name, score|
67
- next if method_name.end_with?("#none")
68
- if score > method_fail
69
- failures << "#{"%.1f" % score}: #{method_name}"
70
- elsif score > method_warn
71
- warnings << "#{"%.1f" % score}: #{method_name}"
72
- end
73
- end
74
-
75
- result = failures.empty? ? :pass : :fail
76
-
77
- lines = ["Flog warnings (#{method_warn}–#{method_fail}):"]
78
- lines.concat(warnings.empty? ? [" (none)"] : warnings.map { |w| " #{w}" })
79
- lines << ""
80
- lines << "Flog failures (>= #{method_fail}):"
81
- lines.concat(failures.empty? ? [" (none)"] : failures.map { |f| " #{f}" })
82
- File.write("flog_output.txt", "#{lines.join("\n")}\n")
83
-
84
- puts "Flog: #{failures.size} failure(s), #{warnings.size} warning(s) (see flog_output.txt)"
85
- result
86
- end
87
-
88
- desc "Check for structural code duplication with Flay (mass >= 150)"
89
- def flay_check
90
- require "flay"
91
-
92
- mass_threshold = 150
93
-
94
- flay = Flay.new(mass: mass_threshold, diff: false, verbose: false, summary: false, timeout: 60)
95
- flay.process(*Dir.glob("lib/**/*.rb"))
96
- flay.analyze
97
-
98
- result = flay.hashes.empty? ? :pass : :fail
99
-
100
- File.open("flay_output.txt", "w") do |f|
101
- if result == :pass
102
- f.puts "Flay: no structural duplication detected (mass >= #{mass_threshold})"
103
- else
104
- flay.report(f)
105
- end
106
- end
107
- puts "Flay: #{flay.hashes.size} duplication pattern(s) found (mass >= #{mass_threshold}) (see flay_output.txt)"
108
- result
109
- end
110
-
111
- desc "Check code smells with Reek"
112
- def reek_check
113
- output = `bundle exec reek lib 2>&1`
114
- result = $?.success? ? :pass : :fail
115
- File.write("reek_output.txt", output)
116
- summary = output.lines.reverse.find { |l| l =~ /total warnings?/ }&.strip || result.to_s
117
- puts "Reek: #{summary} (see reek_output.txt)"
118
- result
119
- end
120
-
121
- desc "Check spelling with typos"
122
- def typos_check
123
- return skip_typos unless command_available?("typos")
124
-
125
- output = `typos 2>&1`
126
- result = $?.success? ? :pass : :fail
127
- File.write("typos_output.txt", output)
128
- count = output.lines.count { |l| l.start_with?("error:") }
129
- summary = result == :pass ? "no typos found" : "#{count} typo(s) found"
130
- puts "Typos: #{summary} (see typos_output.txt)"
131
- result
132
- end
133
-
134
- desc "Auto-correct typos"
135
- def typos_fix
136
- return skip_typos unless command_available?("typos")
137
-
138
- sh "typos -w"
139
- end
140
-
141
- desc "Check ERB templates for style and safety issues with ERBLint"
142
- def erb_lint_check
143
- return skip_erb_lint if Dir.glob("**/*.erb").empty?
144
-
145
- output = `bundle exec erb_lint --lint-all --enable-all-linters 2>&1`
146
- result = $?.success? ? :pass : :fail
147
- File.write("erb_lint_output.txt", output)
148
- summary = output.lines.reverse.find { |l| l =~ /error\(s\)|no errors were found/i }&.strip || result.to_s
149
- puts "ERBLint: #{summary} (see erb_lint_output.txt)"
150
- result
151
- end
152
-
153
- desc "Check for performance suggestions with Fasterer"
154
- def fasterer_check
155
- output = `bundle exec fasterer lib 2>&1`.gsub(/\e\[\d+m/, "")
156
- result = $?.success? ? :pass : :warn
157
- File.write("fasterer_output.txt", output)
158
- summary = output.lines.find { |l| l =~ /files? inspected/ }&.strip || result.to_s
159
- puts "Fasterer: #{summary} (see fasterer_output.txt)"
160
- result
161
- end
162
-
163
- desc "Check Gemfile.lock for known vulnerabilities with bundler-audit"
164
- def bundler_audit_check
165
- output = `bundle exec bundle-audit check --update 2>&1`
166
- result = $?.success? ? :pass : :fail
167
- File.write("bundler_audit_output.txt", output)
168
- summary = output.lines.reverse.find { |l| l =~ /vulnerabilit/i }&.strip || result.to_s
169
- puts "Bundler Audit: #{summary} (see bundler_audit_output.txt)"
170
- result
171
- end
172
-
173
- desc "Check architecture boundaries with ArchSpec"
174
- def archspec_check
175
- return skip_archspec unless command_available?("archspec") && File.exist?("Archspec.rb")
176
-
177
- output = `archspec check 2>&1`
178
- result = $?.success? ? :pass : :fail
179
- File.write("archspec_output.txt", output)
180
- summary = output.lines.reverse.find { |l| l =~ /architecture violations? found|ArchSpec passed/i }&.strip ||
181
- result.to_s
182
- puts "ArchSpec: #{summary} (see archspec_output.txt)"
183
- result
184
- end
185
-
186
- # Curated display labels for the *_check tasks defined in this file. A
187
- # *_check task from elsewhere (e.g. quality_rails.loki's brakeman_check,
188
- # loaded automatically once Rails is defined) isn't listed here — it just
189
- # falls back to a titleized version of its own name in quality_gate_label.
190
- QUALITY_GATE_LABELS = {
191
- "test_check" => "Tests + Coverage",
192
- "rubocop_check" => "RuboCop",
193
- "flog_check" => "Flog Complexity",
194
- "flay_check" => "Flay Duplication",
195
- "reek_check" => "Reek Smells",
196
- "erb_lint_check" => "ERBLint",
197
- "archspec_check" => "ArchSpec",
198
- }.freeze
199
-
200
- no_commands do
201
- # true if +name+ resolves to an executable file somewhere on PATH.
202
- def command_available?(name)
203
- ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).any? do |dir|
204
- File.executable?(File.join(dir, name))
205
- end
206
- end
207
-
208
- def quality_gate_label(check_name)
209
- QUALITY_GATE_LABELS.fetch(check_name) do
210
- check_name.delete_suffix("_check").split("_").map(&:capitalize).join(" ")
211
- end
212
- end
213
-
214
- def skip_typos
215
- puts "Typos: SKIPPED — `typos` command not found. Install it with `brew install typos-cli`."
216
- :skip
217
- end
218
-
219
- def skip_erb_lint
220
- puts "ERBLint: SKIPPED — no .erb files found."
221
- :skip
222
- end
223
-
224
- def skip_archspec
225
- reason = command_available?("archspec") ? "no Archspec.rb found" : "`archspec` command not found"
226
- puts "ArchSpec: SKIPPED — #{reason}. Install it with `gem install archspec` " \
227
- "and run `archspec init` to generate one."
228
- :skip
229
- end
230
-
231
- # Status badges. Only :fail is blocking (aborts the quality task);
232
- # :skip (a required tool isn't installed) and :warn (the check ran but
233
- # has non-blocking suggestions, e.g. a performance-hint gate) are not.
234
- STATUS_BADGES = {
235
- pass: ["PASS", 32], # green
236
- fail: ["FAIL", 31], # red
237
- warn: ["WARN", 33], # yellow
238
- skip: ["SKIP", 36], # cyan
239
- }.freeze
240
-
241
- # Colorized status badges plus a pass/fail/warn/skip tally; aborts only
242
- # if any gate failed — skip and warn are informational, not blocking.
243
- def print_quality_summary(results)
244
- colorize_badge = ->(code, text) { "\e[#{code}m#{text}\e[0m" }
245
- width = results.keys.map(&:length).max
246
-
247
- puts "\n#{"=" * 60}"
248
- puts "Quality Gate Summary"
249
- puts "=" * 60
250
- results.each do |label, status|
251
- text, code = STATUS_BADGES.fetch(status)
252
- puts " [#{colorize_badge.call(code, text)}] #{label.ljust(width)}"
253
- end
254
- puts "-" * 60
255
-
256
- counts = results.values.tally
257
- failed = counts[:fail] || 0
258
- tally = STATUS_BADGES.keys.filter_map { |s| "#{counts[s]} #{s}" if counts[s]&.positive? }.join(", ")
259
- puts " #{colorize_badge.call(failed.zero? ? 32 : 31, tally)}"
260
- puts "=" * 60
261
-
262
- abort "\n#{colorize_badge.call(31, "Quality gate failed.")}" unless failed.zero?
263
- puts "\n#{colorize_badge.call(32, "All quality gates passed.")}"
264
- end
265
- end
266
- end
data/quality_rails.loki DELETED
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
- # Rails-specific quality gate tasks — imported by .loki only when RAILS_ROOT
3
- # is set. asgard runs as a separate process outside the app it's checking,
4
- # so `defined?(Rails)` never sees the app's Rails constant.
5
- #
6
- # `quality` (in quality.loki) discovers every *_check task at run time by
7
- # introspecting Tasks.all_commands, rather than a fixed depends_on list — so
8
- # any *_check task defined here is automatically picked up by `asgard
9
- # quality` too, with no need to redeclare or extend anything.
10
- #
11
- # Note on rubocop-rails: it's a RuboCop plugin, not a standalone CLI, so it
12
- # has no *_check task of its own here. Add the gem to the app's Gemfile and
13
- # `require: rubocop-rails` in its .rubocop.yml — the existing rubocop_check
14
- # (quality.loki) picks up the added Rails cops automatically.
15
-
16
- class Tasks
17
- desc "Check for Rails security vulnerabilities with Brakeman"
18
- def brakeman_check
19
- output = `bundle exec brakeman -q 2>&1`
20
- result = $?.success? ? :pass : :fail
21
- File.write("brakeman_output.txt", output)
22
- summary = output.lines.reverse.find { |l| l =~ /warnings? found/i }&.strip || result.to_s
23
- puts "Brakeman: #{summary} (see brakeman_output.txt)"
24
- result
25
- end
26
-
27
- desc "Check for Rails anti-patterns with RailsBestPractices"
28
- def rails_best_practices_check
29
- output = `bundle exec rails_best_practices --silent --without-color . 2>&1`
30
- result = $?.success? ? :pass : :fail
31
- File.write("rails_best_practices_output.txt", output)
32
- summary = output.lines.reverse.find { |l| l =~ /warning/i }&.strip || result.to_s
33
- puts "RailsBestPractices: #{summary} (see rails_best_practices_output.txt)"
34
- result
35
- end
36
-
37
- desc "Check for database schema issues with ActiveRecordDoctor"
38
- def active_record_doctor_check
39
- output = `bundle exec rake active_record_doctor 2>&1`
40
- result = $?.success? ? :pass : :fail
41
- File.write("active_record_doctor_output.txt", output)
42
- issue_count = output.lines.count { |l| l.strip != "" }
43
- summary = result == :pass ? "no issues found" : "#{issue_count} issue line(s) found"
44
- puts "ActiveRecordDoctor: #{summary} (see active_record_doctor_output.txt)"
45
- result
46
- end
47
- end
data/xyzzy.loki DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
- # example of an overwritten task detected by (--doctor) — imported by .loki
3
-
4
- class Tasks
5
- desc "Push the current branch to its remote"
6
- def xyzzy = puts <<~MAGIC
7
- You are at a fork in the yellow brick road to AI.
8
- To the left is python, the favorite programming language of snake charmers.
9
- To the right is Ruby, the best computer programming language of all time.
10
- Which way do you go?
11
- MAGIC
12
- end