Merge pull request #1124 from trek-e/refactor/deduplicate-and-cleanup
This commit is contained in:
@@ -71,9 +71,9 @@ function cmdListTodos(cwd, area, raw) {
|
||||
area: todoArea,
|
||||
path: toPosixPath(path.join('.planning', 'todos', 'pending', file)),
|
||||
});
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const result = { count, todos };
|
||||
output(result, raw, count.toString());
|
||||
@@ -120,7 +120,7 @@ function cmdHistoryDigest(cwd, raw) {
|
||||
for (const dir of currentDirs) {
|
||||
allPhaseDirs.push({ name: dir, fullPath: path.join(phasesDir, dir), milestone: null });
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
if (allPhaseDirs.length === 0) {
|
||||
@@ -412,7 +412,7 @@ function cmdProgressRender(cwd, format, raw) {
|
||||
|
||||
phases.push({ number: phaseNum, name: phaseName, plans, summaries, status });
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const percent = totalPlans > 0 ? Math.min(100, Math.round((totalSummaries / totalPlans) * 100)) : 0;
|
||||
|
||||
@@ -559,7 +559,7 @@ function cmdStats(cwd, format, raw) {
|
||||
status: 'Not Started',
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
try {
|
||||
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
||||
@@ -595,7 +595,7 @@ function cmdStats(cwd, format, raw) {
|
||||
status,
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const phases = [...phasesByNumber.values()].sort((a, b) => comparePhaseNum(a.number, b.number));
|
||||
const completedPhases = phases.filter(p => p.status === 'Complete').length;
|
||||
@@ -613,7 +613,7 @@ function cmdStats(cwd, format, raw) {
|
||||
requirementsComplete = checked ? checked.length : 0;
|
||||
requirementsTotal = requirementsComplete + (unchecked ? unchecked.length : 0);
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Last activity from STATE.md
|
||||
let lastActivity = null;
|
||||
@@ -626,7 +626,7 @@ function cmdStats(cwd, format, raw) {
|
||||
|| stateContent.match(/^Last activity:\s*(.+)$/im);
|
||||
if (activityMatch) lastActivity = activityMatch[1].trim();
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Git stats
|
||||
let gitCommits = 0;
|
||||
|
||||
@@ -76,7 +76,7 @@ function ensureConfigFile(cwd) {
|
||||
delete userDefaults.depth;
|
||||
try {
|
||||
fs.writeFileSync(globalDefaultsPath, JSON.stringify(userDefaults, null, 2), 'utf-8');
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -76,7 +76,7 @@ function loadConfig(cwd) {
|
||||
const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
|
||||
parsed.granularity = depthToGranularity[parsed.depth] || parsed.depth;
|
||||
delete parsed.depth;
|
||||
try { fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2), 'utf-8'); } catch {}
|
||||
try { fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2), 'utf-8'); } catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
const get = (key, nested) => {
|
||||
@@ -250,6 +250,27 @@ function execGit(cwd, args) {
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Common path helpers ──────────────────────────────────────────────────────
|
||||
|
||||
/** Get the .planning directory path */
|
||||
function planningDir(cwd) {
|
||||
return path.join(cwd, '.planning');
|
||||
}
|
||||
|
||||
/** Get common .planning file paths */
|
||||
function planningPaths(cwd) {
|
||||
const base = path.join(cwd, '.planning');
|
||||
return {
|
||||
planning: base,
|
||||
state: path.join(base, 'STATE.md'),
|
||||
roadmap: path.join(base, 'ROADMAP.md'),
|
||||
project: path.join(base, 'PROJECT.md'),
|
||||
config: path.join(base, 'config.json'),
|
||||
phases: path.join(base, 'phases'),
|
||||
requirements: path.join(base, 'REQUIREMENTS.md'),
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Phase utilities ──────────────────────────────────────────────────────────
|
||||
|
||||
function escapeRegex(value) {
|
||||
@@ -370,7 +391,7 @@ function findPhaseInternal(cwd, phase) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -405,7 +426,7 @@ function getArchivedPhaseDirs(cwd) {
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
return results;
|
||||
}
|
||||
@@ -663,7 +684,7 @@ function getMilestonePhaseFilter(cwd) {
|
||||
while ((m = phasePattern.exec(roadmap)) !== null) {
|
||||
milestonePhaseNums.add(m[1]);
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
if (milestonePhaseNums.size === 0) {
|
||||
const passAll = () => true;
|
||||
@@ -709,4 +730,6 @@ module.exports = {
|
||||
replaceInCurrentMilestone,
|
||||
toPosixPath,
|
||||
MODEL_ALIAS_MAP,
|
||||
planningDir,
|
||||
planningPaths,
|
||||
};
|
||||
|
||||
@@ -153,7 +153,7 @@ function cmdInitPlanPhase(cwd, phase, raw) {
|
||||
if (uatFile) {
|
||||
result.uat_path = toPosixPath(path.join(phaseInfo.directory, uatFile));
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
output(result, raw);
|
||||
@@ -167,17 +167,26 @@ function cmdInitNewProject(cwd, raw) {
|
||||
const braveKeyFile = path.join(homedir, '.gsd', 'brave_api_key');
|
||||
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || fs.existsSync(braveKeyFile));
|
||||
|
||||
// Detect existing code
|
||||
// Detect existing code (cross-platform — no Unix `find` dependency)
|
||||
let hasCode = false;
|
||||
let hasPackageFile = false;
|
||||
try {
|
||||
const files = execSync('find . -maxdepth 3 \\( -name "*.ts" -o -name "*.js" -o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "*.swift" -o -name "*.java" \\) 2>/dev/null | grep -v node_modules | grep -v .git | head -5', {
|
||||
cwd,
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
hasCode = files.trim().length > 0;
|
||||
} catch {}
|
||||
const codeExtensions = new Set(['.ts', '.js', '.py', '.go', '.rs', '.swift', '.java']);
|
||||
const skipDirs = new Set(['node_modules', '.git', '.planning', '.claude', '__pycache__', 'target', 'dist', 'build']);
|
||||
function findCodeFiles(dir, depth) {
|
||||
if (depth > 3) return false;
|
||||
let entries;
|
||||
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return false; }
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile() && codeExtensions.has(path.extname(entry.name))) return true;
|
||||
if (entry.isDirectory() && !skipDirs.has(entry.name)) {
|
||||
if (findCodeFiles(path.join(dir, entry.name), depth + 1)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
hasCode = findCodeFiles(cwd, 0);
|
||||
} catch { /* intentionally empty — best-effort detection */ }
|
||||
|
||||
hasPackageFile = pathExistsInternal(cwd, 'package.json') ||
|
||||
pathExistsInternal(cwd, 'requirements.txt') ||
|
||||
@@ -307,7 +316,7 @@ function cmdInitResume(cwd, raw) {
|
||||
let interruptedAgentId = null;
|
||||
try {
|
||||
interruptedAgentId = fs.readFileSync(path.join(cwd, '.planning', 'current-agent-id.txt'), 'utf-8').trim();
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const result = {
|
||||
// File existence
|
||||
@@ -459,7 +468,7 @@ function cmdInitPhaseOp(cwd, phase, raw) {
|
||||
if (uatFile) {
|
||||
result.uat_path = toPosixPath(path.join(phaseInfo.directory, uatFile));
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
output(result, raw);
|
||||
@@ -494,9 +503,9 @@ function cmdInitTodos(cwd, area, raw) {
|
||||
area: todoArea,
|
||||
path: '.planning/todos/pending/' + file,
|
||||
});
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const result = {
|
||||
// Config
|
||||
@@ -543,9 +552,9 @@ function cmdInitMilestoneOp(cwd, raw) {
|
||||
const phaseFiles = fs.readdirSync(path.join(phasesDir, dir));
|
||||
const hasSummary = phaseFiles.some(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
|
||||
if (hasSummary) completedPhases++;
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Check archive
|
||||
const archiveDir = path.join(cwd, '.planning', 'archive');
|
||||
@@ -554,7 +563,7 @@ function cmdInitMilestoneOp(cwd, raw) {
|
||||
archivedMilestones = fs.readdirSync(archiveDir, { withFileTypes: true })
|
||||
.filter(e => e.isDirectory())
|
||||
.map(e => e.name);
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const result = {
|
||||
// Config
|
||||
@@ -593,7 +602,7 @@ function cmdInitMapCodebase(cwd, raw) {
|
||||
let existingMaps = [];
|
||||
try {
|
||||
existingMaps = fs.readdirSync(codebaseDir).filter(f => f.endsWith('.md'));
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const result = {
|
||||
// Models
|
||||
@@ -642,7 +651,7 @@ function cmdInitProgress(cwd, raw) {
|
||||
roadmapPhaseNums.add(hm[1]);
|
||||
roadmapPhaseNames.set(hm[1], hm[2].replace(/\(INSERTED\)/i, '').trim());
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const isDirInMilestone = getMilestonePhaseFilter(cwd);
|
||||
const seenPhaseNums = new Set();
|
||||
@@ -695,7 +704,7 @@ function cmdInitProgress(cwd, raw) {
|
||||
nextPhase = phaseInfo;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Add phases defined in ROADMAP but not yet scaffolded to disk
|
||||
for (const [num, name] of roadmapPhaseNames) {
|
||||
@@ -726,7 +735,7 @@ function cmdInitProgress(cwd, raw) {
|
||||
const state = fs.readFileSync(path.join(cwd, '.planning', 'STATE.md'), 'utf-8');
|
||||
const pauseMatch = state.match(/\*\*Paused At:\*\*\s*(.+)/);
|
||||
if (pauseMatch) pausedAt = pauseMatch[1].trim();
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const result = {
|
||||
// Models
|
||||
|
||||
@@ -137,10 +137,10 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
||||
// Count tasks
|
||||
const taskMatches = content.match(/##\s*Task\s*\d+/gi) || [];
|
||||
totalTasks += taskMatches.length;
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Archive ROADMAP.md
|
||||
if (fs.existsSync(roadmapPath)) {
|
||||
@@ -220,7 +220,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
||||
archivedCount++;
|
||||
}
|
||||
phasesArchived = archivedCount > 0;
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
const result = {
|
||||
|
||||
@@ -401,7 +401,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
||||
const dm = dir.match(decimalPattern);
|
||||
if (dm) existingDecimals.push(parseInt(dm[1], 10));
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const nextDecimal = existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1;
|
||||
const decimalPhase = `${normalizedBase}.${nextDecimal}`;
|
||||
@@ -470,7 +470,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
||||
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
||||
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => comparePhaseNum(a, b));
|
||||
targetDir = dirs.find(d => d.startsWith(normalized + '-') || d === normalized);
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Check for executed work (SUMMARY.md files)
|
||||
if (targetDir && !force) {
|
||||
@@ -538,7 +538,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
} else {
|
||||
// Integer removal: renumber all subsequent integer phases
|
||||
@@ -598,7 +598,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
// Update ROADMAP.md
|
||||
@@ -818,7 +818,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Fallback: if filesystem found no next phase, check ROADMAP.md
|
||||
// for phases that are defined but not yet planned (no directory on disk)
|
||||
@@ -835,7 +835,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
// Update STATE.md
|
||||
|
||||
@@ -151,7 +151,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
||||
else if (hasContext) diskStatus = 'discussed';
|
||||
else diskStatus = 'empty';
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Check ROADMAP checkbox status
|
||||
const checkboxPattern = new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${escapeRegex(phaseNum)}[:\\s]`, 'i');
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { escapeRegex, loadConfig, getMilestoneInfo, getMilestonePhaseFilter, normalizeMd, output, error } = require('./core.cjs');
|
||||
const { escapeRegex, loadConfig, getMilestoneInfo, getMilestonePhaseFilter, normalizeMd, planningPaths, output, error } = require('./core.cjs');
|
||||
const { extractFrontmatter, reconstructFrontmatter } = require('./frontmatter.cjs');
|
||||
|
||||
/** Shorthand — every state command needs this path */
|
||||
function getStatePath(cwd) {
|
||||
return planningPaths(cwd).state;
|
||||
}
|
||||
|
||||
// Shared helper: extract a field value from STATE.md content.
|
||||
// Supports both **Field:** bold and plain Field: format.
|
||||
function stateExtractField(content, fieldName) {
|
||||
@@ -26,7 +31,7 @@ function cmdStateLoad(cwd, raw) {
|
||||
let stateRaw = '';
|
||||
try {
|
||||
stateRaw = fs.readFileSync(path.join(planningDir, 'STATE.md'), 'utf-8');
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const configExists = fs.existsSync(path.join(planningDir, 'config.json'));
|
||||
const roadmapExists = fs.existsSync(path.join(planningDir, 'ROADMAP.md'));
|
||||
@@ -75,7 +80,7 @@ function cmdStateGet(cwd, section, raw) {
|
||||
}
|
||||
|
||||
// Try to find markdown section or field
|
||||
const fieldEscaped = section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const fieldEscaped = escapeRegex(section);
|
||||
|
||||
// Check for **field:** value (bold format)
|
||||
const boldPattern = new RegExp(`\\*\\*${fieldEscaped}:\\*\\*\\s*(.*)`, 'i');
|
||||
@@ -125,7 +130,7 @@ function cmdStatePatch(cwd, patches, raw) {
|
||||
const results = { updated: [], failed: [] };
|
||||
|
||||
for (const [field, value] of Object.entries(patches)) {
|
||||
const fieldEscaped = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const fieldEscaped = escapeRegex(field);
|
||||
// Try **Field:** bold format first, then plain Field: format
|
||||
const boldPattern = new RegExp(`(\\*\\*${fieldEscaped}:\\*\\*\\s*)(.*)`, 'i');
|
||||
const plainPattern = new RegExp(`(^${fieldEscaped}:\\s*)(.*)`, 'im');
|
||||
@@ -159,7 +164,7 @@ function cmdStateUpdate(cwd, field, value) {
|
||||
const statePath = path.join(cwd, '.planning', 'STATE.md');
|
||||
try {
|
||||
let content = fs.readFileSync(statePath, 'utf-8');
|
||||
const fieldEscaped = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const fieldEscaped = escapeRegex(field);
|
||||
// Try **Field:** bold format first, then plain Field: format
|
||||
const boldPattern = new RegExp(`(\\*\\*${fieldEscaped}:\\*\\*\\s*)(.*)`, 'i');
|
||||
const plainPattern = new RegExp(`(^${fieldEscaped}:\\s*)(.*)`, 'im');
|
||||
@@ -180,21 +185,10 @@ function cmdStateUpdate(cwd, field, value) {
|
||||
}
|
||||
|
||||
// ─── State Progression Engine ────────────────────────────────────────────────
|
||||
|
||||
function stateExtractField(content, fieldName) {
|
||||
const escaped = fieldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
// Try **Field:** bold format first
|
||||
const boldPattern = new RegExp(`\\*\\*${escaped}:\\*\\*\\s*(.+)`, 'i');
|
||||
const boldMatch = content.match(boldPattern);
|
||||
if (boldMatch) return boldMatch[1].trim();
|
||||
// Fall back to plain Field: format
|
||||
const plainPattern = new RegExp(`^${escaped}:\\s*(.+)`, 'im');
|
||||
const plainMatch = content.match(plainPattern);
|
||||
return plainMatch ? plainMatch[1].trim() : null;
|
||||
}
|
||||
// stateExtractField is defined above (shared helper) — do not duplicate.
|
||||
|
||||
function stateReplaceField(content, fieldName, newValue) {
|
||||
const escaped = fieldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const escaped = escapeRegex(fieldName);
|
||||
// Try **Field:** bold format first, then plain Field: format
|
||||
const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, 'i');
|
||||
if (boldPattern.test(content)) {
|
||||
@@ -576,7 +570,7 @@ function buildStateFrontmatter(bodyContent, cwd) {
|
||||
const info = getMilestoneInfo(cwd);
|
||||
milestone = info.version;
|
||||
milestoneName = info.name;
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
let totalPhases = totalPhasesRaw ? parseInt(totalPhasesRaw, 10) : null;
|
||||
@@ -611,7 +605,7 @@ function buildStateFrontmatter(bodyContent, cwd) {
|
||||
totalPlans = diskTotalPlans;
|
||||
completedPlans = diskTotalSummaries;
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
let progressPercent = null;
|
||||
|
||||
@@ -428,7 +428,7 @@ function cmdValidateConsistency(cwd, raw) {
|
||||
const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
|
||||
if (dm) diskPhases.add(dm[1]);
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Check: phases in ROADMAP but not on disk
|
||||
for (const p of roadmapPhases) {
|
||||
@@ -490,7 +490,7 @@ function cmdValidateConsistency(cwd, raw) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Check: frontmatter in plans has required fields
|
||||
try {
|
||||
@@ -510,7 +510,7 @@ function cmdValidateConsistency(cwd, raw) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
const passed = errors.length === 0;
|
||||
output({ passed, errors, warnings, warning_count: warnings.length }, raw, passed ? 'passed' : 'failed');
|
||||
@@ -599,7 +599,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
if (m) diskPhases.add(m[1]);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
// Check for invalid references
|
||||
for (const ref of phaseRefs) {
|
||||
const normalizedRef = String(parseInt(ref, 10)).padStart(2, '0');
|
||||
@@ -641,7 +641,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
addIssue('warning', 'W008', 'config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip)', 'Run /gsd:health --repair to add key', true);
|
||||
if (!repairs.includes('addNyquistKey')) repairs.push('addNyquistKey');
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
|
||||
// ─── Check 6: Phase directory naming (NN-name format) ─────────────────────
|
||||
@@ -652,7 +652,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
addIssue('warning', 'W005', `Phase directory "${e.name}" doesn't follow NN-name format`, 'Rename to match pattern (e.g., 01-setup)');
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// ─── Check 7: Orphaned plans (PLAN without SUMMARY) ───────────────────────
|
||||
try {
|
||||
@@ -671,7 +671,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// ─── Check 7b: Nyquist VALIDATION.md consistency ────────────────────────
|
||||
try {
|
||||
@@ -689,7 +689,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// ─── Check 8: Run existing consistency checks ─────────────────────────────
|
||||
// Inline subset of cmdValidateConsistency
|
||||
@@ -712,7 +712,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
||||
if (dm) diskPhases.add(dm[1]);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch { /* intentionally empty */ }
|
||||
|
||||
// Phases in ROADMAP but not on disk
|
||||
for (const p of roadmapPhases) {
|
||||
|
||||
Reference in New Issue
Block a user