mdtask_core/lib.rs
1//! `mdtask-core` parses a markdown task file into a typed job tree and runs jobs
2//! from it. It is embeddable, execution-capable, and dependency-free.
3//!
4//! A task file is ordinary markdown (a `tasks.md`, a `maskfile.md`, or a project
5//! `README.md`): a heading is a job, the first fenced code block under it is the
6//! script, and `Key: value` lines in the body carry metadata. The format is its
7//! own grammar, a graceful superset that borrows xc's metadata vocabulary and
8//! mask's runtime shape (per-fence interpreter, positional args). It reads cleanly
9//! in those tools where the features overlap, but claims no compatibility.
10//!
11//! ```
12//! let tf = mdtask_core::parse("\
13//! ## greet\n\
14//! \n\
15//! Args: name\n\
16//! \n\
17//! ```sh\n\
18//! echo \"hello {{ name }}\"\n\
19//! ```\n");
20//! let job = tf.job("greet").unwrap();
21//! assert_eq!(job.args[0].name, "name");
22//! ```
23//!
24//! Parsing is pure. A consumer sees only jobs and their metadata: interpreter
25//! selection, argv building, working-directory resolution, and spawning are all
26//! internal. Three entry points run a job and its `Requires:` chain: [`run`]
27//! inherits stdio (streaming, for a CLI), [`run_captured`] captures the aggregated
28//! output (for an embedder), and [`run_agent`] adds the agent allow gate and the
29//! injection guard (for an MCP or agent surface). The parser is line-based (no
30//! CommonMark dependency), so a `#` or `Key:` inside a fenced block is never
31//! mistaken for structure.
32
33mod cancel;
34mod deps;
35mod discover;
36mod model;
37mod parse;
38mod run;
39
40pub use cancel::Cancel;
41pub use discover::find_task_files;
42pub use model::{Arg, DepError, Job, MissingArg, Requirement, RunError, TaskFile};
43pub use parse::parse;
44pub use run::{agent_jobs, run, run_agent, run_agent_cancellable, run_captured};