feat: Read task_number from YAML frontmatter

Updated data fetcher to:
1. Prioritize task_number from frontmatter (new)
2. Fall back to folder/file name pattern (task-XXX-*)
3. Fall back to title pattern (#XX)

Also handles standalone .md files (task-098, task-099)

Chronicler #69
This commit is contained in:
Claude
2026-04-08 14:33:09 +00:00
parent 357f98cb10
commit 35f48d38bb

View File

@@ -26,14 +26,25 @@ module.exports = async function() {
return []; return [];
} }
const dirs = await response.json(); const items = await response.json();
const taskDirs = dirs.filter(d => d.type === 'dir' && d.name !== '_archive'); // Include both directories AND standalone .md files (like task-098, task-099)
const taskItems = items.filter(d =>
(d.type === 'dir' && d.name !== '_archive') ||
(d.type === 'file' && d.name.startsWith('task-') && d.name.endsWith('.md'))
);
// Fetch each task's README for (const item of taskItems) {
for (const dir of taskDirs) {
try { try {
// Try README.md first let readmePath;
let readmePath = `docs/tasks/${dir.name}/README.md`;
if (item.type === 'file') {
// Standalone task file
readmePath = `docs/tasks/${item.name}`;
} else {
// Directory - try README.md first
readmePath = `docs/tasks/${item.name}/README.md`;
}
let readmeResponse = await fetch( let readmeResponse = await fetch(
`${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/${encodeURIComponent(readmePath)}?ref=master`, `${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/${encodeURIComponent(readmePath)}?ref=master`,
{ {
@@ -44,10 +55,10 @@ module.exports = async function() {
} }
); );
if (!readmeResponse.ok) { // If README.md not found in directory, try to find any .md file
// Try to find any .md file if (!readmeResponse.ok && item.type === 'dir') {
const filesResponse = await fetch( const filesResponse = await fetch(
`${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/docs/tasks/${encodeURIComponent(dir.name)}?ref=master`, `${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/docs/tasks/${encodeURIComponent(item.name)}?ref=master`,
{ {
headers: { headers: {
'Authorization': `token ${TOKEN}`, 'Authorization': `token ${TOKEN}`,
@@ -60,7 +71,7 @@ module.exports = async function() {
const files = await filesResponse.json(); const files = await filesResponse.json();
const mdFile = files.find(f => f.name.endsWith('.md')); const mdFile = files.find(f => f.name.endsWith('.md'));
if (mdFile) { if (mdFile) {
readmePath = `docs/tasks/${dir.name}/${mdFile.name}`; readmePath = `docs/tasks/${item.name}/${mdFile.name}`;
readmeResponse = await fetch( readmeResponse = await fetch(
`${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/${encodeURIComponent(readmePath)}?ref=master`, `${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/${encodeURIComponent(readmePath)}?ref=master`,
{ {
@@ -91,25 +102,25 @@ module.exports = async function() {
// Extract title from first H1 // Extract title from first H1
const titleMatch = content.match(/^#\s+(.+)$/m); const titleMatch = content.match(/^#\s+(.+)$/m);
let title = titleMatch ? titleMatch[1] : dir.name; let title = titleMatch ? titleMatch[1] : item.name;
// Extract task number - check multiple sources // Get task number - priority: frontmatter > folder name > title > content
let taskNumber = null; let taskNumber = null;
// 1. Check frontmatter for task_number field // 1. YAML frontmatter task_number (preferred source)
if (frontmatter.task_number) { if (frontmatter.task_number) {
taskNumber = frontmatter.task_number; taskNumber = String(frontmatter.task_number);
} }
// 2. Check folder name (e.g., task-048-n8n-rebuild) // 2. Folder/file name pattern (task-048-*)
if (!taskNumber) { if (!taskNumber) {
const folderNumMatch = dir.name.match(/^task-0*(\d+)/); const nameNumMatch = item.name.match(/^task-0*(\d+)/);
if (folderNumMatch) { if (nameNumMatch) {
taskNumber = folderNumMatch[1]; taskNumber = nameNumMatch[1];
} }
} }
// 3. Check title for #XX pattern // 3. Title pattern (#XX)
if (!taskNumber) { if (!taskNumber) {
const titleNumMatch = title.match(/#(\d+)/); const titleNumMatch = title.match(/#(\d+)/);
if (titleNumMatch) { if (titleNumMatch) {
@@ -117,43 +128,36 @@ module.exports = async function() {
} }
} }
// 4. Check content for Task ID or Task #XX patterns // Build Gitea URL
if (!taskNumber) { const giteaPath = item.type === 'file'
const contentNumMatch = content.match(/\*\*Task ID:\*\*\s*#?(\d+)/i) || ? `docs/tasks/${item.name}`
content.match(/\*\*Task ID:\*\*\s*FFG-TASK-0*(\d+)/i) || : `docs/tasks/${item.name}`;
content.match(/^#\s+Task\s+#(\d+)/im);
if (contentNumMatch) {
taskNumber = contentNumMatch[1];
}
}
tasks.push({ tasks.push({
slug: dir.name, slug: item.name,
title: title, title: title,
taskNumber: taskNumber, taskNumber: taskNumber,
status: frontmatter.status || 'open', status: frontmatter.status || 'open',
priority: frontmatter.priority || 'P3', priority: frontmatter.priority || 'P3',
owner: frontmatter.owner || 'Michael', owner: frontmatter.owner || 'Michael',
created: frontmatter.created || '2026-01-01', created: frontmatter.created || '2026-01-01',
giteaUrl: `https://git.firefrostgaming.com/${REPO_OWNER}/${REPO_NAME}/src/branch/master/docs/tasks/${encodeURIComponent(dir.name)}` giteaUrl: `https://git.firefrostgaming.com/${REPO_OWNER}/${REPO_NAME}/src/branch/master/${giteaPath}`
}); });
} }
} }
} catch (err) { } catch (err) {
console.error(`Error processing ${dir.name}:`, err.message); console.error(`Error processing ${item.name}:`, err.message);
} }
} }
// Sort by priority (P1 first) then by task number (if available) then by title // Sort by priority (P1 first) then by task number then by title
tasks.sort((a, b) => { tasks.sort((a, b) => {
if (a.priority !== b.priority) { if (a.priority !== b.priority) {
return a.priority.localeCompare(b.priority); return a.priority.localeCompare(b.priority);
} }
// Sort by task number if both have one
if (a.taskNumber && b.taskNumber) { if (a.taskNumber && b.taskNumber) {
return parseInt(a.taskNumber) - parseInt(b.taskNumber); return parseInt(a.taskNumber) - parseInt(b.taskNumber);
} }
// Tasks with numbers come before those without
if (a.taskNumber && !b.taskNumber) return -1; if (a.taskNumber && !b.taskNumber) return -1;
if (!a.taskNumber && b.taskNumber) return 1; if (!a.taskNumber && b.taskNumber) return 1;
return a.title.localeCompare(b.title); return a.title.localeCompare(b.title);