mirror of
https://github.com/jparkerweb/plan2code.git
synced 2026-07-21 18:33:22 -07:00
252 lines
7.2 KiB
JavaScript
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());
|