legionio 1.5.22 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc118d8800708d94fc279455bd379cda4f159cc535b6af290997aacf311765bb
4
- data.tar.gz: b144e6d8fa85659d6a929f427c611ddd5e5288bfce404eeb11b41bfe1f85d82f
3
+ metadata.gz: 6413e545fabb634ddda16a99e884701751aa1cdd8245b649799f4836ae0c7360
4
+ data.tar.gz: 4239fd5af0278ba21cf33bbd320e62e67b103d93d36f3f5266eb5269019b7859
5
5
  SHA512:
6
- metadata.gz: ba5b7bfc06588c0a8f3e6e5d123e481a847e72614e76bb312b3dbf78bec3f55d56c539fd501db189a6827ab81a36716377084c8419a11548c5a1463fa4fe5dc8
7
- data.tar.gz: 35580f0a9895e93c225fe596a02379f57a21669e67b2485805beb37ec9082c4098302b6dfb465ec5f21f06d9265e0bc103dd882ec68377e8f4e4ce2d891c3d59
6
+ metadata.gz: 2dee1932ef724c587ff8d26c496913cb7b90b1385f7793b3d13c7e3e703440c0b6ad4f7b431215c395ee742cd920afebcda784e29feca058d67a6afb73cb0abb
7
+ data.tar.gz: fd476599db94cf729ed3e5b66f82f1f090b44215222c24e6a8cf8e6d67c9837fd204561c6054f83505b0fc194a3766aef365eadaa46712babb5750dde82383c3
data/CHANGELOG.md CHANGED
@@ -1,7 +1,24 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.6.0] - 2026-03-26
4
+
5
+ ### Added
6
+ - `legion knowledge monitor add/list/remove/status` — multi-directory corpus monitor management
7
+ - `legion knowledge capture commit` — capture git commit as knowledge (hook-compatible)
8
+ - `legion knowledge capture session` — capture session summary as knowledge (hook-compatible)
9
+ - `legion setup claude-code` now installs write-back hooks for automatic knowledge capture
10
+ - `resolve_corpus_path` falls back to first registered monitor when no explicit path given
11
+
12
+ ## [1.5.23] - 2026-03-26
13
+
14
+ ### Changed
15
+ - Remove all lex-memory references from service.rb, API coldstart, and OpenAPI docs; use lex-agentic-memory namespace everywhere
16
+
3
17
  ## [1.5.22] - 2026-03-26
4
18
 
19
+ ### Fixed
20
+ - `coldstart ingest` no longer crashes when lex-memory is absent; uses lex-agentic-memory trace store instead
21
+
5
22
  ### Changed
6
23
  - Consolidate 48 root CLI commands into 7 groups + 19 root commands
7
24
  - New groups: `ai`, `git`, `pipeline`, `ops`, `serve`, `admin`, `dev`
data/CLAUDE.md CHANGED
@@ -9,7 +9,7 @@ The primary gem for the LegionIO framework. An extensible async job engine for s
9
9
 
10
10
  **GitHub**: https://github.com/LegionIO/LegionIO
11
11
  **Gem**: `legionio`
12
- **Version**: 1.4.197
12
+ **Version**: 1.6.0
13
13
  **License**: Apache-2.0
14
14
  **Docker**: `legionio/legion`
15
15
  **Ruby**: >= 3.4
@@ -47,12 +47,13 @@ Legion.start
47
47
  ├── 6. setup_data (legion-data, MySQL/SQLite + migrations, optional)
48
48
  ├── 7. setup_rbac (legion-rbac, optional)
49
49
  ├── 8. setup_llm (legion-llm, AI provider setup + routing, optional)
