Skip to main content

parallax_cli/
lib.rs

1//! Parallax CLI library (shared by `parallax` and `plx` binaries).
2
3#![deny(unsafe_code)]
4
5mod app;
6mod atlas_cmd;
7mod connectors_cmd;
8mod continuum;
9mod emit;
10mod horizon_cmd;
11mod mirror_cmd;
12mod transmute_cmd;
13
14use clap::{Parser, Subcommand, ValueEnum};
15use parallax_core::{ComponentVersions, ParallaxError, PARALLAX_VERSION};
16use std::path::PathBuf;
17use std::process::ExitCode;
18use std::sync::OnceLock;
19
20fn long_version() -> &'static str {
21    static LONG: OnceLock<String> = OnceLock::new();
22    LONG.get_or_init(|| {
23        let v = ComponentVersions::current();
24        format!(
25            "{}\npir_schema {}\nprotocol {}\nsnapshot {}\nadapter_interface {}\nues_format {}\npcir_schema {}\npuir_schema {}\nmirror_link_format {}",
26            v.parallax,
27            v.pir_schema,
28            v.protocol,
29            v.snapshot,
30            v.adapter_interface,
31            v.ues_format,
32            v.pcir_schema,
33            v.puir_schema,
34            v.mirror_link_format
35        )
36    })
37    .as_str()
38}
39
40/// Parallax CLI.
41#[derive(Debug, Parser)]
42#[command(
43    name = "parallax",
44    about = "Parallax — universal polyglot execution & state migration",
45    version = PARALLAX_VERSION,
46    long_version = long_version()
47)]
48pub struct Cli {
49    /// Emit machine-readable JSON for commands that support it.
50    #[arg(long, global = true)]
51    pub json: bool,
52
53    /// Verbose diagnostics.
54    #[arg(short, long, global = true)]
55    pub verbose: bool,
56
57    /// Enable structured tracing to stderr.
58    #[arg(long, global = true)]
59    pub trace: bool,
60
61    #[command(subcommand)]
62    pub command: Commands,
63}
64
65/// Top-level commands.
66#[derive(Debug, Subcommand)]
67pub enum Commands {
68    /// Execute a guest program.
69    Run {
70        /// Program file.
71        file: PathBuf,
72        /// Runtime override (python|javascript|wasm).
73        #[arg(long, short = 'r')]
74        runtime: Option<String>,
75        /// Timeout in milliseconds.
76        #[arg(long, default_value_t = 30000)]
77        timeout_ms: u64,
78        /// WASM entry export name.
79        #[arg(long)]
80        entry: Option<String>,
81        /// Capture binding names after execution (comma-separated).
82        #[arg(long)]
83        capture: Option<String>,
84    },
85    /// Inspect a `.plx` snapshot.
86    Inspect {
87        /// Snapshot path.
88        snapshot: PathBuf,
89    },
90    /// Capture program state into a `.plx` snapshot.
91    Snapshot {
92        /// Program file.
93        file: PathBuf,
94        /// Output `.plx` path.
95        #[arg(long, short = 'o')]
96        output: PathBuf,
97        /// Runtime override.
98        #[arg(long, short = 'r')]
99        runtime: Option<String>,
100        /// Binding names to capture (default: state).
101        #[arg(long, default_value = "state")]
102        capture: String,
103        /// Optional label.
104        #[arg(long)]
105        label: Option<String>,
106    },
107    /// Restore a snapshot into a runtime (verifies restore).
108    Restore {
109        /// Snapshot path.
110        snapshot: PathBuf,
111        /// Target runtime.
112        #[arg(long, short = 't')]
113        target: String,
114    },
115    /// Migrate state (PIR) or an entire project (Transmute).
116    Migrate {
117        /// Source program file, PIR JSON, or project directory.
118        file: PathBuf,
119        /// Target runtime (python|javascript) or language (rust|go|…).
120        #[arg(long, short = 't')]
121        to: String,
122        /// Source runtime / language override.
123        #[arg(long, short = 'f')]
124        from: Option<String>,
125        /// Binding names to migrate for value mode (default: state).
126        #[arg(long, default_value = "state")]
127        capture: String,
128        /// Allow known-lossy conversions (e.g. unsafe int → Number).
129        #[arg(long)]
130        allow_lossy: bool,
131        /// Disable automatic BigInt for integers outside JS safe range.
132        #[arg(long)]
133        no_prefer_bigint: bool,
134        /// Reject unsupported values instead of encoding them as PIR Unsupported.
135        #[arg(long)]
136        reject_unsupported: bool,
137        /// Strict policy: reject unsupported and potentially-lossy conversions.
138        #[arg(long)]
139        strict: bool,
140        /// Write restored target-language source / project output.
141        #[arg(long, short = 'o')]
142        output: Option<PathBuf>,
143        /// Also write a `.plx` snapshot of migrated PIR (value mode).
144        #[arg(long)]
145        snapshot: Option<PathBuf>,
146        /// Offline-only: treat file as JSON PIR document (skip live capture).
147        #[arg(long)]
148        pir_input: bool,
149        /// Migration mode: `value` (PIR), `continuation` (UES), or `project` (Transmute).
150        #[arg(long, default_value = "auto")]
151        mode: String,
152        /// Dry-run project migration (plan only).
153        #[arg(long)]
154        dry_run: bool,
155        /// Verify migrated project (build/tests/behavior).
156        #[arg(long)]
157        verify: bool,
158        /// Target code style: idiomatic|literal.
159        #[arg(long, default_value = "idiomatic")]
160        target_style: String,
161        /// Preserve source layout when possible.
162        #[arg(long)]
163        preserve_layout: bool,
164        /// Require successful target build.
165        #[arg(long)]
166        require_build: bool,
167        /// Require target tests to pass.
168        #[arg(long)]
169        require_tests: bool,
170        /// Minimum overall confidence \[0.0–1.0\].
171        #[arg(long)]
172        min_confidence: Option<f64>,
173        /// Fail if unsupported regions remain.
174        #[arg(long)]
175        fail_on_unsupported: bool,
176        /// Keep language/role untranslated (repeatable), e.g. sql, shell.
177        #[arg(long)]
178        keep: Vec<String>,
179        /// Incremental update (reuse `.parallax/` workspace).
180        #[arg(long)]
181        update: bool,
182        /// Force project (Transmute) migration even for a single file.
183        #[arg(long)]
184        project: bool,
185    },
186    /// Look up the original source location for a generated line (`file:line`).
187    Origin {
188        /// Generated `path:line` (relative to migrated project).
189        location: String,
190        /// Migrated project root (directory containing `.plxmap.json`).
191        #[arg(long, short = 'C', default_value = ".")]
192        project: PathBuf,
193    },
194    /// Continuum: explicit safepoint capture / UES inspect / checkpoint resume.
195    Continuum {
196        /// Program with `parallax.checkpoint(...)`, or a UES JSON file with `--inspect-ues`.
197        file: PathBuf,
198        /// Runtime override.
199        #[arg(long, short = 'r')]
200        runtime: Option<String>,
201        /// Write captured UES JSON.
202        #[arg(long, short = 'o')]
203        output: Option<PathBuf>,
204        /// Resume post-checkpoint region on the same runtime (experimental).
205        #[arg(long)]
206        resume: bool,
207        /// Target runtime for contract analysis (default: same as source).
208        #[arg(long, short = 't')]
209        to: Option<String>,
210        /// Only analyze the migration contract (no execute).
211        #[arg(long)]
212        analyze_only: bool,
213        /// Inspect a previously written UES file instead of running a program.
214        #[arg(long)]
215        inspect_ues: bool,
216        /// Timeout in milliseconds.
217        #[arg(long, default_value_t = 30000)]
218        timeout_ms: u64,
219    },
220    /// List registered runtimes and status.
221    Runtimes,
222    /// List the full language connector catalog (all languages Parallax knows).
223    Connectors {
224        /// Optional connector id / alias filter.
225        id: Option<String>,
226        /// Show highlighted transmute/mirror pairs.
227        #[arg(long)]
228        pairs: bool,
229        /// Filter by family (systems|scripting|managed_vm|…).
230        #[arg(long)]
231        family: Option<String>,
232        /// Filter by maturity (production|experimental|scaffold|planned).
233        #[arg(long)]
234        maturity: Option<String>,
235    },
236    /// Atlas: list / inspect modular adapters.
237    Adapters {
238        #[command(subcommand)]
239        command: Option<AdaptersCommand>,
240    },
241    /// Atlas: scaffold or validate a third-party adapter (stubs).
242    Adapter {
243        #[command(subcommand)]
244        command: AdapterCommand,
245    },
246    /// Atlas: detect project stack and adapter plan.
247    Analyze {
248        /// Project root.
249        #[arg(default_value = ".")]
250        path: PathBuf,
251        /// Target language for stack suggestion / estimate.
252        #[arg(long, short = 't')]
253        to: Option<String>,
254        /// Write `parallax.lock` into the project root.
255        #[arg(long)]
256        write_lock: bool,
257    },
258    /// Atlas: list known target stack presets.
259    Stacks,
260    /// Atlas: show dependency equivalence mappings.
261    Mappings {
262        /// Optional package / ecosystem filter.
263        query: Option<String>,
264    },
265    /// Atlas: language-pair compatibility scores.
266    Compatibility {
267        /// Source language.
268        source: String,
269        /// Target language.
270        target: String,
271    },
272    /// Atlas: report scaffold / unsupported adapter surface for a project.
273    Unsupported {
274        #[arg(default_value = ".")]
275        path: PathBuf,
276    },
277    /// Atlas: explain why a target stack was selected.
278    ExplainStack {
279        #[arg(default_value = ".")]
280        path: PathBuf,
281        #[arg(long, short = 't')]
282        to: Option<String>,
283    },
284    /// Event Horizon: inspect a project semantically (no migration).
285    Observe {
286        #[arg(default_value = ".")]
287        path: PathBuf,
288    },
289    /// Event Horizon: report hard barriers + preservation strategy.
290    Impossible {
291        #[arg(default_value = ".")]
292        path: PathBuf,
293        #[arg(long, short = 't')]
294        to: Option<String>,
295        /// maximum-compatibility|maximum-native|maximum-performance|minimum-dependencies|fastest-migration
296        #[arg(long)]
297        strategy: Option<String>,
298    },
299    /// Event Horizon: shrink polyglot islands one step.
300    Dissolve {
301        #[arg(default_value = ".")]
302        path: PathBuf,
303        #[arg(long, short = 't')]
304        to: Option<String>,
305    },
306    /// Event Horizon: compatibility debt / target purity.
307    Debt {
308        #[arg(default_value = ".")]
309        path: PathBuf,
310        #[arg(long, short = 't')]
311        to: Option<String>,
312    },
313    /// Event Horizon: check if source runtime can be detached.
314    Detach {
315        #[arg(default_value = ".")]
316        path: PathBuf,
317        #[arg(long, short = 't')]
318        to: Option<String>,
319    },
320    /// Event Horizon: source-less behavioral reconstruction (scaffold).
321    Reconstruct,
322    /// Event Horizon: propose replacing capsules/islands with native semantics.
323    OptimizeMigration {
324        #[arg(default_value = ".")]
325        path: PathBuf,
326        #[arg(long, short = 't')]
327        to: Option<String>,
328    },
329    /// Event Horizon: explain a barrier from `plx impossible`.
330    ExplainBarrier {
331        /// Barrier id from `plx impossible`.
332        id: u32,
333        #[arg(long, short = 'C', default_value = ".")]
334        path: PathBuf,
335        #[arg(long, short = 't')]
336        to: Option<String>,
337    },
338    /// Event Horizon: semantic blame for a generated location (scaffold).
339    Blame {
340        /// `file:line` in the target project.
341        location: String,
342    },
343    /// Event Horizon: semantic cherry-pick (scaffold).
344    CherryPick { commit: String },
345    /// Event Horizon: show / emit a semantic patch (.plxp) example.
346    Patch {
347        /// Print the built-in example patch.
348        #[arg(long)]
349        example: bool,
350    },
351    /// Show capability matrix.
352    Capabilities {
353        /// Optional runtime filter.
354        runtime: Option<String>,
355        /// Show Continuum continuation capability matrix.
356        #[arg(long)]
357        continuations: bool,
358    },
359    /// Probe host for runtime readiness.
360    Doctor,
361    /// Micro-benchmark capture/migrate/restore for the demo shape.
362    Bench {
363        /// Iterations.
364        #[arg(long, default_value_t = 5)]
365        iterations: u32,
366        /// Source file (default: examples/demo.py if present).
367        #[arg(long)]
368        file: Option<PathBuf>,
369        /// Target runtime.
370        #[arg(long, default_value = "javascript")]
371        to: String,
372    },
373    /// Link source and target projects for Mirror sync.
374    Link {
375        /// Source project directory.
376        source: PathBuf,
377        /// Target project directory.
378        target: PathBuf,
379        /// Sync policy: source-authoritative|target-authoritative|bidirectional|manual.
380        #[arg(long, default_value = "source-authoritative")]
381        policy: String,
382    },
383    /// Incrementally synchronize a linked pair.
384    Sync {
385        /// Path to linked project (target or `.parallax-link`).
386        #[arg(default_value = ".")]
387        path: PathBuf,
388        /// Check freshness without modifying files.
389        #[arg(long)]
390        check: bool,
391        /// Attempt reverse sync (target → source) when safe.
392        #[arg(long)]
393        reverse: bool,
394        /// Run formatter/linter checks.
395        #[arg(long)]
396        lint: bool,
397        /// Write a patch preview only.
398        #[arg(long)]
399        patch: bool,
400        /// Skip build/test verification.
401        #[arg(long)]
402        no_verify: bool,
403        /// Request property-based equivalence notes.
404        #[arg(long)]
405        property: bool,
406        /// Deterministic verification notes.
407        #[arg(long)]
408        deterministic: bool,
409    },
410    /// Show Mirror link status / drift.
411    Status {
412        #[arg(default_value = ".")]
413        path: PathBuf,
414    },
415    /// CI entrypoint (sync --check + verify).
416    Ci {
417        #[arg(default_value = ".")]
418        path: PathBuf,
419    },
420    /// Show sync history.
421    History {
422        #[arg(default_value = ".")]
423        path: PathBuf,
424    },
425    /// Rollback last sync snapshot.
426    Rollback {
427        #[arg(default_value = ".")]
428        path: PathBuf,
429    },
430    /// Explain a generated location (`file:line`).
431    Explain {
432        location: String,
433        #[arg(long, short = 'C', default_value = ".")]
434        path: PathBuf,
435    },
436    /// Explain why a target file changed.
437    Why {
438        file: String,
439        #[arg(long, short = 'C', default_value = ".")]
440        path: PathBuf,
441    },
442    /// Verify linked target (differential tests).
443    Verify {
444        #[arg(default_value = ".")]
445        path: PathBuf,
446        #[arg(long)]
447        property: bool,
448        #[arg(long)]
449        deterministic: bool,
450        #[arg(long)]
451        performance: bool,
452    },
453    /// Show version / build info.
454    Version {
455        #[arg(long, value_enum, default_value_t = VersionFmt::Text)]
456        format: VersionFmt,
457    },
458}
459
460/// `plx adapters` subcommands.
461#[derive(Debug, Subcommand)]
462pub enum AdaptersCommand {
463    /// List adapters (default).
464    List {
465        /// Optional filter (id, language, type).
466        query: Option<String>,
467    },
468    /// Show adapter manifest + capabilities.
469    Info {
470        /// Adapter id, name, or language.
471        id: String,
472    },
473    /// Show capability flags only.
474    Capabilities { id: String },
475    /// Health scores.
476    Health,
477    /// Check / apply adapter package updates (built-ins only today).
478    Update {
479        #[arg(long)]
480        check: bool,
481    },
482    /// Local adapter telemetry summary.
483    Report,
484}
485
486/// `plx adapter` tooling.
487#[derive(Debug, Subcommand)]
488pub enum AdapterCommand {
489    /// Generate a new adapter scaffold (not yet implemented).
490    New { name: String },
491    /// Validate an adapter package (not yet implemented).
492    Validate { path: PathBuf },
493}
494
495/// Version output format.
496#[derive(Debug, Clone, ValueEnum)]
497pub enum VersionFmt {
498    /// Plain text.
499    Text,
500    /// JSON object.
501    Json,
502}
503
504/// Entry point shared by binaries.
505pub async fn entry(cli: Cli) -> ExitCode {
506    parallax_diagnostics::init_tracing(cli.trace, cli.verbose);
507    let json = cli.json;
508    match app::run(cli).await {
509        Ok(()) => ExitCode::SUCCESS,
510        Err(err) => {
511            if json {
512                if let Ok(v) = serde_json::to_string_pretty(&err) {
513                    eprintln!("{v}");
514                } else {
515                    eprint!("{}", err.format_report(true));
516                }
517            } else {
518                eprint!("{}", err.format_report(true));
519            }
520            ExitCode::from(1)
521        }
522    }
523}
524
525/// Result alias.
526pub type Result<T> = std::result::Result<T, ParallaxError>;