Skip to main content

qualia_cli/handlers/
misc.rs

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
371/// Run the exact, bounded-memory encoded graph proof.
372pub 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}