Skip to content

Commit 2997045

Browse files
committed
✨ Quick Start!
1 parent feadb1e commit 2997045

File tree

2 files changed

+262
-1
lines changed

2 files changed

+262
-1
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
{
3737
"command": "ralph-runner.showMenu",
3838
"title": "RALPH: Show Menu"
39+
},
40+
{
41+
"command": "ralph-runner.quickStart",
42+
"title": "RALPH: Quick Start"
3943
}
4044
],
4145
"configuration": {

src/extension.ts

Lines changed: 258 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ export function activate(context: vscode.ExtensionContext) {
129129
vscode.commands.registerCommand('ralph-runner.openSettings', () => {
130130
vscode.commands.executeCommand('workbench.action.openSettings', 'ralph-runner');
131131
}),
132-
vscode.commands.registerCommand('ralph-runner.showMenu', () => showCommandMenu())
132+
vscode.commands.registerCommand('ralph-runner.showMenu', () => showCommandMenu()),
133+
vscode.commands.registerCommand('ralph-runner.quickStart', () => quickStart())
133134
);
134135

135136
log('RALPH Runner extension activated.');
@@ -809,6 +810,7 @@ function updateStatusBar(state: 'idle' | 'running'): void {
809810

810811
async function showCommandMenu(): Promise<void> {
811812
const items: vscode.QuickPickItem[] = [
813+
{ label: '$(zap) Quick Start', description: 'Set up migration plan & state files (or generate them via Copilot)' },
812814
{ label: '$(play) Start Migration', description: 'Begin or resume the autonomous migration loop' },
813815
{ label: '$(debug-stop) Stop Migration', description: 'Cancel the current migration run' },
814816
{ label: '$(info) Show Status', description: 'Display migration progress summary' },
@@ -823,6 +825,7 @@ async function showCommandMenu(): Promise<void> {
823825
if (!selected) { return; }
824826

825827
const commandMap: Record<string, string> = {
828+
'$(zap) Quick Start': 'ralph-runner.quickStart',
826829
'$(play) Start Migration': 'ralph-runner.start',
827830
'$(debug-stop) Stop Migration': 'ralph-runner.stop',
828831
'$(info) Show Status': 'ralph-runner.status',
@@ -835,3 +838,257 @@ async function showCommandMenu(): Promise<void> {
835838
vscode.commands.executeCommand(cmd);
836839
}
837840
}
841+
842+
// ── Quick Start ─────────────────────────────────────────────────────────────
843+
// Guides the user through setting up MIGRATION_PLAN.md and MIGRATION_STATE.md.
844+
// 1. Checks if the files already exist in the workspace root.
845+
// 2. If missing, asks the user to provide paths to existing files.
846+
// 3. If the user doesn't have them, asks what they want to accomplish and
847+
// uses Copilot to generate both files in the expected format.
848+
849+
async function quickStart(): Promise<void> {
850+
const workspaceRoot = getWorkspaceRoot();
851+
if (!workspaceRoot) {
852+
vscode.window.showErrorMessage('No workspace folder open.');
853+
return;
854+
}
855+
856+
outputChannel.show(true);
857+
log('═══════════════════════════════════════════════════');
858+
log('RALPH Quick Start');
859+
log('═══════════════════════════════════════════════════');
860+
861+
const planPath = path.join(workspaceRoot, 'MIGRATION_PLAN.md');
862+
const statePath = path.join(workspaceRoot, 'MIGRATION_STATE.md');
863+
864+
const planExists = fs.existsSync(planPath);
865+
const stateExists = fs.existsSync(statePath);
866+
867+
// ── Case 1: Both files already exist ────────────────────────────────────
868+
if (planExists && stateExists) {
869+
log('Both MIGRATION_PLAN.md and MIGRATION_STATE.md already exist.');
870+
const action = await vscode.window.showInformationMessage(
871+
'RALPH: MIGRATION_PLAN.md and MIGRATION_STATE.md already exist in the workspace root.',
872+
'Start Migration', 'Open Plan', 'Open State'
873+
);
874+
if (action === 'Start Migration') {
875+
vscode.commands.executeCommand('ralph-runner.start');
876+
} else if (action === 'Open Plan') {
877+
const doc = await vscode.workspace.openTextDocument(planPath);
878+
vscode.window.showTextDocument(doc);
879+
} else if (action === 'Open State') {
880+
const doc = await vscode.workspace.openTextDocument(statePath);
881+
vscode.window.showTextDocument(doc);
882+
}
883+
return;
884+
}
885+
886+
// ── Case 2: One or both files missing — ask user how to proceed ─────────
887+
const missingFiles: string[] = [];
888+
if (!planExists) { missingFiles.push('MIGRATION_PLAN.md'); }
889+
if (!stateExists) { missingFiles.push('MIGRATION_STATE.md'); }
890+
891+
log(`Missing: ${missingFiles.join(', ')}`);
892+
893+
const choice = await vscode.window.showQuickPick(
894+
[
895+
{
896+
label: '$(file-directory) I have these files — let me provide the path',
897+
description: 'Browse for existing MIGRATION_PLAN.md and MIGRATION_STATE.md files',
898+
value: 'provide'
899+
},
900+
{
901+
label: '$(sparkle) I don\'t have them — generate via Copilot',
902+
description: 'Describe your migration goal and let Copilot create both files',
903+
value: 'generate'
904+
}
905+
],
906+
{ placeHolder: `${missingFiles.join(' and ')} not found in workspace root. How would you like to proceed?` }
907+
);
908+
909+
if (!choice) { return; }
910+
911+
if (choice.value === 'provide') {
912+
await quickStartProvideFiles(planPath, statePath, planExists, stateExists);
913+
} else {
914+
await quickStartGenerate(planPath, statePath, workspaceRoot);
915+
}
916+
}
917+
918+
/**
919+
* Let the user browse for existing MIGRATION_PLAN.md / MIGRATION_STATE.md files
920+
* and copy them into the workspace root.
921+
*/
922+
async function quickStartProvideFiles(
923+
planPath: string, statePath: string,
924+
planExists: boolean, stateExists: boolean
925+
): Promise<void> {
926+
if (!planExists) {
927+
const uris = await vscode.window.showOpenDialog({
928+
title: 'Select your MIGRATION_PLAN.md file',
929+
canSelectMany: false,
930+
canSelectFolders: false,
931+
filters: { 'Markdown': ['md'], 'All Files': ['*'] },
932+
openLabel: 'Select MIGRATION_PLAN.md'
933+
});
934+
if (!uris || uris.length === 0) {
935+
vscode.window.showWarningMessage('RALPH Quick Start cancelled — no MIGRATION_PLAN.md selected.');
936+
return;
937+
}
938+
const srcPath = uris[0].fsPath;
939+
fs.copyFileSync(srcPath, planPath);
940+
log(`Copied MIGRATION_PLAN.md from ${srcPath}`);
941+
}
942+
943+
if (!stateExists) {
944+
const uris = await vscode.window.showOpenDialog({
945+
title: 'Select your MIGRATION_STATE.md file',
946+
canSelectMany: false,
947+
canSelectFolders: false,
948+
filters: { 'Markdown': ['md'], 'All Files': ['*'] },
949+
openLabel: 'Select MIGRATION_STATE.md'
950+
});
951+
if (!uris || uris.length === 0) {
952+
vscode.window.showWarningMessage('RALPH Quick Start cancelled — no MIGRATION_STATE.md selected.');
953+
return;
954+
}
955+
const srcPath = uris[0].fsPath;
956+
fs.copyFileSync(srcPath, statePath);
957+
log(`Copied MIGRATION_STATE.md from ${srcPath}`);
958+
}
959+
960+
vscode.window.showInformationMessage('RALPH: Migration files are ready! You can now run "RALPH: Start Migration".');
961+
log('Quick Start complete — files placed in workspace root.');
962+
}
963+
964+
/**
965+
* Ask the user what they want to accomplish, then send a Copilot prompt that
966+
* generates both MIGRATION_PLAN.md and MIGRATION_STATE.md in the expected
967+
* formats used by the RALPH Runner extension.
968+
*/
969+
async function quickStartGenerate(
970+
planPath: string, statePath: string, workspaceRoot: string
971+
): Promise<void> {
972+
const userGoal = await vscode.window.showInputBox({
973+
title: 'RALPH Quick Start — Describe your goal',
974+
prompt: 'What are you trying to accomplish? (e.g. "Migrate a Java EE 8 app to Spring Boot 3", "Convert a jQuery front-end to React")',
975+
placeHolder: 'Describe the migration or transformation you want to perform…',
976+
ignoreFocusOut: true
977+
});
978+
979+
if (!userGoal || userGoal.trim().length === 0) {
980+
vscode.window.showWarningMessage('RALPH Quick Start cancelled — no goal provided.');
981+
return;
982+
}
983+
984+
log(`User goal: ${userGoal}`);
985+
log('Sending generation prompt to Copilot…');
986+
987+
const prompt = buildQuickStartPrompt(userGoal, workspaceRoot);
988+
989+
try {
990+
await vscode.commands.executeCommand('workbench.action.chat.open', {
991+
query: prompt,
992+
isPartialQuery: false
993+
});
994+
} catch {
995+
try {
996+
await vscode.commands.executeCommand('workbench.panel.chat.view.copilot.focus');
997+
await sleep(1000);
998+
await vscode.commands.executeCommand('workbench.action.chat.open', prompt);
999+
} catch {
1000+
log('WARNING: Could not programmatically send to Copilot. Copying to clipboard.');
1001+
await vscode.env.clipboard.writeText(prompt);
1002+
await vscode.commands.executeCommand('workbench.action.chat.open');
1003+
vscode.window.showInformationMessage('RALPH: Prompt copied to clipboard — paste it into Copilot Chat.');
1004+
}
1005+
}
1006+
1007+
vscode.window.showInformationMessage(
1008+
'RALPH: Copilot is generating your migration files. Once they appear in the workspace root, run "RALPH: Start Migration".'
1009+
);
1010+
log('Quick Start prompt sent to Copilot. Waiting for file generation…');
1011+
}
1012+
1013+
/**
1014+
* Builds the Copilot prompt that instructs it to generate MIGRATION_PLAN.md
1015+
* and MIGRATION_STATE.md in the exact formats the RALPH Runner expects.
1016+
*/
1017+
function buildQuickStartPrompt(userGoal: string, workspaceRoot: string): string {
1018+
return [
1019+
`The user wants to accomplish the following goal:`,
1020+
``,
1021+
`> ${userGoal}`,
1022+
``,
1023+
`Workspace root: ${workspaceRoot}`,
1024+
``,
1025+
`Please analyze the workspace and generate TWO files in the workspace root:`,
1026+
``,
1027+
`────────────────────────────────────────────────────`,
1028+
`FILE 1: MIGRATION_PLAN.md`,
1029+
`────────────────────────────────────────────────────`,
1030+
``,
1031+
`This file must contain a \`\`\`json code block with the following structure:`,
1032+
``,
1033+
'```',
1034+
`{`,
1035+
` "steps": [`,
1036+
` {`,
1037+
` "id": 1,`,
1038+
` "phase": "Phase name (e.g. Setup, Analysis, Migration, Testing)",`,
1039+
` "action": "run_terminal | create_file | copilot_task",`,
1040+
` "command": "(only for run_terminal) the shell command to run",`,
1041+
` "path": "(only for create_file) relative path of the file to create",`,
1042+
` "instruction": "(only for copilot_task) detailed instruction for Copilot",`,
1043+
` "description": "Human-readable description of what this step does"`,
1044+
` }`,
1045+
` ]`,
1046+
`}`,
1047+
'```',
1048+
``,
1049+
`Action types:`,
1050+
`- "run_terminal": executes a shell command (requires "command" field)`,
1051+
`- "create_file": creates a file at the given path (requires "path" field)`,
1052+
`- "copilot_task": a general Copilot coding task (requires "instruction" field)`,
1053+
``,
1054+
`The plan should have a logical sequence of steps organized into phases.`,
1055+
`Each step should be granular enough to be independently executable and verifiable.`,
1056+
`Number steps sequentially starting from 1.`,
1057+
``,
1058+
`────────────────────────────────────────────────────`,
1059+
`FILE 2: MIGRATION_STATE.md`,
1060+
`────────────────────────────────────────────────────`,
1061+
``,
1062+
`This file tracks progress. It MUST contain:`,
1063+
``,
1064+
`1. A Quick Status section with this exact table format:`,
1065+
``,
1066+
`| Metric | Count |`,
1067+
`|--------|-------|`,
1068+
`| Total Steps | <N> |`,
1069+
`| Completed | 0 |`,
1070+
`| In Progress | 0 |`,
1071+
`| Failed | 0 |`,
1072+
`| Pending | <N> |`,
1073+
``,
1074+
`**Current Phase:** Step 1`,
1075+
`**Last Completed Step:** —`,
1076+
``,
1077+
`2. A detailed step tracking table with this exact format:`,
1078+
``,
1079+
`| Step | Phase | Action | Status | Timestamp | Notes |`,
1080+
`|------|-------|--------|--------|-----------|-------|`,
1081+
`| 1 | Phase name | \`action\` — description | \`pending\` | | |`,
1082+
``,
1083+
`One row per step matching the plan. All steps should start as \`pending\`.`,
1084+
``,
1085+
`────────────────────────────────────────────────────`,
1086+
``,
1087+
`IMPORTANT:`,
1088+
`- Create BOTH files at the workspace root: ${workspaceRoot}`,
1089+
`- The JSON in MIGRATION_PLAN.md must be inside a \`\`\`json fenced code block`,
1090+
`- The state table rows must follow the exact pipe-delimited format shown above`,
1091+
`- Be thorough: include all necessary steps for the user's goal`,
1092+
`- Actually create the files — do not just show their content`,
1093+
].join('\n');
1094+
}

0 commit comments

Comments
 (0)