MCPcopy Create free account
hub / github.com/SethGammon/Citadel / readCampaigns

Function readCampaigns

scripts/dashboard.js:259–316  ·  view source on GitHub ↗
(projectRoot)

Source from the content-addressed store, hash-verified

257 };
258}
259
260function readCampaigns(projectRoot) {
261 const campaignDir = path.join(projectRoot, '.planning', 'campaigns');
262 const files = listFiles(campaignDir, (entry) => entry.endsWith('.md'));
263 const campaigns = [];
264 const skipped = [];
265
266 for (const filePath of files) {
267 const content = readText(filePath);
268 if (!content) continue;
269 try {
270 const slug = path.basename(filePath, '.md');
271 const parsed = parseCampaignContent(content, { slug });
272 const direction = extractDirection(content);
273 let status = normalizeStatus(parsed);
274 const packagePhase = packagePhaseReadiness(parsed);
275 const reviewPackageEvidence = reviewPackageEvidenceStatus(content, projectRoot);
276 if (
277 reviewPackageEvidence &&
278 !reviewPackageEvidence.pass &&
279 (packagePhase.readyForPackage || status === 'needs-completion')
280 ) {
281 status = 'needs-review-package';
282 }
283 if (status === 'completed' && path.dirname(filePath) === campaignDir) {
284 status = 'needs-archive';
285 }
286 campaigns.push({
287 slug,
288 filePath,
289 status,
290 direction,
291 phase: phaseSummary(parsed),
292 packagePhase,
293 reviewPackageEvidence,
294 lastDecision: lastDecision(content),
295 modifiedAt: fs.statSync(filePath).mtime.toISOString(),
296 });
297 } catch (error) {
298 skipped.push({ filePath, reason: error.message });
299 }
300 }
301
302 campaigns.sort((left, right) => {
303 const rank = (status) => {
304 if (status === 'active') return 0;
305 if (status === 'needs-review-package') return 1;
306 if (status === 'needs-completion') return 1;
307 if (status === 'needs-archive') return 1;
308 if (status.includes('approval') || status.includes('pending') || status === 'needs-continue') return 1;
309 if (status === 'parked' || status === 'paused') return 2;
310 return 3;
311 };
312 return rank(left.status) - rank(right.status) ||
313 new Date(right.modifiedAt).getTime() - new Date(left.modifiedAt).getTime();
314 });
315
316 return { campaigns, skipped };

Callers 1

collectDashboardFunction · 0.85

Calls 10

parseCampaignContentFunction · 0.85
extractDirectionFunction · 0.85
packagePhaseReadinessFunction · 0.85
phaseSummaryFunction · 0.85
lastDecisionFunction · 0.85
rankFunction · 0.85
listFilesFunction · 0.70
readTextFunction · 0.70
normalizeStatusFunction · 0.70

Tested by

no test coverage detected