Files
TUIkit/docs/scripts/update-plans-data.ts
T
phranck 6c1bda94bf Chore: Consolidate plans directory and standardize AI tool instructions
- Move all plans from plans/ to .claude/plans/ (41 files)
- Add open plans listing to whats-next.md for visibility
- Update docs pipeline (update-plans-data.ts) for new paths
- Rewrite .github/copilot-instructions.md with Swift 6.0 constraint
- Add CONTRIBUTING.md, PR template, AGENTS.md, .cursor/rules, .windsurfrules
- Update global references (~/.claude/CLAUDE.md, project-structure.md)
2026-02-13 16:18:45 +01:00

132 lines
3.4 KiB
TypeScript

/**
* Extracts plan data from .claude/plans/open/ and .claude/plans/done/ directories.
* Generates plans.json with all open and done plans.
*
* Runs via GitHub Actions (hourly) or manual npm script.
* Output: docs/public/data/plans.json
*/
import fs from "fs";
import path from "path";
interface PlanData {
date: string;
slug: string;
title: string;
preface: string;
status: "open" | "done";
}
/**
* Extract date from filename like "2026-02-06-list-scrollable.md"
*/
function extractDate(filename: string): string {
const match = filename.match(/^(\d{4}-\d{2}-\d{2})/);
return match ? match[1] : "";
}
/**
* Extract slug from filename like "2026-02-06-list-scrollable.md"
*/
function extractSlug(filename: string): string {
const match = filename.match(/^\d{4}-\d{2}-\d{2}-(.+)\.md$/);
return match ? match[1] : "";
}
/**
* Extract H1 title from markdown content
*/
function extractTitle(content: string): string {
const match = content.match(/^#\s+(.+)$/m);
return match ? match[1].trim() : "Untitled";
}
/**
* Extract first ## section (Preface) from markdown content.
* Returns the full text until the next ## section or end of file.
*/
function extractPreface(content: string): string {
// Find first ## section
const match = content.match(/^##\s+Preface\s*\n([\s\S]*?)(?=\n##\s|\Z)/m);
return match ? match[1].trim() : "";
}
/**
* Read all plan files from a directory
*/
function readPlansFromDir(dirPath: string, status: "open" | "done"): PlanData[] {
if (!fs.existsSync(dirPath)) {
console.warn(`Directory not found: ${dirPath}`);
return [];
}
const files = fs.readdirSync(dirPath).filter((f) => f.endsWith(".md"));
return files
.map((filename) => {
const filePath = path.join(dirPath, filename);
const content = fs.readFileSync(filePath, "utf-8");
return {
date: extractDate(filename),
slug: extractSlug(filename),
title: extractTitle(content),
preface: extractPreface(content),
status,
};
})
.filter((plan) => plan.date && plan.slug && plan.preface); // Skip invalid plans
}
/**
* Main execution
*/
function main() {
const projectRoot = path.resolve(process.cwd(), "..");
const openDir = path.join(projectRoot, ".claude", "plans", "open");
const doneDir = path.join(projectRoot, ".claude", "plans", "done");
// Read all plans
const openPlans = readPlansFromDir(openDir, "open");
const donePlans = readPlansFromDir(doneDir, "done");
// Sort by date (newest first)
const sortByDateDesc = (a: PlanData, b: PlanData) =>
new Date(b.date).getTime() - new Date(a.date).getTime();
openPlans.sort(sortByDateDesc);
donePlans.sort(sortByDateDesc);
// Build output with all plans
const output = {
generated: new Date().toISOString(),
open: openPlans.map(({ date, slug, title, preface }) => ({
date,
slug,
title,
preface,
})),
done: donePlans.map(({ date, slug, title, preface }) => ({
date,
slug,
title,
preface,
})),
};
// Ensure output directory exists
const outputDir = path.join(process.cwd(), "public", "data");
fs.mkdirSync(outputDir, { recursive: true });
// Write JSON
const outputPath = path.join(outputDir, "plans.json");
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
console.log(
`✓ Generated plans.json (${openPlans.length} open, ${donePlans.length} done)`
);
console.log(` Location: ${outputPath}`);
}
main();