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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa969cacd00cbd7fcd3234d1d7b46e720da00a4193b4faa967d0f4e9b662b4e4
4
- data.tar.gz: 2afe90f9456b0c5abf07f7180d562dd84ac7c901e81aea1b841288f3aae929d6
3
+ metadata.gz: bb76821fb4802e78a19c4518c18f32a961a144cd96f0ef146ce0b582ef96f699
4
+ data.tar.gz: c9c6eb50bef1df98a3ede6ce943e4a45a9dc4c638ce682519708d3d0008eddd5
5
5
  SHA512:
6
- metadata.gz: bf23f83209770f3e9b7c66bd59ccae187f24c136b94059b5bc54a58e9e80188280a94f01ce557eab19eb1a903bcd9e1a9d69cf7a46077ab89297f934ca1b5244
7
- data.tar.gz: cf041438573f2eb546929ef68e35c527b52af9f552e129fd79b6c94f75862361eeee9d63d4f1fc2b26fecbd70078cc97614c6e928c7fa45cb317b3de858c4eea
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, 'legion', '')
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)'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.5.18'
4
+ VERSION = '1.5.19'
5
5
  end
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.18
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