@iloom/cli 0.3.4 → 0.4.0

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.
Files changed (178) hide show
  1. package/README.md +13 -3
  2. package/dist/{BranchNamingService-A77VI6AI.js → BranchNamingService-TOM2KAUT.js} +4 -3
  3. package/dist/ClaudeContextManager-VEGJTS5E.js +16 -0
  4. package/dist/ClaudeService-ICSHJMQ5.js +15 -0
  5. package/dist/GitHubService-RPM27GWD.js +12 -0
  6. package/dist/{LoomLauncher-ZV3ZZIBA.js → LoomLauncher-SJBZFZXE.js} +25 -22
  7. package/dist/LoomLauncher-SJBZFZXE.js.map +1 -0
  8. package/dist/PromptTemplateManager-2TDZAUC6.js +9 -0
  9. package/dist/README.md +13 -3
  10. package/dist/{SettingsManager-I2LRCW2A.js → SettingsManager-FJFU6JJD.js} +7 -3
  11. package/dist/SettingsMigrationManager-EH3J2TCN.js +10 -0
  12. package/dist/{chunk-UJL4HI2R.js → chunk-3NFBZRPR.js} +2 -2
  13. package/dist/chunk-6UIGZD2N.js +20 -0
  14. package/dist/chunk-6UIGZD2N.js.map +1 -0
  15. package/dist/{chunk-RIEO2WML.js → chunk-74VMN2KC.js} +26 -2
  16. package/dist/chunk-74VMN2KC.js.map +1 -0
  17. package/dist/{chunk-OYF4VIFI.js → chunk-75B2HZZ5.js} +147 -22
  18. package/dist/chunk-75B2HZZ5.js.map +1 -0
  19. package/dist/{chunk-PGPI5LR4.js → chunk-ADDNFQJ4.js} +7 -21
  20. package/dist/chunk-ADDNFQJ4.js.map +1 -0
  21. package/dist/{chunk-AKUJXDNW.js → chunk-F4J6KEL6.js} +3 -3
  22. package/dist/{chunk-DLHA5VQ3.js → chunk-HD5SUKI2.js} +36 -179
  23. package/dist/chunk-HD5SUKI2.js.map +1 -0
  24. package/dist/chunk-HHDSIE72.js +667 -0
  25. package/dist/chunk-HHDSIE72.js.map +1 -0
  26. package/dist/{chunk-OXAM2WVC.js → chunk-HVGQP44L.js} +21 -1
  27. package/dist/chunk-HVGQP44L.js.map +1 -0
  28. package/dist/{chunk-RW54ZMBM.js → chunk-JJUPY5MM.js} +2 -2
  29. package/dist/{chunk-UAN4A3YU.js → chunk-KM3W7YQX.js} +11 -11
  30. package/dist/{chunk-3RUPPQRG.js → chunk-KO2FOMHL.js} +43 -2
  31. package/dist/{chunk-3RUPPQRG.js.map → chunk-KO2FOMHL.js.map} +1 -1
  32. package/dist/{chunk-2MAIX45J.js → chunk-LTNDJMTH.js} +104 -43
  33. package/dist/chunk-LTNDJMTH.js.map +1 -0
  34. package/dist/{chunk-2CXREBLZ.js → chunk-M5XUCTTJ.js} +8 -6
  35. package/dist/chunk-M5XUCTTJ.js.map +1 -0
  36. package/dist/{chunk-4XIDC3NF.js → chunk-MD6HA5IK.js} +2 -2
  37. package/dist/chunk-MLS5FAV7.js +189 -0
  38. package/dist/chunk-MLS5FAV7.js.map +1 -0
  39. package/dist/{chunk-2IJEMXOB.js → chunk-NFVFVYAP.js} +419 -427
  40. package/dist/chunk-NFVFVYAP.js.map +1 -0
  41. package/dist/{chunk-OC4H6HJD.js → chunk-O7WHXLCB.js} +2 -2
  42. package/dist/{chunk-M7JJCX53.js → chunk-OEGECBFS.js} +20 -20
  43. package/dist/chunk-OEGECBFS.js.map +1 -0
  44. package/dist/{chunk-MKWYLDFK.js → chunk-OF7BNW4D.js} +43 -3
  45. package/dist/chunk-OF7BNW4D.js.map +1 -0
  46. package/dist/{chunk-SUOXY5WJ.js → chunk-P2WZIDF3.js} +5 -5
  47. package/dist/chunk-P2WZIDF3.js.map +1 -0
  48. package/dist/{chunk-PA6Q6AWM.js → chunk-PSFVTBM7.js} +2 -2
  49. package/dist/chunk-QHA67Q7A.js +281 -0
  50. package/dist/chunk-QHA67Q7A.js.map +1 -0
  51. package/dist/{chunk-ZM3CFL5L.js → chunk-QRBOPFAA.js} +3 -3
  52. package/dist/{chunk-IFB4Z76W.js → chunk-S44CHE3G.js} +13 -12
  53. package/dist/chunk-S44CHE3G.js.map +1 -0
  54. package/dist/{chunk-CE26YH2U.js → chunk-SJ2GZ6RF.js} +48 -50
  55. package/dist/chunk-SJ2GZ6RF.js.map +1 -0
  56. package/dist/{chunk-SSCQCCJ7.js → chunk-THF25ICZ.js} +2 -2
  57. package/dist/{chunk-5Q3NDNNV.js → chunk-TR5MC2U6.js} +153 -6
  58. package/dist/chunk-TR5MC2U6.js.map +1 -0
  59. package/dist/{chunk-5VK4NRSF.js → chunk-UNXRACJ7.js} +35 -36
  60. package/dist/chunk-UNXRACJ7.js.map +1 -0
  61. package/dist/{chunk-GEHQXLEI.js → chunk-UYVWLISQ.js} +18 -35
  62. package/dist/chunk-UYVWLISQ.js.map +1 -0
  63. package/dist/{chunk-OSCLCMDG.js → chunk-UYWAESOT.js} +3 -3
  64. package/dist/{chunk-ZT3YZB4K.js → chunk-VBFDVGAE.js} +12 -12
  65. package/dist/chunk-VBFDVGAE.js.map +1 -0
  66. package/dist/{chunk-CDZERT7Z.js → chunk-VWNS6DH5.js} +48 -4
  67. package/dist/chunk-VWNS6DH5.js.map +1 -0
  68. package/dist/{chunk-CFFQ2Z7A.js → chunk-WUQQNE63.js} +2 -2
  69. package/dist/{claude-W52VKI6L.js → claude-X7EBJRB2.js} +8 -5
  70. package/dist/{cleanup-H4VXU3C3.js → cleanup-7QVPYBJJ.js} +133 -122
  71. package/dist/cleanup-7QVPYBJJ.js.map +1 -0
  72. package/dist/cli.js +901 -425
  73. package/dist/cli.js.map +1 -1
  74. package/dist/{color-F7RU6B6Z.js → color-ZPIIUADB.js} +3 -3
  75. package/dist/{contribute-Y7IQV5QY.js → contribute-RZYCYUDX.js} +8 -6
  76. package/dist/{contribute-Y7IQV5QY.js.map → contribute-RZYCYUDX.js.map} +1 -1
  77. package/dist/dev-server-LOY7YWCP.js +298 -0
  78. package/dist/dev-server-LOY7YWCP.js.map +1 -0
  79. package/dist/{feedback-XTUCKJNT.js → feedback-562KPG5U.js} +13 -12
  80. package/dist/{feedback-XTUCKJNT.js.map → feedback-562KPG5U.js.map} +1 -1
  81. package/dist/{git-IYA53VIC.js → git-OXJACVAU.js} +16 -4
  82. package/dist/hooks/iloom-hook.js +258 -0
  83. package/dist/{ignite-T74RYXCA.js → ignite-VSIPGKKG.js} +245 -39
  84. package/dist/ignite-VSIPGKKG.js.map +1 -0
  85. package/dist/index.d.ts +459 -124
  86. package/dist/index.js +740 -210
  87. package/dist/index.js.map +1 -1
  88. package/dist/init-SCR2LQ4A.js +21 -0
  89. package/dist/{installation-detector-VARGFFRZ.js → installation-detector-6R6YOFVZ.js} +3 -3
  90. package/dist/mcp/issue-management-server.js +2 -1
  91. package/dist/mcp/issue-management-server.js.map +1 -1
  92. package/dist/neon-helpers-L5CXQ5CT.js +11 -0
  93. package/dist/{open-UMXANW5S.js → open-CX7HUE26.js} +12 -10
  94. package/dist/{open-UMXANW5S.js.map → open-CX7HUE26.js.map} +1 -1
  95. package/dist/projects-6DTNDVLH.js +73 -0
  96. package/dist/projects-6DTNDVLH.js.map +1 -0
  97. package/dist/{prompt-QALMYTVC.js → prompt-A7GGRHSY.js} +3 -3
  98. package/dist/prompts/init-prompt.txt +49 -0
  99. package/dist/prompts/issue-prompt.txt +110 -8
  100. package/dist/prompts/regular-prompt.txt +90 -0
  101. package/dist/prompts/session-summary-prompt.txt +82 -0
  102. package/dist/{rebase-VJ2VKR6R.js → rebase-55URTXZC.js} +11 -9
  103. package/dist/{rebase-VJ2VKR6R.js.map → rebase-55URTXZC.js.map} +1 -1
  104. package/dist/{remote-VUNCQZ6J.js → remote-73TZ2ADI.js} +3 -3
  105. package/dist/{run-MJYY4PUT.js → run-DP2U2CA2.js} +12 -10
  106. package/dist/{run-MJYY4PUT.js.map → run-DP2U2CA2.js.map} +1 -1
  107. package/dist/schema/settings.schema.json +49 -0
  108. package/dist/summary-J3CJSM7L.js +244 -0
  109. package/dist/summary-J3CJSM7L.js.map +1 -0
  110. package/dist/{test-git-IT5EWQ5C.js → test-git-QLAIBJLX.js} +6 -4
  111. package/dist/{test-git-IT5EWQ5C.js.map → test-git-QLAIBJLX.js.map} +1 -1
  112. package/dist/{test-prefix-NPWDPUUH.js → test-prefix-6YM2ZOON.js} +6 -4
  113. package/dist/{test-prefix-NPWDPUUH.js.map → test-prefix-6YM2ZOON.js.map} +1 -1
  114. package/dist/{test-tabs-PRMRSHKI.js → test-tabs-JGO3VOXJ.js} +4 -4
  115. package/dist/{test-webserver-DAHONWCS.js → test-webserver-VPNLAFZ3.js} +2 -2
  116. package/dist/{update-4TDDUR5K.js → update-LETF5ASC.js} +4 -4
  117. package/dist/{update-notifier-QEX3CJHA.js → update-notifier-H55ZK7NU.js} +3 -3
  118. package/package.json +6 -6
  119. package/dist/ClaudeContextManager-BN7RE5ZQ.js +0 -15
  120. package/dist/ClaudeService-DLYLJUPA.js +0 -14
  121. package/dist/GitHubService-FZHHBOFG.js +0 -11
  122. package/dist/LoomLauncher-ZV3ZZIBA.js.map +0 -1
  123. package/dist/PromptTemplateManager-6HH3PVXV.js +0 -9
  124. package/dist/SettingsMigrationManager-TJ7UWZG5.js +0 -10
  125. package/dist/chunk-2CXREBLZ.js.map +0 -1
  126. package/dist/chunk-2IJEMXOB.js.map +0 -1
  127. package/dist/chunk-2MAIX45J.js.map +0 -1
  128. package/dist/chunk-5Q3NDNNV.js.map +0 -1
  129. package/dist/chunk-5VK4NRSF.js.map +0 -1
  130. package/dist/chunk-CDZERT7Z.js.map +0 -1
  131. package/dist/chunk-CE26YH2U.js.map +0 -1
  132. package/dist/chunk-DLHA5VQ3.js.map +0 -1
  133. package/dist/chunk-GEHQXLEI.js.map +0 -1
  134. package/dist/chunk-IFB4Z76W.js.map +0 -1
  135. package/dist/chunk-M7JJCX53.js.map +0 -1
  136. package/dist/chunk-MKWYLDFK.js.map +0 -1
  137. package/dist/chunk-OXAM2WVC.js.map +0 -1
  138. package/dist/chunk-OYF4VIFI.js.map +0 -1
  139. package/dist/chunk-PGPI5LR4.js.map +0 -1
  140. package/dist/chunk-RIEO2WML.js.map +0 -1
  141. package/dist/chunk-SUOXY5WJ.js.map +0 -1
  142. package/dist/chunk-ZT3YZB4K.js.map +0 -1
  143. package/dist/cleanup-H4VXU3C3.js.map +0 -1
  144. package/dist/ignite-T74RYXCA.js.map +0 -1
  145. package/dist/init-4FHTAM3F.js +0 -19
  146. package/dist/logger-MKYH4UDV.js +0 -12
  147. package/dist/neon-helpers-77PBPGJ5.js +0 -10
  148. package/dist/update-notifier-QEX3CJHA.js.map +0 -1
  149. /package/dist/{BranchNamingService-A77VI6AI.js.map → BranchNamingService-TOM2KAUT.js.map} +0 -0
  150. /package/dist/{ClaudeContextManager-BN7RE5ZQ.js.map → ClaudeContextManager-VEGJTS5E.js.map} +0 -0
  151. /package/dist/{ClaudeService-DLYLJUPA.js.map → ClaudeService-ICSHJMQ5.js.map} +0 -0
  152. /package/dist/{GitHubService-FZHHBOFG.js.map → GitHubService-RPM27GWD.js.map} +0 -0
  153. /package/dist/{PromptTemplateManager-6HH3PVXV.js.map → PromptTemplateManager-2TDZAUC6.js.map} +0 -0
  154. /package/dist/{SettingsManager-I2LRCW2A.js.map → SettingsManager-FJFU6JJD.js.map} +0 -0
  155. /package/dist/{SettingsMigrationManager-TJ7UWZG5.js.map → SettingsMigrationManager-EH3J2TCN.js.map} +0 -0
  156. /package/dist/{chunk-UJL4HI2R.js.map → chunk-3NFBZRPR.js.map} +0 -0
  157. /package/dist/{chunk-AKUJXDNW.js.map → chunk-F4J6KEL6.js.map} +0 -0
  158. /package/dist/{chunk-RW54ZMBM.js.map → chunk-JJUPY5MM.js.map} +0 -0
  159. /package/dist/{chunk-UAN4A3YU.js.map → chunk-KM3W7YQX.js.map} +0 -0
  160. /package/dist/{chunk-4XIDC3NF.js.map → chunk-MD6HA5IK.js.map} +0 -0
  161. /package/dist/{chunk-OC4H6HJD.js.map → chunk-O7WHXLCB.js.map} +0 -0
  162. /package/dist/{chunk-PA6Q6AWM.js.map → chunk-PSFVTBM7.js.map} +0 -0
  163. /package/dist/{chunk-ZM3CFL5L.js.map → chunk-QRBOPFAA.js.map} +0 -0
  164. /package/dist/{chunk-SSCQCCJ7.js.map → chunk-THF25ICZ.js.map} +0 -0
  165. /package/dist/{chunk-OSCLCMDG.js.map → chunk-UYWAESOT.js.map} +0 -0
  166. /package/dist/{chunk-CFFQ2Z7A.js.map → chunk-WUQQNE63.js.map} +0 -0
  167. /package/dist/{claude-W52VKI6L.js.map → claude-X7EBJRB2.js.map} +0 -0
  168. /package/dist/{color-F7RU6B6Z.js.map → color-ZPIIUADB.js.map} +0 -0
  169. /package/dist/{git-IYA53VIC.js.map → git-OXJACVAU.js.map} +0 -0
  170. /package/dist/{init-4FHTAM3F.js.map → init-SCR2LQ4A.js.map} +0 -0
  171. /package/dist/{installation-detector-VARGFFRZ.js.map → installation-detector-6R6YOFVZ.js.map} +0 -0
  172. /package/dist/{logger-MKYH4UDV.js.map → neon-helpers-L5CXQ5CT.js.map} +0 -0
  173. /package/dist/{neon-helpers-77PBPGJ5.js.map → prompt-A7GGRHSY.js.map} +0 -0
  174. /package/dist/{prompt-QALMYTVC.js.map → remote-73TZ2ADI.js.map} +0 -0
  175. /package/dist/{test-tabs-PRMRSHKI.js.map → test-tabs-JGO3VOXJ.js.map} +0 -0
  176. /package/dist/{test-webserver-DAHONWCS.js.map → test-webserver-VPNLAFZ3.js.map} +0 -0
  177. /package/dist/{update-4TDDUR5K.js.map → update-LETF5ASC.js.map} +0 -0
  178. /package/dist/{remote-VUNCQZ6J.js.map → update-notifier-H55ZK7NU.js.map} +0 -0
