@iloom/cli 0.1.17 → 0.1.19
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 +51 -6
- package/dist/ClaudeContextManager-JKR4WGNU.js +13 -0
- package/dist/ClaudeService-55DQGB7T.js +12 -0
- package/dist/{GitHubService-F7Z3XJOS.js → GitHubService-LWP4GKGH.js} +3 -3
- package/dist/{LoomLauncher-MODG2SEM.js → LoomLauncher-UMMLPIZO.js} +7 -7
- package/dist/{PromptTemplateManager-7FINLRDE.js → PromptTemplateManager-WII75TKH.js} +2 -2
- package/dist/README.md +755 -0
- package/dist/{SettingsManager-VAZF26S2.js → SettingsManager-SKLUVE3K.js} +6 -2
- package/dist/{add-issue-22JBNOML.js → add-issue-X56V3XPB.js} +23 -8
- package/dist/add-issue-X56V3XPB.js.map +1 -0
- package/dist/{chunk-Y7SAGNUT.js → chunk-DEPYQRRB.js} +2 -2
- package/dist/{chunk-WKEWRSDB.js → chunk-ELFT36PV.js} +3 -3
- package/dist/chunk-FXV24OYZ.js +83 -0
- package/dist/chunk-FXV24OYZ.js.map +1 -0
- package/dist/{chunk-HPJJSYNS.js → chunk-H5LDRGVK.js} +6 -8
- package/dist/{chunk-HPJJSYNS.js.map → chunk-H5LDRGVK.js.map} +1 -1
- package/dist/{chunk-QEPVTTHD.js → chunk-IO4WFTL2.js} +17 -11
- package/dist/chunk-IO4WFTL2.js.map +1 -0
- package/dist/{chunk-KQDEK2ZW.js → chunk-JXQXSC45.js} +41 -9
- package/dist/chunk-JXQXSC45.js.map +1 -0
- package/dist/{chunk-JQ7VOSTC.js → chunk-KOCQAD2E.js} +3 -3
- package/dist/{chunk-YYSKGAZT.js → chunk-LAPY6NAE.js} +17 -8
- package/dist/chunk-LAPY6NAE.js.map +1 -0
- package/dist/{chunk-O2QWO64Z.js → chunk-PV3GAXQO.js} +56 -3
- package/dist/chunk-PV3GAXQO.js.map +1 -0
- package/dist/chunk-PVAVNJKS.js +188 -0
- package/dist/chunk-PVAVNJKS.js.map +1 -0
- package/dist/{chunk-CP2NU2JC.js → chunk-Q2KYPAH2.js} +7 -7
- package/dist/{chunk-CP2NU2JC.js.map → chunk-Q2KYPAH2.js.map} +1 -1
- package/dist/{chunk-W3DQTW63.js → chunk-USVVV3FP.js} +4 -4
- package/dist/{chunk-SSR5AVRJ.js → chunk-VCMMAFXQ.js} +21 -8
- package/dist/chunk-VCMMAFXQ.js.map +1 -0
- package/dist/chunk-VVH3ANF2.js +307 -0
- package/dist/chunk-VVH3ANF2.js.map +1 -0
- package/dist/{chunk-JBH2ZYYZ.js → chunk-VYQLLHZ7.js} +22 -3
- package/dist/chunk-VYQLLHZ7.js.map +1 -0
- package/dist/{chunk-SJUQ2NDR.js → chunk-ZMNQBJUI.js} +24 -19
- package/dist/chunk-ZMNQBJUI.js.map +1 -0
- package/dist/{chunk-T7QPXANZ.js → chunk-ZWXJBSUW.js} +17 -17
- package/dist/chunk-ZWXJBSUW.js.map +1 -0
- package/dist/{cleanup-3LUWPSM7.js → cleanup-ZHROIBSQ.js} +12 -16
- package/dist/cleanup-ZHROIBSQ.js.map +1 -0
- package/dist/cli.js +107 -49
- package/dist/cli.js.map +1 -1
- package/dist/contribute-3MQJ3XAQ.js +256 -0
- package/dist/contribute-3MQJ3XAQ.js.map +1 -0
- package/dist/{enhance-XJIQHVPD.js → enhance-VGWUX474.js} +18 -8
- package/dist/enhance-VGWUX474.js.map +1 -0
- package/dist/{feedback-23CLXKFT.js → feedback-ZOUCCHN4.js} +8 -8
- package/dist/{finish-3CQZIULO.js → finish-QJSK6Z7J.js} +36 -313
- package/dist/finish-QJSK6Z7J.js.map +1 -0
- package/dist/{git-LVRZ57GJ.js → git-OUYMVYJX.js} +2 -2
- package/dist/{ignite-WXEF2ID5.js → ignite-HICLZEYU.js} +124 -9
- package/dist/ignite-HICLZEYU.js.map +1 -0
- package/dist/index.d.ts +794 -712
- package/dist/index.js +169 -36
- package/dist/index.js.map +1 -1
- package/dist/init-UMKNHNV5.js +339 -0
- package/dist/init-UMKNHNV5.js.map +1 -0
- package/dist/mcp/github-comment-server.js +12 -9
- package/dist/mcp/github-comment-server.js.map +1 -1
- package/dist/neon-helpers-ZVIRPKCI.js +10 -0
- package/dist/{open-X6BTENPV.js → open-ETZUFSE4.js} +15 -17
- package/dist/{open-X6BTENPV.js.map → open-ETZUFSE4.js.map} +1 -1
- package/dist/prompts/init-prompt.txt +748 -0
- package/dist/prompts/issue-prompt.txt +141 -9
- package/dist/rebase-KBWFDZCN.js +95 -0
- package/dist/rebase-KBWFDZCN.js.map +1 -0
- package/dist/remote-GJEZWRCC.js +14 -0
- package/dist/{run-2JCPQAX3.js → run-4SVQ3WEU.js} +15 -17
- package/dist/{run-2JCPQAX3.js.map → run-4SVQ3WEU.js.map} +1 -1
- package/dist/schema/settings.schema.json +51 -1
- package/dist/{start-LWVRBJ6S.js → start-CT2ZEFP2.js} +54 -53
- package/dist/{start-LWVRBJ6S.js.map → start-CT2ZEFP2.js.map} +1 -1
- package/dist/{test-git-XPF4SZXJ.js → test-git-MKZATGZN.js} +3 -3
- package/dist/{test-prefix-XGFXFAYN.js → test-prefix-ZNLWDI3K.js} +3 -3
- package/dist/{update-DN3FSNKY.js → update-4TDDUR5K.js} +10 -4
- package/dist/{update-DN3FSNKY.js.map → update-4TDDUR5K.js.map} +1 -1
- package/package.json +3 -2
- package/dist/ClaudeContextManager-XOSXQ67R.js +0 -13
- package/dist/ClaudeService-YSZ6EXWP.js +0 -12
- package/dist/NeonProvider-PAGPUH7F.js +0 -12
- package/dist/add-issue-22JBNOML.js.map +0 -1
- package/dist/chunk-37DYYFVK.js +0 -29
- package/dist/chunk-37DYYFVK.js.map +0 -1
- package/dist/chunk-F3XBU2R7.js +0 -110
- package/dist/chunk-F3XBU2R7.js.map +0 -1
- package/dist/chunk-JBH2ZYYZ.js.map +0 -1
- package/dist/chunk-KQDEK2ZW.js.map +0 -1
- package/dist/chunk-O2QWO64Z.js.map +0 -1
- package/dist/chunk-QEPVTTHD.js.map +0 -1
- package/dist/chunk-SJUQ2NDR.js.map +0 -1
- package/dist/chunk-SSR5AVRJ.js.map +0 -1
- package/dist/chunk-T7QPXANZ.js.map +0 -1
- package/dist/chunk-YYSKGAZT.js.map +0 -1
- package/dist/cleanup-3LUWPSM7.js.map +0 -1
- package/dist/enhance-XJIQHVPD.js.map +0 -1
- package/dist/env-MDFL4ZXL.js +0 -23
- package/dist/finish-3CQZIULO.js.map +0 -1
- package/dist/ignite-WXEF2ID5.js.map +0 -1
- package/dist/init-RHACUR4E.js +0 -123
- package/dist/init-RHACUR4E.js.map +0 -1
- /package/dist/{ClaudeContextManager-XOSXQ67R.js.map → ClaudeContextManager-JKR4WGNU.js.map} +0 -0
- /package/dist/{ClaudeService-YSZ6EXWP.js.map → ClaudeService-55DQGB7T.js.map} +0 -0
- /package/dist/{GitHubService-F7Z3XJOS.js.map → GitHubService-LWP4GKGH.js.map} +0 -0
- /package/dist/{LoomLauncher-MODG2SEM.js.map → LoomLauncher-UMMLPIZO.js.map} +0 -0
- /package/dist/{NeonProvider-PAGPUH7F.js.map → PromptTemplateManager-WII75TKH.js.map} +0 -0
- /package/dist/{PromptTemplateManager-7FINLRDE.js.map → SettingsManager-SKLUVE3K.js.map} +0 -0
- /package/dist/{chunk-Y7SAGNUT.js.map → chunk-DEPYQRRB.js.map} +0 -0
- /package/dist/{chunk-WKEWRSDB.js.map → chunk-ELFT36PV.js.map} +0 -0
- /package/dist/{chunk-JQ7VOSTC.js.map → chunk-KOCQAD2E.js.map} +0 -0
- /package/dist/{chunk-W3DQTW63.js.map → chunk-USVVV3FP.js.map} +0 -0
- /package/dist/{feedback-23CLXKFT.js.map → feedback-ZOUCCHN4.js.map} +0 -0
- /package/dist/{SettingsManager-VAZF26S2.js.map → git-OUYMVYJX.js.map} +0 -0
- /package/dist/{env-MDFL4ZXL.js.map → neon-helpers-ZVIRPKCI.js.map} +0 -0
- /package/dist/{git-LVRZ57GJ.js.map → remote-GJEZWRCC.js.map} +0 -0
- /package/dist/{test-git-XPF4SZXJ.js.map → test-git-MKZATGZN.js.map} +0 -0
- /package/dist/{test-prefix-XGFXFAYN.js.map → test-prefix-ZNLWDI3K.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
SettingsManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VYQLLHZ7.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-GEHQXLEI.js";
|
|
@@ -112,7 +112,7 @@ ${blockerMessage}`);
|
|
|
112
112
|
let mainWorktreePath = null;
|
|
113
113
|
if (!options.dryRun) {
|
|
114
114
|
try {
|
|
115
|
-
const { findMainWorktreePathWithSettings } = await import("./git-
|
|
115
|
+
const { findMainWorktreePathWithSettings } = await import("./git-OUYMVYJX.js");
|
|
116
116
|
mainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager);
|
|
117
117
|
} catch (error) {
|
|
118
118
|
logger.warn(
|
|
@@ -363,7 +363,7 @@ ${blockerMessage}`);
|
|
|
363
363
|
logger.info(`[DRY RUN] Would delete branch: ${branchName}`);
|
|
364
364
|
return true;
|
|
365
365
|
}
|
|
366
|
-
const { executeGitCommand, findMainWorktreePathWithSettings } = await import("./git-
|
|
366
|
+
const { executeGitCommand, findMainWorktreePathWithSettings } = await import("./git-OUYMVYJX.js");
|
|
367
367
|
try {
|
|
368
368
|
let workingDir = cwd ?? await findMainWorktreePathWithSettings(void 0, this.settingsManager);
|
|
369
369
|
const deleteFlag = options.force ? "-D" : "-d";
|
|
@@ -407,7 +407,7 @@ ${blockerMessage}`);
|
|
|
407
407
|
const shouldCleanup = await this.database.shouldUseDatabaseBranching(envFilePath);
|
|
408
408
|
let cwd;
|
|
409
409
|
try {
|
|
410
|
-
const { findMainWorktreePathWithSettings } = await import("./git-
|
|
410
|
+
const { findMainWorktreePathWithSettings } = await import("./git-OUYMVYJX.js");
|
|
411
411
|
cwd = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager);
|
|
412
412
|
} catch (error) {
|
|
413
413
|
logger.debug(
|
|
@@ -463,11 +463,11 @@ ${blockerMessage}`);
|
|
|
463
463
|
async validateWorktreeSafety(worktree, identifier) {
|
|
464
464
|
const warnings = [];
|
|
465
465
|
const blockers = [];
|
|
466
|
-
const isMain = await this.gitWorktree.isMainWorktree(worktree);
|
|
466
|
+
const isMain = await this.gitWorktree.isMainWorktree(worktree, this.settingsManager);
|
|
467
467
|
if (isMain) {
|
|
468
468
|
blockers.push(`Cannot cleanup main worktree: "${worktree.branch}" @ "${worktree.path}"`);
|
|
469
469
|
}
|
|
470
|
-
const { hasUncommittedChanges } = await import("./git-
|
|
470
|
+
const { hasUncommittedChanges } = await import("./git-OUYMVYJX.js");
|
|
471
471
|
const hasChanges = await hasUncommittedChanges(worktree.path);
|
|
472
472
|
if (hasChanges) {
|
|
473
473
|
const blockerMessage = `Worktree has uncommitted changes.
|
|
@@ -542,4 +542,4 @@ Please resolve before cleanup - you have some options:
|
|
|
542
542
|
export {
|
|
543
543
|
ResourceCleanup
|
|
544
544
|
};
|
|
545
|
-
//# sourceMappingURL=chunk-
|
|
545
|
+
//# sourceMappingURL=chunk-Q2KYPAH2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/ResourceCleanup.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from './GitWorktreeManager.js'\nimport { DatabaseManager } from './DatabaseManager.js'\nimport { ProcessManager } from './process/ProcessManager.js'\nimport { CLIIsolationManager } from './CLIIsolationManager.js'\nimport { SettingsManager } from './SettingsManager.js'\nimport { logger } from '../utils/logger.js'\nimport type {\n\tResourceCleanupOptions,\n\tCleanupResult,\n\tOperationResult,\n\tSafetyCheck,\n\tBranchDeleteOptions,\n} from '../types/cleanup.js'\nimport type { GitWorktree } from '../types/worktree.js'\nimport type { ParsedInput } from '../commands/start.js'\n\n/**\n * Manages resource cleanup for worktrees\n * Provides shared cleanup functionality for finish and cleanup commands\n */\nexport class ResourceCleanup {\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(\n\t\tprivate gitWorktree: GitWorktreeManager,\n\t\tprivate processManager: ProcessManager,\n\t\tprivate database?: DatabaseManager,\n\t\tprivate cliIsolation?: CLIIsolationManager,\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t}\n\n\t/**\n\t * Cleanup a worktree and associated resources\n\t * Main orchestration method\n\t *\n\t * @param parsed - ParsedInput from IdentifierParser with type information\n\t * @param options - Cleanup options\n\t */\n\tasync cleanupWorktree(\n\t\tparsed: ParsedInput,\n\t\toptions: ResourceCleanupOptions = {}\n\t): Promise<CleanupResult> {\n\t\tconst operations: OperationResult[] = []\n\t\tconst errors: Error[] = []\n\n\t\tconst displayIdentifier = parsed.branchName ?? parsed.number?.toString() ?? parsed.originalInput\n\t\tlogger.info(`Starting cleanup for: ${displayIdentifier}`)\n\n\t\t// Extract number from ParsedInput for port calculation\n\t\tconst number = parsed.number\n\n\t\t// Step 1: Terminate dev server if applicable\n\t\tif (number !== undefined) {\n\t\t\tconst port = this.processManager.calculatePort(number)\n\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would check for dev server on port ${port}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst terminated = await this.terminateDevServer(port)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: terminated\n\t\t\t\t\t\t\t? `Dev server on port ${port} terminated`\n\t\t\t\t\t\t\t: `No dev server running on port ${port}`,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Failed to terminate dev server`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 2: Find worktree using specific methods based on type\n\t\tlet worktree: GitWorktree | null = null\n\t\ttry {\n\t\t\t// Use specific finding methods based on parsed type for precision\n\t\t\tif (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t\t// For PRs, pass empty string for branchName since we're detecting from path pattern\n\t\t\t\tworktree = await this.gitWorktree.findWorktreeForPR(parsed.number, '')\n\t\t\t} else if (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\t\tworktree = await this.gitWorktree.findWorktreeForIssue(parsed.number)\n\t\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\t\tworktree = await this.gitWorktree.findWorktreeForBranch(parsed.branchName)\n\t\t\t}\n\n\t\t\tif (!worktree) {\n\t\t\t\tthrow new Error(`No worktree found for identifier: ${displayIdentifier}`)\n\t\t\t}\n\n\t\t\tlogger.debug(`Found worktree: path=\"${worktree.path}\", branch=\"${worktree.branch}\"`)\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\terrors.push(err)\n\n\t\t\treturn {\n\t\t\t\tidentifier: displayIdentifier,\n\t\t\t\tsuccess: false,\n\t\t\t\toperations,\n\t\t\t\terrors,\n\t\t\t\trollbackRequired: false,\n\t\t\t}\n\t\t}\n\n\t\t// Step 2.5: Validate safety before proceeding with cleanup (unless force flag is set)\n\t\tif (!options.force) {\n\t\t\tconst safety = await this.validateWorktreeSafety(worktree, parsed.originalInput)\n\n\t\t\tif (!safety.isSafe) {\n\t\t\t\t// Format blocker messages for error output\n\t\t\t\tconst blockerMessage = safety.blockers.join('\\n\\n')\n\t\t\t\tthrow new Error(`Cannot cleanup:\\n\\n${blockerMessage}`)\n\t\t\t}\n\n\t\t\t// Log warnings if any\n\t\t\tif (safety.warnings.length > 0) {\n\t\t\t\tsafety.warnings.forEach(warning => {\n\t\t\t\t\tlogger.warn(warning)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Step 3: Pre-fetch database configuration before worktree removal\n\t\t// This config is used AFTER worktree deletion when env file no longer exists\n\t\tlet databaseConfig: { shouldCleanup: boolean; envFilePath: string } | null = null\n\t\tif (!options.keepDatabase && worktree) {\n\t\t\tconst envFilePath = path.join(worktree.path, '.env')\n\t\t\ttry {\n\t\t\t\t// Pre-check if database cleanup should happen by reading .env file now\n\t\t\t\tconst shouldCleanup = this.database\n\t\t\t\t\t? await this.database.shouldUseDatabaseBranching(envFilePath)\n\t\t\t\t\t: false\n\t\t\t\tdatabaseConfig = { shouldCleanup, envFilePath }\n\t\t\t} catch (error) {\n\t\t\t\t// If we can't read the config, we'll skip database cleanup\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to read database config from ${envFilePath}, skipping database cleanup: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`\n\t\t\t\t)\n\t\t\t\tdatabaseConfig = { shouldCleanup: false, envFilePath }\n\t\t\t}\n\t\t}\n\n\t\t// Step 3.5: Find main worktree path before deletion (needed for branch and database operations)\n\t\tlet mainWorktreePath: string | null = null\n\t\tif (!options.dryRun) {\n\t\t\ttry {\n\t\t\t\tconst { findMainWorktreePathWithSettings } = await import('../utils/git.js')\n\t\t\t\tmainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to find main worktree path: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Step 4: Remove worktree\n\t\tif (options.dryRun) {\n\t\t\toperations.push({\n\t\t\t\ttype: 'worktree',\n\t\t\t\tsuccess: true,\n\t\t\t\tmessage: `[DRY RUN] Would remove worktree: ${worktree.path}`,\n\t\t\t})\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst worktreeOptions: { force?: boolean; removeDirectory: true; removeBranch: false } =\n\t\t\t\t\t{\n\t\t\t\t\t\tremoveDirectory: true,\n\t\t\t\t\t\tremoveBranch: false, // Handle branch separately\n\t\t\t\t\t}\n\t\t\t\tif (options.force !== undefined) {\n\t\t\t\t\tworktreeOptions.force = options.force\n\t\t\t\t}\n\t\t\t\tawait this.gitWorktree.removeWorktree(worktree.path, worktreeOptions)\n\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'worktree',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Worktree removed: ${worktree.path}`,\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\terrors.push(err)\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'worktree',\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tmessage: `Failed to remove worktree`,\n\t\t\t\t\terror: err.message,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Step 5: Delete branch if requested\n\t\tif (options.deleteBranch && worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'branch',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would delete branch: ${worktree.branch}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst branchOptions: BranchDeleteOptions = { dryRun: false }\n\t\t\t\t\tif (options.force !== undefined) {\n\t\t\t\t\t\tbranchOptions.force = options.force\n\t\t\t\t\t}\n\t\t\t\t\t// Pass main worktree path to ensure we can execute git commands\n\t\t\t\t\tawait this.deleteBranch(worktree.branch, branchOptions, mainWorktreePath ?? undefined)\n\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'branch',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: `Branch deleted: ${worktree.branch}`,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'branch',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Failed to delete branch`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 5.5: Cleanup CLI symlinks if CLI isolation is available\n\t\t// Derive identifier from parsed input (number for issue/PR, branchName for branch)\n\t\tconst cliIdentifier = parsed.number ?? parsed.branchName\n\t\tif (this.cliIsolation && cliIdentifier !== undefined) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would cleanup CLI symlinks for: ${cliIdentifier}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst removed = await this.cliIsolation.cleanupVersionedExecutables(cliIdentifier)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: removed.length > 0\n\t\t\t\t\t\t\t? `CLI symlinks removed: ${removed.length}`\n\t\t\t\t\t\t\t: 'No CLI symlinks to cleanup',\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log warning but don't fail\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`CLI symlink cleanup failed: ${err.message}`\n\t\t\t\t\t)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: 'CLI symlink cleanup failed (non-fatal)',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 6: Cleanup database after worktree and branch removal (using pre-read config)\n\t\tif (databaseConfig && worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'database',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would cleanup database branch for: ${worktree.branch}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tif (databaseConfig.shouldCleanup && this.database) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Call database deletion with pre-fetched shouldCleanup value and main worktree path\n\t\t\t\t\t\t\t// This avoids reading the already-deleted env file and running commands from deleted directories\n\t\t\t\t\t\t\tconst deletionResult = await this.database.deleteBranchIfConfigured(\n\t\t\t\t\t\t\t\tworktree.branch,\n\t\t\t\t\t\t\t\tdatabaseConfig.shouldCleanup,\n\t\t\t\t\t\t\t\tfalse, // isPreview\n\t\t\t\t\t\t\t\tmainWorktreePath ?? undefined\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\t// Create operation result based on what actually happened\n\t\t\t\t\t\t\tif (deletionResult.deleted) {\n\t\t\t\t\t\t\t\t// Branch was actually deleted\n\t\t\t\t\t\t\t\tlogger.info(`Database branch deleted: ${worktree.branch}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `Database branch deleted`,\n\t\t\t\t\t\t\t\t\tdeleted: true,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (deletionResult.notFound) {\n\t\t\t\t\t\t\t\t// Branch didn't exist - not an error, just nothing to delete\n\t\t\t\t\t\t\t\tlogger.debug(`No database branch found for: ${worktree.branch}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `No database branch found (skipped)`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (deletionResult.userDeclined) {\n\t\t\t\t\t\t\t\t// User declined preview database deletion\n\t\t\t\t\t\t\t\tlogger.info('Preview database deletion declined by user')\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup skipped (user declined)`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (!deletionResult.success) {\n\t\t\t\t\t\t\t\t// Deletion failed with error\n\t\t\t\t\t\t\t\tconst errorMsg = deletionResult.error ?? 'Unknown error'\n\t\t\t\t\t\t\t\terrors.push(new Error(errorMsg))\n\t\t\t\t\t\t\t\tlogger.warn(`Database cleanup failed: ${errorMsg}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: false, // Non-fatal, but report error\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Unexpected state - log for debugging\n\t\t\t\t\t\t\t\terrors.push(new Error('Database cleanup in an unknown state'))\n\t\t\t\t\t\t\t\tlogger.warn('Database deletion returned unexpected result state')\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup in an unknown state`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Unexpected exception (shouldn't happen with result object pattern)\n\t\t\t\t\t\t\terrors.push(error instanceof Error ? error : new Error(String(error)))\n\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t`Unexpected database cleanup exception: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Database manager not available or not configured\n\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tmessage: `Database cleanup skipped (not available)`,\n\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// This catch block is for any unexpected errors in the outer logic\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Calculate overall success\n\t\tconst success = errors.length === 0\n\n\t\treturn {\n\t\t\tidentifier: displayIdentifier,\n\t\t\tbranchName: worktree?.branch,\n\t\t\tsuccess,\n\t\t\toperations,\n\t\t\terrors,\n\t\t\trollbackRequired: false, // Cleanup operations are generally not reversible\n\t\t}\n\t}\n\n\t/**\n\t * Terminate dev server on specified port\n\t */\n\tasync terminateDevServer(port: number): Promise<boolean> {\n\t\tlogger.debug(`Checking for dev server on port ${port}`)\n\n\t\tconst processInfo = await this.processManager.detectDevServer(port)\n\n\t\tif (!processInfo) {\n\t\t\tlogger.debug(`No process found on port ${port}`)\n\t\t\treturn false\n\t\t}\n\n\t\tif (!processInfo.isDevServer) {\n\t\t\tlogger.warn(\n\t\t\t\t`Process on port ${port} (${processInfo.name}) doesn't appear to be a dev server, skipping`\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\n\t\tlogger.info(`Terminating dev server: ${processInfo.name} (PID: ${processInfo.pid})`)\n\n\t\tawait this.processManager.terminateProcess(processInfo.pid)\n\n\t\t// Verify termination\n\t\tconst isFree = await this.processManager.verifyPortFree(port)\n\t\tif (!isFree) {\n\t\t\tthrow new Error(`Dev server may still be running on port ${port}`)\n\t\t}\n\n\t\treturn true\n\t}\n\n\t/**\n\t * Delete a Git branch with safety checks\n\t *\n\t * @param branchName - Name of the branch to delete\n\t * @param options - Delete options (force, dryRun)\n\t * @param cwd - Working directory to execute git command from (defaults to finding main worktree)\n\t */\n\tasync deleteBranch(\n\t\tbranchName: string,\n\t\toptions: BranchDeleteOptions = {},\n\t\tcwd?: string\n\t): Promise<boolean> {\n\t\t// Get protected branches list from centralized method\n\t\tconst protectedBranches = await this.settingsManager.getProtectedBranches(cwd)\n\n\t\t// Check for protected branches\n\t\tif (protectedBranches.includes(branchName)) {\n\t\t\tthrow new Error(`Cannot delete protected branch: ${branchName}`)\n\t\t}\n\n\t\tif (options.dryRun) {\n\t\t\tlogger.info(`[DRY RUN] Would delete branch: ${branchName}`)\n\t\t\treturn true\n\t\t}\n\n\t\t// Use GitWorktreeManager's removeWorktree with removeBranch option\n\t\t// Or execute git branch -D directly via executeGitCommand\n\t\tconst { executeGitCommand, findMainWorktreePathWithSettings } = await import('../utils/git.js')\n\n\t\ttry {\n\t\t\t// Use provided cwd, or find main worktree path as fallback\n\t\t\t// This ensures we're not running git commands from a deleted directory\n\t\t\tlet workingDir = cwd ?? await findMainWorktreePathWithSettings(undefined, this.settingsManager)\t\t\t\n\n\t\t\t// Use safe delete (-d) unless force is specified\n\t\t\tconst deleteFlag = options.force ? '-D' : '-d'\n\t\t\tawait executeGitCommand(['branch', deleteFlag, branchName], {\n\t\t\t\tcwd: workingDir\n\t\t\t})\n\n\t\t\tlogger.info(`Branch deleted: ${branchName}`)\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tif (options.force) {\n\t\t\t\tthrow error\n\t\t\t}\n\n\t\t\t// For safe delete failures, check if it's actually an unmerged branch error\n\t\t\t// and provide helpful message only in that case, otherwise show the real error\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\n\t\t\t// Git error for unmerged branch typically contains \"not fully merged\"\n\t\t\tif (errorMessage.includes('not fully merged')) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Cannot delete unmerged branch '${branchName}'. Use --force to delete anyway.`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// For other errors (like branch doesn't exist), show the actual git error\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Cleanup database branch\n\t * Gracefully handles missing DatabaseManager\n\t *\n\t * @deprecated This method is deprecated and should not be used for post-deletion cleanup.\n\t * Use the pre-fetch mechanism in cleanupWorktree() instead.\n\t * This method will fail if called after worktree deletion because\n\t * it attempts to read the .env file which has been deleted.\n\t *\n\t * @param branchName - Name of the branch to delete\n\t * @param worktreePath - Path to worktree (must still exist with .env file)\n\t */\n\tasync cleanupDatabase(branchName: string, worktreePath: string): Promise<boolean> {\n\t\tif (!this.database) {\n\t\t\tlogger.debug('Database manager not available, skipping database cleanup')\n\t\t\treturn false\n\t\t}\n\n\t\ttry {\n\t\t\t// Pre-fetch configuration before deletion\n\t\t\tconst envFilePath = path.join(worktreePath, '.env')\n\t\t\tconst shouldCleanup = await this.database.shouldUseDatabaseBranching(envFilePath)\n\n\t\t\t// Find main worktree path to avoid running commands from potentially deleted directories\n\t\t\tlet cwd: string | undefined\n\t\t\ttry {\n\t\t\t\tconst { findMainWorktreePathWithSettings } = await import('../utils/git.js')\n\t\t\t\tcwd = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\t\t\t} catch (error) {\n\t\t\t\t// If we can't find main worktree, commands will run from current directory\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`Could not find main worktree path, using current directory: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst result = await this.database.deleteBranchIfConfigured(\n\t\t\t\tbranchName,\n\t\t\t\tshouldCleanup,\n\t\t\t\tfalse, // isPreview\n\t\t\t\tcwd\n\t\t\t)\n\n\t\t\t// Only return true if deletion actually occurred\n\t\t\tif (result.deleted) {\n\t\t\t\tlogger.info(`Database branch deleted: ${branchName}`)\n\t\t\t\treturn true\n\t\t\t} else if (result.notFound) {\n\t\t\t\tlogger.debug(`No database branch found for: ${branchName}`)\n\t\t\t\treturn false\n\t\t\t} else if (result.userDeclined) {\n\t\t\t\tlogger.info('Preview database deletion declined by user')\n\t\t\t\treturn false\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.warn(`Database cleanup failed: ${result.error ?? 'Unknown error'}`)\n\t\t\t\treturn false\n\t\t\t} else {\n\t\t\t\t// Unexpected state\n\t\t\t\tlogger.debug('Database deletion returned unexpected result')\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Unexpected exception\n\t\t\tlogger.warn(\n\t\t\t\t`Unexpected database cleanup error: ${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 * Cleanup multiple worktrees\n\t */\n\tasync cleanupMultipleWorktrees(\n\t\tidentifiers: string[],\n\t\toptions: ResourceCleanupOptions = {}\n\t): Promise<CleanupResult[]> {\n\t\tconst results: CleanupResult[] = []\n\n\t\tfor (const identifier of identifiers) {\n\t\t\t// Parse the identifier to get ParsedInput format\n\t\t\tconst parsed = this.parseIdentifier(identifier)\n\t\t\tconst result = await this.cleanupWorktree(parsed, options)\n\t\t\tresults.push(result)\n\t\t}\n\n\t\treturn results\n\t}\n\n\t/**\n\t * Validate worktree safety given a worktree object\n\t * Private method used internally when worktree is already known\n\t */\n\tprivate async validateWorktreeSafety(\n\t\tworktree: GitWorktree,\n\t\tidentifier: string\n\t): Promise<SafetyCheck> {\n\t\tconst warnings: string[] = []\n\t\tconst blockers: string[] = []\n\n\t\t// Check if main worktree\n\t\tconst isMain = await this.gitWorktree.isMainWorktree(worktree)\n\t\tif (isMain) {\n\t\t\tblockers.push(`Cannot cleanup main worktree: \"${worktree.branch}\" @ \"${worktree.path}\"`)\n\t\t}\n\n\t\t// Check for uncommitted changes\n\t\tconst { hasUncommittedChanges } = await import('../utils/git.js')\n\t\tconst hasChanges = await hasUncommittedChanges(worktree.path)\n\t\tif (hasChanges) {\n\t\t\t// Create simple blocker message with actionable guidance\n\t\t\tconst blockerMessage =\n\t\t\t\t`Worktree has uncommitted changes.\\n\\n` +\n\t\t\t\t`Please resolve before cleanup - you have some options:\\n` +\n\t\t\t\t` • Commit changes: cd ${worktree.path} && git commit -am \"message\"\\n` +\n\t\t\t\t` • Stash changes: cd ${worktree.path} && git stash\\n` +\n\t\t\t\t` • Force cleanup: il cleanup ${identifier} --force (WARNING: will discard changes)`\n\n\t\t\tblockers.push(blockerMessage)\n\t\t}\n\n\t\treturn {\n\t\t\tisSafe: blockers.length === 0,\n\t\t\twarnings,\n\t\t\tblockers,\n\t\t}\n\t}\n\n\t/**\n\t * Validate cleanup safety\n\t */\n\tasync validateCleanupSafety(identifier: string): Promise<SafetyCheck> {\n\t\tconst warnings: string[] = []\n\t\tconst blockers: string[] = []\n\n\t\t// Find worktree\n\t\tconst worktrees = await this.gitWorktree.findWorktreesByIdentifier(identifier)\n\n\t\tif (worktrees.length === 0) {\n\t\t\tblockers.push(`No worktree found for: ${identifier}`)\n\t\t\treturn { isSafe: false, warnings, blockers }\n\t\t}\n\n\t\tconst worktree = worktrees[0]\n\t\tif (!worktree) {\n\t\t\tblockers.push(`No worktree found for: ${identifier}`)\n\t\t\treturn { isSafe: false, warnings, blockers }\n\t\t}\n\n\t\t// Delegate to private method that validates the worktree\n\t\treturn await this.validateWorktreeSafety(worktree, identifier)\n\t}\n\n\t/**\n\t * Parse identifier to determine type and extract number\n\t * Helper method for port calculation\n\t */\n\tprivate parseIdentifier(identifier: string): ParsedInput {\n\t\t// Check for issue pattern\n\t\tconst issueMatch = identifier.match(/issue-(\\d+)/)\n\t\tif (issueMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: parseInt(issueMatch[1], 10),\n\t\t\t\toriginalInput: identifier\n\t\t\t}\n\t\t}\n\n\t\t// Check for PR pattern\n\t\tconst prMatch = identifier.match(/(?:pr|PR)[/-](\\d+)/)\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}\n\t\t}\n\n\t\t// Check for numeric identifier\n\t\tconst numericMatch = identifier.match(/^#?(\\d+)$/)\n\t\tif (numericMatch?.[1]) {\n\t\t\t// Assume issue for numeric identifiers\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: parseInt(numericMatch[1], 10),\n\t\t\t\toriginalInput: identifier\n\t\t\t}\n\t\t}\n\n\t\t// Treat as branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: identifier,\n\t\t\toriginalInput: identifier\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/ResourceCleanup.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from './GitWorktreeManager.js'\nimport { DatabaseManager } from './DatabaseManager.js'\nimport { ProcessManager } from './process/ProcessManager.js'\nimport { CLIIsolationManager } from './CLIIsolationManager.js'\nimport { SettingsManager } from './SettingsManager.js'\nimport { logger } from '../utils/logger.js'\nimport type {\n\tResourceCleanupOptions,\n\tCleanupResult,\n\tOperationResult,\n\tSafetyCheck,\n\tBranchDeleteOptions,\n} from '../types/cleanup.js'\nimport type { GitWorktree } from '../types/worktree.js'\nimport type { ParsedInput } from '../commands/start.js'\n\n/**\n * Manages resource cleanup for worktrees\n * Provides shared cleanup functionality for finish and cleanup commands\n */\nexport class ResourceCleanup {\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(\n\t\tprivate gitWorktree: GitWorktreeManager,\n\t\tprivate processManager: ProcessManager,\n\t\tprivate database?: DatabaseManager,\n\t\tprivate cliIsolation?: CLIIsolationManager,\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t}\n\n\t/**\n\t * Cleanup a worktree and associated resources\n\t * Main orchestration method\n\t *\n\t * @param parsed - ParsedInput from IdentifierParser with type information\n\t * @param options - Cleanup options\n\t */\n\tasync cleanupWorktree(\n\t\tparsed: ParsedInput,\n\t\toptions: ResourceCleanupOptions = {}\n\t): Promise<CleanupResult> {\n\t\tconst operations: OperationResult[] = []\n\t\tconst errors: Error[] = []\n\n\t\tconst displayIdentifier = parsed.branchName ?? parsed.number?.toString() ?? parsed.originalInput\n\t\tlogger.info(`Starting cleanup for: ${displayIdentifier}`)\n\n\t\t// Extract number from ParsedInput for port calculation\n\t\tconst number = parsed.number\n\n\t\t// Step 1: Terminate dev server if applicable\n\t\tif (number !== undefined) {\n\t\t\tconst port = this.processManager.calculatePort(number)\n\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would check for dev server on port ${port}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst terminated = await this.terminateDevServer(port)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: terminated\n\t\t\t\t\t\t\t? `Dev server on port ${port} terminated`\n\t\t\t\t\t\t\t: `No dev server running on port ${port}`,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'dev-server',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Failed to terminate dev server`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 2: Find worktree using specific methods based on type\n\t\tlet worktree: GitWorktree | null = null\n\t\ttry {\n\t\t\t// Use specific finding methods based on parsed type for precision\n\t\t\tif (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t\t// For PRs, pass empty string for branchName since we're detecting from path pattern\n\t\t\t\tworktree = await this.gitWorktree.findWorktreeForPR(parsed.number, '')\n\t\t\t} else if (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\t\tworktree = await this.gitWorktree.findWorktreeForIssue(parsed.number)\n\t\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\t\tworktree = await this.gitWorktree.findWorktreeForBranch(parsed.branchName)\n\t\t\t}\n\n\t\t\tif (!worktree) {\n\t\t\t\tthrow new Error(`No worktree found for identifier: ${displayIdentifier}`)\n\t\t\t}\n\n\t\t\tlogger.debug(`Found worktree: path=\"${worktree.path}\", branch=\"${worktree.branch}\"`)\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\terrors.push(err)\n\n\t\t\treturn {\n\t\t\t\tidentifier: displayIdentifier,\n\t\t\t\tsuccess: false,\n\t\t\t\toperations,\n\t\t\t\terrors,\n\t\t\t\trollbackRequired: false,\n\t\t\t}\n\t\t}\n\n\t\t// Step 2.5: Validate safety before proceeding with cleanup (unless force flag is set)\n\t\tif (!options.force) {\n\t\t\tconst safety = await this.validateWorktreeSafety(worktree, parsed.originalInput)\n\n\t\t\tif (!safety.isSafe) {\n\t\t\t\t// Format blocker messages for error output\n\t\t\t\tconst blockerMessage = safety.blockers.join('\\n\\n')\n\t\t\t\tthrow new Error(`Cannot cleanup:\\n\\n${blockerMessage}`)\n\t\t\t}\n\n\t\t\t// Log warnings if any\n\t\t\tif (safety.warnings.length > 0) {\n\t\t\t\tsafety.warnings.forEach(warning => {\n\t\t\t\t\tlogger.warn(warning)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Step 3: Pre-fetch database configuration before worktree removal\n\t\t// This config is used AFTER worktree deletion when env file no longer exists\n\t\tlet databaseConfig: { shouldCleanup: boolean; envFilePath: string } | null = null\n\t\tif (!options.keepDatabase && worktree) {\n\t\t\tconst envFilePath = path.join(worktree.path, '.env')\n\t\t\ttry {\n\t\t\t\t// Pre-check if database cleanup should happen by reading .env file now\n\t\t\t\tconst shouldCleanup = this.database\n\t\t\t\t\t? await this.database.shouldUseDatabaseBranching(envFilePath)\n\t\t\t\t\t: false\n\t\t\t\tdatabaseConfig = { shouldCleanup, envFilePath }\n\t\t\t} catch (error) {\n\t\t\t\t// If we can't read the config, we'll skip database cleanup\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to read database config from ${envFilePath}, skipping database cleanup: ${\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t}`\n\t\t\t\t)\n\t\t\t\tdatabaseConfig = { shouldCleanup: false, envFilePath }\n\t\t\t}\n\t\t}\n\n\t\t// Step 3.5: Find main worktree path before deletion (needed for branch and database operations)\n\t\tlet mainWorktreePath: string | null = null\n\t\tif (!options.dryRun) {\n\t\t\ttry {\n\t\t\t\tconst { findMainWorktreePathWithSettings } = await import('../utils/git.js')\n\t\t\t\tmainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to find main worktree path: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Step 4: Remove worktree\n\t\tif (options.dryRun) {\n\t\t\toperations.push({\n\t\t\t\ttype: 'worktree',\n\t\t\t\tsuccess: true,\n\t\t\t\tmessage: `[DRY RUN] Would remove worktree: ${worktree.path}`,\n\t\t\t})\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst worktreeOptions: { force?: boolean; removeDirectory: true; removeBranch: false } =\n\t\t\t\t\t{\n\t\t\t\t\t\tremoveDirectory: true,\n\t\t\t\t\t\tremoveBranch: false, // Handle branch separately\n\t\t\t\t\t}\n\t\t\t\tif (options.force !== undefined) {\n\t\t\t\t\tworktreeOptions.force = options.force\n\t\t\t\t}\n\t\t\t\tawait this.gitWorktree.removeWorktree(worktree.path, worktreeOptions)\n\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'worktree',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Worktree removed: ${worktree.path}`,\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\terrors.push(err)\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'worktree',\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\tmessage: `Failed to remove worktree`,\n\t\t\t\t\terror: err.message,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Step 5: Delete branch if requested\n\t\tif (options.deleteBranch && worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'branch',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would delete branch: ${worktree.branch}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst branchOptions: BranchDeleteOptions = { dryRun: false }\n\t\t\t\t\tif (options.force !== undefined) {\n\t\t\t\t\t\tbranchOptions.force = options.force\n\t\t\t\t\t}\n\t\t\t\t\t// Pass main worktree path to ensure we can execute git commands\n\t\t\t\t\tawait this.deleteBranch(worktree.branch, branchOptions, mainWorktreePath ?? undefined)\n\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'branch',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: `Branch deleted: ${worktree.branch}`,\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'branch',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Failed to delete branch`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 5.5: Cleanup CLI symlinks if CLI isolation is available\n\t\t// Derive identifier from parsed input (number for issue/PR, branchName for branch)\n\t\tconst cliIdentifier = parsed.number ?? parsed.branchName\n\t\tif (this.cliIsolation && cliIdentifier !== undefined) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would cleanup CLI symlinks for: ${cliIdentifier}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst removed = await this.cliIsolation.cleanupVersionedExecutables(cliIdentifier)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tmessage: removed.length > 0\n\t\t\t\t\t\t\t? `CLI symlinks removed: ${removed.length}`\n\t\t\t\t\t\t\t: 'No CLI symlinks to cleanup',\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log warning but don't fail\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`CLI symlink cleanup failed: ${err.message}`\n\t\t\t\t\t)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'cli-symlinks',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: 'CLI symlink cleanup failed (non-fatal)',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 6: Cleanup database after worktree and branch removal (using pre-read config)\n\t\tif (databaseConfig && worktree) {\n\t\t\tif (options.dryRun) {\n\t\t\t\toperations.push({\n\t\t\t\t\ttype: 'database',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `[DRY RUN] Would cleanup database branch for: ${worktree.branch}`,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tif (databaseConfig.shouldCleanup && this.database) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Call database deletion with pre-fetched shouldCleanup value and main worktree path\n\t\t\t\t\t\t\t// This avoids reading the already-deleted env file and running commands from deleted directories\n\t\t\t\t\t\t\tconst deletionResult = await this.database.deleteBranchIfConfigured(\n\t\t\t\t\t\t\t\tworktree.branch,\n\t\t\t\t\t\t\t\tdatabaseConfig.shouldCleanup,\n\t\t\t\t\t\t\t\tfalse, // isPreview\n\t\t\t\t\t\t\t\tmainWorktreePath ?? undefined\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\t// Create operation result based on what actually happened\n\t\t\t\t\t\t\tif (deletionResult.deleted) {\n\t\t\t\t\t\t\t\t// Branch was actually deleted\n\t\t\t\t\t\t\t\tlogger.info(`Database branch deleted: ${worktree.branch}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `Database branch deleted`,\n\t\t\t\t\t\t\t\t\tdeleted: true,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (deletionResult.notFound) {\n\t\t\t\t\t\t\t\t// Branch didn't exist - not an error, just nothing to delete\n\t\t\t\t\t\t\t\tlogger.debug(`No database branch found for: ${worktree.branch}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `No database branch found (skipped)`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (deletionResult.userDeclined) {\n\t\t\t\t\t\t\t\t// User declined preview database deletion\n\t\t\t\t\t\t\t\tlogger.info('Preview database deletion declined by user')\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup skipped (user declined)`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else if (!deletionResult.success) {\n\t\t\t\t\t\t\t\t// Deletion failed with error\n\t\t\t\t\t\t\t\tconst errorMsg = deletionResult.error ?? 'Unknown error'\n\t\t\t\t\t\t\t\terrors.push(new Error(errorMsg))\n\t\t\t\t\t\t\t\tlogger.warn(`Database cleanup failed: ${errorMsg}`)\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: false, // Non-fatal, but report error\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\t\t\t\terror: errorMsg,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Unexpected state - log for debugging\n\t\t\t\t\t\t\t\terrors.push(new Error('Database cleanup in an unknown state'))\n\t\t\t\t\t\t\t\tlogger.warn('Database deletion returned unexpected result state')\n\t\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\t\tmessage: `Database cleanup in an unknown state`,\n\t\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Unexpected exception (shouldn't happen with result object pattern)\n\t\t\t\t\t\t\terrors.push(error instanceof Error ? error : new Error(String(error)))\n\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t`Unexpected database cleanup exception: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Database manager not available or not configured\n\t\t\t\t\t\toperations.push({\n\t\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\tmessage: `Database cleanup skipped (not available)`,\n\t\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// This catch block is for any unexpected errors in the outer logic\n\t\t\t\t\tconst err = error instanceof Error ? error : new Error('Unknown error')\n\t\t\t\t\terrors.push(err)\n\t\t\t\t\toperations.push({\n\t\t\t\t\t\ttype: 'database',\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\tmessage: `Database cleanup failed`,\n\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Calculate overall success\n\t\tconst success = errors.length === 0\n\n\t\treturn {\n\t\t\tidentifier: displayIdentifier,\n\t\t\tbranchName: worktree?.branch,\n\t\t\tsuccess,\n\t\t\toperations,\n\t\t\terrors,\n\t\t\trollbackRequired: false, // Cleanup operations are generally not reversible\n\t\t}\n\t}\n\n\t/**\n\t * Terminate dev server on specified port\n\t */\n\tasync terminateDevServer(port: number): Promise<boolean> {\n\t\tlogger.debug(`Checking for dev server on port ${port}`)\n\n\t\tconst processInfo = await this.processManager.detectDevServer(port)\n\n\t\tif (!processInfo) {\n\t\t\tlogger.debug(`No process found on port ${port}`)\n\t\t\treturn false\n\t\t}\n\n\t\tif (!processInfo.isDevServer) {\n\t\t\tlogger.warn(\n\t\t\t\t`Process on port ${port} (${processInfo.name}) doesn't appear to be a dev server, skipping`\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\n\t\tlogger.info(`Terminating dev server: ${processInfo.name} (PID: ${processInfo.pid})`)\n\n\t\tawait this.processManager.terminateProcess(processInfo.pid)\n\n\t\t// Verify termination\n\t\tconst isFree = await this.processManager.verifyPortFree(port)\n\t\tif (!isFree) {\n\t\t\tthrow new Error(`Dev server may still be running on port ${port}`)\n\t\t}\n\n\t\treturn true\n\t}\n\n\t/**\n\t * Delete a Git branch with safety checks\n\t *\n\t * @param branchName - Name of the branch to delete\n\t * @param options - Delete options (force, dryRun)\n\t * @param cwd - Working directory to execute git command from (defaults to finding main worktree)\n\t */\n\tasync deleteBranch(\n\t\tbranchName: string,\n\t\toptions: BranchDeleteOptions = {},\n\t\tcwd?: string\n\t): Promise<boolean> {\n\t\t// Get protected branches list from centralized method\n\t\tconst protectedBranches = await this.settingsManager.getProtectedBranches(cwd)\n\n\t\t// Check for protected branches\n\t\tif (protectedBranches.includes(branchName)) {\n\t\t\tthrow new Error(`Cannot delete protected branch: ${branchName}`)\n\t\t}\n\n\t\tif (options.dryRun) {\n\t\t\tlogger.info(`[DRY RUN] Would delete branch: ${branchName}`)\n\t\t\treturn true\n\t\t}\n\n\t\t// Use GitWorktreeManager's removeWorktree with removeBranch option\n\t\t// Or execute git branch -D directly via executeGitCommand\n\t\tconst { executeGitCommand, findMainWorktreePathWithSettings } = await import('../utils/git.js')\n\n\t\ttry {\n\t\t\t// Use provided cwd, or find main worktree path as fallback\n\t\t\t// This ensures we're not running git commands from a deleted directory\n\t\t\tlet workingDir = cwd ?? await findMainWorktreePathWithSettings(undefined, this.settingsManager)\t\t\t\n\n\t\t\t// Use safe delete (-d) unless force is specified\n\t\t\tconst deleteFlag = options.force ? '-D' : '-d'\n\t\t\tawait executeGitCommand(['branch', deleteFlag, branchName], {\n\t\t\t\tcwd: workingDir\n\t\t\t})\n\n\t\t\tlogger.info(`Branch deleted: ${branchName}`)\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tif (options.force) {\n\t\t\t\tthrow error\n\t\t\t}\n\n\t\t\t// For safe delete failures, check if it's actually an unmerged branch error\n\t\t\t// and provide helpful message only in that case, otherwise show the real error\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error)\n\n\t\t\t// Git error for unmerged branch typically contains \"not fully merged\"\n\t\t\tif (errorMessage.includes('not fully merged')) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Cannot delete unmerged branch '${branchName}'. Use --force to delete anyway.`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// For other errors (like branch doesn't exist), show the actual git error\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Cleanup database branch\n\t * Gracefully handles missing DatabaseManager\n\t *\n\t * @deprecated This method is deprecated and should not be used for post-deletion cleanup.\n\t * Use the pre-fetch mechanism in cleanupWorktree() instead.\n\t * This method will fail if called after worktree deletion because\n\t * it attempts to read the .env file which has been deleted.\n\t *\n\t * @param branchName - Name of the branch to delete\n\t * @param worktreePath - Path to worktree (must still exist with .env file)\n\t */\n\tasync cleanupDatabase(branchName: string, worktreePath: string): Promise<boolean> {\n\t\tif (!this.database) {\n\t\t\tlogger.debug('Database manager not available, skipping database cleanup')\n\t\t\treturn false\n\t\t}\n\n\t\ttry {\n\t\t\t// Pre-fetch configuration before deletion\n\t\t\tconst envFilePath = path.join(worktreePath, '.env')\n\t\t\tconst shouldCleanup = await this.database.shouldUseDatabaseBranching(envFilePath)\n\n\t\t\t// Find main worktree path to avoid running commands from potentially deleted directories\n\t\t\tlet cwd: string | undefined\n\t\t\ttry {\n\t\t\t\tconst { findMainWorktreePathWithSettings } = await import('../utils/git.js')\n\t\t\t\tcwd = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager)\n\t\t\t} catch (error) {\n\t\t\t\t// If we can't find main worktree, commands will run from current directory\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`Could not find main worktree path, using current directory: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tconst result = await this.database.deleteBranchIfConfigured(\n\t\t\t\tbranchName,\n\t\t\t\tshouldCleanup,\n\t\t\t\tfalse, // isPreview\n\t\t\t\tcwd\n\t\t\t)\n\n\t\t\t// Only return true if deletion actually occurred\n\t\t\tif (result.deleted) {\n\t\t\t\tlogger.info(`Database branch deleted: ${branchName}`)\n\t\t\t\treturn true\n\t\t\t} else if (result.notFound) {\n\t\t\t\tlogger.debug(`No database branch found for: ${branchName}`)\n\t\t\t\treturn false\n\t\t\t} else if (result.userDeclined) {\n\t\t\t\tlogger.info('Preview database deletion declined by user')\n\t\t\t\treturn false\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.warn(`Database cleanup failed: ${result.error ?? 'Unknown error'}`)\n\t\t\t\treturn false\n\t\t\t} else {\n\t\t\t\t// Unexpected state\n\t\t\t\tlogger.debug('Database deletion returned unexpected result')\n\t\t\t\treturn false\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Unexpected exception\n\t\t\tlogger.warn(\n\t\t\t\t`Unexpected database cleanup error: ${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 * Cleanup multiple worktrees\n\t */\n\tasync cleanupMultipleWorktrees(\n\t\tidentifiers: string[],\n\t\toptions: ResourceCleanupOptions = {}\n\t): Promise<CleanupResult[]> {\n\t\tconst results: CleanupResult[] = []\n\n\t\tfor (const identifier of identifiers) {\n\t\t\t// Parse the identifier to get ParsedInput format\n\t\t\tconst parsed = this.parseIdentifier(identifier)\n\t\t\tconst result = await this.cleanupWorktree(parsed, options)\n\t\t\tresults.push(result)\n\t\t}\n\n\t\treturn results\n\t}\n\n\t/**\n\t * Validate worktree safety given a worktree object\n\t * Private method used internally when worktree is already known\n\t */\n\tprivate async validateWorktreeSafety(\n\t\tworktree: GitWorktree,\n\t\tidentifier: string\n\t): Promise<SafetyCheck> {\n\t\tconst warnings: string[] = []\n\t\tconst blockers: string[] = []\n\n\t\t// Check if main worktree\n\t\tconst isMain = await this.gitWorktree.isMainWorktree(worktree, this.settingsManager)\n\t\tif (isMain) {\n\t\t\tblockers.push(`Cannot cleanup main worktree: \"${worktree.branch}\" @ \"${worktree.path}\"`)\n\t\t}\n\n\t\t// Check for uncommitted changes\n\t\tconst { hasUncommittedChanges } = await import('../utils/git.js')\n\t\tconst hasChanges = await hasUncommittedChanges(worktree.path)\n\t\tif (hasChanges) {\n\t\t\t// Create simple blocker message with actionable guidance\n\t\t\tconst blockerMessage =\n\t\t\t\t`Worktree has uncommitted changes.\\n\\n` +\n\t\t\t\t`Please resolve before cleanup - you have some options:\\n` +\n\t\t\t\t` • Commit changes: cd ${worktree.path} && git commit -am \"message\"\\n` +\n\t\t\t\t` • Stash changes: cd ${worktree.path} && git stash\\n` +\n\t\t\t\t` • Force cleanup: il cleanup ${identifier} --force (WARNING: will discard changes)`\n\n\t\t\tblockers.push(blockerMessage)\n\t\t}\n\n\t\treturn {\n\t\t\tisSafe: blockers.length === 0,\n\t\t\twarnings,\n\t\t\tblockers,\n\t\t}\n\t}\n\n\t/**\n\t * Validate cleanup safety\n\t */\n\tasync validateCleanupSafety(identifier: string): Promise<SafetyCheck> {\n\t\tconst warnings: string[] = []\n\t\tconst blockers: string[] = []\n\n\t\t// Find worktree\n\t\tconst worktrees = await this.gitWorktree.findWorktreesByIdentifier(identifier)\n\n\t\tif (worktrees.length === 0) {\n\t\t\tblockers.push(`No worktree found for: ${identifier}`)\n\t\t\treturn { isSafe: false, warnings, blockers }\n\t\t}\n\n\t\tconst worktree = worktrees[0]\n\t\tif (!worktree) {\n\t\t\tblockers.push(`No worktree found for: ${identifier}`)\n\t\t\treturn { isSafe: false, warnings, blockers }\n\t\t}\n\n\t\t// Delegate to private method that validates the worktree\n\t\treturn await this.validateWorktreeSafety(worktree, identifier)\n\t}\n\n\t/**\n\t * Parse identifier to determine type and extract number\n\t * Helper method for port calculation\n\t */\n\tprivate parseIdentifier(identifier: string): ParsedInput {\n\t\t// Check for issue pattern\n\t\tconst issueMatch = identifier.match(/issue-(\\d+)/)\n\t\tif (issueMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: parseInt(issueMatch[1], 10),\n\t\t\t\toriginalInput: identifier\n\t\t\t}\n\t\t}\n\n\t\t// Check for PR pattern\n\t\tconst prMatch = identifier.match(/(?:pr|PR)[/-](\\d+)/)\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}\n\t\t}\n\n\t\t// Check for numeric identifier\n\t\tconst numericMatch = identifier.match(/^#?(\\d+)$/)\n\t\tif (numericMatch?.[1]) {\n\t\t\t// Assume issue for numeric identifiers\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: parseInt(numericMatch[1], 10),\n\t\t\t\toriginalInput: identifier\n\t\t\t}\n\t\t}\n\n\t\t// Treat as branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: identifier,\n\t\t\toriginalInput: identifier\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
buildDevServerCommand
|
|
4
|
-
} from "./chunk-GZP4UGGM.js";
|
|
5
2
|
import {
|
|
6
3
|
ProcessManager
|
|
7
4
|
} from "./chunk-SPYPLHMK.js";
|
|
5
|
+
import {
|
|
6
|
+
buildDevServerCommand
|
|
7
|
+
} from "./chunk-GZP4UGGM.js";
|
|
8
8
|
import {
|
|
9
9
|
logger
|
|
10
10
|
} from "./chunk-GEHQXLEI.js";
|
|
@@ -121,4 +121,4 @@ var DevServerManager = class {
|
|
|
121
121
|
export {
|
|
122
122
|
DevServerManager
|
|
123
123
|
};
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-USVVV3FP.js.map
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getRepoInfo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JXQXSC45.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-GEHQXLEI.js";
|
|
8
8
|
|
|
9
9
|
// src/utils/mcp.ts
|
|
10
10
|
import path from "path";
|
|
11
|
-
async function generateGitHubCommentMcpConfig(contextType) {
|
|
12
|
-
|
|
11
|
+
async function generateGitHubCommentMcpConfig(contextType, repo) {
|
|
12
|
+
let owner;
|
|
13
|
+
let name;
|
|
14
|
+
if (repo) {
|
|
15
|
+
const parts = repo.split("/");
|
|
16
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
17
|
+
throw new Error(`Invalid repo format: ${repo}. Expected "owner/repo"`);
|
|
18
|
+
}
|
|
19
|
+
owner = parts[0];
|
|
20
|
+
name = parts[1];
|
|
21
|
+
} else {
|
|
22
|
+
const repoInfo = await getRepoInfo();
|
|
23
|
+
owner = repoInfo.owner;
|
|
24
|
+
name = repoInfo.name;
|
|
25
|
+
}
|
|
13
26
|
const githubEventName = contextType === "issue" ? "issues" : contextType === "pr" ? "pull_request" : void 0;
|
|
14
27
|
const mcpServerConfig = {
|
|
15
28
|
mcpServers: {
|
|
@@ -18,8 +31,8 @@ async function generateGitHubCommentMcpConfig(contextType) {
|
|
|
18
31
|
command: "node",
|
|
19
32
|
args: [path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), "../dist/mcp/github-comment-server.js")],
|
|
20
33
|
env: {
|
|
21
|
-
REPO_OWNER:
|
|
22
|
-
REPO_NAME:
|
|
34
|
+
REPO_OWNER: owner,
|
|
35
|
+
REPO_NAME: name,
|
|
23
36
|
GITHUB_API_URL: "https://api.github.com/",
|
|
24
37
|
...githubEventName && { GITHUB_EVENT_NAME: githubEventName }
|
|
25
38
|
}
|
|
@@ -27,8 +40,8 @@ async function generateGitHubCommentMcpConfig(contextType) {
|
|
|
27
40
|
}
|
|
28
41
|
};
|
|
29
42
|
logger.debug("Generated MCP config for GitHub comment broker", {
|
|
30
|
-
repoOwner:
|
|
31
|
-
repoName:
|
|
43
|
+
repoOwner: owner,
|
|
44
|
+
repoName: name,
|
|
32
45
|
contextType: contextType ?? "auto-detect",
|
|
33
46
|
githubEventName: githubEventName ?? "auto-detect"
|
|
34
47
|
});
|
|
@@ -38,4 +51,4 @@ async function generateGitHubCommentMcpConfig(contextType) {
|
|
|
38
51
|
export {
|
|
39
52
|
generateGitHubCommentMcpConfig
|
|
40
53
|
};
|
|
41
|
-
//# sourceMappingURL=chunk-
|
|
54
|
+
//# sourceMappingURL=chunk-VCMMAFXQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/mcp.ts"],"sourcesContent":["import path from 'path'\nimport { getRepoInfo } from './github.js'\nimport { logger } from './logger.js'\n\n/**\n * Generate MCP configuration for GitHub comment broker\n * Uses a single server that can handle both issues and pull requests\n * Returns array of MCP server config objects\n * @param contextType - Optional context type (issue or pr)\n * @param repo - Optional repo in \"owner/repo\" format. If not provided, will auto-detect from git.\n */\nexport async function generateGitHubCommentMcpConfig(\n\tcontextType?: 'issue' | 'pr',\n\trepo?: string\n): Promise<Record<string, unknown>[]> {\n\t// Get repository information - either from provided repo string or auto-detect\n\tlet owner: string\n\tlet name: string\n\n\tif (repo) {\n\t\tconst parts = repo.split('/')\n\t\tif (parts.length !== 2 || !parts[0] || !parts[1]) {\n\t\t\tthrow new Error(`Invalid repo format: ${repo}. Expected \"owner/repo\"`)\n\t\t}\n\t\towner = parts[0]\n\t\tname = parts[1]\n\t} else {\n\t\tconst repoInfo = await getRepoInfo()\n\t\towner = repoInfo.owner\n\t\tname = repoInfo.name\n\t}\n\n\t// Map logical types to GitHub's webhook event names (handle GitHub's naming quirk here)\n\tconst githubEventName = contextType === 'issue' ? 'issues' : contextType === 'pr' ? 'pull_request' : undefined\n\n\t// Generate single MCP server config\n\tconst mcpServerConfig = {\n\t\tmcpServers: {\n\t\t\tgithub_comment: {\n\t\t\t\ttransport: 'stdio',\n\t\t\t\tcommand: 'node',\n\t\t\t\targs: [path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), '../dist/mcp/github-comment-server.js')],\n\t\t\t\tenv: {\n\t\t\t\t\tREPO_OWNER: owner,\n\t\t\t\t\tREPO_NAME: name,\n\t\t\t\t\tGITHUB_API_URL: 'https://api.github.com/',\n\t\t\t\t\t...(githubEventName && { GITHUB_EVENT_NAME: githubEventName }),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tlogger.debug('Generated MCP config for GitHub comment broker', {\n\t\trepoOwner: owner,\n\t\trepoName: name,\n\t\tcontextType: contextType ?? 'auto-detect',\n\t\tgithubEventName: githubEventName ?? 'auto-detect'\n\t})\n\n\treturn [mcpServerConfig]\n}"],"mappings":";;;;;;;;;AAAA,OAAO,UAAU;AAWjB,eAAsB,+BACrB,aACA,MACqC;AAErC,MAAI;AACJ,MAAI;AAEJ,MAAI,MAAM;AACT,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAI,MAAM,WAAW,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;AACjD,YAAM,IAAI,MAAM,wBAAwB,IAAI,yBAAyB;AAAA,IACtE;AACA,YAAQ,MAAM,CAAC;AACf,WAAO,MAAM,CAAC;AAAA,EACf,OAAO;AACN,UAAM,WAAW,MAAM,YAAY;AACnC,YAAQ,SAAS;AACjB,WAAO,SAAS;AAAA,EACjB;AAGA,QAAM,kBAAkB,gBAAgB,UAAU,WAAW,gBAAgB,OAAO,iBAAiB;AAGrG,QAAM,kBAAkB;AAAA,IACvB,YAAY;AAAA,MACX,gBAAgB;AAAA,QACf,WAAW;AAAA,QACX,SAAS;AAAA,QACT,MAAM,CAAC,KAAK,KAAK,KAAK,QAAQ,IAAI,WAAW,IAAI,YAAY,GAAG,EAAE,QAAQ,GAAG,sCAAsC,CAAC;AAAA,QACpH,KAAK;AAAA,UACJ,YAAY;AAAA,UACZ,WAAW;AAAA,UACX,gBAAgB;AAAA,UAChB,GAAI,mBAAmB,EAAE,mBAAmB,gBAAgB;AAAA,QAC7D;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAEA,SAAO,MAAM,kDAAkD;AAAA,IAC9D,WAAW;AAAA,IACX,UAAU;AAAA,IACV,aAAa,eAAe;AAAA,IAC5B,iBAAiB,mBAAmB;AAAA,EACrC,CAAC;AAED,SAAO,CAAC,eAAe;AACxB;","names":[]}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
detectClaudeCli,
|
|
4
|
+
launchClaude
|
|
5
|
+
} from "./chunk-PXZBAC2M.js";
|
|
6
|
+
import {
|
|
7
|
+
SettingsManager
|
|
8
|
+
} from "./chunk-VYQLLHZ7.js";
|
|
9
|
+
import {
|
|
10
|
+
executeGitCommand,
|
|
11
|
+
findMainWorktreePathWithSettings
|
|
12
|
+
} from "./chunk-KOCQAD2E.js";
|
|
13
|
+
import {
|
|
14
|
+
logger
|
|
15
|
+
} from "./chunk-GEHQXLEI.js";
|
|
16
|
+
|
|
17
|
+
// src/lib/MergeManager.ts
|
|
18
|
+
var MergeManager = class {
|
|
19
|
+
constructor(settingsManager) {
|
|
20
|
+
this.settingsManager = settingsManager ?? new SettingsManager();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get the main branch name from settings (defaults to 'main')
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
async getMainBranch() {
|
|
27
|
+
const settings = await this.settingsManager.loadSettings();
|
|
28
|
+
return settings.mainBranch ?? "main";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Rebase current branch on main with fail-fast on conflicts
|
|
32
|
+
* Ports bash/merge-and-clean.sh lines 781-913
|
|
33
|
+
*
|
|
34
|
+
* @param worktreePath - Path to the worktree
|
|
35
|
+
* @param options - Merge options (dryRun, force)
|
|
36
|
+
* @throws Error if main branch doesn't exist, uncommitted changes exist, or conflicts occur
|
|
37
|
+
*/
|
|
38
|
+
async rebaseOnMain(worktreePath, options = {}) {
|
|
39
|
+
const { dryRun = false, force = false } = options;
|
|
40
|
+
const mainBranch = await this.getMainBranch();
|
|
41
|
+
logger.info(`Starting rebase on ${mainBranch} branch...`);
|
|
42
|
+
try {
|
|
43
|
+
await executeGitCommand(["show-ref", "--verify", "--quiet", `refs/heads/${mainBranch}`], {
|
|
44
|
+
cwd: worktreePath
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Main branch "${mainBranch}" does not exist. Cannot rebase.
|
|
49
|
+
Ensure the repository has a "${mainBranch}" branch or create it first.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const statusOutput = await executeGitCommand(["status", "--porcelain"], {
|
|
53
|
+
cwd: worktreePath
|
|
54
|
+
});
|
|
55
|
+
if (statusOutput.trim()) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"Uncommitted changes detected. Please commit or stash changes before rebasing.\nRun: git status to see uncommitted changes\nOr: il finish will automatically commit them for you"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const mergeBase = await executeGitCommand(["merge-base", mainBranch, "HEAD"], {
|
|
61
|
+
cwd: worktreePath
|
|
62
|
+
});
|
|
63
|
+
const mainHead = await executeGitCommand(["rev-parse", mainBranch], {
|
|
64
|
+
cwd: worktreePath
|
|
65
|
+
});
|
|
66
|
+
const mergeBaseTrimmed = mergeBase.trim();
|
|
67
|
+
const mainHeadTrimmed = mainHead.trim();
|
|
68
|
+
if (mergeBaseTrimmed === mainHeadTrimmed) {
|
|
69
|
+
logger.success(`Branch is already up to date with ${mainBranch}. No rebase needed.`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const commitsOutput = await executeGitCommand(["log", "--oneline", `${mainBranch}..HEAD`], {
|
|
73
|
+
cwd: worktreePath
|
|
74
|
+
});
|
|
75
|
+
const commits = commitsOutput.trim();
|
|
76
|
+
const commitLines = commits ? commits.split("\n") : [];
|
|
77
|
+
if (commits) {
|
|
78
|
+
logger.info(`Found ${commitLines.length} commit(s) to rebase:`);
|
|
79
|
+
commitLines.forEach((commit) => logger.info(` ${commit}`));
|
|
80
|
+
} else {
|
|
81
|
+
logger.info(`${mainBranch} branch has moved forward. Rebasing to update branch...`);
|
|
82
|
+
}
|
|
83
|
+
if (!force && !dryRun) {
|
|
84
|
+
logger.info("Proceeding with rebase... (use --force to skip confirmations)");
|
|
85
|
+
}
|
|
86
|
+
if (dryRun) {
|
|
87
|
+
logger.info(`[DRY RUN] Would execute: git rebase ${mainBranch}`);
|
|
88
|
+
if (commitLines.length > 0) {
|
|
89
|
+
logger.info(`[DRY RUN] This would rebase ${commitLines.length} commit(s)`);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
await executeGitCommand(["rebase", mainBranch], { cwd: worktreePath });
|
|
95
|
+
logger.success("Rebase completed successfully!");
|
|
96
|
+
} catch (error) {
|
|
97
|
+
const conflictedFiles = await this.detectConflictedFiles(worktreePath);
|
|
98
|
+
if (conflictedFiles.length > 0) {
|
|
99
|
+
logger.info("Merge conflicts detected, attempting Claude-assisted resolution...");
|
|
100
|
+
const resolved = await this.attemptClaudeConflictResolution(
|
|
101
|
+
worktreePath,
|
|
102
|
+
conflictedFiles
|
|
103
|
+
);
|
|
104
|
+
if (resolved) {
|
|
105
|
+
logger.success("Conflicts resolved with Claude assistance, rebase completed");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const conflictError = this.formatConflictError(conflictedFiles);
|
|
109
|
+
throw new Error(conflictError);
|
|
110
|
+
}
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Rebase failed: ${error instanceof Error ? error.message : String(error)}
|
|
113
|
+
Run: git status for more details
|
|
114
|
+
Or: git rebase --abort to cancel the rebase`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate that fast-forward merge is possible
|
|
120
|
+
* Ports bash/merge-and-clean.sh lines 957-968
|
|
121
|
+
*
|
|
122
|
+
* @param branchName - Name of the branch to merge
|
|
123
|
+
* @param mainWorktreePath - Path where main branch is checked out
|
|
124
|
+
* @throws Error if fast-forward is not possible
|
|
125
|
+
*/
|
|
126
|
+
async validateFastForwardPossible(branchName, mainWorktreePath) {
|
|
127
|
+
const mainBranch = await this.getMainBranch();
|
|
128
|
+
const mergeBase = await executeGitCommand(["merge-base", mainBranch, branchName], {
|
|
129
|
+
cwd: mainWorktreePath
|
|
130
|
+
});
|
|
131
|
+
const mainHead = await executeGitCommand(["rev-parse", mainBranch], {
|
|
132
|
+
cwd: mainWorktreePath
|
|
133
|
+
});
|
|
134
|
+
const mergeBaseTrimmed = mergeBase.trim();
|
|
135
|
+
const mainHeadTrimmed = mainHead.trim();
|
|
136
|
+
if (mergeBaseTrimmed !== mainHeadTrimmed) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Cannot perform fast-forward merge.
|
|
139
|
+
The ${mainBranch} branch has moved forward since this branch was created.
|
|
140
|
+
Merge base: ${mergeBaseTrimmed}
|
|
141
|
+
Main HEAD: ${mainHeadTrimmed}
|
|
142
|
+
|
|
143
|
+
To fix this:
|
|
144
|
+
1. Rebase the branch on ${mainBranch}: git rebase ${mainBranch}
|
|
145
|
+
2. Or use: il finish to automatically rebase and merge
|
|
146
|
+
`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Perform fast-forward only merge
|
|
152
|
+
* Ports bash/merge-and-clean.sh lines 938-994
|
|
153
|
+
*
|
|
154
|
+
* @param branchName - Name of the branch to merge
|
|
155
|
+
* @param worktreePath - Path to the worktree
|
|
156
|
+
* @param options - Merge options (dryRun, force)
|
|
157
|
+
* @throws Error if checkout, validation, or merge fails
|
|
158
|
+
*/
|
|
159
|
+
async performFastForwardMerge(branchName, worktreePath, options = {}) {
|
|
160
|
+
const { dryRun = false, force = false } = options;
|
|
161
|
+
const mainBranch = await this.getMainBranch();
|
|
162
|
+
logger.info("Starting fast-forward merge...");
|
|
163
|
+
const mainWorktreePath = options.repoRoot ?? await findMainWorktreePathWithSettings(worktreePath, this.settingsManager);
|
|
164
|
+
logger.debug(`Using ${mainBranch} branch location: ${mainWorktreePath}`);
|
|
165
|
+
const currentBranch = await executeGitCommand(["branch", "--show-current"], {
|
|
166
|
+
cwd: mainWorktreePath
|
|
167
|
+
});
|
|
168
|
+
if (currentBranch.trim() !== mainBranch) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`Expected ${mainBranch} branch but found: ${currentBranch.trim()}
|
|
171
|
+
At location: ${mainWorktreePath}
|
|
172
|
+
This indicates the main worktree detection failed.`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
await this.validateFastForwardPossible(branchName, mainWorktreePath);
|
|
176
|
+
const commitsOutput = await executeGitCommand(["log", "--oneline", `${mainBranch}..${branchName}`], {
|
|
177
|
+
cwd: mainWorktreePath
|
|
178
|
+
});
|
|
179
|
+
const commits = commitsOutput.trim();
|
|
180
|
+
if (!commits) {
|
|
181
|
+
logger.success(`Branch is already merged into ${mainBranch}. No merge needed.`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const commitLines = commits.split("\n");
|
|
185
|
+
logger.info(`Found ${commitLines.length} commit(s) to merge:`);
|
|
186
|
+
commitLines.forEach((commit) => logger.info(` ${commit}`));
|
|
187
|
+
if (!force && !dryRun) {
|
|
188
|
+
logger.info("Proceeding with fast-forward merge... (use --force to skip confirmations)");
|
|
189
|
+
}
|
|
190
|
+
if (dryRun) {
|
|
191
|
+
logger.info(`[DRY RUN] Would execute: git merge --ff-only ${branchName}`);
|
|
192
|
+
logger.info(`[DRY RUN] This would merge ${commitLines.length} commit(s)`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
await executeGitCommand(["merge", "--ff-only", branchName], { cwd: mainWorktreePath });
|
|
197
|
+
logger.success(`Fast-forward merge completed! Merged ${commitLines.length} commit(s).`);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`Fast-forward merge failed: ${error instanceof Error ? error.message : String(error)}
|
|
201
|
+
|
|
202
|
+
To recover:
|
|
203
|
+
1. Check merge status: git status
|
|
204
|
+
2. Abort merge if needed: git merge --abort
|
|
205
|
+
3. Verify branch is rebased: git rebase main
|
|
206
|
+
4. Try merge again: il finish`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Helper: Detect conflicted files after failed rebase
|
|
212
|
+
* @private
|
|
213
|
+
*/
|
|
214
|
+
async detectConflictedFiles(worktreePath) {
|
|
215
|
+
try {
|
|
216
|
+
const output = await executeGitCommand(["diff", "--name-only", "--diff-filter=U"], {
|
|
217
|
+
cwd: worktreePath
|
|
218
|
+
});
|
|
219
|
+
return output.trim().split("\n").filter((file) => file.length > 0);
|
|
220
|
+
} catch {
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Helper: Format conflict error message with manual resolution steps
|
|
226
|
+
* @private
|
|
227
|
+
*/
|
|
228
|
+
formatConflictError(conflictedFiles) {
|
|
229
|
+
const fileList = conflictedFiles.map((file) => ` \u2022 ${file}`).join("\n");
|
|
230
|
+
return "Rebase failed - merge conflicts detected in:\n" + fileList + "\n\nTo resolve manually:\n 1. Fix conflicts in the files above\n 2. Stage resolved files: git add <files>\n 3. Continue rebase: git rebase --continue\n 4. Or abort rebase: git rebase --abort\n 5. Then re-run: il finish <issue-number>";
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Attempt to resolve conflicts using Claude
|
|
234
|
+
* Ports bash/merge-and-clean.sh lines 839-894
|
|
235
|
+
*
|
|
236
|
+
* @param worktreePath - Path to the worktree
|
|
237
|
+
* @param conflictedFiles - List of files with conflicts
|
|
238
|
+
* @returns true if conflicts resolved, false otherwise
|
|
239
|
+
* @private
|
|
240
|
+
*/
|
|
241
|
+
async attemptClaudeConflictResolution(worktreePath, conflictedFiles) {
|
|
242
|
+
const isClaudeAvailable = await detectClaudeCli();
|
|
243
|
+
if (!isClaudeAvailable) {
|
|
244
|
+
logger.debug("Claude CLI not available, skipping conflict resolution");
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
logger.info(`Launching Claude to resolve conflicts in ${conflictedFiles.length} file(s)...`);
|
|
248
|
+
const prompt = `Please help resolve the git rebase conflicts in this repository. Analyze the conflicted files, understand the changes from both branches, fix the conflicts, then run 'git add .' to stage the resolved files, and finally run 'git rebase --continue' to continue the rebase process. Handle the entire workflow for me.`;
|
|
249
|
+
try {
|
|
250
|
+
await launchClaude(prompt, {
|
|
251
|
+
addDir: worktreePath,
|
|
252
|
+
headless: false
|
|
253
|
+
// Interactive - runs in current terminal with stdio: inherit
|
|
254
|
+
});
|
|
255
|
+
const remainingConflicts = await this.detectConflictedFiles(worktreePath);
|
|
256
|
+
if (remainingConflicts.length > 0) {
|
|
257
|
+
logger.warn(
|
|
258
|
+
`Conflicts still exist in ${remainingConflicts.length} file(s) after Claude assistance`
|
|
259
|
+
);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
const rebaseInProgress = await this.isRebaseInProgress(worktreePath);
|
|
263
|
+
if (rebaseInProgress) {
|
|
264
|
+
logger.warn("Rebase still in progress after Claude assistance");
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
logger.success("Claude successfully resolved conflicts and completed rebase");
|
|
268
|
+
return true;
|
|
269
|
+
} catch (error) {
|
|
270
|
+
logger.warn("Claude conflict resolution failed", {
|
|
271
|
+
error: error instanceof Error ? error.message : String(error)
|
|
272
|
+
});
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Check if a git rebase is currently in progress
|
|
278
|
+
* Checks for .git/rebase-merge or .git/rebase-apply directories
|
|
279
|
+
* Ports bash script logic from lines 853-856
|
|
280
|
+
*
|
|
281
|
+
* @param worktreePath - Path to the worktree
|
|
282
|
+
* @returns true if rebase in progress, false otherwise
|
|
283
|
+
* @private
|
|
284
|
+
*/
|
|
285
|
+
async isRebaseInProgress(worktreePath) {
|
|
286
|
+
const fs = await import("fs/promises");
|
|
287
|
+
const path = await import("path");
|
|
288
|
+
const rebaseMergePath = path.join(worktreePath, ".git", "rebase-merge");
|
|
289
|
+
const rebaseApplyPath = path.join(worktreePath, ".git", "rebase-apply");
|
|
290
|
+
try {
|
|
291
|
+
await fs.access(rebaseMergePath);
|
|
292
|
+
return true;
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
await fs.access(rebaseApplyPath);
|
|
297
|
+
return true;
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export {
|
|
305
|
+
MergeManager
|
|
306
|
+
};
|
|
307
|
+
//# sourceMappingURL=chunk-VVH3ANF2.js.map
|