lean-spec 0.2.8 → 0.2.9-dev.20251205030455

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,10 @@
1
- import { loadConfig, getSpec, loadAllSpecs } from './chunk-LXOJW2FE.js';
2
- import { updateFrontmatter } from './chunk-LVD7ZAVZ.js';
1
+ import { loadConfig, getSpec, loadAllSpecs } from './chunk-RF5PKL6L.js';
2
+ import { updateFrontmatter } from './chunk-VN5BUHTV.js';
3
+ import * as fs from 'fs/promises';
3
4
  import * as path from 'path';
4
5
  import { Command } from 'commander';
5
6
  import { execSync } from 'child_process';
6
- import * as fs from 'fs/promises';
7
+ import matter from 'gray-matter';
7
8
 
8
9
  function isGitRepository() {
9
10
  try {
@@ -131,7 +132,7 @@ function fileExistsInGit(filePath) {
131
132
  }
132
133
  }
133
134
  function createSpecDirPattern() {
134
- return /(?:^|\D)(\d{2,4})-[a-z]/i;
135
+ return /(?:^|\D)(\d{2,4})-[^0-9-]/i;
135
136
  }
136
137
  async function getGlobalNextSeq(specsDir, digits) {
137
138
  try {
@@ -240,17 +241,199 @@ async function searchInAllDirectories(specsDir, specName) {
240
241
  }
241
242
  return scanDirectory(specsDir);
242
243
  }
244
+ async function loadSpecsForBootstrap(specsDir, options = {}) {
245
+ const specs = [];
246
+ const config = await loadConfig();
247
+ const specPattern = /^(\d{2,})-/;
248
+ async function scanDirectory(dir, relativePath = "") {
249
+ try {
250
+ const entries = await fs.readdir(dir, { withFileTypes: true });
251
+ for (const entry of entries) {
252
+ if (!entry.isDirectory()) continue;
253
+ if (entry.name === "archived" && relativePath === "") continue;
254
+ const entryPath = path.join(dir, entry.name);
255
+ const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
256
+ if (specPattern.test(entry.name)) {
257
+ if (options.targetSpecs && options.targetSpecs.length > 0) {
258
+ const matches = options.targetSpecs.some((target) => {
259
+ const targetNum = target.match(/^0*(\d+)$/)?.[1];
260
+ const entryNum = entry.name.match(/^(\d+)/)?.[1];
261
+ return entry.name === target || entry.name.includes(target) || targetNum && entryNum && parseInt(targetNum, 10) === parseInt(entryNum, 10);
262
+ });
263
+ if (!matches) continue;
264
+ }
265
+ const specFile = path.join(entryPath, config.structure.defaultFile);
266
+ try {
267
+ await fs.access(specFile);
268
+ const specInfo = await analyzeSpecFile(specFile, entryPath, entryRelativePath, entry.name);
269
+ specs.push(specInfo);
270
+ } catch {
271
+ }
272
+ } else {
273
+ await scanDirectory(entryPath, entryRelativePath);
274
+ }
275
+ }
276
+ } catch {
277
+ }
278
+ }
279
+ await scanDirectory(specsDir);
280
+ if (options.includeArchived) {
281
+ const archivedPath = path.join(specsDir, "archived");
282
+ await scanDirectory(archivedPath, "archived");
283
+ }
284
+ specs.sort((a, b) => {
285
+ const aNum = parseInt(a.name.match(/^(\d+)/)?.[1] || "0", 10);
286
+ const bNum = parseInt(b.name.match(/^(\d+)/)?.[1] || "0", 10);
287
+ return bNum - aNum;
288
+ });
289
+ return specs;
290
+ }
291
+ async function analyzeSpecFile(filePath, fullPath, relativePath, name) {
292
+ const content = await fs.readFile(filePath, "utf-8");
293
+ let hasFrontmatter = false;
294
+ let hasValidFrontmatter = false;
295
+ let existingFrontmatter;
296
+ try {
297
+ const parsed = matter(content);
298
+ if (parsed.data && Object.keys(parsed.data).length > 0) {
299
+ hasFrontmatter = true;
300
+ existingFrontmatter = parsed.data;
301
+ hasValidFrontmatter = !!(parsed.data.status && parsed.data.created);
302
+ }
303
+ } catch {
304
+ }
305
+ const inferredStatus = inferStatusFromContent(content) || inferStatusFromGit(filePath) || "planned";
306
+ const inferredCreated = inferCreatedFromContent(content) || inferCreatedFromGit(filePath) || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
307
+ return {
308
+ path: relativePath,
309
+ fullPath,
310
+ filePath,
311
+ name,
312
+ content,
313
+ hasFrontmatter,
314
+ hasValidFrontmatter,
315
+ existingFrontmatter,
316
+ inferredStatus: existingFrontmatter?.status || inferredStatus,
317
+ inferredCreated: existingFrontmatter?.created || inferredCreated
318
+ };
319
+ }
320
+ function inferStatusFromContent(content) {
321
+ const inlineMatch = content.match(/\*\*Status\*\*:\s*(?:✅\s*|📅\s*|⏳\s*|📦\s*)?(\w+(?:-\w+)?)/i);
322
+ if (inlineMatch) {
323
+ return normalizeStatus(inlineMatch[1]);
324
+ }
325
+ const simpleMatch = content.match(/^Status:\s*(\w+(?:-\w+)?)/mi);
326
+ if (simpleMatch) {
327
+ return normalizeStatus(simpleMatch[1]);
328
+ }
329
+ if (/^\s*-\s*\[x\]\s*(done|complete|finished)/mi.test(content)) {
330
+ return "complete";
331
+ }
332
+ const adrMatch = content.match(/^##?\s*Status\s*\n+\s*(\w+)/mi);
333
+ if (adrMatch) {
334
+ const adrStatus = adrMatch[1].toLowerCase();
335
+ if (["accepted", "approved", "done"].includes(adrStatus)) {
336
+ return "complete";
337
+ }
338
+ if (["proposed", "pending", "draft"].includes(adrStatus)) {
339
+ return "planned";
340
+ }
341
+ if (["superseded", "deprecated", "rejected"].includes(adrStatus)) {
342
+ return "archived";
343
+ }
344
+ }
345
+ return null;
346
+ }
347
+ function inferStatusFromGit(filePath) {
348
+ if (!fileExistsInGit(filePath)) {
349
+ return null;
350
+ }
351
+ try {
352
+ const transitions = parseStatusTransitions(filePath);
353
+ if (transitions.length > 0) {
354
+ return transitions[transitions.length - 1].status;
355
+ }
356
+ } catch {
357
+ }
358
+ return null;
359
+ }
360
+ function normalizeStatus(status) {
361
+ const normalized = status.toLowerCase().trim().replace(/\s+/g, "-");
362
+ const statusMap = {
363
+ // Standard statuses
364
+ "planned": "planned",
365
+ "in-progress": "in-progress",
366
+ "inprogress": "in-progress",
367
+ "in_progress": "in-progress",
368
+ "wip": "in-progress",
369
+ "working": "in-progress",
370
+ "active": "in-progress",
371
+ "complete": "complete",
372
+ "completed": "complete",
373
+ "done": "complete",
374
+ "finished": "complete",
375
+ "implemented": "complete",
376
+ "archived": "archived",
377
+ "deprecated": "archived",
378
+ "superseded": "archived",
379
+ "rejected": "archived",
380
+ // ADR statuses
381
+ "accepted": "complete",
382
+ "approved": "complete",
383
+ "proposed": "planned",
384
+ "pending": "planned",
385
+ "draft": "planned"
386
+ };
387
+ return statusMap[normalized] || "planned";
388
+ }
389
+ function inferCreatedFromContent(content) {
390
+ const inlineMatch = content.match(/\*\*Created\*\*:\s*(\d{4}-\d{2}-\d{2})/);
391
+ if (inlineMatch) {
392
+ return inlineMatch[1];
393
+ }
394
+ const simpleMatch = content.match(/^Created:\s*(\d{4}-\d{2}-\d{2})/mi);
395
+ if (simpleMatch) {
396
+ return simpleMatch[1];
397
+ }
398
+ const dateMatch = content.match(/^Date:\s*(\d{4}-\d{2}-\d{2})/mi);
399
+ if (dateMatch) {
400
+ return dateMatch[1];
401
+ }
402
+ const adrMatch = content.match(/^##?\s*Date\s*\n+\s*(\w+\s+\d{1,2},?\s+\d{4})/mi);
403
+ if (adrMatch) {
404
+ try {
405
+ const date = new Date(adrMatch[1]);
406
+ if (!isNaN(date.getTime())) {
407
+ return date.toISOString().split("T")[0];
408
+ }
409
+ } catch {
410
+ }
411
+ }
412
+ const anyDateMatch = content.match(/(\d{4}-\d{2}-\d{2})/);
413
+ if (anyDateMatch) {
414
+ return anyDateMatch[1];
415
+ }
416
+ return null;
417
+ }
418
+ function inferCreatedFromGit(filePath) {
419
+ const timestamp = getFirstCommitTimestamp(filePath);
420
+ if (timestamp) {
421
+ return timestamp.split("T")[0];
422
+ }
423
+ return null;
424
+ }
243
425
 
244
426
  // src/commands/backfill.ts
245
427
  function backfillCommand() {
246
- return new Command("backfill").description("Backfill timestamps from git history").argument("[specs...]", "Specific specs to backfill (optional)").option("--dry-run", "Show what would be updated without making changes").option("--force", "Overwrite existing timestamp values").option("--assignee", "Include assignee from first commit author").option("--transitions", "Include full status transition history").option("--all", "Include all optional fields (assignee + transitions)").option("--json", "Output as JSON").action(async (specs, options) => {
428
+ return new Command("backfill").description("Backfill timestamps from git history").argument("[specs...]", "Specific specs to backfill (optional)").option("--dry-run", "Show what would be updated without making changes").option("--force", "Overwrite existing timestamp values").option("--assignee", "Include assignee from first commit author").option("--transitions", "Include full status transition history").option("--all", "Include all optional fields (assignee + transitions)").option("--bootstrap", "Create frontmatter for files without valid frontmatter").option("--json", "Output as JSON").action(async (specs, options) => {
247
429
  await backfillTimestamps({
248
430
  dryRun: options.dryRun,
249
431
  force: options.force,
250
432
  includeAssignee: options.assignee || options.all,
251
433
  includeTransitions: options.transitions || options.all,
252
434
  specs: specs && specs.length > 0 ? specs : void 0,
253
- json: options.json
435
+ json: options.json,
436
+ bootstrap: options.bootstrap
254
437
  });
255
438
  });
256
439
  }
@@ -261,12 +444,31 @@ async function backfillTimestamps(options = {}) {
261
444
  console.error("Git history is required for backfilling timestamps");
262
445
  process.exit(1);
263
446
  }
447
+ const config = await loadConfig();
448
+ const cwd = process.cwd();
449
+ const specsDir = path.join(cwd, config.specsDir);
450
+ if (options.bootstrap) {
451
+ console.log("\x1B[36m\u{1F527} Bootstrap mode - will create frontmatter for files without it\x1B[0m\n");
452
+ const bootstrapSpecs = await loadSpecsForBootstrap(specsDir, {
453
+ includeArchived: true,
454
+ targetSpecs: options.specs
455
+ });
456
+ if (bootstrapSpecs.length === 0) {
457
+ console.log("No specs found to bootstrap");
458
+ return results;
459
+ }
460
+ console.log(`Analyzing git history for ${bootstrapSpecs.length} spec${bootstrapSpecs.length === 1 ? "" : "s"}...
461
+ `);
462
+ for (const spec of bootstrapSpecs) {
463
+ const result = await bootstrapSpec(spec, options);
464
+ results.push(result);
465
+ }
466
+ printSummary(results, options);
467
+ return results;
468
+ }
264
469
  let specs;
265
470
  if (options.specs && options.specs.length > 0) {
266
471
  specs = [];
267
- const config = await loadConfig();
268
- const cwd = process.cwd();
269
- const specsDir = path.join(cwd, config.specsDir);
270
472
  for (const specPath of options.specs) {
271
473
  const resolved = await resolveSpecPath(specPath, cwd, specsDir);
272
474
  if (!resolved) {
@@ -283,6 +485,7 @@ async function backfillTimestamps(options = {}) {
283
485
  }
284
486
  if (specs.length === 0) {
285
487
  console.log("No specs found to backfill");
488
+ console.log("\x1B[36m\u2139\x1B[0m Use --bootstrap to create frontmatter for files without it");
286
489
  return results;
287
490
  }
288
491
  if (options.dryRun) {
@@ -303,6 +506,11 @@ async function backfillSpecTimestamps(spec, options) {
303
506
  specName: spec.name,
304
507
  source: "skipped"
305
508
  };
509
+ if (!spec.frontmatter.status || !spec.frontmatter.created) {
510
+ result.reason = "Missing required frontmatter (status or created)";
511
+ console.log(`\x1B[33m\u2298\x1B[0m ${spec.name} - Missing required frontmatter`);
512
+ return result;
513
+ }
306
514
  if (!fileExistsInGit(spec.filePath)) {
307
515
  result.reason = "Not in git history";
308
516
  console.log(`\x1B[33m\u2298\x1B[0m ${spec.name} - Not in git history`);
@@ -386,24 +594,107 @@ async function backfillSpecTimestamps(spec, options) {
386
594
  }
387
595
  return result;
388
596
  }
597
+ async function bootstrapSpec(spec, options) {
598
+ const result = {
599
+ specPath: spec.path,
600
+ specName: spec.name,
601
+ source: "skipped",
602
+ bootstrapped: false
603
+ };
604
+ if (spec.hasValidFrontmatter && !options.force) {
605
+ result.reason = "Already has valid frontmatter";
606
+ console.log(`\x1B[90m\u2713\x1B[0m ${spec.name} - Already valid`);
607
+ return result;
608
+ }
609
+ const frontmatter = {
610
+ ...spec.existingFrontmatter || {},
611
+ status: spec.inferredStatus || "planned",
612
+ created: spec.inferredCreated || (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
613
+ };
614
+ if (fileExistsInGit(spec.filePath)) {
615
+ const gitData = extractGitTimestamps(spec.filePath, {
616
+ includeAssignee: options.includeAssignee,
617
+ includeTransitions: options.includeTransitions
618
+ });
619
+ if (gitData.created_at) {
620
+ frontmatter.created_at = gitData.created_at;
621
+ result.created_at = gitData.created_at;
622
+ }
623
+ if (gitData.updated_at) {
624
+ frontmatter.updated_at = gitData.updated_at;
625
+ result.updated_at = gitData.updated_at;
626
+ }
627
+ if (gitData.completed_at) {
628
+ frontmatter.completed_at = gitData.completed_at;
629
+ result.completed_at = gitData.completed_at;
630
+ }
631
+ if (options.includeAssignee && gitData.assignee) {
632
+ frontmatter.assignee = gitData.assignee;
633
+ result.assignee = gitData.assignee;
634
+ }
635
+ if (options.includeTransitions && gitData.transitions && gitData.transitions.length > 0) {
636
+ frontmatter.transitions = gitData.transitions;
637
+ result.transitionsCount = gitData.transitions.length;
638
+ }
639
+ }
640
+ const statusSource = spec.existingFrontmatter?.status ? "existing" : spec.content.match(/\*\*Status\*\*/i) ? "content" : "default";
641
+ const createdSource = spec.existingFrontmatter?.created ? "existing" : spec.content.match(/\*\*Created\*\*/i) ? "content" : fileExistsInGit(spec.filePath) ? "git" : "default";
642
+ if (!options.dryRun) {
643
+ try {
644
+ await writeBootstrapFrontmatter(spec.filePath, spec.content, frontmatter);
645
+ result.source = "bootstrapped";
646
+ result.bootstrapped = true;
647
+ console.log(`\x1B[32m\u2713\x1B[0m ${spec.name} - Bootstrapped`);
648
+ console.log(` status: ${frontmatter.status} (${statusSource})`);
649
+ console.log(` created: ${frontmatter.created} (${createdSource})`);
650
+ } catch (error) {
651
+ result.source = "skipped";
652
+ result.reason = `Error: ${error instanceof Error ? error.message : String(error)}`;
653
+ console.log(`\x1B[31m\u2717\x1B[0m ${spec.name} - Failed: ${result.reason}`);
654
+ }
655
+ } else {
656
+ result.source = "bootstrapped";
657
+ result.bootstrapped = true;
658
+ console.log(`\x1B[36m\u2192\x1B[0m ${spec.name} - Would bootstrap`);
659
+ console.log(` status: ${frontmatter.status} (${statusSource})`);
660
+ console.log(` created: ${frontmatter.created} (${createdSource})`);
661
+ if (frontmatter.created_at) console.log(` created_at: ${frontmatter.created_at} (git)`);
662
+ if (frontmatter.updated_at) console.log(` updated_at: ${frontmatter.updated_at} (git)`);
663
+ if (frontmatter.completed_at) console.log(` completed_at: ${frontmatter.completed_at} (git)`);
664
+ if (frontmatter.assignee) console.log(` assignee: ${frontmatter.assignee} (git)`);
665
+ if (frontmatter.transitions) console.log(` transitions: ${frontmatter.transitions.length} (git)`);
666
+ }
667
+ return result;
668
+ }
669
+ async function writeBootstrapFrontmatter(filePath, originalContent, frontmatter) {
670
+ const matter2 = await import('gray-matter');
671
+ const parsed = matter2.default(originalContent);
672
+ const newData = { ...parsed.data, ...frontmatter };
673
+ const newContent = matter2.default.stringify(parsed.content, newData);
674
+ await fs.writeFile(filePath, newContent, "utf-8");
675
+ }
389
676
  function printSummary(results, options) {
390
677
  console.log("\n" + "\u2500".repeat(60));
391
678
  console.log("\x1B[1mSummary:\x1B[0m\n");
392
679
  const total = results.length;
393
680
  const updated = results.filter((r) => r.source === "git").length;
681
+ const bootstrapped = results.filter((r) => r.source === "bootstrapped").length;
394
682
  const existing = results.filter((r) => r.source === "existing").length;
395
683
  const skipped = results.filter((r) => r.source === "skipped").length;
396
684
  const timestampUpdates = results.filter(
397
- (r) => r.source === "git" && (r.created_at || r.updated_at || r.completed_at)
685
+ (r) => (r.source === "git" || r.source === "bootstrapped") && (r.created_at || r.updated_at || r.completed_at)
398
686
  ).length;
399
687
  const assigneeUpdates = results.filter(
400
- (r) => r.source === "git" && r.assignee
688
+ (r) => (r.source === "git" || r.source === "bootstrapped") && r.assignee
401
689
  ).length;
402
690
  const transitionUpdates = results.filter(
403
- (r) => r.source === "git" && r.transitionsCount
691
+ (r) => (r.source === "git" || r.source === "bootstrapped") && r.transitionsCount
404
692
  ).length;
405
693
  console.log(` ${total} specs analyzed`);
406
694
  if (options.dryRun) {
695
+ if (options.bootstrap) {
696
+ console.log(` ${bootstrapped} would be bootstrapped`);
697
+ }
407
698
  console.log(` ${updated} would be updated`);
408
699
  if (timestampUpdates > 0) {
409
700
  console.log(` \u2514\u2500 ${timestampUpdates} with timestamps`);
@@ -415,6 +706,9 @@ function printSummary(results, options) {
415
706
  console.log(` \u2514\u2500 ${transitionUpdates} with transitions`);
416
707
  }
417
708
  } else {
709
+ if (options.bootstrap) {
710
+ console.log(` ${bootstrapped} bootstrapped`);
711
+ }
418
712
  console.log(` ${updated} updated`);
419
713
  }
420
714
  console.log(` ${existing} already complete`);
@@ -433,12 +727,15 @@ function printSummary(results, options) {
433
727
  if (!options.includeAssignee || !options.includeTransitions) {
434
728
  console.log("\x1B[36m\u2139\x1B[0m Use --all to include optional fields (assignee, transitions)");
435
729
  }
436
- } else if (updated > 0) {
730
+ if (!options.bootstrap && skipped > 0) {
731
+ console.log("\x1B[36m\u2139\x1B[0m Use --bootstrap to create frontmatter for files without it");
732
+ }
733
+ } else if (updated > 0 || bootstrapped > 0) {
437
734
  console.log("\n\x1B[32m\u2713\x1B[0m Backfill complete!");
438
- console.log(" Run \x1B[36mlspec stats\x1B[0m to see velocity metrics");
735
+ console.log(" Run \x1B[36mlean-spec stats\x1B[0m to see velocity metrics");
439
736
  }
440
737
  }
441
738
 
442
739
  export { backfillCommand, backfillTimestamps, createSpecDirPattern, getGlobalNextSeq, resolveSpecPath };
443
- //# sourceMappingURL=chunk-A6JDTXPV.js.map
444
- //# sourceMappingURL=chunk-A6JDTXPV.js.map
740
+ //# sourceMappingURL=chunk-H5MCUMBK.js.map
741
+ //# sourceMappingURL=chunk-H5MCUMBK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/git-timestamps.ts","../src/utils/path-helpers.ts","../src/utils/bootstrap-helpers.ts","../src/commands/backfill.ts"],"names":["result","fs2","path2","path3","matter","fs3"],"mappings":"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","file":"chunk-H5MCUMBK.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport * as path from 'node:path';\nimport type { SpecStatus, StatusTransition } from '../frontmatter.js';\n\nexport interface GitTimestampData {\n created_at?: string;\n updated_at?: string;\n completed_at?: string;\n assignee?: string;\n transitions?: StatusTransition[];\n}\n\n/**\n * Check if the current directory is a git repository\n */\nexport function isGitRepository(): boolean {\n try {\n execSync('git rev-parse --is-inside-work-tree', { \n stdio: 'ignore',\n encoding: 'utf-8' \n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get the timestamp of the first commit that created a file\n */\nexport function getFirstCommitTimestamp(filePath: string): string | null {\n try {\n // Use --follow to track file renames\n // Use --diff-filter=A to find the commit that added the file\n // Format as ISO 8601 timestamp\n const timestamp = execSync(\n `git log --follow --format=\"%aI\" --diff-filter=A -- \"${filePath}\" | tail -1`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n ).trim();\n \n return timestamp || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the timestamp of the most recent commit that modified a file\n */\nexport function getLastCommitTimestamp(filePath: string): string | null {\n try {\n const timestamp = execSync(\n `git log --format=\"%aI\" -n 1 -- \"${filePath}\"`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n ).trim();\n \n return timestamp || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the timestamp when a spec was marked as complete\n * Searches git history for status changes to \"complete\"\n */\nexport function getCompletionTimestamp(filePath: string): string | null {\n try {\n // Get all commits that modified the file, with patch output\n const gitLog = execSync(\n `git log --format=\"%H|%aI\" -p -- \"${filePath}\"`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n );\n \n // Parse commits to find status change to complete\n const commits = gitLog.split('\\ndiff --git').map(section => section.trim());\n \n for (const commit of commits) {\n if (!commit) continue;\n \n // Extract commit hash and timestamp from header\n const headerMatch = commit.match(/^([a-f0-9]{40})\\|([^\\n]+)/);\n if (!headerMatch) continue;\n \n const [, , timestamp] = headerMatch;\n \n // Look for status: complete in the diff\n // Check for both YAML frontmatter and inline status changes\n if (\n /^\\+status:\\s*['\"]?complete['\"]?/m.test(commit) ||\n /^\\+\\*\\*Status\\*\\*:.*complete/mi.test(commit)\n ) {\n return timestamp;\n }\n }\n \n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the author of the first commit (for assignee inference)\n */\nexport function getFirstCommitAuthor(filePath: string): string | null {\n try {\n const author = execSync(\n `git log --follow --format=\"%an\" --diff-filter=A -- \"${filePath}\" | tail -1`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n ).trim();\n \n return author || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse all status transitions from git history\n * Reconstructs the full status change timeline\n */\nexport function parseStatusTransitions(filePath: string): StatusTransition[] {\n const transitions: StatusTransition[] = [];\n \n try {\n // Get all commits that modified the file, with patch output\n const gitLog = execSync(\n `git log --format=\"%H|%aI\" -p --reverse -- \"${filePath}\"`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n );\n \n // Parse commits in chronological order (--reverse)\n const commits = gitLog.split('\\ndiff --git').map(section => section.trim());\n \n const validStatuses: SpecStatus[] = ['planned', 'in-progress', 'complete', 'archived'];\n \n for (const commit of commits) {\n if (!commit) continue;\n \n // Extract commit hash and timestamp from header\n const headerMatch = commit.match(/^([a-f0-9]{40})\\|([^\\n]+)/);\n if (!headerMatch) continue;\n \n const [, , timestamp] = headerMatch;\n \n // Look for status changes in the diff\n // Match: +status: complete or +status: 'complete'\n const statusMatch = commit.match(/^\\+status:\\s*['\"]?(\\w+(?:-\\w+)?)['\"]?/m);\n if (statusMatch) {\n const status = statusMatch[1] as SpecStatus;\n \n // Only record valid status values\n if (validStatuses.includes(status)) {\n // Avoid duplicate consecutive transitions\n const lastTransition = transitions[transitions.length - 1];\n if (!lastTransition || lastTransition.status !== status) {\n transitions.push({ status, at: timestamp });\n }\n }\n }\n }\n \n return transitions;\n } catch {\n return [];\n }\n}\n\n/**\n * Extract all git timestamp data for a spec file\n */\nexport function extractGitTimestamps(\n filePath: string,\n options: {\n includeAssignee?: boolean;\n includeTransitions?: boolean;\n } = {}\n): GitTimestampData {\n const data: GitTimestampData = {};\n \n // Core timestamps (always extracted)\n data.created_at = getFirstCommitTimestamp(filePath) ?? undefined;\n data.updated_at = getLastCommitTimestamp(filePath) ?? undefined;\n data.completed_at = getCompletionTimestamp(filePath) ?? undefined;\n \n // Optional fields\n if (options.includeAssignee) {\n const author = getFirstCommitAuthor(filePath);\n if (author) {\n data.assignee = author;\n }\n }\n \n if (options.includeTransitions) {\n const transitions = parseStatusTransitions(filePath);\n if (transitions.length > 0) {\n data.transitions = transitions;\n }\n }\n \n return data;\n}\n\n/**\n * Validate that a file exists in git history\n */\nexport function fileExistsInGit(filePath: string): boolean {\n try {\n execSync(\n `git log -n 1 -- \"${filePath}\"`,\n { stdio: 'ignore', encoding: 'utf-8' }\n );\n return true;\n } catch {\n return false;\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * Create a regex pattern to match spec directories with sequence numbers\n * Handles optional date prefixes like 20251103-001-name\n */\nexport function createSpecDirPattern(): RegExp {\n // Match spec directories, handling optional date prefix\n // Patterns:\n // - 001-name (simple sequence)\n // - 20251103-001-name (date prefix + sequence)\n // - spec-001-name (custom prefix + sequence)\n // - 001-测试 (Unicode/Chinese name)\n // We look for: optional-prefix + NNN + dash + name\n // The sequence is 2-4 digits (to avoid matching 8-digit dates as sequences)\n // Requires dash followed by non-digit/non-dash character to ensure this is a spec directory name\n // Uses [^0-9-] instead of [a-z] to support Unicode characters (Chinese, Japanese, etc.)\n return /(?:^|\\D)(\\d{2,4})-[^0-9-]/i;\n}\n\n/**\n * Get next global sequence number across entire specs directory\n */\nexport async function getGlobalNextSeq(specsDir: string, digits: number): Promise<string> {\n try {\n // Recursively find all spec directories with sequence numbers\n const seqNumbers: number[] = [];\n const specPattern = createSpecDirPattern();\n \n async function scanDirectory(dir: string): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Check if this is a spec directory (NNN-name format)\n const match = entry.name.match(specPattern);\n if (match) {\n const seqNum = parseInt(match[1], 10);\n if (!isNaN(seqNum) && seqNum > 0) {\n seqNumbers.push(seqNum);\n }\n }\n \n // Skip archived directory to avoid confusion\n if (entry.name === 'archived') continue;\n \n // Recursively scan subdirectories (for custom pattern grouping)\n const subDir = path.join(dir, entry.name);\n await scanDirectory(subDir);\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n }\n \n await scanDirectory(specsDir);\n \n if (seqNumbers.length === 0) {\n return '1'.padStart(digits, '0');\n }\n \n const maxSeq = Math.max(...seqNumbers);\n return String(maxSeq + 1).padStart(digits, '0');\n } catch {\n return '1'.padStart(digits, '0');\n }\n}\n\n/**\n * Get next sequence number for a date directory (legacy, kept for backward compatibility)\n */\nexport async function getNextSeq(dateDir: string, digits: number): Promise<string> {\n try {\n const specPattern = createSpecDirPattern();\n const entries = await fs.readdir(dateDir, { withFileTypes: true });\n const seqNumbers = entries\n .filter((e) => e.isDirectory() && specPattern.test(e.name))\n .map((e) => {\n const match = e.name.match(specPattern);\n return match ? parseInt(match[1], 10) : NaN;\n })\n .filter((n) => !isNaN(n));\n\n if (seqNumbers.length === 0) {\n return '1'.padStart(digits, '0');\n }\n\n const maxSeq = Math.max(...seqNumbers);\n return String(maxSeq + 1).padStart(digits, '0');\n } catch {\n return '1'.padStart(digits, '0');\n }\n}\n\n/**\n * Resolve spec path in multiple ways:\n * 1. Absolute path as given\n * 2. Relative to current directory\n * 3. Relative to specs directory\n * 4. Search by spec name in all subdirectories (flat or grouped)\n * 5. Search by sequence number only\n */\nexport async function resolveSpecPath(\n specPath: string,\n cwd: string,\n specsDir: string\n): Promise<string | null> {\n // Try absolute path\n if (path.isAbsolute(specPath)) {\n try {\n await fs.access(specPath);\n return specPath;\n } catch {\n return null;\n }\n }\n\n // Try relative to cwd\n const cwdPath = path.resolve(cwd, specPath);\n try {\n await fs.access(cwdPath);\n return cwdPath;\n } catch {\n // Continue to next method\n }\n\n // Try relative to specs directory\n const specsPath = path.join(specsDir, specPath);\n try {\n await fs.access(specsPath);\n return specsPath;\n } catch {\n // Continue to next method\n }\n\n // Search by sequence number only (e.g., \"5\" or \"005\")\n const seqMatch = specPath.match(/^0*(\\d+)$/);\n if (seqMatch) {\n const seqNum = parseInt(seqMatch[1], 10);\n const result = await searchBySequence(specsDir, seqNum);\n if (result) return result;\n }\n\n // Last resort: search for spec name in all subdirectories\n const specName = specPath.replace(/^.*\\//, ''); // Get last part\n const result = await searchInAllDirectories(specsDir, specName);\n return result;\n}\n\n/**\n * Search for a spec by sequence number across all directories\n */\nasync function searchBySequence(specsDir: string, seqNum: number): Promise<string | null> {\n const specPattern = createSpecDirPattern();\n \n async function scanDirectory(dir: string): Promise<string | null> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Check if this matches the sequence number\n const match = entry.name.match(specPattern);\n if (match) {\n const entrySeq = parseInt(match[1], 10);\n if (entrySeq === seqNum) {\n return path.join(dir, entry.name);\n }\n }\n \n // Recursively search subdirectories (including archived)\n const subDir = path.join(dir, entry.name);\n const result = await scanDirectory(subDir);\n if (result) return result;\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n \n return null;\n }\n \n return scanDirectory(specsDir);\n}\n\n/**\n * Search for a spec by name in all subdirectories\n */\nasync function searchInAllDirectories(specsDir: string, specName: string): Promise<string | null> {\n async function scanDirectory(dir: string): Promise<string | null> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Check if this matches the spec name\n if (entry.name === specName) {\n return path.join(dir, entry.name);\n }\n \n // Recursively search subdirectories (including archived)\n const subDir = path.join(dir, entry.name);\n const result = await scanDirectory(subDir);\n if (result) return result;\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n \n return null;\n }\n \n return scanDirectory(specsDir);\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport matter from 'gray-matter';\nimport { loadConfig } from '../config.js';\nimport type { SpecStatus, SpecFrontmatter } from '../frontmatter.js';\nimport { \n getFirstCommitTimestamp, \n fileExistsInGit,\n parseStatusTransitions,\n} from './git-timestamps.js';\n\n/**\n * Information about a spec file that may or may not have valid frontmatter\n */\nexport interface BootstrapSpecInfo {\n path: string; // Relative path to spec directory\n fullPath: string; // Absolute path to spec directory\n filePath: string; // Absolute path to spec file (README.md)\n name: string; // Spec directory name\n content: string; // File content\n hasFrontmatter: boolean; // Does file have YAML frontmatter?\n hasValidFrontmatter: boolean; // Does frontmatter have required fields?\n existingFrontmatter?: Partial<SpecFrontmatter>; // Existing frontmatter data\n inferredStatus?: SpecStatus; // Status inferred from content/git\n inferredCreated?: string; // Created date inferred from content/git\n}\n\n/**\n * Load all spec directories, including those without valid frontmatter\n */\nexport async function loadSpecsForBootstrap(\n specsDir: string,\n options: {\n includeArchived?: boolean;\n targetSpecs?: string[];\n } = {}\n): Promise<BootstrapSpecInfo[]> {\n const specs: BootstrapSpecInfo[] = [];\n const config = await loadConfig();\n \n // Pattern to match spec directories (2 or more digits followed by dash)\n const specPattern = /^(\\d{2,})-/;\n \n async function scanDirectory(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Skip archived directory in main scan (handle separately)\n if (entry.name === 'archived' && relativePath === '') continue;\n \n const entryPath = path.join(dir, entry.name);\n const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;\n \n // Check if this is a spec directory (NNN-name format)\n if (specPattern.test(entry.name)) {\n // Filter by target specs if specified\n if (options.targetSpecs && options.targetSpecs.length > 0) {\n const matches = options.targetSpecs.some(target => {\n // Match by name, number, or path\n const targetNum = target.match(/^0*(\\d+)$/)?.[1];\n const entryNum = entry.name.match(/^(\\d+)/)?.[1];\n return entry.name === target || \n entry.name.includes(target) || \n (targetNum && entryNum && parseInt(targetNum, 10) === parseInt(entryNum, 10));\n });\n if (!matches) continue;\n }\n \n const specFile = path.join(entryPath, config.structure.defaultFile);\n \n try {\n await fs.access(specFile);\n const specInfo = await analyzeSpecFile(specFile, entryPath, entryRelativePath, entry.name);\n specs.push(specInfo);\n } catch {\n // No spec file found, skip\n }\n } else {\n // Not a spec directory, scan recursively\n await scanDirectory(entryPath, entryRelativePath);\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n }\n \n await scanDirectory(specsDir);\n \n // Load archived specs if requested\n if (options.includeArchived) {\n const archivedPath = path.join(specsDir, 'archived');\n await scanDirectory(archivedPath, 'archived');\n }\n \n // Sort by spec number descending\n specs.sort((a, b) => {\n const aNum = parseInt(a.name.match(/^(\\d+)/)?.[1] || '0', 10);\n const bNum = parseInt(b.name.match(/^(\\d+)/)?.[1] || '0', 10);\n return bNum - aNum;\n });\n \n return specs;\n}\n\n/**\n * Analyze a spec file and extract/infer frontmatter data\n */\nasync function analyzeSpecFile(\n filePath: string,\n fullPath: string,\n relativePath: string,\n name: string\n): Promise<BootstrapSpecInfo> {\n const content = await fs.readFile(filePath, 'utf-8');\n \n // Try to parse existing frontmatter\n let hasFrontmatter = false;\n let hasValidFrontmatter = false;\n let existingFrontmatter: Partial<SpecFrontmatter> | undefined;\n \n try {\n const parsed = matter(content);\n if (parsed.data && Object.keys(parsed.data).length > 0) {\n hasFrontmatter = true;\n existingFrontmatter = parsed.data as Partial<SpecFrontmatter>;\n \n // Check if required fields exist\n hasValidFrontmatter = !!(parsed.data.status && parsed.data.created);\n }\n } catch {\n // Failed to parse frontmatter\n }\n \n // Infer status from content or git\n const inferredStatus = inferStatusFromContent(content) || \n inferStatusFromGit(filePath) || \n 'planned';\n \n // Infer created date from content or git\n const inferredCreated = inferCreatedFromContent(content) || \n inferCreatedFromGit(filePath) ||\n new Date().toISOString().split('T')[0];\n \n return {\n path: relativePath,\n fullPath,\n filePath,\n name,\n content,\n hasFrontmatter,\n hasValidFrontmatter,\n existingFrontmatter,\n inferredStatus: existingFrontmatter?.status || inferredStatus,\n inferredCreated: existingFrontmatter?.created || inferredCreated,\n };\n}\n\n/**\n * Infer status from markdown content using common patterns\n */\nexport function inferStatusFromContent(content: string): SpecStatus | null {\n // Pattern 1: **Status**: Complete (inline metadata)\n const inlineMatch = content.match(/\\*\\*Status\\*\\*:\\s*(?:✅\\s*|📅\\s*|⏳\\s*|📦\\s*)?(\\w+(?:-\\w+)?)/i);\n if (inlineMatch) {\n return normalizeStatus(inlineMatch[1]);\n }\n \n // Pattern 2: Status: Complete (simple format)\n const simpleMatch = content.match(/^Status:\\s*(\\w+(?:-\\w+)?)/mi);\n if (simpleMatch) {\n return normalizeStatus(simpleMatch[1]);\n }\n \n // Pattern 3: [x] Done / [ ] In Progress (task list format)\n if (/^\\s*-\\s*\\[x\\]\\s*(done|complete|finished)/mi.test(content)) {\n return 'complete';\n }\n \n // Pattern 4: ADR format - \"Status: Accepted\"\n const adrMatch = content.match(/^##?\\s*Status\\s*\\n+\\s*(\\w+)/mi);\n if (adrMatch) {\n const adrStatus = adrMatch[1].toLowerCase();\n if (['accepted', 'approved', 'done'].includes(adrStatus)) {\n return 'complete';\n }\n if (['proposed', 'pending', 'draft'].includes(adrStatus)) {\n return 'planned';\n }\n if (['superseded', 'deprecated', 'rejected'].includes(adrStatus)) {\n return 'archived';\n }\n }\n \n return null;\n}\n\n/**\n * Infer status from git history (check if ever marked complete)\n */\nexport function inferStatusFromGit(filePath: string): SpecStatus | null {\n if (!fileExistsInGit(filePath)) {\n return null;\n }\n \n try {\n const transitions = parseStatusTransitions(filePath);\n if (transitions.length > 0) {\n // Return the last known status from git history\n return transitions[transitions.length - 1].status;\n }\n } catch {\n // Git parsing failed\n }\n \n return null;\n}\n\n/**\n * Normalize various status strings to valid SpecStatus\n */\nfunction normalizeStatus(status: string): SpecStatus {\n const normalized = status.toLowerCase().trim().replace(/\\s+/g, '-');\n \n const statusMap: Record<string, SpecStatus> = {\n // Standard statuses\n 'planned': 'planned',\n 'in-progress': 'in-progress',\n 'inprogress': 'in-progress',\n 'in_progress': 'in-progress',\n 'wip': 'in-progress',\n 'working': 'in-progress',\n 'active': 'in-progress',\n 'complete': 'complete',\n 'completed': 'complete',\n 'done': 'complete',\n 'finished': 'complete',\n 'implemented': 'complete',\n 'archived': 'archived',\n 'deprecated': 'archived',\n 'superseded': 'archived',\n 'rejected': 'archived',\n // ADR statuses\n 'accepted': 'complete',\n 'approved': 'complete',\n 'proposed': 'planned',\n 'pending': 'planned',\n 'draft': 'planned',\n };\n \n return statusMap[normalized] || 'planned';\n}\n\n/**\n * Infer created date from markdown content\n */\nexport function inferCreatedFromContent(content: string): string | null {\n // Pattern 1: **Created**: 2025-01-15 (inline metadata)\n const inlineMatch = content.match(/\\*\\*Created\\*\\*:\\s*(\\d{4}-\\d{2}-\\d{2})/);\n if (inlineMatch) {\n return inlineMatch[1];\n }\n \n // Pattern 2: Created: 2025-01-15 (simple format)\n const simpleMatch = content.match(/^Created:\\s*(\\d{4}-\\d{2}-\\d{2})/mi);\n if (simpleMatch) {\n return simpleMatch[1];\n }\n \n // Pattern 3: Date: 2025-01-15\n const dateMatch = content.match(/^Date:\\s*(\\d{4}-\\d{2}-\\d{2})/mi);\n if (dateMatch) {\n return dateMatch[1];\n }\n \n // Pattern 4: ADR format - \"Date: January 15, 2025\"\n const adrMatch = content.match(/^##?\\s*Date\\s*\\n+\\s*(\\w+\\s+\\d{1,2},?\\s+\\d{4})/mi);\n if (adrMatch) {\n try {\n const date = new Date(adrMatch[1]);\n if (!isNaN(date.getTime())) {\n return date.toISOString().split('T')[0];\n }\n } catch {\n // Failed to parse date\n }\n }\n \n // Pattern 5: Look for dates in common formats anywhere\n const anyDateMatch = content.match(/(\\d{4}-\\d{2}-\\d{2})/);\n if (anyDateMatch) {\n return anyDateMatch[1];\n }\n \n return null;\n}\n\n/**\n * Infer created date from git history (first commit date)\n */\nexport function inferCreatedFromGit(filePath: string): string | null {\n const timestamp = getFirstCommitTimestamp(filePath);\n if (timestamp) {\n // Extract date part from ISO timestamp\n return timestamp.split('T')[0];\n }\n return null;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Command } from 'commander';\nimport { loadAllSpecs, getSpec, type SpecInfo } from '../spec-loader.js';\nimport { updateFrontmatter, type SpecFrontmatter, type SpecStatus } from '../frontmatter.js';\nimport { loadConfig } from '../config.js';\nimport { \n isGitRepository,\n extractGitTimestamps,\n fileExistsInGit,\n getFirstCommitTimestamp,\n type GitTimestampData,\n} from '../utils/git-timestamps.js';\nimport { resolveSpecPath } from '../utils/path-helpers.js';\nimport { \n loadSpecsForBootstrap, \n type BootstrapSpecInfo,\n inferStatusFromContent,\n inferCreatedFromContent,\n} from '../utils/bootstrap-helpers.js';\n\nexport interface BackfillResult {\n specPath: string;\n specName: string;\n created_at?: string;\n updated_at?: string;\n completed_at?: string;\n assignee?: string;\n transitionsCount?: number;\n source: 'git' | 'existing' | 'skipped' | 'bootstrapped';\n reason?: string;\n bootstrapped?: boolean; // Was frontmatter created from scratch?\n}\n\nexport interface BackfillOptions {\n dryRun?: boolean;\n force?: boolean;\n includeAssignee?: boolean;\n includeTransitions?: boolean;\n specs?: string[]; // specific specs to target\n json?: boolean;\n bootstrap?: boolean; // Create frontmatter for files without it\n}\n\n/**\n * Backfill command - backfill timestamps from git history\n */\nexport function backfillCommand(): Command {\n return new Command('backfill')\n .description('Backfill timestamps from git history')\n .argument('[specs...]', 'Specific specs to backfill (optional)')\n .option('--dry-run', 'Show what would be updated without making changes')\n .option('--force', 'Overwrite existing timestamp values')\n .option('--assignee', 'Include assignee from first commit author')\n .option('--transitions', 'Include full status transition history')\n .option('--all', 'Include all optional fields (assignee + transitions)')\n .option('--bootstrap', 'Create frontmatter for files without valid frontmatter')\n .option('--json', 'Output as JSON')\n .action(async (specs: string[] | undefined, options: {\n dryRun?: boolean;\n force?: boolean;\n assignee?: boolean;\n transitions?: boolean;\n all?: boolean;\n json?: boolean;\n bootstrap?: boolean;\n }) => {\n await backfillTimestamps({\n dryRun: options.dryRun,\n force: options.force,\n includeAssignee: options.assignee || options.all,\n includeTransitions: options.transitions || options.all,\n specs: specs && specs.length > 0 ? specs : undefined,\n json: options.json,\n bootstrap: options.bootstrap,\n });\n });\n}\n\n/**\n * Backfill timestamps from git history for all or specific specs\n */\nexport async function backfillTimestamps(options: BackfillOptions = {}): Promise<BackfillResult[]> {\n const results: BackfillResult[] = [];\n \n // Check if we're in a git repository\n if (!isGitRepository()) {\n console.error('\\x1b[31mError:\\x1b[0m Not in a git repository');\n console.error('Git history is required for backfilling timestamps');\n process.exit(1);\n }\n \n const config = await loadConfig();\n const cwd = process.cwd();\n const specsDir = path.join(cwd, config.specsDir);\n \n // Bootstrap mode: also load specs without valid frontmatter\n if (options.bootstrap) {\n console.log('\\x1b[36m🔧 Bootstrap mode - will create frontmatter for files without it\\x1b[0m\\n');\n \n const bootstrapSpecs = await loadSpecsForBootstrap(specsDir, {\n includeArchived: true,\n targetSpecs: options.specs,\n });\n \n if (bootstrapSpecs.length === 0) {\n console.log('No specs found to bootstrap');\n return results;\n }\n \n console.log(`Analyzing git history for ${bootstrapSpecs.length} spec${bootstrapSpecs.length === 1 ? '' : 's'}...\\n`);\n \n for (const spec of bootstrapSpecs) {\n const result = await bootstrapSpec(spec, options);\n results.push(result);\n }\n \n printSummary(results, options);\n return results;\n }\n \n // Normal mode: load specs with valid frontmatter only\n let specs: SpecInfo[];\n \n if (options.specs && options.specs.length > 0) {\n // Load specific specs\n specs = [];\n \n for (const specPath of options.specs) {\n const resolved = await resolveSpecPath(specPath, cwd, specsDir);\n if (!resolved) {\n console.warn(`\\x1b[33mWarning:\\x1b[0m Spec not found: ${specPath}`);\n continue;\n }\n const spec = await getSpec(resolved);\n if (spec) {\n specs.push(spec);\n }\n }\n } else {\n // Load all specs (including archived)\n specs = await loadAllSpecs({ includeArchived: true });\n }\n \n if (specs.length === 0) {\n console.log('No specs found to backfill');\n console.log('\\x1b[36mℹ\\x1b[0m Use --bootstrap to create frontmatter for files without it');\n return results;\n }\n \n // Show what we're doing\n if (options.dryRun) {\n console.log('\\x1b[36m🔍 Dry run mode - no changes will be made\\x1b[0m\\n');\n }\n \n console.log(`Analyzing git history for ${specs.length} spec${specs.length === 1 ? '' : 's'}...\\n`);\n \n // Process each spec\n for (const spec of specs) {\n const result = await backfillSpecTimestamps(spec, options);\n results.push(result);\n }\n \n // Print summary\n printSummary(results, options);\n \n return results;\n}\n\n/**\n * Backfill timestamps for a single spec\n */\nasync function backfillSpecTimestamps(\n spec: SpecInfo,\n options: BackfillOptions\n): Promise<BackfillResult> {\n const result: BackfillResult = {\n specPath: spec.path,\n specName: spec.name,\n source: 'skipped',\n };\n \n // Skip specs without valid frontmatter (missing required fields)\n if (!spec.frontmatter.status || !spec.frontmatter.created) {\n result.reason = 'Missing required frontmatter (status or created)';\n console.log(`\\x1b[33m⊘\\x1b[0m ${spec.name} - Missing required frontmatter`);\n return result;\n }\n \n // Check if file exists in git history\n if (!fileExistsInGit(spec.filePath)) {\n result.reason = 'Not in git history';\n console.log(`\\x1b[33m⊘\\x1b[0m ${spec.name} - Not in git history`);\n return result;\n }\n \n // Extract git timestamps\n const gitData = extractGitTimestamps(spec.filePath, {\n includeAssignee: options.includeAssignee,\n includeTransitions: options.includeTransitions,\n });\n \n // Determine what needs to be updated\n const updates: Partial<SpecFrontmatter> = {};\n let hasUpdates = false;\n \n // Check created_at\n if (gitData.created_at && (options.force || !spec.frontmatter.created_at)) {\n updates.created_at = gitData.created_at;\n result.created_at = gitData.created_at;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.created_at) {\n result.created_at = spec.frontmatter.created_at;\n result.source = 'existing';\n }\n \n // Check updated_at\n if (gitData.updated_at && (options.force || !spec.frontmatter.updated_at)) {\n updates.updated_at = gitData.updated_at;\n result.updated_at = gitData.updated_at;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.updated_at) {\n result.updated_at = spec.frontmatter.updated_at;\n result.source = 'existing';\n }\n \n // Check completed_at\n if (gitData.completed_at && (options.force || !spec.frontmatter.completed_at)) {\n updates.completed_at = gitData.completed_at;\n result.completed_at = gitData.completed_at;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.completed_at) {\n result.completed_at = spec.frontmatter.completed_at;\n result.source = 'existing';\n }\n \n // Check assignee (optional)\n if (options.includeAssignee && gitData.assignee && (options.force || !spec.frontmatter.assignee)) {\n updates.assignee = gitData.assignee;\n result.assignee = gitData.assignee;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.assignee) {\n result.assignee = spec.frontmatter.assignee;\n }\n \n // Check transitions (optional)\n if (options.includeTransitions && gitData.transitions && gitData.transitions.length > 0) {\n if (options.force || !spec.frontmatter.transitions || spec.frontmatter.transitions.length === 0) {\n updates.transitions = gitData.transitions;\n result.transitionsCount = gitData.transitions.length;\n result.source = 'git';\n hasUpdates = true;\n } else {\n // Merge with existing transitions (optional: could implement smart merge)\n result.transitionsCount = spec.frontmatter.transitions.length;\n }\n }\n \n // Sync updated date field with updated_at timestamp\n if (updates.updated_at && !updates.updated) {\n updates.updated = updates.updated_at.split('T')[0];\n }\n \n if (!hasUpdates) {\n result.reason = 'Already has complete data';\n console.log(`\\x1b[90m✓\\x1b[0m ${spec.name} - Already complete`);\n return result;\n }\n \n // Apply updates (unless dry run)\n if (!options.dryRun) {\n try {\n await updateFrontmatter(spec.filePath, updates);\n console.log(`\\x1b[32m✓\\x1b[0m ${spec.name} - Updated`);\n } catch (error) {\n result.source = 'skipped';\n result.reason = `Error: ${error instanceof Error ? error.message : String(error)}`;\n console.log(`\\x1b[31m✗\\x1b[0m ${spec.name} - Failed: ${result.reason}`);\n }\n } else {\n console.log(`\\x1b[36m→\\x1b[0m ${spec.name} - Would update`);\n // Show what would be updated\n if (updates.created_at) console.log(` created_at: ${updates.created_at} (git)`);\n if (updates.updated_at) console.log(` updated_at: ${updates.updated_at} (git)`);\n if (updates.completed_at) console.log(` completed_at: ${updates.completed_at} (git)`);\n if (updates.assignee) console.log(` assignee: ${updates.assignee} (git)`);\n if (updates.transitions) console.log(` transitions: ${updates.transitions.length} status changes (git)`);\n }\n \n return result;\n}\n\n/**\n * Bootstrap a spec file with missing or incomplete frontmatter\n */\nasync function bootstrapSpec(\n spec: BootstrapSpecInfo,\n options: BackfillOptions\n): Promise<BackfillResult> {\n const result: BackfillResult = {\n specPath: spec.path,\n specName: spec.name,\n source: 'skipped',\n bootstrapped: false,\n };\n \n // Skip specs that already have valid frontmatter (unless --force)\n if (spec.hasValidFrontmatter && !options.force) {\n // Still run normal backfill logic for timestamps\n result.reason = 'Already has valid frontmatter';\n console.log(`\\x1b[90m✓\\x1b[0m ${spec.name} - Already valid`);\n return result;\n }\n \n // Build frontmatter from existing + inferred data\n const frontmatter: Partial<SpecFrontmatter> = {\n ...(spec.existingFrontmatter || {}),\n status: spec.inferredStatus || 'planned',\n created: spec.inferredCreated || new Date().toISOString().split('T')[0],\n };\n \n // Get git timestamps\n if (fileExistsInGit(spec.filePath)) {\n const gitData = extractGitTimestamps(spec.filePath, {\n includeAssignee: options.includeAssignee,\n includeTransitions: options.includeTransitions,\n });\n \n if (gitData.created_at) {\n frontmatter.created_at = gitData.created_at;\n result.created_at = gitData.created_at;\n }\n if (gitData.updated_at) {\n frontmatter.updated_at = gitData.updated_at;\n result.updated_at = gitData.updated_at;\n }\n if (gitData.completed_at) {\n frontmatter.completed_at = gitData.completed_at;\n result.completed_at = gitData.completed_at;\n }\n if (options.includeAssignee && gitData.assignee) {\n frontmatter.assignee = gitData.assignee;\n result.assignee = gitData.assignee;\n }\n if (options.includeTransitions && gitData.transitions && gitData.transitions.length > 0) {\n frontmatter.transitions = gitData.transitions;\n result.transitionsCount = gitData.transitions.length;\n }\n }\n \n // Determine source labels for output\n const statusSource = spec.existingFrontmatter?.status ? 'existing' : \n (spec.content.match(/\\*\\*Status\\*\\*/i) ? 'content' : 'default');\n const createdSource = spec.existingFrontmatter?.created ? 'existing' :\n (spec.content.match(/\\*\\*Created\\*\\*/i) ? 'content' : \n (fileExistsInGit(spec.filePath) ? 'git' : 'default'));\n \n if (!options.dryRun) {\n try {\n await writeBootstrapFrontmatter(spec.filePath, spec.content, frontmatter);\n result.source = 'bootstrapped';\n result.bootstrapped = true;\n console.log(`\\x1b[32m✓\\x1b[0m ${spec.name} - Bootstrapped`);\n console.log(` status: ${frontmatter.status} (${statusSource})`);\n console.log(` created: ${frontmatter.created} (${createdSource})`);\n } catch (error) {\n result.source = 'skipped';\n result.reason = `Error: ${error instanceof Error ? error.message : String(error)}`;\n console.log(`\\x1b[31m✗\\x1b[0m ${spec.name} - Failed: ${result.reason}`);\n }\n } else {\n result.source = 'bootstrapped';\n result.bootstrapped = true;\n console.log(`\\x1b[36m→\\x1b[0m ${spec.name} - Would bootstrap`);\n console.log(` status: ${frontmatter.status} (${statusSource})`);\n console.log(` created: ${frontmatter.created} (${createdSource})`);\n if (frontmatter.created_at) console.log(` created_at: ${frontmatter.created_at} (git)`);\n if (frontmatter.updated_at) console.log(` updated_at: ${frontmatter.updated_at} (git)`);\n if (frontmatter.completed_at) console.log(` completed_at: ${frontmatter.completed_at} (git)`);\n if (frontmatter.assignee) console.log(` assignee: ${frontmatter.assignee} (git)`);\n if (frontmatter.transitions) console.log(` transitions: ${frontmatter.transitions.length} (git)`);\n }\n \n return result;\n}\n\n/**\n * Write or update frontmatter in a spec file\n */\nasync function writeBootstrapFrontmatter(\n filePath: string,\n originalContent: string,\n frontmatter: Partial<SpecFrontmatter>\n): Promise<void> {\n const matter = await import('gray-matter');\n \n // Parse existing content (may or may not have frontmatter)\n const parsed = matter.default(originalContent);\n \n // Merge new frontmatter with any existing\n const newData = { ...parsed.data, ...frontmatter };\n \n // Write back\n const newContent = matter.default.stringify(parsed.content, newData);\n await fs.writeFile(filePath, newContent, 'utf-8');\n}\n\n/**\n * Print summary of backfill results\n */\nfunction printSummary(results: BackfillResult[], options: BackfillOptions): void {\n console.log('\\n' + '─'.repeat(60));\n console.log('\\x1b[1mSummary:\\x1b[0m\\n');\n \n const total = results.length;\n const updated = results.filter(r => r.source === 'git').length;\n const bootstrapped = results.filter(r => r.source === 'bootstrapped').length;\n const existing = results.filter(r => r.source === 'existing').length;\n const skipped = results.filter(r => r.source === 'skipped').length;\n \n const timestampUpdates = results.filter(r => \n (r.source === 'git' || r.source === 'bootstrapped') && (r.created_at || r.updated_at || r.completed_at)\n ).length;\n \n const assigneeUpdates = results.filter(r => \n (r.source === 'git' || r.source === 'bootstrapped') && r.assignee\n ).length;\n \n const transitionUpdates = results.filter(r => \n (r.source === 'git' || r.source === 'bootstrapped') && r.transitionsCount\n ).length;\n \n console.log(` ${total} specs analyzed`);\n \n if (options.dryRun) {\n if (options.bootstrap) {\n console.log(` ${bootstrapped} would be bootstrapped`);\n }\n console.log(` ${updated} would be updated`);\n if (timestampUpdates > 0) {\n console.log(` └─ ${timestampUpdates} with timestamps`);\n }\n if (options.includeAssignee && assigneeUpdates > 0) {\n console.log(` └─ ${assigneeUpdates} with assignee`);\n }\n if (options.includeTransitions && transitionUpdates > 0) {\n console.log(` └─ ${transitionUpdates} with transitions`);\n }\n } else {\n if (options.bootstrap) {\n console.log(` ${bootstrapped} bootstrapped`);\n }\n console.log(` ${updated} updated`);\n }\n \n console.log(` ${existing} already complete`);\n console.log(` ${skipped} skipped`);\n \n // Show reasons for skipped specs\n const skipReasons = results\n .filter(r => r.source === 'skipped' && r.reason)\n .map(r => r.reason);\n \n if (skipReasons.length > 0) {\n console.log('\\n\\x1b[33mSkipped reasons:\\x1b[0m');\n const uniqueReasons = [...new Set(skipReasons)];\n for (const reason of uniqueReasons) {\n const count = skipReasons.filter(r => r === reason).length;\n console.log(` - ${reason} (${count})`);\n }\n }\n \n // Guidance\n if (options.dryRun) {\n console.log('\\n\\x1b[36mℹ\\x1b[0m Run without --dry-run to apply changes');\n \n if (!options.includeAssignee || !options.includeTransitions) {\n console.log('\\x1b[36mℹ\\x1b[0m Use --all to include optional fields (assignee, transitions)');\n }\n \n if (!options.bootstrap && skipped > 0) {\n console.log('\\x1b[36mℹ\\x1b[0m Use --bootstrap to create frontmatter for files without it');\n }\n } else if (updated > 0 || bootstrapped > 0) {\n console.log('\\n\\x1b[32m✓\\x1b[0m Backfill complete!');\n console.log(' Run \\x1b[36mlean-spec stats\\x1b[0m to see velocity metrics');\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { getSpecFile, parseFrontmatter, matchesFilter } from './chunk-LVD7ZAVZ.js';
1
+ import { getSpecFile, parseFrontmatter, matchesFilter } from './chunk-VN5BUHTV.js';
2
2
  import * as fs2 from 'fs/promises';
3
3
  import * as path2 from 'path';
4
4
 
@@ -294,5 +294,5 @@ async function getSpec(specPath) {
294
294
  }
295
295
 
296
296
  export { extractGroup, getSpec, loadAllSpecs, loadConfig, loadSubFiles, resolvePrefix, saveConfig };
297
- //# sourceMappingURL=chunk-LXOJW2FE.js.map
298
- //# sourceMappingURL=chunk-LXOJW2FE.js.map
297
+ //# sourceMappingURL=chunk-RF5PKL6L.js.map
298
+ //# sourceMappingURL=chunk-RF5PKL6L.js.map