mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
fix: semver 3+ segment parsing and CRLF frontmatter corruption recovery
- fix(core): getMilestoneInfo() version regex `\d+\.\d+` only matched 2-segment versions (v1.2). Changed to `\d+(?:\.\d+)+` to support 3+ segments (v1.2.1, v2.0.1). Same fix in roadmap.cjs milestone extraction pattern. - fix(state): stripFrontmatter() used `^---\n` (LF-only) which failed to strip CRLF frontmatter blocks. When STATE.md had dual frontmatter blocks from prior CRLF corruption, each writeStateMd() call preserved the stale block and prepended a new wrong one. Now handles CRLF and strips all stacked frontmatter blocks. - fix(frontmatter): extractFrontmatter() always used the first --- block. When dual blocks exist from corruption, the first is stale. Now uses the last block (most recent sync).
This commit is contained in:
@@ -512,7 +512,8 @@ function getMilestoneInfo(cwd) {
|
||||
|
||||
// First: check for list-format roadmaps using 🚧 (in-progress) marker
|
||||
// e.g. "- 🚧 **v2.1 Belgium** — Phases 24-28 (in progress)"
|
||||
const inProgressMatch = roadmap.match(/🚧\s*\*\*v(\d+\.\d+)\s+([^*]+)\*\*/);
|
||||
// e.g. "- 🚧 **v1.2.1 Tech Debt** — Phases 1-8 (in progress)"
|
||||
const inProgressMatch = roadmap.match(/🚧\s*\*\*v(\d+(?:\.\d+)+)\s+([^*]+)\*\*/);
|
||||
if (inProgressMatch) {
|
||||
return {
|
||||
version: 'v' + inProgressMatch[1],
|
||||
@@ -523,15 +524,16 @@ function getMilestoneInfo(cwd) {
|
||||
// Second: heading-format roadmaps — strip shipped milestones in <details> blocks
|
||||
const cleaned = stripShippedMilestones(roadmap);
|
||||
// Extract version and name from the same ## heading for consistency
|
||||
const headingMatch = cleaned.match(/## .*v(\d+\.\d+)[:\s]+([^\n(]+)/);
|
||||
// Supports 2+ segment versions: v1.2, v1.2.1, v2.0.1, etc.
|
||||
const headingMatch = cleaned.match(/## .*v(\d+(?:\.\d+)+)[:\s]+([^\n(]+)/);
|
||||
if (headingMatch) {
|
||||
return {
|
||||
version: 'v' + headingMatch[1],
|
||||
name: headingMatch[2].trim(),
|
||||
};
|
||||
}
|
||||
// Fallback: try bare version match
|
||||
const versionMatch = cleaned.match(/v(\d+\.\d+)/);
|
||||
// Fallback: try bare version match (greedy — capture longest version string)
|
||||
const versionMatch = cleaned.match(/v(\d+(?:\.\d+)+)/);
|
||||
return {
|
||||
version: versionMatch ? versionMatch[0] : 'v1.0',
|
||||
name: 'milestone',
|
||||
|
||||
@@ -10,7 +10,11 @@ const { safeReadFile, normalizeMd, output, error } = require('./core.cjs');
|
||||
|
||||
function extractFrontmatter(content) {
|
||||
const frontmatter = {};
|
||||
const match = content.match(/^---\r?\n([\s\S]+?)\r?\n---/);
|
||||
// Find ALL frontmatter blocks at the start of the file.
|
||||
// If multiple blocks exist (corruption from CRLF mismatch), use the LAST one
|
||||
// since it represents the most recent state sync.
|
||||
const allBlocks = [...content.matchAll(/(?:^|\n)\s*---\r?\n([\s\S]+?)\r?\n---/g)];
|
||||
const match = allBlocks.length > 0 ? allBlocks[allBlocks.length - 1] : null;
|
||||
if (!match) return frontmatter;
|
||||
|
||||
const yaml = match[1];
|
||||
|
||||
@@ -181,7 +181,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
||||
|
||||
// Extract milestone info
|
||||
const milestones = [];
|
||||
const milestonePattern = /##\s*(.*v(\d+\.\d+)[^(\n]*)/gi;
|
||||
const milestonePattern = /##\s*(.*v(\d+(?:\.\d+)+)[^(\n]*)/gi;
|
||||
let mMatch;
|
||||
while ((mMatch = milestonePattern.exec(content)) !== null) {
|
||||
milestones.push({
|
||||
|
||||
@@ -664,7 +664,17 @@ function buildStateFrontmatter(bodyContent, cwd) {
|
||||
}
|
||||
|
||||
function stripFrontmatter(content) {
|
||||
return content.replace(/^---\n[\s\S]*?\n---\n*/, '');
|
||||
// Strip ALL frontmatter blocks at the start of the file.
|
||||
// Handles CRLF line endings and multiple stacked blocks (corruption recovery).
|
||||
// Greedy: keeps stripping ---...--- blocks separated by optional whitespace.
|
||||
let result = content;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const stripped = result.replace(/^\s*---\r?\n[\s\S]*?\r?\n---\s*/, '');
|
||||
if (stripped === result) break;
|
||||
result = stripped;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function syncStateFrontmatter(content, cwd) {
|
||||
|
||||
Reference in New Issue
Block a user