From ea7122413fa69b9fee5bca71cc146dc09afe1cc9 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Mon, 24 Nov 2025 09:20:52 -0800 Subject: [PATCH 01/63] [PB-13614] Plan for SMC Upgrader AI Assist Mode --- AI_ASSIST_MODE_DESIGN.md | 586 +++++++++++++++++++++++++++++++++++ IMPLEMENTATION_PLAN.md | 294 ++++++++++++++++++ SAMPLE_ai-assist-config.json | 98 ++++++ SAMPLE_smc-upgrader-plan.md | 207 +++++++++++++ 4 files changed, 1185 insertions(+) create mode 100644 AI_ASSIST_MODE_DESIGN.md create mode 100644 IMPLEMENTATION_PLAN.md create mode 100644 SAMPLE_ai-assist-config.json create mode 100644 SAMPLE_smc-upgrader-plan.md diff --git a/AI_ASSIST_MODE_DESIGN.md b/AI_ASSIST_MODE_DESIGN.md new file mode 100644 index 0000000..cc84485 --- /dev/null +++ b/AI_ASSIST_MODE_DESIGN.md @@ -0,0 +1,586 @@ +# AI Assist Mode Design Document + +## Overview + +This document outlines the design for an AI-assisted upgrade mode that helps customers complete the Self-Managed Commerce upgrade process with the assistance of Claude Code CLI. + +## Goals + +- Automate the generation of a structured upgrade plan +- Guide customers through each step of the upgrade process +- Integrate Claude Code CLI for AI-assisted problem resolution +- Support multi-version upgrades with sequential intermediate steps +- Allow customers to customize the upgrade plan + +## User Experience + +### Starting an AI-Assisted Upgrade + +```bash +$ smc-upgrader --ai:start 8.7.x +Detected current version: 8.5.x +Upgrade path: 8.5.x -> 8.6.x -> 8.7.x +Generated upgrade plan: smc-upgrader-plan.md + +You can review and customize the plan if needed. + +IMPORTANT: This upgrade process requires Claude Code CLI. +Please ensure Claude Code is installed and you have a Claude Pro account. +For installation instructions, visit: https://www.claude.com/product/claude-code + +To continue with the upgrade, run: + smc-upgrader --ai:continue +``` + +### Starting with Existing Plan + +If a plan already exists, the tool will prompt for confirmation: + +```bash +$ smc-upgrader --ai:start 8.7.x +WARNING: An upgrade plan already exists at: smc-upgrader-plan.md + +This plan may contain customizations or progress from a previous upgrade. +Do you want to overwrite it? [y/N]: n + +Keeping existing plan. To continue with the existing upgrade, run: + smc-upgrader --ai:continue + +To use a different plan file name, move or rename the existing plan first. +``` + +If the customer chooses to overwrite: + +```bash +$ smc-upgrader --ai:start 8.7.x +WARNING: An upgrade plan already exists at: smc-upgrader-plan.md + +This plan may contain customizations or progress from a previous upgrade. +Do you want to overwrite it? [y/N]: y + +Detected current version: 8.5.x +Upgrade path: 8.5.x -> 8.6.x -> 8.7.x +Generated upgrade plan: smc-upgrader-plan.md + +You can review and customize the plan if needed. + +IMPORTANT: This upgrade process requires Claude Code CLI. +Please ensure Claude Code is installed and you have a Claude Pro account. +For installation instructions, visit: https://www.claude.com/product/claude-code + +To continue with the upgrade, run: + smc-upgrader --ai:continue +``` + +### Continuing the Upgrade + +```bash +$ smc-upgrader --ai:continue +Reading plan from: smc-upgrader-plan.md + +Next step: Git merge from 8.5.x to 8.6.x + Task: Upgrade source from 8.5.x to 8.6.x + Tool: smc-upgrader + Status: incomplete + +What would you like to do? + [E] Execute this step + [C] Check if this step is complete + [M] Mark this step as complete + [X] Exit + +Your choice: E + +Executing merge from 8.5.x to 8.6.x... +Merge completed successfully. +Step marked as complete. + +Exiting. Run 'smc-upgrader --ai:continue' to continue with the next step. +``` + +Note: Steps using the smc-upgrader tool are automatically marked complete after execution since the tool knows whether they succeeded. The customer can now move to the next step: + +```bash +$ smc-upgrader --ai:continue +Reading plan from: smc-upgrader-plan.md + +Next step: Resolve 8.6.x merge conflicts + Task: Resolve remaining Git merge conflicts + Tool: claude + Validation command: git diff --check + Status: incomplete + +What would you like to do? + [E] Execute this step + [C] Check if this step is complete + [M] Mark this step as complete + [X] Exit + +Your choice: E + +Launching Claude Code with upgrade context... +(Claude Code session starts) + +Note: Steps using the claude tool are NOT automatically marked complete after execution. +The customer must validate the results and explicitly mark the step complete. +``` + +After the customer finishes working with Claude Code and exits: + +```bash +$ smc-upgrader --ai:continue +Reading plan from: smc-upgrader-plan.md + +Next step: Resolve 8.6.x merge conflicts + Task: Resolve remaining Git merge conflicts + Tool: claude + Validation command: git diff --check + Status: incomplete + +What would you like to do? + [E] Execute this step + [C] Check if this step is complete + [M] Mark this step as complete + [X] Exit + +Your choice: C + +Running validation command: git diff --check +Validation command exited with code: 0 + +Validation passed! Step marked as complete. + +Exiting. Run 'smc-upgrader --ai:continue' to continue with the next step. +``` + +### Validation Failure Example + +If the validation command fails, the step remains incomplete: + +```bash +$ smc-upgrader --ai:continue +Reading plan from: smc-upgrader-plan.md + +Next step: Fix compilation failures + Task: Resolve all compilation issues + Tool: claude + Validation command: mvn clean install -DskipAllTests + Status: incomplete + +What would you like to do? + [E] Execute this step + [C] Check if this step is complete + [M] Mark this step as complete + [X] Exit + +Your choice: C + +Running validation command: mvn clean install -DskipAllTests +Validation command exited with code: 1 + +Validation failed. Step remains incomplete. +Review the output above and run 'smc-upgrader --ai:continue' to try again. +``` + +### Manual Completion + +Customers can also mark steps complete manually without running validation: + +```bash +$ smc-upgrader --ai:continue +Reading plan from: smc-upgrader-plan.md + +Next step: Resolve 8.6.x merge conflicts + Task: Resolve remaining Git merge conflicts + Tool: claude + Validation command: git diff --check + Status: incomplete + +What would you like to do? + [E] Execute this step + [C] Check if this step is complete + [M] Mark this step as complete + [X] Exit + +Your choice: M + +Step marked as complete. + +Exiting. Run 'smc-upgrader --ai:continue' to continue with the next step. +``` + +## Step Completion Behavior + +The tool handles step completion differently based on the tool type: + +### smc-upgrader Tool Steps +- **Automatically marked complete** after successful execution +- These are deterministic operations (git merges) where the tool knows if they succeeded +- Customer can immediately proceed to the next step +- Example: "Git merge from 8.5.x to 8.6.x" + +### claude Tool Steps +- **NOT automatically marked complete** after execution +- These involve AI assistance and require customer validation +- Customer must explicitly mark complete using one of these methods: + 1. **[C] Check validation**: Runs validation command; auto-marks complete if exit code is 0 + 2. **[M] Mark complete**: Manually marks complete without validation +- Examples: "Resolve merge conflicts", "Fix compilation failures" + +This distinction ensures that deterministic operations proceed efficiently while AI-assisted tasks receive proper human oversight. + +## Architecture + +### New Components + +#### 1. Configuration File: `ai-assist-config.json` + +Located in `src/main/resources/ai-assist-config.json` + +```json +{ + "versions": ["8.3.x", "8.4.x", "8.5.x", "8.6.x", "8.7.x", "8.8.x"], + "defaultSteps": [ + { + "title": "Git merge from {FROM_VERSION} to {TO_VERSION}", + "task": "Upgrade source from {FROM_VERSION} to {TO_VERSION}", + "tool": "smc-upgrader", + "status": "incomplete" + }, + { + "title": "Resolve {TO_VERSION} merge conflicts", + "task": "Resolve remaining Git merge conflicts", + "tool": "claude", + "validationCommand": "git diff --check", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." + }, + { + "title": "Fix compilation failures", + "task": "Resolve all compilation issues", + "tool": "claude", + "validationCommand": "mvn clean install -DskipAllTests", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix static analysis, unit test, and integration test failures", + "task": "Resolve all static analysis, unit test, and integration test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipSlowTests", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Local Database Reset Failures", + "task": "Resolve database reset failures", + "tool": "claude", + "validationCommand": "mvn clean install -Dreset-db -f extensions/database", + "status": "incomplete", + "prompt": "..." + }, + { + "title": "Fix Integration Server startup issues", + "task": "Fix Integration Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/integration/ext-integration-webapp", + "status": "incomplete", + "prompt": "..." + }, + { + "title": "Fix Batch Server startup issues", + "task": "Fix Batch Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/batch/ext-batch-webapp", + "status": "incomplete", + "prompt": "..." + }, + { + "title": "Fix Search Server startup issues", + "task": "Fix Search Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/search/ext-search-webapp", + "status": "incomplete", + "prompt": "..." + }, + { + "title": "Fix Cortex startup issues", + "task": "Fix Cortex startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp", + "status": "incomplete", + "prompt": "..." + }, + { + "title": "Fix Commerce Manager startup issues", + "task": "Fix Commerce Manager startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp", + "status": "incomplete", + "prompt": "..." + }, + { + "title": "Fix Cucumber and Selenium test failures", + "task": "Resolve all Cucumber and Selenium test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipTests=true -DskipITests=true", + "status": "incomplete", + "prompt": "..." + } + ] +} +``` + +**Note**: The complete configuration includes 11 steps per version upgrade: +1. Git merge (smc-upgrader tool) +2. Resolve merge conflicts (claude) +3. Fix compilation failures (claude) +4. Fix static analysis/unit/integration tests (claude) +5. Fix local database reset failures (claude) +6. Fix Integration Server startup issues (claude) +7. Fix Batch Server startup issues (claude) +8. Fix Search Server startup issues (claude) +9. Fix Cortex startup issues (claude) +10. Fix Commerce Manager startup issues (claude) +11. Fix Cucumber and Selenium test failures (claude) + +These steps cover the complete Self-Managed Commerce upgrade workflow from code merge through all service validations. See `SAMPLE_ai-assist-config.json` for the complete configuration with all prompts. + +#### 2. Java Classes + +**`AiPlanStep.java`** - Model class for a single plan step +```java +public class AiPlanStep { + private String title; + private String task; + private String tool; // "smc-upgrader" or "claude" + private String validationCommand; // optional + private String status; // "incomplete" or "complete" + private String prompt; // for claude steps + + // getters, setters, constructors +} +``` + +**`UpgradePath.java`** - Model for upgrade path configuration +```java +public class UpgradePath { + private List versions; + private List defaultSteps; + + // Methods to calculate intermediate versions + public List getIntermediateVersions(String from, String to); +} +``` + +**`AiPlanGenerator.java`** - Generates the upgrade plan +```java +public class AiPlanGenerator { + private final UpgradePath upgradePath; + private final String currentVersion; + private final String targetVersion; + + public void generatePlan(File outputFile); + private List calculateVersionSequence(); + private List expandStepsForVersions(List versions); + private String substituteVariables(String template, String from, String to); +} +``` + +**`PlanDocument.java`** - Parser and writer for the markdown plan +```java +public class PlanDocument { + private List steps; + + public static PlanDocument parse(File planFile); + public void write(File planFile); + public AiPlanStep getFirstIncompleteStep(); + public void updateStepStatus(int stepIndex, String status); +} +``` + +**`ClaudeCodeInvoker.java`** - Invokes Claude Code CLI +```java +public class ClaudeCodeInvoker { + public void invokeClaude(String prompt, File workingDirectory) throws IOException; + private File createTempPromptFile(String prompt); +} +``` + +**`AiPlanExecutor.java`** - Main execution controller for AI mode +```java +public class AiPlanExecutor { + private final PlanDocument planDocument; + private final UpgradeController upgradeController; + private final ClaudeCodeInvoker claudeInvoker; + + public void executePlan(); + private void showStepMenu(AiPlanStep step); + private void executeStep(AiPlanStep step); + private boolean checkStepCompletion(AiPlanStep step); +} +``` + +#### 3. Modified Classes + +**`SMCUpgraderCLI.java`** - Add new subcommands + +Add two new options: +```java +@CommandLine.Option(names = { "--ai:start" }, + description = "Start AI-assisted upgrade mode and generate upgrade plan") +private boolean aiStart; + +@CommandLine.Option(names = { "--ai:continue" }, + description = "Continue AI-assisted upgrade from saved plan") +private boolean aiContinue; +``` + +Modify the `call()` method to handle these new options. + +## Plan Document Format + +The generated `smc-upgrader-plan.md` follows this structure: + +```markdown +# SMC Upgrader - AI Assist Plan + +Upgrade from: 8.5.x +Upgrade to: 8.7.x +Generated: 2025-11-24 + +## Git merge from 8.5.x to 8.6.x + +Task: Upgrade source from 8.5.x to 8.6.x +Tool: smc-upgrader +Status: incomplete + +## Resolve 8.6.x merge conflicts + +Task: Resolve remaining Git merge conflicts +Tool: claude +Validation command: git diff --check +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Review the merge conflicts in the current folder and help to address them. "ours" represents the customer's custom code base, and "theirs" represents the new Self-Managed Commerce release code. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step. + +## Fix compilation failures + +Task: Resolve all compilation issues +Tool: claude +Validation command: mvn clean install -DskipAllTests +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +[... more steps ...] +``` + +### Parsing Rules + +- Each `## ` (H2) header starts a new step +- Metadata fields are parsed as `Key: Value` pairs +- The prompt for Claude steps is any text after the metadata and before the next step +- Status field can be edited by customers or updated by the tool + +## Implementation Phases + +### Phase 1: Foundation +1. Create `UpgradePath` and `AiPlanStep` model classes +2. Create `ai-assist-config.json` configuration file +3. Implement `AiPlanGenerator` to generate plan documents + +### Phase 2: Plan Management +4. Implement `PlanDocument` parser and writer +5. Add `--ai:start` command to CLI +6. Test plan generation for various version ranges + +### Phase 3: Execution +7. Implement `ClaudeCodeInvoker` +8. Implement `AiPlanExecutor` with interactive menu +9. Add `--ai:continue` command to CLI +10. Integration testing + +### Phase 4: Polish +11. Add error handling and user feedback +12. Add logging for AI assist operations +13. Update documentation +14. Add unit tests for new components + +## Error Handling + +- **Invalid target version**: Display error if version not in ai-assist-config.json +- **No upgrade path**: Display error if current version is newer than target +- **Existing plan file**: When running `--ai:start`, prompt for confirmation if plan exists; exit if user declines +- **Missing plan file**: When running `--ai:continue`, check if plan exists +- **Corrupted plan file**: Validate plan structure before execution +- **Claude not installed**: Detect if `claude` command is available +- **Validation command failures**: Report exit code but don't block progress + +## Testing Strategy + +### Unit Tests +- Test version sequence calculation +- Test markdown parsing and writing +- Test variable substitution in prompts +- Test step status updates + +### Integration Tests +- Test full plan generation +- Test plan execution (mock Claude invocation) +- Test multi-version upgrade paths + +### Manual Testing +- Test with real SMC codebase upgrade scenarios +- Verify Claude Code integration +- Test plan customization workflows + +## Future Enhancements + +1. **Resume capability**: Allow resuming from interrupted upgrades +2. **Progress tracking**: Show overall progress (e.g., "Step 5 of 12") +3. **Rollback support**: Ability to revert to previous step +4. **Custom step templates**: Allow customers to define reusable step templates +5. **Validation automation**: Optional auto-run of validation commands +6. **Plan branching**: Support alternative paths based on customization complexity +7. **AI advisor**: Pre-analyze codebase and suggest additional steps +8. **Integration with CI/CD**: Generate pipeline configurations + +## Dependencies + +### New Maven Dependencies +```xml + + + com.google.code.gson + gson + 2.10.1 + +``` + +### External Dependencies +- Claude Code CLI (must be installed by customer) +- Claude Pro account (required for Claude Code) + +## Configuration Management + +The `ai-assist-config.json` file should be: +- Packaged in the JAR resources +- Versioned with the tool +- Updated when new SMC versions are released +- Validated during build to ensure proper JSON structure + +## Documentation Updates + +1. Update README.md with AI assist mode instructions +2. Add examples of generated plans +3. Document plan customization options +4. Add troubleshooting section for Claude Code integration +5. Create video tutorial for AI-assisted upgrades + +## Success Metrics + +- Reduction in time to complete upgrades +- Reduction in manual conflict resolution errors +- Customer satisfaction scores +- Adoption rate of AI assist mode vs traditional mode diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..a589dff --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,294 @@ +# AI Assist Mode - Implementation Plan + +## Files to Create + +### 1. Configuration File +- **`src/main/resources/ai-assist-config.json`** + - Purpose: Defines valid upgrade versions and default step templates + - Format: JSON + - Contains: version list, default steps with variable placeholders + +### 2. Model Classes + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java`** + - Purpose: Data model for a single plan step + - Properties: title, task, tool, validationCommand, status, prompt + - Methods: getters, setters, validation + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java`** + - Purpose: Model for upgrade path configuration + - Properties: versions, defaultSteps + - Methods: + - `getIntermediateVersions(String from, String to)` + - `validateVersionPath(String from, String to)` + - Static factory method to load from JSON + +### 3. Core Implementation Classes + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java`** + - Purpose: Generate upgrade plan markdown file + - Dependencies: UpgradePath, UpgradeController + - Key methods: + - `generatePlan(String targetVersion, File outputFile)` - Generates plan, checks for existing file + - `promptForOverwrite(File planFile)` - Prompts user if plan exists, returns true to proceed + - `calculateVersionSequence(String from, String to)` + - `expandStepsForVersions(List versions)` + - `substituteVariables(String template, String from, String to)` + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/PlanDocument.java`** + - Purpose: Parse and manipulate the markdown plan file + - Key methods: + - `static PlanDocument parse(File planFile)` + - `write(File planFile)` + - `getFirstIncompleteStep()` + - `updateStepStatus(int stepIndex, String status)` + - `getStep(int index)` + - `getAllSteps()` + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java`** + - Purpose: Invoke Claude Code CLI with prompts + - Key methods: + - `invokeClaude(String prompt, File workingDirectory)` + - `createTempPromptFile(String prompt)` + - `isClaudeInstalled()` + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java`** + - Purpose: Interactive executor for the upgrade plan + - Dependencies: PlanDocument, UpgradeController, ClaudeCodeInvoker + - Behavior: Finds first incomplete step, shows menu, performs ONE action, then exits + - Key methods: + - `executePlan(File planFile)` - Main entry point; loads plan, shows menu, exits after action + - `showStepMenu(AiPlanStep step, int stepIndex)` - Displays step info and menu options + - `executeStep(AiPlanStep step, int stepIndex)` - Executes step; if tool=smc-upgrader, marks complete and exits; if tool=claude, exits without marking complete + - `executeSmcUpgraderStep(AiPlanStep step, int stepIndex)` - Runs internal merge/upgrade logic, marks step complete + - `executeClaudeStep(AiPlanStep step)` - Launches Claude Code CLI then exits (does NOT mark complete) + - `checkStepCompletion(AiPlanStep step, int stepIndex)` - Runs validation command; if exit code is 0, marks step complete and exits; otherwise exits without marking complete + - `markStepComplete(int stepIndex)` - Marks step complete in plan without validation, exits + - `promptUserChoice()` - Gets user menu selection + +### 4. Utility Classes + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java`** + - Purpose: Parse markdown into structured data + - Key methods: + - `parseSteps(String markdown)` + - `parseMetadata(String section)` + +- **`src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java`** + - Purpose: Write structured data to markdown + - Key methods: + - `generateMarkdown(List steps, String from, String to)` + +## Files to Modify + +### 1. CLI Entry Point +- **`src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java`** + - Changes: + - Add `@CommandLine.Option` for `--ai:start` + - Add `@CommandLine.Option` for `--ai:continue` + - Modify `call()` method to handle AI modes + - Add validation for mutually exclusive options + +### 2. Build Configuration +- **`pom.xml`** + - Changes: + - Add Gson dependency for JSON parsing + - Ensure resources directory is included in build + +## Test Files to Create + +### Unit Tests + +- **`src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java`** + - Test version sequence calculation + - Test invalid version ranges + - Test configuration loading + +- **`src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java`** + - Test plan generation for single version upgrade + - Test plan generation for multi-version upgrade + - Test variable substitution + - Test overwrite prompt when plan file exists + - Test exit behavior when user declines overwrite + +- **`src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java`** + - Test markdown parsing + - Test markdown writing + - Test status updates + - Test finding incomplete steps + +- **`src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java`** + - Test parsing various markdown formats + - Test metadata extraction + - Test error handling for malformed documents + +- **`src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java`** + - Test Claude detection + - Test temp file creation + - Test process invocation (mocked) + +- **`src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java`** + - Test smc-upgrader step execution automatically marks step complete and exits + - Test claude step execution does NOT mark step complete and exits + - Test manual mark complete works for any step type + - Test validation check with exit code 0 marks step complete and exits + - Test validation check with non-zero exit code keeps step incomplete and exits + - Test that only one action is performed per invocation + - Test finding first incomplete step + - Test all menu options work correctly + +### Integration Tests + +- **`src/test/java/com/elasticpath/tools/smcupgrader/ai/AiWorkflowIntegrationTest.java`** + - Test full workflow: generate plan -> execute step -> validate -> mark complete -> next step + - Test plan customization scenarios + - Test execution model: verify no automatic completion or progression + +### Test Resources + +- **`src/test/resources/ai-assist-config-test.json`** + - Test configuration with limited versions + +- **`src/test/resources/sample-plan.md`** + - Sample plan document for parsing tests + +## Implementation Order + +### Sprint 1: Foundation & Models (Week 1) +1. Create package structure (`ai` subpackage) +2. Create `AiPlanStep.java` model +3. Create `ai-assist-config.json` configuration +4. Create `UpgradePath.java` with JSON loading +5. Write unit tests for `UpgradePath` +6. Update `pom.xml` with Gson dependency + +**Deliverable**: Configuration can be loaded and version paths can be calculated + +### Sprint 2: Plan Generation (Week 2) +7. Create `MarkdownWriter.java` +8. Create `AiPlanGenerator.java` +9. Write unit tests for plan generation +10. Integrate with `SMCUpgraderCLI` for `--ai:start` option +11. Manual testing of plan generation + +**Deliverable**: `smc-upgrader --ai:start ` generates valid plan + +### Sprint 3: Plan Parsing & Execution Framework (Week 3) +12. Create `MarkdownParser.java` +13. Create `PlanDocument.java` +14. Write unit tests for parsing and document manipulation +15. Create `ClaudeCodeInvoker.java` +16. Write unit tests for Claude invocation + +**Deliverable**: Plans can be parsed and modified programmatically + +### Sprint 4: Interactive Executor (Week 4) +17. Create `AiPlanExecutor.java` with interactive menu +18. Implement step execution for smc-upgrader tool steps +19. Implement step execution for claude tool steps +20. Integrate with `SMCUpgraderCLI` for `--ai:continue` option +21. End-to-end integration testing + +**Deliverable**: `smc-upgrader --ai:continue` works end-to-end + +### Sprint 5: Polish & Documentation (Week 5) +22. Add comprehensive error handling +23. Improve user prompts and feedback +24. Add logging for all AI operations +25. Update README.md +26. Create user guide with examples +27. Performance testing +28. Bug fixes from testing + +**Deliverable**: Feature complete and documented + +## Technical Decisions + +### JSON Library: Gson +- Lightweight and well-suited for simple JSON parsing +- No additional annotation processing required +- Compatible with Java 8 + +### Markdown Format +- Human-readable and editable +- Simple parsing with regex/line-based approach +- Standard format customers are familiar with + +### Process Invocation +- Use `ProcessBuilder` for Claude Code invocation +- Inherit stdin/stdout/stderr for interactive experience +- Write prompts to temp files to avoid command-line length limits + +### Interactive Menu +- Use `System.console()` for input +- Fallback to `Scanner` if console not available +- Single-character commands for efficiency + +### Execution Model +- **One action per invocation**: Each run of `--ai:continue` performs exactly one action then exits +- **Tool-based completion behavior**: + - **smc-upgrader steps**: Automatically marked complete after successful execution (deterministic operations) + - **claude steps**: NOT automatically marked complete; require explicit customer validation +- **Manual validation for AI steps**: Customers must run `--ai:continue` again to validate or mark claude steps complete +- **Deliberate progression**: Tool never automatically moves to the next step +- **Rationale**: Deterministic operations (git merges) proceed efficiently while AI-assisted tasks receive proper human oversight + +### Error Handling Strategy +- Fail fast on invalid configuration +- Graceful degradation for missing Claude installation +- User-friendly error messages with actionable guidance + +## Dependencies Added + +```xml + + + com.google.code.gson + gson + 2.10.1 + +``` + +## Backward Compatibility + +- All existing CLI options continue to work unchanged +- AI assist mode is opt-in via new flags +- No breaking changes to existing APIs +- Plan file is optional and doesn't affect non-AI workflows + +## Risk Mitigation + +| Risk | Mitigation | +|------|------------| +| Claude Code not installed | Detect early and provide installation instructions | +| Invalid upgrade path | Validate against configuration before generating plan | +| Accidentally overwriting existing plan | Prompt for confirmation if plan exists, default to No | +| Corrupted plan file | Validate structure on load, provide helpful error messages | +| Long-running validation commands | Keep validation manual, let users decide when to run | +| Plan customization breaks parsing | Robust parser that handles variations | +| Version configuration becomes outdated | Document update process, validate in CI | + +## Success Criteria + +- [ ] Can generate plan for single-version upgrade +- [ ] Can generate plan for multi-version upgrade +- [ ] Prompts for confirmation when overwriting existing plan +- [ ] Exits gracefully when user declines overwrite +- [ ] Can parse and modify plan files +- [ ] Can execute smc-upgrader tool steps +- [ ] Can launch Claude Code for claude tool steps +- [ ] Can update step status in plan +- [ ] Plan can be customized by users +- [ ] All unit tests pass +- [ ] Integration tests pass +- [ ] Documentation is complete +- [ ] Manual end-to-end testing successful + +## Documentation Deliverables + +1. Design document (completed: `AI_ASSIST_MODE_DESIGN.md`) +2. Implementation plan (this document) +3. Updated README.md with AI assist examples +4. User guide with screenshots/examples +5. Javadoc for all new classes +6. Release notes entry diff --git a/SAMPLE_ai-assist-config.json b/SAMPLE_ai-assist-config.json new file mode 100644 index 0000000..0519bd5 --- /dev/null +++ b/SAMPLE_ai-assist-config.json @@ -0,0 +1,98 @@ +{ + "versions": [ + "8.3.x", + "8.4.x", + "8.5.x", + "8.6.x", + "8.7.x", + "8.8.x" + ], + "defaultSteps": [ + { + "title": "Git merge from {FROM_VERSION} to {TO_VERSION}", + "task": "Upgrade source from {FROM_VERSION} to {TO_VERSION}", + "tool": "smc-upgrader", + "status": "incomplete" + }, + { + "title": "Resolve {TO_VERSION} merge conflicts", + "task": "Resolve remaining Git merge conflicts", + "tool": "claude", + "validationCommand": "git diff --check", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." + }, + { + "title": "Fix compilation failures", + "task": "Resolve all compilation issues", + "tool": "claude", + "validationCommand": "mvn clean install -DskipAllTests", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix static analysis, unit test, and integration test failures", + "task": "Resolve all static analysis, unit test, and integration test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipSlowTests", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Local Database Reset Failures", + "task": "Resolve database reset failures", + "tool": "claude", + "validationCommand": "mvn clean install -Dreset-db -f extensions/database", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Integration Server startup issues", + "task": "Fix Integration Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/integration/ext-integration-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Batch Server startup issues", + "task": "Fix Batch Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/batch/ext-batch-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Search Server startup issues", + "task": "Fix Search Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/search/ext-search-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Cortex startup issues", + "task": "Fix Cortex startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Commerce Manager startup issues", + "task": "Fix Commerce Manager startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Cucumber and Selenium test failures", + "task": "Resolve all Cucumber and Selenium test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipTests=true -DskipITests=true", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + } + ] +} diff --git a/SAMPLE_smc-upgrader-plan.md b/SAMPLE_smc-upgrader-plan.md new file mode 100644 index 0000000..6bbd82f --- /dev/null +++ b/SAMPLE_smc-upgrader-plan.md @@ -0,0 +1,207 @@ +# SMC Upgrader - AI Assist Plan + +Upgrade from: 8.5.x +Upgrade to: 8.7.x +Generated: 2025-11-24 + +--- + +## Git merge from 8.5.x to 8.6.x + +Task: Upgrade source from 8.5.x to 8.6.x +Tool: smc-upgrader +Status: incomplete + +## Resolve 8.6.x merge conflicts + +Task: Resolve remaining Git merge conflicts +Tool: claude +Validation command: git diff --check +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Review the merge conflicts in the current folder and help to address them. "ours" represents the customer's custom code base, and "theirs" represents the new Self-Managed Commerce release code. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step. + +## Fix compilation failures + +Task: Resolve all compilation issues +Tool: claude +Validation command: mvn clean install -DskipAllTests +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix static analysis, unit test, and integration test failures + +Task: Resolve all static analysis, unit test, and integration test failures +Tool: claude +Validation command: mvn clean install -DskipSlowTests +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Local Database Reset Failures + +Task: Resolve database reset failures +Tool: claude +Validation command: mvn clean install -Dreset-db -f extensions/database +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Integration Server startup issues + +Task: Fix Integration Server startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/integration/ext-integration-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Batch Server startup issues + +Task: Fix Batch Server startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/batch/ext-batch-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Search Server startup issues + +Task: Fix Search Server startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/search/ext-search-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Cortex startup issues + +Task: Fix Cortex startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Commerce Manager startup issues + +Task: Fix Commerce Manager startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Cucumber and Selenium test failures + +Task: Resolve all Cucumber and Selenium test failures +Tool: claude +Validation command: mvn clean install -DskipTests=true -DskipITests=true +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.5.x to version 8.6.x. Run the validation command above and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.6.x/release-notes.html#upgrade-notes may be helpful. + +## Git merge from 8.6.x to 8.7.x + +Task: Upgrade source from 8.6.x to 8.7.x +Tool: smc-upgrader +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Task: Resolve remaining Git merge conflicts +Tool: claude +Validation command: git diff --check +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Review the merge conflicts in the current folder and help to address them. "ours" represents the customer's custom code base, and "theirs" represents the new Self-Managed Commerce release code. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step. + +## Fix compilation failures + +Task: Resolve all compilation issues +Tool: claude +Validation command: mvn clean install -DskipAllTests +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix static analysis, unit test, and integration test failures + +Task: Resolve all static analysis, unit test, and integration test failures +Tool: claude +Validation command: mvn clean install -DskipSlowTests +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Local Database Reset Failures + +Task: Resolve database reset failures +Tool: claude +Validation command: mvn clean install -Dreset-db -f extensions/database +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Integration Server startup issues + +Task: Fix Integration Server startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/integration/ext-integration-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Batch Server startup issues + +Task: Fix Batch Server startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/batch/ext-batch-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Search Server startup issues + +Task: Fix Search Server startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/search/ext-search-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Cortex startup issues + +Task: Fix Cortex startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Commerce Manager startup issues + +Task: Fix Commerce Manager startup issues +Tool: claude +Validation command: mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +## Fix Cucumber and Selenium test failures + +Task: Resolve all Cucumber and Selenium test failures +Tool: claude +Validation command: mvn clean install -DskipTests=true -DskipITests=true +Status: incomplete + +We are in the process of doing an upgrade of the Self-Managed Commerce code base from version 8.6.x to version 8.7.x. Run the validation command above and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/8.7.x/release-notes.html#upgrade-notes may be helpful. + +--- + +## Notes + +Customers can edit this plan to add custom steps or modify existing ones. Each step will be executed in sequence when running `smc-upgrader --ai:continue`. + +To mark a step as complete, change `Status: incomplete` to `Status: complete`. From 050319be59e9b77de1bd3b1ff1825c20554bb46f Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 25 Nov 2025 16:14:43 -0800 Subject: [PATCH 02/63] [PB-13614] Implemented ai:start --- pom.xml | 10 + .../tools/smcupgrader/SMCUpgraderCLI.java | 68 ++++- .../tools/smcupgrader/ai/AiPlanGenerator.java | 207 +++++++++++++++ .../tools/smcupgrader/ai/AiPlanStep.java | 198 +++++++++++++++ .../smcupgrader/ai/ClaudeCodeInvoker.java | 123 +++++++++ .../tools/smcupgrader/ai/MarkdownParser.java | 238 ++++++++++++++++++ .../tools/smcupgrader/ai/MarkdownWriter.java | 88 +++++++ .../tools/smcupgrader/ai/PlanDocument.java | 136 ++++++++++ .../tools/smcupgrader/ai/UpgradePath.java | 158 ++++++++++++ src/main/resources/ai-assist-config.json | 98 ++++++++ .../smcupgrader/ai/AiPlanGeneratorTest.java | 237 +++++++++++++++++ .../smcupgrader/ai/ClaudeCodeInvokerTest.java | 109 ++++++++ .../smcupgrader/ai/MarkdownParserTest.java | 214 ++++++++++++++++ .../smcupgrader/ai/PlanDocumentTest.java | 139 ++++++++++ .../tools/smcupgrader/ai/UpgradePathTest.java | 168 +++++++++++++ 15 files changed, 2189 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/PlanDocument.java create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java create mode 100644 src/main/resources/ai-assist-config.json create mode 100644 src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java create mode 100644 src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java create mode 100644 src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java create mode 100644 src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java create mode 100644 src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java diff --git a/pom.xml b/pom.xml index fba2e42..fcbc6d4 100644 --- a/pom.xml +++ b/pom.xml @@ -273,6 +273,16 @@ 4.11.0 test + + com.google.code.gson + gson + 2.10.1 + + + org.commonmark + commonmark + 0.20.0 + diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java b/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java index c257d86..4042c5a 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java @@ -18,6 +18,7 @@ import static com.elasticpath.tools.smcupgrader.UpgradeController.LOGGER; import java.io.File; +import java.io.IOException; import java.util.concurrent.Callable; import ch.qos.logback.classic.Level; @@ -25,6 +26,9 @@ import org.slf4j.LoggerFactory; import picocli.CommandLine; +import com.elasticpath.tools.smcupgrader.ai.AiPlanGenerator; +import com.elasticpath.tools.smcupgrader.ai.UpgradePath; + /** * The main SMC Upgrader class. */ @@ -33,8 +37,9 @@ public class SMCUpgraderCLI implements Callable { private static final String DEFAULT_UPSTREAM_REPO_URL = "git@code.elasticpath.com:ep-commerce/ep-commerce.git"; - @CommandLine.Parameters(index = "0", - description = "The version of Elastic Path Self-Managed Commerce to upgrade to.") + @CommandLine.Parameters(index = "0", arity = "0..1", + description = "The version of Elastic Path Self-Managed Commerce to upgrade to. " + + "Optional when using --ai:start or --ai:continue.") private String version; @CommandLine.Option(names = { "-C" }, @@ -82,6 +87,14 @@ public class SMCUpgraderCLI implements Callable { negatable = true, defaultValue = "true") private boolean doDiffResolution; + @CommandLine.Option(names = { "--ai:start" }, + description = "Start AI-assisted upgrade mode and generate upgrade plan. Requires version parameter.") + private boolean aiStart; + + @CommandLine.Option(names = { "--ai:continue" }, + description = "Continue AI-assisted upgrade from saved plan.") + private boolean aiContinue; + @Override public Integer call() { try { @@ -92,17 +105,68 @@ public Integer call() { final UpgradeController upgradeController = new UpgradeController(workingDir, upstreamRemoteRepositoryUrl); + // Handle AI assist modes + if (aiStart) { + return handleAiStart(upgradeController); + } else if (aiContinue) { + return handleAiContinue(upgradeController); + } + + // Standard upgrade mode - version is required + if (version == null || version.trim().isEmpty()) { + LOGGER.error("Version parameter is required for standard upgrade mode."); + LOGGER.error("Usage: smc-upgrader "); + LOGGER.error(" or: smc-upgrader --ai:start "); + LOGGER.error(" or: smc-upgrader --ai:continue"); + return 1; + } + upgradeController.performUpgrade(version, doCleanWorkingDirectoryCheck, doRevertPatches, doMerge, doConflictResolution, doDiffResolution); return 0; } catch (RuntimeException e) { LOGGER.error("Unexpected error encountered while upgrading", e); + } catch (IOException e) { + LOGGER.error("IO error encountered", e); } return 1; } + /** + * Handle AI assist start mode. + * + * @param upgradeController the upgrade controller + * @return exit code + * @throws IOException if an error occurs + */ + private Integer handleAiStart(final UpgradeController upgradeController) throws IOException { + if (version == null || version.trim().isEmpty()) { + LOGGER.error("Version parameter is required for --ai:start mode."); + LOGGER.error("Usage: smc-upgrader --ai:start "); + return 1; + } + + UpgradePath upgradePath = UpgradePath.loadFromResource(); + AiPlanGenerator generator = new AiPlanGenerator(upgradePath, upgradeController); + + boolean generated = generator.generatePlan(version, workingDir); + return generated ? 0 : 1; + } + + /** + * Handle AI assist continue mode. + * + * @param upgradeController the upgrade controller + * @return exit code + */ + private Integer handleAiContinue(final UpgradeController upgradeController) { + LOGGER.info("AI continue mode not yet implemented."); + LOGGER.info("This will be implemented in Sprint 4."); + return 1; + } + /** * Main entrypoint. * diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java new file mode 100644 index 0000000..2c2477b --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java @@ -0,0 +1,207 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import java.io.Console; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.elasticpath.tools.smcupgrader.UpgradeController; + +/** + * Generates AI-assisted upgrade plans. + */ +public class AiPlanGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(AiPlanGenerator.class); + private static final String PLAN_FILE_NAME = "smc-upgrader-plan.md"; + + private final UpgradePath upgradePath; + private final UpgradeController upgradeController; + + /** + * Constructor. + * + * @param upgradePath the upgrade path configuration + * @param upgradeController the upgrade controller for version detection + */ + public AiPlanGenerator(final UpgradePath upgradePath, final UpgradeController upgradeController) { + this.upgradePath = upgradePath; + this.upgradeController = upgradeController; + } + + /** + * Generate an upgrade plan. + * + * @param targetVersion the target version to upgrade to + * @param workingDir the working directory + * @return true if plan was generated, false if user cancelled + * @throws IOException if an error occurs + */ + public boolean generatePlan(final String targetVersion, final File workingDir) throws IOException { + File planFile = new File(workingDir, PLAN_FILE_NAME); + + // Check if plan already exists + if (planFile.exists()) { + if (!promptForOverwrite(planFile)) { + LOGGER.info("Keeping existing plan. To continue with the existing upgrade, run:"); + LOGGER.info(" smc-upgrader --ai:continue"); + LOGGER.info(""); + LOGGER.info("To use a different plan file name, move or rename the existing plan first."); + return false; + } + } + + // Validate target version + if (!upgradePath.isValidVersion(targetVersion)) { + throw new IllegalArgumentException("Invalid target version: " + targetVersion + + ". Valid versions are: " + upgradePath.getVersions()); + } + + // Determine current version + String currentVersion = upgradeController.convertVersionToReleaseFormat( + upgradeController.determineCurrentVersion()); + LOGGER.info("Detected current version: {}", currentVersion); + + // Validate upgrade path + if (!upgradePath.validateVersionPath(currentVersion, targetVersion)) { + throw new IllegalArgumentException( + "Cannot upgrade from " + currentVersion + " to " + targetVersion + + ". Current version must be earlier than target version."); + } + + // Calculate version sequence + List versionSequence = calculateVersionSequence(currentVersion, targetVersion); + LOGGER.info("Upgrade path: {}", String.join(" -> ", versionSequence)); + + // Expand steps for all version transitions + List allSteps = expandStepsForVersions(versionSequence); + + // Generate markdown + String markdown = MarkdownWriter.generateMarkdown(allSteps, currentVersion, targetVersion); + + // Write to file + Files.write(planFile.toPath(), markdown.getBytes(StandardCharsets.UTF_8)); + + LOGGER.info("Generated upgrade plan: {}", planFile.getAbsolutePath()); + LOGGER.info(""); + LOGGER.info("You can review and customize the plan if needed."); + LOGGER.info(""); + LOGGER.info("IMPORTANT: This upgrade process requires Claude Code CLI."); + LOGGER.info("Please ensure Claude Code is installed and you have a Claude Pro account."); + LOGGER.info("For installation instructions, visit: https://www.claude.com/product/claude-code"); + LOGGER.info(""); + LOGGER.info("To continue with the upgrade, run:"); + LOGGER.info(" smc-upgrader --ai:continue"); + + return true; + } + + /** + * Prompt the user to confirm overwriting an existing plan file. + * + * @param planFile the existing plan file + * @return true to proceed with overwrite, false to cancel + */ + boolean promptForOverwrite(final File planFile) { + LOGGER.warn("WARNING: An upgrade plan already exists at: {}", planFile.getAbsolutePath()); + LOGGER.warn(""); + LOGGER.warn("This plan may contain customizations or progress from a previous upgrade."); + + Console console = System.console(); + String response; + + if (console != null) { + response = console.readLine("Do you want to overwrite it? [y/N]: "); + } else { + // Fallback for environments without console (like IDEs) + Scanner scanner = new Scanner(System.in); + System.out.print("Do you want to overwrite it? [y/N]: "); + response = scanner.nextLine(); + } + + return response != null && (response.equalsIgnoreCase("y") || response.equalsIgnoreCase("yes")); + } + + /** + * Calculate the version sequence from current to target. + * + * @param fromVersion the starting version + * @param toVersion the target version + * @return list of versions in sequence + */ + List calculateVersionSequence(final String fromVersion, final String toVersion) { + return upgradePath.getIntermediateVersions(fromVersion, toVersion); + } + + /** + * Expand step templates for all version transitions. + * + * @param versionSequence the sequence of versions + * @return list of expanded steps + */ + List expandStepsForVersions(final List versionSequence) { + List allSteps = new ArrayList<>(); + + // For each version transition + for (int i = 0; i < versionSequence.size() - 1; i++) { + String fromVersion = versionSequence.get(i); + String toVersion = versionSequence.get(i + 1); + + // Create steps for this transition + for (AiPlanStep template : upgradePath.getDefaultSteps()) { + AiPlanStep step = new AiPlanStep(); + step.setTitle(substituteVariables(template.getTitle(), fromVersion, toVersion)); + step.setTask(substituteVariables(template.getTask(), fromVersion, toVersion)); + step.setTool(template.getTool()); + step.setStatus(template.getStatus()); + + if (template.getValidationCommand() != null) { + step.setValidationCommand(substituteVariables(template.getValidationCommand(), fromVersion, toVersion)); + } + + if (template.getPrompt() != null) { + step.setPrompt(substituteVariables(template.getPrompt(), fromVersion, toVersion)); + } + + allSteps.add(step); + } + } + + return allSteps; + } + + /** + * Substitute version variables in a template string. + * + * @param template the template string + * @param fromVersion the from version + * @param toVersion the to version + * @return the string with variables substituted + */ + String substituteVariables(final String template, final String fromVersion, final String toVersion) { + return template + .replace("{FROM_VERSION}", fromVersion) + .replace("{TO_VERSION}", toVersion); + } +} diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java new file mode 100644 index 0000000..c33e17f --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java @@ -0,0 +1,198 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +/** + * Model class representing a single step in an AI-assisted upgrade plan. + */ +public class AiPlanStep { + private String title; + private String task; + private String tool; + private String validationCommand; + private String status; + private String prompt; + + /** + * Default constructor for JSON deserialization. + */ + public AiPlanStep() { + } + + /** + * Constructor for creating a plan step. + * + * @param title the step title + * @param task the task description + * @param tool the tool to use (smc-upgrader or claude) + * @param validationCommand optional validation command + * @param status the step status (incomplete or complete) + * @param prompt optional prompt for claude steps + */ + public AiPlanStep(final String title, final String task, final String tool, + final String validationCommand, final String status, final String prompt) { + this.title = title; + this.task = task; + this.tool = tool; + this.validationCommand = validationCommand; + this.status = status; + this.prompt = prompt; + } + + /** + * Get the step title. + * + * @return the title + */ + public String getTitle() { + return title; + } + + /** + * Set the step title. + * + * @param title the title + */ + public void setTitle(final String title) { + this.title = title; + } + + /** + * Get the task description. + * + * @return the task + */ + public String getTask() { + return task; + } + + /** + * Set the task description. + * + * @param task the task + */ + public void setTask(final String task) { + this.task = task; + } + + /** + * Get the tool name. + * + * @return the tool (smc-upgrader or claude) + */ + public String getTool() { + return tool; + } + + /** + * Set the tool name. + * + * @param tool the tool (smc-upgrader or claude) + */ + public void setTool(final String tool) { + this.tool = tool; + } + + /** + * Get the validation command. + * + * @return the validation command, or null if none + */ + public String getValidationCommand() { + return validationCommand; + } + + /** + * Set the validation command. + * + * @param validationCommand the validation command + */ + public void setValidationCommand(final String validationCommand) { + this.validationCommand = validationCommand; + } + + /** + * Get the step status. + * + * @return the status (incomplete or complete) + */ + public String getStatus() { + return status; + } + + /** + * Set the step status. + * + * @param status the status (incomplete or complete) + */ + public void setStatus(final String status) { + this.status = status; + } + + /** + * Get the prompt for claude steps. + * + * @return the prompt, or null if not a claude step + */ + public String getPrompt() { + return prompt; + } + + /** + * Set the prompt for claude steps. + * + * @param prompt the prompt + */ + public void setPrompt(final String prompt) { + this.prompt = prompt; + } + + /** + * Check if this is a claude tool step. + * + * @return true if tool is "claude" + */ + public boolean isClaudeStep() { + return "claude".equals(tool); + } + + /** + * Check if this is an smc-upgrader tool step. + * + * @return true if tool is "smc-upgrader" + */ + public boolean isSmcUpgraderStep() { + return "smc-upgrader".equals(tool); + } + + /** + * Check if this step is complete. + * + * @return true if status is "complete" + */ + public boolean isComplete() { + return "complete".equals(status); + } + + /** + * Check if this step has a validation command. + * + * @return true if validationCommand is not null and not empty + */ + public boolean hasValidationCommand() { + return validationCommand != null && !validationCommand.trim().isEmpty(); + } +} diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java new file mode 100644 index 0000000..6dda191 --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java @@ -0,0 +1,123 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles invocation of Claude Code CLI. + */ +public class ClaudeCodeInvoker { + private static final Logger LOGGER = LoggerFactory.getLogger(ClaudeCodeInvoker.class); + private static final String CLAUDE_PROMPT_FILE = ".claude-prompt.txt"; + + private final File workingDir; + + /** + * Constructor. + * + * @param workingDir the working directory + */ + public ClaudeCodeInvoker(final File workingDir) { + this.workingDir = workingDir; + } + + /** + * Invoke Claude Code with a prompt. + * + * @param prompt the prompt to send to Claude + * @return true if invocation was successful + * @throws IOException if an error occurs + */ + public boolean invokeClaudeCode(final String prompt) throws IOException { + // Write prompt to temporary file + File promptFile = new File(workingDir, CLAUDE_PROMPT_FILE); + Files.write(promptFile.toPath(), prompt.getBytes(StandardCharsets.UTF_8)); + + LOGGER.info("Prompt file created at: {}", promptFile.getAbsolutePath()); + LOGGER.info(""); + LOGGER.info("To execute this step, run:"); + LOGGER.info(" claude --prompt-file {}", CLAUDE_PROMPT_FILE); + LOGGER.info(""); + LOGGER.info("After Claude completes the task, run:"); + LOGGER.info(" smc-upgrader --ai:continue"); + + return true; + } + + /** + * Check if Claude Code CLI is available. + * + * @return true if claude command is available + */ + public boolean isClaudeCodeAvailable() { + try { + Process process = new ProcessBuilder("which", "claude") + .redirectErrorStream(true) + .start(); + + int exitCode = process.waitFor(); + return exitCode == 0; + } catch (IOException | InterruptedException e) { + LOGGER.debug("Error checking for Claude Code availability", e); + return false; + } + } + + /** + * Get the version of Claude Code CLI. + * + * @return the version string, or null if unavailable + */ + public String getClaudeCodeVersion() { + try { + Process process = new ProcessBuilder("claude", "--version") + .redirectErrorStream(true) + .start(); + + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + String version = reader.readLine(); + process.waitFor(); + return version; + } + } catch (IOException | InterruptedException e) { + LOGGER.debug("Error getting Claude Code version", e); + return null; + } + } + + /** + * Clean up the prompt file. + * + * @throws IOException if an error occurs + */ + public void cleanupPromptFile() throws IOException { + File promptFile = new File(workingDir, CLAUDE_PROMPT_FILE); + if (promptFile.exists()) { + Files.delete(promptFile.toPath()); + LOGGER.debug("Cleaned up prompt file: {}", promptFile.getAbsolutePath()); + } + } +} diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java new file mode 100644 index 0000000..40bbc4b --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java @@ -0,0 +1,238 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.commonmark.node.Document; +import org.commonmark.node.HardLineBreak; +import org.commonmark.node.Heading; +import org.commonmark.node.Node; +import org.commonmark.node.Paragraph; +import org.commonmark.node.Text; +import org.commonmark.parser.Parser; + +/** + * Parser for upgrade plan markdown files. + */ +public final class MarkdownParser { + + private static final Parser PARSER = Parser.builder().build(); + private static final Pattern FROM_VERSION_PATTERN = Pattern.compile("Upgrade from:\\s*(.+)"); + private static final Pattern TO_VERSION_PATTERN = Pattern.compile("Upgrade to:\\s*(.+)"); + private static final Pattern TASK_PATTERN = Pattern.compile("Task:\\s*(.+)"); + private static final Pattern TOOL_PATTERN = Pattern.compile("Tool:\\s*(.+)"); + private static final Pattern VALIDATION_PATTERN = Pattern.compile("Validation command:\\s*(.+)"); + private static final Pattern STATUS_PATTERN = Pattern.compile("Status:\\s*(.+)"); + + private MarkdownParser() { + // Utility class + } + + /** + * Parse a markdown plan file into a PlanDocument. + * + * @param planFile the plan file to parse + * @return the parsed plan document + * @throws IOException if an error occurs reading the file + */ + public static PlanDocument parsePlanFile(final File planFile) throws IOException { + byte[] bytes = Files.readAllBytes(planFile.toPath()); + String markdown = new String(bytes, StandardCharsets.UTF_8); + return parsePlan(markdown); + } + + /** + * Parse markdown content into a PlanDocument. + * + * @param markdown the markdown content + * @return the parsed plan document + */ + public static PlanDocument parsePlan(final String markdown) { + PlanDocument plan = new PlanDocument(); + Node document = PARSER.parse(markdown); + + parseDocument(document, plan); + + return plan; + } + + /** + * Parse the document tree. + * + * @param document the document node + * @param plan the plan to populate + */ + private static void parseDocument(final Node document, final PlanDocument plan) { + Node child = document.getFirstChild(); + AiPlanStep currentStep = null; + StringBuilder promptBuilder = null; + + while (child != null) { + if (child instanceof Heading) { + Heading heading = (Heading) child; + + if (heading.getLevel() == 1) { + // Main title, skip + } else if (heading.getLevel() == 2) { + // Save previous step if exists + if (currentStep != null && promptBuilder != null) { + currentStep.setPrompt(promptBuilder.toString().trim()); + } + + // Start new step + String title = extractText(heading); + if (!"Notes".equals(title)) { + currentStep = new AiPlanStep(); + currentStep.setTitle(title); + promptBuilder = new StringBuilder(); + plan.addStep(currentStep); + } else { + currentStep = null; + promptBuilder = null; + } + } + } else if (child instanceof Paragraph) { + String paragraphText = extractText(child); + + // Try to parse metadata + if (parseMetadata(paragraphText, currentStep, plan)) { + // Metadata was parsed + } else if (currentStep != null && currentStep.getTool() != null && promptBuilder != null) { + // This is part of the prompt for claude steps + if (promptBuilder.length() > 0) { + promptBuilder.append("\n\n"); + } + promptBuilder.append(paragraphText); + } + } + + child = child.getNext(); + } + + // Save final step's prompt if needed + if (currentStep != null && promptBuilder != null && promptBuilder.length() > 0) { + currentStep.setPrompt(promptBuilder.toString().trim()); + } + } + + /** + * Parse metadata from a paragraph. + * + * @param text the paragraph text + * @param currentStep the current step being parsed (can be null) + * @param plan the plan document + * @return true if metadata was parsed + */ + private static boolean parseMetadata(final String text, final AiPlanStep currentStep, final PlanDocument plan) { + Matcher matcher; + boolean foundMetadata = false; + + // Check for version metadata + matcher = FROM_VERSION_PATTERN.matcher(text); + if (matcher.find()) { + plan.setFromVersion(matcher.group(1).trim()); + foundMetadata = true; + } + + matcher = TO_VERSION_PATTERN.matcher(text); + if (matcher.find()) { + plan.setToVersion(matcher.group(1).trim()); + foundMetadata = true; + } + + // If we found version metadata, return true + if (foundMetadata) { + return true; + } + + // Check for step metadata (only if we have a current step) + if (currentStep == null) { + return false; + } + + // Parse each line for metadata + String[] lines = text.split("\n"); + + for (String line : lines) { + matcher = TASK_PATTERN.matcher(line); + if (matcher.find()) { + currentStep.setTask(matcher.group(1).trim()); + foundMetadata = true; + continue; + } + + matcher = TOOL_PATTERN.matcher(line); + if (matcher.find()) { + currentStep.setTool(matcher.group(1).trim()); + foundMetadata = true; + continue; + } + + matcher = VALIDATION_PATTERN.matcher(line); + if (matcher.find()) { + currentStep.setValidationCommand(matcher.group(1).trim()); + foundMetadata = true; + continue; + } + + matcher = STATUS_PATTERN.matcher(line); + if (matcher.find()) { + currentStep.setStatus(matcher.group(1).trim()); + foundMetadata = true; + } + } + + return foundMetadata; + } + + /** + * Extract text content from a node and its children. + * + * @param node the node + * @return the text content + */ + private static String extractText(final Node node) { + StringBuilder text = new StringBuilder(); + extractTextRecursive(node, text); + return text.toString(); + } + + /** + * Recursively extract text from a node. + * + * @param node the node + * @param text the text builder + */ + private static void extractTextRecursive(final Node node, final StringBuilder text) { + if (node instanceof Text) { + text.append(((Text) node).getLiteral()); + } else if (node instanceof HardLineBreak) { + text.append("\n"); + } + + Node child = node.getFirstChild(); + while (child != null) { + extractTextRecursive(child, text); + child = child.getNext(); + } + } +} diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java new file mode 100644 index 0000000..8e6bff0 --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java @@ -0,0 +1,88 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import java.time.LocalDate; +import java.util.List; + +/** + * Utility class for writing upgrade plan steps to markdown format. + */ +public final class MarkdownWriter { + + private MarkdownWriter() { + // Utility class + } + + /** + * Generate markdown content for an upgrade plan. + * + * @param steps the list of plan steps + * @param fromVersion the starting version + * @param toVersion the target version + * @return the markdown content as a string + */ + public static String generateMarkdown(final List steps, final String fromVersion, final String toVersion) { + StringBuilder markdown = new StringBuilder(); + + // Header + markdown.append("# SMC Upgrader - AI Assist Plan\n\n"); + markdown.append("Upgrade from: ").append(fromVersion).append(" \n"); + markdown.append("Upgrade to: ").append(toVersion).append(" \n"); + markdown.append("Generated: ").append(LocalDate.now()).append("\n\n"); + markdown.append("---\n\n"); + + // Steps + for (AiPlanStep step : steps) { + appendStep(markdown, step); + } + + // Footer + markdown.append("---\n\n"); + markdown.append("## Notes\n\n"); + markdown.append("Customers can edit this plan to add custom steps or modify existing ones. "); + markdown.append("Each step will be executed in sequence when running `smc-upgrader --ai:continue`.\n\n"); + markdown.append("To mark a step as complete, change `Status: incomplete` to `Status: complete`.\n\n"); + + return markdown.toString(); + } + + /** + * Append a single step to the markdown. + * + * @param markdown the markdown builder + * @param step the step to append + */ + private static void appendStep(final StringBuilder markdown, final AiPlanStep step) { + // Step title + markdown.append("## ").append(step.getTitle()).append("\n\n"); + + // Metadata + markdown.append("Task: ").append(step.getTask()).append(" \n"); + markdown.append("Tool: ").append(step.getTool()).append(" \n"); + + if (step.hasValidationCommand()) { + markdown.append("Validation command: ").append(step.getValidationCommand()).append(" \n"); + } + + markdown.append("Status: ").append(step.getStatus()).append("\n\n"); + + // Prompt for claude steps + if (step.isClaudeStep() && step.getPrompt() != null && !step.getPrompt().trim().isEmpty()) { + markdown.append(step.getPrompt()).append("\n\n"); + } + } +} diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/PlanDocument.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/PlanDocument.java new file mode 100644 index 0000000..75d2861 --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/PlanDocument.java @@ -0,0 +1,136 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a parsed upgrade plan document. + */ +public class PlanDocument { + private String fromVersion; + private String toVersion; + private final List steps; + + /** + * Constructor. + */ + public PlanDocument() { + this.steps = new ArrayList<>(); + } + + /** + * Get the from version. + * + * @return the from version + */ + public String getFromVersion() { + return fromVersion; + } + + /** + * Set the from version. + * + * @param fromVersion the from version + */ + public void setFromVersion(final String fromVersion) { + this.fromVersion = fromVersion; + } + + /** + * Get the to version. + * + * @return the to version + */ + public String getToVersion() { + return toVersion; + } + + /** + * Set the to version. + * + * @param toVersion the to version + */ + public void setToVersion(final String toVersion) { + this.toVersion = toVersion; + } + + /** + * Get all steps. + * + * @return the list of steps + */ + public List getSteps() { + return steps; + } + + /** + * Add a step to the plan. + * + * @param step the step to add + */ + public void addStep(final AiPlanStep step) { + this.steps.add(step); + } + + /** + * Find the next incomplete step. + * + * @return the next incomplete step, or null if all steps are complete + */ + public AiPlanStep findNextIncompleteStep() { + for (AiPlanStep step : steps) { + if (!step.isComplete()) { + return step; + } + } + return null; + } + + /** + * Check if all steps are complete. + * + * @return true if all steps are complete + */ + public boolean isAllStepsComplete() { + return findNextIncompleteStep() == null; + } + + /** + * Count completed steps. + * + * @return the number of completed steps + */ + public int countCompletedSteps() { + int count = 0; + for (AiPlanStep step : steps) { + if (step.isComplete()) { + count++; + } + } + return count; + } + + /** + * Get total number of steps. + * + * @return the total number of steps + */ + public int getTotalSteps() { + return steps.size(); + } +} diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java new file mode 100644 index 0000000..e76c6f3 --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java @@ -0,0 +1,158 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.Gson; + +/** + * Model for upgrade path configuration, including valid versions and default step templates. + */ +public class UpgradePath { + private List versions; + private List defaultSteps; + + /** + * Default constructor for JSON deserialization. + */ + public UpgradePath() { + } + + /** + * Constructor. + * + * @param versions list of valid version strings + * @param defaultSteps list of default step templates + */ + public UpgradePath(final List versions, final List defaultSteps) { + this.versions = versions; + this.defaultSteps = defaultSteps; + } + + /** + * Load the upgrade path configuration from the classpath resource. + * + * @return the loaded UpgradePath + * @throws IOException if the configuration file cannot be loaded + */ + public static UpgradePath loadFromResource() throws IOException { + try (InputStream inputStream = UpgradePath.class.getResourceAsStream("/ai-assist-config.json")) { + if (inputStream == null) { + throw new IOException("Could not find ai-assist-config.json in classpath"); + } + + try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { + Gson gson = new Gson(); + return gson.fromJson(reader, UpgradePath.class); + } + } + } + + /** + * Get the list of intermediate versions between from and to versions. + * + * @param fromVersion the starting version (e.g., "8.5.x") + * @param toVersion the target version (e.g., "8.7.x") + * @return list of intermediate versions including from and to, in order + * @throws IllegalArgumentException if versions are invalid or not in sequence + */ + public List getIntermediateVersions(final String fromVersion, final String toVersion) { + int fromIndex = versions.indexOf(fromVersion); + int toIndex = versions.indexOf(toVersion); + + if (fromIndex == -1) { + throw new IllegalArgumentException("Invalid from version: " + fromVersion); + } + if (toIndex == -1) { + throw new IllegalArgumentException("Invalid to version: " + toVersion); + } + if (fromIndex >= toIndex) { + throw new IllegalArgumentException( + "From version must be earlier than to version. From: " + fromVersion + ", To: " + toVersion); + } + + // Return all versions from fromIndex to toIndex (inclusive) + return new ArrayList<>(versions.subList(fromIndex, toIndex + 1)); + } + + /** + * Validate that an upgrade path exists from fromVersion to toVersion. + * + * @param fromVersion the starting version + * @param toVersion the target version + * @return true if the path is valid + */ + public boolean validateVersionPath(final String fromVersion, final String toVersion) { + try { + getIntermediateVersions(fromVersion, toVersion); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + /** + * Get the list of valid versions. + * + * @return the versions list + */ + public List getVersions() { + return versions; + } + + /** + * Set the list of valid versions. + * + * @param versions the versions list + */ + public void setVersions(final List versions) { + this.versions = versions; + } + + /** + * Get the list of default step templates. + * + * @return the default steps + */ + public List getDefaultSteps() { + return defaultSteps; + } + + /** + * Set the list of default step templates. + * + * @param defaultSteps the default steps + */ + public void setDefaultSteps(final List defaultSteps) { + this.defaultSteps = defaultSteps; + } + + /** + * Check if a version is in the valid versions list. + * + * @param version the version to check + * @return true if the version is valid + */ + public boolean isValidVersion(final String version) { + return versions != null && versions.contains(version); + } +} diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json new file mode 100644 index 0000000..0519bd5 --- /dev/null +++ b/src/main/resources/ai-assist-config.json @@ -0,0 +1,98 @@ +{ + "versions": [ + "8.3.x", + "8.4.x", + "8.5.x", + "8.6.x", + "8.7.x", + "8.8.x" + ], + "defaultSteps": [ + { + "title": "Git merge from {FROM_VERSION} to {TO_VERSION}", + "task": "Upgrade source from {FROM_VERSION} to {TO_VERSION}", + "tool": "smc-upgrader", + "status": "incomplete" + }, + { + "title": "Resolve {TO_VERSION} merge conflicts", + "task": "Resolve remaining Git merge conflicts", + "tool": "claude", + "validationCommand": "git diff --check", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." + }, + { + "title": "Fix compilation failures", + "task": "Resolve all compilation issues", + "tool": "claude", + "validationCommand": "mvn clean install -DskipAllTests", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix static analysis, unit test, and integration test failures", + "task": "Resolve all static analysis, unit test, and integration test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipSlowTests", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Local Database Reset Failures", + "task": "Resolve database reset failures", + "tool": "claude", + "validationCommand": "mvn clean install -Dreset-db -f extensions/database", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Integration Server startup issues", + "task": "Fix Integration Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/integration/ext-integration-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Batch Server startup issues", + "task": "Fix Batch Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/batch/ext-batch-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Search Server startup issues", + "task": "Fix Search Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/search/ext-search-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Cortex startup issues", + "task": "Fix Cortex startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Commerce Manager startup issues", + "task": "Fix Commerce Manager startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + }, + { + "title": "Fix Cucumber and Selenium test failures", + "task": "Resolve all Cucumber and Selenium test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipTests=true -DskipITests=true", + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + } + ] +} diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java new file mode 100644 index 0000000..7248d90 --- /dev/null +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java @@ -0,0 +1,237 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.elasticpath.tools.smcupgrader.UpgradeController; + +/** + * Tests for {@link AiPlanGenerator}. + */ +class AiPlanGeneratorTest { + + @TempDir + File tempDir; + + @Mock + private UpgradeController upgradeController; + + private UpgradePath upgradePath; + private AiPlanGenerator generator; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + + // Create test upgrade path + List versions = Arrays.asList("8.5.x", "8.6.x", "8.7.x", "8.8.x"); + List defaultSteps = Arrays.asList( + createStep("Git merge from {FROM_VERSION} to {TO_VERSION}", "smc-upgrader", null), + createStep("Resolve {TO_VERSION} merge conflicts", "claude", "git diff --check") + ); + upgradePath = new UpgradePath(versions, defaultSteps); + + generator = new AiPlanGenerator(upgradePath, upgradeController); + } + + @Test + void testGeneratePlan_singleVersionUpgrade() throws IOException { + when(upgradeController.determineCurrentVersion()).thenReturn("8.5.0"); + when(upgradeController.convertVersionToReleaseFormat("8.5.0")).thenReturn("8.5.x"); + + boolean result = generator.generatePlan("8.6.x", tempDir); + + assertThat(result).isTrue(); + + File planFile = new File(tempDir, "smc-upgrader-plan.md"); + assertThat(planFile).exists(); + + String content = new String(Files.readAllBytes(planFile.toPath()), java.nio.charset.StandardCharsets.UTF_8); + assertThat(content).contains("# SMC Upgrader - AI Assist Plan"); + assertThat(content).contains("Upgrade from: 8.5.x"); + assertThat(content).contains("Upgrade to: 8.6.x"); + assertThat(content).contains("## Git merge from 8.5.x to 8.6.x"); + assertThat(content).contains("## Resolve 8.6.x merge conflicts"); + assertThat(content).contains("Tool: smc-upgrader"); + assertThat(content).contains("Tool: claude"); + } + + @Test + void testGeneratePlan_multiVersionUpgrade() throws IOException { + when(upgradeController.determineCurrentVersion()).thenReturn("8.5.0"); + when(upgradeController.convertVersionToReleaseFormat("8.5.0")).thenReturn("8.5.x"); + + boolean result = generator.generatePlan("8.7.x", tempDir); + + assertThat(result).isTrue(); + + File planFile = new File(tempDir, "smc-upgrader-plan.md"); + String content = new String(Files.readAllBytes(planFile.toPath()), java.nio.charset.StandardCharsets.UTF_8); + + // Should have steps for 8.5.x -> 8.6.x + assertThat(content).contains("## Git merge from 8.5.x to 8.6.x"); + assertThat(content).contains("## Resolve 8.6.x merge conflicts"); + + // And steps for 8.6.x -> 8.7.x + assertThat(content).contains("## Git merge from 8.6.x to 8.7.x"); + assertThat(content).contains("## Resolve 8.7.x merge conflicts"); + } + + @Test + void testGeneratePlan_invalidTargetVersion() { + when(upgradeController.determineCurrentVersion()).thenReturn("8.5.0"); + when(upgradeController.convertVersionToReleaseFormat("8.5.0")).thenReturn("8.5.x"); + + assertThatThrownBy(() -> generator.generatePlan("9.0.x", tempDir)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid target version"); + } + + @Test + void testGeneratePlan_downgradePath() { + when(upgradeController.determineCurrentVersion()).thenReturn("8.7.0"); + when(upgradeController.convertVersionToReleaseFormat("8.7.0")).thenReturn("8.7.x"); + + assertThatThrownBy(() -> generator.generatePlan("8.5.x", tempDir)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Current version must be earlier than target version"); + } + + @Test + void testCalculateVersionSequence() { + List sequence = generator.calculateVersionSequence("8.5.x", "8.7.x"); + + assertThat(sequence).containsExactly("8.5.x", "8.6.x", "8.7.x"); + } + + @Test + void testExpandStepsForVersions() { + List versions = Arrays.asList("8.5.x", "8.6.x", "8.7.x"); + + List steps = generator.expandStepsForVersions(versions); + + // Should have 2 steps per transition, and we have 2 transitions (8.5->8.6, 8.6->8.7) + assertThat(steps).hasSize(4); + + // Check first transition + assertThat(steps.get(0).getTitle()).isEqualTo("Git merge from 8.5.x to 8.6.x"); + assertThat(steps.get(1).getTitle()).isEqualTo("Resolve 8.6.x merge conflicts"); + + // Check second transition + assertThat(steps.get(2).getTitle()).isEqualTo("Git merge from 8.6.x to 8.7.x"); + assertThat(steps.get(3).getTitle()).isEqualTo("Resolve 8.7.x merge conflicts"); + } + + @Test + void testSubstituteVariables() { + String template = "Upgrade from {FROM_VERSION} to {TO_VERSION}"; + + String result = generator.substituteVariables(template, "8.5.x", "8.6.x"); + + assertThat(result).isEqualTo("Upgrade from 8.5.x to 8.6.x"); + } + + @Test + void testSubstituteVariables_multipleOccurrences() { + String template = "{FROM_VERSION} and {TO_VERSION} and {FROM_VERSION} again"; + + String result = generator.substituteVariables(template, "8.5.x", "8.6.x"); + + assertThat(result).isEqualTo("8.5.x and 8.6.x and 8.5.x again"); + } + + @Test + void testGeneratePlan_existingFile_userDeclines() throws IOException { + when(upgradeController.determineCurrentVersion()).thenReturn("8.5.0"); + when(upgradeController.convertVersionToReleaseFormat("8.5.0")).thenReturn("8.5.x"); + + // Create existing plan file + File planFile = new File(tempDir, "smc-upgrader-plan.md"); + Files.write(planFile.toPath(), "Existing plan".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + + // Create generator that always returns false for overwrite + AiPlanGenerator testGenerator = new AiPlanGenerator(upgradePath, upgradeController) { + @Override + boolean promptForOverwrite(File file) { + return false; + } + }; + + boolean result = testGenerator.generatePlan("8.6.x", tempDir); + + assertThat(result).isFalse(); + // Original file should be unchanged + String content = new String(Files.readAllBytes(planFile.toPath()), java.nio.charset.StandardCharsets.UTF_8); + assertThat(content).isEqualTo("Existing plan"); + } + + @Test + void testGeneratePlan_existingFile_userAccepts() throws IOException { + when(upgradeController.determineCurrentVersion()).thenReturn("8.5.0"); + when(upgradeController.convertVersionToReleaseFormat("8.5.0")).thenReturn("8.5.x"); + + // Create existing plan file + File planFile = new File(tempDir, "smc-upgrader-plan.md"); + Files.write(planFile.toPath(), "Existing plan".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + + // Create generator that always returns true for overwrite + AiPlanGenerator testGenerator = new AiPlanGenerator(upgradePath, upgradeController) { + @Override + boolean promptForOverwrite(File file) { + return true; + } + }; + + boolean result = testGenerator.generatePlan("8.6.x", tempDir); + + assertThat(result).isTrue(); + // File should be overwritten + String content = new String(Files.readAllBytes(planFile.toPath()), java.nio.charset.StandardCharsets.UTF_8); + assertThat(content).contains("# SMC Upgrader - AI Assist Plan"); + assertThat(content).doesNotContain("Existing plan"); + } + + /** + * Helper method to create a test step. + */ + private AiPlanStep createStep(String title, String tool, String validationCommand) { + AiPlanStep step = new AiPlanStep(); + step.setTitle(title); + step.setTask("Test task"); + step.setTool(tool); + step.setStatus("incomplete"); + step.setValidationCommand(validationCommand); + if ("claude".equals(tool)) { + step.setPrompt("Test prompt for {FROM_VERSION} to {TO_VERSION}"); + } + return step; + } +} diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java new file mode 100644 index 0000000..8a10b7c --- /dev/null +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java @@ -0,0 +1,109 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Tests for {@link ClaudeCodeInvoker}. + */ +class ClaudeCodeInvokerTest { + + @TempDir + File tempDir; + + private ClaudeCodeInvoker invoker; + + @BeforeEach + void setUp() { + invoker = new ClaudeCodeInvoker(tempDir); + } + + @Test + void testInvokeClaudeCode_createsPromptFile() throws IOException { + String prompt = "Test prompt for Claude"; + + boolean result = invoker.invokeClaudeCode(prompt); + + assertThat(result).isTrue(); + + File promptFile = new File(tempDir, ".claude-prompt.txt"); + assertThat(promptFile).exists(); + + byte[] bytes = Files.readAllBytes(promptFile.toPath()); + String content = new String(bytes, StandardCharsets.UTF_8); + assertThat(content).isEqualTo(prompt); + } + + @Test + void testInvokeClaudeCode_overwritesExistingFile() throws IOException { + String firstPrompt = "First prompt"; + String secondPrompt = "Second prompt"; + + invoker.invokeClaudeCode(firstPrompt); + invoker.invokeClaudeCode(secondPrompt); + + File promptFile = new File(tempDir, ".claude-prompt.txt"); + byte[] bytes = Files.readAllBytes(promptFile.toPath()); + String content = new String(bytes, StandardCharsets.UTF_8); + assertThat(content).isEqualTo(secondPrompt); + } + + @Test + void testCleanupPromptFile_deletesFile() throws IOException { + String prompt = "Test prompt"; + invoker.invokeClaudeCode(prompt); + + File promptFile = new File(tempDir, ".claude-prompt.txt"); + assertThat(promptFile).exists(); + + invoker.cleanupPromptFile(); + + assertThat(promptFile).doesNotExist(); + } + + @Test + void testCleanupPromptFile_noErrorWhenFileDoesNotExist() throws IOException { + // Should not throw exception + invoker.cleanupPromptFile(); + } + + @Test + void testIsClaudeCodeAvailable() { + // This test will pass or fail depending on whether Claude Code is installed + // We can't make strong assertions, but we can verify the method doesn't crash + boolean available = invoker.isClaudeCodeAvailable(); + assertThat(available).isIn(true, false); + } + + @Test + void testGetClaudeCodeVersion() { + // This test will return a version or null depending on installation + // We can't make strong assertions, but we can verify the method doesn't crash + String version = invoker.getClaudeCodeVersion(); + // Version will be null if Claude is not installed, or a string if it is + assertThat(version == null || version.length() > 0).isTrue(); + } +} diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java new file mode 100644 index 0000000..486278b --- /dev/null +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java @@ -0,0 +1,214 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Tests for {@link MarkdownParser}. + */ +class MarkdownParserTest { + + @TempDir + File tempDir; + + @Test + void testParsePlan_versions() { + String markdown = "# SMC Upgrader - AI Assist Plan\n\n" + + "Upgrade from: 8.5.x \n" + + "Upgrade to: 8.6.x \n\n"; + + PlanDocument plan = MarkdownParser.parsePlan(markdown); + + assertThat(plan.getFromVersion()).isEqualTo("8.5.x"); + assertThat(plan.getToVersion()).isEqualTo("8.6.x"); + } + + @Test + void testParsePlan_singleStep() { + String markdown = "# SMC Upgrader - AI Assist Plan\n\n" + + "Upgrade from: 8.5.x \n" + + "Upgrade to: 8.6.x \n\n" + + "---\n\n" + + "## Git merge from 8.5.x to 8.6.x\n\n" + + "Task: Merge upstream changes \n" + + "Tool: smc-upgrader \n" + + "Status: incomplete\n\n"; + + PlanDocument plan = MarkdownParser.parsePlan(markdown); + + assertThat(plan.getSteps()).hasSize(1); + AiPlanStep step = plan.getSteps().get(0); + assertThat(step.getTitle()).isEqualTo("Git merge from 8.5.x to 8.6.x"); + assertThat(step.getTask()).isEqualTo("Merge upstream changes"); + assertThat(step.getTool()).isEqualTo("smc-upgrader"); + assertThat(step.getStatus()).isEqualTo("incomplete"); + assertThat(step.getValidationCommand()).isNull(); + } + + @Test + void testParsePlan_stepWithValidation() { + String markdown = "# SMC Upgrader - AI Assist Plan\n\n" + + "## Test Step\n\n" + + "Task: Test task \n" + + "Tool: smc-upgrader \n" + + "Validation command: git diff --check \n" + + "Status: incomplete\n\n"; + + PlanDocument plan = MarkdownParser.parsePlan(markdown); + + assertThat(plan.getSteps()).hasSize(1); + AiPlanStep step = plan.getSteps().get(0); + assertThat(step.getValidationCommand()).isEqualTo("git diff --check"); + } + + @Test + void testParsePlan_claudeStepWithPrompt() { + String markdown = "# SMC Upgrader - AI Assist Plan\n\n" + + "## Resolve merge conflicts\n\n" + + "Task: Resolve conflicts \n" + + "Tool: claude \n" + + "Status: incomplete\n\n" + + "Please resolve all merge conflicts in the codebase.\n\n" + + "Focus on maintaining compatibility with existing code.\n\n"; + + PlanDocument plan = MarkdownParser.parsePlan(markdown); + + assertThat(plan.getSteps()).hasSize(1); + AiPlanStep step = plan.getSteps().get(0); + assertThat(step.getTitle()).isEqualTo("Resolve merge conflicts"); + assertThat(step.getTool()).isEqualTo("claude"); + assertThat(step.getPrompt()).contains("Please resolve all merge conflicts"); + assertThat(step.getPrompt()).contains("Focus on maintaining compatibility"); + } + + @Test + void testParsePlan_multipleSteps() { + String markdown = "# SMC Upgrader - AI Assist Plan\n\n" + + "Upgrade from: 8.5.x \n" + + "Upgrade to: 8.6.x \n\n" + + "---\n\n" + + "## Step 1\n\n" + + "Task: Task 1 \n" + + "Tool: smc-upgrader \n" + + "Status: complete\n\n" + + "## Step 2\n\n" + + "Task: Task 2 \n" + + "Tool: claude \n" + + "Status: incomplete\n\n" + + "Prompt for step 2\n\n" + + "## Step 3\n\n" + + "Task: Task 3 \n" + + "Tool: smc-upgrader \n" + + "Status: incomplete\n\n"; + + PlanDocument plan = MarkdownParser.parsePlan(markdown); + + assertThat(plan.getSteps()).hasSize(3); + assertThat(plan.getSteps().get(0).getTitle()).isEqualTo("Step 1"); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("complete"); + assertThat(plan.getSteps().get(1).getTitle()).isEqualTo("Step 2"); + assertThat(plan.getSteps().get(1).getPrompt()).isEqualTo("Prompt for step 2"); + assertThat(plan.getSteps().get(2).getTitle()).isEqualTo("Step 3"); + } + + @Test + void testParsePlan_skipsNotesSection() { + String markdown = "# SMC Upgrader - AI Assist Plan\n\n" + + "## Step 1\n\n" + + "Task: Task 1 \n" + + "Tool: smc-upgrader \n" + + "Status: incomplete\n\n" + + "---\n\n" + + "## Notes\n\n" + + "This is a note section that should not be parsed as a step.\n\n"; + + PlanDocument plan = MarkdownParser.parsePlan(markdown); + + assertThat(plan.getSteps()).hasSize(1); + assertThat(plan.getSteps().get(0).getTitle()).isEqualTo("Step 1"); + } + + @Test + void testParsePlanFile() throws IOException { + File planFile = new File(tempDir, "test-plan.md"); + String markdown = "# SMC Upgrader - AI Assist Plan\n\n" + + "Upgrade from: 8.5.x \n" + + "Upgrade to: 8.6.x \n\n" + + "## Test Step\n\n" + + "Task: Test \n" + + "Tool: smc-upgrader \n" + + "Status: incomplete\n\n"; + + Files.write(planFile.toPath(), markdown.getBytes(StandardCharsets.UTF_8)); + + PlanDocument plan = MarkdownParser.parsePlanFile(planFile); + + assertThat(plan.getFromVersion()).isEqualTo("8.5.x"); + assertThat(plan.getToVersion()).isEqualTo("8.6.x"); + assertThat(plan.getSteps()).hasSize(1); + } + + @Test + void testParsePlan_roundTrip() throws IOException { + // Generate a plan, write it to markdown, then parse it back + AiPlanStep step1 = new AiPlanStep(); + step1.setTitle("Git merge from 8.5.x to 8.6.x"); + step1.setTask("Merge changes"); + step1.setTool("smc-upgrader"); + step1.setStatus("incomplete"); + + AiPlanStep step2 = new AiPlanStep(); + step2.setTitle("Resolve conflicts"); + step2.setTask("Fix conflicts"); + step2.setTool("claude"); + step2.setValidationCommand("git diff --check"); + step2.setStatus("incomplete"); + step2.setPrompt("Please resolve merge conflicts"); + + java.util.List steps = java.util.Arrays.asList(step1, step2); + String markdown = MarkdownWriter.generateMarkdown(steps, "8.5.x", "8.6.x"); + + // Parse it back + PlanDocument plan = MarkdownParser.parsePlan(markdown); + + assertThat(plan.getFromVersion()).isEqualTo("8.5.x"); + assertThat(plan.getToVersion()).isEqualTo("8.6.x"); + assertThat(plan.getSteps()).hasSize(2); + + AiPlanStep parsedStep1 = plan.getSteps().get(0); + assertThat(parsedStep1.getTitle()).isEqualTo("Git merge from 8.5.x to 8.6.x"); + assertThat(parsedStep1.getTask()).isEqualTo("Merge changes"); + assertThat(parsedStep1.getTool()).isEqualTo("smc-upgrader"); + assertThat(parsedStep1.getStatus()).isEqualTo("incomplete"); + + AiPlanStep parsedStep2 = plan.getSteps().get(1); + assertThat(parsedStep2.getTitle()).isEqualTo("Resolve conflicts"); + assertThat(parsedStep2.getTask()).isEqualTo("Fix conflicts"); + assertThat(parsedStep2.getTool()).isEqualTo("claude"); + assertThat(parsedStep2.getValidationCommand()).isEqualTo("git diff --check"); + assertThat(parsedStep2.getStatus()).isEqualTo("incomplete"); + assertThat(parsedStep2.getPrompt()).isEqualTo("Please resolve merge conflicts"); + } +} diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java new file mode 100644 index 0000000..44beeff --- /dev/null +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java @@ -0,0 +1,139 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link PlanDocument}. + */ +class PlanDocumentTest { + + private PlanDocument plan; + + @BeforeEach + void setUp() { + plan = new PlanDocument(); + plan.setFromVersion("8.5.x"); + plan.setToVersion("8.6.x"); + } + + @Test + void testVersions() { + assertThat(plan.getFromVersion()).isEqualTo("8.5.x"); + assertThat(plan.getToVersion()).isEqualTo("8.6.x"); + } + + @Test + void testAddStep() { + AiPlanStep step = createStep("Step 1", "incomplete"); + + plan.addStep(step); + + assertThat(plan.getSteps()).hasSize(1); + assertThat(plan.getSteps().get(0)).isEqualTo(step); + } + + @Test + void testFindNextIncompleteStep_noSteps() { + assertThat(plan.findNextIncompleteStep()).isNull(); + } + + @Test + void testFindNextIncompleteStep_allComplete() { + plan.addStep(createStep("Step 1", "complete")); + plan.addStep(createStep("Step 2", "complete")); + + assertThat(plan.findNextIncompleteStep()).isNull(); + } + + @Test + void testFindNextIncompleteStep_firstIncomplete() { + AiPlanStep step1 = createStep("Step 1", "incomplete"); + plan.addStep(step1); + plan.addStep(createStep("Step 2", "incomplete")); + + assertThat(plan.findNextIncompleteStep()).isEqualTo(step1); + } + + @Test + void testFindNextIncompleteStep_secondIncomplete() { + plan.addStep(createStep("Step 1", "complete")); + AiPlanStep step2 = createStep("Step 2", "incomplete"); + plan.addStep(step2); + plan.addStep(createStep("Step 3", "incomplete")); + + assertThat(plan.findNextIncompleteStep()).isEqualTo(step2); + } + + @Test + void testIsAllStepsComplete_empty() { + assertThat(plan.isAllStepsComplete()).isTrue(); + } + + @Test + void testIsAllStepsComplete_allComplete() { + plan.addStep(createStep("Step 1", "complete")); + plan.addStep(createStep("Step 2", "complete")); + + assertThat(plan.isAllStepsComplete()).isTrue(); + } + + @Test + void testIsAllStepsComplete_someIncomplete() { + plan.addStep(createStep("Step 1", "complete")); + plan.addStep(createStep("Step 2", "incomplete")); + + assertThat(plan.isAllStepsComplete()).isFalse(); + } + + @Test + void testCountCompletedSteps_empty() { + assertThat(plan.countCompletedSteps()).isEqualTo(0); + } + + @Test + void testCountCompletedSteps_mixed() { + plan.addStep(createStep("Step 1", "complete")); + plan.addStep(createStep("Step 2", "incomplete")); + plan.addStep(createStep("Step 3", "complete")); + + assertThat(plan.countCompletedSteps()).isEqualTo(2); + } + + @Test + void testGetTotalSteps() { + plan.addStep(createStep("Step 1", "complete")); + plan.addStep(createStep("Step 2", "incomplete")); + + assertThat(plan.getTotalSteps()).isEqualTo(2); + } + + /** + * Helper to create a test step. + */ + private AiPlanStep createStep(String title, String status) { + AiPlanStep step = new AiPlanStep(); + step.setTitle(title); + step.setTask("Test task"); + step.setTool("smc-upgrader"); + step.setStatus(status); + return step; + } +} diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java new file mode 100644 index 0000000..645e42a --- /dev/null +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java @@ -0,0 +1,168 @@ +/* Copyright 2024 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link UpgradePath}. + */ +class UpgradePathTest { + + @Test + void testLoadFromResource() throws IOException { + UpgradePath upgradePath = UpgradePath.loadFromResource(); + + assertThat(upgradePath).isNotNull(); + assertThat(upgradePath.getVersions()).isNotEmpty(); + assertThat(upgradePath.getDefaultSteps()).isNotEmpty(); + } + + @Test + void testGetIntermediateVersions_singleStep() { + UpgradePath upgradePath = createTestUpgradePath(); + + List intermediates = upgradePath.getIntermediateVersions("8.5.x", "8.6.x"); + + assertThat(intermediates).containsExactly("8.5.x", "8.6.x"); + } + + @Test + void testGetIntermediateVersions_multipleSteps() { + UpgradePath upgradePath = createTestUpgradePath(); + + List intermediates = upgradePath.getIntermediateVersions("8.5.x", "8.8.x"); + + assertThat(intermediates).containsExactly("8.5.x", "8.6.x", "8.7.x", "8.8.x"); + } + + @Test + void testGetIntermediateVersions_allVersions() { + UpgradePath upgradePath = createTestUpgradePath(); + + List intermediates = upgradePath.getIntermediateVersions("8.3.x", "8.8.x"); + + assertThat(intermediates).containsExactly("8.3.x", "8.4.x", "8.5.x", "8.6.x", "8.7.x", "8.8.x"); + } + + @Test + void testGetIntermediateVersions_invalidFromVersion() { + UpgradePath upgradePath = createTestUpgradePath(); + + assertThatThrownBy(() -> upgradePath.getIntermediateVersions("9.0.x", "8.6.x")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid from version"); + } + + @Test + void testGetIntermediateVersions_invalidToVersion() { + UpgradePath upgradePath = createTestUpgradePath(); + + assertThatThrownBy(() -> upgradePath.getIntermediateVersions("8.5.x", "9.0.x")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid to version"); + } + + @Test + void testGetIntermediateVersions_fromGreaterThanTo() { + UpgradePath upgradePath = createTestUpgradePath(); + + assertThatThrownBy(() -> upgradePath.getIntermediateVersions("8.7.x", "8.5.x")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("From version must be earlier than to version"); + } + + @Test + void testGetIntermediateVersions_fromEqualsTo() { + UpgradePath upgradePath = createTestUpgradePath(); + + assertThatThrownBy(() -> upgradePath.getIntermediateVersions("8.6.x", "8.6.x")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("From version must be earlier than to version"); + } + + @Test + void testValidateVersionPath_validPath() { + UpgradePath upgradePath = createTestUpgradePath(); + + boolean isValid = upgradePath.validateVersionPath("8.5.x", "8.7.x"); + + assertThat(isValid).isTrue(); + } + + @Test + void testValidateVersionPath_invalidPath() { + UpgradePath upgradePath = createTestUpgradePath(); + + boolean isValid = upgradePath.validateVersionPath("8.7.x", "8.5.x"); + + assertThat(isValid).isFalse(); + } + + @Test + void testValidateVersionPath_invalidVersion() { + UpgradePath upgradePath = createTestUpgradePath(); + + boolean isValid = upgradePath.validateVersionPath("9.0.x", "9.1.x"); + + assertThat(isValid).isFalse(); + } + + @Test + void testIsValidVersion_validVersion() { + UpgradePath upgradePath = createTestUpgradePath(); + + assertThat(upgradePath.isValidVersion("8.5.x")).isTrue(); + assertThat(upgradePath.isValidVersion("8.6.x")).isTrue(); + assertThat(upgradePath.isValidVersion("8.8.x")).isTrue(); + } + + @Test + void testIsValidVersion_invalidVersion() { + UpgradePath upgradePath = createTestUpgradePath(); + + assertThat(upgradePath.isValidVersion("9.0.x")).isFalse(); + assertThat(upgradePath.isValidVersion("7.0.x")).isFalse(); + } + + @Test + void testDefaultStepsLoaded() throws IOException { + UpgradePath upgradePath = UpgradePath.loadFromResource(); + + List steps = upgradePath.getDefaultSteps(); + + assertThat(steps).isNotEmpty(); + assertThat(steps).anyMatch(step -> "smc-upgrader".equals(step.getTool())); + assertThat(steps).anyMatch(step -> "claude".equals(step.getTool())); + } + + /** + * Create a test UpgradePath with predefined versions. + * + * @return the test UpgradePath + */ + private UpgradePath createTestUpgradePath() { + List versions = Arrays.asList("8.3.x", "8.4.x", "8.5.x", "8.6.x", "8.7.x", "8.8.x"); + return new UpgradePath(versions, null); + } +} From b6e5d09e529cc064ee278a4c2417f77bf371f93f Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Fri, 28 Nov 2025 19:32:03 -0800 Subject: [PATCH 03/63] [PB-13614] Implemented ai:continue --- .../tools/smcupgrader/GitClient.java | 7 + .../tools/smcupgrader/SMCUpgraderCLI.java | 46 +- .../tools/smcupgrader/ai/AiPlanExecutor.java | 443 ++++++++++++++++++ .../tools/smcupgrader/ai/AiPlanGenerator.java | 58 +++ .../tools/smcupgrader/ai/AiPlanStep.java | 19 + .../smcupgrader/ai/ClaudeCodeInvoker.java | 70 ++- .../tools/smcupgrader/ai/MarkdownParser.java | 27 +- .../tools/smcupgrader/ai/MarkdownWriter.java | 4 + .../tools/smcupgrader/impl/GitClientImpl.java | 9 + .../smcupgrader/ai/AiPlanExecutorTest.java | 312 ++++++++++++ .../smcupgrader/ai/ClaudeCodeInvokerTest.java | 57 +-- 11 files changed, 947 insertions(+), 105 deletions(-) create mode 100644 src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java create mode 100644 src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java b/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java index 28ad488..f24283c 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java @@ -141,6 +141,13 @@ public interface GitClient { */ void unstage(String path); + /** + * Commits staged changes with the given message. + * + * @param message the commit message + */ + void commit(String message); + /** * Deletes a file from the local file system and git repository. * diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java b/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java index 4042c5a..b523892 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/SMCUpgraderCLI.java @@ -23,10 +23,12 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; +import org.eclipse.jgit.util.StringUtils; import org.slf4j.LoggerFactory; import picocli.CommandLine; import com.elasticpath.tools.smcupgrader.ai.AiPlanGenerator; +import com.elasticpath.tools.smcupgrader.ai.AiPlanExecutor; import com.elasticpath.tools.smcupgrader.ai.UpgradePath; /** @@ -103,17 +105,8 @@ public Integer call() { rootLogger.setLevel(Level.DEBUG); } - final UpgradeController upgradeController = new UpgradeController(workingDir, upstreamRemoteRepositoryUrl); - - // Handle AI assist modes - if (aiStart) { - return handleAiStart(upgradeController); - } else if (aiContinue) { - return handleAiContinue(upgradeController); - } - // Standard upgrade mode - version is required - if (version == null || version.trim().isEmpty()) { + if (!aiContinue && StringUtils.isEmptyOrNull(version)) { LOGGER.error("Version parameter is required for standard upgrade mode."); LOGGER.error("Usage: smc-upgrader "); LOGGER.error(" or: smc-upgrader --ai:start "); @@ -121,8 +114,17 @@ public Integer call() { return 1; } - upgradeController.performUpgrade(version, doCleanWorkingDirectoryCheck, doRevertPatches, doMerge, doConflictResolution, - doDiffResolution); + final UpgradeController upgradeController = new UpgradeController(workingDir, upstreamRemoteRepositoryUrl); + + // Handle AI assist modes + if (aiStart) { + return handleAiStart(upgradeController); + } else if (aiContinue) { + return handleAiContinue(); + } else { + upgradeController.performUpgrade(version, doCleanWorkingDirectoryCheck, doRevertPatches, doMerge, + doConflictResolution, doDiffResolution); + } return 0; } catch (RuntimeException e) { @@ -142,12 +144,6 @@ public Integer call() { * @throws IOException if an error occurs */ private Integer handleAiStart(final UpgradeController upgradeController) throws IOException { - if (version == null || version.trim().isEmpty()) { - LOGGER.error("Version parameter is required for --ai:start mode."); - LOGGER.error("Usage: smc-upgrader --ai:start "); - return 1; - } - UpgradePath upgradePath = UpgradePath.loadFromResource(); AiPlanGenerator generator = new AiPlanGenerator(upgradePath, upgradeController); @@ -158,13 +154,17 @@ private Integer handleAiStart(final UpgradeController upgradeController) throws /** * Handle AI assist continue mode. * - * @param upgradeController the upgrade controller * @return exit code */ - private Integer handleAiContinue(final UpgradeController upgradeController) { - LOGGER.info("AI continue mode not yet implemented."); - LOGGER.info("This will be implemented in Sprint 4."); - return 1; + private Integer handleAiContinue() { + try { + AiPlanExecutor executor = new AiPlanExecutor(workingDir); + boolean stepExecuted = executor.executeNextStep(); + return stepExecuted ? 0 : 1; + } catch (IOException e) { + LOGGER.error("Error executing plan", e); + return 1; + } } /** diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java new file mode 100644 index 0000000..722153a --- /dev/null +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -0,0 +1,443 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import java.io.BufferedReader; +import java.io.Console; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Scanner; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.elasticpath.tools.smcupgrader.GitClient; +import com.elasticpath.tools.smcupgrader.UpgradeController; +import com.elasticpath.tools.smcupgrader.impl.GitClientImpl; + +/** + * Executes upgrade plan steps. + */ +public class AiPlanExecutor { + private static final Logger LOGGER = LoggerFactory.getLogger(AiPlanExecutor.class); + private static final String PLAN_FILE_NAME = "smc-upgrader-plan.md"; + private static final String DEFAULT_UPSTREAM_REPO_URL = "git@code.elasticpath.com:ep-commerce/ep-commerce.git"; + + private final File workingDir; + private final ClaudeCodeInvoker claudeInvoker; + private final UpgradeController upgradeController; + private final GitClient gitClient; + private String testChoice; // For testing only - bypasses interactive prompt + + /** + * Constructor. + * + * @param workingDir the working directory + */ + public AiPlanExecutor(final File workingDir) { + this.workingDir = workingDir; + this.claudeInvoker = new ClaudeCodeInvoker(workingDir); + this.upgradeController = new UpgradeController(workingDir, DEFAULT_UPSTREAM_REPO_URL); + this.gitClient = createGitClient(workingDir); + } + + /** + * Execute the next step in the plan. + * + * @return true if a step was executed, false if all steps are complete + * @throws IOException if an error occurs + */ + public boolean executeNextStep() throws IOException { + File planFile = new File(workingDir, PLAN_FILE_NAME); + + if (!planFile.exists()) { + LOGGER.error("No upgrade plan found at: {}", planFile.getAbsolutePath()); + LOGGER.error("Run 'smc-upgrader --ai:start ' to generate a plan first."); + return false; + } + + LOGGER.info("Reading plan from: {}", planFile.getAbsolutePath()); + LOGGER.info(""); + + // Parse the plan + PlanDocument plan = MarkdownParser.parsePlanFile(planFile); + + // Find next incomplete step + AiPlanStep nextStep = plan.findNextIncompleteStep(); + + if (nextStep == null) { + LOGGER.info("All steps complete! Upgrade finished."); + LOGGER.info(""); + LOGGER.info("Completed {} of {} steps.", plan.countCompletedSteps(), plan.getTotalSteps()); + return false; + } + + // Show next step info + LOGGER.info("Next step: {}", nextStep.getTitle()); + LOGGER.info(" Task: {}", nextStep.getTask()); + LOGGER.info(" Tool: {}", nextStep.getTool()); + if (nextStep.hasValidationCommand()) { + LOGGER.info(" Validation command: {}", nextStep.getValidationCommand()); + } + LOGGER.info(" Status: {}", nextStep.getStatus()); + LOGGER.info(""); + + // Show menu and get user choice + String choice = promptUserChoice(); + + // Handle user choice + boolean stepCompleted = false; + boolean shouldExit = false; + + switch (choice.toUpperCase()) { + case "E": + // Execute the step + if (nextStep.isSmcUpgraderStep()) { + stepCompleted = executeSmcUpgraderStep(nextStep); + } else if (nextStep.isClaudeStep()) { + stepCompleted = executeClaudeStep(nextStep); + } else { + LOGGER.error("Unknown tool: {}", nextStep.getTool()); + return false; + } + shouldExit = true; + break; + + case "C": + // Check validation + stepCompleted = checkStepValidation(nextStep); + shouldExit = true; + break; + + case "M": + // Mark complete + stepCompleted = true; + shouldExit = true; + break; + + case "X": + // Exit + LOGGER.info("Exiting. Run 'smc-upgrader --ai:continue' to return to this step."); + return false; + + default: + LOGGER.error("Invalid choice: {}", choice); + return false; + } + + // Save updated plan if step was marked complete + if (stepCompleted) { + nextStep.setStatus("complete"); + savePlan(planFile, plan); + LOGGER.info(""); + LOGGER.info("Step marked as complete."); + } + + // Exit after action + if (shouldExit) { + LOGGER.info(""); + LOGGER.info("Exiting. Run 'smc-upgrader --ai:continue' to continue with the next step."); + } + + return true; + } + + /** + * Set the test choice (for testing only). + * + * @param choice the choice to use instead of prompting + */ + void setTestChoice(final String choice) { + this.testChoice = choice; + } + + /** + * Prompt the user for their choice. + * + * @return the user's choice (E, C, M, or X) + */ + private String promptUserChoice() { + // If test choice is set, use it instead of prompting + if (testChoice != null) { + String choice = testChoice; + testChoice = null; // Clear after use + return choice; + } + + LOGGER.info("What would you like to do?"); + LOGGER.info(" [E] Execute this step"); + LOGGER.info(" [C] Check if this step is complete"); + LOGGER.info(" [M] Mark this step as complete"); + LOGGER.info(" [X] Exit"); + LOGGER.info(""); + + Console console = System.console(); + String response; + + if (console != null) { + response = console.readLine("Your choice: "); + } else { + // Fallback for environments without console (like IDEs) + Scanner scanner = new Scanner(System.in); + System.out.print("Your choice: "); + response = scanner.nextLine(); + } + + return response != null ? response.trim() : "X"; + } + + /** + * Check step validation by running the validation command. + * + * @param step the step to validate + * @return true if validation passed and step should be marked complete + * @throws IOException if an error occurs + */ + private boolean checkStepValidation(final AiPlanStep step) throws IOException { + if (!step.hasValidationCommand()) { + LOGGER.warn("No validation command defined for this step."); + LOGGER.warn("Use [M] to manually mark complete or [E] to execute the step."); + return false; + } + + LOGGER.info("Running validation command: {}", step.getValidationCommand()); + LOGGER.info(""); + + boolean validationPassed = runValidationCommand(step.getValidationCommand()); + + LOGGER.info(""); + if (validationPassed) { + LOGGER.info("Validation passed!"); + return true; + } else { + LOGGER.warn("Validation failed. Step remains incomplete."); + LOGGER.warn("Review the output above and run 'smc-upgrader --ai:continue' to try again."); + return false; + } + } + + /** + * Execute an smc-upgrader step. + * + * @param step the step to execute + * @return true if the step should be auto-marked complete + * @throws IOException if an error occurs + */ + private boolean executeSmcUpgraderStep(final AiPlanStep step) throws IOException { + LOGGER.info("Executing smc-upgrader step: {}", step.getTask()); + + // Get target version from step + String targetVersion = step.getVersion(); + + if (targetVersion != null && !targetVersion.trim().isEmpty()) { + LOGGER.info("Performing upgrade to version: {}", targetVersion); + + try { + // Execute the upgrade with standard options + upgradeController.performUpgrade( + targetVersion, + true, // doCleanWorkingDirectoryCheck + true, // doRevertPatches + true, // doMerge + true, // doConflictResolution + true // doDiffResolution + ); + + LOGGER.info("Upgrade completed successfully."); + + // Run validation if specified + if (step.hasValidationCommand()) { + LOGGER.info("Running validation command: {}", step.getValidationCommand()); + boolean validationPassed = runValidationCommand(step.getValidationCommand()); + + if (validationPassed) { + LOGGER.info("Validation passed."); + return true; + } else { + LOGGER.warn("Validation failed. Step not marked as complete."); + LOGGER.warn("Please resolve the issue and run 'smc-upgrader --ai:continue' again."); + return false; + } + } else { + // No validation command - auto-complete after successful upgrade + return true; + } + } catch (RuntimeException e) { + LOGGER.error("Upgrade failed", e); + LOGGER.warn("Step not marked as complete."); + LOGGER.warn("Please resolve the issue and run 'smc-upgrader --ai:continue' again."); + return false; + } + } else { + LOGGER.warn("No version specified for this step."); + LOGGER.warn("Step cannot be executed automatically."); + + // Run validation if specified + if (step.hasValidationCommand()) { + LOGGER.info("Running validation command: {}", step.getValidationCommand()); + boolean validationPassed = runValidationCommand(step.getValidationCommand()); + + if (validationPassed) { + LOGGER.info("Validation passed."); + return true; + } else { + LOGGER.warn("Validation failed. Step not marked as complete."); + LOGGER.warn("Please resolve the issue and run 'smc-upgrader --ai:continue' again."); + return false; + } + } else { + // No validation command and can't execute - don't auto-complete + LOGGER.warn("Please complete this step manually and mark it as complete in the plan file."); + return false; + } + } + } + + /** + * Execute a Claude step. + * + * @param step the step to execute + * @return false (Claude steps are never auto-completed, even on success) + * @throws IOException if an error occurs + */ + private boolean executeClaudeStep(final AiPlanStep step) throws IOException { + LOGGER.info("This step requires Claude Code assistance."); + LOGGER.info(""); + + // Invoke Claude with the prompt + if (step.getPrompt() != null && !step.getPrompt().trim().isEmpty()) { + boolean claudeSuccess = claudeInvoker.invokeClaudeCode(step.getPrompt()); + + LOGGER.info(""); + if (claudeSuccess) { + LOGGER.info("Claude Code completed successfully."); + } else { + LOGGER.warn("Claude Code did not complete successfully."); + } + } else { + LOGGER.warn("No prompt defined for this Claude step."); + LOGGER.info("Please manually complete the task: {}", step.getTask()); + } + + // Claude steps are never auto-completed - user must verify and mark complete manually + LOGGER.info(""); + LOGGER.info("Please verify the work, then run 'smc-upgrader --ai:continue' to resume or proceed to the next step."); + + return false; + } + + /** + * Run a validation command. + * + * @param command the command to run + * @return true if the command exited with code 0 + * @throws IOException if an error occurs + */ + boolean runValidationCommand(final String command) throws IOException { + try { + Process process = new ProcessBuilder("/bin/sh", "-c", command) + .directory(workingDir) + .redirectErrorStream(true) + .start(); + + // Read output + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + LOGGER.debug("Validation output: {}", line); + } + } + + int exitCode = process.waitFor(); + return exitCode == 0; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Validation command interrupted", e); + } + } + + /** + * Save the plan back to the file. + * + * @param planFile the plan file + * @param plan the plan document + * @throws IOException if an error occurs + */ + private void savePlan(final File planFile, final PlanDocument plan) throws IOException { + String markdown = MarkdownWriter.generateMarkdown(plan.getSteps(), plan.getFromVersion(), plan.getToVersion()); + Files.write(planFile.toPath(), markdown.getBytes(StandardCharsets.UTF_8)); + + // Find the completed step to include in commit message + String completedStepTitle = plan.getSteps().stream() + .filter(step -> "complete".equals(step.getStatus())) + .reduce((first, second) -> second) // Get the last completed step + .map(AiPlanStep::getTitle) + .orElse("step"); + + // Commit to git + if (gitClient != null) { + try { + commitPlanFile("Mark step complete: " + completedStepTitle); + } catch (RuntimeException e) { + // Don't fail if git commit fails (e.g., signing service unavailable) + LOGGER.debug("Could not commit plan file to git: {}", e.getMessage()); + } + } + } + + /** + * Create a GitClient for the working directory. + * + * @param workingDir the working directory + * @return a GitClient, or null if not in a git repository + */ + private GitClient createGitClient(final File workingDir) { + try { + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + Repository repository = builder + .setWorkTree(workingDir) + .readEnvironment() + .findGitDir() + .build(); + return new GitClientImpl(repository); + } catch (IOException e) { + // Not in a git repository + LOGGER.debug("Could not create GitClient: {}", e.getMessage()); + return null; + } + } + + /** + * Commit the plan file to git. + * + * @param message the commit message + */ + private void commitPlanFile(final String message) { + // Stage the plan file + gitClient.stage(PLAN_FILE_NAME); + + // Commit the plan file + gitClient.commit(message); + + LOGGER.debug("Committed plan file: {}", message); + } +} diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java index 2c2477b..49548be 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java @@ -24,10 +24,14 @@ import java.util.List; import java.util.Scanner; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.elasticpath.tools.smcupgrader.GitClient; import com.elasticpath.tools.smcupgrader.UpgradeController; +import com.elasticpath.tools.smcupgrader.impl.GitClientImpl; /** * Generates AI-assisted upgrade plans. @@ -103,6 +107,17 @@ public boolean generatePlan(final String targetVersion, final File workingDir) t // Write to file Files.write(planFile.toPath(), markdown.getBytes(StandardCharsets.UTF_8)); + // Commit to git + GitClient gitClient = createGitClient(workingDir); + if (gitClient != null) { + try { + commitPlanFile(gitClient, "Generate upgrade plan from " + currentVersion + " to " + targetVersion); + } catch (RuntimeException e) { + // Don't fail if git commit fails (e.g., signing service unavailable) + LOGGER.debug("Could not commit plan file to git: {}", e.getMessage()); + } + } + LOGGER.info("Generated upgrade plan: {}", planFile.getAbsolutePath()); LOGGER.info(""); LOGGER.info("You can review and customize the plan if needed."); @@ -176,6 +191,11 @@ List expandStepsForVersions(final List versionSequence) { step.setTool(template.getTool()); step.setStatus(template.getStatus()); + // Set version for smc-upgrader steps + if ("smc-upgrader".equals(template.getTool())) { + step.setVersion(toVersion); + } + if (template.getValidationCommand() != null) { step.setValidationCommand(substituteVariables(template.getValidationCommand(), fromVersion, toVersion)); } @@ -204,4 +224,42 @@ String substituteVariables(final String template, final String fromVersion, fina .replace("{FROM_VERSION}", fromVersion) .replace("{TO_VERSION}", toVersion); } + + /** + * Create a GitClient for the working directory. + * + * @param workingDir the working directory + * @return a GitClient, or null if not in a git repository + */ + private GitClient createGitClient(final File workingDir) { + try { + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + Repository repository = builder + .setWorkTree(workingDir) + .readEnvironment() + .findGitDir() + .build(); + return new GitClientImpl(repository); + } catch (IOException e) { + // Not in a git repository + LOGGER.debug("Could not create GitClient: {}", e.getMessage()); + return null; + } + } + + /** + * Commit the plan file to git. + * + * @param gitClient the git client + * @param message the commit message + */ + private void commitPlanFile(final GitClient gitClient, final String message) { + // Stage the plan file + gitClient.stage(PLAN_FILE_NAME); + + // Commit the plan file + gitClient.commit(message); + + LOGGER.debug("Committed plan file: {}", message); + } } diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java index c33e17f..2bb4bc4 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java @@ -22,6 +22,7 @@ public class AiPlanStep { private String title; private String task; private String tool; + private String version; private String validationCommand; private String status; private String prompt; @@ -106,6 +107,24 @@ public void setTool(final String tool) { this.tool = tool; } + /** + * Get the version. + * + * @return the version, or null if not applicable + */ + public String getVersion() { + return version; + } + + /** + * Set the version. + * + * @param version the version + */ + public void setVersion(final String version) { + this.version = version; + } + /** * Get the validation command. * diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java index 6dda191..9f56cbf 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +29,8 @@ */ public class ClaudeCodeInvoker { private static final Logger LOGGER = LoggerFactory.getLogger(ClaudeCodeInvoker.class); - private static final String CLAUDE_PROMPT_FILE = ".claude-prompt.txt"; + private static final int PROMPT_MAX_DISPLAY_LENGTH = 50; + private static final int COMMAND_MAX_DISPLAY_LENGTH = 100; private final File workingDir; @@ -47,23 +47,55 @@ public ClaudeCodeInvoker(final File workingDir) { * Invoke Claude Code with a prompt. * * @param prompt the prompt to send to Claude - * @return true if invocation was successful + * @return true if Claude completed successfully * @throws IOException if an error occurs */ public boolean invokeClaudeCode(final String prompt) throws IOException { - // Write prompt to temporary file - File promptFile = new File(workingDir, CLAUDE_PROMPT_FILE); - Files.write(promptFile.toPath(), prompt.getBytes(StandardCharsets.UTF_8)); + // Check if Claude Code is available + if (!isClaudeCodeAvailable()) { + LOGGER.error("Claude Code CLI is not available."); + LOGGER.error("Please install Claude Code from: https://claude.com/claude-code"); + return false; + } - LOGGER.info("Prompt file created at: {}", promptFile.getAbsolutePath()); - LOGGER.info(""); - LOGGER.info("To execute this step, run:"); - LOGGER.info(" claude --prompt-file {}", CLAUDE_PROMPT_FILE); + // Check if we have console access + boolean hasConsole = System.console() != null; + LOGGER.debug("System console available: {}", hasConsole); + LOGGER.debug("Executing command: claude {}", prompt.substring(0, Math.min(PROMPT_MAX_DISPLAY_LENGTH, prompt.length())) + "..."); + + LOGGER.info("Executing Claude Code with prompt..."); LOGGER.info(""); - LOGGER.info("After Claude completes the task, run:"); - LOGGER.info(" smc-upgrader --ai:continue"); - return true; + try { + // Execute Claude Code through shell to ensure proper terminal allocation + // Use single quotes for safer shell escaping (only need to escape single quotes themselves) + String escapedPrompt = prompt.replace("'", "'\\''"); + String command = "claude '" + escapedPrompt + "'"; + + LOGGER.debug("Shell command: {}", command.substring(0, Math.min(COMMAND_MAX_DISPLAY_LENGTH, command.length())) + "..."); + + Process process = new ProcessBuilder("/bin/sh", "-c", command) + .directory(workingDir) + .redirectInput(ProcessBuilder.Redirect.INHERIT) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start(); + + // Wait for Claude to complete + int exitCode = process.waitFor(); + + LOGGER.info(""); + if (exitCode == 0) { + return true; + } else { + LOGGER.warn("Claude Code exited with code: {}", exitCode); + return false; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.error("Claude Code execution was interrupted", e); + return false; + } } /** @@ -108,16 +140,4 @@ public String getClaudeCodeVersion() { } } - /** - * Clean up the prompt file. - * - * @throws IOException if an error occurs - */ - public void cleanupPromptFile() throws IOException { - File promptFile = new File(workingDir, CLAUDE_PROMPT_FILE); - if (promptFile.exists()) { - Files.delete(promptFile.toPath()); - LOGGER.debug("Cleaned up prompt file: {}", promptFile.getAbsolutePath()); - } - } } diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java index 40bbc4b..89af507 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java @@ -22,7 +22,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.commonmark.node.Document; import org.commonmark.node.HardLineBreak; import org.commonmark.node.Heading; import org.commonmark.node.Node; @@ -40,6 +39,7 @@ public final class MarkdownParser { private static final Pattern TO_VERSION_PATTERN = Pattern.compile("Upgrade to:\\s*(.+)"); private static final Pattern TASK_PATTERN = Pattern.compile("Task:\\s*(.+)"); private static final Pattern TOOL_PATTERN = Pattern.compile("Tool:\\s*(.+)"); + private static final Pattern VERSION_PATTERN = Pattern.compile("Version:\\s*(.+)"); private static final Pattern VALIDATION_PATTERN = Pattern.compile("Validation command:\\s*(.+)"); private static final Pattern STATUS_PATTERN = Pattern.compile("Status:\\s*(.+)"); @@ -90,9 +90,7 @@ private static void parseDocument(final Node document, final PlanDocument plan) if (child instanceof Heading) { Heading heading = (Heading) child; - if (heading.getLevel() == 1) { - // Main title, skip - } else if (heading.getLevel() == 2) { + if (heading.getLevel() == 2) { // Save previous step if exists if (currentStep != null && promptBuilder != null) { currentStep.setPrompt(promptBuilder.toString().trim()); @@ -114,14 +112,14 @@ private static void parseDocument(final Node document, final PlanDocument plan) String paragraphText = extractText(child); // Try to parse metadata - if (parseMetadata(paragraphText, currentStep, plan)) { - // Metadata was parsed - } else if (currentStep != null && currentStep.getTool() != null && promptBuilder != null) { - // This is part of the prompt for claude steps - if (promptBuilder.length() > 0) { - promptBuilder.append("\n\n"); + if (!parseMetadata(paragraphText, currentStep, plan)) { + if (currentStep != null && currentStep.getTool() != null && promptBuilder != null) { + // This is part of the prompt for claude steps + if (promptBuilder.length() > 0) { + promptBuilder.append("\n\n"); + } + promptBuilder.append(paragraphText); } - promptBuilder.append(paragraphText); } } @@ -187,6 +185,13 @@ private static boolean parseMetadata(final String text, final AiPlanStep current continue; } + matcher = VERSION_PATTERN.matcher(line); + if (matcher.find()) { + currentStep.setVersion(matcher.group(1).trim()); + foundMetadata = true; + continue; + } + matcher = VALIDATION_PATTERN.matcher(line); if (matcher.find()) { currentStep.setValidationCommand(matcher.group(1).trim()); diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java index 8e6bff0..39e51d3 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java @@ -74,6 +74,10 @@ private static void appendStep(final StringBuilder markdown, final AiPlanStep st markdown.append("Task: ").append(step.getTask()).append(" \n"); markdown.append("Tool: ").append(step.getTool()).append(" \n"); + if (step.getVersion() != null && !step.getVersion().trim().isEmpty()) { + markdown.append("Version: ").append(step.getVersion()).append(" \n"); + } + if (step.hasValidationCommand()) { markdown.append("Validation command: ").append(step.getValidationCommand()).append(" \n"); } diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java b/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java index 573b4bc..e67cf83 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java @@ -319,6 +319,15 @@ public void unstage(final String path) { } } + @Override + public void commit(final String message) { + try (Git git = new Git(repository)) { + git.commit().setMessage(message).call(); + } catch (final GitAPIException e) { + throw new RuntimeException(e); + } + } + @Override public void delete(final String path) { try (Git git = new Git(repository)) { diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java new file mode 100644 index 0000000..6a6971c --- /dev/null +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java @@ -0,0 +1,312 @@ +/* Copyright 2025 Elastic Path Software Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.elasticpath.tools.smcupgrader.ai; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import com.elasticpath.tools.smcupgrader.UpgradeController; + +/** + * Tests for {@link AiPlanExecutor}. + */ +class AiPlanExecutorTest { + + @TempDir + File tempDir; + + private UpgradeController upgradeController; + private AiPlanExecutor executor; + + @BeforeEach + void setUp() { + upgradeController = mock(UpgradeController.class); + executor = new AiPlanExecutor(tempDir); + } + + @Test + void testExecuteNextStep_noPlanFile() throws IOException { + boolean result = executor.executeNextStep(); + + assertThat(result).isFalse(); + } + + @Test + void testExecuteNextStep_allStepsComplete() throws IOException { + // Create plan with all steps complete + AiPlanStep step1 = createStep("Step 1", "smc-upgrader", "complete"); + AiPlanStep step2 = createStep("Step 2", "smc-upgrader", "complete"); + + writePlanFile(Arrays.asList(step1, step2)); + + boolean result = executor.executeNextStep(); + + assertThat(result).isFalse(); + } + + @Test + void testExecuteNextStep_smcUpgraderStepNoValidation() throws IOException { + // Create plan with incomplete smc-upgrader step (no validation, no version) + AiPlanStep step = createStep("Step 1", "smc-upgrader", "incomplete"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("E"); // Execute the step + boolean result = executor.executeNextStep(); + + // Should execute but NOT auto-complete (no version, no validation) + assertThat(result).isTrue(); + + // Verify plan was NOT updated + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("incomplete"); + } + + @Test + void testExecuteNextStep_smcUpgraderStepWithValidationSuccess() throws IOException { + // Create plan with incomplete smc-upgrader step with validation + AiPlanStep step = createStep("Step 1", "smc-upgrader", "incomplete"); + step.setValidationCommand("exit 0"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("E"); // Execute the step + boolean result = executor.executeNextStep(); + + // Should auto-complete + assertThat(result).isTrue(); + + // Verify plan was updated + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("complete"); + } + + @Test + void testExecuteNextStep_smcUpgraderStepWithValidationFailure() throws IOException { + // Create plan with incomplete smc-upgrader step with validation + AiPlanStep step = createStep("Step 1", "smc-upgrader", "incomplete"); + step.setValidationCommand("exit 1"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("E"); // Execute the step + boolean result = executor.executeNextStep(); + + // Should NOT auto-complete + assertThat(result).isTrue(); + + // Verify plan was NOT updated + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("incomplete"); + } + + @Test + void testExecuteNextStep_claudeStep() throws IOException { + // Create plan with incomplete claude step + AiPlanStep step = createStep("Step 1", "claude", "incomplete"); + step.setPrompt("Test prompt"); + + writePlanFile(Arrays.asList(step)); + + // Only run this test if Claude is NOT available (to avoid interactive blocking) + ClaudeCodeInvoker invoker = new ClaudeCodeInvoker(tempDir); + if (invoker.isClaudeCodeAvailable()) { + // Skip test - would block on interactive prompt + return; + } + + executor.setTestChoice("E"); // Execute the step + boolean result = executor.executeNextStep(); + + // Should execute but not auto-complete + assertThat(result).isTrue(); + + // Verify plan was NOT updated (Claude steps are never auto-completed) + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("incomplete"); + } + + @Test + void testExecuteNextStep_skipsCompletedSteps() throws IOException { + // Create plan with mixed completed/incomplete steps + AiPlanStep step1 = createStep("Step 1", "smc-upgrader", "complete"); + AiPlanStep step2 = createStep("Step 2", "smc-upgrader", "incomplete"); + step2.setValidationCommand("exit 0"); // Add validation so it can complete + AiPlanStep step3 = createStep("Step 3", "smc-upgrader", "incomplete"); + + writePlanFile(Arrays.asList(step1, step2, step3)); + + executor.setTestChoice("E"); // Execute the step + boolean result = executor.executeNextStep(); + + assertThat(result).isTrue(); + + // Verify step 2 was completed, step 3 remains incomplete + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("complete"); + assertThat(plan.getSteps().get(1).getStatus()).isEqualTo("complete"); + assertThat(plan.getSteps().get(2).getStatus()).isEqualTo("incomplete"); + } + + @Test + void testRunValidationCommand_success() throws IOException { + boolean result = executor.runValidationCommand("exit 0"); + + assertThat(result).isTrue(); + } + + @Test + void testRunValidationCommand_failure() throws IOException { + boolean result = executor.runValidationCommand("exit 1"); + + assertThat(result).isFalse(); + } + + @Test + void testRunValidationCommand_withOutput() throws IOException { + boolean result = executor.runValidationCommand("echo 'test output' && exit 0"); + + assertThat(result).isTrue(); + } + + @Test + void testExecuteNextStep_withVersion() throws IOException { + // Test execution with version field set + AiPlanStep step = createStep("Git merge from 8.6.x to 8.7.x", "smc-upgrader", "incomplete"); + step.setVersion("8.7.x"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("E"); // Execute the step + // The step should fail because it tries to execute the upgrade + // but in a test environment the git repo doesn't exist + // However, we can verify the execution works by checking logs + // This test mainly verifies no exceptions are thrown + boolean result = executor.executeNextStep(); + + assertThat(result).isTrue(); + } + + @Test + void testExecuteNextStep_checkValidation_success() throws IOException { + // Create plan with step that has validation + AiPlanStep step = createStep("Step 1", "claude", "incomplete"); + step.setValidationCommand("exit 0"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("C"); // Check validation + boolean result = executor.executeNextStep(); + + assertThat(result).isTrue(); + + // Verify plan was updated (validation passed) + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("complete"); + } + + @Test + void testExecuteNextStep_checkValidation_failure() throws IOException { + // Create plan with step that has validation + AiPlanStep step = createStep("Step 1", "claude", "incomplete"); + step.setValidationCommand("exit 1"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("C"); // Check validation + boolean result = executor.executeNextStep(); + + assertThat(result).isTrue(); + + // Verify plan was NOT updated (validation failed) + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("incomplete"); + } + + @Test + void testExecuteNextStep_manualMarkComplete() throws IOException { + // Create plan with incomplete step + AiPlanStep step = createStep("Step 1", "claude", "incomplete"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("M"); // Mark complete + boolean result = executor.executeNextStep(); + + assertThat(result).isTrue(); + + // Verify plan was updated + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("complete"); + } + + @Test + void testExecuteNextStep_exitChoice() throws IOException { + // Create plan with incomplete step + AiPlanStep step = createStep("Step 1", "smc-upgrader", "incomplete"); + + writePlanFile(Arrays.asList(step)); + + executor.setTestChoice("X"); // Exit + boolean result = executor.executeNextStep(); + + assertThat(result).isFalse(); + + // Verify plan was NOT updated + PlanDocument plan = readPlanFile(); + assertThat(plan.getSteps().get(0).getStatus()).isEqualTo("incomplete"); + } + + /** + * Helper to create a test step. + */ + private AiPlanStep createStep(String title, String tool, String status) { + AiPlanStep step = new AiPlanStep(); + step.setTitle(title); + step.setTask("Test task"); + step.setTool(tool); + step.setStatus(status); + return step; + } + + /** + * Helper to write a plan file. + */ + private void writePlanFile(List steps) throws IOException { + String markdown = MarkdownWriter.generateMarkdown(steps, "8.5.x", "8.6.x"); + File planFile = new File(tempDir, "smc-upgrader-plan.md"); + Files.write(planFile.toPath(), markdown.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Helper to read the plan file. + */ + private PlanDocument readPlanFile() throws IOException { + File planFile = new File(tempDir, "smc-upgrader-plan.md"); + return MarkdownParser.parsePlanFile(planFile); + } +} diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java index 8a10b7c..0d6921d 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvokerTest.java @@ -19,8 +19,6 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,52 +40,19 @@ void setUp() { } @Test - void testInvokeClaudeCode_createsPromptFile() throws IOException { - String prompt = "Test prompt for Claude"; - - boolean result = invoker.invokeClaudeCode(prompt); - - assertThat(result).isTrue(); - - File promptFile = new File(tempDir, ".claude-prompt.txt"); - assertThat(promptFile).exists(); - - byte[] bytes = Files.readAllBytes(promptFile.toPath()); - String content = new String(bytes, StandardCharsets.UTF_8); - assertThat(content).isEqualTo(prompt); - } - - @Test - void testInvokeClaudeCode_overwritesExistingFile() throws IOException { - String firstPrompt = "First prompt"; - String secondPrompt = "Second prompt"; - - invoker.invokeClaudeCode(firstPrompt); - invoker.invokeClaudeCode(secondPrompt); - - File promptFile = new File(tempDir, ".claude-prompt.txt"); - byte[] bytes = Files.readAllBytes(promptFile.toPath()); - String content = new String(bytes, StandardCharsets.UTF_8); - assertThat(content).isEqualTo(secondPrompt); - } - - @Test - void testCleanupPromptFile_deletesFile() throws IOException { - String prompt = "Test prompt"; - invoker.invokeClaudeCode(prompt); - - File promptFile = new File(tempDir, ".claude-prompt.txt"); - assertThat(promptFile).exists(); - - invoker.cleanupPromptFile(); + void testInvokeClaudeCode_whenClaudeNotAvailable_returnsFalse() throws IOException { + // This test verifies graceful handling when Claude is not installed + // Only run this test if Claude is actually not available + boolean available = invoker.isClaudeCodeAvailable(); - assertThat(promptFile).doesNotExist(); - } + if (!available) { + String prompt = "Test prompt for Claude"; + boolean result = invoker.invokeClaudeCode(prompt); - @Test - void testCleanupPromptFile_noErrorWhenFileDoesNotExist() throws IOException { - // Should not throw exception - invoker.cleanupPromptFile(); + // Should return false when Claude is not available + assertThat(result).isFalse(); + } + // If Claude IS available, we skip this test as we can't test execution without user interaction } @Test From b041afe30187758e25a4612332cee8b767897721 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 06:56:16 -0800 Subject: [PATCH 04/63] [PB-13614] Automatically commit changes when step completes --- .../tools/smcupgrader/GitClient.java | 5 ++++ .../tools/smcupgrader/ai/AiPlanExecutor.java | 29 +++++++++++++++++++ .../tools/smcupgrader/impl/GitClientImpl.java | 12 ++++++++ 3 files changed, 46 insertions(+) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java b/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java index f24283c..0b2dafb 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/GitClient.java @@ -134,6 +134,11 @@ public interface GitClient { */ void stage(String path); + /** + * Adds all modified, new, and deleted files to the git index. + */ + void stageAll(); + /** * Removes a file from the git index. * diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index 722153a..8830fec 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -129,6 +129,10 @@ public boolean executeNextStep() throws IOException { case "M": // Mark complete + // For Claude steps, commit all changes before marking complete + if (nextStep.isClaudeStep()) { + commitAllChanges(nextStep.getTitle()); + } stepCompleted = true; shouldExit = true; break; @@ -440,4 +444,29 @@ private void commitPlanFile(final String message) { LOGGER.debug("Committed plan file: {}", message); } + + /** + * Commit all changes in the working directory. + * + * @param message the commit message + */ + private void commitAllChanges(final String message) { + if (gitClient == null) { + LOGGER.debug("No git client available, skipping commit"); + return; + } + + try { + // Stage all changes (modified, new, and deleted files) + gitClient.stageAll(); + + // Commit all changes + gitClient.commit(message); + + LOGGER.info("Committed all changes: {}", message); + } catch (RuntimeException e) { + // Don't fail if git commit fails (e.g., signing service unavailable, nothing to commit) + LOGGER.debug("Could not commit changes to git: {}", e.getMessage()); + } + } } diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java b/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java index e67cf83..538d93f 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java @@ -310,6 +310,18 @@ public void stage(final String path) { } } + @Override + public void stageAll() { + try (Git git = new Git(repository)) { + // Stage all modified and new files + git.add().addFilepattern(".").call(); + // Stage deletions + git.add().addFilepattern(".").setUpdate(true).call(); + } catch (final GitAPIException e) { + throw new RuntimeException(e); + } + } + @Override public void unstage(final String path) { try (Git git = new Git(repository)) { From bbf32c48d3b54ed2f48345c66ae1ee7a096db613 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 07:28:48 -0800 Subject: [PATCH 05/63] [PB-13614] Changed smc-upgrader to fail if Git commands fail --- .../tools/smcupgrader/ai/AiPlanExecutor.java | 52 ++++++++----------- src/main/resources/ai-assist-config.json | 2 +- .../smcupgrader/ai/AiPlanExecutorTest.java | 13 ++++- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index 8830fec..ef9dd6b 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -59,6 +59,19 @@ public AiPlanExecutor(final File workingDir) { this.gitClient = createGitClient(workingDir); } + /** + * Package-private constructor for testing. + * + * @param workingDir the working directory + * @param gitClient the git client (for testing) + */ + AiPlanExecutor(final File workingDir, final GitClient gitClient) { + this.workingDir = workingDir; + this.claudeInvoker = new ClaudeCodeInvoker(workingDir); + this.upgradeController = new UpgradeController(workingDir, DEFAULT_UPSTREAM_REPO_URL); + this.gitClient = gitClient; + } + /** * Execute the next step in the plan. * @@ -105,7 +118,6 @@ public boolean executeNextStep() throws IOException { // Handle user choice boolean stepCompleted = false; - boolean shouldExit = false; switch (choice.toUpperCase()) { case "E": @@ -118,13 +130,11 @@ public boolean executeNextStep() throws IOException { LOGGER.error("Unknown tool: {}", nextStep.getTool()); return false; } - shouldExit = true; break; case "C": // Check validation stepCompleted = checkStepValidation(nextStep); - shouldExit = true; break; case "M": @@ -134,7 +144,6 @@ public boolean executeNextStep() throws IOException { commitAllChanges(nextStep.getTitle()); } stepCompleted = true; - shouldExit = true; break; case "X": @@ -156,10 +165,8 @@ public boolean executeNextStep() throws IOException { } // Exit after action - if (shouldExit) { - LOGGER.info(""); - LOGGER.info("Exiting. Run 'smc-upgrader --ai:continue' to continue with the next step."); - } + LOGGER.info(""); + LOGGER.info("Exiting. Run 'smc-upgrader --ai:continue' to continue with the next step."); return true; } @@ -398,14 +405,7 @@ private void savePlan(final File planFile, final PlanDocument plan) throws IOExc .orElse("step"); // Commit to git - if (gitClient != null) { - try { - commitPlanFile("Mark step complete: " + completedStepTitle); - } catch (RuntimeException e) { - // Don't fail if git commit fails (e.g., signing service unavailable) - LOGGER.debug("Could not commit plan file to git: {}", e.getMessage()); - } - } + commitPlanFile("Mark step complete: " + completedStepTitle); } /** @@ -451,22 +451,12 @@ private void commitPlanFile(final String message) { * @param message the commit message */ private void commitAllChanges(final String message) { - if (gitClient == null) { - LOGGER.debug("No git client available, skipping commit"); - return; - } - - try { - // Stage all changes (modified, new, and deleted files) - gitClient.stageAll(); + // Stage all changes (modified, new, and deleted files) + gitClient.stageAll(); - // Commit all changes - gitClient.commit(message); + // Commit all changes + gitClient.commit(message); - LOGGER.info("Committed all changes: {}", message); - } catch (RuntimeException e) { - // Don't fail if git commit fails (e.g., signing service unavailable, nothing to commit) - LOGGER.debug("Could not commit changes to git: {}", e.getMessage()); - } + LOGGER.info("Committed all changes: {}", message); } } diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 0519bd5..8f5c585 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -20,7 +20,7 @@ "tool": "claude", "validationCommand": "git diff --check", "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. Ensure that you do not overwrite important \"ours\" changes with \"theirs\". Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." }, { "title": "Fix compilation failures", diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java index 6a6971c..1c845fb 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java @@ -16,6 +16,8 @@ package com.elasticpath.tools.smcupgrader.ai; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import java.io.File; @@ -29,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import com.elasticpath.tools.smcupgrader.GitClient; import com.elasticpath.tools.smcupgrader.UpgradeController; /** @@ -40,12 +43,20 @@ class AiPlanExecutorTest { File tempDir; private UpgradeController upgradeController; + private GitClient gitClient; private AiPlanExecutor executor; @BeforeEach void setUp() { upgradeController = mock(UpgradeController.class); - executor = new AiPlanExecutor(tempDir); + gitClient = mock(GitClient.class); + + // Configure mock to accept git operations without doing anything + doNothing().when(gitClient).stage(anyString()); + doNothing().when(gitClient).stageAll(); + doNothing().when(gitClient).commit(anyString()); + + executor = new AiPlanExecutor(tempDir, gitClient); } @Test From b884a81ddecc4d46b402e9200630c6592b90b652 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 07:52:53 -0800 Subject: [PATCH 06/63] [PB-13614] Added commitAllChangesOnCompletion and commitPlanOnCompletion flags --- .../tools/smcupgrader/ai/AiPlanExecutor.java | 29 +++++++------- .../tools/smcupgrader/ai/AiPlanStep.java | 38 +++++++++++++++++++ .../tools/smcupgrader/ai/MarkdownParser.java | 16 ++++++++ .../tools/smcupgrader/ai/MarkdownWriter.java | 2 + src/main/resources/ai-assist-config.json | 22 +++++++++++ .../smcupgrader/ai/AiPlanExecutorTest.java | 3 ++ 6 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index ef9dd6b..9f57b57 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -139,8 +139,8 @@ public boolean executeNextStep() throws IOException { case "M": // Mark complete - // For Claude steps, commit all changes before marking complete - if (nextStep.isClaudeStep()) { + // Commit all changes if configured to do so + if (nextStep.isCommitAllChangesOnCompletion()) { commitAllChanges(nextStep.getTitle()); } stepCompleted = true; @@ -159,7 +159,7 @@ public boolean executeNextStep() throws IOException { // Save updated plan if step was marked complete if (stepCompleted) { nextStep.setStatus("complete"); - savePlan(planFile, plan); + savePlan(planFile, plan, nextStep); LOGGER.info(""); LOGGER.info("Step marked as complete."); } @@ -389,23 +389,19 @@ boolean runValidationCommand(final String command) throws IOException { /** * Save the plan back to the file. * - * @param planFile the plan file - * @param plan the plan document + * @param planFile the plan file + * @param plan the plan document + * @param completedStep the step that was just completed * @throws IOException if an error occurs */ - private void savePlan(final File planFile, final PlanDocument plan) throws IOException { + private void savePlan(final File planFile, final PlanDocument plan, final AiPlanStep completedStep) throws IOException { String markdown = MarkdownWriter.generateMarkdown(plan.getSteps(), plan.getFromVersion(), plan.getToVersion()); Files.write(planFile.toPath(), markdown.getBytes(StandardCharsets.UTF_8)); - // Find the completed step to include in commit message - String completedStepTitle = plan.getSteps().stream() - .filter(step -> "complete".equals(step.getStatus())) - .reduce((first, second) -> second) // Get the last completed step - .map(AiPlanStep::getTitle) - .orElse("step"); - - // Commit to git - commitPlanFile("Mark step complete: " + completedStepTitle); + // Commit to git if configured to do so + if (completedStep.isCommitPlanOnCompletion()) { + commitPlanFile("Mark step complete: " + completedStep.getTitle()); + } } /** @@ -454,6 +450,9 @@ private void commitAllChanges(final String message) { // Stage all changes (modified, new, and deleted files) gitClient.stageAll(); + // Unstage the plan file - it should be committed separately + gitClient.unstage(PLAN_FILE_NAME); + // Commit all changes gitClient.commit(message); diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java index 2bb4bc4..ab8fedb 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java @@ -26,6 +26,8 @@ public class AiPlanStep { private String validationCommand; private String status; private String prompt; + private boolean commitAllChangesOnCompletion; + private boolean commitPlanOnCompletion; /** * Default constructor for JSON deserialization. @@ -179,6 +181,42 @@ public void setPrompt(final String prompt) { this.prompt = prompt; } + /** + * Check if all changes should be committed when this step is completed. + * + * @return true if all changes should be committed on completion + */ + public boolean isCommitAllChangesOnCompletion() { + return commitAllChangesOnCompletion; + } + + /** + * Set whether all changes should be committed when this step is completed. + * + * @param commitAllChangesOnCompletion true to commit all changes on completion + */ + public void setCommitAllChangesOnCompletion(final boolean commitAllChangesOnCompletion) { + this.commitAllChangesOnCompletion = commitAllChangesOnCompletion; + } + + /** + * Check if the plan file should be committed when this step is completed. + * + * @return true if the plan file should be committed on completion + */ + public boolean isCommitPlanOnCompletion() { + return commitPlanOnCompletion; + } + + /** + * Set whether the plan file should be committed when this step is completed. + * + * @param commitPlanOnCompletion true to commit the plan file on completion + */ + public void setCommitPlanOnCompletion(final boolean commitPlanOnCompletion) { + this.commitPlanOnCompletion = commitPlanOnCompletion; + } + /** * Check if this is a claude tool step. * diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java index 89af507..42b58df 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java @@ -41,6 +41,8 @@ public final class MarkdownParser { private static final Pattern TOOL_PATTERN = Pattern.compile("Tool:\\s*(.+)"); private static final Pattern VERSION_PATTERN = Pattern.compile("Version:\\s*(.+)"); private static final Pattern VALIDATION_PATTERN = Pattern.compile("Validation command:\\s*(.+)"); + private static final Pattern COMMIT_ALL_CHANGES_PATTERN = Pattern.compile("Commit all changes on completion:\\s*(.+)"); + private static final Pattern COMMIT_PLAN_PATTERN = Pattern.compile("Commit plan on completion:\\s*(.+)"); private static final Pattern STATUS_PATTERN = Pattern.compile("Status:\\s*(.+)"); private MarkdownParser() { @@ -199,6 +201,20 @@ private static boolean parseMetadata(final String text, final AiPlanStep current continue; } + matcher = COMMIT_ALL_CHANGES_PATTERN.matcher(line); + if (matcher.find()) { + currentStep.setCommitAllChangesOnCompletion(Boolean.parseBoolean(matcher.group(1).trim())); + foundMetadata = true; + continue; + } + + matcher = COMMIT_PLAN_PATTERN.matcher(line); + if (matcher.find()) { + currentStep.setCommitPlanOnCompletion(Boolean.parseBoolean(matcher.group(1).trim())); + foundMetadata = true; + continue; + } + matcher = STATUS_PATTERN.matcher(line); if (matcher.find()) { currentStep.setStatus(matcher.group(1).trim()); diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java index 39e51d3..514cd1d 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java @@ -82,6 +82,8 @@ private static void appendStep(final StringBuilder markdown, final AiPlanStep st markdown.append("Validation command: ").append(step.getValidationCommand()).append(" \n"); } + markdown.append("Commit all changes on completion: ").append(step.isCommitAllChangesOnCompletion()).append(" \n"); + markdown.append("Commit plan on completion: ").append(step.isCommitPlanOnCompletion()).append(" \n"); markdown.append("Status: ").append(step.getStatus()).append("\n\n"); // Prompt for claude steps diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 8f5c585..6782ab6 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -12,6 +12,8 @@ "title": "Git merge from {FROM_VERSION} to {TO_VERSION}", "task": "Upgrade source from {FROM_VERSION} to {TO_VERSION}", "tool": "smc-upgrader", + "commitAllChangesOnCompletion": false, + "commitPlanOnCompletion": false, "status": "incomplete" }, { @@ -19,6 +21,8 @@ "task": "Resolve remaining Git merge conflicts", "tool": "claude", "validationCommand": "git diff --check", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. Ensure that you do not overwrite important \"ours\" changes with \"theirs\". Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." }, @@ -27,6 +31,8 @@ "task": "Resolve all compilation issues", "tool": "claude", "validationCommand": "mvn clean install -DskipAllTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -35,6 +41,8 @@ "task": "Resolve all static analysis, unit test, and integration test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipSlowTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -43,6 +51,8 @@ "task": "Resolve database reset failures", "tool": "claude", "validationCommand": "mvn clean install -Dreset-db -f extensions/database", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -51,6 +61,8 @@ "task": "Fix Integration Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/integration/ext-integration-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -59,6 +71,8 @@ "task": "Fix Batch Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/batch/ext-batch-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -67,6 +81,8 @@ "task": "Fix Search Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/search/ext-search-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -75,6 +91,8 @@ "task": "Fix Cortex startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -83,6 +101,8 @@ "task": "Fix Commerce Manager startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." }, @@ -91,6 +111,8 @@ "task": "Resolve all Cucumber and Selenium test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipTests=true -DskipITests=true", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." } diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java index 1c845fb..a4e0643 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java @@ -54,6 +54,7 @@ void setUp() { // Configure mock to accept git operations without doing anything doNothing().when(gitClient).stage(anyString()); doNothing().when(gitClient).stageAll(); + doNothing().when(gitClient).unstage(anyString()); doNothing().when(gitClient).commit(anyString()); executor = new AiPlanExecutor(tempDir, gitClient); @@ -301,6 +302,8 @@ private AiPlanStep createStep(String title, String tool, String status) { step.setTask("Test task"); step.setTool(tool); step.setStatus(status); + step.setCommitAllChangesOnCompletion(true); + step.setCommitPlanOnCompletion(true); return step; } From 77dd817be677dfc7c15235a801a888864684f281 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 07:53:58 -0800 Subject: [PATCH 07/63] [PB-13614] Removed unnecessary spaces at the end of Markdown lines --- .../tools/smcupgrader/ai/MarkdownWriter.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java index 514cd1d..3bbfaa2 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java @@ -40,8 +40,8 @@ public static String generateMarkdown(final List steps, final String // Header markdown.append("# SMC Upgrader - AI Assist Plan\n\n"); - markdown.append("Upgrade from: ").append(fromVersion).append(" \n"); - markdown.append("Upgrade to: ").append(toVersion).append(" \n"); + markdown.append("Upgrade from: ").append(fromVersion).append("\n"); + markdown.append("Upgrade to: ").append(toVersion).append("\n"); markdown.append("Generated: ").append(LocalDate.now()).append("\n\n"); markdown.append("---\n\n"); @@ -71,19 +71,19 @@ private static void appendStep(final StringBuilder markdown, final AiPlanStep st markdown.append("## ").append(step.getTitle()).append("\n\n"); // Metadata - markdown.append("Task: ").append(step.getTask()).append(" \n"); - markdown.append("Tool: ").append(step.getTool()).append(" \n"); + markdown.append("Task: ").append(step.getTask()).append("\n"); + markdown.append("Tool: ").append(step.getTool()).append("\n"); if (step.getVersion() != null && !step.getVersion().trim().isEmpty()) { - markdown.append("Version: ").append(step.getVersion()).append(" \n"); + markdown.append("Version: ").append(step.getVersion()).append("\n"); } if (step.hasValidationCommand()) { - markdown.append("Validation command: ").append(step.getValidationCommand()).append(" \n"); + markdown.append("Validation command: ").append(step.getValidationCommand()).append("\n"); } - markdown.append("Commit all changes on completion: ").append(step.isCommitAllChangesOnCompletion()).append(" \n"); - markdown.append("Commit plan on completion: ").append(step.isCommitPlanOnCompletion()).append(" \n"); + markdown.append("Commit all changes on completion: ").append(step.isCommitAllChangesOnCompletion()).append("\n"); + markdown.append("Commit plan on completion: ").append(step.isCommitPlanOnCompletion()).append("\n"); markdown.append("Status: ").append(step.getStatus()).append("\n\n"); // Prompt for claude steps From 5a38505810eddbf4e3208b797fbe53ed7fa72906 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 11:13:21 -0800 Subject: [PATCH 08/63] [PB-13614] Fixed test issues --- .../tools/smcupgrader/ai/AiPlanExecutor.java | 9 ++++---- .../tools/smcupgrader/ai/MarkdownParser.java | 3 +++ .../smcupgrader/ai/AiPlanExecutorTest.java | 21 ++++++++----------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index 9f57b57..510ef3b 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -62,12 +62,13 @@ public AiPlanExecutor(final File workingDir) { /** * Package-private constructor for testing. * - * @param workingDir the working directory - * @param gitClient the git client (for testing) + * @param workingDir the working directory + * @param gitClient the git client (for testing) + * @param claudeInvoker the claude invoker (for testing) */ - AiPlanExecutor(final File workingDir, final GitClient gitClient) { + AiPlanExecutor(final File workingDir, final GitClient gitClient, final ClaudeCodeInvoker claudeInvoker) { this.workingDir = workingDir; - this.claudeInvoker = new ClaudeCodeInvoker(workingDir); + this.claudeInvoker = claudeInvoker; this.upgradeController = new UpgradeController(workingDir, DEFAULT_UPSTREAM_REPO_URL); this.gitClient = gitClient; } diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java index 42b58df..b715417 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java @@ -26,6 +26,7 @@ import org.commonmark.node.Heading; import org.commonmark.node.Node; import org.commonmark.node.Paragraph; +import org.commonmark.node.SoftLineBreak; import org.commonmark.node.Text; import org.commonmark.parser.Parser; @@ -248,6 +249,8 @@ private static void extractTextRecursive(final Node node, final StringBuilder te text.append(((Text) node).getLiteral()); } else if (node instanceof HardLineBreak) { text.append("\n"); + } else if (node instanceof SoftLineBreak) { + text.append("\n"); } Node child = node.getFirstChild(); diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java index a4e0643..c86796a 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java @@ -19,6 +19,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; @@ -32,7 +33,6 @@ import org.junit.jupiter.api.io.TempDir; import com.elasticpath.tools.smcupgrader.GitClient; -import com.elasticpath.tools.smcupgrader.UpgradeController; /** * Tests for {@link AiPlanExecutor}. @@ -42,14 +42,14 @@ class AiPlanExecutorTest { @TempDir File tempDir; - private UpgradeController upgradeController; private GitClient gitClient; + private ClaudeCodeInvoker claudeInvoker; private AiPlanExecutor executor; @BeforeEach - void setUp() { - upgradeController = mock(UpgradeController.class); + void setUp() throws IOException { gitClient = mock(GitClient.class); + claudeInvoker = mock(ClaudeCodeInvoker.class); // Configure mock to accept git operations without doing anything doNothing().when(gitClient).stage(anyString()); @@ -57,7 +57,11 @@ void setUp() { doNothing().when(gitClient).unstage(anyString()); doNothing().when(gitClient).commit(anyString()); - executor = new AiPlanExecutor(tempDir, gitClient); + // Configure mock Claude invoker to always return false (Claude not available) + when(claudeInvoker.isClaudeCodeAvailable()).thenReturn(false); + when(claudeInvoker.invokeClaudeCode(anyString())).thenReturn(false); + + executor = new AiPlanExecutor(tempDir, gitClient, claudeInvoker); } @Test @@ -144,13 +148,6 @@ void testExecuteNextStep_claudeStep() throws IOException { writePlanFile(Arrays.asList(step)); - // Only run this test if Claude is NOT available (to avoid interactive blocking) - ClaudeCodeInvoker invoker = new ClaudeCodeInvoker(tempDir); - if (invoker.isClaudeCodeAvailable()) { - // Skip test - would block on interactive prompt - return; - } - executor.setTestChoice("E"); // Execute the step boolean result = executor.executeNextStep(); From 5dabfdea49b9d1f868d4c11f997c0b0db55291ac Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 11:24:06 -0800 Subject: [PATCH 09/63] [PB-13614] Disabled working directory check for ai mode, which seems unreliable --- .../com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index 510ef3b..f602102 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -266,7 +266,7 @@ private boolean executeSmcUpgraderStep(final AiPlanStep step) throws IOException // Execute the upgrade with standard options upgradeController.performUpgrade( targetVersion, - true, // doCleanWorkingDirectoryCheck + false, // doCleanWorkingDirectoryCheck true, // doRevertPatches true, // doMerge true, // doConflictResolution From eab8f574c186f89199127bc8610a53739caa4ac7 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 11:24:22 -0800 Subject: [PATCH 10/63] [PB_13614] Improved prompt for resolve merge conflicts step --- src/main/resources/ai-assist-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 6782ab6..01ae2dc 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -24,7 +24,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. Ensure that you do not overwrite important \"ours\" changes with \"theirs\". Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. IMPORTANT: Ensure that you do not overwrite \"ours\" changes with \"theirs\" unless the differences don't affect functionality. Assume that files deleted in \"theirs\" were removed intentionally and accept the deletion. Accept the new \"theirs\" versions for all dependency upgrades. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." }, { "title": "Fix compilation failures", From 906a4ac00701fc5f673932837a5a47a77df7daa1 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 13:05:09 -0800 Subject: [PATCH 11/63] [PB-13614] Claude command improvements --- .../tools/smcupgrader/ai/AiPlanExecutor.java | 8 +++- src/main/resources/ai-assist-config.json | 46 +++++++++++++------ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index f602102..b8d7485 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -336,7 +336,13 @@ private boolean executeClaudeStep(final AiPlanStep step) throws IOException { // Invoke Claude with the prompt if (step.getPrompt() != null && !step.getPrompt().trim().isEmpty()) { - boolean claudeSuccess = claudeInvoker.invokeClaudeCode(step.getPrompt()); + // Build the full prompt including validation command if present + String fullPrompt = step.getPrompt(); + if (step.hasValidationCommand()) { + fullPrompt += "\n\nValidation command: " + step.getValidationCommand(); + } + + boolean claudeSuccess = claudeInvoker.invokeClaudeCode(fullPrompt); LOGGER.info(""); if (claudeSuccess) { diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 01ae2dc..681c2e3 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -34,27 +34,47 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { - "title": "Fix static analysis, unit test, and integration test failures", - "task": "Resolve all static analysis, unit test, and integration test failures", + "title": "Fix static analysis failures", + "task": "Resolve all static analysis failures", "tool": "claude", - "validationCommand": "mvn clean install -DskipSlowTests", + "validationCommand": "mvn clean install -DskipTests -DskipITests -DskipCucumberTests -DskipFitTests", "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { - "title": "Fix Local Database Reset Failures", + "title": "Fix unit test failures", + "task": "Resolve all unit test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipITests -DskipCucumberTests -DskipFitTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + }, + { + "title": "Fix integration test failures", + "task": "Resolve all integration test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipCucumberTests -DskipFitTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + }, + { + "title": "Fix local database reset failures", "task": "Resolve database reset failures", "tool": "claude", "validationCommand": "mvn clean install -Dreset-db -f extensions/database", "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { "title": "Fix Integration Server startup issues", @@ -64,7 +84,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { "title": "Fix Batch Server startup issues", @@ -74,7 +94,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { "title": "Fix Search Server startup issues", @@ -84,7 +104,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { "title": "Fix Cortex startup issues", @@ -94,7 +114,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { "title": "Fix Commerce Manager startup issues", @@ -104,7 +124,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." }, { "title": "Fix Cucumber and Selenium test failures", @@ -114,7 +134,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command above and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." } ] } From 78900cd8dd64bb2f2c100b8da752e5de9d4b55f2 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 13:33:36 -0800 Subject: [PATCH 12/63] [PB-13614] Fixed minor step completion bug --- .../elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index b8d7485..7dd241e 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -140,10 +140,6 @@ public boolean executeNextStep() throws IOException { case "M": // Mark complete - // Commit all changes if configured to do so - if (nextStep.isCommitAllChangesOnCompletion()) { - commitAllChanges(nextStep.getTitle()); - } stepCompleted = true; break; @@ -159,6 +155,11 @@ public boolean executeNextStep() throws IOException { // Save updated plan if step was marked complete if (stepCompleted) { + // Commit all changes if configured to do so + if (nextStep.isCommitAllChangesOnCompletion()) { + commitAllChanges(nextStep.getTitle()); + } + nextStep.setStatus("complete"); savePlan(planFile, plan, nextStep); LOGGER.info(""); From c94b78649ee35576f913d02e741f355c0648e86b Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 13:41:35 -0800 Subject: [PATCH 13/63] [PB-13614] Fixed bugs in AI plan generator --- .../elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java | 1 - .../elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index 7dd241e..75da367 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -232,7 +232,6 @@ private boolean checkStepValidation(final AiPlanStep step) throws IOException { } LOGGER.info("Running validation command: {}", step.getValidationCommand()); - LOGGER.info(""); boolean validationPassed = runValidationCommand(step.getValidationCommand()); diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java index 49548be..6f26101 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java @@ -190,11 +190,9 @@ List expandStepsForVersions(final List versionSequence) { step.setTask(substituteVariables(template.getTask(), fromVersion, toVersion)); step.setTool(template.getTool()); step.setStatus(template.getStatus()); - - // Set version for smc-upgrader steps - if ("smc-upgrader".equals(template.getTool())) { - step.setVersion(toVersion); - } + step.setCommitPlanOnCompletion(template.isCommitPlanOnCompletion()); + step.setCommitAllChangesOnCompletion(template.isCommitAllChangesOnCompletion()); + step.setVersion(toVersion); if (template.getValidationCommand() != null) { step.setValidationCommand(substituteVariables(template.getValidationCommand(), fromVersion, toVersion)); From 34b574a5e4dc059896b2ee4176ebf1c0baa7492b Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sun, 30 Nov 2025 20:47:24 -0800 Subject: [PATCH 14/63] [PB-13614] More prompt improvements --- src/main/resources/ai-assist-config.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 681c2e3..67a2139 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -34,7 +34,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix static analysis failures", @@ -44,7 +44,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix unit test failures", @@ -54,7 +54,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix integration test failures", @@ -64,7 +64,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix local database reset failures", @@ -74,7 +74,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix Integration Server startup issues", @@ -84,7 +84,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix Batch Server startup issues", @@ -94,7 +94,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix Search Server startup issues", @@ -104,7 +104,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix Cortex startup issues", @@ -114,7 +114,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix Commerce Manager startup issues", @@ -124,7 +124,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { "title": "Fix Cucumber and Selenium test failures", @@ -134,7 +134,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." } ] } From 7e1a0a5e71a6621e5a156411a00d3a85867fd2b8 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Mon, 1 Dec 2025 14:17:46 -0800 Subject: [PATCH 15/63] [PB-13614] Added list of steps to output --- .../tools/smcupgrader/ai/AiPlanExecutor.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index 75da367..569a8bf 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -22,6 +22,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.List; import java.util.Scanner; import org.eclipse.jgit.lib.Repository; @@ -94,6 +95,9 @@ public boolean executeNextStep() throws IOException { // Parse the plan PlanDocument plan = MarkdownParser.parsePlanFile(planFile); + // Show all steps with completion status + displayStepList(plan); + // Find next incomplete step AiPlanStep nextStep = plan.findNextIncompleteStep(); @@ -111,7 +115,6 @@ public boolean executeNextStep() throws IOException { if (nextStep.hasValidationCommand()) { LOGGER.info(" Validation command: {}", nextStep.getValidationCommand()); } - LOGGER.info(" Status: {}", nextStep.getStatus()); LOGGER.info(""); // Show menu and get user choice @@ -182,6 +185,34 @@ void setTestChoice(final String choice) { this.testChoice = choice; } + /** + * Display a numbered list of all steps in the plan with completion status. + * + * @param plan the plan document + */ + private void displayStepList(final PlanDocument plan) { + LOGGER.info("Upgrade Steps:"); + LOGGER.info(""); + + List steps = plan.getSteps(); + for (int i = 0; i < steps.size(); i++) { + AiPlanStep step = steps.get(i); + String stepNumber = (i + 1) + ". "; + String title = step.getTitle(); + + if ("complete".equals(step.getStatus())) { + // Use ANSI strikethrough for completed steps + LOGGER.info("{}\u001B[9m{}\u001B[0m", stepNumber, title); + } else { + LOGGER.info("{}{}", stepNumber, title); + } + } + + LOGGER.info(""); + LOGGER.info("Progress: {} of {} steps completed", plan.countCompletedSteps(), plan.getTotalSteps()); + LOGGER.info(""); + } + /** * Prompt the user for their choice. * From dd45ac76bb27c1763aea8b795757b62d35fcc9d7 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 07:21:00 -0800 Subject: [PATCH 16/63] [PB-13614] Removed 8.8.x version and added 8.7.x-pre-jakarta version --- src/main/resources/ai-assist-config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 67a2139..67950a5 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -4,8 +4,8 @@ "8.4.x", "8.5.x", "8.6.x", - "8.7.x", - "8.8.x" + "8.7.x-pre-jakarta", + "8.7.x" ], "defaultSteps": [ { From d80aeb327760f9735ab5d5630364f771f3b87561 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 07:21:31 -0800 Subject: [PATCH 17/63] [PB-13614] Added skip permissions flag to Claude --- .../com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java index 9f56cbf..7f723cb 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/ClaudeCodeInvoker.java @@ -70,7 +70,7 @@ public boolean invokeClaudeCode(final String prompt) throws IOException { // Execute Claude Code through shell to ensure proper terminal allocation // Use single quotes for safer shell escaping (only need to escape single quotes themselves) String escapedPrompt = prompt.replace("'", "'\\''"); - String command = "claude '" + escapedPrompt + "'"; + String command = "claude --dangerously-skip-permissions '" + escapedPrompt + "'"; LOGGER.debug("Shell command: {}", command.substring(0, Math.min(COMMAND_MAX_DISPLAY_LENGTH, command.length())) + "..."); From a52bf241c91e8657d3d07d22ada61d32081f4c10 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 07:27:38 -0800 Subject: [PATCH 18/63] [PB-13614] Prompt improvement to prevent ext-core/pom.xml from being overwritten --- src/main/resources/ai-assist-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 67950a5..9b29cbc 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -24,7 +24,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. IMPORTANT: Ensure that you do not overwrite \"ours\" changes with \"theirs\" unless the differences don't affect functionality. Assume that files deleted in \"theirs\" were removed intentionally and accept the deletion. Accept the new \"theirs\" versions for all dependency upgrades. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." + "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. IMPORTANT: Ensure that you do not overwrite \"ours\" changes with \"theirs\" unless the differences don't affect functionality. Make sure to check each file manually - don't simply replace with \"theirs\" unless the changes are whitespace or no longer needed due to the platform changes. Assume that files deleted in \"theirs\" were removed intentionally and accept the deletion. Accept the new \"theirs\" versions for all dependency upgrades. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." }, { "title": "Fix compilation failures", From 893b9ea75b7d004bacf09102f1a47994beaaeac1 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 07:42:58 -0800 Subject: [PATCH 19/63] [PB-13614] Added support for upgrading to latest patches --- .../tools/smcupgrader/ai/AiPlanGenerator.java | 9 +- .../tools/smcupgrader/ai/UpgradePath.java | 60 ++++++-- src/main/resources/ai-assist-config.json | 132 +++++++++++++++++- .../smcupgrader/ai/AiPlanGeneratorTest.java | 10 +- .../tools/smcupgrader/ai/UpgradePathTest.java | 29 ++-- 5 files changed, 208 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java index 6f26101..05f8941 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java @@ -91,7 +91,7 @@ public boolean generatePlan(final String targetVersion, final File workingDir) t if (!upgradePath.validateVersionPath(currentVersion, targetVersion)) { throw new IllegalArgumentException( "Cannot upgrade from " + currentVersion + " to " + targetVersion - + ". Current version must be earlier than target version."); + + ". Current version must not be later than target version."); } // Calculate version sequence @@ -183,8 +183,13 @@ List expandStepsForVersions(final List versionSequence) { String fromVersion = versionSequence.get(i); String toVersion = versionSequence.get(i + 1); + // Determine which steps to use based on whether this is an upgrade or patch consumption + List stepsToUse = fromVersion.equals(toVersion) + ? upgradePath.getPatchConsumptionSteps() + : upgradePath.getUpgradeSteps(); + // Create steps for this transition - for (AiPlanStep template : upgradePath.getDefaultSteps()) { + for (AiPlanStep template : stepsToUse) { AiPlanStep step = new AiPlanStep(); step.setTitle(substituteVariables(template.getTitle(), fromVersion, toVersion)); step.setTask(substituteVariables(template.getTask(), fromVersion, toVersion)); diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java index e76c6f3..d68d23f 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/UpgradePath.java @@ -29,7 +29,8 @@ */ public class UpgradePath { private List versions; - private List defaultSteps; + private List upgradeSteps; + private List patchConsumptionSteps; /** * Default constructor for JSON deserialization. @@ -40,12 +41,14 @@ public UpgradePath() { /** * Constructor. * - * @param versions list of valid version strings - * @param defaultSteps list of default step templates + * @param versions list of valid version strings + * @param upgradeSteps list of upgrade step templates + * @param patchConsumptionSteps list of patch consumption step templates */ - public UpgradePath(final List versions, final List defaultSteps) { + public UpgradePath(final List versions, final List upgradeSteps, final List patchConsumptionSteps) { this.versions = versions; - this.defaultSteps = defaultSteps; + this.upgradeSteps = upgradeSteps; + this.patchConsumptionSteps = patchConsumptionSteps; } /** @@ -85,9 +88,18 @@ public List getIntermediateVersions(final String fromVersion, final Stri if (toIndex == -1) { throw new IllegalArgumentException("Invalid to version: " + toVersion); } - if (fromIndex >= toIndex) { + if (fromIndex > toIndex) { throw new IllegalArgumentException( - "From version must be earlier than to version. From: " + fromVersion + ", To: " + toVersion); + "From version must not be later than to version. From: " + fromVersion + ", To: " + toVersion); + } + + // Special case: if from equals to (patch consumption), return a sequence with the version repeated + // This creates one transition from version to version for applying patches + if (fromIndex == toIndex) { + List result = new ArrayList<>(); + result.add(fromVersion); + result.add(toVersion); + return result; } // Return all versions from fromIndex to toIndex (inclusive) @@ -129,21 +141,39 @@ public void setVersions(final List versions) { } /** - * Get the list of default step templates. + * Get the list of upgrade step templates. + * + * @return the upgrade steps + */ + public List getUpgradeSteps() { + return upgradeSteps; + } + + /** + * Set the list of upgrade step templates. + * + * @param upgradeSteps the upgrade steps + */ + public void setUpgradeSteps(final List upgradeSteps) { + this.upgradeSteps = upgradeSteps; + } + + /** + * Get the list of patch consumption step templates. * - * @return the default steps + * @return the patch consumption steps */ - public List getDefaultSteps() { - return defaultSteps; + public List getPatchConsumptionSteps() { + return patchConsumptionSteps; } /** - * Set the list of default step templates. + * Set the list of patch consumption step templates. * - * @param defaultSteps the default steps + * @param patchConsumptionSteps the patch consumption steps */ - public void setDefaultSteps(final List defaultSteps) { - this.defaultSteps = defaultSteps; + public void setPatchConsumptionSteps(final List patchConsumptionSteps) { + this.patchConsumptionSteps = patchConsumptionSteps; } /** diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 9b29cbc..86b36ec 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -7,7 +7,7 @@ "8.7.x-pre-jakarta", "8.7.x" ], - "defaultSteps": [ + "upgradeSteps": [ { "title": "Git merge from {FROM_VERSION} to {TO_VERSION}", "task": "Upgrade source from {FROM_VERSION} to {TO_VERSION}", @@ -136,5 +136,135 @@ "status": "incomplete", "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any Cucumber or Selenium test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." } + ], + "patchConsumptionSteps": [ + { + "title": "Git merge latest patches", + "task": "Upgrade source with latest patches", + "tool": "smc-upgrader", + "commitAllChangesOnCompletion": false, + "commitPlanOnCompletion": false, + "status": "incomplete" + }, + { + "title": "Resolve merge conflicts", + "task": "Resolve remaining Git merge conflicts", + "tool": "claude", + "validationCommand": "git diff --check", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. IMPORTANT: Ensure that you do not overwrite \"ours\" changes with \"theirs\" unless the differences don't affect functionality. Make sure to check each file manually - don't simply replace with \"theirs\" unless the changes are whitespace or no longer needed due to the platform changes. Assume that files deleted in \"theirs\" were removed intentionally and accept the deletion. Accept the new \"theirs\" versions for all dependency upgrades. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." + }, + { + "title": "Fix compilation failures", + "task": "Resolve all compilation issues", + "tool": "claude", + "validationCommand": "mvn clean install -DskipAllTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any compilation issues. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix static analysis failures", + "task": "Resolve all static analysis failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipTests -DskipITests -DskipCucumberTests -DskipFitTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix unit test failures", + "task": "Resolve all unit test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipITests -DskipCucumberTests -DskipFitTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix integration test failures", + "task": "Resolve all integration test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipCucumberTests -DskipFitTests", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix local database reset failures", + "task": "Resolve database reset failures", + "tool": "claude", + "validationCommand": "mvn clean install -Dreset-db -f extensions/database", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix Integration Server startup issues", + "task": "Fix Integration Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/integration/ext-integration-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix Batch Server startup issues", + "task": "Fix Batch Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/batch/ext-batch-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix Search Server startup issues", + "task": "Fix Search Server startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/search/ext-search-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix Cortex startup issues", + "task": "Fix Cortex startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix Commerce Manager startup issues", + "task": "Fix Commerce Manager startup issues", + "tool": "claude", + "validationCommand": "mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + }, + { + "title": "Fix Cucumber and Selenium test failures", + "task": "Resolve all Cucumber and Selenium test failures", + "tool": "claude", + "validationCommand": "mvn clean install -DskipTests=true -DskipITests=true", + "commitAllChangesOnCompletion": true, + "commitPlanOnCompletion": true, + "status": "incomplete", + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + } ] } diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java index 7248d90..3873800 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java @@ -53,11 +53,15 @@ void setUp() { // Create test upgrade path List versions = Arrays.asList("8.5.x", "8.6.x", "8.7.x", "8.8.x"); - List defaultSteps = Arrays.asList( + List upgradeSteps = Arrays.asList( createStep("Git merge from {FROM_VERSION} to {TO_VERSION}", "smc-upgrader", null), createStep("Resolve {TO_VERSION} merge conflicts", "claude", "git diff --check") ); - upgradePath = new UpgradePath(versions, defaultSteps); + List patchSteps = Arrays.asList( + createStep("Git merge latest patches", "smc-upgrader", null), + createStep("Resolve merge conflicts", "claude", "git diff --check") + ); + upgradePath = new UpgradePath(versions, upgradeSteps, patchSteps); generator = new AiPlanGenerator(upgradePath, upgradeController); } @@ -122,7 +126,7 @@ void testGeneratePlan_downgradePath() { assertThatThrownBy(() -> generator.generatePlan("8.5.x", tempDir)) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Current version must be earlier than target version"); + .hasMessageContaining("Current version must not be later than target version"); } @Test diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java index 645e42a..9846e7c 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/UpgradePathTest.java @@ -35,7 +35,8 @@ void testLoadFromResource() throws IOException { assertThat(upgradePath).isNotNull(); assertThat(upgradePath.getVersions()).isNotEmpty(); - assertThat(upgradePath.getDefaultSteps()).isNotEmpty(); + assertThat(upgradePath.getUpgradeSteps()).isNotEmpty(); + assertThat(upgradePath.getPatchConsumptionSteps()).isNotEmpty(); } @Test @@ -89,16 +90,17 @@ void testGetIntermediateVersions_fromGreaterThanTo() { assertThatThrownBy(() -> upgradePath.getIntermediateVersions("8.7.x", "8.5.x")) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("From version must be earlier than to version"); + .hasMessageContaining("From version must not be later than to version"); } @Test void testGetIntermediateVersions_fromEqualsTo() { UpgradePath upgradePath = createTestUpgradePath(); - assertThatThrownBy(() -> upgradePath.getIntermediateVersions("8.6.x", "8.6.x")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("From version must be earlier than to version"); + List intermediates = upgradePath.getIntermediateVersions("8.6.x", "8.6.x"); + + // For patch consumption, from can equal to + assertThat(intermediates).containsExactly("8.6.x", "8.6.x"); } @Test @@ -146,14 +148,19 @@ void testIsValidVersion_invalidVersion() { } @Test - void testDefaultStepsLoaded() throws IOException { + void testUpgradeStepsLoaded() throws IOException { UpgradePath upgradePath = UpgradePath.loadFromResource(); - List steps = upgradePath.getDefaultSteps(); + List upgradeSteps = upgradePath.getUpgradeSteps(); + List patchSteps = upgradePath.getPatchConsumptionSteps(); + + assertThat(upgradeSteps).isNotEmpty(); + assertThat(upgradeSteps).anyMatch(step -> "smc-upgrader".equals(step.getTool())); + assertThat(upgradeSteps).anyMatch(step -> "claude".equals(step.getTool())); - assertThat(steps).isNotEmpty(); - assertThat(steps).anyMatch(step -> "smc-upgrader".equals(step.getTool())); - assertThat(steps).anyMatch(step -> "claude".equals(step.getTool())); + assertThat(patchSteps).isNotEmpty(); + assertThat(patchSteps).anyMatch(step -> "smc-upgrader".equals(step.getTool())); + assertThat(patchSteps).anyMatch(step -> "claude".equals(step.getTool())); } /** @@ -163,6 +170,6 @@ void testDefaultStepsLoaded() throws IOException { */ private UpgradePath createTestUpgradePath() { List versions = Arrays.asList("8.3.x", "8.4.x", "8.5.x", "8.6.x", "8.7.x", "8.8.x"); - return new UpgradePath(versions, null); + return new UpgradePath(versions, null, null); } } From 6526650ac82cc6b9589bc974bccd0634ef61a96b Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 07:54:37 -0800 Subject: [PATCH 20/63] [PB-13614] Removed task field from ai-assist-config.json --- .../tools/smcupgrader/ai/AiPlanExecutor.java | 5 +- .../tools/smcupgrader/ai/AiPlanGenerator.java | 1 - .../tools/smcupgrader/ai/AiPlanStep.java | 23 +----- .../tools/smcupgrader/ai/MarkdownParser.java | 8 -- .../tools/smcupgrader/ai/MarkdownWriter.java | 1 - src/main/resources/ai-assist-config.json | 76 ++++++------------- .../smcupgrader/ai/AiPlanExecutorTest.java | 1 - .../smcupgrader/ai/AiPlanGeneratorTest.java | 1 - .../smcupgrader/ai/MarkdownParserTest.java | 6 -- .../smcupgrader/ai/PlanDocumentTest.java | 1 - 10 files changed, 28 insertions(+), 95 deletions(-) diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java index 569a8bf..f04e1da 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutor.java @@ -110,7 +110,6 @@ public boolean executeNextStep() throws IOException { // Show next step info LOGGER.info("Next step: {}", nextStep.getTitle()); - LOGGER.info(" Task: {}", nextStep.getTask()); LOGGER.info(" Tool: {}", nextStep.getTool()); if (nextStep.hasValidationCommand()) { LOGGER.info(" Validation command: {}", nextStep.getValidationCommand()); @@ -285,7 +284,7 @@ private boolean checkStepValidation(final AiPlanStep step) throws IOException { * @throws IOException if an error occurs */ private boolean executeSmcUpgraderStep(final AiPlanStep step) throws IOException { - LOGGER.info("Executing smc-upgrader step: {}", step.getTask()); + LOGGER.info("Executing smc-upgrader step: {}", step.getTitle()); // Get target version from step String targetVersion = step.getVersion(); @@ -383,7 +382,7 @@ private boolean executeClaudeStep(final AiPlanStep step) throws IOException { } } else { LOGGER.warn("No prompt defined for this Claude step."); - LOGGER.info("Please manually complete the task: {}", step.getTask()); + LOGGER.info("Please manually complete: {}", step.getTitle()); } // Claude steps are never auto-completed - user must verify and mark complete manually diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java index 05f8941..8cbd694 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java @@ -192,7 +192,6 @@ List expandStepsForVersions(final List versionSequence) { for (AiPlanStep template : stepsToUse) { AiPlanStep step = new AiPlanStep(); step.setTitle(substituteVariables(template.getTitle(), fromVersion, toVersion)); - step.setTask(substituteVariables(template.getTask(), fromVersion, toVersion)); step.setTool(template.getTool()); step.setStatus(template.getStatus()); step.setCommitPlanOnCompletion(template.isCommitPlanOnCompletion()); diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java index ab8fedb..c78c2b3 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanStep.java @@ -20,7 +20,6 @@ */ public class AiPlanStep { private String title; - private String task; private String tool; private String version; private String validationCommand; @@ -39,16 +38,14 @@ public AiPlanStep() { * Constructor for creating a plan step. * * @param title the step title - * @param task the task description * @param tool the tool to use (smc-upgrader or claude) * @param validationCommand optional validation command * @param status the step status (incomplete or complete) * @param prompt optional prompt for claude steps */ - public AiPlanStep(final String title, final String task, final String tool, + public AiPlanStep(final String title, final String tool, final String validationCommand, final String status, final String prompt) { this.title = title; - this.task = task; this.tool = tool; this.validationCommand = validationCommand; this.status = status; @@ -73,24 +70,6 @@ public void setTitle(final String title) { this.title = title; } - /** - * Get the task description. - * - * @return the task - */ - public String getTask() { - return task; - } - - /** - * Set the task description. - * - * @param task the task - */ - public void setTask(final String task) { - this.task = task; - } - /** * Get the tool name. * diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java index b715417..328e7fd 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParser.java @@ -38,7 +38,6 @@ public final class MarkdownParser { private static final Parser PARSER = Parser.builder().build(); private static final Pattern FROM_VERSION_PATTERN = Pattern.compile("Upgrade from:\\s*(.+)"); private static final Pattern TO_VERSION_PATTERN = Pattern.compile("Upgrade to:\\s*(.+)"); - private static final Pattern TASK_PATTERN = Pattern.compile("Task:\\s*(.+)"); private static final Pattern TOOL_PATTERN = Pattern.compile("Tool:\\s*(.+)"); private static final Pattern VERSION_PATTERN = Pattern.compile("Version:\\s*(.+)"); private static final Pattern VALIDATION_PATTERN = Pattern.compile("Validation command:\\s*(.+)"); @@ -174,13 +173,6 @@ private static boolean parseMetadata(final String text, final AiPlanStep current String[] lines = text.split("\n"); for (String line : lines) { - matcher = TASK_PATTERN.matcher(line); - if (matcher.find()) { - currentStep.setTask(matcher.group(1).trim()); - foundMetadata = true; - continue; - } - matcher = TOOL_PATTERN.matcher(line); if (matcher.find()) { currentStep.setTool(matcher.group(1).trim()); diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java index 3bbfaa2..7cb544d 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/MarkdownWriter.java @@ -71,7 +71,6 @@ private static void appendStep(final StringBuilder markdown, final AiPlanStep st markdown.append("## ").append(step.getTitle()).append("\n\n"); // Metadata - markdown.append("Task: ").append(step.getTask()).append("\n"); markdown.append("Tool: ").append(step.getTool()).append("\n"); if (step.getVersion() != null && !step.getVersion().trim().isEmpty()) { diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 86b36ec..27079eb 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -10,15 +10,13 @@ "upgradeSteps": [ { "title": "Git merge from {FROM_VERSION} to {TO_VERSION}", - "task": "Upgrade source from {FROM_VERSION} to {TO_VERSION}", "tool": "smc-upgrader", "commitAllChangesOnCompletion": false, "commitPlanOnCompletion": false, "status": "incomplete" }, { - "title": "Resolve {TO_VERSION} merge conflicts", - "task": "Resolve remaining Git merge conflicts", + "title": "Resolve remaining Git merge conflicts", "tool": "claude", "validationCommand": "git diff --check", "commitAllChangesOnCompletion": true, @@ -27,8 +25,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. IMPORTANT: Ensure that you do not overwrite \"ours\" changes with \"theirs\" unless the differences don't affect functionality. Make sure to check each file manually - don't simply replace with \"theirs\" unless the changes are whitespace or no longer needed due to the platform changes. Assume that files deleted in \"theirs\" were removed intentionally and accept the deletion. Accept the new \"theirs\" versions for all dependency upgrades. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." }, { - "title": "Fix compilation failures", - "task": "Resolve all compilation issues", + "title": "Resolve all compilation issues", "tool": "claude", "validationCommand": "mvn clean install -DskipAllTests", "commitAllChangesOnCompletion": true, @@ -37,8 +34,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any compilation issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix static analysis failures", - "task": "Resolve all static analysis failures", + "title": "Resolve all static analysis failures", "tool": "claude", "validationCommand": "mvn clean install -DskipTests -DskipITests -DskipCucumberTests -DskipFitTests", "commitAllChangesOnCompletion": true, @@ -47,8 +43,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix unit test failures", - "task": "Resolve all unit test failures", + "title": "Resolve all unit test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipITests -DskipCucumberTests -DskipFitTests", "commitAllChangesOnCompletion": true, @@ -57,8 +52,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix integration test failures", - "task": "Resolve all integration test failures", + "title": "Resolve all integration test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipCucumberTests -DskipFitTests", "commitAllChangesOnCompletion": true, @@ -67,8 +61,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below and help to fix any static analysis or test failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix local database reset failures", - "task": "Resolve database reset failures", + "title": "Resolve local database reset failures", "tool": "claude", "validationCommand": "mvn clean install -Dreset-db -f extensions/database", "commitAllChangesOnCompletion": true, @@ -77,8 +70,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to reset the local database and help to fix any failures. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Integration Server startup issues", - "task": "Fix Integration Server startup issues", + "title": "Resolve Integration Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/integration/ext-integration-webapp", "commitAllChangesOnCompletion": true, @@ -87,8 +79,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Integration Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Batch Server startup issues", - "task": "Fix Batch Server startup issues", + "title": "Resolve Batch Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/batch/ext-batch-webapp", "commitAllChangesOnCompletion": true, @@ -97,8 +88,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Batch Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Search Server startup issues", - "task": "Fix Search Server startup issues", + "title": "Resolve Search Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/search/ext-search-webapp", "commitAllChangesOnCompletion": true, @@ -107,8 +97,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start the Search Server and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Cortex startup issues", - "task": "Fix Cortex startup issues", + "title": "Resolve Cortex startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp", "commitAllChangesOnCompletion": true, @@ -117,8 +106,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Cortex and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Commerce Manager startup issues", - "task": "Fix Commerce Manager startup issues", + "title": "Resolve Commerce Manager startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp", "commitAllChangesOnCompletion": true, @@ -127,8 +115,7 @@ "prompt": "We are in the process of doing an upgrade of the Self-Managed Commerce code base from version {FROM_VERSION} to version {TO_VERSION}. Run the validation command below to start Commerce Manager and help to fix any startup issues. The upgrade notes at https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/upgrade-notes.html, https://documentation.elasticpath.com/commerce/docs/{TO_VERSION}/release-notes.html#upgrade-notes, or https://documentation.elasticpath.com/commerce/docs/upgrade-notes.html (use the first URL that doesn't return 404) may be helpful. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Cucumber and Selenium test failures", - "task": "Resolve all Cucumber and Selenium test failures", + "title": "Resolve all Cucumber and Selenium test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipTests=true -DskipITests=true", "commitAllChangesOnCompletion": true, @@ -139,16 +126,14 @@ ], "patchConsumptionSteps": [ { - "title": "Git merge latest patches", - "task": "Upgrade source with latest patches", + "title": "Git merge from latest {TO_VERSION} patches", "tool": "smc-upgrader", "commitAllChangesOnCompletion": false, "commitPlanOnCompletion": false, "status": "incomplete" }, { - "title": "Resolve merge conflicts", - "task": "Resolve remaining Git merge conflicts", + "title": "Resolve remaining Git merge conflicts", "tool": "claude", "validationCommand": "git diff --check", "commitAllChangesOnCompletion": true, @@ -157,8 +142,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Review the merge conflicts in the current folder and help to address them. \"ours\" represents the customer's custom code base, and \"theirs\" represents the new Self-Managed Commerce release code. IMPORTANT: Ensure that you do not overwrite \"ours\" changes with \"theirs\" unless the differences don't affect functionality. Make sure to check each file manually - don't simply replace with \"theirs\" unless the changes are whitespace or no longer needed due to the platform changes. Assume that files deleted in \"theirs\" were removed intentionally and accept the deletion. Accept the new \"theirs\" versions for all dependency upgrades. Continue working until all Git merge conflicts are resolved. We do not need to run Maven commands to check if the code compiles in this step." }, { - "title": "Fix compilation failures", - "task": "Resolve all compilation issues", + "title": "Resolve all compilation issues", "tool": "claude", "validationCommand": "mvn clean install -DskipAllTests", "commitAllChangesOnCompletion": true, @@ -167,8 +151,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any compilation issues. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix static analysis failures", - "task": "Resolve all static analysis failures", + "title": "Resolve all static analysis failures", "tool": "claude", "validationCommand": "mvn clean install -DskipTests -DskipITests -DskipCucumberTests -DskipFitTests", "commitAllChangesOnCompletion": true, @@ -177,8 +160,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix unit test failures", - "task": "Resolve all unit test failures", + "title": "Resolve all unit test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipITests -DskipCucumberTests -DskipFitTests", "commitAllChangesOnCompletion": true, @@ -187,8 +169,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix integration test failures", - "task": "Resolve all integration test failures", + "title": "Resolve all integration test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipCucumberTests -DskipFitTests", "commitAllChangesOnCompletion": true, @@ -197,8 +178,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix local database reset failures", - "task": "Resolve database reset failures", + "title": "Resolve local database reset failures", "tool": "claude", "validationCommand": "mvn clean install -Dreset-db -f extensions/database", "commitAllChangesOnCompletion": true, @@ -207,8 +187,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Integration Server startup issues", - "task": "Fix Integration Server startup issues", + "title": "Resolve Integration Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/integration/ext-integration-webapp", "commitAllChangesOnCompletion": true, @@ -217,8 +196,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Batch Server startup issues", - "task": "Fix Batch Server startup issues", + "title": "Resolve Batch Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/batch/ext-batch-webapp", "commitAllChangesOnCompletion": true, @@ -227,8 +205,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Search Server startup issues", - "task": "Fix Search Server startup issues", + "title": "Resolve Search Server startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/search/ext-search-webapp", "commitAllChangesOnCompletion": true, @@ -237,8 +214,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Cortex startup issues", - "task": "Fix Cortex startup issues", + "title": "Resolve Cortex startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/cortex/ext-cortex-webapp", "commitAllChangesOnCompletion": true, @@ -247,8 +223,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Commerce Manager startup issues", - "task": "Fix Commerce Manager startup issues", + "title": "Resolve Commerce Manager startup issues", "tool": "claude", "validationCommand": "mvn clean package cargo:run -f extensions/cm/ext-cm-modules/ext-cm-webapp", "commitAllChangesOnCompletion": true, @@ -257,8 +232,7 @@ "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." }, { - "title": "Fix Cucumber and Selenium test failures", - "task": "Resolve all Cucumber and Selenium test failures", + "title": "Resolve Cucumber and Selenium test failures", "tool": "claude", "validationCommand": "mvn clean install -DskipTests=true -DskipITests=true", "commitAllChangesOnCompletion": true, diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java index c86796a..ddbbb13 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanExecutorTest.java @@ -296,7 +296,6 @@ void testExecuteNextStep_exitChoice() throws IOException { private AiPlanStep createStep(String title, String tool, String status) { AiPlanStep step = new AiPlanStep(); step.setTitle(title); - step.setTask("Test task"); step.setTool(tool); step.setStatus(status); step.setCommitAllChangesOnCompletion(true); diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java index 3873800..3034215 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java @@ -229,7 +229,6 @@ boolean promptForOverwrite(File file) { private AiPlanStep createStep(String title, String tool, String validationCommand) { AiPlanStep step = new AiPlanStep(); step.setTitle(title); - step.setTask("Test task"); step.setTool(tool); step.setStatus("incomplete"); step.setValidationCommand(validationCommand); diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java index 486278b..f580324 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/MarkdownParserTest.java @@ -52,7 +52,6 @@ void testParsePlan_singleStep() { + "Upgrade to: 8.6.x \n\n" + "---\n\n" + "## Git merge from 8.5.x to 8.6.x\n\n" - + "Task: Merge upstream changes \n" + "Tool: smc-upgrader \n" + "Status: incomplete\n\n"; @@ -61,7 +60,6 @@ void testParsePlan_singleStep() { assertThat(plan.getSteps()).hasSize(1); AiPlanStep step = plan.getSteps().get(0); assertThat(step.getTitle()).isEqualTo("Git merge from 8.5.x to 8.6.x"); - assertThat(step.getTask()).isEqualTo("Merge upstream changes"); assertThat(step.getTool()).isEqualTo("smc-upgrader"); assertThat(step.getStatus()).isEqualTo("incomplete"); assertThat(step.getValidationCommand()).isNull(); @@ -175,13 +173,11 @@ void testParsePlan_roundTrip() throws IOException { // Generate a plan, write it to markdown, then parse it back AiPlanStep step1 = new AiPlanStep(); step1.setTitle("Git merge from 8.5.x to 8.6.x"); - step1.setTask("Merge changes"); step1.setTool("smc-upgrader"); step1.setStatus("incomplete"); AiPlanStep step2 = new AiPlanStep(); step2.setTitle("Resolve conflicts"); - step2.setTask("Fix conflicts"); step2.setTool("claude"); step2.setValidationCommand("git diff --check"); step2.setStatus("incomplete"); @@ -199,13 +195,11 @@ void testParsePlan_roundTrip() throws IOException { AiPlanStep parsedStep1 = plan.getSteps().get(0); assertThat(parsedStep1.getTitle()).isEqualTo("Git merge from 8.5.x to 8.6.x"); - assertThat(parsedStep1.getTask()).isEqualTo("Merge changes"); assertThat(parsedStep1.getTool()).isEqualTo("smc-upgrader"); assertThat(parsedStep1.getStatus()).isEqualTo("incomplete"); AiPlanStep parsedStep2 = plan.getSteps().get(1); assertThat(parsedStep2.getTitle()).isEqualTo("Resolve conflicts"); - assertThat(parsedStep2.getTask()).isEqualTo("Fix conflicts"); assertThat(parsedStep2.getTool()).isEqualTo("claude"); assertThat(parsedStep2.getValidationCommand()).isEqualTo("git diff --check"); assertThat(parsedStep2.getStatus()).isEqualTo("incomplete"); diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java index 44beeff..ff22ecb 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/PlanDocumentTest.java @@ -131,7 +131,6 @@ void testGetTotalSteps() { private AiPlanStep createStep(String title, String status) { AiPlanStep step = new AiPlanStep(); step.setTitle(title); - step.setTask("Test task"); step.setTool("smc-upgrader"); step.setStatus(status); return step; From 76f3ee55f36aaddef624b035514f5448b0fe4b2c Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:17:46 -0800 Subject: [PATCH 21/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 smc-upgrader-plan.md diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md new file mode 100644 index 0000000..1bce341 --- /dev/null +++ b/smc-upgrader-plan.md @@ -0,0 +1,35 @@ +# SMC Upgrader - AI Assist Plan + +Upgrade from: 8.5.x +Upgrade to: 8.6.x +Generated: 2025-12-16 + +--- + +## Git merge from 8.5.x to 8.6.x + +Tool: smc-upgrader +Version: 8.6.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.6.x merge conflicts + +Tool: claude +Version: 8.6.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.5.x to 8.6.x + +--- + +## Notes + +Customers can edit this plan to add custom steps or modify existing ones. Each step will be executed in sequence when running `smc-upgrader --ai:continue`. + +To mark a step as complete, change `Status: incomplete` to `Status: complete`. + From adb7e8e3b17e69b659a48b7bb16cf5ed3af2765c Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:17:46 -0800 Subject: [PATCH 22/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 252e1735b1ae5f3736620706daf2d96b62b873ac Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:17:46 -0800 Subject: [PATCH 23/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 8a8a703d62754b8873c660de68846272391ae43a Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:19:04 -0800 Subject: [PATCH 24/63] Generate upgrade plan from 8.5.x to 8.6.x From b3daea79f5cb05f853af5fbb4c4c3a2dd721528a Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:19:04 -0800 Subject: [PATCH 25/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From dde0f38a78cbb5b8bd1aef5b51cf3235db11ffd4 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:19:04 -0800 Subject: [PATCH 26/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From cd2c0e95705e6616599913623d4082ffdd70220a Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:19:21 -0800 Subject: [PATCH 27/63] Generate upgrade plan from 8.5.x to 8.6.x From b253a0e43076257c5b1ffc4544e037f76583a459 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:19:21 -0800 Subject: [PATCH 28/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From ffe8791083b5ed123708c97ce9e619fbd16d53fd Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:19:21 -0800 Subject: [PATCH 29/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 01076a2c6ed2d909bea066222dac70bd7cb1eaf1 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:53:49 -0800 Subject: [PATCH 30/63] Generate upgrade plan from 8.5.x to 8.6.x From d51d912c516e1ecd05f064d29760d0602295a217 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:53:49 -0800 Subject: [PATCH 31/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 9839805ba3468938ee1d70573190a3c9689ec451 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:53:49 -0800 Subject: [PATCH 32/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 5f693dd2d95aa905e6bc25ea089c34765d6fa07c Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:57:55 -0800 Subject: [PATCH 33/63] Generate upgrade plan from 8.5.x to 8.6.x From 0356352fc30295963192323585c566bc06cd9409 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 08:58:42 -0800 Subject: [PATCH 34/63] Generate upgrade plan from 8.5.x to 8.6.x From 6eb4f5e721726aba16b80febf50ef3f1dcf4e6ac Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:00:15 -0800 Subject: [PATCH 35/63] Generate upgrade plan from 8.5.x to 8.6.x From 4423a5a059908f335d958d61c9268c57c05d4de8 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:00:15 -0800 Subject: [PATCH 36/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From caf40dac385944417f4a0af9fcf06c6c543cc18b Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:00:15 -0800 Subject: [PATCH 37/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 9692581ed0a7d338e4a27d44f8dd51c93f2cd950 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:00:53 -0800 Subject: [PATCH 38/63] Generate upgrade plan from 8.5.x to 8.6.x From 2429c2e6d6cc36e9c316936371f59aabe400ec5a Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:00:53 -0800 Subject: [PATCH 39/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 306d435df86e38b47e6cc25775ca2fb28675ad2d Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:00:53 -0800 Subject: [PATCH 40/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 00762c175241f5c6afdfa0b1f7132f065b3d1936 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:38:49 -0800 Subject: [PATCH 41/63] Generate upgrade plan from 8.5.x to 8.6.x From cb8fac7a0f59d002eadf898dcea9b66427c89fcd Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:38:49 -0800 Subject: [PATCH 42/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 89fc5f23380798c3803cd10c1f40fa874866e952 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:38:49 -0800 Subject: [PATCH 43/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From f82961150e429efe91281b4fdab333dca698d588 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:40:35 -0800 Subject: [PATCH 44/63] Generate upgrade plan from 8.5.x to 8.6.x From 6980158d106980a1cd0cc797e3b30744dfd6299f Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:40:35 -0800 Subject: [PATCH 45/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 4359fcacf3bcabe1bef11ea396fde10f103dbebe Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:40:35 -0800 Subject: [PATCH 46/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 17ea6c815ad0b133c93c7462b9878b1eda744f63 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:41:58 -0800 Subject: [PATCH 47/63] Generate upgrade plan from 8.5.x to 8.6.x From f32c33aa5e8a32a0f91c1a00712dbda21d77e3a6 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:41:58 -0800 Subject: [PATCH 48/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From e1ed664084b4d9b7f88bd4b0283573fa5daa5fb4 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:41:58 -0800 Subject: [PATCH 49/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 36e206c67bcdec2373fe03b9e739fdf854a2cfff Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:42:53 -0800 Subject: [PATCH 50/63] Generate upgrade plan from 8.5.x to 8.6.x From d4e70a25aa144797b2d9bdd0e4e30fb184261f42 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:42:53 -0800 Subject: [PATCH 51/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 3a5a02331bad83474aba54583045616591fbe3a2 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:42:53 -0800 Subject: [PATCH 52/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 1c9a59390b8012729ea86a6c254e2d1543657da3 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:46:21 -0800 Subject: [PATCH 53/63] Generate upgrade plan from 8.5.x to 8.6.x From dd706b455511d9233c7d4fba967f465bd7fcbae8 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:46:21 -0800 Subject: [PATCH 54/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 1bce341..3f59414 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-16 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 9905db8a244af773101b80aa6d8b74d2ba935940 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 09:46:21 -0800 Subject: [PATCH 55/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 3f59414..1bce341 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-16 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From fffcd3b25b186b9c9b00a289c9f01c1205bf0ddf Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Tue, 16 Dec 2025 15:28:42 -0800 Subject: [PATCH 56/63] [PB-13614] Various fixes --- smc-upgrader-plan.md | 35 ------------------- .../tools/smcupgrader/ai/AiPlanGenerator.java | 32 ++++++++++++----- .../tools/smcupgrader/impl/GitClientImpl.java | 2 +- .../smcupgrader/ai/AiPlanGeneratorTest.java | 4 +-- 4 files changed, 27 insertions(+), 46 deletions(-) delete mode 100644 smc-upgrader-plan.md diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md deleted file mode 100644 index 1bce341..0000000 --- a/smc-upgrader-plan.md +++ /dev/null @@ -1,35 +0,0 @@ -# SMC Upgrader - AI Assist Plan - -Upgrade from: 8.5.x -Upgrade to: 8.6.x -Generated: 2025-12-16 - ---- - -## Git merge from 8.5.x to 8.6.x - -Tool: smc-upgrader -Version: 8.6.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.6.x merge conflicts - -Tool: claude -Version: 8.6.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.5.x to 8.6.x - ---- - -## Notes - -Customers can edit this plan to add custom steps or modify existing ones. Each step will be executed in sequence when running `smc-upgrader --ai:continue`. - -To mark a step as complete, change `Status: incomplete` to `Status: complete`. - diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java index 8cbd694..7fdfeb2 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGenerator.java @@ -65,6 +65,11 @@ public AiPlanGenerator(final UpgradePath upgradePath, final UpgradeController up public boolean generatePlan(final String targetVersion, final File workingDir) throws IOException { File planFile = new File(workingDir, PLAN_FILE_NAME); + // Determine current version first + String currentVersion = upgradeController.convertVersionToReleaseFormat( + upgradeController.determineCurrentVersion()); + LOGGER.info("Detected current version: {}", currentVersion); + // Check if plan already exists if (planFile.exists()) { if (!promptForOverwrite(planFile)) { @@ -82,11 +87,6 @@ public boolean generatePlan(final String targetVersion, final File workingDir) t + ". Valid versions are: " + upgradePath.getVersions()); } - // Determine current version - String currentVersion = upgradeController.convertVersionToReleaseFormat( - upgradeController.determineCurrentVersion()); - LOGGER.info("Detected current version: {}", currentVersion); - // Validate upgrade path if (!upgradePath.validateVersionPath(currentVersion, targetVersion)) { throw new IllegalArgumentException( @@ -111,10 +111,17 @@ public boolean generatePlan(final String targetVersion, final File workingDir) t GitClient gitClient = createGitClient(workingDir); if (gitClient != null) { try { - commitPlanFile(gitClient, "Generate upgrade plan from " + currentVersion + " to " + targetVersion); + String commitMessage; + if (currentVersion.equals(targetVersion)) { + commitMessage = "Generate upgrade plan for latest patches"; + } else { + commitMessage = "Generate upgrade plan from " + currentVersion + " to " + targetVersion; + } + commitPlanFile(gitClient, commitMessage); + LOGGER.info("Committed plan file to git"); } catch (RuntimeException e) { // Don't fail if git commit fails (e.g., signing service unavailable) - LOGGER.debug("Could not commit plan file to git: {}", e.getMessage()); + LOGGER.warn("Could not commit plan file to git: {}", e.getMessage(), e); } } @@ -138,10 +145,11 @@ public boolean generatePlan(final String targetVersion, final File workingDir) t * @param planFile the existing plan file * @return true to proceed with overwrite, false to cancel */ - boolean promptForOverwrite(final File planFile) { + boolean promptForOverwrite(final File planFile) throws IOException { LOGGER.warn("WARNING: An upgrade plan already exists at: {}", planFile.getAbsolutePath()); LOGGER.warn(""); LOGGER.warn("This plan may contain customizations or progress from a previous upgrade."); + LOGGER.warn(""); Console console = System.console(); String response; @@ -149,9 +157,17 @@ boolean promptForOverwrite(final File planFile) { if (console != null) { response = console.readLine("Do you want to overwrite it? [y/N]: "); } else { + // Check if stdin is available (non-blocking check) + if (System.in.available() == 0) { + // In test/automated environments with no stdin, default to not overwriting + LOGGER.debug("No stdin available, defaulting to not overwrite"); + return false; + } + // Fallback for environments without console (like IDEs) Scanner scanner = new Scanner(System.in); System.out.print("Do you want to overwrite it? [y/N]: "); + System.out.flush(); // Ensure prompt is displayed response = scanner.nextLine(); } diff --git a/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java b/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java index 538d93f..039a9d3 100644 --- a/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java +++ b/src/main/java/com/elasticpath/tools/smcupgrader/impl/GitClientImpl.java @@ -334,7 +334,7 @@ public void unstage(final String path) { @Override public void commit(final String message) { try (Git git = new Git(repository)) { - git.commit().setMessage(message).call(); + git.commit().setMessage(message).setSign(false).call(); } catch (final GitAPIException e) { throw new RuntimeException(e); } diff --git a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java index 3034215..deaa528 100644 --- a/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java +++ b/src/test/java/com/elasticpath/tools/smcupgrader/ai/AiPlanGeneratorTest.java @@ -184,7 +184,7 @@ void testGeneratePlan_existingFile_userDeclines() throws IOException { // Create generator that always returns false for overwrite AiPlanGenerator testGenerator = new AiPlanGenerator(upgradePath, upgradeController) { @Override - boolean promptForOverwrite(File file) { + boolean promptForOverwrite(File file) throws IOException { return false; } }; @@ -209,7 +209,7 @@ void testGeneratePlan_existingFile_userAccepts() throws IOException { // Create generator that always returns true for overwrite AiPlanGenerator testGenerator = new AiPlanGenerator(upgradePath, upgradeController) { @Override - boolean promptForOverwrite(File file) { + boolean promptForOverwrite(File file) throws IOException { return true; } }; From f707eff66991337acda92016617ea90a467c3c4c Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Wed, 17 Dec 2025 07:35:09 -0800 Subject: [PATCH 57/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 smc-upgrader-plan.md diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md new file mode 100644 index 0000000..5210b1d --- /dev/null +++ b/smc-upgrader-plan.md @@ -0,0 +1,35 @@ +# SMC Upgrader - AI Assist Plan + +Upgrade from: 8.5.x +Upgrade to: 8.6.x +Generated: 2025-12-17 + +--- + +## Git merge from 8.5.x to 8.6.x + +Tool: smc-upgrader +Version: 8.6.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.6.x merge conflicts + +Tool: claude +Version: 8.6.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.5.x to 8.6.x + +--- + +## Notes + +Customers can edit this plan to add custom steps or modify existing ones. Each step will be executed in sequence when running `smc-upgrader --ai:continue`. + +To mark a step as complete, change `Status: incomplete` to `Status: complete`. + From d5d4d1373fb223f002ef56595c0e1cdf1d7cccb0 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Wed, 17 Dec 2025 07:35:09 -0800 Subject: [PATCH 58/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 5210b1d..06910d2 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-17 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From 33afe9d13b554670b1139c77d279ea2a0a553af5 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Wed, 17 Dec 2025 07:35:09 -0800 Subject: [PATCH 59/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 06910d2..5210b1d 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-17 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 6a1ce7acfea5768eee7124e7a79aabb1df503fd9 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sat, 20 Dec 2025 07:09:59 -0800 Subject: [PATCH 60/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 5210b1d..2a27b35 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -2,7 +2,7 @@ Upgrade from: 8.5.x Upgrade to: 8.6.x -Generated: 2025-12-17 +Generated: 2025-12-20 --- From df65feab9ee15bbb307c8183df29dc48f96002cd Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sat, 20 Dec 2025 07:09:59 -0800 Subject: [PATCH 61/63] Generate upgrade plan from 8.5.x to 8.7.x --- smc-upgrader-plan.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index 2a27b35..d7ab208 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.6.x +Upgrade to: 8.7.x Generated: 2025-12-20 --- @@ -25,6 +25,25 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x +## Git merge from 8.6.x to 8.7.x + +Tool: smc-upgrader +Version: 8.7.x +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +## Resolve 8.7.x merge conflicts + +Tool: claude +Version: 8.7.x +Validation command: git diff --check +Commit all changes on completion: false +Commit plan on completion: false +Status: incomplete + +Test prompt for 8.6.x to 8.7.x + --- ## Notes From c66026ef183ca5500f3419e090b2aa81e0080542 Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sat, 20 Dec 2025 07:09:59 -0800 Subject: [PATCH 62/63] Generate upgrade plan from 8.5.x to 8.6.x --- smc-upgrader-plan.md | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md index d7ab208..2a27b35 100644 --- a/smc-upgrader-plan.md +++ b/smc-upgrader-plan.md @@ -1,7 +1,7 @@ # SMC Upgrader - AI Assist Plan Upgrade from: 8.5.x -Upgrade to: 8.7.x +Upgrade to: 8.6.x Generated: 2025-12-20 --- @@ -25,25 +25,6 @@ Status: incomplete Test prompt for 8.5.x to 8.6.x -## Git merge from 8.6.x to 8.7.x - -Tool: smc-upgrader -Version: 8.7.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.7.x merge conflicts - -Tool: claude -Version: 8.7.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.6.x to 8.7.x - --- ## Notes From 299f1a3c995a0ef586435eb6489d2bcba3ef9a0d Mon Sep 17 00:00:00 2001 From: Geoff Denning Date: Sat, 20 Dec 2025 08:24:26 -0800 Subject: [PATCH 63/63] [PB-13614] Added prompt details to resume builds from fixed module --- smc-upgrader-plan.md | 35 ------------------------ src/main/resources/ai-assist-config.json | 10 +++---- 2 files changed, 5 insertions(+), 40 deletions(-) delete mode 100644 smc-upgrader-plan.md diff --git a/smc-upgrader-plan.md b/smc-upgrader-plan.md deleted file mode 100644 index 2a27b35..0000000 --- a/smc-upgrader-plan.md +++ /dev/null @@ -1,35 +0,0 @@ -# SMC Upgrader - AI Assist Plan - -Upgrade from: 8.5.x -Upgrade to: 8.6.x -Generated: 2025-12-20 - ---- - -## Git merge from 8.5.x to 8.6.x - -Tool: smc-upgrader -Version: 8.6.x -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -## Resolve 8.6.x merge conflicts - -Tool: claude -Version: 8.6.x -Validation command: git diff --check -Commit all changes on completion: false -Commit plan on completion: false -Status: incomplete - -Test prompt for 8.5.x to 8.6.x - ---- - -## Notes - -Customers can edit this plan to add custom steps or modify existing ones. Each step will be executed in sequence when running `smc-upgrader --ai:continue`. - -To mark a step as complete, change `Status: incomplete` to `Status: complete`. - diff --git a/src/main/resources/ai-assist-config.json b/src/main/resources/ai-assist-config.json index 27079eb..0c17f62 100644 --- a/src/main/resources/ai-assist-config.json +++ b/src/main/resources/ai-assist-config.json @@ -148,7 +148,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any compilation issues. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any compilation issues. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch. After fixing issues in a module, use the -rf Maven option to continue the build from the failed module to save time." }, { "title": "Resolve all static analysis failures", @@ -157,7 +157,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch. After fixing issues in a module, use the -rf Maven option to continue the build from the failed module to save time." }, { "title": "Resolve all unit test failures", @@ -166,7 +166,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch. After fixing issues in a module, use the -rf Maven option to continue the build from the failed module to save time." }, { "title": "Resolve all integration test failures", @@ -175,7 +175,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below and help to fix any static analysis or test failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch. After fixing issues in a module, use the -rf Maven option to continue the build from the failed module to save time." }, { "title": "Resolve local database reset failures", @@ -238,7 +238,7 @@ "commitAllChangesOnCompletion": true, "commitPlanOnCompletion": true, "status": "incomplete", - "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch." + "prompt": "We are in the process of consuming the latest patches from the Self-Managed Commerce code base. Run the validation command below to reset the local database and help to fix any failures. If you need access to the platform version of a file, you can retrieve it from the smc-upgrades remote in the release/{TO_VERSION} branch. After fixing issues in a module, use the -rf Maven option to continue the build from the failed module to save time." } ] }