///|
fn usage() -> String {
let text =
#|MoonFlowGraph workflow CLI
#|
#|Usage:
#| moon run cmd/workflow
#| moon run cmd/workflow -- --json ''
#| moon run cmd/workflow -- --help
#|
#|With no arguments, a built-in research workflow example is imported,
#|validated, planned, rendered as Mermaid, and exported back to normalized
#|workflow-spec-v1 JSON.
#|
text
}
///|
fn sample_workflow_json() -> String {
let text =
#|{
#| "schema_version": 1,
#| "tasks": [
#| {
#| "id": "collect_papers",
#| "title": "Collect papers",
#| "description": "Record the search scope and collect papers for the research question.",
#| "outputs": ["search_protocol.md", "paper_index.md"],
#| "tags": ["research", "input"]
#| },
#| {
#| "id": "prepare_dataset",
#| "title": "Prepare dataset",
#| "description": "Freeze the dataset snapshot used by the baseline run.",
#| "outputs": ["dataset.parquet", "dataset_card.md"],
#| "tags": ["data", "input"]
#| },
#| {
#| "id": "extract_claims",
#| "title": "Extract claims",
#| "description": "Map paper claims and assumptions to measurable evaluation criteria.",
#| "inputs": ["search_protocol.md", "paper_index.md"],
#| "outputs": ["claims.json"],
#| "tags": ["agent", "analysis"]
#| },
#| {
#| "id": "run_baseline",
#| "title": "Run baseline",
#| "description": "Run the baseline with a recorded configuration and random seed.",
#| "inputs": ["dataset.parquet"],
#| "outputs": ["baseline_config.json", "baseline_metrics.json"],
#| "tags": ["experiment"]
#| },
#| {
#| "id": "compare_metrics",
#| "title": "Compare metrics",
#| "description": "Compare results against the criteria extracted from the literature.",
#| "inputs": ["claims.json", "baseline_metrics.json"],
#| "outputs": ["comparison.md"],
#| "tags": ["evaluation"]
#| },
#| {
#| "id": "write_report",
#| "title": "Write report",
#| "description": "Produce a Markdown handoff report.",
#| "inputs": ["comparison.md"],
#| "outputs": ["report.md"],
#| "tags": ["report"]
#| }
#| ],
#| "dependencies": [
#| { "before": "collect_papers", "after": "extract_claims" },
#| { "before": "prepare_dataset", "after": "run_baseline" },
#| { "before": "extract_claims", "after": "compare_metrics" },
#| { "before": "run_baseline", "after": "compare_metrics" },
#| { "before": "compare_metrics", "after": "write_report" }
#| ]
#|}
#|
text
}
///|
fn parse_input_arg(args : Array[String]) -> Result[String?, String] {
if args.length() <= 1 {
return Ok(None)
}
let first = args[1]
if first == "--help" || first == "-h" {
return Ok(Some("__help__"))
}
if first == "--json" {
if args.length() < 3 {
return Err("missing value after --json")
}
if args.length() > 3 {
return Err("unexpected extra arguments after --json")
}
return Ok(Some(args[2]))
}
Err("unknown argument: \{first}")
}
///|
fn join_task_ids(ids : Array[@moonflowgraph.TaskId], sep : String) -> String {
let out = StringBuilder()
for i = 0; i < ids.length(); i = i + 1 {
if i > 0 {
out.write_string(sep)
}
out.write_string(ids[i].value())
}
out.to_string()
}
///|
fn write_execution_order(
out : StringBuilder,
order : Array[@moonflowgraph.TaskId],
) -> Unit {
for i = 0; i < order.length(); i = i + 1 {
out <+ "\{i + 1}. \{order[i].value()}\n"
}
}
///|
fn write_batches(
out : StringBuilder,
batches : Array[Array[@moonflowgraph.TaskId]],
) -> Unit {
for i = 0; i < batches.length(); i = i + 1 {
out <+ "- batch \{i + 1}: \{join_task_ids(batches[i], ", ")}\n"
}
}
///|
fn render_workflow_report(graph : @moonflowgraph.FlowGraph) -> String {
let roots = match graph.roots() {
Ok(ids) => ids
Err(err) => abort("roots: \{err.message()}")
}
let leaves = match graph.leaves() {
Ok(ids) => ids
Err(err) => abort("leaves: \{err.message()}")
}
let plan = match graph.plan() {
Ok(plan) => plan
Err(err) => abort("plan: \{err.message()}")
}
let mermaid = match graph.to_mermaid() {
Ok(value) => value
Err(err) => abort("mermaid: \{err.message()}")
}
let workflow_json = graph.to_workflow_spec().to_json()
let out = StringBuilder()
out <+ "# MoonFlowGraph Workflow Check\n\n"
out <+ "- Tasks: \{graph.task_count()}\n"
out <+ "- Dependencies: \{graph.dependency_count()}\n"
out <+ "- Roots: \{join_task_ids(roots, ", ")}\n"
out <+ "- Leaves: \{join_task_ids(leaves, ", ")}\n\n"
out <+ "## Execution Order\n\n"
write_execution_order(out, plan.order())
out <+ "\n## Parallel Batches\n\n"
write_batches(out, plan.batches())
out <+ "\n--- MERMAID ---\n"
out.write_string(mermaid)
out <+ "--- WORKFLOW JSON ---\n"
out.write_string(workflow_json)
out.to_string()
}
///|
fn main {
let input = match parse_input_arg(@env.args()) {
Ok(None) => sample_workflow_json()
Ok(Some(value)) =>
if value == "__help__" {
println(usage())
return
} else {
value
}
Err(message) => {
println("argument error: \{message}")
println(usage())
abort("invalid workflow CLI arguments")
}
}
let graph = match @moonflowgraph.FlowGraph::from_workflow_json(input) {
Ok(graph) => graph
Err(err) => {
println("workflow import failed: \{err.message()}")
abort("workflow import failed")
}
}
println(render_workflow_report(graph))
}