@iloom/cli 0.3.4 → 0.4.1

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