Skip to content

collect

collect(root, options?): Promise<Catalog>

@sksizer/detect-runners — find the task runners a directory uses, and normalize their tasks into one searchable model.

The problem it solves: a repository of any size runs its work through several tools at once — a build graph, a justfile, package.json scripts, a Makefile — and each answers only for itself. “How do I start that app?” then requires knowing which tool owns it before you can even look. This inverts that: one inventory, keyed by the call chain you would actually type.

import { collect, tasks } from "@sksizer/detect-runners";
const catalog = await collect(process.cwd(), { allowExec: true });
for (const task of tasks(catalog)) console.log(task.display);

Three things worth knowing before using it:

Execution is opt-in. allowExec defaults to false. Some runners can only be read by asking them — moon query tasks runs the repo’s toolchain configuration — so pointing this at a repository you did not write is code execution. Providers declare kind: "parse" | "exec", and only parse ones run by default.

The CLIs add a second gate on top of that one: per-repository TRUST, kept in the XDG state directory and granted by waypost trust, plus a USER-layer trust.paths/trust.owners policy in the config file. The library gates on allowExec and nothing else — a programmatic caller’s answer must not depend on a store it never asked about — while a CLI, which knows it is acting for a person, resolves both through @sksizer/sdlc-tool’s policyExecGate before calling collect. A repository cannot trust itself: allowExec: true and a trust block are both ignored in waypost.config.json, because a file inside the scanned repository authorizing the execution of that repository’s code is no gate at all.

Collection is a function, not a snapshot. Every collect() call re-probes and holds nothing across calls, so a catalog cannot silently go stale after someone adds a task. Storing an answer is available as a separate opt-in layer — readCachedCatalog paints what was last measured with a per-runner fresh/stale/miss verdict, collectAndCache probes and stores — for surfaces that would rather show a dated answer instantly than an empty screen for four seconds. A caller that does not reach for it gets a fresh probe, always.

argv and display are different strings. argv is executable and carries no placeholders; display is for showing and copying and may contain <required> / [optional] / *variadic markers. Never spawn display, and never build a command by joining it.

ParameterType
rootstring
optionsCollectOptions

Promise<Catalog>