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>;