@@ -5,23 +5,31 @@ import {
5
5
  import {
6
6
  installDependencies,
7
7
  runScript
8
- } from "./chunk-ZT3YZB4K.js";
8
+ } from "./chunk-VBFDVGAE.js";
9
9
  import {
10
10
  hasScript,
11
11
  readPackageJson
12
12
  } from "./chunk-2ZPFJQ3B.js";
13
13
  import {
14
14
  branchExists,
15
+ checkRemoteBranchStatus,
15
16
  ensureRepositoryHasCommits,
16
17
  executeGitCommand,
17
18
  extractIssueNumber,
19
+ extractPRNumber,
18
20
  findMainWorktreePathWithSettings,
21
+ findWorktreeForBranch,
22
+ getMergeTargetBranch,
19
23
  hasUncommittedChanges,
24
+ isBranchMergedIntoMain,
20
25
  isFileTrackedByGit
21
- } from "./chunk-5Q3NDNNV.js";
26
+ } from "./chunk-TR5MC2U6.js";
27
+ import {
28
+ MetadataManager
29
+ } from "./chunk-MLS5FAV7.js";
22
30
  import {
23
31
  SettingsManager
24
- } from "./chunk-CDZERT7Z.js";
32
+ } from "./chunk-VWNS6DH5.js";
25
33
  import {
26
34
  calculateForegroundColor,
27
35
  generateColorFromBranchName,
@@ -29,7 +37,10 @@ import {
29
37
  lightenColor,
30
38
  rgbToHex,
31
39
  selectDistinctColor
32
- } from "./chunk-CFFQ2Z7A.js";
40
+ } from "./chunk-WUQQNE63.js";
41
+ import {
42
+ generateDeterministicSessionId
43
+ } from "./chunk-75B2HZZ5.js";
33
44
  import {
34
45
  findEnvFileForDatabaseUrl,
35
46
  formatEnvLine,
@@ -37,193 +48,21 @@ import {
37
48
  loadEnvIntoProcess,
38
49
  parseEnvFile,
39
50
  validateEnvVariable
40
- } from "./chunk-UJL4HI2R.js";
51
+ } from "./chunk-3NFBZRPR.js";
52
+ import {
53
+ getLogger
54
+ } from "./chunk-6UIGZD2N.js";
41
55
  import {
42
- createLogger,
43
56
  logger
44
- } from "./chunk-GEHQXLEI.js";
45
-
46
- // src/lib/MetadataManager.ts
47
- import path from "path";
48
- import os from "os";
49
- import fs from "fs-extra";
50
- var MetadataManager = class {
51
- constructor() {
52
- this.loomsDir = path.join(os.homedir(), ".config", "iloom-ai", "looms");
53
- }
54
- /**
55
- * Convert worktree path to filename slug per spec section 2.2
56
- *
57
- * Algorithm:
58
- * 1. Trim trailing slashes
59
- * 2. Replace all path separators (/ or \) with __ (double underscore)
60
- * 3. Replace any other non-alphanumeric characters (except _ and -) with -
61
- * 4. Append .json
62
- *
63
- * Example:
64
- * - Worktree: /Users/jane/dev/repo
65
- * - Filename: _Users__jane__dev__repo.json
66
- */
67
- slugifyPath(worktreePath) {
68
- let slug = worktreePath.replace(/[/\\]+$/, "");
69
- slug = slug.replace(/[/\\]/g, "___");
70
- slug = slug.replace(/[^a-zA-Z0-9_-]/g, "-");
71
- return `${slug}.json`;
72
- }
73
- /**
74
- * Get the full path to the metadata file for a worktree
75
- */
76
- getFilePath(worktreePath) {
77
- const filename = this.slugifyPath(worktreePath);
78
- return path.join(this.loomsDir, filename);
79
- }
80
- /**
81
- * Write metadata for a worktree (spec section 3.1)
82
- *
83
- * @param worktreePath - Absolute path to the worktree (used for file naming)
84
- * @param input - Metadata to write (description plus additional fields)
85
- */
86
- async writeMetadata(worktreePath, input) {
87
- try {
88
- await fs.ensureDir(this.loomsDir, { mode: 493 });
89
- const content = {
90
- description: input.description,
91
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
92
- version: 1,
93
- branchName: input.branchName,
94
- worktreePath: input.worktreePath,
95
- issueType: input.issueType,
96
- issue_numbers: input.issue_numbers,
97
- pr_numbers: input.pr_numbers,
98
- issueTracker: input.issueTracker,
99
- colorHex: input.colorHex
100
- };
101
- const filePath = this.getFilePath(worktreePath);
102
- await fs.writeFile(filePath, JSON.stringify(content, null, 2), { mode: 420 });
103
- logger.debug(`Metadata written for worktree: ${worktreePath}`);
104
- } catch (error) {
105
- logger.warn(
106
- `Failed to write metadata for worktree: ${error instanceof Error ? error.message : String(error)}`
107
- );
108
- }
109
- }
110
- /**
111
- * Read metadata for a worktree (spec section 3.2)
112
- *
113
- * @param worktreePath - Absolute path to the worktree
114
- * @returns The metadata object with all fields, or null if not found/invalid
115
- */
116
- async readMetadata(worktreePath) {
117
- try {
118
- const filePath = this.getFilePath(worktreePath);
119
- if (!await fs.pathExists(filePath)) {
120
- return null;
121
- }
122
- const content = await fs.readFile(filePath, "utf8");
123
- const data = JSON.parse(content);
124
- if (!data.description) {
125
- return null;
126
- }
127
- return {
128
- description: data.description,
129
- created_at: data.created_at ?? null,
130
- branchName: data.branchName ?? null,
131
- worktreePath: data.worktreePath ?? null,
132
- issueType: data.issueType ?? null,
133
- issue_numbers: data.issue_numbers ?? [],
134
- pr_numbers: data.pr_numbers ?? [],
135
- issueTracker: data.issueTracker ?? null,
136
- colorHex: data.colorHex ?? null
137
- };
138
- } catch (error) {
139
- logger.debug(
140
- `Could not read metadata for worktree ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`
141
- );
142
- return null;
143
- }
144
- }
145
- /**
146
- * List all stored loom metadata files
147
- *
148
- * Returns an array of LoomMetadata objects for all valid metadata files
149
- * in the looms directory. Invalid or unreadable files are skipped.
150
- *
151
- * @returns Array of LoomMetadata objects from all stored files
152
- */
153
- async listAllMetadata() {
154
- const results = [];
155
- try {
156
- if (!await fs.pathExists(this.loomsDir)) {
157
- return results;
158
- }
159
- const files = await fs.readdir(this.loomsDir);
160
- for (const file of files) {
161
- if (!file.endsWith(".json")) {
162
- continue;
163
- }
164
- try {
165
- const filePath = path.join(this.loomsDir, file);
166
- const content = await fs.readFile(filePath, "utf8");
167
- const data = JSON.parse(content);
168
- if (!data.description) {
169
- continue;
170
- }
171
- results.push({
172
- description: data.description,
173
- created_at: data.created_at ?? null,
174
- branchName: data.branchName ?? null,
175
- worktreePath: data.worktreePath ?? null,
176
- issueType: data.issueType ?? null,
177
- issue_numbers: data.issue_numbers ?? [],
178
- pr_numbers: data.pr_numbers ?? [],
179
- issueTracker: data.issueTracker ?? null,
180
- colorHex: data.colorHex ?? null
181
- });
182
- } catch (error) {
183
- logger.debug(
184
- `Skipping metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`
185
- );
186
- }
187
- }
188
- } catch (error) {
189
- logger.debug(
190
- `Could not list metadata files: ${error instanceof Error ? error.message : String(error)}`
191
- );
192
- }
193
- return results;
194
- }
195
- /**
196
- * Delete metadata for a worktree (spec section 3.3)
197
- *
198
- * Idempotent: silently succeeds if file doesn't exist
199
- * Non-fatal: logs warning on permission errors but doesn't throw
200
- *
201
- * @param worktreePath - Absolute path to the worktree
202
- */
203
- async deleteMetadata(worktreePath) {
204
- try {
205
- const filePath = this.getFilePath(worktreePath);
206
- if (!await fs.pathExists(filePath)) {
207
- logger.debug(`No metadata file to delete for worktree: ${worktreePath}`);
208
- return;
209
- }
210
- await fs.unlink(filePath);
211
- logger.debug(`Metadata deleted for worktree: ${worktreePath}`);
212
- } catch (error) {
213
- logger.warn(
214
- `Failed to delete metadata for worktree: ${error instanceof Error ? error.message : String(error)}`
215
- );
216
- }
217
- }
218
- };
57
+ } from "./chunk-UYVWLISQ.js";
219
58
 
220
59
  // src/lib/LoomManager.ts
221
- import path3 from "path";
222
- import fs3 from "fs-extra";
60
+ import path2 from "path";
61
+ import fs2 from "fs-extra";
223
62
 
224
63
  // src/lib/VSCodeIntegration.ts
225
- import fs2 from "fs-extra";
226
- import path2 from "path";
64
+ import fs from "fs-extra";
65
+ import path from "path";
227
66
  import { parse, modify, applyEdits } from "jsonc-parser";
