docs: refresh taskmaster roadmap share

This commit is contained in:
Siavash Sameni
2026-05-24 09:54:04 +04:00
parent 5188cba9e2
commit 1a2b9f1f5d
5 changed files with 859 additions and 179 deletions

View File

@@ -0,0 +1,106 @@
import { readFile, writeFile } from 'node:fs/promises';
const root = new URL('../../', import.meta.url);
const taskmasterPath = new URL('.taskmaster/tasks/tasks.json', root);
const shareTasksPath = new URL('taskmaster-share/tasks.json', root);
const indexPath = new URL('taskmaster-share/index.html', root);
const raw = JSON.parse(await readFile(taskmasterPath, 'utf8'));
const data = raw.master ?? raw;
await writeFile(shareTasksPath, `${JSON.stringify(data, null, 2)}\n`);
let html = await readFile(indexPath, 'utf8');
const tasks = data.tasks ?? [];
const subtasks = tasks.flatMap((task) => task.subtasks ?? []);
const allItems = [...tasks, ...subtasks];
const counts = {
parent: tasks.length,
subtask: subtasks.length,
pending: allItems.filter((item) => item.status !== 'done').length,
done: allItems.filter((item) => item.status === 'done').length,
};
const escapeHtml = (value) =>
String(value ?? '').replace(/[&<>"']/g, (char) => ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
})[char]);
const badge = (value, prefix = '') =>
`<span class="badge ${prefix}${escapeHtml(value)}">${escapeHtml(value)}</span>`;
const formatDeps = (dependencies, parentId) => {
if (!dependencies?.length) return '';
const values = dependencies.map((dependency) =>
String(dependency).includes('.') ? dependency : `${parentId}.${dependency}`
);
return `<p class="deps">Depends on: ${escapeHtml(values.join(', '))}</p>`;
};
const renderSubtask = (task, subtask) => `
<li class="subtask" data-status="${escapeHtml(subtask.status)}" data-priority="${escapeHtml(subtask.priority)}">
<div class="subtask-top">
<span class="sub-id">${escapeHtml(task.id)}.${escapeHtml(subtask.id)}</span>
<strong>${escapeHtml(subtask.title)}</strong>
${badge(subtask.status)}
${badge(subtask.priority, 'priority-')}
</div>
<p>${escapeHtml(subtask.description || subtask.details || '')}</p>
${formatDeps(subtask.dependencies, task.id)}
</li>`;
const renderTask = (task) => `
<article class="task" data-status="${escapeHtml(task.status)}" data-priority="${escapeHtml(task.priority)}">
<div class="task-head">
<div>
<p class="eyebrow">Task ${escapeHtml(task.id)}</p>
<h2>${escapeHtml(task.title)}</h2>
</div>
<div class="badges">${badge(task.status)}${badge(task.priority, 'priority-')}</div>
</div>
<p class="desc">${escapeHtml(task.description)}</p>
<details>
<summary>Details and test strategy</summary>
<p>${escapeHtml(task.details)}</p>
${task.testStrategy ? `<p><strong>Test strategy:</strong> ${escapeHtml(task.testStrategy)}</p>` : ''}
${task.dependencies?.length ? `<p><strong>Depends on:</strong> ${escapeHtml(task.dependencies.join(', '))}</p>` : ''}
</details>
<h3>Subtasks (${(task.subtasks ?? []).length})</h3>
<ul class="subtasks">${(task.subtasks ?? []).map((subtask) => renderSubtask(task, subtask)).join('')}
</ul>
</article>`;
const taskFive = tasks.find((task) => Number(task.id) === 5);
if (!taskFive) {
throw new Error('Task 5 is missing from Taskmaster data.');
}
html = html
.replace(/<div class="stat"><strong>\d+<\/strong><span>parent tasks<\/span><\/div>/, `<div class="stat"><strong>${counts.parent}</strong><span>parent tasks</span></div>`)
.replace(/<div class="stat"><strong>\d+<\/strong><span>subtasks<\/span><\/div>/, `<div class="stat"><strong>${counts.subtask}</strong><span>subtasks</span></div>`)
.replace(/<div class="stat"><strong>\d+<\/strong><span>pending items<\/span><\/div>/, `<div class="stat"><strong>${counts.pending}</strong><span>pending items</span></div>`)
.replace(/<div class="stat"><strong>\d+<\/strong><span>done items<\/span><\/div>/, `<div class="stat"><strong>${counts.done}</strong><span>done items</span></div>`)
.replace(
'Track security remediation, payment architecture, refactor decisions, and completed documentation work from one place.',
'Track security remediation, payment architecture, Telegram-native work, refactor decisions, and completed documentation work from one place.'
);
const article = renderTask(taskFive);
if (html.includes('Task 5</p>')) {
html = html.replace(
/\n <article class="task" data-status="pending" data-priority="high">\n <div class="task-head">\n <div>\n <p class="eyebrow">Task 5<\/p>[\s\S]*?\n <article class="task" data-status="done" data-priority="medium">/,
`\n${article}\n <article class="task" data-status="done" data-priority="medium">`
);
} else {
html = html.replace(
'\n <article class="task" data-status="done" data-priority="medium">',
`\n${article}\n <article class="task" data-status="done" data-priority="medium">`
);
}
await writeFile(indexPath, html);
console.log(`Updated share page: ${counts.parent} tasks, ${counts.subtask} subtasks.`);