legionio 1.5.18 → 1.5.19
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/CHANGELOG.md +9 -0
- data/lib/legion/cli/knowledge_command.rb +140 -0
- data/lib/legion/cli.rb +5 -1
- data/lib/legion/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bb76821fb4802e78a19c4518c18f32a961a144cd96f0ef146ce0b582ef96f699
|
|
4
|
+
data.tar.gz: c9c6eb50bef1df98a3ede6ce943e4a45a9dc4c638ce682519708d3d0008eddd5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e502641a0f4177baeab32e78b839f80fa163953fe55770cd0a31364b352b5eda156e0971e67057ad5263160cc5f874b7cdbfdafc1a7cc9799fdd7a9ebd73a65
|
|
7
|
+
data.tar.gz: 90ec056445d835e01d044dd1ca6272c7e30ef5cffa0e30c39d93d4c2e8004f0e4d3ff59d83e12753bc485484a68a2526e90ebe1ccfdd1fb39e12b89e549d01d1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.19] - 2026-03-26
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `legion knowledge` CLI subcommand: query, retrieve, ingest, status (closes #36)
|
|
7
|
+
- `legion knowledge query QUESTION` — synthesized LLM answer + ranked source chunks
|
|
8
|
+
- `legion knowledge retrieve QUESTION` — raw source chunks without synthesis
|
|
9
|
+
- `legion knowledge ingest PATH` — ingest file or directory corpus
|
|
10
|
+
- `legion knowledge status` — show corpus file count and size
|
|
11
|
+
|
|
3
12
|
## [1.5.18] - 2026-03-25
|
|
4
13
|
|
|
5
14
|
### Added
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module CLI
|
|
5
|
+
class Knowledge < Thor
|
|
6
|
+
def self.exit_on_failure?
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
|
|
11
|
+
class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
|
|
12
|
+
|
|
13
|
+
desc 'query QUESTION', 'Query the knowledge base with optional LLM synthesis'
|
|
14
|
+
option :top_k, type: :numeric, default: 5, desc: 'Number of source chunks'
|
|
15
|
+
option :synthesize, type: :boolean, default: true, desc: 'Synthesize an LLM answer'
|
|
16
|
+
option :verbose, type: :boolean, default: false, desc: 'Show full source metadata'
|
|
17
|
+
def query(question)
|
|
18
|
+
require_knowledge!
|
|
19
|
+
result = knowledge_query.query(question: question, top_k: options[:top_k],
|
|
20
|
+
synthesize: options[:synthesize])
|
|
21
|
+
out = formatter
|
|
22
|
+
if options[:json]
|
|
23
|
+
out.json(result)
|
|
24
|
+
elsif result[:success]
|
|
25
|
+
out.header('Knowledge Query')
|
|
26
|
+
if result[:answer]
|
|
27
|
+
out.spacer
|
|
28
|
+
puts result[:answer]
|
|
29
|
+
out.spacer
|
|
30
|
+
end
|
|
31
|
+
print_sources(result[:sources] || [], out, verbose: options[:verbose])
|
|
32
|
+
else
|
|
33
|
+
out.warn("Query failed: #{result[:error]}")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
default_task :help
|
|
37
|
+
|
|
38
|
+
desc 'retrieve QUESTION', 'Retrieve source chunks without LLM synthesis'
|
|
39
|
+
option :top_k, type: :numeric, default: 5, desc: 'Number of source chunks'
|
|
40
|
+
def retrieve(question)
|
|
41
|
+
require_knowledge!
|
|
42
|
+
result = knowledge_query.retrieve(question: question, top_k: options[:top_k])
|
|
43
|
+
out = formatter
|
|
44
|
+
if options[:json]
|
|
45
|
+
out.json(result)
|
|
46
|
+
elsif result[:success]
|
|
47
|
+
out.header("Knowledge Retrieve (#{(result[:sources] || []).size} chunks)")
|
|
48
|
+
print_sources(result[:sources] || [], out, verbose: true)
|
|
49
|
+
else
|
|
50
|
+
out.warn("Retrieve failed: #{result[:error]}")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
desc 'ingest PATH', 'Ingest a file or directory into the knowledge base'
|
|
55
|
+
option :force, type: :boolean, default: false, desc: 'Re-ingest even unchanged files'
|
|
56
|
+
option :dry_run, type: :boolean, default: false, desc: 'Preview without writing'
|
|
57
|
+
def ingest(path)
|
|
58
|
+
require_ingest!
|
|
59
|
+
result = if ::File.directory?(path)
|
|
60
|
+
knowledge_ingest.ingest_corpus(path: path, force: options[:force],
|
|
61
|
+
dry_run: options[:dry_run])
|
|
62
|
+
else
|
|
63
|
+
knowledge_ingest.ingest_file(file_path: path, force: options[:force],
|
|
64
|
+
dry_run: options[:dry_run])
|
|
65
|
+
end
|
|
66
|
+
out = formatter
|
|
67
|
+
if options[:json]
|
|
68
|
+
out.json(result)
|
|
69
|
+
elsif result[:success]
|
|
70
|
+
out.success('Ingest complete')
|
|
71
|
+
out.detail(result.except(:success))
|
|
72
|
+
else
|
|
73
|
+
out.warn("Ingest failed: #{result[:error]}")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
desc 'status', 'Show knowledge base status'
|
|
78
|
+
def status
|
|
79
|
+
require_ingest!
|
|
80
|
+
result = knowledge_ingest.scan_corpus(path: ::Dir.pwd)
|
|
81
|
+
out = formatter
|
|
82
|
+
if options[:json]
|
|
83
|
+
out.json(result)
|
|
84
|
+
else
|
|
85
|
+
out.header('Knowledge Status')
|
|
86
|
+
out.detail({
|
|
87
|
+
'Path' => result[:path].to_s,
|
|
88
|
+
'Files' => result[:file_count].to_s,
|
|
89
|
+
'Total size' => "#{result[:total_bytes]} bytes"
|
|
90
|
+
})
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
no_commands do
|
|
95
|
+
def formatter
|
|
96
|
+
@formatter ||= Output::Formatter.new(json: options[:json], color: !options[:no_color])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def require_knowledge!
|
|
100
|
+
return if defined?(Legion::Extensions::Knowledge::Runners::Query)
|
|
101
|
+
|
|
102
|
+
raise CLI::Error, 'lex-knowledge extension is not loaded. Install and enable it first.'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def require_ingest!
|
|
106
|
+
return if defined?(Legion::Extensions::Knowledge::Runners::Ingest)
|
|
107
|
+
|
|
108
|
+
raise CLI::Error, 'lex-knowledge extension is not loaded. Install and enable it first.'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def knowledge_query
|
|
112
|
+
Legion::Extensions::Knowledge::Runners::Query
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def knowledge_ingest
|
|
116
|
+
Legion::Extensions::Knowledge::Runners::Ingest
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def print_sources(sources, out, verbose:)
|
|
120
|
+
return out.warn('No sources found') if sources.empty?
|
|
121
|
+
|
|
122
|
+
out.header("Sources (#{sources.size})")
|
|
123
|
+
sources.each_with_index do |s, i|
|
|
124
|
+
score = format('%.2f', s[:score].to_f)
|
|
125
|
+
heading = s[:heading].to_s.empty? ? '' : " \u00a7 #{s[:heading]}"
|
|
126
|
+
puts " #{i + 1}. #{s[:source_file]}#{heading} score: #{score}"
|
|
127
|
+
puts " #{truncate(s[:content].to_s, 100)}" if verbose
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def truncate(text, max)
|
|
132
|
+
return text if text.length <= max
|
|
133
|
+
return text[0, max] if max < 4
|
|
134
|
+
|
|
135
|
+
"#{text[0, max - 3]}..."
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
data/lib/legion/cli.rb
CHANGED
|
@@ -42,6 +42,7 @@ module Legion
|
|
|
42
42
|
autoload :Eval, 'legion/cli/eval_command'
|
|
43
43
|
autoload :Update, 'legion/cli/update_command'
|
|
44
44
|
autoload :Init, 'legion/cli/init_command'
|
|
45
|
+
autoload :Knowledge, 'legion/cli/knowledge_command'
|
|
45
46
|
autoload :Setup, 'legion/cli/setup_command'
|
|
46
47
|
autoload :Skill, 'legion/cli/skill_command'
|
|
47
48
|
autoload :Prompt, 'legion/cli/prompt_command'
|
|
@@ -256,6 +257,9 @@ module Legion
|
|
|
256
257
|
desc 'apollo SUBCOMMAND', 'Apollo knowledge graph'
|
|
257
258
|
subcommand 'apollo', Legion::CLI::Apollo
|
|
258
259
|
|
|
260
|
+
desc 'knowledge SUBCOMMAND', 'Search and manage the document knowledge base'
|
|
261
|
+
subcommand 'knowledge', Legion::CLI::Knowledge
|
|
262
|
+
|
|
259
263
|
desc 'schedule SUBCOMMAND', 'Manage schedules'
|
|
260
264
|
subcommand 'schedule', Legion::CLI::Schedule
|
|
261
265
|
|
|
@@ -348,7 +352,7 @@ module Legion
|
|
|
348
352
|
|
|
349
353
|
desc 'tree', 'Print a tree of all available commands'
|
|
350
354
|
def tree
|
|
351
|
-
legion_print_command_tree(self.class,
|
|
355
|
+
legion_print_command_tree(self.class, ::File.basename($PROGRAM_NAME), '')
|
|
352
356
|
end
|
|
353
357
|
|
|
354
358
|
desc 'ask TEXT', 'Quick AI prompt (shortcut for chat prompt)'
|
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.5.
|
|
4
|
+
version: 1.5.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -630,6 +630,7 @@ files:
|
|
|
630
630
|
- lib/legion/cli/init/environment_detector.rb
|
|
631
631
|
- lib/legion/cli/init_command.rb
|
|
632
632
|
- lib/legion/cli/interactive.rb
|
|
633
|
+
- lib/legion/cli/knowledge_command.rb
|
|
633
634
|
- lib/legion/cli/lex/actor.rb
|
|
634
635
|
- lib/legion/cli/lex/exchange.rb
|
|
635
636
|
- lib/legion/cli/lex/message.rb
|