legionio 1.4.103 → 1.4.104
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 +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +11 -0
- data/lib/legion/cli/notebook_command.rb +188 -37
- data/lib/legion/notebook/generator.rb +81 -0
- data/lib/legion/notebook/parser.rb +44 -0
- data/lib/legion/notebook/renderer.rb +73 -0
- data/lib/legion/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 12afbe73936b06db9067ffa5e4fc445fe52c76687cd13aedbdb4b390a5515d7f
|
|
4
|
+
data.tar.gz: 960ca1400fe432d4ee1dbe51658afaa2e988982b178da080b8f20795bc42fa7e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ead15dbef330a1329a4b5a7b973235fa0d45d84b95cbaf5d7e0415ebde0d19154ef836fc6a84753780dd0270595bc087159a821b5cf06ed737b911a5707a054
|
|
7
|
+
data.tar.gz: 61edda120bb39a6868df425f5acb2ba39e602708c9959c5c4ad54ad8d6310ee15e72bbcb024f38907cc46136b5d28548af45e4752cb66df6b46b676fcb6a8a45
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.104] - 2026-03-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `legion notebook read PATH` — parse and display a .ipynb notebook with Rouge syntax highlighting
|
|
7
|
+
- `legion notebook cells PATH` — list all cells with index numbers and line counts
|
|
8
|
+
- `legion notebook export PATH --format md|script` — export notebook to markdown or Python script
|
|
9
|
+
- `legion notebook create PATH --description "..."` — generate a new notebook from natural language via LLM (requires legion-llm)
|
|
10
|
+
- `Legion::Notebook::Parser` — parse .ipynb JSON into structured data (metadata, kernel, language, cells with outputs)
|
|
11
|
+
- `Legion::Notebook::Renderer` — display notebook cells in terminal with Rouge syntax highlighting
|
|
12
|
+
- `Legion::Notebook::Generator` — generate notebooks from natural language; strips LLM markdown fences; validates .ipynb structure
|
|
13
|
+
|
|
3
14
|
## [1.4.103] - 2026-03-21
|
|
4
15
|
|
|
5
16
|
### Added
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'legion/cli/output'
|
|
6
|
+
require 'legion/cli/error'
|
|
7
|
+
require 'legion/cli/connection'
|
|
4
8
|
|
|
5
9
|
module Legion
|
|
6
10
|
module CLI
|
|
@@ -9,60 +13,207 @@ module Legion
|
|
|
9
13
|
true
|
|
10
14
|
end
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
|
|
17
|
+
class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
|
|
18
|
+
class_option :verbose, type: :boolean, default: false, aliases: ['-V'], desc: 'Verbose logging'
|
|
19
|
+
class_option :config_dir, type: :string, desc: 'Config directory path'
|
|
20
|
+
|
|
21
|
+
desc 'read PATH', 'Parse and display a Jupyter notebook with syntax highlighting'
|
|
13
22
|
def read(path)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
out = formatter
|
|
24
|
+
load_notebook(path, out)
|
|
25
|
+
color = !options[:no_color]
|
|
26
|
+
|
|
27
|
+
require 'legion/notebook/parser'
|
|
28
|
+
require 'legion/notebook/renderer'
|
|
29
|
+
|
|
30
|
+
parsed = Legion::Notebook::Parser.parse(path)
|
|
31
|
+
rendered = Legion::Notebook::Renderer.render_notebook(parsed, color: color)
|
|
32
|
+
|
|
33
|
+
if options[:json]
|
|
34
|
+
out.json(cells: parsed[:cells].length, kernel: parsed[:kernel], path: path)
|
|
35
|
+
else
|
|
36
|
+
puts rendered
|
|
37
|
+
out.spacer
|
|
38
|
+
count = parsed[:cells].length
|
|
39
|
+
puts "#{count} cell#{'s' unless count == 1} total"
|
|
23
40
|
end
|
|
24
|
-
|
|
41
|
+
rescue CLI::Error => e
|
|
42
|
+
formatter.error(e.message)
|
|
43
|
+
raise SystemExit, 1
|
|
25
44
|
end
|
|
26
45
|
|
|
27
|
-
desc '
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
desc 'cells PATH', 'List all cells with index numbers and types'
|
|
47
|
+
def cells(path)
|
|
48
|
+
out = formatter
|
|
49
|
+
load_notebook(path, out)
|
|
50
|
+
|
|
51
|
+
require 'legion/notebook/parser'
|
|
52
|
+
|
|
53
|
+
parsed = Legion::Notebook::Parser.parse(path)
|
|
54
|
+
color = !options[:no_color]
|
|
55
|
+
|
|
56
|
+
if options[:json]
|
|
57
|
+
cell_list = parsed[:cells].each_with_index.map do |cell, i|
|
|
58
|
+
{ index: i + 1, type: cell[:type], lines: cell[:source].lines.count }
|
|
39
59
|
end
|
|
60
|
+
out.json(cells: cell_list, total: parsed[:cells].length)
|
|
40
61
|
else
|
|
41
|
-
cells.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
parsed[:cells].each_with_index do |cell, i|
|
|
63
|
+
lines = cell[:source].lines.count
|
|
64
|
+
plural = lines == 1 ? '' : 's'
|
|
65
|
+
label = " [#{(i + 1).to_s.rjust(2)}] #{cell[:type].to_s.ljust(8)} #{lines} line#{plural}"
|
|
66
|
+
if color
|
|
67
|
+
type_color = cell[:type] == 'code' ? "\e[36m" : "\e[33m"
|
|
68
|
+
puts "#{type_color}#{label}\e[0m"
|
|
46
69
|
else
|
|
47
|
-
|
|
70
|
+
puts label
|
|
48
71
|
end
|
|
49
|
-
say ''
|
|
50
72
|
end
|
|
73
|
+
out.spacer
|
|
74
|
+
puts "Total: #{parsed[:cells].length} cell#{'s' unless parsed[:cells].length == 1}"
|
|
51
75
|
end
|
|
76
|
+
rescue CLI::Error => e
|
|
77
|
+
formatter.error(e.message)
|
|
78
|
+
raise SystemExit, 1
|
|
52
79
|
end
|
|
53
80
|
|
|
54
|
-
|
|
81
|
+
desc 'export PATH', 'Export notebook to another format'
|
|
82
|
+
option :format, type: :string, default: 'md', enum: %w[md markdown script], desc: 'Export format: md or script'
|
|
83
|
+
option :output, type: :string, aliases: ['-o'], desc: 'Write to file instead of stdout'
|
|
84
|
+
def export(path)
|
|
85
|
+
out = formatter
|
|
86
|
+
load_notebook(path, out)
|
|
87
|
+
|
|
88
|
+
require 'legion/notebook/parser'
|
|
55
89
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
90
|
+
parsed = Legion::Notebook::Parser.parse(path)
|
|
91
|
+
lang = parsed[:language]
|
|
92
|
+
|
|
93
|
+
content = case options[:format]
|
|
94
|
+
when 'script'
|
|
95
|
+
export_as_script(parsed[:cells], lang)
|
|
96
|
+
else
|
|
97
|
+
export_as_markdown(parsed[:cells], lang)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if options[:output]
|
|
101
|
+
File.write(options[:output], content)
|
|
102
|
+
out.success("Exported to #{options[:output]}")
|
|
103
|
+
elsif options[:json]
|
|
104
|
+
out.json(content: content, format: options[:format], path: path)
|
|
105
|
+
else
|
|
106
|
+
puts content
|
|
107
|
+
end
|
|
108
|
+
rescue CLI::Error => e
|
|
109
|
+
formatter.error(e.message)
|
|
110
|
+
raise SystemExit, 1
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
desc 'create PATH', 'Generate a Jupyter notebook from a natural language description (requires legion-llm)'
|
|
114
|
+
option :description, type: :string, aliases: ['-d'], desc: 'What the notebook should do'
|
|
115
|
+
option :kernel, type: :string, default: 'python3', desc: 'Kernel name (default: python3)'
|
|
116
|
+
option :model, type: :string, aliases: ['-m'], desc: 'LLM model override'
|
|
117
|
+
option :provider, type: :string, desc: 'LLM provider override'
|
|
118
|
+
def create(path)
|
|
119
|
+
out = formatter
|
|
120
|
+
setup_llm_connection(out)
|
|
121
|
+
|
|
122
|
+
require 'legion/notebook/generator'
|
|
123
|
+
|
|
124
|
+
description = options[:description]
|
|
125
|
+
if description.nil? || description.strip.empty?
|
|
126
|
+
out.error('--description is required for notebook creation')
|
|
59
127
|
raise SystemExit, 1
|
|
60
128
|
end
|
|
61
129
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
130
|
+
out.success("Generating notebook: #{description}") unless options[:json]
|
|
131
|
+
|
|
132
|
+
notebook_data = Legion::Notebook::Generator.generate(
|
|
133
|
+
description: description,
|
|
134
|
+
kernel: options[:kernel],
|
|
135
|
+
model: options[:model],
|
|
136
|
+
provider: options[:provider]
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
Legion::Notebook::Generator.write(path, notebook_data)
|
|
140
|
+
cell_count = Array(notebook_data['cells']).length
|
|
141
|
+
|
|
142
|
+
if options[:json]
|
|
143
|
+
out.json(path: path, cells: cell_count, kernel: options[:kernel])
|
|
144
|
+
else
|
|
145
|
+
out.success("Created #{path} (#{cell_count} cells)")
|
|
146
|
+
end
|
|
147
|
+
rescue ArgumentError, CLI::Error => e
|
|
148
|
+
formatter.error(e.message)
|
|
65
149
|
raise SystemExit, 1
|
|
150
|
+
ensure
|
|
151
|
+
Connection.shutdown
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
no_commands do
|
|
155
|
+
def formatter
|
|
156
|
+
@formatter ||= Output::Formatter.new(
|
|
157
|
+
json: options[:json],
|
|
158
|
+
color: !options[:no_color]
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def setup_llm_connection(out)
|
|
163
|
+
Connection.config_dir = options[:config_dir] if options[:config_dir]
|
|
164
|
+
Connection.log_level = options[:verbose] ? 'debug' : 'error'
|
|
165
|
+
Connection.ensure_llm
|
|
166
|
+
rescue CLI::Error => e
|
|
167
|
+
out.error(e.message)
|
|
168
|
+
raise SystemExit, 1
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def load_notebook(path, out)
|
|
172
|
+
unless File.exist?(path)
|
|
173
|
+
out.error("File not found: #{path}")
|
|
174
|
+
raise SystemExit, 1
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
unless path.end_with?('.ipynb')
|
|
178
|
+
out.error("Expected a .ipynb file, got: #{File.basename(path)}")
|
|
179
|
+
raise SystemExit, 1
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
::JSON.parse(File.read(path))
|
|
183
|
+
rescue ::JSON::ParserError => e
|
|
184
|
+
out.error("Invalid notebook JSON: #{e.message}")
|
|
185
|
+
raise SystemExit, 1
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def export_as_markdown(cells, lang)
|
|
189
|
+
lines = []
|
|
190
|
+
cells.each do |cell|
|
|
191
|
+
if cell[:type] == 'code'
|
|
192
|
+
lines << "```#{lang}"
|
|
193
|
+
lines << cell[:source]
|
|
194
|
+
lines << '```'
|
|
195
|
+
else
|
|
196
|
+
lines << cell[:source]
|
|
197
|
+
end
|
|
198
|
+
lines << ''
|
|
199
|
+
end
|
|
200
|
+
lines.join("\n")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def export_as_script(cells, _lang)
|
|
204
|
+
lines = []
|
|
205
|
+
cells.each do |cell|
|
|
206
|
+
if cell[:type] == 'code'
|
|
207
|
+
lines << cell[:source]
|
|
208
|
+
else
|
|
209
|
+
cell[:source].each_line do |line|
|
|
210
|
+
lines << "# #{line.chomp}"
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
lines << ''
|
|
214
|
+
end
|
|
215
|
+
lines.join("\n")
|
|
216
|
+
end
|
|
66
217
|
end
|
|
67
218
|
end
|
|
68
219
|
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Notebook
|
|
7
|
+
module Generator
|
|
8
|
+
NOTEBOOK_TEMPLATE = {
|
|
9
|
+
'nbformat' => 4,
|
|
10
|
+
'nbformat_minor' => 5,
|
|
11
|
+
'metadata' => {
|
|
12
|
+
'kernelspec' => {
|
|
13
|
+
'display_name' => 'Python 3',
|
|
14
|
+
'language' => 'python',
|
|
15
|
+
'name' => 'python3'
|
|
16
|
+
},
|
|
17
|
+
'language_info' => {
|
|
18
|
+
'name' => 'python'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
'cells' => []
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
def self.generate(description:, kernel: 'python3', model: nil, provider: nil)
|
|
25
|
+
raise ArgumentError, 'legion-llm is required for notebook generation' unless defined?(Legion::LLM)
|
|
26
|
+
|
|
27
|
+
prompt = build_prompt(description, kernel)
|
|
28
|
+
response = call_llm(prompt, model: model, provider: provider)
|
|
29
|
+
parse_notebook_response(response)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.write(path, notebook_data)
|
|
33
|
+
File.write(path, ::JSON.pretty_generate(notebook_data))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.build_prompt(description, kernel)
|
|
37
|
+
<<~PROMPT
|
|
38
|
+
Generate a Jupyter notebook as valid JSON (.ipynb format) for the following task:
|
|
39
|
+
|
|
40
|
+
#{description}
|
|
41
|
+
|
|
42
|
+
Requirements:
|
|
43
|
+
- Use kernel: #{kernel}
|
|
44
|
+
- Include a markdown cell with a title and description at the top
|
|
45
|
+
- Include well-commented code cells
|
|
46
|
+
- Include markdown explanation cells between code sections
|
|
47
|
+
- Return ONLY the raw JSON, no markdown fences, no explanation
|
|
48
|
+
|
|
49
|
+
The JSON must follow the .ipynb format with these top-level keys:
|
|
50
|
+
nbformat, nbformat_minor, metadata, cells
|
|
51
|
+
|
|
52
|
+
Each cell must have: cell_type, metadata, source (array of strings), outputs (array), execution_count
|
|
53
|
+
PROMPT
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.call_llm(prompt, model: nil, provider: nil)
|
|
57
|
+
kwargs = { messages: [{ role: 'user', content: prompt }] }
|
|
58
|
+
kwargs[:model] = model if model
|
|
59
|
+
kwargs[:provider] = provider.to_sym if provider
|
|
60
|
+
Legion::LLM.chat(**kwargs)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.parse_notebook_response(response)
|
|
64
|
+
content = response[:content].to_s.strip
|
|
65
|
+
# Strip markdown fences if the LLM wrapped the JSON
|
|
66
|
+
content = content.gsub(/\A```(?:json)?\n?/, '').gsub(/\n?```\z/, '').strip
|
|
67
|
+
data = ::JSON.parse(content)
|
|
68
|
+
validate_notebook!(data)
|
|
69
|
+
data
|
|
70
|
+
rescue ::JSON::ParserError => e
|
|
71
|
+
raise ArgumentError, "LLM returned invalid JSON: #{e.message}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.validate_notebook!(data)
|
|
75
|
+
raise ArgumentError, 'Missing nbformat key' unless data.key?('nbformat')
|
|
76
|
+
raise ArgumentError, 'Missing cells key' unless data.key?('cells')
|
|
77
|
+
raise ArgumentError, 'cells must be an array' unless data['cells'].is_a?(Array)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Notebook
|
|
7
|
+
module Parser
|
|
8
|
+
def self.parse(path)
|
|
9
|
+
data = ::JSON.parse(File.read(path))
|
|
10
|
+
{
|
|
11
|
+
metadata: data['metadata'],
|
|
12
|
+
kernel: data.dig('metadata', 'kernelspec', 'display_name'),
|
|
13
|
+
language: data.dig('metadata', 'kernelspec', 'language') || 'python',
|
|
14
|
+
cells: Array(data['cells']).map { |c| parse_cell(c) }
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.parse_cell(cell)
|
|
19
|
+
{
|
|
20
|
+
type: cell['cell_type'],
|
|
21
|
+
source: Array(cell['source']).join,
|
|
22
|
+
outputs: Array(cell.fetch('outputs', [])).map { |o| parse_output(o) }
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.parse_output(output)
|
|
27
|
+
text = case output['output_type']
|
|
28
|
+
when 'execute_result', 'display_data'
|
|
29
|
+
data = output.fetch('data', {})
|
|
30
|
+
Array(data.fetch('text/plain', [])).join
|
|
31
|
+
when 'error'
|
|
32
|
+
"#{output['ename']}: #{output['evalue']}"
|
|
33
|
+
else
|
|
34
|
+
Array(output.fetch('text', [])).join
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
output_type: output['output_type'],
|
|
39
|
+
text: text
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Notebook
|
|
5
|
+
module Renderer
|
|
6
|
+
RESET = "\e[0m"
|
|
7
|
+
BOLD = "\e[1m"
|
|
8
|
+
DIM = "\e[2m"
|
|
9
|
+
YELLOW = "\e[33m"
|
|
10
|
+
CYAN = "\e[36m"
|
|
11
|
+
GREEN = "\e[32m"
|
|
12
|
+
RED = "\e[31m"
|
|
13
|
+
RULE = "\e[2m#{'─' * 60}\e[0m".freeze
|
|
14
|
+
|
|
15
|
+
def self.render_notebook(notebook, color: true)
|
|
16
|
+
lines = []
|
|
17
|
+
kernel = notebook[:kernel]
|
|
18
|
+
lines << (color ? "#{BOLD}#{CYAN}Kernel: #{kernel}#{RESET}" : "Kernel: #{kernel}") if kernel
|
|
19
|
+
|
|
20
|
+
notebook[:cells].each_with_index do |cell, idx|
|
|
21
|
+
lines << ''
|
|
22
|
+
lines << render_cell_header(idx + 1, cell[:type], color)
|
|
23
|
+
lines << render_cell_source(cell, notebook[:language], color)
|
|
24
|
+
lines += render_cell_outputs(cell[:outputs], color) unless cell[:outputs].empty?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
lines.join("\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.render_cell_header(index, type, color)
|
|
31
|
+
label = "[#{type}] Cell #{index}"
|
|
32
|
+
color ? "#{BOLD}#{YELLOW}#{label}#{RESET}" : label
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.render_cell_source(cell, language, color)
|
|
36
|
+
return '' if cell[:source].empty?
|
|
37
|
+
|
|
38
|
+
if cell[:type] == 'code'
|
|
39
|
+
highlight(cell[:source], language, color)
|
|
40
|
+
else
|
|
41
|
+
color ? "#{DIM}#{cell[:source]}#{RESET}" : cell[:source]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.render_cell_outputs(outputs, color)
|
|
46
|
+
outputs.filter_map do |output|
|
|
47
|
+
next if output[:text].to_s.strip.empty?
|
|
48
|
+
|
|
49
|
+
prefix = color ? "#{DIM} => " : ' => '
|
|
50
|
+
suffix = color ? RESET : ''
|
|
51
|
+
"#{prefix}#{output[:text].strip}#{suffix}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.highlight(code, language, color)
|
|
56
|
+
return code unless color
|
|
57
|
+
|
|
58
|
+
begin
|
|
59
|
+
require 'rouge'
|
|
60
|
+
lexer = Rouge::Lexer.find(language.to_s) || Rouge::Lexers::PlainText.new
|
|
61
|
+
formatter = Rouge::Formatters::Terminal256.new(Rouge::Themes::Monokai.new)
|
|
62
|
+
formatter.format(lexer.lex(code))
|
|
63
|
+
rescue LoadError
|
|
64
|
+
code
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.rule(color)
|
|
69
|
+
color ? RULE : ('-' * 60)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/legion/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legionio
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.104
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -609,6 +609,9 @@ files:
|
|
|
609
609
|
- lib/legion/isolation.rb
|
|
610
610
|
- lib/legion/lex.rb
|
|
611
611
|
- lib/legion/metrics.rb
|
|
612
|
+
- lib/legion/notebook/generator.rb
|
|
613
|
+
- lib/legion/notebook/parser.rb
|
|
614
|
+
- lib/legion/notebook/renderer.rb
|
|
612
615
|
- lib/legion/process.rb
|
|
613
616
|
- lib/legion/readiness.rb
|
|
614
617
|
- lib/legion/registry.rb
|