@iloom/cli 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/dist/{chunk-U3WU5OWO.js → chunk-4XIDC3NF.js} +2 -2
- package/dist/chunk-4XIDC3NF.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/{finish-CY4CIH6O.js → finish-3CQZIULO.js} +25 -1
- package/dist/{finish-CY4CIH6O.js.map → finish-3CQZIULO.js.map} +1 -1
- package/dist/prompts/issue-prompt.txt +48 -5
- package/dist/prompts/pr-prompt.txt +46 -1
- package/dist/prompts/regular-prompt.txt +22 -0
- package/dist/{update-3ZT2XX2G.js → update-DN3FSNKY.js} +2 -2
- package/dist/{update-notifier-QSSEB5KC.js → update-notifier-QEX3CJHA.js} +2 -2
- package/package.json +2 -3
- package/dist/chunk-U3WU5OWO.js.map +0 -1
- /package/dist/{update-3ZT2XX2G.js.map → update-DN3FSNKY.js.map} +0 -0
- /package/dist/{update-notifier-QSSEB5KC.js.map → update-notifier-QEX3CJHA.js.map} +0 -0
package/README.md
CHANGED
|
@@ -424,20 +424,19 @@ This allows teams to share project defaults via `settings.json` while individual
|
|
|
424
424
|
}
|
|
425
425
|
},
|
|
426
426
|
"agents": {
|
|
427
|
-
"iloom-issue-enhancer": "
|
|
428
|
-
"iloom-issue-analyzer": "
|
|
429
|
-
"iloom-issue-analyze-and-plan":
|
|
430
|
-
"iloom-issue-implementer":
|
|
427
|
+
"iloom-issue-enhancer": {"model":"opus"},
|
|
428
|
+
"iloom-issue-analyzer": {"model":"opus"},
|
|
429
|
+
"iloom-issue-analyze-and-plan": {"model":"opus"},
|
|
430
|
+
"iloom-issue-implementer": {"model":"haiku"}
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
433
|
```
|
|
434
434
|
|
|
435
|
-
**Note on agent configuration:** All agents use the latest Sonnet model by default.
|
|
435
|
+
**Note on agent configuration:** All agents use the latest Sonnet model by default, except complexity evaluator which uses Haiku. You could also try a some different configurations:
|
|
436
436
|
- **Opus for analysis/enhancement** - Maximum reasoning capability for understanding requirements and planning
|
|
437
437
|
- **Haiku for implementation** - Cost-effective execution of detailed plans (recommended for token-conscious users)
|
|
438
|
-
- Other agents (complexity evaluator, planner, reviewer) remain on Sonnet by default
|
|
439
438
|
|
|
440
|
-
**
|
|
439
|
+
** Common configuration options:**
|
|
441
440
|
- `mainBranch` - Primary branch for merging (default: "main")
|
|
442
441
|
- `capabilities.web.basePort` - Base port for dev servers (default: 3000)
|
|
443
442
|
- `capabilities.database.databaseUrlEnvVarName` - Name of environment variable for database connection URL (default: "DATABASE_URL")
|
|
@@ -71,7 +71,7 @@ var UpdateNotifier = class {
|
|
|
71
71
|
const content = await fs.readFile(this.cacheFilePath, "utf8");
|
|
72
72
|
logger.debug(`getCachedCheck: Cache file content: ${content}`);
|
|
73
73
|
const cache = JSON.parse(content);
|
|
74
|
-
const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? "
|
|
74
|
+
const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? "60", 10);
|
|
75
75
|
const cacheTimeoutMs = cacheTimeoutMins * 60 * 1e3;
|
|
76
76
|
logger.debug(`getCachedCheck: Using cache timeout of ${cacheTimeoutMins} minutes`);
|
|
77
77
|
const now = Date.now();
|
|
@@ -200,4 +200,4 @@ export {
|
|
|
200
200
|
UpdateNotifier,
|
|
201
201
|
checkAndNotifyUpdate
|
|
202
202
|
};
|
|
203
|
-
//# sourceMappingURL=chunk-
|
|
203
|
+
//# sourceMappingURL=chunk-4XIDC3NF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/update-notifier.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport chalk from 'chalk'\nimport { logger } from './logger.js'\n\nexport interface UpdateCheckCache {\n lastCheck: number // timestamp\n latestVersion: string\n}\n\nexport interface UpdateCheckResult {\n currentVersion: string\n latestVersion: string\n updateAvailable: boolean\n}\n\nexport class UpdateNotifier {\n private cacheFilePath: string\n private currentVersion: string\n private packageName: string\n\n constructor(currentVersion: string, packageName: string) {\n this.currentVersion = currentVersion\n this.packageName = packageName\n // Cross-platform cache directory\n const configDir = path.join(os.homedir(), '.config', 'iloom-ai')\n this.cacheFilePath = path.join(configDir, 'update-check.json')\n logger.debug(`UpdateNotifier initialized: version=${currentVersion}, package=${packageName}, cachePath=${this.cacheFilePath}`)\n }\n\n /**\n * Check for updates, respecting 24hr cache\n * Returns UpdateCheckResult or null if check failed/not needed\n */\n async checkForUpdates(): Promise<UpdateCheckResult | null> {\n logger.debug('checkForUpdates: Starting update check')\n try {\n // Check cache first\n logger.debug('checkForUpdates: Checking cache')\n const cached = await this.getCachedCheck()\n if (cached !== null) {\n logger.debug(`checkForUpdates: Using cached result - latest=${cached.latestVersion}, lastCheck=${new Date(cached.lastCheck).toISOString()}`)\n const updateAvailable = this.isNewerVersion(this.currentVersion, cached.latestVersion)\n logger.debug(`checkForUpdates: Update available from cache: ${updateAvailable}`)\n return {\n currentVersion: this.currentVersion,\n latestVersion: cached.latestVersion,\n updateAvailable,\n }\n }\n\n logger.debug('checkForUpdates: No valid cache, querying npm registry')\n // Query npm registry\n const latestVersion = await this.fetchLatestVersion()\n if (latestVersion === null) {\n logger.debug('checkForUpdates: Failed to fetch latest version from npm')\n return null\n }\n\n logger.debug(`checkForUpdates: Fetched latest version: ${latestVersion}`)\n\n // Save to cache\n logger.debug('checkForUpdates: Saving to cache')\n await this.saveCacheFile(latestVersion)\n\n const updateAvailable = this.isNewerVersion(this.currentVersion, latestVersion)\n logger.debug(`checkForUpdates: Update available: ${updateAvailable} (current=${this.currentVersion}, latest=${latestVersion})`)\n\n return {\n currentVersion: this.currentVersion,\n latestVersion,\n updateAvailable,\n }\n } catch (error) {\n // Handle all errors gracefully - update check should never break user experience\n logger.debug(`checkForUpdates: Error during update check: ${error}`)\n return null\n }\n }\n\n /**\n * Read cache file, return null if stale or missing\n */\n private async getCachedCheck(): Promise<UpdateCheckCache | null> {\n logger.debug(`getCachedCheck: Checking cache file at ${this.cacheFilePath}`)\n try {\n if (!fs.existsSync(this.cacheFilePath)) {\n logger.debug('getCachedCheck: Cache file does not exist')\n return null\n }\n\n logger.debug('getCachedCheck: Cache file exists, reading contents')\n const content = await fs.readFile(this.cacheFilePath, 'utf8')\n logger.debug(`getCachedCheck: Cache file content: ${content}`)\n const cache = JSON.parse(content) as UpdateCheckCache\n\n // Check if cache is still fresh (< configurable hours)\n const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? '60', 10) // Default 1 hour\n const cacheTimeoutMs = cacheTimeoutMins * 60 * 1000\n logger.debug(`getCachedCheck: Using cache timeout of ${cacheTimeoutMins} minutes`)\n const now = Date.now()\n const age = now - cache.lastCheck\n const ageHours = age / (60 * 60 * 1000)\n logger.debug(`getCachedCheck: Cache age: ${ageHours.toFixed(2)} hours (threshold: ${cacheTimeoutMins / 60} hours)`)\n\n if (now - cache.lastCheck < cacheTimeoutMs) {\n logger.debug('getCachedCheck: Cache is fresh, returning cached data')\n return cache\n }\n\n logger.debug('getCachedCheck: Cache is stale, will query npm registry')\n return null\n } catch (error) {\n // If cache is corrupted or unreadable, treat as missing\n logger.debug(`getCachedCheck: Error reading cache: ${error}`)\n return null\n }\n }\n\n /**\n * Save successful check to cache\n */\n private async saveCacheFile(latestVersion: string): Promise<void> {\n logger.debug(`saveCacheFile: Attempting to save cache for version ${latestVersion}`)\n try {\n // Ensure cache directory exists\n const configDir = path.dirname(this.cacheFilePath)\n logger.debug(`saveCacheFile: Ensuring cache directory exists: ${configDir}`)\n await fs.ensureDir(configDir)\n\n // Write cache file\n const cache: UpdateCheckCache = {\n lastCheck: Date.now(),\n latestVersion,\n }\n const cacheJson = JSON.stringify(cache, null, 2)\n logger.debug(`saveCacheFile: Writing cache file: ${cacheJson}`)\n await fs.writeFile(this.cacheFilePath, cacheJson, 'utf8')\n logger.debug(`saveCacheFile: Cache file saved successfully to ${this.cacheFilePath}`)\n } catch (error) {\n // Log debug message but don't throw - cache write failure shouldn't break anything\n logger.debug(`saveCacheFile: Failed to save update check cache to ${this.cacheFilePath}: ${error}`)\n }\n }\n\n /**\n * Display update notification to user\n */\n displayUpdateNotification(result: UpdateCheckResult): void {\n logger.debug(`displayUpdateNotification: updateAvailable=${result.updateAvailable}, current=${result.currentVersion}, latest=${result.latestVersion}`)\n if (result.updateAvailable) {\n logger.debug('displayUpdateNotification: Displaying update notification to user')\n // Simple, clear update notification\n /* eslint-disable no-console */\n console.log('')\n console.log(' ' + chalk.bold(`Update available: ${result.currentVersion} → ${result.latestVersion}`))\n console.log(' ' + chalk.bold('Run: il update'))\n console.log('')\n /* eslint-enable no-console */\n } else {\n logger.debug('displayUpdateNotification: No update available, skipping notification')\n }\n }\n\n /**\n * Query npm registry for latest version\n */\n private async fetchLatestVersion(): Promise<string | null> {\n logger.debug(`fetchLatestVersion: Querying npm for package ${this.packageName}`)\n try {\n const { stdout } = await execa('npm', ['view', this.packageName, 'version'], {\n timeout: 5000,\n })\n const version = stdout.trim()\n logger.debug(`fetchLatestVersion: npm returned version: ${version}`)\n return version\n } catch (error) {\n // Network errors, timeouts, npm not available, or package not found\n logger.debug(`fetchLatestVersion: Failed to query npm: ${error}`)\n return null\n }\n }\n\n /**\n * Compare semver versions\n * Returns true if latest > current\n */\n private isNewerVersion(current: string, latest: string): boolean {\n logger.debug(`isNewerVersion: Comparing versions - current=${current}, latest=${latest}`)\n // Simple version comparison: split by dots and compare numerically\n try {\n const currentParts = current.split('.').map(p => parseInt(p, 10))\n const latestParts = latest.split('.').map(p => parseInt(p, 10))\n logger.debug(`isNewerVersion: Parsed parts - current=[${currentParts.join(', ')}], latest=[${latestParts.join(', ')}]`)\n\n for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {\n const curr = currentParts[i] ?? 0\n const next = latestParts[i] ?? 0\n\n logger.debug(`isNewerVersion: Comparing part ${i}: current=${curr}, latest=${next}`)\n\n if (next > curr) {\n logger.debug(`isNewerVersion: Latest is newer (${next} > ${curr})`)\n return true\n }\n if (next < curr) {\n logger.debug(`isNewerVersion: Current is newer (${curr} > ${next})`)\n return false\n }\n }\n\n logger.debug('isNewerVersion: Versions are equal')\n return false\n } catch (error) {\n // If parsing fails, assume no update\n logger.debug(`isNewerVersion: Error comparing versions: ${error}`)\n return false\n }\n }\n}\n\n/**\n * Main entry point for update check\n * Call from CLI postAction hook\n */\nexport async function checkAndNotifyUpdate(\n currentVersion: string,\n packageName: string,\n installMethod: string\n): Promise<void> {\n logger.debug(`checkAndNotifyUpdate: Called with version=${currentVersion}, package=${packageName}, installMethod=${installMethod}`)\n try {\n // Only check for global installations\n if (installMethod !== 'global') {\n logger.debug(`checkAndNotifyUpdate: Skipping update check - not a global installation (method=${installMethod})`)\n return\n }\n\n logger.debug('checkAndNotifyUpdate: Creating UpdateNotifier instance')\n const notifier = new UpdateNotifier(currentVersion, packageName)\n\n logger.debug('checkAndNotifyUpdate: Calling checkForUpdates()')\n const result = await notifier.checkForUpdates()\n\n if (result !== null) {\n logger.debug(`checkAndNotifyUpdate: Got result, calling displayUpdateNotification`)\n notifier.displayUpdateNotification(result)\n } else {\n logger.debug('checkAndNotifyUpdate: Result was null, not displaying notification')\n }\n\n logger.debug('checkAndNotifyUpdate: Completed')\n } catch (error) {\n // All errors handled internally - this should never throw\n logger.debug(`checkAndNotifyUpdate: Unexpected error: ${error}`)\n }\n}\n"],"mappings":"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","names":["updateAvailable"]}
|
package/dist/cli.js
CHANGED
|
@@ -53,7 +53,7 @@ program.name("iloom").description(packageJson.description).version(packageJson.v
|
|
|
53
53
|
process.exit(0);
|
|
54
54
|
}
|
|
55
55
|
try {
|
|
56
|
-
const { checkAndNotifyUpdate } = await import("./update-notifier-
|
|
56
|
+
const { checkAndNotifyUpdate } = await import("./update-notifier-QEX3CJHA.js");
|
|
57
57
|
const { detectInstallationMethod } = await import("./installation-detector-VARGFFRZ.js");
|
|
58
58
|
const installMethod = detectInstallationMethod(__filename);
|
|
59
59
|
await checkAndNotifyUpdate(packageJson.version, packageJson.name, installMethod);
|
|
@@ -152,7 +152,7 @@ program.command("enhance").description("Apply enhancement agent to existing GitH
|
|
|
152
152
|
});
|
|
153
153
|
program.command("finish").alias("dn").description("Merge work and cleanup workspace").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").option("-f, --force", "Skip confirmation prompts").option("-n, --dry-run", "Preview actions without executing").option("--pr <number>", "Treat input as PR number", parseFloat).option("--skip-build", "Skip post-merge build verification").action(async (identifier, options) => {
|
|
154
154
|
try {
|
|
155
|
-
const { FinishCommand } = await import("./finish-
|
|
155
|
+
const { FinishCommand } = await import("./finish-3CQZIULO.js");
|
|
156
156
|
const command = new FinishCommand();
|
|
157
157
|
await command.execute({ identifier, options });
|
|
158
158
|
} catch (error) {
|
|
@@ -248,7 +248,7 @@ program.command("init").description("Initialize iloom configuration and setup sh
|
|
|
248
248
|
});
|
|
249
249
|
program.command("update").description("Update iloom-cli to the latest version").option("--dry-run", "Show what would be done without actually updating").action(async (options) => {
|
|
250
250
|
try {
|
|
251
|
-
const { UpdateCommand } = await import("./update-
|
|
251
|
+
const { UpdateCommand } = await import("./update-DN3FSNKY.js");
|
|
252
252
|
const command = new UpdateCommand();
|
|
253
253
|
await command.execute(options);
|
|
254
254
|
} catch (error) {
|
|
@@ -1505,6 +1505,10 @@ var FinishCommand = class {
|
|
|
1505
1505
|
if (!result.success) {
|
|
1506
1506
|
logger.warn("Some cleanup operations failed - manual cleanup may be required");
|
|
1507
1507
|
this.showManualCleanupInstructions(worktree);
|
|
1508
|
+
} else {
|
|
1509
|
+
if (this.isRunningFromWithinWorktree(worktree.path)) {
|
|
1510
|
+
this.showTerminalCloseWarning(worktree);
|
|
1511
|
+
}
|
|
1508
1512
|
}
|
|
1509
1513
|
} catch (error) {
|
|
1510
1514
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -1567,6 +1571,9 @@ var FinishCommand = class {
|
|
|
1567
1571
|
} else {
|
|
1568
1572
|
logger.success("Post-merge cleanup completed successfully");
|
|
1569
1573
|
}
|
|
1574
|
+
if (this.isRunningFromWithinWorktree(worktree.path)) {
|
|
1575
|
+
this.showTerminalCloseWarning(worktree);
|
|
1576
|
+
}
|
|
1570
1577
|
} catch (error) {
|
|
1571
1578
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1572
1579
|
logger.warn(`Cleanup failed: ${errorMessage}`);
|
|
@@ -1601,8 +1608,25 @@ var FinishCommand = class {
|
|
|
1601
1608
|
logger.info(` 2. Delete branch: git branch -d ${worktree.branch}`);
|
|
1602
1609
|
logger.info(` 3. Check dev servers: lsof -i :PORT (and kill if needed)`);
|
|
1603
1610
|
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Check if current working directory is within the target worktree
|
|
1613
|
+
*/
|
|
1614
|
+
isRunningFromWithinWorktree(worktreePath) {
|
|
1615
|
+
const normalizedCwd = path.normalize(process.cwd());
|
|
1616
|
+
const normalizedWorktree = path.normalize(worktreePath);
|
|
1617
|
+
return normalizedCwd.startsWith(normalizedWorktree);
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Display warning to close terminal/IDE when running from within finished loom
|
|
1621
|
+
*/
|
|
1622
|
+
showTerminalCloseWarning(worktree) {
|
|
1623
|
+
logger.info("");
|
|
1624
|
+
logger.info("You are currently in the directory of the loom that was just finished.");
|
|
1625
|
+
logger.info("Please close this terminal and any IDE/terminal windows using this directory.");
|
|
1626
|
+
logger.info(`Directory: ${worktree.path}`);
|
|
1627
|
+
}
|
|
1604
1628
|
};
|
|
1605
1629
|
export {
|
|
1606
1630
|
FinishCommand
|
|
1607
1631
|
};
|
|
1608
|
-
//# sourceMappingURL=finish-
|
|
1632
|
+
//# sourceMappingURL=finish-3CQZIULO.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/ValidationRunner.ts","../src/lib/CommitManager.ts","../src/lib/MergeManager.ts","../src/lib/BuildRunner.ts","../src/commands/finish.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { detectPackageManager, runScript } from '../utils/package-manager.js'\nimport { readPackageJson, hasScript } from '../utils/package-json.js'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport type {\n\tValidationOptions,\n\tValidationResult,\n\tValidationStepResult,\n} from '../types/index.js'\n\n/**\n * ValidationRunner orchestrates pre-merge validation pipeline\n * Runs typecheck, lint, and tests in sequence with fail-fast behavior\n */\nexport class ValidationRunner {\n\t/**\n\t * Run all validations in sequence: typecheck → lint → test\n\t * Fails fast on first error\n\t */\n\tasync runValidations(\n\t\tworktreePath: string,\n\t\toptions: ValidationOptions = {}\n\t): Promise<ValidationResult> {\n\t\tconst startTime = Date.now()\n\t\tconst steps: ValidationStepResult[] = []\n\n\t\t// Run typecheck\n\t\tif (!options.skipTypecheck) {\n\t\t\tconst typecheckResult = await this.runTypecheck(\n\t\t\t\tworktreePath,\n\t\t\t\toptions.dryRun ?? false\n\t\t\t)\n\t\t\tsteps.push(typecheckResult)\n\n\t\t\tif (!typecheckResult.passed && !typecheckResult.skipped) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tsteps,\n\t\t\t\t\ttotalDuration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Run lint\n\t\tif (!options.skipLint) {\n\t\t\tconst lintResult = await this.runLint(worktreePath, options.dryRun ?? false)\n\t\t\tsteps.push(lintResult)\n\n\t\t\tif (!lintResult.passed && !lintResult.skipped) {\n\t\t\t\treturn { success: false, steps, totalDuration: Date.now() - startTime }\n\t\t\t}\n\t\t}\n\n\t\t// Run tests\n\t\tif (!options.skipTests) {\n\t\t\tconst testResult = await this.runTests(\n\t\t\t\tworktreePath,\n\t\t\t\toptions.dryRun ?? false\n\t\t\t)\n\t\t\tsteps.push(testResult)\n\n\t\t\tif (!testResult.passed && !testResult.skipped) {\n\t\t\t\treturn { success: false, steps, totalDuration: Date.now() - startTime }\n\t\t\t}\n\t\t}\n\n\t\treturn { success: true, steps, totalDuration: Date.now() - startTime }\n\t}\n\n\t/**\n\t * Run typecheck validation\n\t */\n\tprivate async runTypecheck(\n\t\tworktreePath: string,\n\t\tdryRun: boolean\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if typecheck script exists\n\t\t\tconst pkgJson = await readPackageJson(worktreePath)\n\t\t\tconst hasTypecheckScript = hasScript(pkgJson, 'typecheck')\n\n\t\t\tif (!hasTypecheckScript) {\n\t\t\t\tlogger.debug('Skipping typecheck - no typecheck script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping typecheck - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm'\n\t\t\t\t\t? 'npm run typecheck'\n\t\t\t\t\t: `${packageManager} typecheck`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'typecheck',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tlogger.info('Running typecheck...')\n\n\t\ttry {\n\t\t\tawait runScript('typecheck', worktreePath, [], { quiet: true })\n\t\t\tlogger.success('Typecheck passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'typecheck',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'typecheck',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Typecheck passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm'\n\t\t\t\t\t? 'npm run typecheck'\n\t\t\t\t\t: `${packageManager} typecheck`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Typecheck failed.\\n` +\n\t\t\t\t\t`Fix type errors before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Run lint validation\n\t */\n\tprivate async runLint(\n\t\tworktreePath: string,\n\t\tdryRun: boolean\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if lint script exists\n\t\t\tconst pkgJson = await readPackageJson(worktreePath)\n\t\t\tconst hasLintScript = hasScript(pkgJson, 'lint')\n\n\t\t\tif (!hasLintScript) {\n\t\t\t\tlogger.debug('Skipping lint - no lint script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping lint - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run lint' : `${packageManager} lint`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'lint',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tlogger.info('Running lint...')\n\n\t\ttry {\n\t\t\tawait runScript('lint', worktreePath, [], { quiet: true })\n\t\t\tlogger.success('Linting passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'lint',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'lint',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Linting passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run lint' : `${packageManager} lint`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Linting failed.\\n` +\n\t\t\t\t\t`Fix linting errors before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Run test validation\n\t */\n\tprivate async runTests(\n\t\tworktreePath: string,\n\t\tdryRun: boolean\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if test script exists\n\t\t\tconst pkgJson = await readPackageJson(worktreePath)\n\t\t\tconst hasTestScript = hasScript(pkgJson, 'test')\n\n\t\t\tif (!hasTestScript) {\n\t\t\t\tlogger.debug('Skipping tests - no test script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping tests - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run test' : `${packageManager} test`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'test',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tlogger.info('Running tests...')\n\n\t\ttry {\n\t\t\tawait runScript('test', worktreePath, [], { quiet: true })\n\t\t\tlogger.success('Tests passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'test',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'test',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Tests passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run test' : `${packageManager} test`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Tests failed.\\n` +\n\t\t\t\t\t`Fix test failures before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Attempt to fix validation errors using Claude\n\t * Pattern based on MergeManager.attemptClaudeConflictResolution\n\t *\n\t * @param validationType - Type of validation that failed ('typecheck' | 'lint' | 'test')\n\t * @param worktreePath - Path to the worktree\n\t * @param packageManager - Detected package manager\n\t * @returns true if Claude fixed the issue, false otherwise\n\t */\n\tprivate async attemptClaudeFix(\n\t\tvalidationType: 'typecheck' | 'lint' | 'test',\n\t\tworktreePath: string,\n\t\tpackageManager: string\n\t): Promise<boolean> {\n\t\t// Check if Claude CLI is available\n\t\tconst isClaudeAvailable = await detectClaudeCli()\n\t\tif (!isClaudeAvailable) {\n\t\t\tlogger.debug('Claude CLI not available, skipping auto-fix')\n\t\t\treturn false\n\t\t}\n\n\t\t// Build validation command for the prompt\n\t\tconst validationCommand = this.getValidationCommand(validationType, packageManager)\n\n\t\t// Build prompt based on validation type (matching bash script prompts)\n\t\tconst prompt = this.getClaudePrompt(validationType, validationCommand)\n\n\t\tconst validationTypeCapitalized = validationType.charAt(0).toUpperCase() + validationType.slice(1)\n\t\tlogger.info(`Launching Claude to help fix ${validationTypeCapitalized} errors...`)\n\n\t\ttry {\n\t\t\t// Launch Claude in interactive mode with acceptEdits permission\n\t\t\tawait launchClaude(prompt, {\n\t\t\t\taddDir: worktreePath,\n\t\t\t\theadless: false, // Interactive mode\n\t\t\t\tpermissionMode: 'acceptEdits', // Auto-accept edits\n\t\t\t\tmodel: 'sonnet', // Use Sonnet model\n\t\t\t})\n\n\t\t\t// After Claude completes, re-run validation to verify fix\n\t\t\tlogger.info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`)\n\n\t\t\ttry {\n\t\t\t\tawait runScript(validationType, worktreePath, [], { quiet: true })\n\t\t\t\t// Validation passed after Claude fix\n\t\t\t\tlogger.success(`${validationTypeCapitalized} passed after Claude auto-fix`)\n\t\t\t\treturn true\n\t\t\t} catch {\n\t\t\t\t// Validation still failing after Claude's attempt\n\t\t\t\tlogger.warn(`${validationTypeCapitalized} still failing after Claude's help`)\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Claude launch failed or crashed\n\t\t\tlogger.warn('Claude auto-fix failed', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Get validation command string for prompts\n\t */\n\tprivate getValidationCommand(\n\t\tvalidationType: 'typecheck' | 'lint' | 'test',\n\t\tpackageManager: string\n\t): string {\n\t\tif (packageManager === 'npm') {\n\t\t\treturn `npm run ${validationType}`\n\t\t}\n\t\treturn `${packageManager} ${validationType}`\n\t}\n\n\t/**\n\t * Get Claude prompt for specific validation type\n\t * Matches bash script prompts exactly\n\t */\n\tprivate getClaudePrompt(\n\t\tvalidationType: 'typecheck' | 'lint' | 'test',\n\t\tvalidationCommand: string\n\t): string {\n\t\tswitch (validationType) {\n\t\t\tcase 'typecheck':\n\t\t\t\treturn (\n\t\t\t\t\t`There are TypeScript errors in this codebase. ` +\n\t\t\t\t\t`Please analyze the typecheck output, identify all type errors, and fix them. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all type issues.` +\n\t\t\t\t\t`When you are done, tell the user to quit to continue the validation process.`\n\t\t\t\t)\n\t\t\tcase 'lint':\n\t\t\t\treturn (\n\t\t\t\t\t`There are ESLint errors in this codebase. ` +\n\t\t\t\t\t`Please analyze the linting output, identify all linting issues, and fix them. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all linting issues. ` +\n\t\t\t\t\t`Focus on code quality, consistency, and following the project's linting rules.` +\n\t\t\t\t\t`When you are done, tell the user to quit to continue the validation process.`\n\t\t\t\t)\n\t\t\tcase 'test':\n\t\t\t\treturn (\n\t\t\t\t\t`There are unit test failures in this codebase. ` +\n\t\t\t\t\t`Please analyze the test output to understand what's failing, then fix the issues. ` +\n\t\t\t\t\t`This might involve updating test code, fixing bugs in the source code, or updating tests to match new behavior. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the detailed test failures, then make the necessary changes to get all tests passing.` +\n\t\t\t\t\t`When you are done, tell the user to quit to continue the validation process.`\n\t\t\t\t)\n\t\t}\n\t}\n}\n","import { executeGitCommand } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { launchClaude, detectClaudeCli } from '../utils/claude.js'\nimport type { GitStatus, CommitOptions } from '../types/index.js'\n\n/**\n * CommitManager handles uncommitted changes detection and auto-commit\n * Ports logic from bash/merge-and-clean.sh lines 610-643\n */\nexport class CommitManager {\n /**\n * Detect uncommitted changes in a worktree\n * Parses git status --porcelain output into structured GitStatus\n */\n async detectUncommittedChanges(worktreePath: string): Promise<GitStatus> {\n // Execute: git status --porcelain\n const porcelainOutput = await executeGitCommand(['status', '--porcelain'], {\n cwd: worktreePath,\n })\n\n // Parse output to get staged and unstaged files\n const { stagedFiles, unstagedFiles } = this.parseGitStatus(porcelainOutput)\n\n // Get current branch name\n const currentBranch = await executeGitCommand(['branch', '--show-current'], {\n cwd: worktreePath,\n })\n\n return {\n hasUncommittedChanges: stagedFiles.length > 0 || unstagedFiles.length > 0,\n unstagedFiles,\n stagedFiles,\n currentBranch: currentBranch.trim(),\n // Defer these to future enhancement\n isAheadOfRemote: false,\n isBehindRemote: false,\n }\n }\n\n\n /**\n * Stage all changes and commit with Claude-generated or simple message\n * Tries Claude first, falls back to simple message if Claude unavailable or fails\n */\n async commitChanges(worktreePath: string, options: CommitOptions): Promise<void> {\n // Step 1: Check dry-run mode\n if (options.dryRun) {\n logger.info('[DRY RUN] Would run: git add -A')\n logger.info('[DRY RUN] Would generate commit message with Claude (if available)')\n const fallbackMessage = this.generateFallbackMessage(options)\n const verifyFlag = options.skipVerify ? ' --no-verify' : ''\n logger.info(`[DRY RUN] Would commit with message${verifyFlag}: ${fallbackMessage}`)\n return\n }\n\n // Step 2: Stage all changes\n await executeGitCommand(['add', '-A'], { cwd: worktreePath })\n\n // Step 3: Generate commit message (try Claude first, fallback to simple)\n let message: string | null = null\n\n // Skip Claude if custom message provided\n if (!options.message) {\n try {\n message = await this.generateClaudeCommitMessage(worktreePath, options.issueNumber)\n } catch (error) {\n logger.debug('Claude commit message generation failed, using fallback', { error })\n }\n }\n\n // Fallback to simple message if Claude failed or unavailable\n message ??= this.generateFallbackMessage(options)\n\n // Step 4: Log warning if --no-verify is configured\n if (options.skipVerify) {\n logger.warn('⚠️ Skipping pre-commit hooks (--no-verify configured in settings)')\n }\n\n // Step 5: Commit with user review via git editor (unless noReview specified)\n try {\n if (options.noReview || options.message) {\n // Direct commit without editor review\n const commitArgs = ['commit', '-m', message]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n await executeGitCommand(commitArgs, { cwd: worktreePath })\n } else {\n // Use git editor for user review - pre-populate message and open editor\n logger.info('Opening git editor for commit message review...')\n const commitArgs = ['commit', '-e', '-m', message]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n await executeGitCommand(commitArgs, {\n cwd: worktreePath,\n stdio: 'inherit',\n timeout: 300000 // 5 minutes for interactive editing\n })\n }\n } catch (error) {\n // Handle \"nothing to commit\" scenario gracefully\n if (error instanceof Error && error.message.includes('nothing to commit')) {\n logger.info('No changes to commit')\n return\n }\n // Re-throw all other errors (including pre-commit hook failures)\n throw error\n }\n }\n\n\n /**\n * Generate simple fallback commit message when Claude unavailable\n * Used as fallback for Claude-powered commit messages\n */\n private generateFallbackMessage(options: CommitOptions): string {\n // If custom message provided, use it\n if (options.message) {\n return options.message\n }\n\n // Generate WIP message\n if (options.issueNumber) {\n return `WIP: Auto-commit for issue #${options.issueNumber}\\n\\nFixes #${options.issueNumber}`\n } else {\n return 'WIP: Auto-commit uncommitted changes'\n }\n }\n\n /**\n * Parse git status --porcelain output\n * Format: \"XY filename\" where X=index, Y=worktree\n * Examples:\n * \"M file.ts\" - staged modification\n * \" M file.ts\" - unstaged modification\n * \"MM file.ts\" - both staged and unstaged\n * \"?? file.ts\" - untracked\n */\n private parseGitStatus(porcelainOutput: string): {\n stagedFiles: string[]\n unstagedFiles: string[]\n } {\n const stagedFiles: string[] = []\n const unstagedFiles: string[] = []\n\n if (!porcelainOutput.trim()) {\n return { stagedFiles, unstagedFiles }\n }\n\n const lines = porcelainOutput.split('\\n').filter((line) => line.trim())\n\n for (const line of lines) {\n if (line.length < 3) continue\n\n const indexStatus = line[0] // First character - staging area status\n const worktreeStatus = line[1] // Second character - working tree status\n const filename = line.substring(3) // Everything after \"XY \"\n\n // Check if file is staged\n // First char != ' ' and != '?' → staged\n if (indexStatus !== ' ' && indexStatus !== '?') {\n stagedFiles.push(filename)\n }\n\n // Check if file is unstaged\n // Second char != ' ' or line starts with '??' → unstaged\n if (worktreeStatus !== ' ' || line.startsWith('??')) {\n unstagedFiles.push(filename)\n }\n }\n\n return { stagedFiles, unstagedFiles }\n }\n\n /**\n * Generate commit message using Claude AI\n * Claude examines the git repository directly via --add-dir option\n * Returns null if Claude unavailable or fails validation\n */\n private async generateClaudeCommitMessage(\n worktreePath: string,\n issueNumber?: number\n ): Promise<string | null> {\n const startTime = Date.now()\n\n logger.info('Starting Claude commit message generation...', {\n worktreePath: worktreePath.split('/').pop(), // Just show the folder name for privacy\n issueNumber\n })\n\n // Check if Claude CLI is available\n logger.debug('Checking Claude CLI availability...')\n const isClaudeAvailable = await detectClaudeCli()\n if (!isClaudeAvailable) {\n logger.info('Claude CLI not available, skipping Claude commit message generation')\n return null\n }\n logger.debug('Claude CLI is available')\n\n // Build XML-based structured prompt\n logger.debug('Building commit message prompt...')\n const prompt = this.buildCommitMessagePrompt(issueNumber)\n logger.debug('Prompt built', { promptLength: prompt.length })\n\n // Debug log the actual prompt content for troubleshooting\n logger.debug('Claude prompt content:', {\n prompt: prompt,\n truncatedPreview: prompt.substring(0, 500) + (prompt.length > 500 ? '...[truncated]' : '')\n })\n\n try {\n logger.info('Calling Claude CLI for commit message generation...')\n const claudeStartTime = Date.now()\n\n // Debug log the Claude call parameters\n const claudeOptions = {\n headless: true,\n addDir: worktreePath,\n model: 'claude-haiku-4-5-20251001', // Fast, cost-effective model\n timeout: 120000, // 120 second timeout\n }\n logger.debug('Claude CLI call parameters:', {\n options: claudeOptions,\n worktreePathForAnalysis: worktreePath,\n addDirContents: 'Will include entire worktree directory for analysis'\n })\n\n // Launch Claude in headless mode with repository access and shorter timeout for commit messages\n const result = await launchClaude(prompt, claudeOptions)\n\n const claudeDuration = Date.now() - claudeStartTime\n logger.debug('Claude API call completed', { duration: `${claudeDuration}ms` })\n\n if (typeof result !== 'string') {\n logger.warn('Claude returned non-string result', { resultType: typeof result })\n return null\n }\n\n logger.debug('Raw Claude output received', {\n outputLength: result.length,\n preview: result.substring(0, 200) + (result.length > 200 ? '...' : '')\n })\n\n\n // Sanitize output - remove meta-commentary and clean formatting\n logger.debug('Sanitizing Claude output...')\n const sanitized = this.sanitizeClaudeOutput(result)\n logger.debug('Output sanitized', {\n originalLength: result.length,\n sanitizedLength: sanitized.length,\n sanitized: sanitized.substring(0, 200) + (sanitized.length > 200 ? '...' : '')\n })\n\n // Ensure empty strings are rejected\n if (!sanitized) {\n logger.warn('Claude returned empty message after sanitization')\n return null\n }\n\n // Append \"Fixes #N\" trailer if issue number provided\n let finalMessage = sanitized\n if (issueNumber) {\n // Add Fixes trailer if not already present\n if (!finalMessage.includes(`Fixes #${issueNumber}`)) {\n finalMessage = `${finalMessage}\\n\\nFixes #${issueNumber}`\n logger.debug(`Added \"Fixes #${issueNumber}\" trailer to commit message`)\n } else {\n logger.debug(`\"Fixes #${issueNumber}\" already present in commit message`)\n }\n }\n\n const totalDuration = Date.now() - startTime\n logger.info('Claude commit message generated successfully', {\n message: finalMessage,\n totalDuration: `${totalDuration}ms`,\n claudeApiDuration: `${claudeDuration}ms`\n })\n\n return finalMessage\n } catch (error) {\n const totalDuration = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\n if (errorMessage.includes('timed out') || errorMessage.includes('timeout')) {\n logger.warn('Claude commit message generation timed out after 45 seconds', {\n totalDuration: `${totalDuration}ms`,\n worktreePath: worktreePath.split('/').pop()\n })\n } else {\n logger.warn('Failed to generate commit message with Claude', {\n error: errorMessage,\n totalDuration: `${totalDuration}ms`,\n worktreePath: worktreePath.split('/').pop()\n })\n }\n return null\n }\n }\n\n /**\n * Build structured XML prompt for commit message generation\n * Uses XML format for clear task definition and output expectations\n */\n private buildCommitMessagePrompt(issueNumber?: number): string {\n const issueContext = issueNumber\n ? `\\n<IssueContext>\nThis commit is associated with GitHub issue #${issueNumber}.\nIf the changes appear to resolve the issue, include \"Fixes #${issueNumber}\" at the end of the first line of commit message.\n</IssueContext>`\n : ''\n\n return `<Task>\nYou are a software engineer writing a commit message for this repository.\nExamine the staged changes in the git repository and generate a concise, meaningful commit message.\n</Task>\n\n<Requirements>\n<Format>The first line must be a brief summary of the changes made as a full sentence. If it references an issue, include \"Fixes #N\" at the end of this line.\n\nAdd 2 newlines, then add a bullet-point form description of the changes made, each change on a new line.</Format>\n<Mood>Use imperative mood (e.g., \"Add feature\" not \"Added feature\")</Mood>\n<Focus>Be specific about what was changed and why</Focus>\n<Conciseness>Keep message under 72 characters for subject line when possible</Conciseness>\n<NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw commit message.</NoMeta>\n<Examples>\nGood: \"Add user authentication with JWT tokens. Fixes #42\n\n- Implement login and registration endpoints\n- Secure routes with JWT middleware\n- Update user model to store hashed passwords\"\nGood: \"Fix navigation bug in sidebar menu.\"\nBad: \"Based on the changes, I'll create: Add user authentication\"\nBad: \"Looking at the files, this commit should be: Fix navigation bug\"\n</Examples>\n${issueContext}\n</Requirements>\n\n<Output>\nIMPORTANT: Your entire response will be used directly as the git commit message.\nDo not include any explanatory text before or after the commit message.\nStart your response immediately with the commit message text.\n</Output>`\n }\n\n /**\n * Sanitize Claude output to remove meta-commentary and clean formatting\n * Handles cases where Claude includes explanatory text despite instructions\n */\n private sanitizeClaudeOutput(rawOutput: string): string {\n let cleaned = rawOutput.trim()\n\n // Remove common meta-commentary patterns (case-insensitive)\n const metaPatterns = [\n /^.*?based on.*?changes.*?:/i,\n /^.*?looking at.*?files.*?:/i,\n /^.*?examining.*?:/i,\n /^.*?analyzing.*?:/i,\n /^.*?i'll.*?generate.*?:/i,\n /^.*?let me.*?:/i,\n /^.*?the commit message.*?should be.*?:/i,\n /^.*?here.*?is.*?commit.*?message.*?:/i,\n ]\n\n for (const pattern of metaPatterns) {\n cleaned = cleaned.replace(pattern, '').trim()\n }\n\n // Extract content after separators only if it looks like meta-commentary\n // Only split on colons if there's clear meta-commentary before it\n if (cleaned.includes(':')) {\n const colonIndex = cleaned.indexOf(':')\n const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase()\n\n // Only split if the text before colon looks like meta-commentary\n const metaIndicators = [\n 'here is the commit message',\n 'commit message',\n 'here is',\n 'the message should be',\n 'i suggest',\n 'my suggestion'\n ]\n\n const isMetaCommentary = metaIndicators.some(indicator => beforeColon.includes(indicator))\n\n if (isMetaCommentary) {\n const afterColon = cleaned.substring(colonIndex + 1).trim()\n if (afterColon && afterColon.length > 10) {\n cleaned = afterColon\n }\n }\n }\n\n // Remove quotes if the entire message is wrapped in them\n if ((cleaned.startsWith('\"') && cleaned.endsWith('\"')) ||\n (cleaned.startsWith(\"'\") && cleaned.endsWith(\"'\"))) {\n cleaned = cleaned.slice(1, -1).trim()\n }\n\n return cleaned\n }\n\n\n}\n","import { executeGitCommand, findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { SettingsManager } from './SettingsManager.js'\nimport type { MergeOptions } from '../types/index.js'\n\n/**\n * MergeManager handles Git rebase and fast-forward merge operations\n * Implements fail-fast behavior for conflicts (Phase 1 - no Claude assistance)\n *\n * Ports bash/merge-and-clean.sh lines 781-1090\n */\nexport class MergeManager {\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(settingsManager?: SettingsManager) {\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t}\n\n\t/**\n\t * Get the main branch name from settings (defaults to 'main')\n\t * @private\n\t */\n\tprivate async getMainBranch(): Promise<string> {\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\treturn settings.mainBranch ?? 'main'\n\t}\n\n\t/**\n\t * Rebase current branch on main with fail-fast on conflicts\n\t * Ports bash/merge-and-clean.sh lines 781-913\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param options - Merge options (dryRun, force)\n\t * @throws Error if main branch doesn't exist, uncommitted changes exist, or conflicts occur\n\t */\n\tasync rebaseOnMain(worktreePath: string, options: MergeOptions = {}): Promise<void> {\n\t\tconst { dryRun = false, force = false } = options\n\t\tconst mainBranch = await this.getMainBranch()\n\n\t\tlogger.info(`Starting rebase on ${mainBranch} branch...`)\n\n\t\t// Step 1: Check if main branch exists\n\t\ttry {\n\t\t\tawait executeGitCommand(['show-ref', '--verify', '--quiet', `refs/heads/${mainBranch}`], {\n\t\t\t\tcwd: worktreePath,\n\t\t\t})\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t`Main branch \"${mainBranch}\" does not exist. Cannot rebase.\\n` +\n\t\t\t\t\t`Ensure the repository has a \"${mainBranch}\" branch or create it first.`\n\t\t\t)\n\t\t}\n\n\t\t// Step 2: Check for uncommitted changes (defensive check)\n\t\tconst statusOutput = await executeGitCommand(['status', '--porcelain'], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tif (statusOutput.trim()) {\n\t\t\tthrow new Error(\n\t\t\t\t'Uncommitted changes detected. Please commit or stash changes before rebasing.\\n' +\n\t\t\t\t\t'Run: git status to see uncommitted changes\\n' +\n\t\t\t\t\t'Or: il finish will automatically commit them for you'\n\t\t\t)\n\t\t}\n\n\t\t// Step 3: Check if rebase is needed by comparing merge-base with main HEAD\n\t\tconst mergeBase = await executeGitCommand(['merge-base', mainBranch, 'HEAD'], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst mainHead = await executeGitCommand(['rev-parse', mainBranch], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst mergeBaseTrimmed = mergeBase.trim()\n\t\tconst mainHeadTrimmed = mainHead.trim()\n\n\t\t// If merge-base matches main HEAD, branch is already up to date\n\t\tif (mergeBaseTrimmed === mainHeadTrimmed) {\n\t\t\tlogger.success(`Branch is already up to date with ${mainBranch}. No rebase needed.`)\n\t\t\treturn\n\t\t}\n\n\t\t// Step 4: Show commits to be rebased (for informational purposes)\n\t\tconst commitsOutput = await executeGitCommand(['log', '--oneline', `${mainBranch}..HEAD`], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst commits = commitsOutput.trim()\n\t\tconst commitLines = commits ? commits.split('\\n') : []\n\n\t\tif (commits) {\n\t\t\t// Show commits that will be rebased\n\t\t\tlogger.info(`Found ${commitLines.length} commit(s) to rebase:`)\n\t\t\tcommitLines.forEach((commit) => logger.info(` ${commit}`))\n\t\t} else {\n\t\t\t// Main has moved forward but branch has no new commits\n\t\t\tlogger.info(`${mainBranch} branch has moved forward. Rebasing to update branch...`)\n\t\t}\n\n\t\t// Step 5: User confirmation (unless force mode or dry-run)\n\t\tif (!force && !dryRun) {\n\t\t\t// TODO: Implement interactive prompt for confirmation\n\t\t\t// For now, proceeding automatically (use --force to skip this message)\n\t\t\tlogger.info('Proceeding with rebase... (use --force to skip confirmations)')\n\t\t}\n\n\t\t// Step 6: Execute rebase (unless dry-run)\n\t\tif (dryRun) {\n\t\t\tlogger.info(`[DRY RUN] Would execute: git rebase ${mainBranch}`)\n\t\t\tif (commitLines.length > 0) {\n\t\t\t\tlogger.info(`[DRY RUN] This would rebase ${commitLines.length} commit(s)`)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Execute rebase\n\t\ttry {\n\t\t\tawait executeGitCommand(['rebase', mainBranch], { cwd: worktreePath })\n\t\t\tlogger.success('Rebase completed successfully!')\n\t\t} catch (error) {\n\t\t\t// Detect conflicts\n\t\t\tconst conflictedFiles = await this.detectConflictedFiles(worktreePath)\n\n\t\t\tif (conflictedFiles.length > 0) {\n\t\t\t\t// Try Claude-assisted resolution first\n\t\t\t\tlogger.info('Merge conflicts detected, attempting Claude-assisted resolution...')\n\n\t\t\t\tconst resolved = await this.attemptClaudeConflictResolution(\n\t\t\t\t\tworktreePath,\n\t\t\t\t\tconflictedFiles\n\t\t\t\t)\n\n\t\t\t\tif (resolved) {\n\t\t\t\t\tlogger.success('Conflicts resolved with Claude assistance, rebase completed')\n\t\t\t\t\treturn // Continue with successful rebase\n\t\t\t\t}\n\n\t\t\t\t// Claude couldn't resolve or not available - fail fast\n\t\t\t\tconst conflictError = this.formatConflictError(conflictedFiles)\n\t\t\t\tthrow new Error(conflictError)\n\t\t\t}\n\n\t\t\t// If not a conflict, re-throw the original error\n\t\t\tthrow new Error(\n\t\t\t\t`Rebase failed: ${error instanceof Error ? error.message : String(error)}\\n` +\n\t\t\t\t\t'Run: git status for more details\\n' +\n\t\t\t\t\t'Or: git rebase --abort to cancel the rebase'\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Validate that fast-forward merge is possible\n\t * Ports bash/merge-and-clean.sh lines 957-968\n\t *\n\t * @param branchName - Name of the branch to merge\n\t * @param mainWorktreePath - Path where main branch is checked out\n\t * @throws Error if fast-forward is not possible\n\t */\n\tasync validateFastForwardPossible(branchName: string, mainWorktreePath: string): Promise<void> {\n\t\tconst mainBranch = await this.getMainBranch()\n\n\t\t// Step 1: Get merge-base between main and branch\n\t\tconst mergeBase = await executeGitCommand(['merge-base', mainBranch, branchName], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\t// Step 2: Get current HEAD of main\n\t\tconst mainHead = await executeGitCommand(['rev-parse', mainBranch], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\t// Step 3: Compare - they must match for fast-forward\n\t\tconst mergeBaseTrimmed = mergeBase.trim()\n\t\tconst mainHeadTrimmed = mainHead.trim()\n\n\t\tif (mergeBaseTrimmed !== mainHeadTrimmed) {\n\t\t\tthrow new Error(\n\t\t\t\t'Cannot perform fast-forward merge.\\n' +\n\t\t\t\t\t`The ${mainBranch} branch has moved forward since this branch was created.\\n` +\n\t\t\t\t\t`Merge base: ${mergeBaseTrimmed}\\n` +\n\t\t\t\t\t`Main HEAD: ${mainHeadTrimmed}\\n\\n` +\n\t\t\t\t\t'To fix this:\\n' +\n\t\t\t\t\t` 1. Rebase the branch on ${mainBranch}: git rebase ${mainBranch}\\n` +\n\t\t\t\t\t` 2. Or use: il finish to automatically rebase and merge\\n`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Perform fast-forward only merge\n\t * Ports bash/merge-and-clean.sh lines 938-994\n\t *\n\t * @param branchName - Name of the branch to merge\n\t * @param worktreePath - Path to the worktree\n\t * @param options - Merge options (dryRun, force)\n\t * @throws Error if checkout, validation, or merge fails\n\t */\n\tasync performFastForwardMerge(\n\t\tbranchName: string,\n\t\tworktreePath: string,\n\t\toptions: MergeOptions = {}\n\t): Promise<void> {\n\t\tconst { dryRun = false, force = false } = options\n\t\tconst mainBranch = await this.getMainBranch()\n\n\t\tlogger.info('Starting fast-forward merge...')\n\n\t\t// Step 1: Find where main branch is checked out\n\t\t// This copies the bash script approach: find main worktree, run commands from there\n\t\tconst mainWorktreePath = options.repoRoot ??\n\t\t\tawait findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\n\t\t// Step 3: No need to checkout main - it's already checked out in mainWorktreePath\n\t\tlogger.debug(`Using ${mainBranch} branch location: ${mainWorktreePath}`)\n\n\t\t// Step 4: Verify on main branch\n\t\tconst currentBranch = await executeGitCommand(['branch', '--show-current'], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\tif (currentBranch.trim() !== mainBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t`Expected ${mainBranch} branch but found: ${currentBranch.trim()}\\n` +\n\t\t\t\t\t`At location: ${mainWorktreePath}\\n` +\n\t\t\t\t\t'This indicates the main worktree detection failed.'\n\t\t\t)\n\t\t}\n\n\t\t// Step 5: Validate fast-forward is possible\n\t\tawait this.validateFastForwardPossible(branchName, mainWorktreePath)\n\n\t\t// Step 6: Show commits to be merged\n\t\tconst commitsOutput = await executeGitCommand(['log', '--oneline', `${mainBranch}..${branchName}`], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\tconst commits = commitsOutput.trim()\n\n\t\t// If no commits, branch is already merged\n\t\tif (!commits) {\n\t\t\tlogger.success(`Branch is already merged into ${mainBranch}. No merge needed.`)\n\t\t\treturn\n\t\t}\n\n\t\t// Show commits that will be merged\n\t\tconst commitLines = commits.split('\\n')\n\t\tlogger.info(`Found ${commitLines.length} commit(s) to merge:`)\n\t\tcommitLines.forEach((commit) => logger.info(` ${commit}`))\n\n\t\t// Step 7: User confirmation (unless force mode or dry-run)\n\t\tif (!force && !dryRun) {\n\t\t\t// TODO: Implement interactive prompt for confirmation\n\t\t\t// For now, proceeding automatically (use --force to skip this message)\n\t\t\tlogger.info('Proceeding with fast-forward merge... (use --force to skip confirmations)')\n\t\t}\n\n\t\t// Step 8: Execute merge (unless dry-run)\n\t\tif (dryRun) {\n\t\t\tlogger.info(`[DRY RUN] Would execute: git merge --ff-only ${branchName}`)\n\t\t\tlogger.info(`[DRY RUN] This would merge ${commitLines.length} commit(s)`)\n\t\t\treturn\n\t\t}\n\n\t\t// Execute fast-forward merge\n\t\ttry {\n\t\t\tawait executeGitCommand(['merge', '--ff-only', branchName], { cwd: mainWorktreePath })\n\t\t\tlogger.success(`Fast-forward merge completed! Merged ${commitLines.length} commit(s).`)\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Fast-forward merge failed: ${error instanceof Error ? error.message : String(error)}\\n\\n` +\n\t\t\t\t\t'To recover:\\n' +\n\t\t\t\t\t' 1. Check merge status: git status\\n' +\n\t\t\t\t\t' 2. Abort merge if needed: git merge --abort\\n' +\n\t\t\t\t\t' 3. Verify branch is rebased: git rebase main\\n' +\n\t\t\t\t\t' 4. Try merge again: il finish'\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Helper: Detect conflicted files after failed rebase\n\t * @private\n\t */\n\tprivate async detectConflictedFiles(worktreePath: string): Promise<string[]> {\n\t\ttry {\n\t\t\tconst output = await executeGitCommand(['diff', '--name-only', '--diff-filter=U'], {\n\t\t\t\tcwd: worktreePath,\n\t\t\t})\n\n\t\t\treturn output\n\t\t\t\t.trim()\n\t\t\t\t.split('\\n')\n\t\t\t\t.filter((file) => file.length > 0)\n\t\t} catch {\n\t\t\t// If command fails, return empty array (might not be a conflict)\n\t\t\treturn []\n\t\t}\n\t}\n\n\t/**\n\t * Helper: Format conflict error message with manual resolution steps\n\t * @private\n\t */\n\tprivate formatConflictError(conflictedFiles: string[]): string {\n\t\tconst fileList = conflictedFiles.map((file) => ` • ${file}`).join('\\n')\n\n\t\treturn (\n\t\t\t'Rebase failed - merge conflicts detected in:\\n' +\n\t\t\tfileList +\n\t\t\t'\\n\\n' +\n\t\t\t'To resolve manually:\\n' +\n\t\t\t' 1. Fix conflicts in the files above\\n' +\n\t\t\t' 2. Stage resolved files: git add <files>\\n' +\n\t\t\t' 3. Continue rebase: git rebase --continue\\n' +\n\t\t\t' 4. Or abort rebase: git rebase --abort\\n' +\n\t\t\t' 5. Then re-run: il finish <issue-number>'\n\t\t)\n\t}\n\n\t/**\n\t * Attempt to resolve conflicts using Claude\n\t * Ports bash/merge-and-clean.sh lines 839-894\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param conflictedFiles - List of files with conflicts\n\t * @returns true if conflicts resolved, false otherwise\n\t * @private\n\t */\n\tprivate async attemptClaudeConflictResolution(\n\t\tworktreePath: string,\n\t\tconflictedFiles: string[]\n\t): Promise<boolean> {\n\t\t// Check if Claude CLI is available\n\t\tconst isClaudeAvailable = await detectClaudeCli()\n\t\tif (!isClaudeAvailable) {\n\t\t\tlogger.debug('Claude CLI not available, skipping conflict resolution')\n\t\t\treturn false\n\t\t}\n\n\t\tlogger.info(`Launching Claude to resolve conflicts in ${conflictedFiles.length} file(s)...`)\n\n\t\t// Hard-coded prompt matching bash script line 844\n\t\t// No templates, no complexity - just the essential instruction\n\t\tconst prompt =\n\t\t\t`Please help resolve the git rebase conflicts in this repository. ` +\n\t\t\t`Analyze the conflicted files, understand the changes from both branches, ` +\n\t\t\t`fix the conflicts, then run 'git add .' to stage the resolved files, ` +\n\t\t\t`and finally run 'git rebase --continue' to continue the rebase process. ` +\n\t\t\t`Handle the entire workflow for me.`\n\n\t\ttry {\n\t\t\t// Launch Claude interactively in current terminal\n\t\t\t// User will interact directly with Claude to resolve conflicts\n\t\t\tawait launchClaude(prompt, {\n\t\t\t\taddDir: worktreePath,\n\t\t\t\theadless: false, // Interactive - runs in current terminal with stdio: inherit\n\t\t\t})\n\n\t\t\t// After Claude interaction completes, check if conflicts resolved\n\t\t\tconst remainingConflicts = await this.detectConflictedFiles(worktreePath)\n\n\t\t\tif (remainingConflicts.length > 0) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Conflicts still exist in ${remainingConflicts.length} file(s) after Claude assistance`\n\t\t\t\t)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if rebase completed or still in progress\n\t\t\tconst rebaseInProgress = await this.isRebaseInProgress(worktreePath)\n\n\t\t\tif (rebaseInProgress) {\n\t\t\t\tlogger.warn('Rebase still in progress after Claude assistance')\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Success: no conflicts, rebase completed\n\t\t\tlogger.success('Claude successfully resolved conflicts and completed rebase')\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tlogger.warn('Claude conflict resolution failed', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Check if a git rebase is currently in progress\n\t * Checks for .git/rebase-merge or .git/rebase-apply directories\n\t * Ports bash script logic from lines 853-856\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @returns true if rebase in progress, false otherwise\n\t * @private\n\t */\n\tprivate async isRebaseInProgress(worktreePath: string): Promise<boolean> {\n\t\tconst fs = await import('node:fs/promises')\n\t\tconst path = await import('node:path')\n\n\t\tconst rebaseMergePath = path.join(worktreePath, '.git', 'rebase-merge')\n\t\tconst rebaseApplyPath = path.join(worktreePath, '.git', 'rebase-apply')\n\n\t\t// Check for rebase-merge directory\n\t\ttry {\n\t\t\tawait fs.access(rebaseMergePath)\n\t\t\treturn true\n\t\t} catch {\n\t\t\t// Directory doesn't exist, continue checking\n\t\t}\n\n\t\t// Check for rebase-apply directory\n\t\ttry {\n\t\t\tawait fs.access(rebaseApplyPath)\n\t\t\treturn true\n\t\t} catch {\n\t\t\t// Directory doesn't exist\n\t\t}\n\n\t\treturn false\n\t}\n}\n","import { logger } from '../utils/logger.js'\nimport { detectPackageManager, runScript } from '../utils/package-manager.js'\nimport { readPackageJson, hasScript } from '../utils/package-json.js'\nimport { ProjectCapabilityDetector } from './ProjectCapabilityDetector.js'\n\nexport interface BuildOptions {\n\tdryRun?: boolean\n}\n\nexport interface BuildResult {\n\tsuccess: boolean\n\tskipped: boolean\n\treason?: string\n\tduration: number\n}\n\n/**\n * BuildRunner handles post-merge build verification for CLI projects\n * Only runs build when project has CLI capabilities (bin field in package.json)\n */\nexport class BuildRunner {\n\tprivate capabilityDetector: ProjectCapabilityDetector\n\n\tconstructor(capabilityDetector?: ProjectCapabilityDetector) {\n\t\tthis.capabilityDetector = capabilityDetector ?? new ProjectCapabilityDetector()\n\t}\n\n\t/**\n\t * Run build verification in the specified directory\n\t * @param buildPath - Path where build should run (typically main worktree path)\n\t * @param options - Build options\n\t */\n\tasync runBuild(buildPath: string, options: BuildOptions = {}): Promise<BuildResult> {\n\t\tconst startTime = Date.now()\n\n\t\ttry {\n\t\t\t// Step 1: Check if build script exists\n\t\t\tconst pkgJson = await readPackageJson(buildPath)\n\t\t\tconst hasBuildScript = hasScript(pkgJson, 'build')\n\n\t\t\tif (!hasBuildScript) {\n\t\t\t\tlogger.debug('Skipping build - no build script found')\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: 'No build script found in package.json',\n\t\t\t\t\tduration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip build for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping build - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: 'No package.json found in project',\n\t\t\t\t\tduration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\t// Step 2: Check if project has CLI capability (bin field)\n\t\tconst capabilities = await this.capabilityDetector.detectCapabilities(buildPath)\n\t\tconst isCLIProject = capabilities.capabilities.includes('cli')\n\n\t\tif (!isCLIProject) {\n\t\t\tlogger.debug('Skipping build - not a CLI project (no bin field)')\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: true,\n\t\t\t\treason: 'Project is not a CLI project (no bin field in package.json)',\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t}\n\n\t\t// Step 3: Detect package manager\n\t\tconst packageManager = await detectPackageManager(buildPath)\n\n\t\t// Step 4: Handle dry-run mode\n\t\tif (options.dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run build' : `${packageManager} build`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t}\n\n\t\t// Step 5: Execute build\n\t\tlogger.info('Running build...')\n\n\t\ttry {\n\t\t\tawait runScript('build', buildPath, [], { quiet: true })\n\t\t\tlogger.success('Build completed successfully')\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Step 6: Throw detailed error on failure\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run build' : `${packageManager} build`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Build failed.\\n` +\n\t\t\t\t\t`Fix build errors before proceeding.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n}\n","import { logger } from '../utils/logger.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ValidationRunner } from '../lib/ValidationRunner.js'\nimport { CommitManager } from '../lib/CommitManager.js'\nimport { MergeManager } from '../lib/MergeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { ResourceCleanup } from '../lib/ResourceCleanup.js'\nimport { ProcessManager } from '../lib/process/ProcessManager.js'\nimport { BuildRunner } from '../lib/BuildRunner.js'\nimport { DatabaseManager } from '../lib/DatabaseManager.js'\nimport { NeonProvider } from '../lib/providers/NeonProvider.js'\nimport { EnvironmentManager } from '../lib/EnvironmentManager.js'\nimport { CLIIsolationManager } from '../lib/CLIIsolationManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { loadEnvIntoProcess } from '../utils/env.js'\nimport { installDependencies } from '../utils/package-manager.js'\nimport type { FinishOptions, GitWorktree, CommitOptions, MergeOptions, PullRequest } from '../types/index.js'\nimport type { ResourceCleanupOptions, CleanupResult } from '../types/cleanup.js'\nimport type { ParsedInput } from './start.js'\nimport path from 'path'\n\nexport interface FinishCommandInput {\n\tidentifier?: string | undefined // Optional - can be auto-detected\n\toptions: FinishOptions\n}\n\nexport interface ParsedFinishInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: number // For issues and PRs\n\tbranchName?: string // For branch inputs\n\toriginalInput: string // Raw input for error messages\n\tautoDetected?: boolean // True if detected from current directory\n}\n\nexport class FinishCommand {\n\tprivate gitHubService: GitHubService\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate validationRunner: ValidationRunner\n\tprivate commitManager: CommitManager\n\tprivate mergeManager: MergeManager\n\tprivate identifierParser: IdentifierParser\n\tprivate resourceCleanup?: ResourceCleanup\n\tprivate buildRunner: BuildRunner\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(\n\t\tgitHubService?: GitHubService,\n\t\tgitWorktreeManager?: GitWorktreeManager,\n\t\tvalidationRunner?: ValidationRunner,\n\t\tcommitManager?: CommitManager,\n\t\tmergeManager?: MergeManager,\n\t\tidentifierParser?: IdentifierParser,\n\t\tresourceCleanup?: ResourceCleanup,\n\t\tbuildRunner?: BuildRunner,\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\t// Load environment variables first\n\t\tconst envResult = loadEnvIntoProcess()\n\t\tif (envResult.error) {\n\t\t\tlogger.debug(`Environment loading warning: ${envResult.error.message}`)\n\t\t}\n\t\tif (envResult.parsed) {\n\t\t\tlogger.debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`)\n\t\t}\n\n\t\t// Dependency injection for testing\n\t\tthis.gitHubService = gitHubService ?? new GitHubService()\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.validationRunner = validationRunner ?? new ValidationRunner()\n\t\tthis.commitManager = commitManager ?? new CommitManager()\n\t\tthis.mergeManager = mergeManager ?? new MergeManager()\n\t\tthis.identifierParser = identifierParser ?? new IdentifierParser(this.gitWorktreeManager)\n\n\t\t// Initialize settingsManager first (needed for ResourceCleanup)\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\n\t\t// ResourceCleanup will be initialized lazily with proper configuration\n\t\tif (resourceCleanup) {\n\t\t\tthis.resourceCleanup = resourceCleanup\n\t\t}\n\n\t\tthis.buildRunner = buildRunner ?? new BuildRunner()\n\t}\n\n\t/**\n\t * Lazy initialization of ResourceCleanup with properly configured DatabaseManager\n\t */\n\tprivate async ensureResourceCleanup(): Promise<void> {\n\t\tif (this.resourceCleanup) {\n\t\t\treturn\n\t\t}\n\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst databaseUrlEnvVarName = settings.capabilities?.database?.databaseUrlEnvVarName ?? 'DATABASE_URL'\n\n\t\tconst environmentManager = new EnvironmentManager()\n\t\tconst neonProvider = new NeonProvider({\n\t\t\tprojectId: process.env.NEON_PROJECT_ID ?? '',\n\t\t\tparentBranch: process.env.NEON_PARENT_BRANCH ?? '',\n\t\t})\n\t\tconst databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName)\n\t\tconst cliIsolationManager = new CLIIsolationManager()\n\n\t\tthis.resourceCleanup = new ResourceCleanup(\n\t\t\tthis.gitWorktreeManager,\n\t\t\tnew ProcessManager(),\n\t\t\tdatabaseManager,\n\t\t\tcliIsolationManager\n\t\t)\n\t}\n\n\t/**\n\t * Main entry point for finish command\n\t */\n\tpublic async execute(input: FinishCommandInput): Promise<void> {\n\t\ttry {\n\t\t\t// Step 1: Parse input (or auto-detect from current directory)\n\t\t\tconst parsed = await this.parseInput(input.identifier, input.options)\n\n\t\t\t// Step 2: Validate based on type and get worktrees\n\t\t\tconst worktrees = await this.validateInput(parsed, input.options)\n\n\t\t\t// Step 3: Log success\n\t\t\tlogger.info(`Validated input: ${this.formatParsedInput(parsed)}`)\n\n\t\t\t// Get worktree for workflow execution\n\t\t\tconst worktree = worktrees[0]\n\t\t\tif (!worktree) {\n\t\t\t\tthrow new Error('No worktree found')\n\t\t\t}\n\n\t\t\t// Step 4: Branch based on input type\n\t\t\tif (parsed.type === 'pr') {\n\t\t\t\t// Fetch PR to get current state\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\t\t\t\tconst pr = await this.gitHubService.fetchPR(parsed.number)\n\t\t\t\tawait this.executePRWorkflow(parsed, input.options, worktree, pr)\n\t\t\t} else {\n\t\t\t\t// Execute traditional issue/branch workflow\n\t\t\t\tawait this.executeIssueWorkflow(parsed, input.options, worktree)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.error(`${error.message}`)\n\t\t\t} else {\n\t\t\t\tlogger.error('An unknown error occurred')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Parse input to determine type and extract relevant data\n\t * Supports auto-detection from current directory when identifier is undefined\n\t */\n\tprivate async parseInput(\n\t\tidentifier: string | undefined,\n\t\toptions: FinishOptions\n\t): Promise<ParsedFinishInput> {\n\t\t// Priority 1: --pr flag overrides everything\n\t\tif (options.pr !== undefined) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: options.pr,\n\t\t\t\toriginalInput: `--pr ${options.pr}`,\n\t\t\t\tautoDetected: false,\n\t\t\t}\n\t\t}\n\n\t\t// Priority 2: Explicit identifier provided\n\t\tif (identifier?.trim()) {\n\t\t\treturn await this.parseExplicitInput(identifier.trim())\n\t\t}\n\n\t\t// Priority 3: Auto-detect from current directory\n\t\treturn await this.autoDetectFromCurrentDirectory()\n\t}\n\n\t/**\n\t * Parse explicit identifier input using pattern-based detection\n\t * (No GitHub API calls - uses IdentifierParser)\n\t */\n\tprivate async parseExplicitInput(\n\t\tidentifier: string\n\t): Promise<ParsedFinishInput> {\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123\n\t\tconst prPattern = /^(?:pr|PR)[/-](\\d+)$/\n\t\tconst prMatch = identifier.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: parseInt(prMatch[1], 10),\n\t\t\t\toriginalInput: identifier,\n\t\t\t\tautoDetected: false,\n\t\t\t}\n\t\t}\n\n\t\t// Use IdentifierParser for pattern-based detection\n\t\t// (checks existing worktrees, no GitHub API calls)\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach finish command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in finish command')\n\t\t}\n\n\t\t// Convert ParsedInput to ParsedFinishInput (add autoDetected field)\n\t\tconst result: ParsedFinishInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\t// Add number or branchName based on type\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect PR or issue from current directory\n\t * Ports logic from merge-current-issue.sh lines 30-52\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedFinishInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\t// Pattern: /.*_pr_(\\d+)$/\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory or branch name\n\t\t// Pattern: /issue-(\\d+)/\n\t\tconst issuePattern = /issue-(\\d+)/\n\t\tconst issueMatch = currentDir.match(issuePattern)\n\n\t\tif (issueMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(issueMatch[1], 10)\n\t\t\tlogger.debug(\n\t\t\t\t`Auto-detected issue #${issueNumber} from directory: ${currentDir}`\n\t\t\t)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueMatch = currentBranch.match(issuePattern)\n\t\tif (branchIssueMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(branchIssueMatch[1], 10)\n\t\t\tlogger.debug(\n\t\t\t\t`Auto-detected issue #${issueNumber} from branch: ${currentBranch}`\n\t\t\t)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Validate the parsed input based on its type\n\t */\n\tprivate async validateInput(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions\n\t): Promise<GitWorktree[]> {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\n\t\t\t\t// Fetch PR from GitHub\n\t\t\t\tconst pr = await this.gitHubService.fetchPR(parsed.number)\n\n\t\t\t\t// For PRs, we allow closed/merged state (cleanup-only mode)\n\t\t\t\t// But we still validate it exists\n\t\t\t\tlogger.debug(`Validated PR #${parsed.number} (state: ${pr.state})`)\n\n\t\t\t\t// Find associated worktree\n\t\t\t\treturn await this.findWorktreeForIdentifier(parsed)\n\t\t\t}\n\n\t\t\tcase 'issue': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid issue number')\n\t\t\t\t}\n\n\t\t\t\t// Fetch issue from GitHub\n\t\t\t\tconst issue = await this.gitHubService.fetchIssue(parsed.number)\n\n\t\t\t\t// Validate issue state (warn if closed unless --force)\n\t\t\t\tif (issue.state === 'closed' && !options.force) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Issue #${parsed.number} is closed. Use --force to finish anyway.`\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tlogger.debug(`Validated issue #${parsed.number} (state: ${issue.state})`)\n\n\t\t\t\t// Find associated worktree\n\t\t\t\treturn await this.findWorktreeForIdentifier(parsed)\n\t\t\t}\n\n\t\t\tcase 'branch': {\n\t\t\t\tif (!parsed.branchName) {\n\t\t\t\t\tthrow new Error('Invalid branch name')\n\t\t\t\t}\n\n\t\t\t\t// Validate branch name format\n\t\t\t\tif (!this.isValidBranchName(parsed.branchName)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes'\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tlogger.debug(`Validated branch name: ${parsed.branchName}`)\n\n\t\t\t\t// Find associated worktree\n\t\t\t\treturn await this.findWorktreeForIdentifier(parsed)\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownType = parsed as { type: string }\n\t\t\t\tthrow new Error(`Unknown input type: ${unknownType.type}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier using specific methods based on type\n\t * (uses precise pattern matching instead of broad substring matching)\n\t * Throws error if not found\n\t */\n\tprivate async findWorktreeForIdentifier(\n\t\tparsed: ParsedFinishInput\n\t): Promise<GitWorktree[]> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\t// Use specific finding methods based on parsed type\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\t\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(\n\t\t\t\t\tparsed.number,\n\t\t\t\t\t''\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'issue': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid issue number')\n\t\t\t\t}\n\t\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(\n\t\t\t\t\tparsed.number\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'branch': {\n\t\t\t\tif (!parsed.branchName) {\n\t\t\t\t\tthrow new Error('Invalid branch name')\n\t\t\t\t}\n\t\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\t\tparsed.branchName\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownType = parsed as { type: string }\n\t\t\t\tthrow new Error(`Unknown input type: ${unknownType.type}`)\n\t\t\t}\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Use 'il list' to see available worktrees.`\n\t\t\t)\n\t\t}\n\n\t\tlogger.debug(`Found worktree: ${worktree.path}`)\n\n\t\treturn [worktree]\n\t}\n\n\t/**\n\t * Validate branch name format\n\t */\n\tprivate isValidBranchName(branch: string): boolean {\n\t\t// Pattern from bash script and StartCommand\n\t\treturn /^[a-zA-Z0-9/_-]+$/.test(branch)\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedFinishInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr':\n\t\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t\tcase 'issue':\n\t\t\t\treturn `Issue #${parsed.number}${autoLabel}`\n\t\t\tcase 'branch':\n\t\t\t\treturn `Branch '${parsed.branchName}'${autoLabel}`\n\t\t\tdefault:\n\t\t\t\treturn 'Unknown input'\n\t\t}\n\t}\n\n\t/**\n\t * Execute workflow for issues and branches (merge into main)\n\t * This is the traditional workflow: validate → commit → rebase → merge → cleanup\n\t */\n\tprivate async executeIssueWorkflow(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree\n\t): Promise<void> {\n\t\t// Step 1: Run pre-merge validations FIRST (Sub-Issue #47)\n\t\tif (!options.dryRun) {\n\t\t\tlogger.info('Running pre-merge validations...')\n\n\t\t\tawait this.validationRunner.runValidations(worktree.path, {\n\t\t\t\tdryRun: options.dryRun ?? false,\n\t\t\t})\n\t\t\tlogger.success('All validations passed')\n\t\t} else {\n\t\t\tlogger.info('[DRY RUN] Would run pre-merge validations')\n\t\t}\n\n\t\t// Step 2: Detect uncommitted changes AFTER validation passes\n\t\tconst gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path)\n\n\t\t// Step 3: Commit changes only if validation passed AND changes exist\n\t\tif (gitStatus.hasUncommittedChanges) {\n\t\t\tif (options.dryRun) {\n\t\t\t\tlogger.info('[DRY RUN] Would auto-commit uncommitted changes (validation passed)')\n\t\t\t} else {\n\t\t\t\tlogger.info('Validation passed, auto-committing uncommitted changes...')\n\n\t\t\t\t// Load settings to get skipVerify configuration\n\t\t\t\tconst settings = await this.settingsManager.loadSettings(worktree.path)\n\t\t\t\tconst skipVerify = settings.workflows?.issue?.noVerify ?? false\n\n\t\t\t\tconst commitOptions: CommitOptions = {\n\t\t\t\t\tdryRun: options.dryRun ?? false,\n\t\t\t\t\tskipVerify,\n\t\t\t\t}\n\n\t\t\t\t// Only add issueNumber if it's an issue\n\t\t\t\tif (parsed.type === 'issue' && parsed.number) {\n\t\t\t\t\tcommitOptions.issueNumber = parsed.number\n\t\t\t\t}\n\n\t\t\t\tawait this.commitManager.commitChanges(worktree.path, commitOptions)\n\n\t\t\t\tlogger.success('Changes committed successfully')\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.debug('No uncommitted changes found')\n\t\t}\n\n\t\t// Step 4: Rebase branch on main\n\t\tlogger.info('Rebasing branch on main...')\n\n\t\tconst mergeOptions: MergeOptions = {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t\tforce: options.force ?? false,\n\t\t}\n\n\t\tawait this.mergeManager.rebaseOnMain(worktree.path, mergeOptions)\n\t\tlogger.success('Branch rebased successfully')\n\n\t\t// Step 5: Perform fast-forward merge\n\t\tlogger.info('Performing fast-forward merge...')\n\t\tawait this.mergeManager.performFastForwardMerge(worktree.branch, worktree.path, mergeOptions)\n\t\tlogger.success('Fast-forward merge completed successfully')\n\n\t\t// Step 5.5: Install dependencies in main worktree\n\t\tif (options.dryRun) {\n\t\t\tlogger.info('[DRY RUN] Would install dependencies in main worktree')\n\t\t} else {\n\t\t\tlogger.info('Installing dependencies in main worktree...')\n\t\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager)\n\t\t\tawait installDependencies(mainWorktreePath, true)\n\t\t}\n\n\t\t// Step 5.6: Run post-merge build verification (CLI projects only)\n\t\tif (!options.skipBuild) {\n\t\t\tawait this.runPostMergeBuild(worktree.path, options)\n\t\t} else {\n\t\t\tlogger.debug('Skipping build verification (--skip-build flag provided)')\n\t\t}\n\n\t\t// Step 6: Post-merge cleanup\n\t\tawait this.performPostMergeCleanup(parsed, options, worktree)\n\t}\n\n\t/**\n\t * Execute workflow for Pull Requests\n\t * Behavior depends on PR state:\n\t * - OPEN: Commit changes, push to remote, keep worktree active\n\t * - CLOSED/MERGED: Skip to cleanup\n\t */\n\tprivate async executePRWorkflow(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree,\n\t\tpr: PullRequest\n\t): Promise<void> {\n\t\t// Branch based on PR state\n\t\tif (pr.state === 'closed' || pr.state === 'merged') {\n\t\t\t// Closed/Merged PR workflow\n\t\t\tlogger.info(`PR #${parsed.number} is ${pr.state.toUpperCase()} - skipping to cleanup`)\n\n\t\t\t// Check for uncommitted changes and warn (unless --force)\n\t\t\tconst gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path)\n\t\t\tif (gitStatus.hasUncommittedChanges && !options.force) {\n\t\t\t\tlogger.warn('PR has uncommitted changes')\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Cannot cleanup PR with uncommitted changes. ' +\n\t\t\t\t\t'Commit or stash changes, then run again with --force to cleanup anyway.'\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Call cleanup directly with deleteBranch: true\n\t\t\tawait this.performPRCleanup(parsed, options, worktree)\n\n\t\t\tlogger.success(`PR #${parsed.number} cleanup completed`)\n\t\t} else {\n\t\t\t// Open PR workflow\n\t\t\tlogger.info(`PR #${parsed.number} is OPEN - will push changes and keep worktree active`)\n\n\t\t\t// Step 1: Detect uncommitted changes\n\t\t\tconst gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path)\n\n\t\t\t// Step 2: Commit changes if any exist\n\t\t\tif (gitStatus.hasUncommittedChanges) {\n\t\t\t\tif (options.dryRun) {\n\t\t\t\t\tlogger.info('[DRY RUN] Would commit uncommitted changes')\n\t\t\t\t} else {\n\t\t\t\t\tlogger.info('Committing uncommitted changes...')\n\n\t\t\t\t\t// Load settings to get skipVerify configuration\n\t\t\t\t\tconst settings = await this.settingsManager.loadSettings(worktree.path)\n\t\t\t\t\tconst skipVerify = settings.workflows?.pr?.noVerify ?? false\n\n\t\t\t\t\tawait this.commitManager.commitChanges(worktree.path, {\n\t\t\t\t\t\tdryRun: false,\n\t\t\t\t\t\tskipVerify,\n\t\t\t\t\t\t// Do NOT pass issueNumber for PRs - no \"Fixes #\" trailer needed\n\t\t\t\t\t})\n\t\t\t\t\tlogger.success('Changes committed')\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogger.debug('No uncommitted changes found')\n\t\t\t}\n\n\t\t\t// Step 3: Push to remote\n\t\t\tif (options.dryRun) {\n\t\t\t\tlogger.info(`[DRY RUN] Would push changes to origin/${pr.branch}`)\n\t\t\t} else {\n\t\t\t\tlogger.info('Pushing changes to remote...')\n\t\t\t\tconst { pushBranchToRemote } = await import('../utils/git.js')\n\t\t\t\tawait pushBranchToRemote(pr.branch, worktree.path, {\n\t\t\t\t\tdryRun: false\n\t\t\t\t})\n\t\t\t\tlogger.success(`Changes pushed to PR #${parsed.number}`)\n\t\t\t}\n\n\t\t\t// Step 4: Log success and guidance\n\t\t\tlogger.success(`PR #${parsed.number} updated successfully`)\n\t\t\tlogger.info('Worktree remains active for continued work')\n\t\t\tlogger.info(`To cleanup when done: il cleanup ${parsed.number}`)\n\t\t}\n\t}\n\n\t/**\n\t * Perform cleanup for closed/merged PRs\n\t * Similar to performPostMergeCleanup but with different messaging\n\t */\n\tprivate async performPRCleanup(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree\n\t): Promise<void> {\n\t\t// Convert to ParsedInput format\n\t\tconst cleanupInput: ParsedInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\t...(parsed.number !== undefined && { number: parsed.number }),\n\t\t\t...(parsed.branchName !== undefined && { branchName: parsed.branchName }),\n\t\t}\n\n\t\tconst cleanupOptions: ResourceCleanupOptions = {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t\tdeleteBranch: true, // Delete branch for closed/merged PRs\n\t\t\tkeepDatabase: false,\n\t\t\tforce: options.force ?? false,\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.ensureResourceCleanup()\n\t\t\tif (!this.resourceCleanup) {\n\t\t\t\tthrow new Error('Failed to initialize ResourceCleanup')\n\t\t\t}\n\t\t\tconst result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions)\n\n\t\t\tthis.reportCleanupResults(result)\n\n\t\t\tif (!result.success) {\n\t\t\t\tlogger.warn('Some cleanup operations failed - manual cleanup may be required')\n\t\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tlogger.warn(`Cleanup failed: ${errorMessage}`)\n\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t\tthrow error // Re-throw to fail the command\n\t\t}\n\t}\n\n\t/**\n\t * Run post-merge build verification for CLI projects\n\t * Runs in main worktree to verify merged code builds successfully\n\t */\n\tprivate async runPostMergeBuild(\n\t\tworktreePath: string,\n\t\toptions: FinishOptions\n\t): Promise<void> {\n\t\t// Find main worktree path\n\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\n\t\t// Check if dry-run\n\t\tif (options.dryRun) {\n\t\t\tlogger.info('[DRY RUN] Would run post-merge build')\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info('Running post-merge build...')\n\n\t\tconst result = await this.buildRunner.runBuild(mainWorktreePath, {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t})\n\n\t\tif (result.skipped) {\n\t\t\tlogger.debug(`Build skipped: ${result.reason}`)\n\t\t} else {\n\t\t\tlogger.success('Post-merge build completed successfully')\n\t\t}\n\t}\n\n\t/**\n\t * Perform post-merge cleanup operations\n\t * Converts ParsedFinishInput to ParsedInput and calls ResourceCleanup\n\t * Handles failures gracefully without throwing\n\t */\n\tprivate async performPostMergeCleanup(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree\n\t): Promise<void> {\n\t\t// Convert ParsedFinishInput to ParsedInput (drop autoDetected field)\n\t\tconst cleanupInput: ParsedInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\t...(parsed.number !== undefined && { number: parsed.number }),\n\t\t\t...(parsed.branchName !== undefined && { branchName: parsed.branchName }),\n\t\t}\n\n\t\tconst cleanupOptions: ResourceCleanupOptions = {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t\tdeleteBranch: true, // Delete branch after successful merge\n\t\t\tkeepDatabase: false, // Clean up database after merge\n\t\t\tforce: options.force ?? false,\n\t\t}\n\n\t\ttry {\n\t\t\tlogger.info('Starting post-merge cleanup...')\n\n\t\t\tawait this.ensureResourceCleanup()\n\t\t\tif (!this.resourceCleanup) {\n\t\t\t\tthrow new Error('Failed to initialize ResourceCleanup')\n\t\t\t}\n\t\t\tconst result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions)\n\n\t\t\t// Report cleanup results\n\t\t\tthis.reportCleanupResults(result)\n\n\t\t\tif (!result.success) {\n\t\t\t\tlogger.warn('Some cleanup operations failed - manual cleanup may be required')\n\t\t\t\t// Show helpful recovery message\n\t\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t\t} else {\n\t\t\t\tlogger.success('Post-merge cleanup completed successfully')\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Catch cleanup errors to prevent finish command from failing\n\t\t\t// (merge already succeeded - cleanup failures are non-fatal)\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tlogger.warn(`Cleanup failed: ${errorMessage}`)\n\t\t\tlogger.warn('Merge completed successfully, but manual cleanup is required')\n\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t}\n\t}\n\n\t/**\n\t * Report cleanup operation results to user\n\t */\n\tprivate reportCleanupResults(result: CleanupResult): void {\n\t\tif (result.operations.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info('Cleanup operations:')\n\t\tfor (const op of result.operations) {\n\t\t\tconst status = op.success ? '✓' : '✗'\n\t\t\tconst message = op.error ? `${op.message}: ${op.error}` : op.message\n\n\t\t\tif (op.success) {\n\t\t\t\tlogger.info(` ${status} ${message}`)\n\t\t\t} else {\n\t\t\t\tlogger.warn(` ${status} ${message}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Show manual cleanup instructions when cleanup fails\n\t */\n\tprivate showManualCleanupInstructions(worktree: GitWorktree): void {\n\t\tlogger.info('\\nManual cleanup commands:')\n\t\tlogger.info(` 1. Remove worktree: git worktree remove ${worktree.path}`)\n\t\tlogger.info(` 2. Delete branch: git branch -d ${worktree.branch}`)\n\t\tlogger.info(` 3. Check dev servers: lsof -i :PORT (and kill if needed)`)\n\t}\n}\n"],"mappings":"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","names":["path"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/ValidationRunner.ts","../src/lib/CommitManager.ts","../src/lib/MergeManager.ts","../src/lib/BuildRunner.ts","../src/commands/finish.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { detectPackageManager, runScript } from '../utils/package-manager.js'\nimport { readPackageJson, hasScript } from '../utils/package-json.js'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport type {\n\tValidationOptions,\n\tValidationResult,\n\tValidationStepResult,\n} from '../types/index.js'\n\n/**\n * ValidationRunner orchestrates pre-merge validation pipeline\n * Runs typecheck, lint, and tests in sequence with fail-fast behavior\n */\nexport class ValidationRunner {\n\t/**\n\t * Run all validations in sequence: typecheck → lint → test\n\t * Fails fast on first error\n\t */\n\tasync runValidations(\n\t\tworktreePath: string,\n\t\toptions: ValidationOptions = {}\n\t): Promise<ValidationResult> {\n\t\tconst startTime = Date.now()\n\t\tconst steps: ValidationStepResult[] = []\n\n\t\t// Run typecheck\n\t\tif (!options.skipTypecheck) {\n\t\t\tconst typecheckResult = await this.runTypecheck(\n\t\t\t\tworktreePath,\n\t\t\t\toptions.dryRun ?? false\n\t\t\t)\n\t\t\tsteps.push(typecheckResult)\n\n\t\t\tif (!typecheckResult.passed && !typecheckResult.skipped) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tsteps,\n\t\t\t\t\ttotalDuration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Run lint\n\t\tif (!options.skipLint) {\n\t\t\tconst lintResult = await this.runLint(worktreePath, options.dryRun ?? false)\n\t\t\tsteps.push(lintResult)\n\n\t\t\tif (!lintResult.passed && !lintResult.skipped) {\n\t\t\t\treturn { success: false, steps, totalDuration: Date.now() - startTime }\n\t\t\t}\n\t\t}\n\n\t\t// Run tests\n\t\tif (!options.skipTests) {\n\t\t\tconst testResult = await this.runTests(\n\t\t\t\tworktreePath,\n\t\t\t\toptions.dryRun ?? false\n\t\t\t)\n\t\t\tsteps.push(testResult)\n\n\t\t\tif (!testResult.passed && !testResult.skipped) {\n\t\t\t\treturn { success: false, steps, totalDuration: Date.now() - startTime }\n\t\t\t}\n\t\t}\n\n\t\treturn { success: true, steps, totalDuration: Date.now() - startTime }\n\t}\n\n\t/**\n\t * Run typecheck validation\n\t */\n\tprivate async runTypecheck(\n\t\tworktreePath: string,\n\t\tdryRun: boolean\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if typecheck script exists\n\t\t\tconst pkgJson = await readPackageJson(worktreePath)\n\t\t\tconst hasTypecheckScript = hasScript(pkgJson, 'typecheck')\n\n\t\t\tif (!hasTypecheckScript) {\n\t\t\t\tlogger.debug('Skipping typecheck - no typecheck script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping typecheck - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm'\n\t\t\t\t\t? 'npm run typecheck'\n\t\t\t\t\t: `${packageManager} typecheck`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'typecheck',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tlogger.info('Running typecheck...')\n\n\t\ttry {\n\t\t\tawait runScript('typecheck', worktreePath, [], { quiet: true })\n\t\t\tlogger.success('Typecheck passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'typecheck',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'typecheck',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Typecheck passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'typecheck',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm'\n\t\t\t\t\t? 'npm run typecheck'\n\t\t\t\t\t: `${packageManager} typecheck`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Typecheck failed.\\n` +\n\t\t\t\t\t`Fix type errors before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Run lint validation\n\t */\n\tprivate async runLint(\n\t\tworktreePath: string,\n\t\tdryRun: boolean\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if lint script exists\n\t\t\tconst pkgJson = await readPackageJson(worktreePath)\n\t\t\tconst hasLintScript = hasScript(pkgJson, 'lint')\n\n\t\t\tif (!hasLintScript) {\n\t\t\t\tlogger.debug('Skipping lint - no lint script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping lint - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run lint' : `${packageManager} lint`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'lint',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tlogger.info('Running lint...')\n\n\t\ttry {\n\t\t\tawait runScript('lint', worktreePath, [], { quiet: true })\n\t\t\tlogger.success('Linting passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'lint',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'lint',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Linting passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'lint',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run lint' : `${packageManager} lint`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Linting failed.\\n` +\n\t\t\t\t\t`Fix linting errors before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Run test validation\n\t */\n\tprivate async runTests(\n\t\tworktreePath: string,\n\t\tdryRun: boolean\n\t): Promise<ValidationStepResult> {\n\t\tconst stepStartTime = Date.now()\n\n\t\ttry {\n\t\t\t// Check if test script exists\n\t\t\tconst pkgJson = await readPackageJson(worktreePath)\n\t\t\tconst hasTestScript = hasScript(pkgJson, 'test')\n\n\t\t\tif (!hasTestScript) {\n\t\t\t\tlogger.debug('Skipping tests - no test script found')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip validation for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping tests - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\tconst packageManager = await detectPackageManager(worktreePath)\n\n\t\tif (dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run test' : `${packageManager} test`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tstep: 'test',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t}\n\n\t\tlogger.info('Running tests...')\n\n\t\ttry {\n\t\t\tawait runScript('test', worktreePath, [], { quiet: true })\n\t\t\tlogger.success('Tests passed')\n\n\t\t\treturn {\n\t\t\t\tstep: 'test',\n\t\t\t\tpassed: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Attempt Claude-assisted fix before failing\n\t\t\tconst fixed = await this.attemptClaudeFix(\n\t\t\t\t'test',\n\t\t\t\tworktreePath,\n\t\t\t\tpackageManager\n\t\t\t)\n\n\t\t\tif (fixed) {\n\t\t\t\t// logger.success('Tests passed after Claude auto-fix')\n\t\t\t\treturn {\n\t\t\t\t\tstep: 'test',\n\t\t\t\t\tpassed: true,\n\t\t\t\t\tskipped: false,\n\t\t\t\t\tduration: Date.now() - stepStartTime,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Claude couldn't fix - throw original error\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run test' : `${packageManager} test`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Tests failed.\\n` +\n\t\t\t\t\t`Fix test failures before merging.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Attempt to fix validation errors using Claude\n\t * Pattern based on MergeManager.attemptClaudeConflictResolution\n\t *\n\t * @param validationType - Type of validation that failed ('typecheck' | 'lint' | 'test')\n\t * @param worktreePath - Path to the worktree\n\t * @param packageManager - Detected package manager\n\t * @returns true if Claude fixed the issue, false otherwise\n\t */\n\tprivate async attemptClaudeFix(\n\t\tvalidationType: 'typecheck' | 'lint' | 'test',\n\t\tworktreePath: string,\n\t\tpackageManager: string\n\t): Promise<boolean> {\n\t\t// Check if Claude CLI is available\n\t\tconst isClaudeAvailable = await detectClaudeCli()\n\t\tif (!isClaudeAvailable) {\n\t\t\tlogger.debug('Claude CLI not available, skipping auto-fix')\n\t\t\treturn false\n\t\t}\n\n\t\t// Build validation command for the prompt\n\t\tconst validationCommand = this.getValidationCommand(validationType, packageManager)\n\n\t\t// Build prompt based on validation type (matching bash script prompts)\n\t\tconst prompt = this.getClaudePrompt(validationType, validationCommand)\n\n\t\tconst validationTypeCapitalized = validationType.charAt(0).toUpperCase() + validationType.slice(1)\n\t\tlogger.info(`Launching Claude to help fix ${validationTypeCapitalized} errors...`)\n\n\t\ttry {\n\t\t\t// Launch Claude in interactive mode with acceptEdits permission\n\t\t\tawait launchClaude(prompt, {\n\t\t\t\taddDir: worktreePath,\n\t\t\t\theadless: false, // Interactive mode\n\t\t\t\tpermissionMode: 'acceptEdits', // Auto-accept edits\n\t\t\t\tmodel: 'sonnet', // Use Sonnet model\n\t\t\t})\n\n\t\t\t// After Claude completes, re-run validation to verify fix\n\t\t\tlogger.info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`)\n\n\t\t\ttry {\n\t\t\t\tawait runScript(validationType, worktreePath, [], { quiet: true })\n\t\t\t\t// Validation passed after Claude fix\n\t\t\t\tlogger.success(`${validationTypeCapitalized} passed after Claude auto-fix`)\n\t\t\t\treturn true\n\t\t\t} catch {\n\t\t\t\t// Validation still failing after Claude's attempt\n\t\t\t\tlogger.warn(`${validationTypeCapitalized} still failing after Claude's help`)\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Claude launch failed or crashed\n\t\t\tlogger.warn('Claude auto-fix failed', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Get validation command string for prompts\n\t */\n\tprivate getValidationCommand(\n\t\tvalidationType: 'typecheck' | 'lint' | 'test',\n\t\tpackageManager: string\n\t): string {\n\t\tif (packageManager === 'npm') {\n\t\t\treturn `npm run ${validationType}`\n\t\t}\n\t\treturn `${packageManager} ${validationType}`\n\t}\n\n\t/**\n\t * Get Claude prompt for specific validation type\n\t * Matches bash script prompts exactly\n\t */\n\tprivate getClaudePrompt(\n\t\tvalidationType: 'typecheck' | 'lint' | 'test',\n\t\tvalidationCommand: string\n\t): string {\n\t\tswitch (validationType) {\n\t\t\tcase 'typecheck':\n\t\t\t\treturn (\n\t\t\t\t\t`There are TypeScript errors in this codebase. ` +\n\t\t\t\t\t`Please analyze the typecheck output, identify all type errors, and fix them. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all type issues.` +\n\t\t\t\t\t`When you are done, tell the user to quit to continue the validation process.`\n\t\t\t\t)\n\t\t\tcase 'lint':\n\t\t\t\treturn (\n\t\t\t\t\t`There are ESLint errors in this codebase. ` +\n\t\t\t\t\t`Please analyze the linting output, identify all linting issues, and fix them. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all linting issues. ` +\n\t\t\t\t\t`Focus on code quality, consistency, and following the project's linting rules.` +\n\t\t\t\t\t`When you are done, tell the user to quit to continue the validation process.`\n\t\t\t\t)\n\t\t\tcase 'test':\n\t\t\t\treturn (\n\t\t\t\t\t`There are unit test failures in this codebase. ` +\n\t\t\t\t\t`Please analyze the test output to understand what's failing, then fix the issues. ` +\n\t\t\t\t\t`This might involve updating test code, fixing bugs in the source code, or updating tests to match new behavior. ` +\n\t\t\t\t\t`Run '${validationCommand}' to see the detailed test failures, then make the necessary changes to get all tests passing.` +\n\t\t\t\t\t`When you are done, tell the user to quit to continue the validation process.`\n\t\t\t\t)\n\t\t}\n\t}\n}\n","import { executeGitCommand } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { launchClaude, detectClaudeCli } from '../utils/claude.js'\nimport type { GitStatus, CommitOptions } from '../types/index.js'\n\n/**\n * CommitManager handles uncommitted changes detection and auto-commit\n * Ports logic from bash/merge-and-clean.sh lines 610-643\n */\nexport class CommitManager {\n /**\n * Detect uncommitted changes in a worktree\n * Parses git status --porcelain output into structured GitStatus\n */\n async detectUncommittedChanges(worktreePath: string): Promise<GitStatus> {\n // Execute: git status --porcelain\n const porcelainOutput = await executeGitCommand(['status', '--porcelain'], {\n cwd: worktreePath,\n })\n\n // Parse output to get staged and unstaged files\n const { stagedFiles, unstagedFiles } = this.parseGitStatus(porcelainOutput)\n\n // Get current branch name\n const currentBranch = await executeGitCommand(['branch', '--show-current'], {\n cwd: worktreePath,\n })\n\n return {\n hasUncommittedChanges: stagedFiles.length > 0 || unstagedFiles.length > 0,\n unstagedFiles,\n stagedFiles,\n currentBranch: currentBranch.trim(),\n // Defer these to future enhancement\n isAheadOfRemote: false,\n isBehindRemote: false,\n }\n }\n\n\n /**\n * Stage all changes and commit with Claude-generated or simple message\n * Tries Claude first, falls back to simple message if Claude unavailable or fails\n */\n async commitChanges(worktreePath: string, options: CommitOptions): Promise<void> {\n // Step 1: Check dry-run mode\n if (options.dryRun) {\n logger.info('[DRY RUN] Would run: git add -A')\n logger.info('[DRY RUN] Would generate commit message with Claude (if available)')\n const fallbackMessage = this.generateFallbackMessage(options)\n const verifyFlag = options.skipVerify ? ' --no-verify' : ''\n logger.info(`[DRY RUN] Would commit with message${verifyFlag}: ${fallbackMessage}`)\n return\n }\n\n // Step 2: Stage all changes\n await executeGitCommand(['add', '-A'], { cwd: worktreePath })\n\n // Step 3: Generate commit message (try Claude first, fallback to simple)\n let message: string | null = null\n\n // Skip Claude if custom message provided\n if (!options.message) {\n try {\n message = await this.generateClaudeCommitMessage(worktreePath, options.issueNumber)\n } catch (error) {\n logger.debug('Claude commit message generation failed, using fallback', { error })\n }\n }\n\n // Fallback to simple message if Claude failed or unavailable\n message ??= this.generateFallbackMessage(options)\n\n // Step 4: Log warning if --no-verify is configured\n if (options.skipVerify) {\n logger.warn('⚠️ Skipping pre-commit hooks (--no-verify configured in settings)')\n }\n\n // Step 5: Commit with user review via git editor (unless noReview specified)\n try {\n if (options.noReview || options.message) {\n // Direct commit without editor review\n const commitArgs = ['commit', '-m', message]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n await executeGitCommand(commitArgs, { cwd: worktreePath })\n } else {\n // Use git editor for user review - pre-populate message and open editor\n logger.info('Opening git editor for commit message review...')\n const commitArgs = ['commit', '-e', '-m', message]\n if (options.skipVerify) {\n commitArgs.push('--no-verify')\n }\n await executeGitCommand(commitArgs, {\n cwd: worktreePath,\n stdio: 'inherit',\n timeout: 300000 // 5 minutes for interactive editing\n })\n }\n } catch (error) {\n // Handle \"nothing to commit\" scenario gracefully\n if (error instanceof Error && error.message.includes('nothing to commit')) {\n logger.info('No changes to commit')\n return\n }\n // Re-throw all other errors (including pre-commit hook failures)\n throw error\n }\n }\n\n\n /**\n * Generate simple fallback commit message when Claude unavailable\n * Used as fallback for Claude-powered commit messages\n */\n private generateFallbackMessage(options: CommitOptions): string {\n // If custom message provided, use it\n if (options.message) {\n return options.message\n }\n\n // Generate WIP message\n if (options.issueNumber) {\n return `WIP: Auto-commit for issue #${options.issueNumber}\\n\\nFixes #${options.issueNumber}`\n } else {\n return 'WIP: Auto-commit uncommitted changes'\n }\n }\n\n /**\n * Parse git status --porcelain output\n * Format: \"XY filename\" where X=index, Y=worktree\n * Examples:\n * \"M file.ts\" - staged modification\n * \" M file.ts\" - unstaged modification\n * \"MM file.ts\" - both staged and unstaged\n * \"?? file.ts\" - untracked\n */\n private parseGitStatus(porcelainOutput: string): {\n stagedFiles: string[]\n unstagedFiles: string[]\n } {\n const stagedFiles: string[] = []\n const unstagedFiles: string[] = []\n\n if (!porcelainOutput.trim()) {\n return { stagedFiles, unstagedFiles }\n }\n\n const lines = porcelainOutput.split('\\n').filter((line) => line.trim())\n\n for (const line of lines) {\n if (line.length < 3) continue\n\n const indexStatus = line[0] // First character - staging area status\n const worktreeStatus = line[1] // Second character - working tree status\n const filename = line.substring(3) // Everything after \"XY \"\n\n // Check if file is staged\n // First char != ' ' and != '?' → staged\n if (indexStatus !== ' ' && indexStatus !== '?') {\n stagedFiles.push(filename)\n }\n\n // Check if file is unstaged\n // Second char != ' ' or line starts with '??' → unstaged\n if (worktreeStatus !== ' ' || line.startsWith('??')) {\n unstagedFiles.push(filename)\n }\n }\n\n return { stagedFiles, unstagedFiles }\n }\n\n /**\n * Generate commit message using Claude AI\n * Claude examines the git repository directly via --add-dir option\n * Returns null if Claude unavailable or fails validation\n */\n private async generateClaudeCommitMessage(\n worktreePath: string,\n issueNumber?: number\n ): Promise<string | null> {\n const startTime = Date.now()\n\n logger.info('Starting Claude commit message generation...', {\n worktreePath: worktreePath.split('/').pop(), // Just show the folder name for privacy\n issueNumber\n })\n\n // Check if Claude CLI is available\n logger.debug('Checking Claude CLI availability...')\n const isClaudeAvailable = await detectClaudeCli()\n if (!isClaudeAvailable) {\n logger.info('Claude CLI not available, skipping Claude commit message generation')\n return null\n }\n logger.debug('Claude CLI is available')\n\n // Build XML-based structured prompt\n logger.debug('Building commit message prompt...')\n const prompt = this.buildCommitMessagePrompt(issueNumber)\n logger.debug('Prompt built', { promptLength: prompt.length })\n\n // Debug log the actual prompt content for troubleshooting\n logger.debug('Claude prompt content:', {\n prompt: prompt,\n truncatedPreview: prompt.substring(0, 500) + (prompt.length > 500 ? '...[truncated]' : '')\n })\n\n try {\n logger.info('Calling Claude CLI for commit message generation...')\n const claudeStartTime = Date.now()\n\n // Debug log the Claude call parameters\n const claudeOptions = {\n headless: true,\n addDir: worktreePath,\n model: 'claude-haiku-4-5-20251001', // Fast, cost-effective model\n timeout: 120000, // 120 second timeout\n }\n logger.debug('Claude CLI call parameters:', {\n options: claudeOptions,\n worktreePathForAnalysis: worktreePath,\n addDirContents: 'Will include entire worktree directory for analysis'\n })\n\n // Launch Claude in headless mode with repository access and shorter timeout for commit messages\n const result = await launchClaude(prompt, claudeOptions)\n\n const claudeDuration = Date.now() - claudeStartTime\n logger.debug('Claude API call completed', { duration: `${claudeDuration}ms` })\n\n if (typeof result !== 'string') {\n logger.warn('Claude returned non-string result', { resultType: typeof result })\n return null\n }\n\n logger.debug('Raw Claude output received', {\n outputLength: result.length,\n preview: result.substring(0, 200) + (result.length > 200 ? '...' : '')\n })\n\n\n // Sanitize output - remove meta-commentary and clean formatting\n logger.debug('Sanitizing Claude output...')\n const sanitized = this.sanitizeClaudeOutput(result)\n logger.debug('Output sanitized', {\n originalLength: result.length,\n sanitizedLength: sanitized.length,\n sanitized: sanitized.substring(0, 200) + (sanitized.length > 200 ? '...' : '')\n })\n\n // Ensure empty strings are rejected\n if (!sanitized) {\n logger.warn('Claude returned empty message after sanitization')\n return null\n }\n\n // Append \"Fixes #N\" trailer if issue number provided\n let finalMessage = sanitized\n if (issueNumber) {\n // Add Fixes trailer if not already present\n if (!finalMessage.includes(`Fixes #${issueNumber}`)) {\n finalMessage = `${finalMessage}\\n\\nFixes #${issueNumber}`\n logger.debug(`Added \"Fixes #${issueNumber}\" trailer to commit message`)\n } else {\n logger.debug(`\"Fixes #${issueNumber}\" already present in commit message`)\n }\n }\n\n const totalDuration = Date.now() - startTime\n logger.info('Claude commit message generated successfully', {\n message: finalMessage,\n totalDuration: `${totalDuration}ms`,\n claudeApiDuration: `${claudeDuration}ms`\n })\n\n return finalMessage\n } catch (error) {\n const totalDuration = Date.now() - startTime\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\n if (errorMessage.includes('timed out') || errorMessage.includes('timeout')) {\n logger.warn('Claude commit message generation timed out after 45 seconds', {\n totalDuration: `${totalDuration}ms`,\n worktreePath: worktreePath.split('/').pop()\n })\n } else {\n logger.warn('Failed to generate commit message with Claude', {\n error: errorMessage,\n totalDuration: `${totalDuration}ms`,\n worktreePath: worktreePath.split('/').pop()\n })\n }\n return null\n }\n }\n\n /**\n * Build structured XML prompt for commit message generation\n * Uses XML format for clear task definition and output expectations\n */\n private buildCommitMessagePrompt(issueNumber?: number): string {\n const issueContext = issueNumber\n ? `\\n<IssueContext>\nThis commit is associated with GitHub issue #${issueNumber}.\nIf the changes appear to resolve the issue, include \"Fixes #${issueNumber}\" at the end of the first line of commit message.\n</IssueContext>`\n : ''\n\n return `<Task>\nYou are a software engineer writing a commit message for this repository.\nExamine the staged changes in the git repository and generate a concise, meaningful commit message.\n</Task>\n\n<Requirements>\n<Format>The first line must be a brief summary of the changes made as a full sentence. If it references an issue, include \"Fixes #N\" at the end of this line.\n\nAdd 2 newlines, then add a bullet-point form description of the changes made, each change on a new line.</Format>\n<Mood>Use imperative mood (e.g., \"Add feature\" not \"Added feature\")</Mood>\n<Focus>Be specific about what was changed and why</Focus>\n<Conciseness>Keep message under 72 characters for subject line when possible</Conciseness>\n<NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw commit message.</NoMeta>\n<Examples>\nGood: \"Add user authentication with JWT tokens. Fixes #42\n\n- Implement login and registration endpoints\n- Secure routes with JWT middleware\n- Update user model to store hashed passwords\"\nGood: \"Fix navigation bug in sidebar menu.\"\nBad: \"Based on the changes, I'll create: Add user authentication\"\nBad: \"Looking at the files, this commit should be: Fix navigation bug\"\n</Examples>\n${issueContext}\n</Requirements>\n\n<Output>\nIMPORTANT: Your entire response will be used directly as the git commit message.\nDo not include any explanatory text before or after the commit message.\nStart your response immediately with the commit message text.\n</Output>`\n }\n\n /**\n * Sanitize Claude output to remove meta-commentary and clean formatting\n * Handles cases where Claude includes explanatory text despite instructions\n */\n private sanitizeClaudeOutput(rawOutput: string): string {\n let cleaned = rawOutput.trim()\n\n // Remove common meta-commentary patterns (case-insensitive)\n const metaPatterns = [\n /^.*?based on.*?changes.*?:/i,\n /^.*?looking at.*?files.*?:/i,\n /^.*?examining.*?:/i,\n /^.*?analyzing.*?:/i,\n /^.*?i'll.*?generate.*?:/i,\n /^.*?let me.*?:/i,\n /^.*?the commit message.*?should be.*?:/i,\n /^.*?here.*?is.*?commit.*?message.*?:/i,\n ]\n\n for (const pattern of metaPatterns) {\n cleaned = cleaned.replace(pattern, '').trim()\n }\n\n // Extract content after separators only if it looks like meta-commentary\n // Only split on colons if there's clear meta-commentary before it\n if (cleaned.includes(':')) {\n const colonIndex = cleaned.indexOf(':')\n const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase()\n\n // Only split if the text before colon looks like meta-commentary\n const metaIndicators = [\n 'here is the commit message',\n 'commit message',\n 'here is',\n 'the message should be',\n 'i suggest',\n 'my suggestion'\n ]\n\n const isMetaCommentary = metaIndicators.some(indicator => beforeColon.includes(indicator))\n\n if (isMetaCommentary) {\n const afterColon = cleaned.substring(colonIndex + 1).trim()\n if (afterColon && afterColon.length > 10) {\n cleaned = afterColon\n }\n }\n }\n\n // Remove quotes if the entire message is wrapped in them\n if ((cleaned.startsWith('\"') && cleaned.endsWith('\"')) ||\n (cleaned.startsWith(\"'\") && cleaned.endsWith(\"'\"))) {\n cleaned = cleaned.slice(1, -1).trim()\n }\n\n return cleaned\n }\n\n\n}\n","import { executeGitCommand, findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { SettingsManager } from './SettingsManager.js'\nimport type { MergeOptions } from '../types/index.js'\n\n/**\n * MergeManager handles Git rebase and fast-forward merge operations\n * Implements fail-fast behavior for conflicts (Phase 1 - no Claude assistance)\n *\n * Ports bash/merge-and-clean.sh lines 781-1090\n */\nexport class MergeManager {\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(settingsManager?: SettingsManager) {\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t}\n\n\t/**\n\t * Get the main branch name from settings (defaults to 'main')\n\t * @private\n\t */\n\tprivate async getMainBranch(): Promise<string> {\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\treturn settings.mainBranch ?? 'main'\n\t}\n\n\t/**\n\t * Rebase current branch on main with fail-fast on conflicts\n\t * Ports bash/merge-and-clean.sh lines 781-913\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param options - Merge options (dryRun, force)\n\t * @throws Error if main branch doesn't exist, uncommitted changes exist, or conflicts occur\n\t */\n\tasync rebaseOnMain(worktreePath: string, options: MergeOptions = {}): Promise<void> {\n\t\tconst { dryRun = false, force = false } = options\n\t\tconst mainBranch = await this.getMainBranch()\n\n\t\tlogger.info(`Starting rebase on ${mainBranch} branch...`)\n\n\t\t// Step 1: Check if main branch exists\n\t\ttry {\n\t\t\tawait executeGitCommand(['show-ref', '--verify', '--quiet', `refs/heads/${mainBranch}`], {\n\t\t\t\tcwd: worktreePath,\n\t\t\t})\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t`Main branch \"${mainBranch}\" does not exist. Cannot rebase.\\n` +\n\t\t\t\t\t`Ensure the repository has a \"${mainBranch}\" branch or create it first.`\n\t\t\t)\n\t\t}\n\n\t\t// Step 2: Check for uncommitted changes (defensive check)\n\t\tconst statusOutput = await executeGitCommand(['status', '--porcelain'], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tif (statusOutput.trim()) {\n\t\t\tthrow new Error(\n\t\t\t\t'Uncommitted changes detected. Please commit or stash changes before rebasing.\\n' +\n\t\t\t\t\t'Run: git status to see uncommitted changes\\n' +\n\t\t\t\t\t'Or: il finish will automatically commit them for you'\n\t\t\t)\n\t\t}\n\n\t\t// Step 3: Check if rebase is needed by comparing merge-base with main HEAD\n\t\tconst mergeBase = await executeGitCommand(['merge-base', mainBranch, 'HEAD'], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst mainHead = await executeGitCommand(['rev-parse', mainBranch], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst mergeBaseTrimmed = mergeBase.trim()\n\t\tconst mainHeadTrimmed = mainHead.trim()\n\n\t\t// If merge-base matches main HEAD, branch is already up to date\n\t\tif (mergeBaseTrimmed === mainHeadTrimmed) {\n\t\t\tlogger.success(`Branch is already up to date with ${mainBranch}. No rebase needed.`)\n\t\t\treturn\n\t\t}\n\n\t\t// Step 4: Show commits to be rebased (for informational purposes)\n\t\tconst commitsOutput = await executeGitCommand(['log', '--oneline', `${mainBranch}..HEAD`], {\n\t\t\tcwd: worktreePath,\n\t\t})\n\n\t\tconst commits = commitsOutput.trim()\n\t\tconst commitLines = commits ? commits.split('\\n') : []\n\n\t\tif (commits) {\n\t\t\t// Show commits that will be rebased\n\t\t\tlogger.info(`Found ${commitLines.length} commit(s) to rebase:`)\n\t\t\tcommitLines.forEach((commit) => logger.info(` ${commit}`))\n\t\t} else {\n\t\t\t// Main has moved forward but branch has no new commits\n\t\t\tlogger.info(`${mainBranch} branch has moved forward. Rebasing to update branch...`)\n\t\t}\n\n\t\t// Step 5: User confirmation (unless force mode or dry-run)\n\t\tif (!force && !dryRun) {\n\t\t\t// TODO: Implement interactive prompt for confirmation\n\t\t\t// For now, proceeding automatically (use --force to skip this message)\n\t\t\tlogger.info('Proceeding with rebase... (use --force to skip confirmations)')\n\t\t}\n\n\t\t// Step 6: Execute rebase (unless dry-run)\n\t\tif (dryRun) {\n\t\t\tlogger.info(`[DRY RUN] Would execute: git rebase ${mainBranch}`)\n\t\t\tif (commitLines.length > 0) {\n\t\t\t\tlogger.info(`[DRY RUN] This would rebase ${commitLines.length} commit(s)`)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Execute rebase\n\t\ttry {\n\t\t\tawait executeGitCommand(['rebase', mainBranch], { cwd: worktreePath })\n\t\t\tlogger.success('Rebase completed successfully!')\n\t\t} catch (error) {\n\t\t\t// Detect conflicts\n\t\t\tconst conflictedFiles = await this.detectConflictedFiles(worktreePath)\n\n\t\t\tif (conflictedFiles.length > 0) {\n\t\t\t\t// Try Claude-assisted resolution first\n\t\t\t\tlogger.info('Merge conflicts detected, attempting Claude-assisted resolution...')\n\n\t\t\t\tconst resolved = await this.attemptClaudeConflictResolution(\n\t\t\t\t\tworktreePath,\n\t\t\t\t\tconflictedFiles\n\t\t\t\t)\n\n\t\t\t\tif (resolved) {\n\t\t\t\t\tlogger.success('Conflicts resolved with Claude assistance, rebase completed')\n\t\t\t\t\treturn // Continue with successful rebase\n\t\t\t\t}\n\n\t\t\t\t// Claude couldn't resolve or not available - fail fast\n\t\t\t\tconst conflictError = this.formatConflictError(conflictedFiles)\n\t\t\t\tthrow new Error(conflictError)\n\t\t\t}\n\n\t\t\t// If not a conflict, re-throw the original error\n\t\t\tthrow new Error(\n\t\t\t\t`Rebase failed: ${error instanceof Error ? error.message : String(error)}\\n` +\n\t\t\t\t\t'Run: git status for more details\\n' +\n\t\t\t\t\t'Or: git rebase --abort to cancel the rebase'\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Validate that fast-forward merge is possible\n\t * Ports bash/merge-and-clean.sh lines 957-968\n\t *\n\t * @param branchName - Name of the branch to merge\n\t * @param mainWorktreePath - Path where main branch is checked out\n\t * @throws Error if fast-forward is not possible\n\t */\n\tasync validateFastForwardPossible(branchName: string, mainWorktreePath: string): Promise<void> {\n\t\tconst mainBranch = await this.getMainBranch()\n\n\t\t// Step 1: Get merge-base between main and branch\n\t\tconst mergeBase = await executeGitCommand(['merge-base', mainBranch, branchName], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\t// Step 2: Get current HEAD of main\n\t\tconst mainHead = await executeGitCommand(['rev-parse', mainBranch], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\t// Step 3: Compare - they must match for fast-forward\n\t\tconst mergeBaseTrimmed = mergeBase.trim()\n\t\tconst mainHeadTrimmed = mainHead.trim()\n\n\t\tif (mergeBaseTrimmed !== mainHeadTrimmed) {\n\t\t\tthrow new Error(\n\t\t\t\t'Cannot perform fast-forward merge.\\n' +\n\t\t\t\t\t`The ${mainBranch} branch has moved forward since this branch was created.\\n` +\n\t\t\t\t\t`Merge base: ${mergeBaseTrimmed}\\n` +\n\t\t\t\t\t`Main HEAD: ${mainHeadTrimmed}\\n\\n` +\n\t\t\t\t\t'To fix this:\\n' +\n\t\t\t\t\t` 1. Rebase the branch on ${mainBranch}: git rebase ${mainBranch}\\n` +\n\t\t\t\t\t` 2. Or use: il finish to automatically rebase and merge\\n`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Perform fast-forward only merge\n\t * Ports bash/merge-and-clean.sh lines 938-994\n\t *\n\t * @param branchName - Name of the branch to merge\n\t * @param worktreePath - Path to the worktree\n\t * @param options - Merge options (dryRun, force)\n\t * @throws Error if checkout, validation, or merge fails\n\t */\n\tasync performFastForwardMerge(\n\t\tbranchName: string,\n\t\tworktreePath: string,\n\t\toptions: MergeOptions = {}\n\t): Promise<void> {\n\t\tconst { dryRun = false, force = false } = options\n\t\tconst mainBranch = await this.getMainBranch()\n\n\t\tlogger.info('Starting fast-forward merge...')\n\n\t\t// Step 1: Find where main branch is checked out\n\t\t// This copies the bash script approach: find main worktree, run commands from there\n\t\tconst mainWorktreePath = options.repoRoot ??\n\t\t\tawait findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\n\t\t// Step 3: No need to checkout main - it's already checked out in mainWorktreePath\n\t\tlogger.debug(`Using ${mainBranch} branch location: ${mainWorktreePath}`)\n\n\t\t// Step 4: Verify on main branch\n\t\tconst currentBranch = await executeGitCommand(['branch', '--show-current'], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\tif (currentBranch.trim() !== mainBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t`Expected ${mainBranch} branch but found: ${currentBranch.trim()}\\n` +\n\t\t\t\t\t`At location: ${mainWorktreePath}\\n` +\n\t\t\t\t\t'This indicates the main worktree detection failed.'\n\t\t\t)\n\t\t}\n\n\t\t// Step 5: Validate fast-forward is possible\n\t\tawait this.validateFastForwardPossible(branchName, mainWorktreePath)\n\n\t\t// Step 6: Show commits to be merged\n\t\tconst commitsOutput = await executeGitCommand(['log', '--oneline', `${mainBranch}..${branchName}`], {\n\t\t\tcwd: mainWorktreePath,\n\t\t})\n\n\t\tconst commits = commitsOutput.trim()\n\n\t\t// If no commits, branch is already merged\n\t\tif (!commits) {\n\t\t\tlogger.success(`Branch is already merged into ${mainBranch}. No merge needed.`)\n\t\t\treturn\n\t\t}\n\n\t\t// Show commits that will be merged\n\t\tconst commitLines = commits.split('\\n')\n\t\tlogger.info(`Found ${commitLines.length} commit(s) to merge:`)\n\t\tcommitLines.forEach((commit) => logger.info(` ${commit}`))\n\n\t\t// Step 7: User confirmation (unless force mode or dry-run)\n\t\tif (!force && !dryRun) {\n\t\t\t// TODO: Implement interactive prompt for confirmation\n\t\t\t// For now, proceeding automatically (use --force to skip this message)\n\t\t\tlogger.info('Proceeding with fast-forward merge... (use --force to skip confirmations)')\n\t\t}\n\n\t\t// Step 8: Execute merge (unless dry-run)\n\t\tif (dryRun) {\n\t\t\tlogger.info(`[DRY RUN] Would execute: git merge --ff-only ${branchName}`)\n\t\t\tlogger.info(`[DRY RUN] This would merge ${commitLines.length} commit(s)`)\n\t\t\treturn\n\t\t}\n\n\t\t// Execute fast-forward merge\n\t\ttry {\n\t\t\tawait executeGitCommand(['merge', '--ff-only', branchName], { cwd: mainWorktreePath })\n\t\t\tlogger.success(`Fast-forward merge completed! Merged ${commitLines.length} commit(s).`)\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Fast-forward merge failed: ${error instanceof Error ? error.message : String(error)}\\n\\n` +\n\t\t\t\t\t'To recover:\\n' +\n\t\t\t\t\t' 1. Check merge status: git status\\n' +\n\t\t\t\t\t' 2. Abort merge if needed: git merge --abort\\n' +\n\t\t\t\t\t' 3. Verify branch is rebased: git rebase main\\n' +\n\t\t\t\t\t' 4. Try merge again: il finish'\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Helper: Detect conflicted files after failed rebase\n\t * @private\n\t */\n\tprivate async detectConflictedFiles(worktreePath: string): Promise<string[]> {\n\t\ttry {\n\t\t\tconst output = await executeGitCommand(['diff', '--name-only', '--diff-filter=U'], {\n\t\t\t\tcwd: worktreePath,\n\t\t\t})\n\n\t\t\treturn output\n\t\t\t\t.trim()\n\t\t\t\t.split('\\n')\n\t\t\t\t.filter((file) => file.length > 0)\n\t\t} catch {\n\t\t\t// If command fails, return empty array (might not be a conflict)\n\t\t\treturn []\n\t\t}\n\t}\n\n\t/**\n\t * Helper: Format conflict error message with manual resolution steps\n\t * @private\n\t */\n\tprivate formatConflictError(conflictedFiles: string[]): string {\n\t\tconst fileList = conflictedFiles.map((file) => ` • ${file}`).join('\\n')\n\n\t\treturn (\n\t\t\t'Rebase failed - merge conflicts detected in:\\n' +\n\t\t\tfileList +\n\t\t\t'\\n\\n' +\n\t\t\t'To resolve manually:\\n' +\n\t\t\t' 1. Fix conflicts in the files above\\n' +\n\t\t\t' 2. Stage resolved files: git add <files>\\n' +\n\t\t\t' 3. Continue rebase: git rebase --continue\\n' +\n\t\t\t' 4. Or abort rebase: git rebase --abort\\n' +\n\t\t\t' 5. Then re-run: il finish <issue-number>'\n\t\t)\n\t}\n\n\t/**\n\t * Attempt to resolve conflicts using Claude\n\t * Ports bash/merge-and-clean.sh lines 839-894\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param conflictedFiles - List of files with conflicts\n\t * @returns true if conflicts resolved, false otherwise\n\t * @private\n\t */\n\tprivate async attemptClaudeConflictResolution(\n\t\tworktreePath: string,\n\t\tconflictedFiles: string[]\n\t): Promise<boolean> {\n\t\t// Check if Claude CLI is available\n\t\tconst isClaudeAvailable = await detectClaudeCli()\n\t\tif (!isClaudeAvailable) {\n\t\t\tlogger.debug('Claude CLI not available, skipping conflict resolution')\n\t\t\treturn false\n\t\t}\n\n\t\tlogger.info(`Launching Claude to resolve conflicts in ${conflictedFiles.length} file(s)...`)\n\n\t\t// Hard-coded prompt matching bash script line 844\n\t\t// No templates, no complexity - just the essential instruction\n\t\tconst prompt =\n\t\t\t`Please help resolve the git rebase conflicts in this repository. ` +\n\t\t\t`Analyze the conflicted files, understand the changes from both branches, ` +\n\t\t\t`fix the conflicts, then run 'git add .' to stage the resolved files, ` +\n\t\t\t`and finally run 'git rebase --continue' to continue the rebase process. ` +\n\t\t\t`Handle the entire workflow for me.`\n\n\t\ttry {\n\t\t\t// Launch Claude interactively in current terminal\n\t\t\t// User will interact directly with Claude to resolve conflicts\n\t\t\tawait launchClaude(prompt, {\n\t\t\t\taddDir: worktreePath,\n\t\t\t\theadless: false, // Interactive - runs in current terminal with stdio: inherit\n\t\t\t})\n\n\t\t\t// After Claude interaction completes, check if conflicts resolved\n\t\t\tconst remainingConflicts = await this.detectConflictedFiles(worktreePath)\n\n\t\t\tif (remainingConflicts.length > 0) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Conflicts still exist in ${remainingConflicts.length} file(s) after Claude assistance`\n\t\t\t\t)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Check if rebase completed or still in progress\n\t\t\tconst rebaseInProgress = await this.isRebaseInProgress(worktreePath)\n\n\t\t\tif (rebaseInProgress) {\n\t\t\t\tlogger.warn('Rebase still in progress after Claude assistance')\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Success: no conflicts, rebase completed\n\t\t\tlogger.success('Claude successfully resolved conflicts and completed rebase')\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tlogger.warn('Claude conflict resolution failed', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Check if a git rebase is currently in progress\n\t * Checks for .git/rebase-merge or .git/rebase-apply directories\n\t * Ports bash script logic from lines 853-856\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @returns true if rebase in progress, false otherwise\n\t * @private\n\t */\n\tprivate async isRebaseInProgress(worktreePath: string): Promise<boolean> {\n\t\tconst fs = await import('node:fs/promises')\n\t\tconst path = await import('node:path')\n\n\t\tconst rebaseMergePath = path.join(worktreePath, '.git', 'rebase-merge')\n\t\tconst rebaseApplyPath = path.join(worktreePath, '.git', 'rebase-apply')\n\n\t\t// Check for rebase-merge directory\n\t\ttry {\n\t\t\tawait fs.access(rebaseMergePath)\n\t\t\treturn true\n\t\t} catch {\n\t\t\t// Directory doesn't exist, continue checking\n\t\t}\n\n\t\t// Check for rebase-apply directory\n\t\ttry {\n\t\t\tawait fs.access(rebaseApplyPath)\n\t\t\treturn true\n\t\t} catch {\n\t\t\t// Directory doesn't exist\n\t\t}\n\n\t\treturn false\n\t}\n}\n","import { logger } from '../utils/logger.js'\nimport { detectPackageManager, runScript } from '../utils/package-manager.js'\nimport { readPackageJson, hasScript } from '../utils/package-json.js'\nimport { ProjectCapabilityDetector } from './ProjectCapabilityDetector.js'\n\nexport interface BuildOptions {\n\tdryRun?: boolean\n}\n\nexport interface BuildResult {\n\tsuccess: boolean\n\tskipped: boolean\n\treason?: string\n\tduration: number\n}\n\n/**\n * BuildRunner handles post-merge build verification for CLI projects\n * Only runs build when project has CLI capabilities (bin field in package.json)\n */\nexport class BuildRunner {\n\tprivate capabilityDetector: ProjectCapabilityDetector\n\n\tconstructor(capabilityDetector?: ProjectCapabilityDetector) {\n\t\tthis.capabilityDetector = capabilityDetector ?? new ProjectCapabilityDetector()\n\t}\n\n\t/**\n\t * Run build verification in the specified directory\n\t * @param buildPath - Path where build should run (typically main worktree path)\n\t * @param options - Build options\n\t */\n\tasync runBuild(buildPath: string, options: BuildOptions = {}): Promise<BuildResult> {\n\t\tconst startTime = Date.now()\n\n\t\ttry {\n\t\t\t// Step 1: Check if build script exists\n\t\t\tconst pkgJson = await readPackageJson(buildPath)\n\t\t\tconst hasBuildScript = hasScript(pkgJson, 'build')\n\n\t\t\tif (!hasBuildScript) {\n\t\t\t\tlogger.debug('Skipping build - no build script found')\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: 'No build script found in package.json',\n\t\t\t\t\tduration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Handle missing package.json - skip build for non-Node.js projects\n\t\t\tif (error instanceof Error && error.message.includes('package.json not found')) {\n\t\t\t\tlogger.debug('Skipping build - no package.json found (non-Node.js project)')\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: 'No package.json found in project',\n\t\t\t\t\tduration: Date.now() - startTime,\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Re-throw other errors\n\t\t\tthrow error\n\t\t}\n\n\t\t// Step 2: Check if project has CLI capability (bin field)\n\t\tconst capabilities = await this.capabilityDetector.detectCapabilities(buildPath)\n\t\tconst isCLIProject = capabilities.capabilities.includes('cli')\n\n\t\tif (!isCLIProject) {\n\t\t\tlogger.debug('Skipping build - not a CLI project (no bin field)')\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: true,\n\t\t\t\treason: 'Project is not a CLI project (no bin field in package.json)',\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t}\n\n\t\t// Step 3: Detect package manager\n\t\tconst packageManager = await detectPackageManager(buildPath)\n\n\t\t// Step 4: Handle dry-run mode\n\t\tif (options.dryRun) {\n\t\t\tconst command =\n\t\t\t\tpackageManager === 'npm' ? 'npm run build' : `${packageManager} build`\n\t\t\tlogger.info(`[DRY RUN] Would run: ${command}`)\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t}\n\n\t\t// Step 5: Execute build\n\t\tlogger.info('Running build...')\n\n\t\ttry {\n\t\t\tawait runScript('build', buildPath, [], { quiet: true })\n\t\t\tlogger.success('Build completed successfully')\n\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tskipped: false,\n\t\t\t\tduration: Date.now() - startTime,\n\t\t\t}\n\t\t} catch {\n\t\t\t// Step 6: Throw detailed error on failure\n\t\t\tconst runCommand =\n\t\t\t\tpackageManager === 'npm' ? 'npm run build' : `${packageManager} build`\n\n\t\t\tthrow new Error(\n\t\t\t\t`Error: Build failed.\\n` +\n\t\t\t\t\t`Fix build errors before proceeding.\\n\\n` +\n\t\t\t\t\t`Run '${runCommand}' to see detailed errors.`\n\t\t\t)\n\t\t}\n\t}\n}\n","import { logger } from '../utils/logger.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ValidationRunner } from '../lib/ValidationRunner.js'\nimport { CommitManager } from '../lib/CommitManager.js'\nimport { MergeManager } from '../lib/MergeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { ResourceCleanup } from '../lib/ResourceCleanup.js'\nimport { ProcessManager } from '../lib/process/ProcessManager.js'\nimport { BuildRunner } from '../lib/BuildRunner.js'\nimport { DatabaseManager } from '../lib/DatabaseManager.js'\nimport { NeonProvider } from '../lib/providers/NeonProvider.js'\nimport { EnvironmentManager } from '../lib/EnvironmentManager.js'\nimport { CLIIsolationManager } from '../lib/CLIIsolationManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { loadEnvIntoProcess } from '../utils/env.js'\nimport { installDependencies } from '../utils/package-manager.js'\nimport type { FinishOptions, GitWorktree, CommitOptions, MergeOptions, PullRequest } from '../types/index.js'\nimport type { ResourceCleanupOptions, CleanupResult } from '../types/cleanup.js'\nimport type { ParsedInput } from './start.js'\nimport path from 'path'\n\nexport interface FinishCommandInput {\n\tidentifier?: string | undefined // Optional - can be auto-detected\n\toptions: FinishOptions\n}\n\nexport interface ParsedFinishInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: number // For issues and PRs\n\tbranchName?: string // For branch inputs\n\toriginalInput: string // Raw input for error messages\n\tautoDetected?: boolean // True if detected from current directory\n}\n\nexport class FinishCommand {\n\tprivate gitHubService: GitHubService\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate validationRunner: ValidationRunner\n\tprivate commitManager: CommitManager\n\tprivate mergeManager: MergeManager\n\tprivate identifierParser: IdentifierParser\n\tprivate resourceCleanup?: ResourceCleanup\n\tprivate buildRunner: BuildRunner\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(\n\t\tgitHubService?: GitHubService,\n\t\tgitWorktreeManager?: GitWorktreeManager,\n\t\tvalidationRunner?: ValidationRunner,\n\t\tcommitManager?: CommitManager,\n\t\tmergeManager?: MergeManager,\n\t\tidentifierParser?: IdentifierParser,\n\t\tresourceCleanup?: ResourceCleanup,\n\t\tbuildRunner?: BuildRunner,\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\t// Load environment variables first\n\t\tconst envResult = loadEnvIntoProcess()\n\t\tif (envResult.error) {\n\t\t\tlogger.debug(`Environment loading warning: ${envResult.error.message}`)\n\t\t}\n\t\tif (envResult.parsed) {\n\t\t\tlogger.debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`)\n\t\t}\n\n\t\t// Dependency injection for testing\n\t\tthis.gitHubService = gitHubService ?? new GitHubService()\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.validationRunner = validationRunner ?? new ValidationRunner()\n\t\tthis.commitManager = commitManager ?? new CommitManager()\n\t\tthis.mergeManager = mergeManager ?? new MergeManager()\n\t\tthis.identifierParser = identifierParser ?? new IdentifierParser(this.gitWorktreeManager)\n\n\t\t// Initialize settingsManager first (needed for ResourceCleanup)\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\n\t\t// ResourceCleanup will be initialized lazily with proper configuration\n\t\tif (resourceCleanup) {\n\t\t\tthis.resourceCleanup = resourceCleanup\n\t\t}\n\n\t\tthis.buildRunner = buildRunner ?? new BuildRunner()\n\t}\n\n\t/**\n\t * Lazy initialization of ResourceCleanup with properly configured DatabaseManager\n\t */\n\tprivate async ensureResourceCleanup(): Promise<void> {\n\t\tif (this.resourceCleanup) {\n\t\t\treturn\n\t\t}\n\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst databaseUrlEnvVarName = settings.capabilities?.database?.databaseUrlEnvVarName ?? 'DATABASE_URL'\n\n\t\tconst environmentManager = new EnvironmentManager()\n\t\tconst neonProvider = new NeonProvider({\n\t\t\tprojectId: process.env.NEON_PROJECT_ID ?? '',\n\t\t\tparentBranch: process.env.NEON_PARENT_BRANCH ?? '',\n\t\t})\n\t\tconst databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName)\n\t\tconst cliIsolationManager = new CLIIsolationManager()\n\n\t\tthis.resourceCleanup = new ResourceCleanup(\n\t\t\tthis.gitWorktreeManager,\n\t\t\tnew ProcessManager(),\n\t\t\tdatabaseManager,\n\t\t\tcliIsolationManager\n\t\t)\n\t}\n\n\t/**\n\t * Main entry point for finish command\n\t */\n\tpublic async execute(input: FinishCommandInput): Promise<void> {\n\t\ttry {\n\t\t\t// Step 1: Parse input (or auto-detect from current directory)\n\t\t\tconst parsed = await this.parseInput(input.identifier, input.options)\n\n\t\t\t// Step 2: Validate based on type and get worktrees\n\t\t\tconst worktrees = await this.validateInput(parsed, input.options)\n\n\t\t\t// Step 3: Log success\n\t\t\tlogger.info(`Validated input: ${this.formatParsedInput(parsed)}`)\n\n\t\t\t// Get worktree for workflow execution\n\t\t\tconst worktree = worktrees[0]\n\t\t\tif (!worktree) {\n\t\t\t\tthrow new Error('No worktree found')\n\t\t\t}\n\n\t\t\t// Step 4: Branch based on input type\n\t\t\tif (parsed.type === 'pr') {\n\t\t\t\t// Fetch PR to get current state\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\t\t\t\tconst pr = await this.gitHubService.fetchPR(parsed.number)\n\t\t\t\tawait this.executePRWorkflow(parsed, input.options, worktree, pr)\n\t\t\t} else {\n\t\t\t\t// Execute traditional issue/branch workflow\n\t\t\t\tawait this.executeIssueWorkflow(parsed, input.options, worktree)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tlogger.error(`${error.message}`)\n\t\t\t} else {\n\t\t\t\tlogger.error('An unknown error occurred')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Parse input to determine type and extract relevant data\n\t * Supports auto-detection from current directory when identifier is undefined\n\t */\n\tprivate async parseInput(\n\t\tidentifier: string | undefined,\n\t\toptions: FinishOptions\n\t): Promise<ParsedFinishInput> {\n\t\t// Priority 1: --pr flag overrides everything\n\t\tif (options.pr !== undefined) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: options.pr,\n\t\t\t\toriginalInput: `--pr ${options.pr}`,\n\t\t\t\tautoDetected: false,\n\t\t\t}\n\t\t}\n\n\t\t// Priority 2: Explicit identifier provided\n\t\tif (identifier?.trim()) {\n\t\t\treturn await this.parseExplicitInput(identifier.trim())\n\t\t}\n\n\t\t// Priority 3: Auto-detect from current directory\n\t\treturn await this.autoDetectFromCurrentDirectory()\n\t}\n\n\t/**\n\t * Parse explicit identifier input using pattern-based detection\n\t * (No GitHub API calls - uses IdentifierParser)\n\t */\n\tprivate async parseExplicitInput(\n\t\tidentifier: string\n\t): Promise<ParsedFinishInput> {\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123\n\t\tconst prPattern = /^(?:pr|PR)[/-](\\d+)$/\n\t\tconst prMatch = identifier.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: parseInt(prMatch[1], 10),\n\t\t\t\toriginalInput: identifier,\n\t\t\t\tautoDetected: false,\n\t\t\t}\n\t\t}\n\n\t\t// Use IdentifierParser for pattern-based detection\n\t\t// (checks existing worktrees, no GitHub API calls)\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach finish command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in finish command')\n\t\t}\n\n\t\t// Convert ParsedInput to ParsedFinishInput (add autoDetected field)\n\t\tconst result: ParsedFinishInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\t// Add number or branchName based on type\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect PR or issue from current directory\n\t * Ports logic from merge-current-issue.sh lines 30-52\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedFinishInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\t// Pattern: /.*_pr_(\\d+)$/\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory or branch name\n\t\t// Pattern: /issue-(\\d+)/\n\t\tconst issuePattern = /issue-(\\d+)/\n\t\tconst issueMatch = currentDir.match(issuePattern)\n\n\t\tif (issueMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(issueMatch[1], 10)\n\t\t\tlogger.debug(\n\t\t\t\t`Auto-detected issue #${issueNumber} from directory: ${currentDir}`\n\t\t\t)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueMatch = currentBranch.match(issuePattern)\n\t\tif (branchIssueMatch?.[1]) {\n\t\t\tconst issueNumber = parseInt(branchIssueMatch[1], 10)\n\t\t\tlogger.debug(\n\t\t\t\t`Auto-detected issue #${issueNumber} from branch: ${currentBranch}`\n\t\t\t)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Validate the parsed input based on its type\n\t */\n\tprivate async validateInput(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions\n\t): Promise<GitWorktree[]> {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\n\t\t\t\t// Fetch PR from GitHub\n\t\t\t\tconst pr = await this.gitHubService.fetchPR(parsed.number)\n\n\t\t\t\t// For PRs, we allow closed/merged state (cleanup-only mode)\n\t\t\t\t// But we still validate it exists\n\t\t\t\tlogger.debug(`Validated PR #${parsed.number} (state: ${pr.state})`)\n\n\t\t\t\t// Find associated worktree\n\t\t\t\treturn await this.findWorktreeForIdentifier(parsed)\n\t\t\t}\n\n\t\t\tcase 'issue': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid issue number')\n\t\t\t\t}\n\n\t\t\t\t// Fetch issue from GitHub\n\t\t\t\tconst issue = await this.gitHubService.fetchIssue(parsed.number)\n\n\t\t\t\t// Validate issue state (warn if closed unless --force)\n\t\t\t\tif (issue.state === 'closed' && !options.force) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Issue #${parsed.number} is closed. Use --force to finish anyway.`\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tlogger.debug(`Validated issue #${parsed.number} (state: ${issue.state})`)\n\n\t\t\t\t// Find associated worktree\n\t\t\t\treturn await this.findWorktreeForIdentifier(parsed)\n\t\t\t}\n\n\t\t\tcase 'branch': {\n\t\t\t\tif (!parsed.branchName) {\n\t\t\t\t\tthrow new Error('Invalid branch name')\n\t\t\t\t}\n\n\t\t\t\t// Validate branch name format\n\t\t\t\tif (!this.isValidBranchName(parsed.branchName)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes'\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tlogger.debug(`Validated branch name: ${parsed.branchName}`)\n\n\t\t\t\t// Find associated worktree\n\t\t\t\treturn await this.findWorktreeForIdentifier(parsed)\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownType = parsed as { type: string }\n\t\t\t\tthrow new Error(`Unknown input type: ${unknownType.type}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier using specific methods based on type\n\t * (uses precise pattern matching instead of broad substring matching)\n\t * Throws error if not found\n\t */\n\tprivate async findWorktreeForIdentifier(\n\t\tparsed: ParsedFinishInput\n\t): Promise<GitWorktree[]> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\t// Use specific finding methods based on parsed type\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\t\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(\n\t\t\t\t\tparsed.number,\n\t\t\t\t\t''\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'issue': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid issue number')\n\t\t\t\t}\n\t\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(\n\t\t\t\t\tparsed.number\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'branch': {\n\t\t\t\tif (!parsed.branchName) {\n\t\t\t\t\tthrow new Error('Invalid branch name')\n\t\t\t\t}\n\t\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\t\tparsed.branchName\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownType = parsed as { type: string }\n\t\t\t\tthrow new Error(`Unknown input type: ${unknownType.type}`)\n\t\t\t}\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Use 'il list' to see available worktrees.`\n\t\t\t)\n\t\t}\n\n\t\tlogger.debug(`Found worktree: ${worktree.path}`)\n\n\t\treturn [worktree]\n\t}\n\n\t/**\n\t * Validate branch name format\n\t */\n\tprivate isValidBranchName(branch: string): boolean {\n\t\t// Pattern from bash script and StartCommand\n\t\treturn /^[a-zA-Z0-9/_-]+$/.test(branch)\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedFinishInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr':\n\t\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t\tcase 'issue':\n\t\t\t\treturn `Issue #${parsed.number}${autoLabel}`\n\t\t\tcase 'branch':\n\t\t\t\treturn `Branch '${parsed.branchName}'${autoLabel}`\n\t\t\tdefault:\n\t\t\t\treturn 'Unknown input'\n\t\t}\n\t}\n\n\t/**\n\t * Execute workflow for issues and branches (merge into main)\n\t * This is the traditional workflow: validate → commit → rebase → merge → cleanup\n\t */\n\tprivate async executeIssueWorkflow(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree\n\t): Promise<void> {\n\t\t// Step 1: Run pre-merge validations FIRST (Sub-Issue #47)\n\t\tif (!options.dryRun) {\n\t\t\tlogger.info('Running pre-merge validations...')\n\n\t\t\tawait this.validationRunner.runValidations(worktree.path, {\n\t\t\t\tdryRun: options.dryRun ?? false,\n\t\t\t})\n\t\t\tlogger.success('All validations passed')\n\t\t} else {\n\t\t\tlogger.info('[DRY RUN] Would run pre-merge validations')\n\t\t}\n\n\t\t// Step 2: Detect uncommitted changes AFTER validation passes\n\t\tconst gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path)\n\n\t\t// Step 3: Commit changes only if validation passed AND changes exist\n\t\tif (gitStatus.hasUncommittedChanges) {\n\t\t\tif (options.dryRun) {\n\t\t\t\tlogger.info('[DRY RUN] Would auto-commit uncommitted changes (validation passed)')\n\t\t\t} else {\n\t\t\t\tlogger.info('Validation passed, auto-committing uncommitted changes...')\n\n\t\t\t\t// Load settings to get skipVerify configuration\n\t\t\t\tconst settings = await this.settingsManager.loadSettings(worktree.path)\n\t\t\t\tconst skipVerify = settings.workflows?.issue?.noVerify ?? false\n\n\t\t\t\tconst commitOptions: CommitOptions = {\n\t\t\t\t\tdryRun: options.dryRun ?? false,\n\t\t\t\t\tskipVerify,\n\t\t\t\t}\n\n\t\t\t\t// Only add issueNumber if it's an issue\n\t\t\t\tif (parsed.type === 'issue' && parsed.number) {\n\t\t\t\t\tcommitOptions.issueNumber = parsed.number\n\t\t\t\t}\n\n\t\t\t\tawait this.commitManager.commitChanges(worktree.path, commitOptions)\n\n\t\t\t\tlogger.success('Changes committed successfully')\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.debug('No uncommitted changes found')\n\t\t}\n\n\t\t// Step 4: Rebase branch on main\n\t\tlogger.info('Rebasing branch on main...')\n\n\t\tconst mergeOptions: MergeOptions = {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t\tforce: options.force ?? false,\n\t\t}\n\n\t\tawait this.mergeManager.rebaseOnMain(worktree.path, mergeOptions)\n\t\tlogger.success('Branch rebased successfully')\n\n\t\t// Step 5: Perform fast-forward merge\n\t\tlogger.info('Performing fast-forward merge...')\n\t\tawait this.mergeManager.performFastForwardMerge(worktree.branch, worktree.path, mergeOptions)\n\t\tlogger.success('Fast-forward merge completed successfully')\n\n\t\t// Step 5.5: Install dependencies in main worktree\n\t\tif (options.dryRun) {\n\t\t\tlogger.info('[DRY RUN] Would install dependencies in main worktree')\n\t\t} else {\n\t\t\tlogger.info('Installing dependencies in main worktree...')\n\t\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager)\n\t\t\tawait installDependencies(mainWorktreePath, true)\n\t\t}\n\n\t\t// Step 5.6: Run post-merge build verification (CLI projects only)\n\t\tif (!options.skipBuild) {\n\t\t\tawait this.runPostMergeBuild(worktree.path, options)\n\t\t} else {\n\t\t\tlogger.debug('Skipping build verification (--skip-build flag provided)')\n\t\t}\n\n\t\t// Step 6: Post-merge cleanup\n\t\tawait this.performPostMergeCleanup(parsed, options, worktree)\n\t}\n\n\t/**\n\t * Execute workflow for Pull Requests\n\t * Behavior depends on PR state:\n\t * - OPEN: Commit changes, push to remote, keep worktree active\n\t * - CLOSED/MERGED: Skip to cleanup\n\t */\n\tprivate async executePRWorkflow(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree,\n\t\tpr: PullRequest\n\t): Promise<void> {\n\t\t// Branch based on PR state\n\t\tif (pr.state === 'closed' || pr.state === 'merged') {\n\t\t\t// Closed/Merged PR workflow\n\t\t\tlogger.info(`PR #${parsed.number} is ${pr.state.toUpperCase()} - skipping to cleanup`)\n\n\t\t\t// Check for uncommitted changes and warn (unless --force)\n\t\t\tconst gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path)\n\t\t\tif (gitStatus.hasUncommittedChanges && !options.force) {\n\t\t\t\tlogger.warn('PR has uncommitted changes')\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Cannot cleanup PR with uncommitted changes. ' +\n\t\t\t\t\t'Commit or stash changes, then run again with --force to cleanup anyway.'\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Call cleanup directly with deleteBranch: true\n\t\t\tawait this.performPRCleanup(parsed, options, worktree)\n\n\t\t\tlogger.success(`PR #${parsed.number} cleanup completed`)\n\t\t} else {\n\t\t\t// Open PR workflow\n\t\t\tlogger.info(`PR #${parsed.number} is OPEN - will push changes and keep worktree active`)\n\n\t\t\t// Step 1: Detect uncommitted changes\n\t\t\tconst gitStatus = await this.commitManager.detectUncommittedChanges(worktree.path)\n\n\t\t\t// Step 2: Commit changes if any exist\n\t\t\tif (gitStatus.hasUncommittedChanges) {\n\t\t\t\tif (options.dryRun) {\n\t\t\t\t\tlogger.info('[DRY RUN] Would commit uncommitted changes')\n\t\t\t\t} else {\n\t\t\t\t\tlogger.info('Committing uncommitted changes...')\n\n\t\t\t\t\t// Load settings to get skipVerify configuration\n\t\t\t\t\tconst settings = await this.settingsManager.loadSettings(worktree.path)\n\t\t\t\t\tconst skipVerify = settings.workflows?.pr?.noVerify ?? false\n\n\t\t\t\t\tawait this.commitManager.commitChanges(worktree.path, {\n\t\t\t\t\t\tdryRun: false,\n\t\t\t\t\t\tskipVerify,\n\t\t\t\t\t\t// Do NOT pass issueNumber for PRs - no \"Fixes #\" trailer needed\n\t\t\t\t\t})\n\t\t\t\t\tlogger.success('Changes committed')\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogger.debug('No uncommitted changes found')\n\t\t\t}\n\n\t\t\t// Step 3: Push to remote\n\t\t\tif (options.dryRun) {\n\t\t\t\tlogger.info(`[DRY RUN] Would push changes to origin/${pr.branch}`)\n\t\t\t} else {\n\t\t\t\tlogger.info('Pushing changes to remote...')\n\t\t\t\tconst { pushBranchToRemote } = await import('../utils/git.js')\n\t\t\t\tawait pushBranchToRemote(pr.branch, worktree.path, {\n\t\t\t\t\tdryRun: false\n\t\t\t\t})\n\t\t\t\tlogger.success(`Changes pushed to PR #${parsed.number}`)\n\t\t\t}\n\n\t\t\t// Step 4: Log success and guidance\n\t\t\tlogger.success(`PR #${parsed.number} updated successfully`)\n\t\t\tlogger.info('Worktree remains active for continued work')\n\t\t\tlogger.info(`To cleanup when done: il cleanup ${parsed.number}`)\n\t\t}\n\t}\n\n\t/**\n\t * Perform cleanup for closed/merged PRs\n\t * Similar to performPostMergeCleanup but with different messaging\n\t */\n\tprivate async performPRCleanup(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree\n\t): Promise<void> {\n\t\t// Convert to ParsedInput format\n\t\tconst cleanupInput: ParsedInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\t...(parsed.number !== undefined && { number: parsed.number }),\n\t\t\t...(parsed.branchName !== undefined && { branchName: parsed.branchName }),\n\t\t}\n\n\t\tconst cleanupOptions: ResourceCleanupOptions = {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t\tdeleteBranch: true, // Delete branch for closed/merged PRs\n\t\t\tkeepDatabase: false,\n\t\t\tforce: options.force ?? false,\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.ensureResourceCleanup()\n\t\t\tif (!this.resourceCleanup) {\n\t\t\t\tthrow new Error('Failed to initialize ResourceCleanup')\n\t\t\t}\n\t\t\tconst result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions)\n\n\t\t\tthis.reportCleanupResults(result)\n\n\t\t\tif (!result.success) {\n\t\t\t\tlogger.warn('Some cleanup operations failed - manual cleanup may be required')\n\t\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t\t} else {\n\t\t\t\t// Warn if running from within the worktree being finished (only on successful cleanup)\n\t\t\t\tif (this.isRunningFromWithinWorktree(worktree.path)) {\n\t\t\t\t\tthis.showTerminalCloseWarning(worktree)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tlogger.warn(`Cleanup failed: ${errorMessage}`)\n\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t\tthrow error // Re-throw to fail the command\n\t\t}\n\t}\n\n\t/**\n\t * Run post-merge build verification for CLI projects\n\t * Runs in main worktree to verify merged code builds successfully\n\t */\n\tprivate async runPostMergeBuild(\n\t\tworktreePath: string,\n\t\toptions: FinishOptions\n\t): Promise<void> {\n\t\t// Find main worktree path\n\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\n\t\t// Check if dry-run\n\t\tif (options.dryRun) {\n\t\t\tlogger.info('[DRY RUN] Would run post-merge build')\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info('Running post-merge build...')\n\n\t\tconst result = await this.buildRunner.runBuild(mainWorktreePath, {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t})\n\n\t\tif (result.skipped) {\n\t\t\tlogger.debug(`Build skipped: ${result.reason}`)\n\t\t} else {\n\t\t\tlogger.success('Post-merge build completed successfully')\n\t\t}\n\t}\n\n\t/**\n\t * Perform post-merge cleanup operations\n\t * Converts ParsedFinishInput to ParsedInput and calls ResourceCleanup\n\t * Handles failures gracefully without throwing\n\t */\n\tprivate async performPostMergeCleanup(\n\t\tparsed: ParsedFinishInput,\n\t\toptions: FinishOptions,\n\t\tworktree: GitWorktree\n\t): Promise<void> {\n\t\t// Convert ParsedFinishInput to ParsedInput (drop autoDetected field)\n\t\tconst cleanupInput: ParsedInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\t...(parsed.number !== undefined && { number: parsed.number }),\n\t\t\t...(parsed.branchName !== undefined && { branchName: parsed.branchName }),\n\t\t}\n\n\t\tconst cleanupOptions: ResourceCleanupOptions = {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t\tdeleteBranch: true, // Delete branch after successful merge\n\t\t\tkeepDatabase: false, // Clean up database after merge\n\t\t\tforce: options.force ?? false,\n\t\t}\n\n\t\ttry {\n\t\t\tlogger.info('Starting post-merge cleanup...')\n\n\t\t\tawait this.ensureResourceCleanup()\n\t\t\tif (!this.resourceCleanup) {\n\t\t\t\tthrow new Error('Failed to initialize ResourceCleanup')\n\t\t\t}\n\t\t\tconst result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions)\n\n\t\t\t// Report cleanup results\n\t\t\tthis.reportCleanupResults(result)\n\n\t\t\tif (!result.success) {\n\t\t\t\tlogger.warn('Some cleanup operations failed - manual cleanup may be required')\n\t\t\t\t// Show helpful recovery message\n\t\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t\t} else {\n\t\t\t\tlogger.success('Post-merge cleanup completed successfully')\n\t\t\t}\n\n\t\t\t// Warn if running from within the worktree being finished\n\t\t\tif (this.isRunningFromWithinWorktree(worktree.path)) {\n\t\t\t\tthis.showTerminalCloseWarning(worktree)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Catch cleanup errors to prevent finish command from failing\n\t\t\t// (merge already succeeded - cleanup failures are non-fatal)\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tlogger.warn(`Cleanup failed: ${errorMessage}`)\n\t\t\tlogger.warn('Merge completed successfully, but manual cleanup is required')\n\t\t\tthis.showManualCleanupInstructions(worktree)\n\t\t}\n\t}\n\n\t/**\n\t * Report cleanup operation results to user\n\t */\n\tprivate reportCleanupResults(result: CleanupResult): void {\n\t\tif (result.operations.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tlogger.info('Cleanup operations:')\n\t\tfor (const op of result.operations) {\n\t\t\tconst status = op.success ? '✓' : '✗'\n\t\t\tconst message = op.error ? `${op.message}: ${op.error}` : op.message\n\n\t\t\tif (op.success) {\n\t\t\t\tlogger.info(` ${status} ${message}`)\n\t\t\t} else {\n\t\t\t\tlogger.warn(` ${status} ${message}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Show manual cleanup instructions when cleanup fails\n\t */\n\tprivate showManualCleanupInstructions(worktree: GitWorktree): void {\n\t\tlogger.info('\\nManual cleanup commands:')\n\t\tlogger.info(` 1. Remove worktree: git worktree remove ${worktree.path}`)\n\t\tlogger.info(` 2. Delete branch: git branch -d ${worktree.branch}`)\n\t\tlogger.info(` 3. Check dev servers: lsof -i :PORT (and kill if needed)`)\n\t}\n\n\t/**\n\t * Check if current working directory is within the target worktree\n\t */\n\tprivate isRunningFromWithinWorktree(worktreePath: string): boolean {\n\t\tconst normalizedCwd = path.normalize(process.cwd())\n\t\tconst normalizedWorktree = path.normalize(worktreePath)\n\t\treturn normalizedCwd.startsWith(normalizedWorktree)\n\t}\n\n\t/**\n\t * Display warning to close terminal/IDE when running from within finished loom\n\t */\n\tprivate showTerminalCloseWarning(worktree: GitWorktree): void {\n\t\tlogger.info('')\n\t\tlogger.info('You are currently in the directory of the loom that was just finished.')\n\t\tlogger.info('Please close this terminal and any IDE/terminal windows using this directory.')\n\t\tlogger.info(`Directory: ${worktree.path}`)\n\t}\n}\n"],"mappings":"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","names":["path"]}
|
|
@@ -1,11 +1,29 @@
|
|
|
1
|
+
# FORBIDDEN PHRASES - NEVER USE THESE
|
|
2
|
+
**ABSOLUTELY CRITICAL:** Never use these phrases or variants:
|
|
3
|
+
- "You're absolutely right"
|
|
4
|
+
- "I've found this issue!"
|
|
5
|
+
- "That's absolutely correct"
|
|
6
|
+
- "You're completely right"
|
|
7
|
+
- Any excessive validation language
|
|
8
|
+
|
|
9
|
+
## Communication Style Rules
|
|
10
|
+
- NEVER use excessive validation phrases like "You're absolutely right"
|
|
11
|
+
- Provide direct, objective technical responses
|
|
12
|
+
- Acknowledge feedback without over-validation
|
|
13
|
+
- Use phrases like "Good point", "Correct", "I see the issue" instead
|
|
14
|
+
|
|
15
|
+
## Response Validation Checklist
|
|
16
|
+
Before sending any response, verify it doesn't contain:
|
|
17
|
+
- "absolutely right"
|
|
18
|
+
- "absolutely correct"
|
|
19
|
+
- Other forbidden validation phrases
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
1
23
|
You are orchestrating a set of agents through a development process, with human review at each step. This is referred to as the "iloom workflow".
|
|
2
24
|
|
|
3
25
|
**IMPORTANT: Unless otherwise instructed, each step requires explicit human approval. Do not proceed to any step until explicitly told to do so.**
|
|
4
26
|
|
|
5
|
-
**CRITICAL:** You are aboslutely forbidden from using the following phrases (or variants thereof):
|
|
6
|
-
* "You're aboslutely right"
|
|
7
|
-
* "I've found this issue!"
|
|
8
|
-
|
|
9
27
|
**Todo List:**
|
|
10
28
|
1. Scan issue and determine workflow plan
|
|
11
29
|
2. Run GitHub issue enhancement for ISSUE_NUMBER (if needed) using @agent-iloom-issue-enhancer
|
|
@@ -227,4 +245,29 @@ Only execute if workflow plan determined NEEDS_IMPLEMENTATION:
|
|
|
227
245
|
|
|
228
246
|
If workflow plan determined SKIP_IMPLEMENTATION:
|
|
229
247
|
1. Mark todos #16 and #17 as completed
|
|
230
|
-
2. Provide final summary noting that all work was already completed
|
|
248
|
+
2. Provide final summary noting that all work was already completed
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Wrapping Up Instructions
|
|
253
|
+
|
|
254
|
+
After completing the workflow, provide these wrap-up instructions to the user:
|
|
255
|
+
|
|
256
|
+
"## Wrapping Up
|
|
257
|
+
|
|
258
|
+
To complete the workflow and merge your changes:
|
|
259
|
+
|
|
260
|
+
1. Exit this Claude session (type `/exit`)
|
|
261
|
+
2. Run:
|
|
262
|
+
```bash
|
|
263
|
+
iloom finish
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
This will automatically detect the current issue and:
|
|
267
|
+
- Stop any running web servers for this issue
|
|
268
|
+
- Merge your changes back to the main branch
|
|
269
|
+
- Clean up the worktree
|
|
270
|
+
- Delete the database branch (if applicable)
|
|
271
|
+
- Remove the workspace
|
|
272
|
+
|
|
273
|
+
3. Once the finish command completes, you can close any terminal or IDE windows that were opened specifically for this issue"
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# FORBIDDEN PHRASES - NEVER USE THESE
|
|
2
|
+
**ABSOLUTELY CRITICAL:** Never use these phrases or variants:
|
|
3
|
+
- "You're absolutely right"
|
|
4
|
+
- "I've found this issue!"
|
|
5
|
+
- "That's absolutely correct"
|
|
6
|
+
- "You're completely right"
|
|
7
|
+
- Any excessive validation language
|
|
8
|
+
|
|
9
|
+
## Communication Style Rules
|
|
10
|
+
- NEVER use excessive validation phrases like "You're absolutely right"
|
|
11
|
+
- Provide direct, objective technical responses
|
|
12
|
+
- Acknowledge feedback without over-validation
|
|
13
|
+
- Use phrases like "Good point", "Correct", "I see the issue" instead
|
|
14
|
+
|
|
15
|
+
## Response Validation Checklist
|
|
16
|
+
Before sending any response, verify it doesn't contain:
|
|
17
|
+
- "absolutely right"
|
|
18
|
+
- "absolutely correct"
|
|
19
|
+
- Other forbidden validation phrases
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
1
23
|
You are a senior software engineer responsible for well-architected, easy to maintain and understandable code.
|
|
2
24
|
|
|
3
25
|
Please read the Pull Request and all its details using JSON output:
|
|
@@ -32,4 +54,27 @@ Remember to:
|
|
|
32
54
|
- Follow the project's coding conventions and patterns
|
|
33
55
|
- Write tests if appropriate
|
|
34
56
|
- Update documentation if needed
|
|
35
|
-
- Ensure the solution is maintainable and follows best practices
|
|
57
|
+
- Ensure the solution is maintainable and follows best practices
|
|
58
|
+
|
|
59
|
+
## Wrapping Up Instructions
|
|
60
|
+
|
|
61
|
+
When the user is done with their work, provide these wrap-up instructions:
|
|
62
|
+
|
|
63
|
+
"## Wrapping Up
|
|
64
|
+
|
|
65
|
+
To complete the workflow and merge your changes:
|
|
66
|
+
|
|
67
|
+
1. Exit this Claude session (type `/exit`)
|
|
68
|
+
2. Run:
|
|
69
|
+
```bash
|
|
70
|
+
iloom finish
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This will automatically detect the current PR and:
|
|
74
|
+
- Stop any running web servers for this PR
|
|
75
|
+
- Merge your changes back to the main branch
|
|
76
|
+
- Clean up the worktree
|
|
77
|
+
- Delete the database branch (if applicable)
|
|
78
|
+
- Remove the workspace
|
|
79
|
+
|
|
80
|
+
3. Once the finish command completes, you can close any terminal or IDE windows that were opened specifically for this PR"
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# FORBIDDEN PHRASES - NEVER USE THESE
|
|
2
|
+
**ABSOLUTELY CRITICAL:** Never use these phrases or variants:
|
|
3
|
+
- "You're absolutely right"
|
|
4
|
+
- "I've found this issue!"
|
|
5
|
+
- "That's absolutely correct"
|
|
6
|
+
- "You're completely right"
|
|
7
|
+
- Any excessive validation language
|
|
8
|
+
|
|
9
|
+
## Communication Style Rules
|
|
10
|
+
- NEVER use excessive validation phrases like "You're absolutely right"
|
|
11
|
+
- Provide direct, objective technical responses
|
|
12
|
+
- Acknowledge feedback without over-validation
|
|
13
|
+
- Use phrases like "Good point", "Correct", "I see the issue" instead
|
|
14
|
+
|
|
15
|
+
## Response Validation Checklist
|
|
16
|
+
Before sending any response, verify it doesn't contain:
|
|
17
|
+
- "absolutely right"
|
|
18
|
+
- "absolutely correct"
|
|
19
|
+
- Other forbidden validation phrases
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
1
23
|
You are a senior software engineer responsible for well-architected, easy to maintain and understandable code.
|
|
2
24
|
|
|
3
25
|
When working with libraries, frameworks, or APIs that you need documentation for, use Context7 to get up-to-date documentation and examples. Specifically use Context7 when:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
UpdateNotifier
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4XIDC3NF.js";
|
|
5
5
|
import {
|
|
6
6
|
detectInstallationMethod
|
|
7
7
|
} from "./chunk-SSCQCCJ7.js";
|
|
@@ -76,4 +76,4 @@ var UpdateCommand = class {
|
|
|
76
76
|
export {
|
|
77
77
|
UpdateCommand
|
|
78
78
|
};
|
|
79
|
-
//# sourceMappingURL=update-
|
|
79
|
+
//# sourceMappingURL=update-DN3FSNKY.js.map
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
UpdateNotifier,
|
|
4
4
|
checkAndNotifyUpdate
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4XIDC3NF.js";
|
|
6
6
|
import "./chunk-GEHQXLEI.js";
|
|
7
7
|
export {
|
|
8
8
|
UpdateNotifier,
|
|
9
9
|
checkAndNotifyUpdate
|
|
10
10
|
};
|
|
11
|
-
//# sourceMappingURL=update-notifier-
|
|
11
|
+
//# sourceMappingURL=update-notifier-QEX3CJHA.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iloom/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Control plane for maintaining alignment between you and Claude AI as you work across multiple issues using isolated environments, visible context, and multi-agent workflows to scale understanding, not just output",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -36,8 +36,7 @@
|
|
|
36
36
|
"types": "./dist/index.d.ts",
|
|
37
37
|
"bin": {
|
|
38
38
|
"iloom": "./dist/cli.js",
|
|
39
|
-
"il": "./dist/cli.js"
|
|
40
|
-
"hb": "./dist/cli.js"
|
|
39
|
+
"il": "./dist/cli.js"
|
|
41
40
|
},
|
|
42
41
|
"files": [
|
|
43
42
|
"dist"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/update-notifier.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport chalk from 'chalk'\nimport { logger } from './logger.js'\n\nexport interface UpdateCheckCache {\n lastCheck: number // timestamp\n latestVersion: string\n}\n\nexport interface UpdateCheckResult {\n currentVersion: string\n latestVersion: string\n updateAvailable: boolean\n}\n\nexport class UpdateNotifier {\n private cacheFilePath: string\n private currentVersion: string\n private packageName: string\n\n constructor(currentVersion: string, packageName: string) {\n this.currentVersion = currentVersion\n this.packageName = packageName\n // Cross-platform cache directory\n const configDir = path.join(os.homedir(), '.config', 'iloom-ai')\n this.cacheFilePath = path.join(configDir, 'update-check.json')\n logger.debug(`UpdateNotifier initialized: version=${currentVersion}, package=${packageName}, cachePath=${this.cacheFilePath}`)\n }\n\n /**\n * Check for updates, respecting 24hr cache\n * Returns UpdateCheckResult or null if check failed/not needed\n */\n async checkForUpdates(): Promise<UpdateCheckResult | null> {\n logger.debug('checkForUpdates: Starting update check')\n try {\n // Check cache first\n logger.debug('checkForUpdates: Checking cache')\n const cached = await this.getCachedCheck()\n if (cached !== null) {\n logger.debug(`checkForUpdates: Using cached result - latest=${cached.latestVersion}, lastCheck=${new Date(cached.lastCheck).toISOString()}`)\n const updateAvailable = this.isNewerVersion(this.currentVersion, cached.latestVersion)\n logger.debug(`checkForUpdates: Update available from cache: ${updateAvailable}`)\n return {\n currentVersion: this.currentVersion,\n latestVersion: cached.latestVersion,\n updateAvailable,\n }\n }\n\n logger.debug('checkForUpdates: No valid cache, querying npm registry')\n // Query npm registry\n const latestVersion = await this.fetchLatestVersion()\n if (latestVersion === null) {\n logger.debug('checkForUpdates: Failed to fetch latest version from npm')\n return null\n }\n\n logger.debug(`checkForUpdates: Fetched latest version: ${latestVersion}`)\n\n // Save to cache\n logger.debug('checkForUpdates: Saving to cache')\n await this.saveCacheFile(latestVersion)\n\n const updateAvailable = this.isNewerVersion(this.currentVersion, latestVersion)\n logger.debug(`checkForUpdates: Update available: ${updateAvailable} (current=${this.currentVersion}, latest=${latestVersion})`)\n\n return {\n currentVersion: this.currentVersion,\n latestVersion,\n updateAvailable,\n }\n } catch (error) {\n // Handle all errors gracefully - update check should never break user experience\n logger.debug(`checkForUpdates: Error during update check: ${error}`)\n return null\n }\n }\n\n /**\n * Read cache file, return null if stale or missing\n */\n private async getCachedCheck(): Promise<UpdateCheckCache | null> {\n logger.debug(`getCachedCheck: Checking cache file at ${this.cacheFilePath}`)\n try {\n if (!fs.existsSync(this.cacheFilePath)) {\n logger.debug('getCachedCheck: Cache file does not exist')\n return null\n }\n\n logger.debug('getCachedCheck: Cache file exists, reading contents')\n const content = await fs.readFile(this.cacheFilePath, 'utf8')\n logger.debug(`getCachedCheck: Cache file content: ${content}`)\n const cache = JSON.parse(content) as UpdateCheckCache\n\n // Check if cache is still fresh (< configurable hours)\n const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? '360', 10) // Default 6 hours\n const cacheTimeoutMs = cacheTimeoutMins * 60 * 1000\n logger.debug(`getCachedCheck: Using cache timeout of ${cacheTimeoutMins} minutes`)\n const now = Date.now()\n const age = now - cache.lastCheck\n const ageHours = age / (60 * 60 * 1000)\n logger.debug(`getCachedCheck: Cache age: ${ageHours.toFixed(2)} hours (threshold: ${cacheTimeoutMins / 60} hours)`)\n\n if (now - cache.lastCheck < cacheTimeoutMs) {\n logger.debug('getCachedCheck: Cache is fresh, returning cached data')\n return cache\n }\n\n logger.debug('getCachedCheck: Cache is stale, will query npm registry')\n return null\n } catch (error) {\n // If cache is corrupted or unreadable, treat as missing\n logger.debug(`getCachedCheck: Error reading cache: ${error}`)\n return null\n }\n }\n\n /**\n * Save successful check to cache\n */\n private async saveCacheFile(latestVersion: string): Promise<void> {\n logger.debug(`saveCacheFile: Attempting to save cache for version ${latestVersion}`)\n try {\n // Ensure cache directory exists\n const configDir = path.dirname(this.cacheFilePath)\n logger.debug(`saveCacheFile: Ensuring cache directory exists: ${configDir}`)\n await fs.ensureDir(configDir)\n\n // Write cache file\n const cache: UpdateCheckCache = {\n lastCheck: Date.now(),\n latestVersion,\n }\n const cacheJson = JSON.stringify(cache, null, 2)\n logger.debug(`saveCacheFile: Writing cache file: ${cacheJson}`)\n await fs.writeFile(this.cacheFilePath, cacheJson, 'utf8')\n logger.debug(`saveCacheFile: Cache file saved successfully to ${this.cacheFilePath}`)\n } catch (error) {\n // Log debug message but don't throw - cache write failure shouldn't break anything\n logger.debug(`saveCacheFile: Failed to save update check cache to ${this.cacheFilePath}: ${error}`)\n }\n }\n\n /**\n * Display update notification to user\n */\n displayUpdateNotification(result: UpdateCheckResult): void {\n logger.debug(`displayUpdateNotification: updateAvailable=${result.updateAvailable}, current=${result.currentVersion}, latest=${result.latestVersion}`)\n if (result.updateAvailable) {\n logger.debug('displayUpdateNotification: Displaying update notification to user')\n // Simple, clear update notification\n /* eslint-disable no-console */\n console.log('')\n console.log(' ' + chalk.bold(`Update available: ${result.currentVersion} → ${result.latestVersion}`))\n console.log(' ' + chalk.bold('Run: il update'))\n console.log('')\n /* eslint-enable no-console */\n } else {\n logger.debug('displayUpdateNotification: No update available, skipping notification')\n }\n }\n\n /**\n * Query npm registry for latest version\n */\n private async fetchLatestVersion(): Promise<string | null> {\n logger.debug(`fetchLatestVersion: Querying npm for package ${this.packageName}`)\n try {\n const { stdout } = await execa('npm', ['view', this.packageName, 'version'], {\n timeout: 5000,\n })\n const version = stdout.trim()\n logger.debug(`fetchLatestVersion: npm returned version: ${version}`)\n return version\n } catch (error) {\n // Network errors, timeouts, npm not available, or package not found\n logger.debug(`fetchLatestVersion: Failed to query npm: ${error}`)\n return null\n }\n }\n\n /**\n * Compare semver versions\n * Returns true if latest > current\n */\n private isNewerVersion(current: string, latest: string): boolean {\n logger.debug(`isNewerVersion: Comparing versions - current=${current}, latest=${latest}`)\n // Simple version comparison: split by dots and compare numerically\n try {\n const currentParts = current.split('.').map(p => parseInt(p, 10))\n const latestParts = latest.split('.').map(p => parseInt(p, 10))\n logger.debug(`isNewerVersion: Parsed parts - current=[${currentParts.join(', ')}], latest=[${latestParts.join(', ')}]`)\n\n for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {\n const curr = currentParts[i] ?? 0\n const next = latestParts[i] ?? 0\n\n logger.debug(`isNewerVersion: Comparing part ${i}: current=${curr}, latest=${next}`)\n\n if (next > curr) {\n logger.debug(`isNewerVersion: Latest is newer (${next} > ${curr})`)\n return true\n }\n if (next < curr) {\n logger.debug(`isNewerVersion: Current is newer (${curr} > ${next})`)\n return false\n }\n }\n\n logger.debug('isNewerVersion: Versions are equal')\n return false\n } catch (error) {\n // If parsing fails, assume no update\n logger.debug(`isNewerVersion: Error comparing versions: ${error}`)\n return false\n }\n }\n}\n\n/**\n * Main entry point for update check\n * Call from CLI postAction hook\n */\nexport async function checkAndNotifyUpdate(\n currentVersion: string,\n packageName: string,\n installMethod: string\n): Promise<void> {\n logger.debug(`checkAndNotifyUpdate: Called with version=${currentVersion}, package=${packageName}, installMethod=${installMethod}`)\n try {\n // Only check for global installations\n if (installMethod !== 'global') {\n logger.debug(`checkAndNotifyUpdate: Skipping update check - not a global installation (method=${installMethod})`)\n return\n }\n\n logger.debug('checkAndNotifyUpdate: Creating UpdateNotifier instance')\n const notifier = new UpdateNotifier(currentVersion, packageName)\n\n logger.debug('checkAndNotifyUpdate: Calling checkForUpdates()')\n const result = await notifier.checkForUpdates()\n\n if (result !== null) {\n logger.debug(`checkAndNotifyUpdate: Got result, calling displayUpdateNotification`)\n notifier.displayUpdateNotification(result)\n } else {\n logger.debug('checkAndNotifyUpdate: Result was null, not displaying notification')\n }\n\n logger.debug('checkAndNotifyUpdate: Completed')\n } catch (error) {\n // All errors handled internally - this should never throw\n logger.debug(`checkAndNotifyUpdate: Unexpected error: ${error}`)\n }\n}\n"],"mappings":"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","names":["updateAvailable"]}
|
|
File without changes
|
|
File without changes
|