1
0

generate-migration-prompt.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { readFileSync, writeFileSync } from 'node:fs';
  2. import { join } from 'node:path';
  3. // eslint-disable no-console
  4. /**
  5. * Extracts the first heading (# or ##) from markdown content
  6. */
  7. function extractTitle(content: string): string {
  8. const titleRegex = /^##?\s+(.+)$/m;
  9. const match = content.match(titleRegex);
  10. return match ? match[1].trim() : '';
  11. }
  12. /**
  13. * Extracts the Instructions section from SKILL.md
  14. */
  15. function extractInstructions(content: string): string {
  16. const instructionsRegex = /## Instructions\s+([\s\S]*?)(?=\n##|$)/;
  17. const match = content.match(instructionsRegex);
  18. return match ? match[1].trim() : '';
  19. }
  20. /**
  21. * Replaces file references with section references
  22. */
  23. function replaceFileReferences(text: string, fileToTitleMap: Map<string, string>): string {
  24. let result = text;
  25. // Replace patterns like ./01-general.md or ./filename.md
  26. for (const [filename, title] of fileToTitleMap.entries()) {
  27. const patterns = [new RegExp(`\\./${filename}`, 'g'), new RegExp(`\\b${filename}\\b`, 'g')];
  28. patterns.forEach(pattern => {
  29. result = result.replace(pattern, `the "${title}" section below`);
  30. });
  31. }
  32. return result;
  33. }
  34. /**
  35. * Counts the number of lines in text
  36. */
  37. function countLines(text: string): number {
  38. return text.split('\n').length;
  39. }
  40. /**
  41. * Generates a migration prompt by concatenating all markdown files from the
  42. * vendure-dashboard-migration skill directory and inserting it into the
  43. * migration index.md documentation file.
  44. */
  45. function generateMigrationPrompt() {
  46. const skillsDir = join(__dirname, '../../.claude/skills/vendure-dashboard-migration');
  47. const docsFile = join(__dirname, '../docs/guides/extending-the-dashboard/migration/index.md');
  48. // Read the SKILL.md to extract instructions
  49. const skillContent = readFileSync(join(skillsDir, 'SKILL.md'), 'utf-8');
  50. const instructions = extractInstructions(skillContent);
  51. // Files to concatenate in order
  52. const files = [
  53. '01-general.md',
  54. '01a-common-tasks.md',
  55. '01b-tsconfig-setup.md',
  56. '02-forms.md',
  57. '03-custom-field-inputs.md',
  58. '04-list-pages.md',
  59. '05-detail-pages.md',
  60. '06-adding-nav-menu-items.md',
  61. '07-action-bar-items.md',
  62. '08-custom-detail-components.md',
  63. '09-page-tabs.md',
  64. '10-widgets.md',
  65. ];
  66. // Build a map of filenames to their section titles
  67. const fileToTitleMap = new Map<string, string>();
  68. const fileLineCounts: { file: string; lines: number }[] = [];
  69. let totalInputLines = 0;
  70. for (const file of files) {
  71. const filePath = join(skillsDir, file);
  72. try {
  73. const content = readFileSync(filePath, 'utf-8');
  74. const title = extractTitle(content);
  75. if (title) {
  76. fileToTitleMap.set(file, title);
  77. }
  78. } catch (error) {
  79. console.error(`Warning: Could not read ${file}:`, error);
  80. }
  81. }
  82. // Start with instructions if found, and replace file references
  83. const sections: string[] = [];
  84. let instructionsLines = 0;
  85. if (instructions) {
  86. const updatedInstructions = replaceFileReferences(instructions, fileToTitleMap);
  87. sections.push('## Instructions\n\n' + updatedInstructions);
  88. instructionsLines = countLines(updatedInstructions);
  89. totalInputLines += instructionsLines;
  90. }
  91. // Read and add all content files
  92. for (const file of files) {
  93. const filePath = join(skillsDir, file);
  94. try {
  95. const content = readFileSync(filePath, 'utf-8');
  96. const lineCount = countLines(content);
  97. fileLineCounts.push({ file, lines: lineCount });
  98. totalInputLines += lineCount;
  99. sections.push(content.trim());
  100. } catch (error) {
  101. console.error(`Warning: Could not read ${file}:`, error);
  102. }
  103. }
  104. // Join sections with double newline
  105. const prompt = sections.join('\n\n');
  106. const outputLines = countLines(prompt);
  107. // Read the current index.md
  108. const indexContent = readFileSync(docsFile, 'utf-8');
  109. // Find the code block after the HTML comment marker
  110. // Use greedy match to capture all content until the LAST closing backticks
  111. // This is important because the content itself contains code blocks with backticks
  112. const promptSectionRegex = /(<!-- Note: the following code block[\s\S]*?-->\n)(````md\n)([\s\S]*)(````)/;
  113. const match = indexContent.match(promptSectionRegex);
  114. if (!match) {
  115. throw new Error('Could not find the HTML comment marker and code block in index.md');
  116. }
  117. // Replace the content inside the code block, preserving the backtick style
  118. const updatedContent = indexContent.replace(promptSectionRegex, `$1$2${prompt}\n$4`);
  119. // Write back to the file
  120. writeFileSync(docsFile, updatedContent, 'utf-8');
  121. // Print success message and statistics
  122. console.log('✓ Migration prompt successfully generated and inserted into index.md');
  123. console.log(`\n📊 Line Count Report:`);
  124. console.log(` ─────────────────────────────────────────────────────`);
  125. if (instructionsLines > 0) {
  126. console.log(` Instructions (SKILL.md): ${instructionsLines.toString().padStart(6)} lines`);
  127. }
  128. fileLineCounts.forEach(({ file, lines }) => {
  129. console.log(` ${file.padEnd(30)} ${lines.toString().padStart(6)} lines`);
  130. });
  131. console.log(` ─────────────────────────────────────────────────────`);
  132. console.log(` Total input lines: ${totalInputLines.toString().padStart(6)} lines`);
  133. console.log(` Output lines (concatenated): ${outputLines.toString().padStart(6)} lines`);
  134. // Calculate the difference (should account for section separators)
  135. const separatorLines = sections.length - 1; // Number of "\n\n" separators
  136. const expectedOutputLines = totalInputLines + separatorLines;
  137. const lineDifference = outputLines - expectedOutputLines;
  138. console.log(
  139. ` Expected (with ${separatorLines} separators): ${expectedOutputLines.toString().padStart(6)} lines`,
  140. );
  141. console.log(
  142. ` Difference: ${lineDifference >= 0 ? '+' : ''}${lineDifference} lines`,
  143. );
  144. console.log(`\n Total files concatenated: ${files.length}`);
  145. console.log(` Total characters: ${prompt.length.toLocaleString()}`);
  146. }
  147. // Run the script
  148. generateMigrationPrompt();