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 +4 -4
- data/CHANGELOG.md +17 -0
- data/CLAUDE.md +12 -11
- data/README.md +56 -19
- data/lib/legion/api/coldstart.rb +4 -3
- data/lib/legion/api/openapi.rb +4 -4
- data/lib/legion/cli/coldstart_command.rb +19 -6
- data/lib/legion/cli/knowledge_command.rb +193 -0
- data/lib/legion/cli/setup_command.rb +38 -0
- data/lib/legion/service.rb +1 -1
- data/lib/legion/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6413e545fabb634ddda16a99e884701751aa1cdd8245b649799f4836ae0c7360
|
|
4
|
+
data.tar.gz: 4239fd5af0278ba21cf33bbd320e62e67b103d93d36f3f5266eb5269019b7859
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
51
|
-
├── 10.
|
|
52
|
-
├── 11.
|
|
53
|
-
├── 12.
|
|
54
|
-
├── 13.
|
|
55
|
-
|
|
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
|
-
│ └── (
|
|
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.
|
|
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 #
|
|
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 ·
|
|
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.
|
|
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** —
|
|
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
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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.
|
|
479
|
-
├── 2.
|
|
480
|
-
├── 3.
|
|
481
|
-
├── 4.
|
|
482
|
-
├── 5.
|
|
483
|
-
├── 6.
|
|
484
|
-
├── 7.
|
|
485
|
-
├── 8.
|
|
486
|
-
├── 9.
|
|
487
|
-
├── 10.
|
|
488
|
-
├── 11.
|
|
489
|
-
├── 12.
|
|
490
|
-
|
|
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-apollo — shared/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.
|
data/lib/legion/api/coldstart.rb
CHANGED
|
@@ -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))
|
data/lib/legion/api/openapi.rb
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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'] || {}
|
data/lib/legion/service.rb
CHANGED
|
@@ -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
|
|
data/lib/legion/version.rb
CHANGED