50
- ├── 9. setup_gaia (legion-gaia, cognitive coordination layer, optional)
51
- ├── 10. setup_telemetry (OpenTelemetry, optional)
52
- ├── 11. setup_supervision (process supervision)
53
- ├── 12. load_extensions (parallel require+autobuild on 4-thread pool, then hook_all_actors)
54
- ├── 13. Legion::Crypt.cs (distribute cluster secret)
55
- └── 14. setup_api (start Sinatra/Puma on port 4567)
50
+ ├── 9. setup_apollo (legion-apollo, shared + local knowledge store, optional)
51
+ ├── 10. setup_gaia (legion-gaia, cognitive coordination layer, optional)
52
+ ├── 11. setup_telemetry (OpenTelemetry, optional)
53
+ ├── 12. setup_supervision (process supervision)
54
+ ├── 13. load_extensions (two-phase parallel: require+autobuild on FixedThreadPool, then hook_all_actors)
55
+ ├── 14. Legion::Crypt.cs (distribute cluster secret)
56
+ └── 15. setup_api (start Sinatra/Puma on port 4567)
56
57
  ```
57
58
 
58
59
  Each phase calls `Legion::Readiness.mark_ready(:component)`. All phases are individually toggleable via `Service.new(transport: false, ...)`.
@@ -150,7 +151,7 @@ Legion (lib/legion.rb)
150
151
  │ # Populated by Builders::Routes during autobuild
151
152
 
152
153
  ├── MCP (legion-mcp gem) # Extracted to standalone gem — see legion-mcp/CLAUDE.md
153
- │ └── (41 tools, 2 resources, TierRouter, PatternStore, ContextGuard, Observer, EmbeddingIndex)
154
+ │ └── (58 tools, 2 resources, TierRouter, PatternStore, ContextGuard, Observer, EmbeddingIndex)
154
155
 
155
156
  ├── DigitalWorker # Digital worker platform (AI-as-labor governance)
156
157
  │ ├── Lifecycle # Worker state machine (active/paused/retired/terminated)
@@ -194,7 +195,7 @@ Legion (lib/legion.rb)
194
195
  │ ├── Session # Multi-turn chat session with streaming
195
196
  │ ├── SessionStore # Persistent session save/load/list/resume/fork
196
197
  │ ├── Permissions # Tool permission model (interactive/auto_approve/read_only)
197
- │ ├── ToolRegistry # Chat tool discovery and registration (40 built-in + extension tools)
198
+ │ ├── ToolRegistry # Chat tool discovery and registration (40 built-in tools + extension tools)
198
199
  │ ├── ExtensionTool # permission_tier DSL module for LEX chat tools (:read/:write/:shell)
199
200
  │ ├── ExtensionToolLoader # Lazy discovery of tools/ directories from loaded extensions
200
201
  │ ├── Context # Project awareness (git, language, instructions, extra dirs)
@@ -480,7 +481,7 @@ legion
480
481
 
481
482
  ### MCP Design
482
483
 
483
- Extracted to the `legion-mcp` gem (v0.5.5). See `legion-mcp/CLAUDE.md` for full architecture.
484
+ Extracted to the `legion-mcp` gem (v0.5.9). See `legion-mcp/CLAUDE.md` for full architecture.
484
485
 
485
486
  - `Legion::MCP.server` is memoized singleton — call `Legion::MCP.reset!` in tests
486
487
  - Tool naming: `legion.snake_case_name` (dot namespace, not slash)
@@ -768,7 +769,7 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
768
769
 
769
770
  ```bash
770
771
  bundle install
771
- bundle exec rspec # 3194 examples, 0 failures
772
+ bundle exec rspec # ~3500+ examples, 0 failures
772
773
  bundle exec rubocop # 0 offenses
773
774
  ```
774
775
 
data/README.md CHANGED
@@ -8,13 +8,13 @@ Schedule tasks, chain services into dependency graphs, run them concurrently via
8
8
  ╭──────────────────────────────────────╮
9
9
  │ L E G I O N I O │
10
10
  │ │
11
- │ 280+ extensions · 41 MCP tools │
11
+ │ 280+ extensions · 58 MCP tools │
12
12
  │ AI chat CLI · REST API · HA │
13
13
  │ cognitive architecture · Vault │
14
14
  ╰──────────────────────────────────────╯
15
15
  ```
16
16
 