228
67
  var VSCodeIntegration = class {
229
68
  /**
@@ -233,10 +72,10 @@ var VSCodeIntegration = class {
233
72
  * @param hexColor - Hex color string (e.g., "#dcebf8")
234
73
  */
235
74
  async setTitleBarColor(workspacePath, hexColor) {
236
- const vscodeDir = path2.join(workspacePath, ".vscode");
237
- const settingsPath = path2.join(vscodeDir, "settings.json");
75
+ const vscodeDir = path.join(workspacePath, ".vscode");
76
+ const settingsPath = path.join(vscodeDir, "settings.json");
238
77
  try {
239
- await fs2.ensureDir(vscodeDir);
78
+ await fs.ensureDir(vscodeDir);
240
79
  const settings = await this.readSettings(settingsPath);
241
80
  const updatedSettings = this.mergeColorSettings(settings, hexColor);
242
81
  await this.writeSettings(settingsPath, updatedSettings);
@@ -256,10 +95,10 @@ var VSCodeIntegration = class {
256
95
  */
257
96
  async readSettings(settingsPath) {
258
97
  try {
259
- if (!await fs2.pathExists(settingsPath)) {
98
+ if (!await fs.pathExists(settingsPath)) {
260
99
  return {};
261
100
  }
262
- const content = await fs2.readFile(settingsPath, "utf8");
101
+ const content = await fs.readFile(settingsPath, "utf8");
263
102
  const errors = [];
264
103
  const settings = parse(content, errors, { allowTrailingComma: true });
265
104
  if (errors.length > 0) {
@@ -283,8 +122,8 @@ var VSCodeIntegration = class {
283
122
  async writeSettings(settingsPath, settings) {
284
123
  try {
285
124
  let content;
286
- if (await fs2.pathExists(settingsPath)) {
287
- const existingContent = await fs2.readFile(settingsPath, "utf8");
125
+ if (await fs.pathExists(settingsPath)) {
126
+ const existingContent = await fs.readFile(settingsPath, "utf8");
288
127
  if (existingContent.includes("//") || existingContent.includes("/*")) {
289
128
  content = await this.modifyWithCommentsPreserved(existingContent, settings);
290
129
  } else {
@@ -294,8 +133,8 @@ var VSCodeIntegration = class {
294
133
  content = JSON.stringify(settings, null, 2) + "\n";
295
134
  }
296
135
  const tempPath = `${settingsPath}.tmp`;
297
- await fs2.writeFile(tempPath, content, "utf8");
298
- await fs2.rename(tempPath, settingsPath);
136
+ await fs.writeFile(tempPath, content, "utf8");
137
+ await fs.rename(tempPath, settingsPath);
299
138
  } catch (error) {
300
139
  throw new Error(
301
140
  `Failed to write settings.json: ${error instanceof Error ? error.message : "Unknown error"}`
@@ -373,7 +212,7 @@ var LoomManager = class {
373
212
  return null;
374
213
  }
375
214
  try {
376
- const envFilePath = path3.join(loomPath, ".env");
215
+ const envFilePath = path2.join(loomPath, ".env");
377
216
  const settings = await this.settings.loadSettings();
378
217
  const databaseUrlVarName = ((_b = (_a = settings.capabilities) == null ? void 0 : _a.database) == null ? void 0 : _b.databaseUrlEnvVarName) ?? "DATABASE_URL";
379
218
  const connectionString = await this.environment.getEnvVariable(envFilePath, databaseUrlVarName);
@@ -382,7 +221,7 @@ var LoomManager = class {
382
221
  }
383
222
  return await this.database.getBranchNameFromConnectionString(connectionString, loomPath);
384
223
  } catch (error) {
385
- logger.debug(`Could not get database branch for loom at ${loomPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
224
+ getLogger().debug(`Could not get database branch for loom at ${loomPath}: ${error instanceof Error ? error.message : "Unknown error"}`);
386
225
  return null;
387
226
  }
388
227
  }
@@ -392,28 +231,29 @@ var LoomManager = class {
392
231
  * NEW: Checks for existing worktrees and reuses them if found
393
232
  */
394
233
  async createIloom(input) {
395
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
396
- logger.info("Fetching issue data...");
234
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
235
+ getLogger().info("Fetching issue data...");
397
236
  const issueData = await this.fetchIssueData(input);
398
237
  if (input.type === "issue" || input.type === "pr" || input.type === "branch") {
399
- logger.info("Checking for existing worktree...");
238
+ getLogger().info("Checking for existing worktree...");
400
239
  const existing = await this.findExistingIloom(input, issueData);
401
240
  if (existing) {
402
- logger.success(`Found existing worktree, reusing: ${existing.path}`);
241
+ getLogger().success(`Found existing worktree, reusing: ${existing.path}`);
403
242
  return await this.reuseIloom(existing, input, issueData);
404
243
  }
405
- logger.info("No existing worktree found, creating new one...");
244
+ getLogger().info("No existing worktree found, creating new one...");
406
245
  }
407
- logger.info("Preparing branch name...");
246
+ getLogger().info("Preparing branch name...");
408
247
  const branchName = await this.prepareBranchName(input, issueData);
409
- logger.info("Creating git worktree...");
248
+ getLogger().info("Creating git worktree...");
410
249
  const worktreePath = await this.createWorktreeOnly(input, branchName);
411
250
  this.loadMainEnvFile();
412
251
  const { capabilities, binEntries } = await this.capabilityDetector.detectCapabilities(worktreePath);
413
252
  await this.copyEnvironmentFiles(worktreePath);
414
- await this.copyIloomSettings(worktreePath, (_a = input.parentLoom) == null ? void 0 : _a.branchName);
253
+ await this.copyIloomSettings(worktreePath);
254
+ await this.copyClaudeSettings(worktreePath);
415
255
  const settingsData = await this.settings.loadSettings();
416
- const basePort = ((_c = (_b = settingsData.capabilities) == null ? void 0 : _b.web) == null ? void 0 : _c.basePort) ?? 3e3;
256
+ const basePort = ((_b = (_a = settingsData.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
417
257
  let port = basePort;
418
258
  if (capabilities.includes("web")) {
419
259
  port = await this.setupPortForWeb(worktreePath, input, basePort);
@@ -421,10 +261,10 @@ var LoomManager = class {
421
261
  try {
422
262
  await installDependencies(worktreePath, true, true);
423
263
  } catch (error) {
424
- logger.warn(`Failed to install dependencies: ${error instanceof Error ? error.message : "Unknown error"}`, error);
264
+ getLogger().warn(`Failed to install dependencies: ${error instanceof Error ? error.message : "Unknown error"}`, error);
425
265
  }
426
266
  let databaseBranch = void 0;
427
- if (this.database && !((_d = input.options) == null ? void 0 : _d.skipDatabase)) {
267
+ if (this.database && !((_c = input.options) == null ? void 0 : _c.skipDatabase)) {
428
268
  try {
429
269
  const connectionString = await this.database.createBranchIfConfigured(
430
270
  branchName,
@@ -432,7 +272,7 @@ var LoomManager = class {
432
272
  // workspace path - checks all dotenv-flow files
433
273
  void 0,
434
274
  // cwd
435
- (_e = input.parentLoom) == null ? void 0 : _e.databaseBranch
275
+ (_d = input.parentLoom) == null ? void 0 : _d.databaseBranch
436
276
  // fromBranch - use parent's database branch for child looms
437
277
  );
438
278
  if (connectionString) {
@@ -441,19 +281,19 @@ var LoomManager = class {
441
281
  worktreePath,
442
282
  varName,
443
283
  isFileTrackedByGit,
444
- async (p) => fs3.pathExists(p),
284
+ async (p) => fs2.pathExists(p),
445
285
  async (p, v) => this.environment.getEnvVariable(p, v)
446
286
  );
447
287
  await this.environment.setEnvVar(
448
- path3.join(worktreePath, targetFile),
288
+ path2.join(worktreePath, targetFile),
449
289
  varName,
450
290
  connectionString
451
291
  );
452
- logger.success("Database branch configured");
292
+ getLogger().success("Database branch configured");
453
293
  databaseBranch = branchName;
454
294
  }
455
295
  } catch (error) {
456
- logger.error(
296
+ getLogger().error(
457
297
  `Failed to setup database branch: ${error instanceof Error ? error.message : "Unknown error"}`
458
298
  );
459
299
  throw error;
@@ -468,7 +308,7 @@ var LoomManager = class {
468
308
  binEntries
469
309
  );
470
310
  } catch (error) {
471
- logger.warn(
311
+ getLogger().warn(
472
312
  `Failed to setup CLI isolation: ${error instanceof Error ? error.message : "Unknown error"}`,
473
313
  error
474
314
  );
@@ -477,38 +317,38 @@ var LoomManager = class {
477
317
  const allMetadata = await this.metadataManager.listAllMetadata();
478
318
  const usedHexColors = allMetadata.filter((metadata) => metadata.colorHex !== null).map((metadata) => metadata.colorHex);
479
319
  const colorData = selectDistinctColor(branchName, usedHexColors);
480
- logger.debug(`Selected color ${colorData.hex} for branch ${branchName} (${usedHexColors.length} colors in use globally)`);
320
+ getLogger().debug(`Selected color ${colorData.hex} for branch ${branchName} (${usedHexColors.length} colors in use globally)`);
481
321
  try {
482
322
  await this.applyColorSynchronization(worktreePath, branchName, colorData, settingsData, input.options);
483
323
  } catch (error) {
484
- logger.warn(
324
+ getLogger().warn(
485
325
  `Failed to apply color synchronization: ${error instanceof Error ? error.message : "Unknown error"}`,
486
326
  error
487
327
  );
488
328
  }
489
329
  if (input.type === "issue") {
490
330
  try {
491
- logger.info("Moving issue to In Progress...");
331
+ getLogger().info("Moving issue to In Progress...");
492
332
  if (this.issueTracker.moveIssueToInProgress) {
493
333
  await this.issueTracker.moveIssueToInProgress(input.identifier);
494
334
  }
495
335
  } catch (error) {
496
- logger.warn(
336
+ getLogger().warn(
497
337
  `Failed to move issue to In Progress: ${error instanceof Error ? error.message : "Unknown error"}`,
498
338
  error
499
339
  );
500
340
  }
501
341
  }
502
- const enableClaude = ((_f = input.options) == null ? void 0 : _f.enableClaude) !== false;
503
- const enableCode = ((_g = input.options) == null ? void 0 : _g.enableCode) !== false;
504
- const enableDevServer = ((_h = input.options) == null ? void 0 : _h.enableDevServer) !== false;
505
- const enableTerminal = ((_i = input.options) == null ? void 0 : _i.enableTerminal) ?? false;
506
- const oneShot = ((_j = input.options) == null ? void 0 : _j.oneShot) ?? "default";
507
- const setArguments = (_k = input.options) == null ? void 0 : _k.setArguments;
508
- const executablePath = (_l = input.options) == null ? void 0 : _l.executablePath;
342
+ const enableClaude = ((_e = input.options) == null ? void 0 : _e.enableClaude) !== false;
343
+ const enableCode = ((_f = input.options) == null ? void 0 : _f.enableCode) !== false;
344
+ const enableDevServer = ((_g = input.options) == null ? void 0 : _g.enableDevServer) !== false;
345
+ const enableTerminal = ((_h = input.options) == null ? void 0 : _h.enableTerminal) ?? false;
346
+ const oneShot = ((_i = input.options) == null ? void 0 : _i.oneShot) ?? "default";
347
+ const setArguments = (_j = input.options) == null ? void 0 : _j.setArguments;
348
+ const executablePath = (_k = input.options) == null ? void 0 : _k.executablePath;
509
349
  if (enableClaude || enableCode || enableDevServer || enableTerminal) {
510
- const { LoomLauncher } = await import("./LoomLauncher-ZV3ZZIBA.js");
511
- const { ClaudeContextManager } = await import("./ClaudeContextManager-BN7RE5ZQ.js");
350
+ const { LoomLauncher } = await import("./LoomLauncher-SJBZFZXE.js");
351
+ const { ClaudeContextManager } = await import("./ClaudeContextManager-VEGJTS5E.js");
512
352
  const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
513
353
  const launcher = new LoomLauncher(claudeContext, this.settings);
514
354
  await launcher.launchLoom({
@@ -527,13 +367,14 @@ var LoomManager = class {
527
367
  ...setArguments && { setArguments },
528
368
  ...executablePath && { executablePath },
529
369
  sourceEnvOnStart: settingsData.sourceEnvOnStart ?? false,
530
- colorTerminal: ((_m = input.options) == null ? void 0 : _m.colorTerminal) ?? ((_n = settingsData.colors) == null ? void 0 : _n.terminal) ?? true,
370
+ colorTerminal: ((_l = input.options) == null ? void 0 : _l.colorTerminal) ?? ((_m = settingsData.colors) == null ? void 0 : _m.terminal) ?? true,
531
371
  colorHex: colorData.hex
532
372
  });
533
373
  }
534
374
  const description = (issueData == null ? void 0 : issueData.title) ?? branchName;
535
375
  const issue_numbers = input.type === "issue" ? [String(input.identifier)] : [];
536
376
  const pr_numbers = input.type === "pr" ? [String(input.identifier)] : [];
377
+ const sessionId = generateDeterministicSessionId(worktreePath);
537
378
  const metadataInput = {
538
379
  description,
539
380
  branchName,
@@ -542,7 +383,9 @@ var LoomManager = class {
542
383
  issue_numbers,
543
384
  pr_numbers,
544
385
  issueTracker: this.issueTracker.providerName,
545
- colorHex: colorData.hex
386
+ colorHex: colorData.hex,
387
+ sessionId,
388
+ ...input.parentLoom && { parentLoom: input.parentLoom }
546
389
  };
547
390
  await this.metadataManager.writeMetadata(worktreePath, metadataInput);
548
391
  const loom = {
@@ -568,7 +411,7 @@ var LoomManager = class {
568
411
  }
569
412
  }
570
413
  };
571
- logger.success(`Created loom: ${loom.id} at ${loom.path}`);
414
+ getLogger().success(`Created loom: ${loom.id} at ${loom.path}`);
572
415
  return loom;
573
416
  }
574
417
  /**
@@ -616,7 +459,7 @@ var LoomManager = class {
616
459
  const pattern = `${sanitizedBranchName}-looms/`;
617
460
  return worktrees.filter((wt) => wt.path.includes(pattern));
618
461
  } catch (error) {
619
- logger.debug(`Failed to find child looms: ${error instanceof Error ? error.message : "Unknown error"}`);
462
+ getLogger().debug(`Failed to find child looms: ${error instanceof Error ? error.message : "Unknown error"}`);
620
463
  return [];
621
464
  }
622
465
  }
@@ -630,7 +473,7 @@ var LoomManager = class {
630
473
  async checkAndWarnChildLooms(branchName) {
631
474
  let targetBranch = branchName;
632
475
  if (!targetBranch) {
633
- const { getCurrentBranch } = await import("./git-IYA53VIC.js");
476
+ const { getCurrentBranch } = await import("./git-OXJACVAU.js");
634
477
  targetBranch = await getCurrentBranch();
635
478
  }
636
479
  if (!targetBranch) {
@@ -638,19 +481,19 @@ var LoomManager = class {
638
481
  }
639
482
  const childLooms = await this.findChildLooms(targetBranch);
640
483
  if (childLooms.length > 0) {
641
- logger.warn(`Found ${childLooms.length} child loom(s) that should be finished first:`);
484
+ getLogger().warn(`Found ${childLooms.length} child loom(s) that should be finished first:`);
642
485
  for (const child of childLooms) {
643
- logger.warn(` - ${child.path}`);
486
+ getLogger().warn(` - ${child.path}`);
644
487
  }
645
- logger.warn("");
646
- logger.warn("To finish child looms:");
488
+ getLogger().warn("");
489
+ getLogger().warn("To finish child looms:");
647
490
  for (const child of childLooms) {
648
491
  const prMatch = child.branch.match(/_pr_(\d+)/);
649
492
  const issueId = extractIssueNumber(child.branch);
650
493
  const childIdentifier = prMatch ? prMatch[1] : issueId ?? child.branch;
651
- logger.warn(` il finish ${childIdentifier}`);
494
+ getLogger().warn(` il finish ${childIdentifier}`);
652
495
  }
653
- logger.warn("");
496
+ getLogger().warn("");
654
497
  return true;
655
498
  }
656
499
  return false;
@@ -696,14 +539,14 @@ var LoomManager = class {
696
539
  */
697
540
  async createWorktreeOnly(input, branchName) {
698
541
  var _a;
699
- logger.info("Ensuring repository has initial commit...");
542
+ getLogger().info("Ensuring repository has initial commit...");
700
543
  await ensureRepositoryHasCommits(this.gitWorktree.workingDirectory);
701
544
  const settingsData = await this.settings.loadSettings();
702
545
  let worktreePrefix = settingsData.worktreePrefix;
703
546
  if (input.parentLoom) {
704
547
  const sanitizedBranchName = input.parentLoom.branchName.replace(/\//g, "-").replace(/[^a-zA-Z0-9-_]/g, "-");
705
548
  worktreePrefix = `${sanitizedBranchName}-looms/`;
706
- logger.info(`Creating child loom with prefix: ${worktreePrefix}`);
549
+ getLogger().info(`Creating child loom with prefix: ${worktreePrefix}`);
707
550
  }
708
551
  const pathOptions = input.type === "pr" ? { isPR: true, prNumber: input.identifier } : {};
709
552
  if (worktreePrefix !== void 0) {
@@ -715,10 +558,10 @@ var LoomManager = class {
715
558
  pathOptions
716
559
  );
717
560
  if (input.type === "pr") {
718
- logger.info("Fetching all remote branches...");
561
+ getLogger().info("Fetching all remote branches...");
719
562
  try {
720
563
  await executeGitCommand(["fetch", "origin"], { cwd: this.gitWorktree.workingDirectory });
721
- logger.success("Successfully fetched from remote");
564
+ getLogger().success("Successfully fetched from remote");
722
565
  } catch (error) {
723
566
  throw new Error(
724
567
  `Failed to fetch from remote: ${error instanceof Error ? error.message : "Unknown error"}. Make sure you have access to the repository.`
@@ -740,13 +583,13 @@ var LoomManager = class {
740
583
  ...baseBranch && { baseBranch }
741
584
  });
742
585
  if (input.type === "pr" && !branchExistedLocally) {
743
- logger.info("Resetting new PR branch to match remote exactly...");
586
+ getLogger().info("Resetting new PR branch to match remote exactly...");
744
587
  try {
745
588
  await executeGitCommand(["reset", "--hard", `origin/${branchName}`], { cwd: worktreePath });
746
589
  await executeGitCommand(["branch", "--set-upstream-to", `origin/${branchName}`], { cwd: worktreePath });
747
- logger.success("Successfully reset to match remote");
590
+ getLogger().success("Successfully reset to match remote");
748
591
  } catch (error) {
749
- logger.warn(`Failed to reset to match remote: ${error instanceof Error ? error.message : "Unknown error"}`);
592
+ getLogger().warn(`Failed to reset to match remote: ${error instanceof Error ? error.message : "Unknown error"}`);
750
593
  }
751
594
  }
752
595
  return worktreePath;
@@ -768,23 +611,23 @@ var LoomManager = class {
768
611
  ];
769
612
  for (const pattern of envFilePatterns) {
770
613
  try {
771
- const mainEnvPath = path3.join(mainWorkspacePath, pattern);
772
- const worktreeEnvPath = path3.join(worktreePath, pattern);
773
- if (!await fs3.pathExists(mainEnvPath)) {
614
+ const mainEnvPath = path2.join(mainWorkspacePath, pattern);
615
+ const worktreeEnvPath = path2.join(worktreePath, pattern);
616
+ if (!await fs2.pathExists(mainEnvPath)) {
774
617
  continue;
775
618
  }
776
619
  if (await isFileTrackedByGit(pattern, mainWorkspacePath)) {
777
- logger.debug(`Skipping ${pattern} (tracked by git, already in worktree)`);
620
+ getLogger().debug(`Skipping ${pattern} (tracked by git, already in worktree)`);
778
621
  continue;
779
622
  }
780
- if (await fs3.pathExists(worktreeEnvPath)) {
781
- logger.warn(`${pattern} already exists in worktree, skipping copy`);
623
+ if (await fs2.pathExists(worktreeEnvPath)) {
624
+ getLogger().warn(`${pattern} already exists in worktree, skipping copy`);
782
625
  continue;
783
626
  }
784
627
  await this.environment.copyIfExists(mainEnvPath, worktreeEnvPath);
785
- logger.debug(`Copied ${pattern} to worktree`);
628
+ getLogger().debug(`Copied ${pattern} to worktree`);
786
629
  } catch (error) {
787
- logger.warn(`Warning: Failed to copy ${pattern}: ${error instanceof Error ? error.message : "Unknown error"}`);
630
+ getLogger().warn(`Warning: Failed to copy ${pattern}: ${error instanceof Error ? error.message : "Unknown error"}`);
788
631
  }
789
632
  }
790
633
  }
@@ -792,35 +635,41 @@ var LoomManager = class {
792
635
  * Copy iloom configuration (settings.local.json) from main repo to worktree
793
636
  * Always called regardless of project capabilities
794
637
  * @param worktreePath Path to the worktree
795
- * @param parentBranchName Optional parent branch name for child looms (sets mainBranch)
796
638
  */
797
- async copyIloomSettings(worktreePath, parentBranchName) {
798
- const mainSettingsLocalPath = path3.join(process.cwd(), ".iloom", "settings.local.json");
639
+ async copyIloomSettings(worktreePath) {
640
+ const mainSettingsLocalPath = path2.join(process.cwd(), ".iloom", "settings.local.json");
799
641
  try {
800
- const worktreeIloomDir = path3.join(worktreePath, ".iloom");
801
- await fs3.ensureDir(worktreeIloomDir);
802
- const worktreeSettingsLocalPath = path3.join(worktreeIloomDir, "settings.local.json");
803
- if (await fs3.pathExists(worktreeSettingsLocalPath)) {
804
- logger.warn("settings.local.json already exists in worktree, skipping copy");
642
+ const worktreeIloomDir = path2.join(worktreePath, ".iloom");
643
+ await fs2.ensureDir(worktreeIloomDir);
644
+ const worktreeSettingsLocalPath = path2.join(worktreeIloomDir, "settings.local.json");
645
+ if (await fs2.pathExists(worktreeSettingsLocalPath)) {
646
+ getLogger().warn("settings.local.json already exists in worktree, skipping copy");
805
647
  } else {
806
648
  await this.environment.copyIfExists(mainSettingsLocalPath, worktreeSettingsLocalPath);
807
649
  }
808
- if (parentBranchName) {
809
- let existingSettings = {};
810
- try {
811
- const content = await fs3.readFile(worktreeSettingsLocalPath, "utf8");
812
- existingSettings = JSON.parse(content);
813
- } catch {
814
- }
815
- const updatedSettings = {
816
- ...existingSettings,
817
- mainBranch: parentBranchName
818
- };
819
- await fs3.writeFile(worktreeSettingsLocalPath, JSON.stringify(updatedSettings, null, 2));
820
- logger.info(`Set mainBranch to ${parentBranchName} for child loom`);
650
+ } catch (error) {
651
+ getLogger().warn(`Warning: Failed to copy settings.local.json: ${error instanceof Error ? error.message : "Unknown error"}`);
652
+ }
653
+ }
654
+ /**
655
+ * Copy Claude settings (settings.local.json) from main repo to worktree
656
+ * Always called regardless of project capabilities
657
+ * Follows the same pattern as copyIloomSettings()
658
+ * @param worktreePath Path to the worktree
659
+ */
660
+ async copyClaudeSettings(worktreePath) {
661
+ const mainClaudeSettingsPath = path2.join(process.cwd(), ".claude", "settings.local.json");
662
+ try {
663
+ const worktreeClaudeDir = path2.join(worktreePath, ".claude");
664
+ await fs2.ensureDir(worktreeClaudeDir);
665
+ const worktreeClaudeSettingsPath = path2.join(worktreeClaudeDir, "settings.local.json");
666
+ if (await fs2.pathExists(worktreeClaudeSettingsPath)) {
667
+ getLogger().debug(".claude/settings.local.json already exists in worktree, skipping copy");
668
+ } else {
669
+ await this.environment.copyIfExists(mainClaudeSettingsPath, worktreeClaudeSettingsPath);
821
670
  }
822
671
  } catch (error) {
823
- logger.warn(`Warning: Failed to copy settings.local.json: ${error instanceof Error ? error.message : "Unknown error"}`);
672
+ getLogger().warn(`Warning: Failed to copy .claude/settings.local.json: ${error instanceof Error ? error.message : "Unknown error"}`);
824
673
  }
825
674
  }
826
675
  /**
@@ -828,7 +677,7 @@ var LoomManager = class {
828
677
  * Only called when project has web capabilities
829
678
  */
830
679
  async setupPortForWeb(worktreePath, input, basePort) {
831
- const envFilePath = path3.join(worktreePath, ".env.local");
680
+ const envFilePath = path2.join(worktreePath, ".env.local");
832
681
  const options = { basePort };
833
682
  if (input.type === "issue") {
834
683
  options.issueNumber = input.identifier;
@@ -848,11 +697,11 @@ var LoomManager = class {
848
697
  loadMainEnvFile() {
849
698
  const result = loadEnvIntoProcess({ path: process.cwd() });
850
699
  if (result.error) {
851
- logger.warn(`Warning: Could not load .env files: ${result.error.message}`);
700
+ getLogger().warn(`Warning: Could not load .env files: ${result.error.message}`);
852
701
  } else {
853
- logger.info("Loaded environment variables using dotenv-flow");
702
+ getLogger().info("Loaded environment variables using dotenv-flow");
854
703
  if (result.parsed && Object.keys(result.parsed).length > 0) {
855
- logger.debug(`Loaded ${Object.keys(result.parsed).length} environment variables`);
704
+ getLogger().debug(`Loaded ${Object.keys(result.parsed).length} environment variables`);
856
705
  }
857
706
  }
858
707
  }
@@ -871,8 +720,12 @@ var LoomManager = class {
871
720
  var _a, _b;
872
721
  const settingsData = await this.settings.loadSettings();
873
722
  const basePort = ((_b = (_a = settingsData.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
874
- if (input.type === "issue" && typeof input.identifier === "number") {
875
- return this.environment.calculatePort({ basePort, issueNumber: input.identifier });
723
+ if (input.type === "issue") {
724
+ if (typeof input.identifier === "number") {
725
+ return this.environment.calculatePort({ basePort, issueNumber: input.identifier });
726
+ } else if (typeof input.identifier === "string") {
727
+ return this.environment.calculatePort({ basePort, branchName: input.identifier });
728
+ }
876
729
  }
877
730
  if (input.type === "pr" && typeof input.identifier === "number") {
878
731
  return this.environment.calculatePort({ basePort, prNumber: input.identifier });
@@ -880,7 +733,7 @@ var LoomManager = class {
880
733
  if (input.type === "branch" && typeof input.identifier === "string") {
881
734
  return this.environment.calculatePort({ basePort, branchName: input.identifier });
882
735
  }
883
- throw new Error(`Unknown input type: ${input.type}`);
736
+ throw new Error(`Unknown input type: ${input.type} with identifier type: ${typeof input.identifier}`);
884
737
  }
885
738
  /**
886
739
  * Apply color synchronization to both VSCode and terminal
@@ -898,34 +751,56 @@ var LoomManager = class {
898
751
  const colorVscode = (options == null ? void 0 : options.colorVscode) ?? ((_a = settings.colors) == null ? void 0 : _a.vscode) ?? false;
899
752
  const colorTerminal = (options == null ? void 0 : options.colorTerminal) ?? ((_b = settings.colors) == null ? void 0 : _b.terminal) ?? true;
900
753
  if (!colorVscode && !colorTerminal) {
901
- logger.debug("Color synchronization disabled for both VSCode and terminal");
754
+ getLogger().debug("Color synchronization disabled for both VSCode and terminal");
902
755
  return;
903
756
  }
904
757
  if (colorVscode) {
905
758
  const vscode = new VSCodeIntegration();
906
759
  await vscode.setTitleBarColor(worktreePath, colorData.hex);
907
- logger.info(`Applied VSCode title bar color: ${colorData.hex} for branch: ${branchName}`);
760
+ getLogger().info(`Applied VSCode title bar color: ${colorData.hex} for branch: ${branchName}`);
908
761
  } else {
909
- logger.debug("VSCode color sync disabled (default: false for safety)");
762
+ getLogger().debug("VSCode color sync disabled (default: false for safety)");
910
763
  }
911
764
  }
912
765
  /**
913
766
  * Map worktrees to loom objects
914
- * This is a simplified conversion - in production we'd store loom metadata
915
- * Now reads metadata from MetadataManager (spec section 3.2)
767
+ * Reads loom metadata from MetadataManager with branch name parsing as fallback
916
768
  */
917
769
  async mapWorktreesToLooms(worktrees) {
918
770
  return await Promise.all(worktrees.map(async (wt) => {
771
+ var _a, _b;
772
+ const loomMetadata = await this.metadataManager.readMetadata(wt.path);
919
773
  let type = "branch";
920
774
  let identifier = wt.branch;
921
- if (wt.branch.startsWith("issue-")) {
922
- type = "issue";
923
- identifier = parseInt(wt.branch.replace("issue-", ""), 10);
924
- } else if (wt.branch.startsWith("pr-")) {
925
- type = "pr";
926
- identifier = parseInt(wt.branch.replace("pr-", ""), 10);
775
+ if (loomMetadata == null ? void 0 : loomMetadata.issueType) {
776
+ type = loomMetadata.issueType;
777
+ if (type === "issue" && ((_a = loomMetadata.issue_numbers) == null ? void 0 : _a[0])) {
778
+ const issueId = loomMetadata.issue_numbers[0];
779
+ const numericId = parseInt(issueId, 10);
780
+ identifier = isNaN(numericId) ? issueId : numericId;
781
+ } else if (type === "pr" && ((_b = loomMetadata.pr_numbers) == null ? void 0 : _b[0])) {
782
+ const prId = loomMetadata.pr_numbers[0];
783
+ identifier = parseInt(prId, 10);
784
+ } else if (type === "branch") {
785
+ identifier = wt.branch;
786
+ }
787
+ } else {
788
+ const prNumber = extractPRNumber(wt.branch);
789
+ if (prNumber !== null) {
790
+ type = "pr";
791
+ identifier = prNumber;
792
+ } else {
793
+ const issueNumber = extractIssueNumber(wt.branch);
794
+ if (issueNumber !== null) {
795
+ type = "issue";
796
+ const numericId = parseInt(issueNumber, 10);
797
+ identifier = isNaN(numericId) ? issueNumber : numericId;
798
+ } else {
799
+ type = "branch";
800
+ identifier = wt.branch;
801
+ }
802
+ }
927
803
  }
928
- const loomMetadata = await this.metadataManager.readMetadata(wt.path);
929
804
  return {
930
805
  id: `${type}-${identifier}`,
931
806
  path: wt.path,
@@ -969,41 +844,42 @@ var LoomManager = class {
969
844
  const { capabilities, binEntries } = await this.capabilityDetector.detectCapabilities(worktreePath);
970
845
  await this.copyEnvironmentFiles(worktreePath);
971
846
  await this.copyIloomSettings(worktreePath);
847
+ await this.copyClaudeSettings(worktreePath);
972
848
  const settingsData = await this.settings.loadSettings();
973
849
  const basePort = ((_b = (_a = settingsData.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
974
850
  let port = basePort;
975
851
  if (capabilities.includes("web")) {
976
852
  port = await this.setupPortForWeb(worktreePath, input, basePort);
977
853
  }
978
- logger.info("Database branch assumed to be already configured for existing worktree");
854
+ getLogger().info("Database branch assumed to be already configured for existing worktree");
979
855
  const databaseBranch = void 0;
980
856
  const existingMetadata = await this.metadataManager.readMetadata(worktreePath);
981
857
  let colorHex;
982
858
  if (existingMetadata == null ? void 0 : existingMetadata.colorHex) {
983
859
  colorHex = existingMetadata.colorHex;
984
- logger.debug(`Reusing stored color ${colorHex} for branch ${branchName}`);
860
+ getLogger().debug(`Reusing stored color ${colorHex} for branch ${branchName}`);
985
861
  } else {
986
862
  const colorData = generateColorFromBranchName(branchName);
987
863
  colorHex = colorData.hex;
988
- logger.debug(`No stored color, using hash-based color ${colorHex} for branch ${branchName}`);
864
+ getLogger().debug(`No stored color, using hash-based color ${colorHex} for branch ${branchName}`);
989
865
  }
990
866
  try {
991
867
  const colorData = { hex: colorHex, rgb: hexToRgb(colorHex), index: 0 };
992
868
  await this.applyColorSynchronization(worktreePath, branchName, colorData, settingsData, input.options);
993
869
  } catch (error) {
994
- logger.warn(
870
+ getLogger().warn(
995
871
  `Failed to apply color synchronization: ${error instanceof Error ? error.message : "Unknown error"}`,
996
872
  error
997
873
  );
998
874
  }
999
875
  if (input.type === "issue") {
1000
876
  try {
1001
- logger.info("Moving issue to In Progress...");
877
+ getLogger().info("Moving issue to In Progress...");
1002
878
  if (this.issueTracker.moveIssueToInProgress) {
1003
879
  await this.issueTracker.moveIssueToInProgress(input.identifier);
1004
880
  }
1005
881
  } catch (error) {
1006
- logger.warn(
882
+ getLogger().warn(
1007
883
  `Failed to move issue to In Progress: ${error instanceof Error ? error.message : "Unknown error"}`,
1008
884
  error
1009
885
  );
@@ -1017,9 +893,9 @@ var LoomManager = class {
1017
893
  const setArguments = (_h = input.options) == null ? void 0 : _h.setArguments;
1018
894
  const executablePath = (_i = input.options) == null ? void 0 : _i.executablePath;
1019
895
  if (enableClaude || enableCode || enableDevServer || enableTerminal) {
1020
- logger.info("Launching workspace components...");
1021
- const { LoomLauncher } = await import("./LoomLauncher-ZV3ZZIBA.js");
1022
- const { ClaudeContextManager } = await import("./ClaudeContextManager-BN7RE5ZQ.js");
896
+ getLogger().info("Launching workspace components...");
897
+ const { LoomLauncher } = await import("./LoomLauncher-SJBZFZXE.js");
898
+ const { ClaudeContextManager } = await import("./ClaudeContextManager-VEGJTS5E.js");
1023
899
  const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
1024
900
  const launcher = new LoomLauncher(claudeContext, this.settings);
1025
901
  await launcher.launchLoom({
@@ -1046,6 +922,7 @@ var LoomManager = class {
1046
922
  if (!existingMetadata) {
1047
923
  const issue_numbers = input.type === "issue" ? [String(input.identifier)] : [];
1048
924
  const pr_numbers = input.type === "pr" ? [String(input.identifier)] : [];
925
+ const sessionId = generateDeterministicSessionId(worktreePath);
1049
926
  const metadataInput = {
1050
927
  description,
1051
928
  branchName,
@@ -1054,7 +931,9 @@ var LoomManager = class {
1054
931
  issue_numbers,
1055
932
  pr_numbers,
1056
933
  issueTracker: this.issueTracker.providerName,
1057
- colorHex
934
+ colorHex,
935
+ sessionId,
936
+ ...input.parentLoom && { parentLoom: input.parentLoom }
1058
937
  };
1059
938
  await this.metadataManager.writeMetadata(worktreePath, metadataInput);
1060
939
  }
@@ -1081,14 +960,13 @@ var LoomManager = class {
1081
960
  }
1082
961
  }
1083
962
  };
1084
- logger.success(`Reused existing loom: ${loom.id} at ${loom.path}`);
963
+ getLogger().success(`Reused existing loom: ${loom.id} at ${loom.path}`);
1085
964
  return loom;
1086
965
  }
1087
966
  };
1088
967
 
1089
968
  // src/lib/EnvironmentManager.ts
1090
- import fs4 from "fs-extra";
1091
- var logger2 = createLogger({ prefix: "\u{1F4DD}" });
969
+ import fs3 from "fs-extra";
1092
970
  var EnvironmentManager = class {
1093
971
  constructor() {
1094
972
  this.backupSuffix = ".backup";
@@ -1103,15 +981,15 @@ var EnvironmentManager = class {
1103
981
  if (!validation.valid) {
1104
982
  throw new Error(validation.error ?? "Invalid variable name");
1105
983
  }
1106
- const fileExists = await fs4.pathExists(filePath);
984
+ const fileExists = await fs3.pathExists(filePath);
1107
985
  if (!fileExists) {
1108
- logger2.info(`Creating ${filePath} with ${key}...`);
986
+ getLogger().info(`Creating ${filePath} with ${key}...`);
1109
987
  const content = formatEnvLine(key, value);
1110
- await fs4.writeFile(filePath, content, "utf8");
1111
- logger2.success(`${filePath} created with ${key}`);
988
+ await fs3.writeFile(filePath, content, "utf8");
989
+ getLogger().success(`${filePath} created with ${key}`);
1112
990
  return;
1113
991
  }
1114
- const existingContent = await fs4.readFile(filePath, "utf8");
992
+ const existingContent = await fs3.readFile(filePath, "utf8");
1115
993
  const envMap = parseEnvFile(existingContent);
1116
994
  let backupPath;
1117
995
  if (backup) {
@@ -1140,15 +1018,15 @@ var EnvironmentManager = class {
1140
1018
  newLines.push(line);
1141
1019
  }
1142
1020
  if (!variableUpdated) {
1143
- logger2.info(`Adding ${key} to ${filePath}...`);
1021
+ getLogger().info(`Adding ${key} to ${filePath}...`);
1144
1022
  newLines.push(formatEnvLine(key, value));
1145
- logger2.success(`${key} added successfully`);
1023
+ getLogger().success(`${key} added successfully`);
1146
1024
  } else {
1147
- logger2.info(`Updating ${key} in ${filePath}...`);
1148
- logger2.success(`${key} updated successfully`);
1025
+ getLogger().info(`Updating ${key} in ${filePath}...`);
1026
+ getLogger().success(`${key} updated successfully`);
1149
1027
  }
1150
1028
  const newContent = newLines.join("\n");
1151
- await fs4.writeFile(filePath, newContent, "utf8");
1029
+ await fs3.writeFile(filePath, newContent, "utf8");
1152
1030
  return backupPath;
1153
1031
  }
1154
1032
  /**
@@ -1156,10 +1034,10 @@ var EnvironmentManager = class {
1156
1034
  */
1157
1035
  async readEnvFile(filePath) {
1158
1036
  try {
1159
- const content = await fs4.readFile(filePath, "utf8");
1037
+ const content = await fs3.readFile(filePath, "utf8");
1160
1038
  return parseEnvFile(content);
1161
1039
  } catch (error) {
1162
- logger2.debug(
1040
+ getLogger().debug(
1163
1041
  `Could not read env file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
1164
1042
  );
1165
1043
  return /* @__PURE__ */ new Map();
@@ -1179,13 +1057,13 @@ var EnvironmentManager = class {
1179
1057
  * @private
1180
1058
  */
1181
1059
  async copyIfExists(source, destination) {
1182
- const sourceExists = await fs4.pathExists(source);
1060
+ const sourceExists = await fs3.pathExists(source);
1183
1061
  if (!sourceExists) {
1184
- logger2.debug(`Source file ${source} does not exist, skipping copy`);
1062
+ getLogger().debug(`Source file ${source} does not exist, skipping copy`);
1185
1063
  return;
1186
1064
  }
1187
- await fs4.copy(source, destination, { overwrite: false });
1188
- logger2.success(`Copied ${source} to ${destination}`);
1065
+ await fs3.copy(source, destination, { overwrite: false });
1066
+ getLogger().success(`Copied ${source} to ${destination}`);
1189
1067
  }
1190
1068
  /**
1191
1069
  * Calculate unique port for workspace
@@ -1245,7 +1123,7 @@ var EnvironmentManager = class {
1245
1123
  */
1246
1124
  async validateEnvFile(filePath) {
1247
1125
  try {
1248
- const content = await fs4.readFile(filePath, "utf8");
1126
+ const content = await fs3.readFile(filePath, "utf8");
1249
1127
  const envMap = parseEnvFile(content);
1250
1128
  const errors = [];
1251
1129
  for (const [key, value] of envMap.entries()) {
@@ -1273,19 +1151,19 @@ var EnvironmentManager = class {
1273
1151
  async createBackup(filePath) {
1274
1152
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1275
1153
  const backupPath = `${filePath}${this.backupSuffix}-${timestamp}`;
1276
- await fs4.copy(filePath, backupPath);
1277
- logger2.debug(`Created backup at ${backupPath}`);
1154
+ await fs3.copy(filePath, backupPath);
1155
+ getLogger().debug(`Created backup at ${backupPath}`);
1278
1156
  return backupPath;
1279
1157
  }
1280
1158
  };
1281
1159
 
1282
1160
  // src/lib/CLIIsolationManager.ts
1283
- import fs5 from "fs-extra";
1284
- import path4 from "path";
1285
- import os2 from "os";
1161
+ import fs4 from "fs-extra";
1162
+ import path3 from "path";
1163
+ import os from "os";
1286
1164
  var CLIIsolationManager = class {
1287
1165
  constructor() {
1288
- this.iloomBinDir = path4.join(os2.homedir(), ".iloom", "bin");
1166
+ this.iloomBinDir = path3.join(os.homedir(), ".iloom", "bin");
1289
1167
  }
1290
1168
  /**
1291
1169
  * Setup CLI isolation for a worktree
@@ -1300,7 +1178,7 @@ var CLIIsolationManager = class {
1300
1178
  async setupCLIIsolation(worktreePath, identifier, binEntries) {
1301
1179
  await this.buildProject(worktreePath);
1302
1180
  await this.verifyBinTargets(worktreePath, binEntries);
1303
- await fs5.ensureDir(this.iloomBinDir);
1181
+ await fs4.ensureDir(this.iloomBinDir);
1304
1182
  const symlinkNames = await this.createVersionedSymlinks(
1305
1183
  worktreePath,
1306
1184
  identifier,
@@ -1316,12 +1194,12 @@ var CLIIsolationManager = class {
1316
1194
  async buildProject(worktreePath) {
1317
1195
  const pkgJson = await readPackageJson(worktreePath);
1318
1196
  if (!hasScript(pkgJson, "build")) {
1319
- logger.warn("No build script found in package.json - skipping build");
1197
+ getLogger().warn("No build script found in package.json - skipping build");
1320
1198
  return;
1321
1199
  }
1322
- logger.info("Building CLI tool...");
1200
+ getLogger().info("Building CLI tool...");
1323
1201
  await runScript("build", worktreePath, [], { quiet: true });
1324
- logger.success("Build completed");
1202
+ getLogger().success("Build completed");
1325
1203
  }
1326
1204
  /**
1327
1205
  * Verify bin targets exist and are executable
@@ -1330,13 +1208,13 @@ var CLIIsolationManager = class {
1330
1208
  */
1331
1209
  async verifyBinTargets(worktreePath, binEntries) {
1332
1210
  for (const binPath of Object.values(binEntries)) {
1333
- const targetPath = path4.resolve(worktreePath, binPath);
1334
- const exists = await fs5.pathExists(targetPath);
1211
+ const targetPath = path3.resolve(worktreePath, binPath);
1212
+ const exists = await fs4.pathExists(targetPath);
1335
1213
  if (!exists) {
1336
1214
  throw new Error(`Bin target does not exist: ${targetPath}`);
1337
1215
  }
1338
1216
  try {
1339
- await fs5.access(targetPath, fs5.constants.X_OK);
1217
+ await fs4.access(targetPath, fs4.constants.X_OK);
1340
1218
  } catch {
1341
1219
  }
1342
1220
  }
@@ -1352,10 +1230,10 @@ var CLIIsolationManager = class {
1352
1230
  const symlinkNames = [];
1353
1231
  for (const [binName, binPath] of Object.entries(binEntries)) {
1354
1232
  const versionedName = `${binName}-${identifier}`;
1355
- const targetPath = path4.resolve(worktreePath, binPath);
1356
- const symlinkPath = path4.join(this.iloomBinDir, versionedName);
1357
- await fs5.symlink(targetPath, symlinkPath);
1358
- logger.success(`CLI available: ${versionedName}`);
1233
+ const targetPath = path3.resolve(worktreePath, binPath);
1234
+ const symlinkPath = path3.join(this.iloomBinDir, versionedName);
1235
+ await fs4.symlink(targetPath, symlinkPath);
1236
+ getLogger().success(`CLI available: ${versionedName}`);
1359
1237
  symlinkNames.push(versionedName);
1360
1238
  }
1361
1239
  return symlinkNames;
@@ -1370,10 +1248,10 @@ var CLIIsolationManager = class {
1370
1248
  }
1371
1249
  const shell = this.detectShell();
1372
1250
  const rcFile = this.getShellRcFile(shell);
1373
- logger.warn("\n\u26A0\uFE0F One-time PATH setup required:");
1374
- logger.warn(` Add to ${rcFile}:`);
1375
- logger.warn(` export PATH="$HOME/.iloom/bin:$PATH"`);
1376
- logger.warn(` Then run: source ${rcFile}
1251
+ getLogger().warn("\n\u26A0\uFE0F One-time PATH setup required:");
1252
+ getLogger().warn(` Add to ${rcFile}:`);
1253
+ getLogger().warn(` export PATH="$HOME/.iloom/bin:$PATH"`);
1254
+ getLogger().warn(` Then run: source ${rcFile}
1377
1255
  `);
1378
1256
  }
1379
1257
  /**
@@ -1407,12 +1285,12 @@ var CLIIsolationManager = class {
1407
1285
  async cleanupVersionedExecutables(identifier) {
1408
1286
  const removed = [];
1409
1287
  try {
1410
- const files = await fs5.readdir(this.iloomBinDir);
1288
+ const files = await fs4.readdir(this.iloomBinDir);
1411
1289
  for (const file of files) {
1412
1290
  if (this.matchesIdentifier(file, identifier)) {
1413
- const symlinkPath = path4.join(this.iloomBinDir, file);
1291
+ const symlinkPath = path3.join(this.iloomBinDir, file);
1414
1292
  try {
1415
- await fs5.unlink(symlinkPath);
1293
+ await fs4.unlink(symlinkPath);
1416
1294
  removed.push(file);
1417
1295
  } catch (error) {
1418
1296
  const isEnoent = error && typeof error === "object" && "code" in error && error.code === "ENOENT";
@@ -1420,7 +1298,7 @@ var CLIIsolationManager = class {
1420
1298
  removed.push(file);
1421
1299
  continue;
1422
1300
  }
1423
- logger.warn(
1301
+ getLogger().warn(
1424
1302
  `Failed to remove symlink ${file}: ${error instanceof Error ? error.message : "Unknown error"}`
1425
1303
  );
1426
1304
  }
@@ -1429,13 +1307,13 @@ var CLIIsolationManager = class {
1429
1307
  } catch (error) {
1430
1308
  const isEnoent = error && typeof error === "object" && "code" in error && error.code === "ENOENT";
1431
1309
  if (isEnoent) {
1432
- logger.warn("No CLI executables directory found - nothing to cleanup");
1310
+ getLogger().warn("No CLI executables directory found - nothing to cleanup");
1433
1311
  return [];
1434
1312
  }
1435
1313
  throw error;
1436
1314
  }
1437
1315
  if (removed.length > 0) {
1438
- logger.success(`Removed CLI executables: ${removed.join(", ")}`);
1316
+ getLogger().success(`Removed CLI executables: ${removed.join(", ")}`);
1439
1317
  }
1440
1318
  return removed;
1441
1319
  }
@@ -1448,15 +1326,15 @@ var CLIIsolationManager = class {
1448
1326
  async findOrphanedSymlinks() {
1449
1327
  const orphaned = [];
1450
1328
  try {
1451
- const files = await fs5.readdir(this.iloomBinDir);
1329
+ const files = await fs4.readdir(this.iloomBinDir);
1452
1330
  for (const file of files) {
1453
- const symlinkPath = path4.join(this.iloomBinDir, file);
1331
+ const symlinkPath = path3.join(this.iloomBinDir, file);
1454
1332
  try {
1455
- const stats = await fs5.lstat(symlinkPath);
1333
+ const stats = await fs4.lstat(symlinkPath);
1456
1334
  if (stats.isSymbolicLink()) {
1457
- const target = await fs5.readlink(symlinkPath);
1335
+ const target = await fs4.readlink(symlinkPath);
1458
1336
  try {
1459
- await fs5.access(target);
1337
+ await fs4.access(target);
1460
1338
  } catch {
1461
1339
  orphaned.push({
1462
1340
  name: file,
@@ -1466,7 +1344,7 @@ var CLIIsolationManager = class {
1466
1344
  }
1467
1345
  }
1468
1346
  } catch (error) {
1469
- logger.warn(
1347
+ getLogger().warn(
1470
1348
  `Failed to check symlink ${file}: ${error instanceof Error ? error.message : "Unknown error"}`
1471
1349
  );
1472
1350
  }
@@ -1491,11 +1369,11 @@ var CLIIsolationManager = class {
1491
1369
  let removedCount = 0;
1492
1370
  for (const symlink of orphaned) {
1493
1371
  try {
1494
- await fs5.unlink(symlink.path);
1372
+ await fs4.unlink(symlink.path);
1495
1373
  removedCount++;
1496
- logger.success(`Removed orphaned symlink: ${symlink.name}`);
1374
+ getLogger().success(`Removed orphaned symlink: ${symlink.name}`);
1497
1375
  } catch (error) {
1498
- logger.warn(
1376
+ getLogger().warn(
1499
1377
  `Failed to remove orphaned symlink ${symlink.name}: ${error instanceof Error ? error.message : "Unknown error"}`
1500
1378
  );
1501
1379
  }
@@ -1517,17 +1395,16 @@ var CLIIsolationManager = class {
1517
1395
  };
1518
1396
 
1519
1397
  // src/lib/DatabaseManager.ts
1520
- import fs6 from "fs-extra";
1521
- var logger3 = createLogger({ prefix: "\u{1F5C2}\uFE0F" });
1398
+ import fs5 from "fs-extra";
1522
1399
  var DatabaseManager = class {
1523
1400
  constructor(provider, environment, databaseUrlEnvVarName = "DATABASE_URL") {
1524
1401
  this.provider = provider;
1525
1402
  this.environment = environment;
1526
1403
  this.databaseUrlEnvVarName = databaseUrlEnvVarName;
1527
1404
  if (databaseUrlEnvVarName !== "DATABASE_URL") {
1528
- logger3.debug(`\u{1F527} DatabaseManager configured with custom variable: ${databaseUrlEnvVarName}`);
1405
+ getLogger().debug(`DatabaseManager configured with custom variable: ${databaseUrlEnvVarName}`);
1529
1406
  } else {
1530
- logger3.debug("\u{1F527} DatabaseManager using default variable: DATABASE_URL");
1407
+ getLogger().debug("DatabaseManager using default variable: DATABASE_URL");
1531
1408
  }
1532
1409
  }
1533
1410
  /**
@@ -1544,12 +1421,12 @@ var DatabaseManager = class {
1544
1421
  */
1545
1422
  async shouldUseDatabaseBranching(workspacePath) {
1546
1423
  if (!this.provider.isConfigured()) {
1547
- logger3.debug("Skipping database branching: Database provider not configured");
1424
+ getLogger().debug("Skipping database branching: Database provider not configured");
1548
1425
  return false;
1549
1426
  }
1550
1427
  const hasDatabaseUrl = await this.hasDatabaseUrlInEnv(workspacePath);
1551
1428
  if (!hasDatabaseUrl) {
1552
- logger3.debug(
1429
+ getLogger().debug(
1553
1430
  "Skipping database branching: configured database URL variable not found in any env file"
1554
1431
  );
1555
1432
  return false;
@@ -1570,28 +1447,28 @@ var DatabaseManager = class {
1570
1447
  return null;
1571
1448
  }
1572
1449
  if (!await this.provider.isCliAvailable()) {
1573
- logger3.warn("Skipping database branch creation: Neon CLI not available");
1574
- logger3.warn("Install with: npm install -g neonctl");
1450
+ getLogger().warn("Skipping database branch creation: Neon CLI not available");
1451
+ getLogger().warn("Install with: npm install -g neonctl");
1575
1452
  return null;
1576
1453
  }
1577
1454
  try {
1578
1455
  const isAuth = await this.provider.isAuthenticated(cwd);
1579
1456
  if (!isAuth) {
1580
- logger3.warn("Skipping database branch creation: Not authenticated with Neon CLI");
1581
- logger3.warn("Run: neon auth");
1457
+ getLogger().warn("Skipping database branch creation: Not authenticated with Neon CLI");
1458
+ getLogger().warn("Run: neon auth");
1582
1459
  return null;
1583
1460
  }
1584
1461
  } catch (error) {
1585
1462
  const errorMessage = error instanceof Error ? error.message : String(error);
1586
- logger3.error(`Database authentication check failed: ${errorMessage}`);
1463
+ getLogger().error(`Database authentication check failed: ${errorMessage}`);
1587
1464
  throw error;
1588
1465
  }
1589
1466
  try {
1590
1467
  const connectionString = await this.provider.createBranch(branchName, fromBranch, cwd);
1591
- logger3.success(`Database branch ready: ${this.provider.sanitizeBranchName(branchName)}`);
1468
+ getLogger().success(`Database branch ready: ${this.provider.sanitizeBranchName(branchName)}`);
1592
1469
  return connectionString;
1593
1470
  } catch (error) {
1594
- logger3.error(
1471
+ getLogger().error(
1595
1472
  `Failed to create database branch: ${error instanceof Error ? error.message : String(error)}`
1596
1473
  );
1597
1474
  throw error;
@@ -1617,7 +1494,7 @@ var DatabaseManager = class {
1617
1494
  };
1618
1495
  }
1619
1496
  if (!this.provider.isConfigured()) {
1620
- logger3.debug("Skipping database branch deletion: Database provider not configured");
1497
+ getLogger().debug("Skipping database branch deletion: Database provider not configured");
1621
1498
  return {
1622
1499
  success: true,
1623
1500
  deleted: false,
@@ -1626,7 +1503,7 @@ var DatabaseManager = class {
1626
1503
  };
1627
1504
  }
1628
1505
  if (!await this.provider.isCliAvailable()) {
1629
- logger3.info("Skipping database branch deletion: CLI tool not available");
1506
+ getLogger().info("Skipping database branch deletion: CLI tool not available");
1630
1507
  return {
1631
1508
  success: false,
1632
1509
  deleted: false,
@@ -1638,7 +1515,7 @@ var DatabaseManager = class {
1638
1515
  try {
1639
1516
  const isAuth = await this.provider.isAuthenticated(cwd);
1640
1517
  if (!isAuth) {
1641
- logger3.warn("Skipping database branch deletion: Not authenticated with DB Provider");
1518
+ getLogger().warn("Skipping database branch deletion: Not authenticated with DB Provider");
1642
1519
  return {
1643
1520
  success: false,
1644
1521
  deleted: false,
@@ -1649,7 +1526,7 @@ var DatabaseManager = class {
1649
1526
  }
1650
1527
  } catch (error) {
1651
1528
  const errorMessage = error instanceof Error ? error.message : String(error);
1652
- logger3.error(`Database authentication check failed: ${errorMessage}`);
1529
+ getLogger().error(`Database authentication check failed: ${errorMessage}`);
1653
1530
  return {
1654
1531
  success: false,
1655
1532
  deleted: false,
@@ -1662,7 +1539,7 @@ var DatabaseManager = class {
1662
1539
  const result = await this.provider.deleteBranch(branchName, isPreview, cwd);
1663
1540
  return result;
1664
1541
  } catch (error) {
1665
- logger3.warn(
1542
+ getLogger().warn(
1666
1543
  `Unexpected error in database deletion: ${error instanceof Error ? error.message : String(error)}`
1667
1544
  );
1668
1545
  return {
@@ -1683,13 +1560,13 @@ var DatabaseManager = class {
1683
1560
  */
1684
1561
  async getBranchNameFromConnectionString(connectionString, cwd) {
1685
1562
  if (!this.provider.isConfigured()) {
1686
- logger3.debug("Provider not configured, skipping reverse lookup");
1563
+ getLogger().debug("Provider not configured, skipping reverse lookup");
1687
1564
  return null;
1688
1565
  }
1689
1566
  if ("getBranchNameFromConnectionString" in this.provider && typeof this.provider.getBranchNameFromConnectionString === "function") {
1690
1567
  return this.provider.getBranchNameFromConnectionString(connectionString, cwd);
1691
1568
  }
1692
- logger3.debug("Provider does not support reverse lookup");
1569
+ getLogger().debug("Provider does not support reverse lookup");
1693
1570
  return null;
1694
1571
  }
1695
1572
  /**
@@ -1700,26 +1577,26 @@ var DatabaseManager = class {
1700
1577
  async hasDatabaseUrlInEnv(workspacePath) {
1701
1578
  try {
1702
1579
  if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
1703
- logger3.debug(`Looking for custom database URL variable: ${this.databaseUrlEnvVarName}`);
1580
+ getLogger().debug(`Looking for custom database URL variable: ${this.databaseUrlEnvVarName}`);
1704
1581
  } else {
1705
- logger3.debug("Looking for default database URL variable: DATABASE_URL");
1582
+ getLogger().debug("Looking for default database URL variable: DATABASE_URL");
1706
1583
  }
1707
1584
  const hasConfiguredVar = await hasVariableInAnyEnvFile(
1708
1585
  workspacePath,
1709
1586
  this.databaseUrlEnvVarName,
1710
- async (p) => fs6.pathExists(p),
1587
+ async (p) => fs5.pathExists(p),
1711
1588
  async (p, v) => this.environment.getEnvVariable(p, v)
1712
1589
  );
1713
1590
  if (hasConfiguredVar) {
1714
1591
  if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
1715
- logger3.debug(`\u2705 Found custom database URL variable: ${this.databaseUrlEnvVarName}`);
1592
+ getLogger().debug(`\u2705 Found custom database URL variable: ${this.databaseUrlEnvVarName}`);
1716
1593
  } else {
1717
- logger3.debug(`\u2705 Found default database URL variable: DATABASE_URL`);
1594
+ getLogger().debug(`\u2705 Found default database URL variable: DATABASE_URL`);
1718
1595
  }
1719
1596
  return true;
1720
1597
  }
1721
1598
  if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
1722
- logger3.debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in any env file`);
1599
+ getLogger().debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in any env file`);
1723
1600
  throw new Error(
1724
1601
  `Configured database URL environment variable '${this.databaseUrlEnvVarName}' not found in any dotenv-flow file. Please add it to an .env file or update your iloom configuration.`
1725
1602
  );
@@ -1727,13 +1604,13 @@ var DatabaseManager = class {
1727
1604
  const hasDefaultVar = await hasVariableInAnyEnvFile(
1728
1605
  workspacePath,
1729
1606
  "DATABASE_URL",
1730
- async (p) => fs6.pathExists(p),
1607
+ async (p) => fs5.pathExists(p),
1731
1608
  async (p, v) => this.environment.getEnvVariable(p, v)
1732
1609
  );
1733
1610
  if (hasDefaultVar) {
1734
- logger3.debug("\u2705 Found fallback DATABASE_URL variable");
1611
+ getLogger().debug("\u2705 Found fallback DATABASE_URL variable");
1735
1612
  } else {
1736
- logger3.debug("\u274C No DATABASE_URL variable found in any env file");
1613
+ getLogger().debug("\u274C No DATABASE_URL variable found in any env file");
1737
1614
  }
1738
1615
  return hasDefaultVar;
1739
1616
  } catch (error) {
@@ -1746,7 +1623,7 @@ var DatabaseManager = class {
1746
1623
  };
1747
1624
 
1748
1625
  // src/lib/ResourceCleanup.ts
1749
- import path5 from "path";
1626
+ import path4 from "path";
1750
1627
  var ResourceCleanup = class {
1751
1628
  constructor(gitWorktree, processManager, database, cliIsolation, settingsManager) {
1752
1629
  this.gitWorktree = gitWorktree;
@@ -1768,7 +1645,7 @@ var ResourceCleanup = class {
1768
1645
  const operations = [];
1769
1646
  const errors = [];
1770
1647
  const displayIdentifier = parsed.branchName ?? ((_a = parsed.number) == null ? void 0 : _a.toString()) ?? parsed.originalInput;
1771
- logger.info(`Starting cleanup for: ${displayIdentifier}`);
1648
+ getLogger().info(`Starting cleanup for: ${displayIdentifier}`);
1772
1649
  const number = parsed.number;
1773
1650
  if (number !== void 0) {
1774
1651
  const port = this.processManager.calculatePort(number);
@@ -1814,7 +1691,7 @@ var ResourceCleanup = class {
1814
1691
  if (!worktree) {
1815
1692
  throw new Error(`No worktree found for identifier: ${displayIdentifier}`);
1816
1693
  }
1817
- logger.debug(`Found worktree: path="${worktree.path}", branch="${worktree.branch}"`);
1694
+ getLogger().debug(`Found worktree: path="${worktree.path}", branch="${worktree.branch}"`);
1818
1695
  } catch (error) {
1819
1696
  const err = error instanceof Error ? error : new Error("Unknown error");
1820
1697
  errors.push(err);
@@ -1827,7 +1704,9 @@ var ResourceCleanup = class {
1827
1704
  };
1828
1705
  }
1829
1706
  if (!options.force) {
1830
- const safety = await this.validateWorktreeSafety(worktree, parsed.originalInput);
1707
+ const shouldCheckMergeSafety = options.checkMergeSafety ?? options.deleteBranch === true;
1708
+ const shouldCheckRemoteBranch = options.checkRemoteBranch ?? false;
1709
+ const safety = await this.validateWorktreeSafety(worktree, parsed.originalInput, shouldCheckMergeSafety, shouldCheckRemoteBranch);
1831
1710
  if (!safety.isSafe) {
1832
1711
  const blockerMessage = safety.blockers.join("\n\n");
1833
1712
  throw new Error(`Cannot cleanup:
@@ -1836,18 +1715,18 @@ ${blockerMessage}`);
1836
1715
  }
1837
1716
  if (safety.warnings.length > 0) {
1838
1717
  safety.warnings.forEach((warning) => {
1839
- logger.warn(warning);
1718
+ getLogger().warn(warning);
1840
1719
  });
1841
1720
  }
1842
1721
  }
1843
1722
  let databaseConfig = null;
1844
1723
  if (!options.keepDatabase && worktree) {
1845
- const envFilePath = path5.join(worktree.path, ".env");
1724
+ const envFilePath = path4.join(worktree.path, ".env");
1846
1725
  try {
1847
1726
  const shouldCleanup = this.database ? await this.database.shouldUseDatabaseBranching(envFilePath) : false;
1848
1727
  databaseConfig = { shouldCleanup, envFilePath };
1849
1728
  } catch (error) {
1850
- logger.warn(
1729
+ getLogger().warn(
1851
1730
  `Failed to read database config from ${envFilePath}, skipping database cleanup: ${error instanceof Error ? error.message : String(error)}`
1852
1731
  );
1853
1732
  databaseConfig = { shouldCleanup: false, envFilePath };
@@ -1858,11 +1737,25 @@ ${blockerMessage}`);
1858
1737
  try {
1859
1738
  mainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, this.settingsManager);
1860
1739
  } catch (error) {
1861
- logger.warn(
1740
+ getLogger().warn(
1862
1741
  `Failed to find main worktree path: ${error instanceof Error ? error.message : String(error)}`
1863
1742
  );
1864
1743
  }
1865
1744
  }
1745
+ let mergeTargetBranch = null;
1746
+ if (options.deleteBranch && worktree && !options.dryRun) {
1747
+ try {
1748
+ mergeTargetBranch = await getMergeTargetBranch(worktree.path, {
1749
+ settingsManager: this.settingsManager,
1750
+ metadataManager: this.metadataManager
1751
+ });
1752
+ getLogger().debug(`Pre-fetched merge target branch: ${mergeTargetBranch}`);
1753
+ } catch (error) {
1754
+ getLogger().warn(
1755
+ `Failed to pre-fetch merge target branch: ${error instanceof Error ? error.message : String(error)}`
1756
+ );
1757
+ }
1758
+ }
1866
1759
  if (options.dryRun) {
1867
1760
  operations.push({
1868
1761
  type: "worktree",
@@ -1886,7 +1779,7 @@ ${blockerMessage}`);
1886
1779
  message: `Worktree removed: ${worktree.path}`
1887
1780
  });
1888
1781
  await this.metadataManager.deleteMetadata(worktree.path);
1889
- logger.debug(`Metadata file cleanup attempted for: ${worktree.path}`);
1782
+ getLogger().debug(`Metadata file cleanup attempted for: ${worktree.path}`);
1890
1783
  } catch (error) {
1891
1784
  const err = error instanceof Error ? error : new Error("Unknown error");
1892
1785
  errors.push(err);
@@ -1907,7 +1800,12 @@ ${blockerMessage}`);
1907
1800
  });
1908
1801
  } else {
1909
1802
  try {
1910
- const branchOptions = { dryRun: false };
1803
+ const branchOptions = {
1804
+ dryRun: false
1805
+ };
1806
+ if (mergeTargetBranch !== null) {
1807
+ branchOptions.mergeTargetBranch = mergeTargetBranch;
1808
+ }
1911
1809
  if (options.force !== void 0) {
1912
1810
  branchOptions.force = options.force;
1913
1811
  }
@@ -1948,7 +1846,7 @@ ${blockerMessage}`);
1948
1846
  } catch (error) {
1949
1847
  const err = error instanceof Error ? error : new Error("Unknown error");
1950
1848
  errors.push(err);
1951
- logger.warn(
1849
+ getLogger().warn(
1952
1850
  `CLI symlink cleanup failed: ${err.message}`
1953
1851
  );
1954
1852
  operations.push({
@@ -1978,7 +1876,7 @@ ${blockerMessage}`);
1978
1876
  mainWorktreePath ?? void 0
1979
1877
  );
1980
1878
  if (deletionResult.deleted) {
1981
- logger.info(`Database branch deleted: ${worktree.branch}`);
1879
+ getLogger().info(`Database branch deleted: ${worktree.branch}`);
1982
1880
  operations.push({
1983
1881
  type: "database",
1984
1882
  success: true,
@@ -1986,7 +1884,7 @@ ${blockerMessage}`);
1986
1884
  deleted: true
1987
1885
  });
1988
1886
  } else if (deletionResult.notFound) {
1989
- logger.debug(`No database branch found for: ${worktree.branch}`);
1887
+ getLogger().debug(`No database branch found for: ${worktree.branch}`);
1990
1888
  operations.push({
1991
1889
  type: "database",
1992
1890
  success: true,
@@ -1994,7 +1892,7 @@ ${blockerMessage}`);
1994
1892
  deleted: false
1995
1893
  });
1996
1894
  } else if (deletionResult.userDeclined) {
1997
- logger.info("Preview database deletion declined by user");
1895
+ getLogger().info("Preview database deletion declined by user");
1998
1896
  operations.push({
1999
1897
  type: "database",
2000
1898
  success: true,
@@ -2004,7 +1902,7 @@ ${blockerMessage}`);
2004
1902
  } else if (!deletionResult.success) {
2005
1903
  const errorMsg = deletionResult.error ?? "Unknown error";
2006
1904
  errors.push(new Error(errorMsg));
2007
- logger.warn(`Database cleanup failed: ${errorMsg}`);
1905
+ getLogger().warn(`Database cleanup failed: ${errorMsg}`);
2008
1906
  operations.push({
2009
1907
  type: "database",
2010
1908
  success: false,
@@ -2015,7 +1913,7 @@ ${blockerMessage}`);
2015
1913
  });
2016
1914
  } else {
2017
1915
  errors.push(new Error("Database cleanup in an unknown state"));
2018
- logger.warn("Database deletion returned unexpected result state");
1916
+ getLogger().warn("Database deletion returned unexpected result state");
2019
1917
  operations.push({
2020
1918
  type: "database",
2021
1919
  success: false,
@@ -2025,7 +1923,7 @@ ${blockerMessage}`);
2025
1923
  }
2026
1924
  } catch (error) {
2027
1925
  errors.push(error instanceof Error ? error : new Error(String(error)));
2028
- logger.warn(
1926
+ getLogger().warn(
2029
1927
  `Unexpected database cleanup exception: ${error instanceof Error ? error.message : String(error)}`
2030
1928
  );
2031
1929
  operations.push({
@@ -2072,19 +1970,19 @@ ${blockerMessage}`);
2072
1970
  * Terminate dev server on specified port
2073
1971
  */
2074
1972
  async terminateDevServer(port) {
2075
- logger.debug(`Checking for dev server on port ${port}`);
1973
+ getLogger().debug(`Checking for dev server on port ${port}`);
2076
1974
  const processInfo = await this.processManager.detectDevServer(port);
2077
1975
  if (!processInfo) {
2078
- logger.debug(`No process found on port ${port}`);
1976
+ getLogger().debug(`No process found on port ${port}`);
2079
1977
  return false;
2080
1978
  }
2081
1979
  if (!processInfo.isDevServer) {
2082
- logger.warn(
1980
+ getLogger().warn(
2083
1981
  `Process on port ${port} (${processInfo.name}) doesn't appear to be a dev server, skipping`
2084
1982
  );
2085
1983
  return false;
2086
1984
  }
2087
- logger.info(`Terminating dev server: ${processInfo.name} (PID: ${processInfo.pid})`);
1985
+ getLogger().info(`Terminating dev server: ${processInfo.name} (PID: ${processInfo.pid})`);
2088
1986
  await this.processManager.terminateProcess(processInfo.pid);
2089
1987
  const isFree = await this.processManager.verifyPortFree(port);
2090
1988
  if (!isFree) {
@@ -2104,23 +2002,75 @@ ${blockerMessage}`);
2104
2002
  if (protectedBranches.includes(branchName)) {
2105
2003
  throw new Error(`Cannot delete protected branch: ${branchName}`);
2106
2004
  }
2005
+ const workingDir = cwd ?? await findMainWorktreePathWithSettings(void 0, this.settingsManager);
2006
+ try {
2007
+ await executeGitCommand(["rev-parse", "--verify", `refs/heads/${branchName}`], {
2008
+ cwd: workingDir
2009
+ });
2010
+ } catch {
2011
+ getLogger().debug(`Branch ${branchName} does not exist, skipping deletion`);
2012
+ return true;
2013
+ }
2107
2014
  if (options.dryRun) {
2108
- logger.info(`[DRY RUN] Would delete branch: ${branchName}`);
2015
+ getLogger().info(`[DRY RUN] Would delete branch: ${branchName}`);
2109
2016
  return true;
2110
2017
  }
2111
2018
  try {
2112
- let workingDir = cwd ?? await findMainWorktreePathWithSettings(void 0, this.settingsManager);
2113
- const deleteFlag = options.force ? "-D" : "-d";
2019
+ let deleteFlag = "-d";
2020
+ let deleteCwd = workingDir;
2021
+ if (options.force) {
2022
+ deleteFlag = "-D";
2023
+ } else if (options.mergeTargetBranch) {
2024
+ const mergeTarget = options.mergeTargetBranch;
2025
+ try {
2026
+ const targetWorktreePath = await findWorktreeForBranch(mergeTarget, workingDir);
2027
+ getLogger().debug(`Running branch delete from worktree where '${mergeTarget}' is checked out: ${targetWorktreePath}`);
2028
+ deleteCwd = targetWorktreePath;
2029
+ } catch {
2030
+ getLogger().debug(`Could not find worktree for branch '${mergeTarget}', falling back to merge check`);
2031
+ const isMerged = await isBranchMergedIntoMain(branchName, mergeTarget, workingDir);
2032
+ if (isMerged) {
2033
+ getLogger().debug(`Branch '${branchName}' verified merged into '${mergeTarget}', using force delete`);
2034
+ deleteFlag = "-D";
2035
+ }
2036
+ }
2037
+ } else if (options.worktreePath) {
2038
+ getLogger().warn("deleteBranch called with worktreePath but no mergeTargetBranch - this may fail if worktree was deleted");
2039
+ try {
2040
+ const mergeTarget = await getMergeTargetBranch(options.worktreePath, {
2041
+ settingsManager: this.settingsManager,
2042
+ metadataManager: this.metadataManager
2043
+ });
2044
+ try {
2045
+ const targetWorktreePath = await findWorktreeForBranch(mergeTarget, workingDir);
2046
+ getLogger().debug(`Running branch delete from worktree where '${mergeTarget}' is checked out: ${targetWorktreePath}`);
2047
+ deleteCwd = targetWorktreePath;
2048
+ } catch {
2049
+ getLogger().debug(`Could not find worktree for branch '${mergeTarget}', falling back to merge check`);
2050
+ const isMerged = await isBranchMergedIntoMain(branchName, mergeTarget, workingDir);
2051
+ if (isMerged) {
2052
+ getLogger().debug(`Branch '${branchName}' verified merged into '${mergeTarget}', using force delete`);
2053
+ deleteFlag = "-D";
2054
+ }
2055
+ }
2056
+ } catch (error) {
2057
+ getLogger().debug(`Could not read merge target from worktreePath: ${error instanceof Error ? error.message : String(error)}`);
2058
+ }
2059
+ }
2114
2060
  await executeGitCommand(["branch", deleteFlag, branchName], {
2115
- cwd: workingDir
2061
+ cwd: deleteCwd
2116
2062
  });
2117
- logger.info(`Branch deleted: ${branchName}`);
2063
+ getLogger().info(`Branch deleted: ${branchName}`);
2118
2064
  return true;
2119
2065
  } catch (error) {
2066
+ const errorMessage = error instanceof Error ? error.message : String(error);
2067
+ if (errorMessage.includes("not found") || errorMessage.includes("does not exist")) {
2068
+ getLogger().debug(`Branch ${branchName} already deleted`);
2069
+ return true;
2070
+ }
2120
2071
  if (options.force) {
2121
2072
  throw error;
2122
2073
  }
2123
- const errorMessage = error instanceof Error ? error.message : String(error);
2124
2074
  if (errorMessage.includes("not fully merged")) {
2125
2075
  throw new Error(
2126
2076
  `Cannot delete unmerged branch '${branchName}'. Use --force to delete anyway.`
@@ -2143,17 +2093,17 @@ ${blockerMessage}`);
2143
2093
  */
2144
2094
  async cleanupDatabase(branchName, worktreePath) {
2145
2095
  if (!this.database) {
2146
- logger.debug("Database manager not available, skipping database cleanup");
2096
+ getLogger().debug("Database manager not available, skipping database cleanup");
2147
2097
  return false;
2148
2098
  }
2149
2099
  try {
2150
- const envFilePath = path5.join(worktreePath, ".env");
2100
+ const envFilePath = path4.join(worktreePath, ".env");
2151
2101
  const shouldCleanup = await this.database.shouldUseDatabaseBranching(envFilePath);
2152
2102
  let cwd;
2153
2103
  try {
2154
2104
  cwd = await findMainWorktreePathWithSettings(worktreePath, this.settingsManager);
2155
2105
  } catch (error) {
2156
- logger.debug(
2106
+ getLogger().debug(
2157
2107
  `Could not find main worktree path, using current directory: ${error instanceof Error ? error.message : String(error)}`
2158
2108
  );
2159
2109
  }
@@ -2165,23 +2115,23 @@ ${blockerMessage}`);
2165
2115
  cwd
2166
2116
  );
2167
2117
  if (result.deleted) {
2168
- logger.info(`Database branch deleted: ${branchName}`);
2118
+ getLogger().info(`Database branch deleted: ${branchName}`);
2169
2119
  return true;
2170
2120
  } else if (result.notFound) {
2171
- logger.debug(`No database branch found for: ${branchName}`);
2121
+ getLogger().debug(`No database branch found for: ${branchName}`);
2172
2122
  return false;
2173
2123
  } else if (result.userDeclined) {
2174
- logger.info("Preview database deletion declined by user");
2124
+ getLogger().info("Preview database deletion declined by user");
2175
2125
  return false;
2176
2126
  } else if (!result.success) {
2177
- logger.warn(`Database cleanup failed: ${result.error ?? "Unknown error"}`);
2127
+ getLogger().warn(`Database cleanup failed: ${result.error ?? "Unknown error"}`);
2178
2128
  return false;
2179
2129
  } else {
2180
- logger.debug("Database deletion returned unexpected result");
2130
+ getLogger().debug("Database deletion returned unexpected result");
2181
2131
  return false;
2182
2132
  }
2183
2133
  } catch (error) {
2184
- logger.warn(
2134
+ getLogger().warn(
2185
2135
  `Unexpected database cleanup error: ${error instanceof Error ? error.message : String(error)}`
2186
2136
  );
2187
2137
  return false;
@@ -2202,8 +2152,13 @@ ${blockerMessage}`);
2202
2152
  /**
2203
2153
  * Validate worktree safety given a worktree object
2204
2154
  * Private method used internally when worktree is already known
2155
+ *
2156
+ * @param worktree - The worktree to validate
2157
+ * @param identifier - The original identifier used (for error messages)
2158
+ * @param checkBranchMerge - Whether to check if branch is merged into main (for branch deletion)
2159
+ * @param checkRemoteBranch - Whether to check if branch exists on remote (for GitHub-PR mode)
2205
2160
  */
2206
- async validateWorktreeSafety(worktree, identifier) {
2161
+ async validateWorktreeSafety(worktree, identifier, checkBranchMerge = false, checkRemoteBranch = false) {
2207
2162
  const warnings = [];
2208
2163
  const blockers = [];
2209
2164
  const isMain = await this.gitWorktree.isMainWorktree(worktree, this.settingsManager);
@@ -2220,6 +2175,44 @@ Please resolve before cleanup - you have some options:
2220
2175
  \u2022 Force cleanup: il cleanup ${identifier} --force (WARNING: will discard changes)`;
2221
2176
  blockers.push(blockerMessage);
2222
2177
  }
2178
+ if ((checkBranchMerge || checkRemoteBranch) && worktree.branch) {
2179
+ const mainBranch = await getMergeTargetBranch(worktree.path, {
2180
+ settingsManager: this.settingsManager,
2181
+ metadataManager: this.metadataManager
2182
+ });
2183
+ const remoteStatus = await checkRemoteBranchStatus(worktree.branch, worktree.path);
2184
+ if (remoteStatus.networkError) {
2185
+ const blockerMessage = `Cannot verify remote branch status due to network error.
2186
+
2187
+ Error: ${remoteStatus.errorMessage ?? "Unknown network error"}
2188
+
2189
+ Unable to determine if branch '${worktree.branch}' is safely backed up.
2190
+ Use --force to proceed without verification.`;
2191
+ blockers.push(blockerMessage);
2192
+ } else if (remoteStatus.exists && remoteStatus.localAhead) {
2193
+ const blockerMessage = `Branch '${worktree.branch}' has unpushed commits that would be lost.
2194
+ The remote branch exists but your local branch is ahead.
2195
+
2196
+ Please resolve before cleanup:
2197
+ \u2022 Push your commits: git push origin ${worktree.branch}
2198
+ \u2022 Force cleanup: il cleanup ${identifier} --force (WARNING: will lose commits)`;
2199
+ blockers.push(blockerMessage);
2200
+ } else if (remoteStatus.exists && !remoteStatus.localAhead) {
2201
+ } else if (!remoteStatus.exists) {
2202
+ const isMerged = await isBranchMergedIntoMain(worktree.branch, mainBranch, worktree.path);
2203
+ if (isMerged) {
2204
+ } else {
2205
+ const blockerMessage = `Branch '${worktree.branch}' has not been pushed to remote and is not merged into '${mainBranch}'.
2206
+ Deleting this branch would result in data loss.
2207
+
2208
+ Please resolve before cleanup - you have some options:
2209
+ \u2022 Push to remote: git push -u origin ${worktree.branch}
2210
+ \u2022 Merge to ${mainBranch}: git checkout ${mainBranch} && git merge ${worktree.branch}
2211
+ \u2022 Force cleanup: il cleanup ${identifier} --force (WARNING: will lose commits)`;
2212
+ blockers.push(blockerMessage);
2213
+ }
2214
+ }
2215
+ }
2223
2216
  return {
2224
2217
  isSafe: blockers.length === 0,
2225
2218
  warnings,
@@ -2282,11 +2275,10 @@ Please resolve before cleanup - you have some options:
2282
2275
  };
2283
2276
 
2284
2277
  export {
2285
- MetadataManager,
2286
2278
  LoomManager,
2287
2279
  EnvironmentManager,
2288
2280
  CLIIsolationManager,
2289
2281
  DatabaseManager,
2290
2282
  ResourceCleanup
2291
2283
  };
2292
- //# sourceMappingURL=chunk-2IJEMXOB.js.map
2284
+ //# sourceMappingURL=chunk-NFVFVYAP.js.map