Files
plan2code/sync-prompts.js
T
2025-12-04 17:34:22 -08:00

252 lines
7.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Plan2Code Prompt Sync Utility
*
* Syncs the source prompts (plan2code-*.md) to all destination formats
* with appropriate file names and YAML headers for each tool/IDE.
*
* Usage: node sync-prompts.js [--dry-run]
*/
const fs = require('fs');
const path = require('path');
// Configuration for source prompts
const SOURCE_PROMPTS = [
{
source: 'plan2code-1--plan.md',
stepNumber: 1,
name: 'plan',
displayName: 'Planning Mode',
description: 'Requirements analysis and architecture design'
},
{
source: 'plan2code-2--document.md',
stepNumber: 2,
name: 'document',
displayName: 'Documentation Mode',
description: 'Transform planning output into structured implementation docs'
},
{
source: 'plan2code-3--implement.md',
stepNumber: 3,
name: 'implement',
displayName: 'Implementation Mode',
description: 'Execute implementation phase by phase'
},
{
source: 'plan2code-4--finalize.md',
stepNumber: 4,
name: 'finalize',
displayName: 'Finalization Mode',
description: 'Validate, summarize, and archive completed work'
}
];
// Destination configurations
const DESTINATIONS = [
{
name: 'Claude Code Commands',
dir: '.claude/commands',
filePattern: (prompt) => `plan2code-${prompt.stepNumber}--${prompt.name}.md`,
header: null // No YAML header
},
{
name: 'Cursor Rules',
dir: '.cursor/rules',
filePattern: (prompt) => `plan2code-${prompt.stepNumber}--${prompt.name}.mdc`,
header: (prompt) => [
'---',
`description: "Plan2Code Step ${prompt.stepNumber}: ${prompt.displayName} - ${prompt.description}"`,
'alwaysApply: false',
'---'
].join('\n')
},
{
name: 'GitHub Copilot Prompts',
dir: '.github/prompts',
filePattern: (prompt) => `plan2code-${prompt.stepNumber}--${prompt.name}.prompt.md`,
header: (prompt) => [
'---',
'mode: agent',
`description: "Plan2Code Step ${prompt.stepNumber}: ${prompt.displayName} - ${prompt.description}"`,
'---'
].join('\n')
},
{
name: 'GitHub Copilot Agents',
dir: '.github/agents',
filePattern: (prompt) => `plan2code-${prompt.stepNumber}--${prompt.name}.agent.md`,
header: (prompt) => [
'---',
`description: "Plan2Code Step ${prompt.stepNumber}: ${prompt.displayName} - ${prompt.description}"`,
'---'
].join('\n')
},
{
name: 'Continue Prompts',
dir: '.continue/prompts',
filePattern: (prompt) => `plan2code-${prompt.stepNumber}--${prompt.name}.prompt.md`,
header: (prompt) => [
'---',
`name: ${prompt.name}`,
`description: "Plan2Code Step ${prompt.stepNumber}: ${prompt.displayName} - ${prompt.description}"`,
'---'
].join('\n')
},
{
name: 'Windsurf Workflows',
dir: '.windsurf/workflows',
filePattern: (prompt) => `plan2code-${prompt.stepNumber}--${prompt.name}.md`,
header: (prompt) => [
'---',
`description: "Plan2Code Step ${prompt.stepNumber}: ${prompt.displayName} - ${prompt.description}"`,
'---'
].join('\n')
},
{
name: 'Agent Workflows',
dir: '.agent/workflows',
filePattern: (prompt) => `plan2code-${prompt.stepNumber}--${prompt.name}.md`,
header: (prompt) => [
'---',
`description: "Plan2Code Step ${prompt.stepNumber}: ${prompt.displayName} - ${prompt.description}"`,
'---'
].join('\n')
}
];
// Parse command line arguments
const args = process.argv.slice(2);
const dryRun = args.includes('--dry-run');
const verbose = args.includes('--verbose') || args.includes('-v');
if (args.includes('--help') || args.includes('-h')) {
console.log(`
Plan2Code Prompt Sync Utility
Usage: node sync-prompts.js [options]
Options:
--dry-run Show what would be changed without making changes
--verbose Show detailed output
--help Show this help message
This script syncs the source prompts from the root directory to all
destination folders with appropriate formatting for each tool/IDE.
`);
process.exit(0);
}
// Main sync function
function syncPrompts() {
const rootDir = __dirname;
let totalUpdated = 0;
let totalSkipped = 0;
let totalErrors = 0;
console.log('Plan2Code Prompt Sync Utility');
console.log('=============================');
if (dryRun) {
console.log('(DRY RUN - no files will be modified)\n');
}
console.log('');
// Process each source prompt
for (const prompt of SOURCE_PROMPTS) {
const sourcePath = path.join(rootDir, prompt.source);
// Read source content
let sourceContent;
try {
sourceContent = fs.readFileSync(sourcePath, 'utf8');
} catch (err) {
console.error(`ERROR: Could not read source file: ${prompt.source}`);
console.error(` ${err.message}`);
totalErrors++;
continue;
}
console.log(`Source: ${prompt.source}`);
// Process each destination
for (const dest of DESTINATIONS) {
const destDir = path.join(rootDir, dest.dir);
const destFile = dest.filePattern(prompt);
const destPath = path.join(destDir, destFile);
// Build the output content
let outputContent;
if (dest.header) {
const header = dest.header(prompt);
outputContent = header + '\n\n' + sourceContent;
} else {
outputContent = sourceContent;
}
// Ensure destination directory exists
if (!dryRun && !fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
if (verbose) {
console.log(` Created directory: ${dest.dir}`);
}
}
// Check if file needs updating
let needsUpdate = true;
let existingContent = null;
if (fs.existsSync(destPath)) {
try {
existingContent = fs.readFileSync(destPath, 'utf8');
needsUpdate = existingContent !== outputContent;
} catch (err) {
// File exists but can't be read, will try to write
}
}
if (needsUpdate) {
if (!dryRun) {
try {
fs.writeFileSync(destPath, outputContent, 'utf8');
console.log(` ✓ Updated: ${dest.dir}/${destFile}`);
} catch (err) {
console.error(` ✗ ERROR writing: ${dest.dir}/${destFile}`);
console.error(` ${err.message}`);
totalErrors++;
continue;
}
} else {
console.log(` → Would update: ${dest.dir}/${destFile}`);
}
totalUpdated++;
} else {
if (verbose) {
console.log(` - Unchanged: ${dest.dir}/${destFile}`);
}
totalSkipped++;
}
}
console.log('');
}
// Summary
console.log('=============================');
console.log('Summary:');
console.log(` Updated: ${totalUpdated} files`);
console.log(` Unchanged: ${totalSkipped} files`);
if (totalErrors > 0) {
console.log(` Errors: ${totalErrors}`);
}
if (dryRun && totalUpdated > 0) {
console.log('\nRun without --dry-run to apply changes.');
}
return totalErrors === 0 ? 0 : 1;
}
// Run the sync
process.exit(syncPrompts());