Brainfile (Main Class)
The main class provides static methods for parsing, serializing, validating, and working with brainfile documents.
Static Methods
parse(content: string): Board | null
Parse markdown content into a Board object.
const board = Brainfile.parse(markdownString);
if (board) {
console.log(board.title);
}parseWithErrors(content: string): ParseResult
Parse with detailed error information.
const result = Brainfile.parseWithErrors(markdownString);
if (result.board) {
console.log("Parsed successfully");
} else {
console.error("Parse error:", result.error);
}serialize(board: Board, options?: SerializeOptions): string
Serialize a Board object back to markdown format.
const markdown = Brainfile.serialize(board, {
indent: 2,
lineWidth: 80,
trailingNewline: true,
});validate(board: Board): ValidationResult
Validate a board object against the schema.
const validation = Brainfile.validate(board);
if (!validation.valid) {
validation.errors.forEach((err) => {
console.log(`${err.path}: ${err.message}`);
});
}getBuiltInTemplates(): TaskTemplate[]
Get all built-in task templates.
const templates = Brainfile.getBuiltInTemplates();
templates.forEach((template) => {
console.log(`${template.id}: ${template.name}`);
});getTemplate(id: string): TaskTemplate | undefined
Get a specific template by ID.
const bugTemplate = Brainfile.getTemplate("bug-report");
if (bugTemplate) {
console.log(bugTemplate.name);
}createFromTemplate(templateId: string, values: Record<string, string>): Partial<Task>
Create a task from a template with variable substitution.
const task = Brainfile.createFromTemplate("bug-report", {
title: "Login fails on mobile",
description: "Users cannot log in on iOS devices",
});findTaskLocation(content: string, taskId: string): Location
Find the line number and position of a task in the source file.
const location = Brainfile.findTaskLocation(markdown, "task-1");
console.log(`Task at line ${location.line}`);findRuleLocation(content: string, ruleId: number, ruleType: RuleType): Location
Find the location of a rule in the source file.
const location = Brainfile.findRuleLocation(markdown, 1, "always");
console.log(`Rule at line ${location.line}`);Types
Board
interface Board {
title: string;
protocolVersion?: string;
schema?: string;
agent?: AgentInstructions;
rules?: Rules;
statsConfig?: StatsConfig;
columns: Column[];
archive?: Task[]; // legacy compat
strict?: boolean; // v2: enforce type validation
types?: TypesConfig; // v2: custom document types
}Column
interface Column {
id: string;
title: string;
tasks: Task[]; // legacy: embedded tasks
completionColumn?: boolean; // v2: auto-complete on move
}v2 architecture
In v2, columns are config-only (no embedded tasks). Tasks are standalone files in .brainfile/board/. The tasks array in Column is used for legacy compatibility and in-memory board operations.
Task
interface Task {
id: string;
title: string;
column?: string; // v2: column ID reference
type?: string; // v2: document type (e.g., "epic", "adr")
description?: string;
relatedFiles?: string[];
assignee?: string;
tags?: string[];
priority?: "low" | "medium" | "high" | "critical";
effort?: "trivial" | "small" | "medium" | "large" | "xlarge";
blockedBy?: string[];
dueDate?: string;
subtasks?: Subtask[];
contract?: Contract;
parentId?: string; // v2: parent document ID
createdAt?: string;
completedAt?: string;
}Subtask
interface Subtask {
id: string;
title: string;
completed: boolean;
}AgentInstructions
interface AgentInstructions {
instructions?: string[];
llmNotes?: string;
}Rules
interface Rules {
always?: Rule[];
never?: Rule[];
prefer?: Rule[];
context?: Rule[];
}
interface Rule {
id: number;
rule: string;
}StatsConfig
interface StatsConfig {
columns?: string[];
}SerializeOptions
interface SerializeOptions {
indent?: number; // Number of spaces for indentation (default: 2)
lineWidth?: number; // Maximum line width (default: 80)
trailingNewline?: boolean; // Add trailing newline (default: true)
}ValidationResult
interface ValidationResult {
valid: boolean;
errors: ValidationError[];
}
interface ValidationError {
path: string;
message: string;
}ParseResult
interface ParseResult {
board: Board | null;
error?: string;
}TaskTemplate
interface TaskTemplate {
id: string;
name: string;
description: string;
variables: string[];
task: Partial<Task>;
}Board Operations
Immutable operations that return a new board without mutating the original.
addTask(board: Board, columnId: string, input: TaskInput): BoardOperationResult
Add a new task to a column with all optional fields.
import { addTask, type TaskInput } from "@brainfile/core";
const result = addTask(board, "todo", {
title: "Implement auth",
description: "Add OAuth2 support",
priority: "high",
tags: ["security", "feature"],
assignee: "john",
dueDate: "2025-02-01",
subtasks: ["Research providers", "Implement flow", "Add tests"],
});
if (result.success) {
board = result.board!;
}patchTask(board: Board, taskId: string, patch: TaskPatch): BoardOperationResult
Partially update a task. Set fields to null to remove them.
import { patchTask } from "@brainfile/core";
// Update specific fields
const result = patchTask(board, "task-1", {
priority: "critical",
tags: ["urgent", "bug"],
});
// Remove fields by setting to null
const removeResult = patchTask(board, "task-1", {
assignee: null,
dueDate: null,
});moveTask(board: Board, taskId: string, fromColumn: string, toColumn: string, toIndex: number): BoardOperationResult
Move a task between columns or reorder within the same column.
import { moveTask } from "@brainfile/core";
const result = moveTask(board, "task-1", "todo", "in-progress", 0);deleteTask(board: Board, columnId: string, taskId: string): BoardOperationResult
Delete a task from a column.
import { deleteTask } from "@brainfile/core";
const result = deleteTask(board, "todo", "task-1");archiveTask(board: Board, columnId: string, taskId: string): BoardOperationResult
Move a task to the archive.
import { archiveTask } from "@brainfile/core";
const result = archiveTask(board, "done", "task-5");restoreTask(board: Board, taskId: string, columnId: string): BoardOperationResult
Restore a task from the archive to a column.
import { restoreTask } from "@brainfile/core";
const result = restoreTask(board, "task-5", "todo");Subtask Operations
import {
addSubtask,
deleteSubtask,
updateSubtask,
toggleSubtask,
} from "@brainfile/core";
// Add a subtask (ID auto-generated as task-N-M)
const addResult = addSubtask(board, "task-1", "New subtask title");
// Delete a subtask
const deleteResult = deleteSubtask(board, "task-1", "task-1-2");
// Update subtask title
const updateResult = updateSubtask(board, "task-1", "task-1-1", "Updated title");
// Toggle subtask completion
const toggleResult = toggleSubtask(board, "task-1", "task-1-1");TaskInput
Input type for creating tasks with addTask().
interface TaskInput {
title: string;
description?: string;
priority?: "low" | "medium" | "high" | "critical";
tags?: string[];
assignee?: string;
dueDate?: string;
relatedFiles?: string[];
template?: "bug" | "feature" | "refactor";
subtasks?: string[]; // Just titles - IDs are auto-generated
}TaskPatch
Input type for partial updates with patchTask(). Use null to remove optional fields.
interface TaskPatch {
title?: string;
description?: string;
priority?: "low" | "medium" | "high" | "critical" | null;
tags?: string[] | null;
assignee?: string | null;
dueDate?: string | null;
relatedFiles?: string[] | null;
template?: "bug" | "feature" | "refactor" | null;
}BoardOperationResult
All operations return this result type.
interface BoardOperationResult {
success: boolean;
board?: Board; // New board if success
error?: string; // Error message if failed
}Low-Level Classes
For advanced usage, you can import and use the low-level classes directly:
BrainfileParser
import { BrainfileParser } from "@brainfile/core";
const board = BrainfileParser.parse(markdown);
const result = BrainfileParser.parseWithErrors(markdown);
const location = BrainfileParser.findTaskLocation(markdown, "task-1");BrainfileSerializer
import { BrainfileSerializer } from "@brainfile/core";
const markdown = BrainfileSerializer.serialize(board, options);BrainfileValidator
import { BrainfileValidator } from "@brainfile/core";
const validation = BrainfileValidator.validate(board);Realtime Sync Utilities
Use these helpers to coordinate live Brainfile updates across editors, CLIs, and automation:
hashBoardContent(content: string): string
Returns a SHA-256 hash for raw brainfile.md content. Handy for file watchers to skip redundant refreshes or detect external changes before writing.
const currentHash = hashBoardContent(content);
if (currentHash !== lastKnownHash) {
lastKnownHash = currentHash;
refreshBoard();
}hashBoard(board: Board): string
Serializes a Board via BrainfileSerializer and then hashes the result. Use this when you already have parsed objects and need a deterministic fingerprint to share between workers or clients.
diffBoards(previous: Board, next: Board): BoardDiff
Computes structural differences between two board states, returning:
interface BoardDiff {
metadataChanged: boolean;
columnsAdded: ColumnDiff[];
columnsRemoved: ColumnDiff[];
columnsUpdated: ColumnDiff[];
columnsMoved: ColumnDiff[];
tasksAdded: TaskDiff[];
tasksRemoved: TaskDiff[];
tasksUpdated: TaskDiff[];
tasksMoved: TaskDiff[];
}This lets clients avoid re-rendering entire boards—only touched columns or tasks need to be updated. Each diff entry includes before/after snapshots, indexes, and changedFields so UIs can highlight precise changes.
Migration note: these utilities ship in
@brainfile/core@0.4.0+. Replace bespoke hashing/diff logic with the shared helpers to ensure consistent behavior across Pi extension, CLI, MCP tooling, and future integrations.
v2 File Operations
These functions operate on the per-task file architecture (.brainfile/board/ and .brainfile/logs/):
readTaskFile(filePath): TaskDocument
Read a single task file.
import { readTaskFile } from '@brainfile/core';
const doc = readTaskFile('.brainfile/board/task-1.md');
console.log(doc.task.title);
console.log(doc.body);readTasksDir(dirPath): TaskDocument[]
Read all task files from a directory.
import { readTasksDir } from '@brainfile/core';
const tasks = readTasksDir('.brainfile/board/');addTaskFile(boardDir, input): TaskOperationResult
Add a new task file (auto-generates ID and filename).
import { addTaskFile } from '@brainfile/core';
addTaskFile('.brainfile/board/', {
title: 'New task',
column: 'todo',
priority: 'high'
});moveTaskFile(filePath, newColumn): void
Move a task to a different column (updates frontmatter).
completeTaskFile(boardPath, logsDir): void
Move a task from board/ to logs/, setting completedAt.
appendLog(filePath, message, agent?): void
Append a timestamped log entry to a task file.
readV2BoardConfig(brainfilePath): Board
Read and parse the v2 board configuration file.
import { readV2BoardConfig } from '@brainfile/core';
const board = readV2BoardConfig('.brainfile/brainfile.md');
console.log(board.columns.map(c => c.title));findBrainfile(startDir): FoundBrainfile
Auto-detect the brainfile location from a starting directory.
import { findBrainfile } from '@brainfile/core';
const found = findBrainfile(process.cwd());generateNextFileTaskId(boardDir, logsDir): string
Generate the next sequential task ID by scanning existing files.
Board Validation
getBoardTypes(config): TypesConfig
Get available document types from board config.
validateType(config, typeName): string | null
Validate a type name against the board config (returns error message or null).
validateColumn(config, columnId): string | null
Validate a column ID against the board config.
v2 Types
import type {
Board,
BoardConfig,
TypeEntry,
TypesConfig,
TaskDocument
} from '@brainfile/core';
// legacy single-file board with embedded tasks
interface Board {
columns: Column[];
archive?: Task[];
// ... inherits BrainfileBase
}
// v2 board config (columns + types, no embedded tasks)
interface BoardConfig {
columns: ColumnConfig[];
strict?: boolean;
types?: TypesConfig;
// ... inherits BrainfileBase
}
// Per-type configuration
interface TypeEntry {
idPrefix: string;
completable?: boolean;
schema?: string;
}
// Map of type name -> type configuration
interface TypesConfig {
[typeName: string]: TypeEntry;
}
// Parsed task file content
interface TaskDocument {
task: Task;
body: string;
filePath: string;
}Constants
RuleType
type RuleType = "always" | "never" | "prefer" | "context";Priority
type Priority = "low" | "medium" | "high" | "critical";Effort
type Effort = "trivial" | "small" | "medium" | "large" | "xlarge";Template
type Template = "bug" | "feature" | "refactor";