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:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user