@mclawnet/task
v0.1.1
Published
Task as first-class — file-backed task store with DAG dependencies (任务持久化层).
Readme
@mclawnet/task
Task as first-class — file-backed task store with DAG dependencies (任务持久化层).
Tasks live on disk as one JSON file per task plus a flat index.json for fast
listing. Mutations are serialized through an exclusive lock on the index file,
so multi-process callers (CLI, daemon, swarm coordinator) can safely share the
same project directory.
Persistence layout
All data is rooted under the per-project directory:
~/.clawnet/projects/<encoded-cwd>/
tasks/
index.json # { version: 1, tasks: { [id]: IndexEntry } }
<task-id>.json # one file per task
meta.json<encoded-cwd> is the absolute working directory with / replaced by -
(e.g. /Users/alice/code/foo → -Users-alice-code-foo). See encodeCwd()
in path.ts.
API
TaskStore
class TaskStore {
constructor(opts: { workDir: string; home?: string });
create(input: CreateInput): Promise<Task>;
get(id: string): Task; // throws if missing
tryGet(id: string): Task | null;
update(id: string, patch: Partial<Task>): Promise<Task>;
// update() refuses to touch blockedBy/blocks — use link()/unlink().
addComment(id: string, comment: TaskComment): Promise<void>;
list(): IndexEntry[]; // reads index.json
rebuildIndex(): Promise<void>; // recover index from task files
link(fromId: string, toId: string): Promise<void>; // fromId blockedBy toId
unlink(fromId: string, toId: string): Promise<void>;
}link() rejects edges that would form a cycle and self-heals half-written
edges from a previous crash.
DAG helpers
function wouldCreateCycle(store: TaskStore, fromId: string, toId: string): boolean;
function classifyBlockers(task: Task, store: TaskStore): {
broken: string[]; // dependency missing or cancelled
waiting: string[]; // dependency exists but not completed
isBlocked: boolean;
};Path helpers
function encodeCwd(absPath: string): string;
function projectRoot(workDir: string, home?: string): string;
function tasksDir(workDir: string, home?: string): string;
function taskFile(workDir: string, taskId: string, home?: string): string;
function indexFile(workDir: string, home?: string): string;
function metaFile(workDir: string, home?: string): string;Other packages (notably @mclawnet/swarm) reuse projectRoot() so all
per-project state stays under the same encoded folder.
Example
import { TaskStore, classifyBlockers } from "@mclawnet/task";
const store = new TaskStore({ workDir: process.cwd() });
const root = await store.create({
teamName: "alpha",
subject: "Ship Phase 1",
});
const child = await store.create({
teamName: "alpha",
subject: "Wire CLI",
blockedBy: [root.id],
});
const status = classifyBlockers(child, store);
console.log(status.isBlocked, status.waiting); // true, [root.id]
await store.update(root.id, { status: "completed" });