1use std::fs::OpenOptions;
2use std::io::{Read, Write};
3use std::path::PathBuf;
4
5use qualia_core_db::NQuin;
6
7use crate::cli::{
8 CompileAction, ExtensionAction, GovernanceAction, IngestFormat, MigrateAction, ProfileAction,
9 Q42Action, QueryDialect, ShaclAction,
10};
11use crate::sparql::run_sparql_query;
12
13pub fn handle_extension(action: &ExtensionAction) {
14 match action {
15 ExtensionAction::Register { manifest_path } => {
16 println!("Registering extension from {:?}", manifest_path);
17 }
18 ExtensionAction::List => {
19 println!("Listing registered extensions...");
20 }
21 ExtensionAction::Dispatch { id, input } => {
22 println!("Dispatching to extension '{}': {}", id, input);
23 }
24 }
25}
26
27pub fn handle_governance(action: &GovernanceAction) {
28 match action {
29 GovernanceAction::WalAppend { quin, sign } => {
30 println!("Appending to WAL: {} (signed by {})", quin, sign);
31 }
32 GovernanceAction::Ratify { agreement_did } => {
33 println!("Ratifying agreement: {}", agreement_did);
34 }
35 }
36}
37
38pub fn handle_compile(action: &CompileAction) {
39 match action {
40 CompileAction::N3ToDeontic { file } => {
41 println!("Compiling N3 logic to native norms from {:?}", file);
42 }
43 }
44}
45
46pub fn handle_shacl(action: &ShaclAction) {
47 match action {
48 ShaclAction::List => {
49 println!("============================================================");
50 println!("⚙️ QualiaDB SHACL Extensions Active in Binary");
51 println!("============================================================");
52 println!(" - DeonticObligate");
53 println!(" - DeonticPermit");
54 println!(" - DeonticForbid");
55 println!(" - DeonticNotExpired");
56 println!(" - EpistemicKnowledge");
57 println!(" - EpistemicBelief");
58 println!(" - CommonKnowledge");
59 println!("============================================================");
60 }
61 ShaclAction::Validate { dataset, shapes } => {
62 println!(
63 "Validating {:?} against SHACL shapes in {:?}...",
64 dataset, shapes
65 );
66 }
67 }
68}
69
70pub fn handle_vault(init: bool) {
71 if init {
72 println!("Initializing Memory-Mapped Vault...");
73 let storage_dir = std::env::var("QUALIA_DATA_DIR").unwrap_or_else(|_| ".".to_string());
74 let _vault = qualia_core_db::key_vault::KeyVault::load_or_generate(&storage_dir)
75 .expect("Failed to load KeyVault");
76 println!("Vault Initialization Complete!");
77 }
78}
79
80pub fn handle_migrate(action: &MigrateAction) -> Result<(), Box<dyn std::error::Error>> {
81 match action {
82 MigrateAction::Meta { path, dry_run } => {
83 if *dry_run {
84 use std::fs::File;
85 use std::io::Read as _;
86 let mut f = File::open(path)?;
87 let mut magic = [0u8; 6];
88 f.read_exact(&mut magic)?;
89 let version = u16::from_le_bytes([magic[4], magic[5]]);
90 if version >= 3 {
91 println!(
92 "[dry-run] {} is already v3 — no migration needed.",
93 path.display()
94 );
95 } else {
96 println!("[dry-run] {} is v{version} — would migrate to v3 (Lamport bits [60:32]→[31:0], header bump).", path.display());
97 }
98 } else {
99 println!("Migrating {} to Q42 v3…", path.display());
100 qualia_core_db::q42_volume::migrate_v2_to_v3(path)?;
101 println!("Migration complete: {} is now v3.", path.display());
102 }
103 }
104 }
105 Ok(())
106}
107
108pub fn handle_mem(inspect: bool) {
109 if inspect {
110 println!("Please use `qualia-cli inspect <superblock_path>` directly to inspect specific layouts.");
111 }
112}
113
114pub fn handle_q42(action: &Q42Action) -> Result<(), Box<dyn std::error::Error>> {
115 match action {
116 Q42Action::Inspect { path } => {
117 let report = qualia_core_db::q42_volume::Q42InspectReport::from_path(path)?;
118 print!("{}", report.to_text());
119 }
120 Q42Action::Verify { path, level } => {
121 let level = qualia_core_db::q42_volume::VerifyLevel::parse(level)?;
122 let report =
123 qualia_core_db::q42_volume::verify_volume_set_from_root(path, level)?;
124 print!("{}", report.to_text());
125 match report.overall {
126 qualia_core_db::q42_volume::CheckStatus::Fail => {
127 return Err("Q42 verify failed".into());
128 }
129 qualia_core_db::q42_volume::CheckStatus::Incomplete => {
130 return Err("Q42 verify incomplete".into());
131 }
132 _ => {}
133 }
134 }
135 Q42Action::Magnet {
136 path,
137 name,
138 port,
139 webseed,
140 set,
141 commons,
142 } => {
143 let intent = if *commons {
144 qualia_core_db::q42_volume::PublicationIntent::CommonsCatalog
145 } else {
146 qualia_core_db::q42_volume::PublicationIntent::Default
147 };
148 if *set {
149 let base = webseed
150 .clone()
151 .unwrap_or_else(|| format!("http://127.0.0.1:{port}/torrent/webseed/{{hash}}"));
152 let set = qualia_core_db::q42_volume::Q42VolumeSetMagnets::for_root_with_intent(
153 path,
154 Some(&base),
155 intent,
156 )?;
157 println!("{}", set.root.magnet_uri);
158 for child in set.children {
159 println!("{}", child.magnet_uri);
160 }
161 } else {
162 let display = name
163 .clone()
164 .unwrap_or_else(|| {
165 path.file_name()
166 .and_then(|n| n.to_str())
167 .unwrap_or("volume.q42")
168 .to_string()
169 });
170 let magnet = if let Some(ws) = webseed {
171 qualia_core_db::q42_volume::Q42Magnet::for_path_named_with_intent(
172 path,
173 &display,
174 Some(ws),
175 intent,
176 )?
177 } else {
178 qualia_core_db::q42_volume::Q42Magnet::for_daemon_seed_with_intent(
179 path, &display, *port, intent,
180 )?
181 };
182 println!("{}", magnet.magnet_uri);
183 }
184 }
185 Q42Action::Compact { root, out } => {
186 let out_dir = out.clone().unwrap_or_else(|| {
187 root.parent()
188 .unwrap_or_else(|| std::path::Path::new("."))
189 .join("compacted")
190 });
191 let produced = qualia_core_db::q42_volume::compact_volume_set(root, &out_dir)?;
192 println!("{}", produced.display());
193 }
194 Q42Action::Seed {
195 path,
196 name,
197 id,
198 commons,
199 } => {
200 let intent = if *commons {
201 qualia_core_db::q42_volume::PublicationIntent::CommonsCatalog
202 } else {
203 qualia_core_db::q42_volume::PublicationIntent::Default
204 };
205 let display = name.clone().unwrap_or_else(|| {
206 path.file_name()
207 .and_then(|n| n.to_str())
208 .unwrap_or("volume.q42")
209 .to_string()
210 });
211 let ontology_id = id.clone().unwrap_or_else(|| display.clone());
212 let magnet = qualia_core_db::q42_volume::Q42Magnet::for_daemon_seed_with_intent(
213 path, &display, 4242, intent,
214 )?;
215 let record = qualia_core_db::webtorrent_seeder::register_seed(
216 qualia_core_db::webtorrent_seeder::RegisterSeedRequest {
217 info_hash: magnet.info_hash_sha1.clone(),
218 file_path: path.display().to_string(),
219 display_name: display,
220 ontology_id,
221 bandwidth_limit_kbps: 512,
222 commons_asserted: *commons,
223 },
224 )
225 .map_err(|e| e.to_string())?;
226 println!("{}", magnet.magnet_uri);
227 println!("seeded {} bytes as {}", record.file_size, record.info_hash);
228 }
229 }
230 Ok(())
231}
232
233pub fn handle_inspect(file_path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
234 if qualia_core_db::q42_volume::is_unified_volume(file_path)? {
235 let report = qualia_core_db::q42_volume::Q42InspectReport::from_path(file_path)?;
236 print!("{}", report.to_text());
237 return Ok(());
238 }
239 println!("Initializing Block Inspector for: {:?}", file_path);
240
241 let mut file = std::fs::File::open(file_path)?;
242 let mut buffer = Vec::new();
243 file.read_to_end(&mut buffer)?;
244
245 if buffer.len() % 48 != 0 {
246 eprintln!("WARNING: File size {} is not a multiple of 48 bytes (NQuin alignment). File may be corrupted.", buffer.len());
247 }
248
249 let quin_size = std::mem::size_of::<NQuin>();
250 let mut count = 0;
251
252 for chunk in buffer.chunks_exact(quin_size) {
253 let quin: NQuin = unsafe { std::ptr::read_unaligned(chunk.as_ptr() as *const NQuin) };
254 let lamport_clock = quin.extract_lamport_clock();
255 let geometric_payload = quin.extract_clean_metadata_value();
256
257 println!(
258 "[Quin {}] S: {}, P: {}, O: {}, Ctx: {}, LamportClock: {}, GeoPayload: {}, Parity: {}",
259 count,
260 quin.subject,
261 quin.predicate,
262 quin.object,
263 quin.context,
264 lamport_clock,
265 geometric_payload,
266 quin.parity
267 );
268 count += 1;
269 }
270
271 println!("Successfully inspected {} Quins.", count);
272 Ok(())
273}
274
275pub fn handle_dump(out_path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
276 println!("Dumping raw SuperBlock to: {:?}", out_path);
277
278 let mut file = OpenOptions::new()
279 .create(true)
280 .write(true)
281 .truncate(true)
282 .open(out_path)?;
283
284 let mut q1 = NQuin {
285 subject: 100,
286 predicate: 200,
287 object: 300,
288 context: 50,
289 metadata: 0,
290 parity: 0,
291 };
292 q1.set_lamport_clock(1);
293 let mut q2 = NQuin {
294 subject: 101,
295 predicate: 201,
296 object: 301,
297 context: 51,
298 metadata: 555,
299 parity: 0,
300 };
301 q2.set_lamport_clock(2);
302 let mut q3 = NQuin {
303 subject: 102,
304 predicate: 202,
305 object: 302,
306 context: 52,
307 metadata: 999,
308 parity: 0,
309 };
310 q3.set_lamport_clock(3);
311
312 let quins = [q1, q2, q3];
313
314 for quin in quins.iter() {
315 let bytes = unsafe {
316 std::slice::from_raw_parts(
317 (quin as *const NQuin) as *const u8,
318 std::mem::size_of::<NQuin>(),
319 )
320 };
321 file.write_all(bytes)?;
322 }
323
324 file.sync_all()?;
325 println!("Dumped 3 mocked Quins (144 bytes) to .q42 successfully.");
326 Ok(())
327}
328
329pub fn handle_export_solid(input: &PathBuf, output: &PathBuf) {
330 println!("============================================================");
331 println!("🌐 W3C Solid Exporter Bridge");
332 println!("============================================================");
333
334 let in_path = input.to_string_lossy().to_string();
335 let out_path = output.to_string_lossy().to_string();
336
337 match qualia_core_db::solid_ldp::SolidExporter::export_to_solid_pod(&in_path, &out_path) {
338 Ok(_) => {
339 println!("✅ Export Complete! Your data is now fully portable to any Solid Pod.");
340 }
341 Err(e) => {
342 eprintln!("❌ Export Failed: {}", e);
343 }
344 }
345}
346
347pub fn handle_verify_integrity(input: &PathBuf, dataset: &PathBuf) {
348 println!("============================================================");
349 println!("🔒 QualiaDB Zero-Allocation Integrity Verification");
350 println!(" Input : {}", input.display());
351 println!(" Dataset : {}", dataset.display());
352 println!("============================================================");
353
354 match qualia_core_db::ingest::verify_integrity(input.clone(), dataset.clone()) {
355 Ok(true) => {
356 println!("Warning: this legacy XOR result is diagnostic only, not a proof of exact graph equality.");
357 println!("\n✅ Integrity Check Passed: 100% Exact Match!");
358 println!("XOR folds and record counts match; this is not a losslessness or graph-equality proof. Use `verify-graph` for the bounded encoded-set proof.");
359 }
360 Ok(false) => {
361 eprintln!("\n❌ Integrity Check Failed: Checksums mismatch!");
362 std::process::exit(1);
363 }
364 Err(e) => {
365 eprintln!("\n❌ Integrity Verification Error: {}", e);
366 std::process::exit(1);
367 }
368 }
369}
370
371pub fn handle_verify_graph(
373 input: &PathBuf,
374 dataset: &PathBuf,
375 memory_mib: u64,
376 temp_gib: u64,
377) -> Result<(), Box<dyn std::error::Error>> {
378 let memory_limit_bytes = memory_mib
379 .checked_mul(1024 * 1024)
380 .and_then(|bytes| usize::try_from(bytes).ok())
381 .ok_or_else(|| std::io::Error::other("--memory-mib is too large for this platform"))?;
382 let temporary_byte_budget = temp_gib
383 .checked_mul(1024 * 1024 * 1024)
384 .ok_or_else(|| std::io::Error::other("--temp-gib is too large"))?;
385
386 println!("============================================================");
387 println!("QualiaDB bounded encoded-graph proof");
388 println!(" Input : {}", input.display());
389 println!(" Q42 : {}", dataset.display());
390 println!(" RAM budget : {memory_mib} MiB");
391 println!(" Temp budget : {temp_gib} GiB");
392 println!("============================================================");
393
394 let report = qualia_core_db::graph_proof::prove_cli_ntriples_q42_equivalence(
395 input,
396 dataset,
397 qualia_core_db::graph_proof::GraphProofOptions {
398 memory_limit_bytes,
399 temporary_byte_budget,
400 },
401 )?;
402
403 println!("Source records : {}", report.source_records);
404 println!("Q42 records : {}", report.q42_records);
405 println!("Unique source quads : {}", report.source_unique_records);
406 println!("Unique Q42 quads : {}", report.q42_unique_records);
407 println!("Missing from Q42 : {}", report.missing_from_q42);
408 println!("Unexpected in Q42 : {}", report.unexpected_in_q42);
409 println!("Skipped source lines : {}", report.source_skipped_lines);
410
411 if !report.encoded_sets_match() {
412 if let Some(record) = report.first_missing {
413 println!("First missing encoded quad : {record:016X?}");
414 }
415 if let Some(record) = report.first_unexpected {
416 println!("First unexpected encoded quad : {record:016X?}");
417 }
418 return Err(std::io::Error::other("encoded graph sets differ").into());
419 }
420
421 match report.rdf_isomorphism {
422 qualia_core_db::graph_proof::RdfIsomorphismStatus::GroundGraphProven => {
423 println!("PASS: exact ground-graph equivalence is proven in the Q42 encoding.");
424 Ok(())
425 }
426 qualia_core_db::graph_proof::RdfIsomorphismStatus::BlankNodeCanonicalizationRequired => {
427 Err(std::io::Error::other(
428 "encoded sets match only under blank-node label identity; RDF isomorphism requires canonical lexical blank-node support",
429 )
430 .into())
431 }
432 qualia_core_db::graph_proof::RdfIsomorphismStatus::Different => {
433 Err(std::io::Error::other("encoded graph sets differ").into())
434 }
435 }
436}
437
438pub fn handle_import(
439 input: &PathBuf,
440 output: &PathBuf,
441 strip_literals: bool,
442 segment_mib: Option<u64>,
443) {
444 println!("============================================================");
445 println!("📥 QualiaDB Native RDF/XML Ingestion Pipeline");
446 println!("============================================================");
447
448 let in_path = input.to_string_lossy().to_string();
449 let out_path = output.to_string_lossy().to_string();
450 let mode = if strip_literals {
451 qualia_core_db::ingest::IngestMode::StripLiterals
452 } else {
453 qualia_core_db::ingest::IngestMode::Complete
454 };
455
456 let result = match segment_mib {
457 Some(mib) => match mib.checked_mul(1024 * 1024) {
458 Some(bytes) if bytes != 0 => {
459 println!("Publishing a Q42 logical volume with {mib} MiB child cap.");
460 qualia_core_db::ingest::streaming_import_rdf_volume_set_with_mode(
461 &in_path, &out_path, mode, bytes,
462 )
463 }
464 _ => Err(std::io::Error::new(
465 std::io::ErrorKind::InvalidInput,
466 "--segment-mib must be greater than zero",
467 )),
468 },
469 None => qualia_core_db::ingest::streaming_import_rdf_with_mode(&in_path, &out_path, mode),
470 };
471 match result {
472 Ok(quin_count) => {
473 println!("✨ Done! Wrote {quin_count} Super-Quins.");
474 }
475 Err(e) => {
476 eprintln!("❌ Import Failed: {}", e);
477 }
478 }
479}
480
481pub fn handle_ingest(format: &IngestFormat) {
482 match format {
483 IngestFormat::Semantic { file } => {
484 let out_path = file.with_extension("q42");
485 println!("Detecting format for: {}", file.display());
486 match crate::ingest::ingest_auto(&file, &out_path) {
487 Ok((stats, fmt)) => {
488 println!("Format : {}", fmt.label());
489 println!("Triples: {}", stats.triples_ingested);
490 println!("Blocks : {}", stats.blocks_written);
491 println!("Output : {}", out_path.display());
492 println!("Done.");
493 }
494 Err(e) => eprintln!("Ingest error: {e}"),
495 }
496 }
497 IngestFormat::Csv { file, map } => {
498 println!("CSV ingest for {:?} using map {:?}", file, map);
499 match crate::ingest::mapper::compile_shacl_mapping(map) {
500 Ok(mut profile) => {
501 let path_str = file.to_string_lossy();
502 let out_path = file.with_extension("q42").to_string_lossy().into_owned();
503 crate::ingest::csv_mapper::stream_csv_to_quins(
504 &path_str,
505 &out_path,
506 &mut profile,
507 );
508 println!("✅ CSV Ingest Complete");
509 }
510 Err(e) => eprintln!("❌ Failed to compile SHACL mapping: {}", e),
511 }
512 }
513 IngestFormat::Json { file, map } => {
514 println!("JSON ingest for {:?} using map {:?}", file, map);
515 match crate::ingest::mapper::compile_shacl_mapping(map) {
516 Ok(profile) => {
517 let path_str = file.to_string_lossy();
518 let out_path = file.with_extension("q42").to_string_lossy().into_owned();
519 crate::ingest::json_mapper::stream_json_to_quins(
520 &path_str, &out_path, &profile,
521 );
522 println!("✅ JSON Ingest Complete");
523 }
524 Err(e) => eprintln!("❌ Failed to compile SHACL mapping: {}", e),
525 }
526 }
527 }
528}
529
530pub fn handle_query(dialect: &QueryDialect) {
531 match dialect {
532 QueryDialect::Sparql {
533 vault,
534 query_string,
535 file,
536 } => {
537 let qs = if let Some(q) = query_string {
538 q.clone()
539 } else if let Some(f) = file {
540 std::fs::read_to_string(f).expect("Failed to read SPARQL file")
541 } else {
542 panic!("Must provide either a query_string or a file");
543 };
544 run_sparql_query(&vault, &qs);
545 }
546 QueryDialect::SparqlStar {
547 vault,
548 query_string,
549 file,
550 } => {
551 let qs = if let Some(q) = query_string {
552 q.clone()
553 } else if let Some(f) = file {
554 std::fs::read_to_string(f).expect("Failed to read SPARQL-Star file")
555 } else {
556 panic!("Must provide either a query_string or a file");
557 };
558 run_sparql_query(&vault, &qs);
559 }
560 }
561}
562
563pub fn handle_compress(input: &PathBuf, output: &PathBuf) {
564 let ext = input
565 .extension()
566 .and_then(|e| e.to_str())
567 .unwrap_or("")
568 .to_lowercase();
569 let is_q42 = ext == "q42";
570
571 println!("============================================================");
572 println!("QualiaDB LZ4 Block-Stream Compressor");
573 println!(" input : {}", input.display());
574 println!(" output : {}", output.display());
575 println!(
576 " mode : {}",
577 if is_q42 {
578 "SuperBlock → raw Quins"
579 } else {
580 "raw bytes"
581 }
582 );
583 println!("============================================================");
584
585 let result = if is_q42 {
586 crate::compress::compress_q42(input, output)
587 } else {
588 crate::compress::compress_raw(input, output)
589 };
590
591 match result {
592 Ok(stats) => {
593 println!("Done.");
594 println!(
595 " Input : {:.1} MB",
596 stats.input_bytes as f64 / 1_048_576.0
597 );
598 println!(
599 " Output : {:.1} MB",
600 stats.output_bytes as f64 / 1_048_576.0
601 );
602 println!(" Blocks : {}", stats.blocks);
603 println!(" Ratio : {:.2}x", stats.ratio);
604 }
605 Err(e) => eprintln!("Compression failed: {}", e),
606 }
607}
608
609pub fn handle_profile(action: &ProfileAction) {
610 match action {
611 ProfileAction::Compile { input, out } => {
612 let out_path = out.clone().unwrap_or_else(|| input.with_extension("qchk"));
613 println!("============================================================");
614 println!("⚡ Qualia Capability Profile Compiler");
615 println!(" input : {}", input.display());
616 println!(" output : {}", out_path.display());
617 println!("============================================================");
618 match std::fs::read_to_string(input) {
619 Err(e) => eprintln!("❌ Failed to read profile source: {}", e),
620 Ok(jsonld_src) => {
621 let stem = input.file_stem().unwrap_or_default().to_string_lossy();
622 let profile_id = qualia_core_db::q_hash(&format!("profile:{}", stem));
623 let mut chk_bytes: Vec<u8> = Vec::new();
624 chk_bytes.extend_from_slice(b"QCHK");
625 chk_bytes.extend_from_slice(&profile_id.to_le_bytes());
626 chk_bytes.extend_from_slice(&(jsonld_src.len() as u32).to_le_bytes());
627 chk_bytes.extend_from_slice(jsonld_src.as_bytes());
628 match std::fs::write(&out_path, &chk_bytes) {
629 Ok(_) => {
630 println!(
631 "✅ Compiled profile 0x{:016X} ({} bytes)",
632 profile_id,
633 chk_bytes.len()
634 );
635 println!(" Stem : {}", stem);
636 println!(" Output: {}", out_path.display());
637 println!(" Next : qualia-cli ingest --input data.nt --output out --profile {}", out_path.display());
638 }
639 Err(e) => eprintln!("❌ Write failed: {}", e),
640 }
641 }
642 }
643 }
644 ProfileAction::List => {
645 println!("============================================================");
646 println!("📋 Registered Capability Profiles");
647 println!("============================================================");
648 println!(" (Profiles are registered when ingested via ExternalSorter)");
649 println!(" Known profile ID namespaces:");
650 let known = [
651 (
652 "profile:general",
653 "General purpose — no engine restrictions",
654 ),
655 (
656 "profile:health",
657 "Health/Clinical — NativeClinicalRisk, NativeBioAlignment",
658 ),
659 (
660 "profile:chemistry",
661 "Organic Chemistry — NativeChemicalSynthesis, NativeLipinski",
662 ),
663 (
664 "profile:research",
665 "Research — all scientific opcodes, no financial engines",
666 ),
667 (
668 "profile:legal",
669 "Legal/Deontic — OP_OBLIGATE, OP_FORBID, OP_PERMIT",
670 ),
671 (
672 "profile:financial",
673 "Financial — ILP dispatchers, tax schema, audit trail",
674 ),
675 ];
676 for (name, desc) in &known {
677 println!(
678 " 0x{:016X} {} — {}",
679 qualia_core_db::q_hash(name),
680 name,
681 desc
682 );
683 }
684 }
685 ProfileAction::Inspect { file } => {
686 println!("============================================================");
687 println!("🔎 Profile Inspector: {}", file.display());
688 println!("============================================================");
689 match std::fs::read(file) {
690 Err(e) => eprintln!("❌ Cannot read file: {}", e),
691 Ok(bytes) => {
692 if bytes.len() < 16 || &bytes[0..4] != b"QCHK" {
693 eprintln!(
694 "❌ Not a valid QCHK profile (.qchk or legacy .chk missing QCHK magic)"
695 );
696 } else {
697 let profile_id = u64::from_le_bytes(bytes[4..12].try_into().unwrap());
698 let payload_len =
699 u32::from_le_bytes(bytes[12..16].try_into().unwrap()) as usize;
700 let payload =
701 &bytes[16..16 + payload_len.min(bytes.len().saturating_sub(16))];
702 println!(" Profile ID : 0x{:016X}", profile_id);
703 println!(" Payload : {} bytes (JSON-LD source)", payload_len);
704 println!(" Total file : {} bytes", bytes.len());
705 println!();
706 println!("--- JSON-LD Source ---");
707 println!("{}", String::from_utf8_lossy(payload));
708 }
709 }
710 }
711 }
712 }
713}
714
715pub fn handle_capabilities(list: bool) {
716 if list {
717 println!("============================================================");
718 println!("🧠 QualiaDB Runtime Capability Registry");
719 println!("============================================================");
720 for capability in qualia_core_db::CAPABILITY_DESCRIPTORS {
721 println!(
722 " - {} [{}] -> {}",
723 capability.name,
724 capability.domain,
725 capability.mcp_tools.join(", ")
726 );
727 }
728 println!("============================================================");
729 } else {
730 println!("Use `qualia-cli capabilities --list` to view capabilities.");
731 }
732}