Expand description
mdtask-core parses a markdown task file into a typed job tree and runs jobs
from it. It is embeddable, execution-capable, and dependency-free.
A task file is ordinary markdown (a tasks.md, a maskfile.md, or a project
README.md): a heading is a job, the first fenced code block under it is the
script, and Key: value lines in the body carry metadata. The format is its
own grammar, a graceful superset that borrows xc’s metadata vocabulary and
mask’s runtime shape (per-fence interpreter, positional args). It reads cleanly
in those tools where the features overlap, but claims no compatibility.
let tf = mdtask_core::parse("\
# greet\n\
\n\
Args: name\n\
\n\
```sh\n\
echo \"hello {{ name }}\"\n\
```\n");
let job = tf.job("greet").unwrap();
assert_eq!(job.args[0].name, "name");Parsing is pure. A consumer sees only jobs and their metadata: interpreter
selection, argv building, working-directory resolution, and spawning are all
internal. Three entry points run a job and its Requires: chain: run
inherits stdio (streaming, for a CLI), run_captured captures the aggregated
output (for an embedder), and run_agent adds the agent allow gate and the
injection guard (for an MCP or agent surface). The parser is line-based (no
CommonMark dependency), so a # or Key: inside a fenced block is never
mistaken for structure.
Structs§
- Arg
- One declared positional argument.
- Cancel
- A handle for stopping a run from another thread.
- Job
- One job: a named script with its metadata. The script, its interpreter
language, its
Opts:flags, and its extra environment are internal mechanics; a consumer deals in the name, description, declared args, dependencies, and the agent gate, and runs the job throughrun,run_captured, orrun_agent. - Missing
Arg - A declared argument had no value supplied when binding a job’s args.
- Requirement
- One entry in a
Requires:list: a job to run first, and the arguments to run it with. - Task
File - A parsed task file: the jobs, any file-level environment hoisted to all of
them (an
Env:under a section heading applies to every job regardless of where in the document it appears; hoisting is not positional), and any parse warnings (an unterminated fence, a duplicate job, an unknown fence language). Parsing is infallible. A malformed file still yields what it can, so an embedder should surfacewarnings()rather than trust silence. The internal fields carry execution mechanics; a consumer reaches jobs throughjobsandjob, and runs them throughrun,run_captured, orrun_agent.
Enums§
- DepError
- A
Requires:dependency chain could not be resolved. - RunError
- Why a
run*call could not complete. It reports the failure to resolve or dispatch a job; a job that runs to a non-zero exit is not an error here (the exit status rides back in theOk). OnlyDebugis derived, becauseIowraps astd::io::Error, which is neitherClonenorPartialEq.
Functions§
- agent_
jobs - The jobs a set of layered files exposes to an agent or MCP surface: one per
name using the nearest definition (so a nearer non-allowed job shadows a
farther allowed one, matching run semantics), keeping only those whose nearest
definition carries
Agent: allow. This is the enforcement point for listing;run_agentis the enforcement point for running. A surface exposing jobs to an agent should list only these. - find_
task_ files - Search for task files from
startupward, nearest first. In each directory the first oftasks.md,maskfile.md,README.mdthat parses to at least one job is taken. - parse
- Parse a markdown task file. It is line-based (no CommonMark dependency): a
heading starts a job, the first fenced block under it is the script, and
Key: valuelines set metadata. Parsing is infallible; problems are reported inTaskFile::warningsrather than dropped to silence. CRLF endings are normalized. - run
- Run
nameand itsRequires:chain across the layeredfiles, inheriting the parent’s stdio so output streams straight through (the CLI path: a job is an interactive command, not a captured subprocess). Dependencies run first, each once, and each with its own defaults; onlynamereceivesargs. Returns the first failing step’s exit status, or the last step’s on full success. This is trusted: it applies no agent gate, so a caller must not hand it a name from an untrusted source. - run_
agent - The agent gate: run
namefor an MCP or agent surface, captured, failing closed. Enforced, in order: - run_
agent_ cancellable run_agent, stoppable through aCancelhandle held by another thread.- run_
captured - Like
run, but captured: runnameand itsRequires:chain across the layeredfileswith output aggregated across steps into a singlestd::process::Output(its status is the failing step’s, or the last on success). For an embedder (a TUI, an editor) that wants the text rather than a stream. Also trusted: no agent gate.