backlog-md/src
Jeff Emmett 6c3563c7e4 fix: handle CRLF line endings in aggregator task updates
The updateTaskField, addUpdatedDate, and updateTaskDescription functions
only matched Unix line endings (\n), causing task updates to silently
fail when files had Windows line endings (\r\n).

Updated all three functions to:
- Match both \r\n and \n line endings in regexes
- Detect and preserve the original line ending style when writing back

This fixes drag & drop status updates in the aggregator web UI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 12:28:39 -08:00
..
aggregator fix: handle CRLF line endings in aggregator task updates 2025-12-05 12:28:39 -08:00
commands Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
completions Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
constants Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
core feat: add status history tracking for velocity statistics 2025-12-04 03:11:47 -08:00
file-system Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
formatters Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
git Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
guidelines Fix symlink for Docker: replace with actual file 2025-12-04 02:23:26 -08:00
markdown feat: add status history tracking for velocity statistics 2025-12-04 03:11:47 -08:00
mcp Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
server fix: route ordering for /api/tasks/reorder endpoint 2025-12-04 04:45:36 -08:00
test Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
types feat: add status history tracking for velocity statistics 2025-12-04 03:11:47 -08:00
ui Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
utils Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
web feat: add status history tracking for velocity statistics 2025-12-04 03:11:47 -08:00
agent-instructions.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
board.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
cli.ts Add multi-project backlog aggregator with real-time updates 2025-12-03 20:35:43 -08:00
index.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00
readme.ts Initial commit - fork of Backlog.md with Docker deployment for backlog.jeffemmett.com 2025-12-03 17:57:16 -08:00

readme.ts

import { join } from "node:path";
import { exportKanbanBoardToFile } from "./board.ts";
import type { Task } from "./types/index.ts";

const BOARD_START = "<!-- BOARD_START -->";
const BOARD_END = "<!-- BOARD_END -->";

export async function updateReadmeWithBoard(tasks: Task[], statuses: string[], projectName: string, version?: string) {
	const readmePath = join(process.cwd(), "README.md");
	let readmeContent = "";
	try {
		readmeContent = await Bun.file(readmePath).text();
	} catch {
		// If README.md doesn't exist, create it.
	}

	// Use the same high-quality board generation as file export
	// Create a temporary file to get the properly formatted board
	const tempPath = join(process.cwd(), ".temp-board.md");
	await exportKanbanBoardToFile(tasks, statuses, tempPath, projectName);
	const fullBoardContent = await Bun.file(tempPath).text();

	// Extract timestamp from the board content
	const timestampMatch = fullBoardContent.match(/Generated on: ([^\n]+)/);
	const timestamp = timestampMatch ? timestampMatch[1] : new Date().toISOString().replace("T", " ").substring(0, 19);

	// Extract just the board table (skip all metadata headers)
	const lines = fullBoardContent.split("\n");
	const tableStartIndex = lines.findIndex(
		(line) =>
			line.includes("|") &&
			(line.includes("To Do") || line.includes("In Progress") || line.includes("Done") || line.includes("---")),
	);
	const boardTable = lines.slice(tableStartIndex).join("\n").trim();

	// Clean up temp file
	try {
		await Bun.file(tempPath).write("");
		await Bun.$`rm -f ${tempPath}`;
	} catch {
		// Ignore cleanup errors
	}

	// Create the board section with a nice title
	const versionText = version ? ` (${version})` : "";
	const statusTitle = `## 📊 ${projectName} Project Status${versionText}`;
	const subtitle = "This board was automatically generated by [Backlog.md](https://backlog.md)";
	const boardSection = `${statusTitle}\n\n${subtitle}\n\nGenerated on: ${timestamp}\n\n${boardTable}`;

	const startMarkerIndex = readmeContent.indexOf(BOARD_START);
	const endMarkerIndex = readmeContent.indexOf(BOARD_END);
	const licenseIndex = readmeContent.indexOf("## License");

	if (startMarkerIndex !== -1 && endMarkerIndex !== -1) {
		const preContent = readmeContent.substring(0, startMarkerIndex + BOARD_START.length);
		const postContent = readmeContent.substring(endMarkerIndex);
		readmeContent = `${preContent}\n\n${boardSection}\n\n${postContent}`;
	} else if (licenseIndex !== -1) {
		const preContent = readmeContent.substring(0, licenseIndex);
		const postContent = readmeContent.substring(licenseIndex);
		readmeContent = `${preContent}${BOARD_START}\n\n${boardSection}\n\n${BOARD_END}\n\n${postContent}`;
	} else {
		// If markers are not found, append the board at the end of the file.
		readmeContent += `\n\n${BOARD_START}\n\n${boardSection}\n\n${BOARD_END}`;
	}

	await Bun.write(readmePath, readmeContent);
}