17
- **Ruby >= 3.4** | **v1.4.197** | **Apache-2.0** | [@Esity](https://github.com/Esity)
17
+ **Ruby >= 3.4** | **v1.5.20** | **Apache-2.0** | [@Esity](https://github.com/Esity)
18
18
 
19
19
  ---
20
20
 
@@ -33,7 +33,7 @@ When A completes, B runs. B triggers C, D, and E in parallel. Conditions gate ex
33
33
  But that's just the foundation. LegionIO is also:
34
34
 
35
35
  - **An AI coding assistant** — interactive chat with tools, code review, commit messages, PR generation, and multi-agent workflows
36
- - **An MCP server** — 35 tools that let any AI agent run tasks, manage extensions, and query your infrastructure
36
+ - **An MCP server** — 58 tools that let any AI agent run tasks, manage extensions, and query your infrastructure
37
37
  - **A cognitive computing platform** — 242 brain-modeled extensions across 18 cognitive domains
38
38
  - **A digital worker platform** — AI-as-labor with governance, risk tiers, and cost tracking
39
39
 
@@ -49,7 +49,7 @@ For the AI features:
49
49
 
50
50
  ```bash
51
51
  legion # launch the interactive TTY shell
52
- legion chat # interactive AI REPL with 10 built-in tools
52
+ legion chat # interactive AI REPL with 40 built-in tools
53
53
  legion commit # AI-generated commit message from staged changes
54
54
  legion review # AI code review of your code
55
55
  ```
@@ -162,7 +162,7 @@ legion chat prompt "explain main.rb" # single-prompt mode
162
162
  echo "fix the bug" | legion chat prompt - # pipe from stdin
163
163
  ```
164
164
 
165
- **10 built-in tools**: read_file, write_file, edit_file, search_files, search_content, run_command, save_memory, search_memory, web_search, spawn_agent
165
+ **40 built-in tools**: read_file, write_file, edit_file, search_files, search_content, run_command, save_memory, search_memory, web_search, spawn_agent, search_traces, query_knowledge, ingest_knowledge, consolidate_memory, relate_knowledge, knowledge_maintenance, knowledge_stats, summarize_traces, list_extensions, manage_tasks, system_status, view_events, cost_summary, reflect, manage_schedules, worker_status, detect_anomalies, view_trends, trigger_dream, generate_insights, budget_status, provider_health, model_comparison, shadow_eval_status, entity_extract, arbitrage_status, escalation_status, graph_explore, scheduling_status, memory_status
166
166
 
167
167
  **Slash commands**: `/help` `/quit` `/cost` `/status` `/clear` `/new` `/save` `/load` `/sessions` `/compact` `/fetch URL` `/search QUERY` `/diff` `/copy` `/rewind` `/memory` `/agent` `/agents` `/plan` `/swarm` `/review` `/permissions` `/personality` `/model` `/edit` `/commit` `/workers` `/dream`
168
168
 
@@ -200,6 +200,39 @@ legion memory search "testing"
200
200
  legion memory forget 3
201
201
  ```
202
202
 
203
+ ### Knowledge
204
+
205
+ Query and manage the Apollo shared knowledge store and local knowledge index:
206
+
207
+ ```bash
208
+ legion knowledge query "how does transport routing work?"
209
+ legion knowledge retrieve "embedding cosine similarity" --scope global
210
+ legion knowledge ingest /path/to/docs/
211
+ legion knowledge status # index stats, embedding coverage
212
+ legion knowledge health # detect orphans, quality metrics
213
+ legion knowledge maintain # cleanup orphans, reindex
214
+ legion knowledge quality # quality report
215
+ ```
216
+
217
+ ### Mind Growth
218
+
219
+ Autonomous cognitive architecture expansion system. Analyzes gaps, proposes new cognitive extensions, and builds them via a staged pipeline:
220
+
221
+ ```bash
222
+ legion mind-growth status # current growth cycle state
223
+ legion mind-growth analyze # gap analysis against 5 reference models
224
+ legion mind-growth propose # propose a new concept
225
+ legion mind-growth evaluate <id> # evaluate a proposal
226
+ legion mind-growth build <id> # run staged build pipeline
227
+ legion mind-growth list # list proposals
228
+ legion mind-growth approve <id> # manually approve
229
+ legion mind-growth reject <id> # manually reject
230
+ legion mind-growth profile # cognitive profile across all models
231
+ legion mind-growth health # extension fitness validation
232
+ ```
233
+
234
+ Requires `lex-mind-growth`. Also exposes 6 MCP tools in the `legion.mind_growth_*` namespace.
235
+
203
236
  ### Digital Workers
204
237
 
205
238
  AI-as-labor with governance, risk tiers, and cost tracking:
@@ -326,7 +359,7 @@ legion mcp http # streamable HTTP on localhost:9393
326
359
  legion mcp http --port 8080 --host 0.0.0.0
327
360
  ```
328
361
 
329
- **41 tools** in the `legion.*` namespace:
362
+ **58 tools** in the `legion.*` namespace:
330
363
 
331
364
  | Category | Tools |
332
365
  |----------|-------|
@@ -340,6 +373,8 @@ legion mcp http --port 8080 --host 0.0.0.0
340
373
  | **Workers** | `list_workers`, `show_worker`, `worker_lifecycle`, `worker_costs`, `team_summary` |
341
374
  | **RBAC** | `rbac_assignments`, `rbac_check`, `rbac_grants` |
342
375
  | **Analytics** | `routing_stats` |
376
+ | **Knowledge** | `query_knowledge`, `knowledge_health` |
377
+ | **Mind Growth** | `mind_growth_status`, `mind_growth_analyze`, `mind_growth_propose`, `mind_growth_evaluate`, `mind_growth_build`, `mind_growth_profile` |
343
378
 
344
379
  **Resources**: `legion://runners` (full runner catalog), `legion://extensions/{name}` (extension detail)
345
380
 
@@ -475,19 +510,21 @@ Before any Legion code loads, the executable applies three performance optimizat
475
510
  ```
476
511
  legion start
477
512
  └── Legion::Service
478
- ├── 1. Logging (legion-logging)
479
- ├── 2. Settings (legion-settings — /etc/legionio, ~/legionio, ./settings)
480
- ├── 3. Crypt (legion-crypt — Vault connection)
481
- ├── 4. Transport (legion-transport — RabbitMQ)
482
- ├── 5. Cache (legion-cache — Redis/Memcached)
483
- ├── 6. Data (legion-data — database + migrations)
484
- ├── 7. RBAC (legion-rbac — optional role-based access control)
485
- ├── 8. LLM (legion-llm — AI provider setup + routing)
486
- ├── 9. GAIA (legion-gaiacognitive coordination layer)
487
- ├── 10. Supervision (process supervision)
488
- ├── 11. Extensions (discover + load 280+ LEX gems, filtered by role profile)
489
- ├── 12. Cluster Secret (distribute via Vault or memory)
490
- └── 13. API (Sinatra/Puma on port 4567)
513
+ ├── 1. Logging (legion-logging)
514
+ ├── 2. Settings (legion-settings — /etc/legionio, ~/legionio, ./settings)
515
+ ├── 3. Crypt (legion-crypt — Vault connection)
516
+ ├── 4. Transport (legion-transport — RabbitMQ)
517
+ ├── 5. Cache (legion-cache — Redis/Memcached)
518
+ ├── 6. Data (legion-data — database + migrations)
519
+ ├── 7. RBAC (legion-rbac — optional role-based access control)
520
+ ├── 8. LLM (legion-llm — AI provider setup + routing)
521
+ ├── 9. Apollo (legion-apolloshared/local knowledge store)
522
+ ├── 10. GAIA (legion-gaia — cognitive coordination layer)
523
+ ├── 11. Telemetry (OpenTelemetry optional)
524
+ ├── 12. Supervision (process supervision)
525
+ ├── 13. Extensions (two-phase parallel load: require+autobuild, then hook_all_actors)
526
+ ├── 14. Cluster Secret (distribute via Vault or memory)
527
+ └── 15. API (Sinatra/Puma on port 4567)
491
528
  ```
492
529
 
493
530
  Each phase registers with `Legion::Readiness`. All phases are individually toggleable.
@@ -19,12 +19,13 @@ module Legion
19
19
  halt 503, json_error('coldstart_unavailable', 'lex-coldstart is not loaded', status_code: 503)
20
20
  end
21
21
 
22
- unless defined?(Legion::Extensions::Memory)
23
- Legion::Logging.warn 'API POST /api/coldstart/ingest returned 503: lex-memory is not loaded'
24
- halt 503, json_error('memory_unavailable', 'lex-memory is not loaded', status_code: 503)
22
+ unless defined?(Legion::Extensions::Agentic::Memory::Trace)
23
+ Legion::Logging.warn 'API POST /api/coldstart/ingest returned 503: lex-agentic-memory is not loaded'
24
+ halt 503, json_error('memory_unavailable', 'lex-agentic-memory is not loaded', status_code: 503)
25
25
  end
26
26
 
27
27
  runner = Object.new.extend(Legion::Extensions::Coldstart::Runners::Ingest)
28
+ runner.define_singleton_method(:log) { Legion::Logging } unless runner.respond_to?(:log)
28
29
 
29
30
  result = if File.file?(path)
30
31
  runner.ingest_file(file_path: File.expand_path(path))
@@ -128,7 +128,7 @@ module Legion
128
128
  { name: 'Lex', description: 'Auto-registered LEX runner routes' },
129
129
  { name: 'Workers', description: 'Digital worker lifecycle management' },
130
130
  { name: 'Teams', description: 'Team-level worker and cost views' },
131
- { name: 'Coldstart', description: 'Cold-start memory ingestion (requires lex-coldstart + lex-memory)' },
131
+ { name: 'Coldstart', description: 'Cold-start memory ingestion (requires lex-coldstart + lex-agentic-memory)' },
132
132
  { name: 'Gaia', description: 'Gaia cognitive layer status' },
133
133
  { name: 'Apollo', description: 'Apollo knowledge graph (requires lex-apollo + legion-data)' },
134
134
  { name: 'OpenAPI', description: 'OpenAPI spec endpoint' }
@@ -1384,8 +1384,8 @@ module Legion
1384
1384
  '/api/coldstart/ingest' => {
1385
1385
  post: {
1386
1386
  tags: ['Coldstart'],
1387
- summary: 'Ingest a file or directory into lex-memory',
1388
- description: 'Requires lex-coldstart and lex-memory to be loaded.',
1387
+ summary: 'Ingest a file or directory into agentic memory',
1388
+ description: 'Requires lex-coldstart and lex-agentic-memory to be loaded.',
1389
1389
  operationId: 'coldstartIngest',
1390
1390
  requestBody: {
1391
1391
  required: true,
@@ -1404,7 +1404,7 @@ module Legion
1404
1404
  '401' => UNAUTH_RESPONSE,
1405
1405
  '404' => NOT_FOUND_RESPONSE,
1406
1406
  '422' => UNPROCESSABLE_RESPONSE,
1407
- '503' => { description: 'lex-coldstart or lex-memory not loaded' }
1407
+ '503' => { description: 'lex-coldstart or lex-agentic-memory not loaded' }
1408
1408
  }
1409
1409
  }
1410
1410
  }
@@ -11,10 +11,10 @@ module Legion
11
11
  class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
12
12
  class_option :verbose, type: :boolean, default: false, aliases: ['-V'], desc: 'Verbose logging'
13
13
 
14
- desc 'ingest [PATH...]', 'Ingest Claude memory/CLAUDE.md files into lex-memory traces'
14
+ desc 'ingest [PATH...]', 'Ingest Claude memory/CLAUDE.md files into agentic memory traces'
15
15
  long_desc <<~DESC
16
16
  Parse Claude Code MEMORY.md or CLAUDE.md files and convert them into
17
- lex-memory traces for cold start bootstrapping.
17
+ agentic memory traces for cold start bootstrapping.
18
18
 
19
19
  Accepts any number of file or directory paths. When given a directory,
20
20
  all CLAUDE.md and MEMORY.md files are discovered recursively.
@@ -59,7 +59,7 @@ module Legion
59
59
  require_coldstart!
60
60
  paths = [Dir.pwd] if paths.empty?
61
61
 
62
- runner = Object.new.extend(Legion::Extensions::Coldstart::Runners::Ingest)
62
+ runner = build_runner(Legion::Extensions::Coldstart::Runners::Ingest)
63
63
 
64
64
  paths.each do |path|
65
65
  if File.file?(path)
@@ -83,7 +83,7 @@ module Legion
83
83
  out = formatter
84
84
  require_coldstart!
85
85
 
86
- runner = Object.new.extend(Legion::Extensions::Coldstart::Runners::Coldstart)
86
+ runner = build_runner(Legion::Extensions::Coldstart::Runners::Coldstart)
87
87
  progress = runner.coldstart_progress
88
88
 
89
89
  if options[:json]
@@ -111,7 +111,7 @@ module Legion
111
111
  end
112
112
 
113
113
  def run_local_ingest(out, path, dry_run:)
114
- runner = Object.new.extend(Legion::Extensions::Coldstart::Runners::Ingest)
114
+ runner = build_runner(Legion::Extensions::Coldstart::Runners::Ingest)
115
115
 
116
116
  if File.file?(path)
117
117
  result = dry_run ? runner.preview_ingest(file_path: File.expand_path(path)) : runner.ingest_file(file_path: File.expand_path(path))
@@ -152,10 +152,23 @@ module Legion
152
152
  4567
153
153
  end
154
154
 
155
+ def build_runner(mod)
156
+ obj = Object.new
157
+ obj.extend(mod)
158
+ obj.define_singleton_method(:log) { Legion::Logging } unless obj.respond_to?(:log)
159
+ obj
160
+ end
161
+
155
162
  def require_coldstart!
156
163
  require 'legion/logging'
157
164
  Legion::Logging.setup(level: options[:verbose] ? 'debug' : 'warn') unless Legion::Logging.instance_variable_get(:@log)
158
- require 'legion/extensions/memory'
165
+
166
+ begin
167
+ require 'legion/extensions/agentic/memory/trace'
168
+ rescue LoadError
169
+ Legion::Logging.debug('lex-agentic-memory not available, traces will be parsed but not stored') if defined?(Legion::Logging)
170
+ end
171
+
159
172
  require 'legion/extensions/coldstart'
160
173
  rescue LoadError => e
161
174
  formatter.error("lex-coldstart not available: #{e.message}")
@@ -2,6 +2,184 @@
2
2
 
3
3
  module Legion
4
4
  module CLI
5
+ class MonitorCommand < 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 'add PATH', 'Add a directory to corpus monitors'
14
+ option :extensions, type: :string, desc: 'Comma-separated file extensions to watch (e.g. md,rb)'
15
+ option :label, type: :string, desc: 'Human-readable label for this monitor'
16
+ def add(path)
17
+ require_monitor!
18
+ exts = options[:extensions]&.split(',')&.map(&:strip)
19
+ result = Legion::Extensions::Knowledge::Runners::Monitor.add_monitor(
20
+ path: path,
21
+ extensions: exts,
22
+ label: options[:label]
23
+ )
24
+ out = formatter
25
+ if options[:json]
26
+ out.json(result)
27
+ elsif result[:success]
28
+ out.success("Monitor added: #{path}")
29
+ else
30
+ out.warn("Failed to add monitor: #{result[:error]}")
31
+ end
32
+ end
33
+
34
+ desc 'list', 'List registered corpus monitors'
35
+ def list
36
+ require_monitor!
37
+ monitors = Legion::Extensions::Knowledge::Runners::Monitor.list_monitors
38
+ out = formatter
39
+ if options[:json]
40
+ out.json(monitors)
41
+ elsif monitors.nil? || monitors.empty?
42
+ out.warn('No monitors registered')
43
+ else
44
+ out.header('Knowledge Monitors')
45
+ monitors.each do |m|
46
+ label = m[:label] ? " [#{m[:label]}]" : ''
47
+ exts = m[:extensions]&.join(', ')
48
+ puts " #{m[:path]}#{label}"
49
+ puts " Extensions: #{exts}" if exts && !exts.empty?
50
+ end
51
+ end
52
+ end
53
+ default_task :list
54
+
55
+ desc 'remove IDENTIFIER', 'Remove a corpus monitor by path or label'
56
+ def remove(identifier)
57
+ require_monitor!
58
+ result = Legion::Extensions::Knowledge::Runners::Monitor.remove_monitor(identifier:)
59
+ out = formatter
60
+ if options[:json]
61
+ out.json(result)
62
+ elsif result[:success]
63
+ out.success("Monitor removed: #{identifier}")
64
+ else
65
+ out.warn("Failed to remove monitor: #{result[:error]}")
66
+ end
67
+ end
68
+
69
+ desc 'status', 'Show monitor status (counts)'
70
+ def status
71
+ require_monitor!
72
+ result = Legion::Extensions::Knowledge::Runners::Monitor.monitor_status
73
+ out = formatter
74
+ if options[:json]
75
+ out.json(result)
76
+ else
77
+ out.header('Monitor Status')
78
+ out.detail({
79
+ 'Total monitors' => result[:total_monitors].to_s,
80
+ 'Total files' => result[:total_files].to_s
81
+ })
82
+ end
83
+ end
84
+
85
+ no_commands do
86
+ def formatter
87
+ @formatter ||= Output::Formatter.new(json: options[:json], color: !options[:no_color])
88
+ end
89
+
90
+ def require_monitor!
91
+ return if defined?(Legion::Extensions::Knowledge::Runners::Monitor)
92
+
93
+ raise CLI::Error, 'lex-knowledge extension is not loaded. Install and enable it first.'
94
+ end
95
+ end
96
+ end
97
+
98
+ class CaptureCommand < Thor
99
+ def self.exit_on_failure?
100
+ true
101
+ end
102
+
103
+ class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
104
+ class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
105
+
106
+ desc 'commit', 'Capture the last git commit as knowledge'
107
+ def commit
108
+ log_line = `git log -1 --format='%H %s' 2>/dev/null`.strip
109
+ diff_stat = `git diff HEAD~1 --stat 2>/dev/null`.strip
110
+
111
+ if log_line.empty?
112
+ formatter.warn('No git commit found')
113
+ return
114
+ end
115
+
116
+ sha, *subject_parts = log_line.split
117
+ subject = subject_parts.join(' ')
118
+ content = "Git commit: #{sha}\nSubject: #{subject}\n\nDiff stat:\n#{diff_stat}"
119
+ tags = %w[git commit knowledge-capture]
120
+
121
+ result = if defined?(Legion::Extensions::Knowledge::Runners::Ingest)
122
+ Legion::Extensions::Knowledge::Runners::Ingest.ingest_file(
123
+ content: content,
124
+ tags: tags,
125
+ source: "git:#{sha}"
126
+ )
127
+ else
128
+ { success: false, error: 'lex-knowledge not loaded' }
129
+ end
130
+
131
+ out = formatter
132
+ if options[:json]
133
+ out.json(result)
134
+ elsif result[:success]
135
+ out.success("Captured commit #{sha[0, 8]}: #{subject}")
136
+ else
137
+ out.warn("Capture failed: #{result[:error]}")
138
+ end
139
+ end
140
+
141
+ desc 'session', 'Capture a session note from stdin'
142
+ def session
143
+ input = $stdin.gets(nil) if $stdin.ready? rescue nil # rubocop:disable Style/RescueModifier
144
+ input = input.to_s.strip
145
+
146
+ if input.empty?
147
+ formatter.warn('No session input provided (pipe text to stdin)')
148
+ return
149
+ end
150
+
151
+ repo = `git rev-parse --show-toplevel 2>/dev/null`.strip.split('/').last
152
+ content = "Session note (#{::Time.now.strftime('%Y-%m-%d')}):\n\n#{input}"
153
+ tags = ['session', 'knowledge-capture', ::Time.now.strftime('%Y-%m-%d')]
154
+ tags << "repo:#{repo}" unless repo.empty?
155
+
156
+ result = if defined?(Legion::Extensions::Knowledge::Runners::Ingest)
157
+ Legion::Extensions::Knowledge::Runners::Ingest.ingest_file(
158
+ content: content,
159
+ tags: tags,
160
+ source: "session:#{::Time.now.iso8601}"
161
+ )
162
+ else
163
+ { success: false, error: 'lex-knowledge not loaded' }
164
+ end
165
+
166
+ out = formatter
167
+ if options[:json]
168
+ out.json(result)
169
+ elsif result[:success]
170
+ out.success('Session captured')
171
+ else
172
+ out.warn("Capture failed: #{result[:error]}")
173
+ end
174
+ end
175
+
176
+ no_commands do
177
+ def formatter
178
+ @formatter ||= Output::Formatter.new(json: options[:json], color: !options[:no_color])
179
+ end
180
+ end
181
+ end
182
+
5
183
  class Knowledge < Thor
6
184
  def self.exit_on_failure?
7
185
  true
@@ -161,6 +339,12 @@ module Legion
161
339
  end
162
340
  end
163
341
 
342
+ desc 'monitor SUBCOMMAND', 'Manage knowledge corpus monitors'
343
+ subcommand 'monitor', MonitorCommand
344
+
345
+ desc 'capture SUBCOMMAND', 'Capture knowledge from git commits or sessions'
346
+ subcommand 'capture', CaptureCommand
347
+
164
348
  no_commands do # rubocop:disable Metrics/BlockLength
165
349
  def formatter
166
350
  @formatter ||= Output::Formatter.new(json: options[:json], color: !options[:no_color])
@@ -199,6 +383,9 @@ module Legion
199
383
  def resolve_corpus_path
200
384
  if options[:corpus_path]
201
385
  options[:corpus_path]
386
+ elsif defined?(Legion::Extensions::Knowledge::Runners::Monitor)
387
+ monitors = Legion::Extensions::Knowledge::Runners::Monitor.resolve_monitors
388
+ monitors.first&.dig(:path) || legacy_corpus_path || ::Dir.pwd
202
389
  elsif defined?(Legion::Settings)
203
390
  Legion::Settings.dig(:knowledge, :corpus_path) || ::Dir.pwd
204
391
  else
@@ -206,6 +393,12 @@ module Legion
206
393
  end
207
394
  end
208
395
 
396
+ def legacy_corpus_path
397
+ return unless defined?(Legion::Settings)
398
+
399
+ Legion::Settings.dig(:knowledge, :corpus_path)
400
+ end
401
+
209
402
  def print_sources(sources, out, verbose:)
210
403
  return out.warn('No sources found') if sources.empty?
211
404
 
@@ -78,6 +78,7 @@ module Legion
78
78
 
79
79
  install_claude_mcp(installed)
80
80
  install_claude_skill(installed)
81
+ install_claude_hooks(installed)
81
82
 
82
83
  if options[:json]
83
84
  out.json(platform: 'claude-code', installed: installed)
@@ -343,6 +344,43 @@ module Legion
343
344
  puts " Wrote slash command skill to #{skill_path}" unless options[:json]
344
345
  end
345
346
 
347
+ def install_claude_hooks(installed)
348
+ settings_path = File.expand_path('~/.claude/settings.json')
349
+ existing = load_json_file(settings_path)
350
+
351
+ hooks = existing['hooks'] || {}
352
+
353
+ has_commit = Array(hooks['PostToolUse']).any? { |h| h['command']&.include?('knowledge capture commit') }
354
+ has_session = Array(hooks['Stop']).any? { |h| h['command']&.include?('knowledge capture session') }
355
+ if has_commit && has_session && !options[:force]
356
+ puts ' Write-back hooks already present (use --force to overwrite)' unless options[:json]
357
+ return
358
+ end
359
+
360
+ hooks['PostToolUse'] ||= []
361
+ hooks['Stop'] ||= []
362
+
363
+ unless has_commit
364
+ hooks['PostToolUse'] << {
365
+ 'matcher' => 'Bash',
366
+ 'command' => 'legionio knowledge capture commit',
367
+ 'timeout' => 10_000
368
+ }
369
+ end
370
+
371
+ unless has_session
372
+ hooks['Stop'] << {
373
+ 'command' => 'legionio knowledge capture session',
374
+ 'timeout' => 15_000
375
+ }
376
+ end
377
+
378
+ existing['hooks'] = hooks
379
+ write_json_file(settings_path, existing)
380
+ installed << 'hooks'
381
+ puts ' Installed write-back hooks for knowledge capture' unless options[:json]
382
+ end
383
+
346
384
  def write_mcp_servers_json(_out, path, installed)
347
385
  existing = load_json_file(path)
348
386
  servers = existing['mcpServers'] || {}
@@ -130,7 +130,7 @@ module Legion
130
130
 
131
131
  Legion::Gaia.registry&.rediscover if gaia && defined?(Legion::Gaia) && Legion::Gaia.started?
132
132
 
133
- Legion::Extensions::Memory::Helpers::ErrorTracer.setup if defined?(Legion::Extensions::Memory::Helpers::ErrorTracer)
133
+ Legion::Extensions::Agentic::Memory::Trace::Helpers::ErrorTracer.setup if defined?(Legion::Extensions::Agentic::Memory::Trace::Helpers::ErrorTracer)
134
134
 
135
135
  Legion::Crypt.cs if crypt
136
136
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.5.22'
4
+ VERSION = '1.6.0'
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.22
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity