@iloom/cli 0.5.0 → 0.5.2

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 (172) hide show
  1. package/dist/{BranchNamingService-GCCWB3LK.js → BranchNamingService-B5PVRR7F.js} +4 -4
  2. package/dist/ClaudeContextManager-PQ46VILL.js +14 -0
  3. package/dist/ClaudeService-6OMO552H.js +13 -0
  4. package/dist/GitHubService-S2OGUTDR.js +12 -0
  5. package/dist/{LoomLauncher-4UG2E4CD.js → LoomLauncher-ZHDTPKED.js} +15 -79
  6. package/dist/LoomLauncher-ZHDTPKED.js.map +1 -0
  7. package/dist/MetadataManager-DFI73J3G.js +10 -0
  8. package/dist/PRManager-OCSB2HPT.js +14 -0
  9. package/dist/PromptTemplateManager-5GNF7FCP.js +9 -0
  10. package/dist/{SettingsManager-XPR4TEQL.js → SettingsManager-CNYBGXDT.js} +3 -3
  11. package/dist/SettingsMigrationManager-KZKDG66H.js +10 -0
  12. package/dist/{chunk-OEGECBFS.js → chunk-3PT7RKL5.js} +4 -4
  13. package/dist/{chunk-WUQQNE63.js → chunk-433MOLAU.js} +44 -7
  14. package/dist/chunk-433MOLAU.js.map +1 -0
  15. package/dist/{chunk-LN4H3A6A.js → chunk-53OMUNUN.js} +5 -5
  16. package/dist/{chunk-THF25ICZ.js → chunk-5F6IWWRS.js} +2 -2
  17. package/dist/{chunk-P2ZQ5LKB.js → chunk-5IWU3HXE.js} +5 -5
  18. package/dist/{chunk-QIUJPPJQ.js → chunk-5TXLVEXT.js} +3 -3
  19. package/dist/{chunk-RFUOIUQF.js → chunk-66BMJ25W.js} +13 -7
  20. package/dist/chunk-66BMJ25W.js.map +1 -0
  21. package/dist/{chunk-6UIGZD2N.js → chunk-6MLEBAYZ.js} +2 -2
  22. package/dist/{chunk-VTXCGKV5.js → chunk-7HIRPCKU.js} +14 -6
  23. package/dist/chunk-7HIRPCKU.js.map +1 -0
  24. package/dist/{chunk-UNXRACJ7.js → chunk-7LSSNB7Y.js} +3 -3
  25. package/dist/{chunk-QHA67Q7A.js → chunk-7Q66W4OH.js} +2 -2
  26. package/dist/{chunk-RUC7OULH.js → chunk-AEIMYF4P.js} +6 -8
  27. package/dist/{chunk-RUC7OULH.js.map → chunk-AEIMYF4P.js.map} +1 -1
  28. package/dist/{chunk-MD6HA5IK.js → chunk-B2UO6EYE.js} +2 -2
  29. package/dist/{chunk-YZTDGPFB.js → chunk-CFUWQHCJ.js} +2 -2
  30. package/dist/{chunk-UYWAESOT.js → chunk-F2PWIRV4.js} +3 -3
  31. package/dist/{chunk-PSFVTBM7.js → chunk-FXDYIV3K.js} +2 -2
  32. package/dist/{chunk-CDQEK2WD.js → chunk-FXJKNVZW.js} +5 -5
  33. package/dist/{chunk-3CMGCRB5.js → chunk-HMMO2LDS.js} +3 -3
  34. package/dist/{chunk-OOU3DKNT.js → chunk-IDUICCZY.js} +2 -2
  35. package/dist/{chunk-HABINPX2.js → chunk-J7GHNTYK.js} +67 -11
  36. package/dist/chunk-J7GHNTYK.js.map +1 -0
  37. package/dist/{chunk-DKQ4SUII.js → chunk-K5G5SFWY.js} +2 -2
  38. package/dist/chunk-K5G5SFWY.js.map +1 -0
  39. package/dist/{chunk-KO2FOMHL.js → chunk-LT3SGBR7.js} +2 -2
  40. package/dist/{chunk-VBFDVGAE.js → chunk-LVLRMP7V.js} +2 -2
  41. package/dist/{chunk-RNZMHJK7.js → chunk-N4ZJVATC.js} +3 -3
  42. package/dist/chunk-NXMDEL3F.js +54 -0
  43. package/dist/chunk-NXMDEL3F.js.map +1 -0
  44. package/dist/chunk-O7VL5N6S.js +72 -0
  45. package/dist/chunk-O7VL5N6S.js.map +1 -0
  46. package/dist/{chunk-S65T4O6I.js → chunk-QPS6TZUW.js} +3 -3
  47. package/dist/{chunk-RJKMF6BC.js → chunk-SHVB3EFE.js} +3 -3
  48. package/dist/chunk-VT4PDUYT.js +578 -0
  49. package/dist/chunk-VT4PDUYT.js.map +1 -0
  50. package/dist/{chunk-4YTILIIH.js → chunk-VV66DH6T.js} +8 -8
  51. package/dist/{chunk-GVRO4PWE.js → chunk-XNNXAAZT.js} +6 -6
  52. package/dist/{chunk-AS2IRKLU.js → chunk-YU5HVI6B.js} +2 -2
  53. package/dist/{chunk-T5IIUG4Z.js → chunk-Z5BM4JWB.js} +25 -24
  54. package/dist/chunk-Z5BM4JWB.js.map +1 -0
  55. package/dist/{chunk-SJ2GZ6RF.js → chunk-ZX3GTM7O.js} +2 -2
  56. package/dist/{claude-ACVXNB6N.js → claude-H33OQMXO.js} +4 -6
  57. package/dist/{cleanup-MIDJVSIU.js → cleanup-Y5W3CNUV.js} +20 -22
  58. package/dist/{cleanup-MIDJVSIU.js.map → cleanup-Y5W3CNUV.js.map} +1 -1
  59. package/dist/cli.js +125 -81
  60. package/dist/cli.js.map +1 -1
  61. package/dist/{color-ZPIIUADB.js → color-4TJ4P5EY.js} +5 -3
  62. package/dist/{contribute-RS3DO3WP.js → contribute-K7UXBOML.js} +8 -8
  63. package/dist/{dev-server-ASH7HJVI.js → dev-server-HNBRWGCD.js} +11 -13
  64. package/dist/{dev-server-ASH7HJVI.js.map → dev-server-HNBRWGCD.js.map} +1 -1
  65. package/dist/{feedback-RVIGHBJG.js → feedback-567ZH2O7.js} +16 -14
  66. package/dist/{feedback-RVIGHBJG.js.map → feedback-567ZH2O7.js.map} +1 -1
  67. package/dist/{git-OQAPUPLP.js → git-OV6ADVO7.js} +6 -6
  68. package/dist/{ignite-XJALWFAT.js → ignite-3HB3ZBEW.js} +15 -17
  69. package/dist/{ignite-XJALWFAT.js.map → ignite-3HB3ZBEW.js.map} +1 -1
  70. package/dist/index.d.ts +54 -35
  71. package/dist/index.js +377 -276
  72. package/dist/index.js.map +1 -1
  73. package/dist/init-CMIRHFSR.js +19 -0
  74. package/dist/{installation-detector-6R6YOFVZ.js → installation-detector-VXZOCL6P.js} +3 -3
  75. package/dist/mcp/issue-management-server.js +62 -7
  76. package/dist/mcp/issue-management-server.js.map +1 -1
  77. package/dist/neon-helpers-3KBC4A3Y.js +11 -0
  78. package/dist/{open-KW4NTLXH.js → open-AXE225Z5.js} +11 -13
  79. package/dist/{open-KW4NTLXH.js.map → open-AXE225Z5.js.map} +1 -1
  80. package/dist/{projects-QEAEBAT2.js → projects-GVEMCN5R.js} +4 -4
  81. package/dist/{prompt-A7GGRHSY.js → prompt-3SAZYRUN.js} +3 -3
  82. package/dist/prompts/session-summary-prompt.txt +58 -4
  83. package/dist/{rebase-WZHHE5LU.js → rebase-6UIHMUWS.js} +9 -11
  84. package/dist/{rebase-WZHHE5LU.js.map → rebase-6UIHMUWS.js.map} +1 -1
  85. package/dist/{recap-33NPZ3ZO.js → recap-XTBNMEMO.js} +12 -19
  86. package/dist/recap-XTBNMEMO.js.map +1 -0
  87. package/dist/{remote-73TZ2ADI.js → remote-IJAMOEAP.js} +3 -3
  88. package/dist/{run-HRYQ7TR7.js → run-H375EYRB.js} +11 -13
  89. package/dist/{run-HRYQ7TR7.js.map → run-H375EYRB.js.map} +1 -1
  90. package/dist/{shell-JMU5XTHW.js → shell-33FJCWJQ.js} +9 -11
  91. package/dist/{shell-JMU5XTHW.js.map → shell-33FJCWJQ.js.map} +1 -1
  92. package/dist/{summary-4SSGGH7N.js → summary-JUMOCNLR.js} +14 -15
  93. package/dist/{summary-4SSGGH7N.js.map → summary-JUMOCNLR.js.map} +1 -1
  94. package/dist/{test-git-6SAIRBUD.js → test-git-CO3BA4BV.js} +6 -6
  95. package/dist/{test-prefix-RLVRK5ZD.js → test-prefix-HZYSDQYT.js} +6 -6
  96. package/dist/{test-tabs-3SCJWRKT.js → test-tabs-D3POYOJ5.js} +3 -6
  97. package/dist/{test-tabs-3SCJWRKT.js.map → test-tabs-D3POYOJ5.js.map} +1 -1
  98. package/dist/{test-webserver-VPNLAFZ3.js → test-webserver-YVQD42W6.js} +2 -2
  99. package/dist/{update-LETF5ASC.js → update-5NOHT4SG.js} +4 -4
  100. package/dist/{update-notifier-H55ZK7NU.js → update-notifier-ARA5SPUW.js} +3 -3
  101. package/package.json +1 -1
  102. package/dist/ClaudeContextManager-DQFKIMEP.js +0 -16
  103. package/dist/ClaudeService-CJS32WG2.js +0 -15
  104. package/dist/GitHubService-RPM27GWD.js +0 -12
  105. package/dist/LoomLauncher-4UG2E4CD.js.map +0 -1
  106. package/dist/MetadataManager-WXUVXKUS.js +0 -10
  107. package/dist/PRManager-7DSIMCAD.js +0 -16
  108. package/dist/PromptTemplateManager-72FEOGT6.js +0 -9
  109. package/dist/SettingsMigrationManager-EH3J2TCN.js +0 -10
  110. package/dist/chunk-DKQ4SUII.js.map +0 -1
  111. package/dist/chunk-HABINPX2.js.map +0 -1
  112. package/dist/chunk-RFUOIUQF.js.map +0 -1
  113. package/dist/chunk-T5IIUG4Z.js.map +0 -1
  114. package/dist/chunk-UYVWLISQ.js +0 -113
  115. package/dist/chunk-UYVWLISQ.js.map +0 -1
  116. package/dist/chunk-VAYGNQTE.js +0 -234
  117. package/dist/chunk-VAYGNQTE.js.map +0 -1
  118. package/dist/chunk-VTXCGKV5.js.map +0 -1
  119. package/dist/chunk-WUQQNE63.js.map +0 -1
  120. package/dist/chunk-Z5NXYJIG.js +0 -207
  121. package/dist/chunk-Z5NXYJIG.js.map +0 -1
  122. package/dist/init-F6PFMSU5.js +0 -21
  123. package/dist/neon-helpers-L5CXQ5CT.js +0 -11
  124. package/dist/recap-33NPZ3ZO.js.map +0 -1
  125. /package/dist/{BranchNamingService-GCCWB3LK.js.map → BranchNamingService-B5PVRR7F.js.map} +0 -0
  126. /package/dist/{ClaudeContextManager-DQFKIMEP.js.map → ClaudeContextManager-PQ46VILL.js.map} +0 -0
  127. /package/dist/{ClaudeService-CJS32WG2.js.map → ClaudeService-6OMO552H.js.map} +0 -0
  128. /package/dist/{GitHubService-RPM27GWD.js.map → GitHubService-S2OGUTDR.js.map} +0 -0
  129. /package/dist/{MetadataManager-WXUVXKUS.js.map → MetadataManager-DFI73J3G.js.map} +0 -0
  130. /package/dist/{PRManager-7DSIMCAD.js.map → PRManager-OCSB2HPT.js.map} +0 -0
  131. /package/dist/{PromptTemplateManager-72FEOGT6.js.map → PromptTemplateManager-5GNF7FCP.js.map} +0 -0
  132. /package/dist/{SettingsManager-XPR4TEQL.js.map → SettingsManager-CNYBGXDT.js.map} +0 -0
  133. /package/dist/{SettingsMigrationManager-EH3J2TCN.js.map → SettingsMigrationManager-KZKDG66H.js.map} +0 -0
  134. /package/dist/{chunk-OEGECBFS.js.map → chunk-3PT7RKL5.js.map} +0 -0
  135. /package/dist/{chunk-LN4H3A6A.js.map → chunk-53OMUNUN.js.map} +0 -0
  136. /package/dist/{chunk-THF25ICZ.js.map → chunk-5F6IWWRS.js.map} +0 -0
  137. /package/dist/{chunk-P2ZQ5LKB.js.map → chunk-5IWU3HXE.js.map} +0 -0
  138. /package/dist/{chunk-QIUJPPJQ.js.map → chunk-5TXLVEXT.js.map} +0 -0
  139. /package/dist/{chunk-6UIGZD2N.js.map → chunk-6MLEBAYZ.js.map} +0 -0
  140. /package/dist/{chunk-UNXRACJ7.js.map → chunk-7LSSNB7Y.js.map} +0 -0
  141. /package/dist/{chunk-QHA67Q7A.js.map → chunk-7Q66W4OH.js.map} +0 -0
  142. /package/dist/{chunk-MD6HA5IK.js.map → chunk-B2UO6EYE.js.map} +0 -0
  143. /package/dist/{chunk-YZTDGPFB.js.map → chunk-CFUWQHCJ.js.map} +0 -0
  144. /package/dist/{chunk-UYWAESOT.js.map → chunk-F2PWIRV4.js.map} +0 -0
  145. /package/dist/{chunk-PSFVTBM7.js.map → chunk-FXDYIV3K.js.map} +0 -0
  146. /package/dist/{chunk-CDQEK2WD.js.map → chunk-FXJKNVZW.js.map} +0 -0
  147. /package/dist/{chunk-3CMGCRB5.js.map → chunk-HMMO2LDS.js.map} +0 -0
  148. /package/dist/{chunk-OOU3DKNT.js.map → chunk-IDUICCZY.js.map} +0 -0
  149. /package/dist/{chunk-KO2FOMHL.js.map → chunk-LT3SGBR7.js.map} +0 -0
  150. /package/dist/{chunk-VBFDVGAE.js.map → chunk-LVLRMP7V.js.map} +0 -0
  151. /package/dist/{chunk-RNZMHJK7.js.map → chunk-N4ZJVATC.js.map} +0 -0
  152. /package/dist/{chunk-S65T4O6I.js.map → chunk-QPS6TZUW.js.map} +0 -0
  153. /package/dist/{chunk-RJKMF6BC.js.map → chunk-SHVB3EFE.js.map} +0 -0
  154. /package/dist/{chunk-4YTILIIH.js.map → chunk-VV66DH6T.js.map} +0 -0
  155. /package/dist/{chunk-GVRO4PWE.js.map → chunk-XNNXAAZT.js.map} +0 -0
  156. /package/dist/{chunk-AS2IRKLU.js.map → chunk-YU5HVI6B.js.map} +0 -0
  157. /package/dist/{chunk-SJ2GZ6RF.js.map → chunk-ZX3GTM7O.js.map} +0 -0
  158. /package/dist/{claude-ACVXNB6N.js.map → claude-H33OQMXO.js.map} +0 -0
  159. /package/dist/{color-ZPIIUADB.js.map → color-4TJ4P5EY.js.map} +0 -0
  160. /package/dist/{contribute-RS3DO3WP.js.map → contribute-K7UXBOML.js.map} +0 -0
  161. /package/dist/{git-OQAPUPLP.js.map → git-OV6ADVO7.js.map} +0 -0
  162. /package/dist/{init-F6PFMSU5.js.map → init-CMIRHFSR.js.map} +0 -0
  163. /package/dist/{installation-detector-6R6YOFVZ.js.map → installation-detector-VXZOCL6P.js.map} +0 -0
  164. /package/dist/{neon-helpers-L5CXQ5CT.js.map → neon-helpers-3KBC4A3Y.js.map} +0 -0
  165. /package/dist/{projects-QEAEBAT2.js.map → projects-GVEMCN5R.js.map} +0 -0
  166. /package/dist/{prompt-A7GGRHSY.js.map → prompt-3SAZYRUN.js.map} +0 -0
  167. /package/dist/{remote-73TZ2ADI.js.map → remote-IJAMOEAP.js.map} +0 -0
  168. /package/dist/{test-git-6SAIRBUD.js.map → test-git-CO3BA4BV.js.map} +0 -0
  169. /package/dist/{test-prefix-RLVRK5ZD.js.map → test-prefix-HZYSDQYT.js.map} +0 -0
  170. /package/dist/{test-webserver-VPNLAFZ3.js.map → test-webserver-YVQD42W6.js.map} +0 -0
  171. /package/dist/{update-LETF5ASC.js.map → update-5NOHT4SG.js.map} +0 -0
  172. /package/dist/{update-notifier-H55ZK7NU.js.map → update-notifier-ARA5SPUW.js.map} +0 -0
@@ -1,234 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- buildEnvSourceCommands
4
- } from "./chunk-Z5NXYJIG.js";
5
-
6
- // src/utils/terminal.ts
7
- import { execa } from "execa";
8
- import { existsSync } from "fs";
9
- function detectPlatform() {
10
- const platform = process.platform;
11
- if (platform === "darwin") return "darwin";
12
- if (platform === "linux") return "linux";
13
- if (platform === "win32") return "win32";
14
- return "unsupported";
15
- }
16
- async function detectITerm2() {
17
- const platform = detectPlatform();
18
- if (platform !== "darwin") return false;
19
- return existsSync("/Applications/iTerm.app");
20
- }
21
- async function openTerminalWindow(options) {
22
- const platform = detectPlatform();
23
- if (platform !== "darwin") {
24
- throw new Error(
25
- `Terminal window launching not yet supported on ${platform}. Currently only macOS is supported.`
26
- );
27
- }
28
- const hasITerm2 = await detectITerm2();
29
- const applescript = hasITerm2 ? await buildITerm2SingleTabScript(options) : await buildAppleScript(options);
30
- try {
31
- await execa("osascript", ["-e", applescript]);
32
- if (!hasITerm2) {
33
- await execa("osascript", ["-e", 'tell application "Terminal" to activate']);
34
- }
35
- } catch (error) {
36
- throw new Error(
37
- `Failed to open terminal window: ${error instanceof Error ? error.message : "Unknown error"}`
38
- );
39
- }
40
- }
41
- async function buildAppleScript(options) {
42
- const {
43
- workspacePath,
44
- command,
45
- backgroundColor,
46
- port,
47
- includeEnvSetup,
48
- includePortExport
49
- } = options;
50
- const commands = [];
51
- if (workspacePath) {
52
- commands.push(`cd '${escapePathForAppleScript(workspacePath)}'`);
53
- }
54
- if (includeEnvSetup && workspacePath) {
55
- const sourceCommands = await buildEnvSourceCommands(
56
- workspacePath,
57
- async (p) => existsSync(p)
58
- );
59
- commands.push(...sourceCommands);
60
- }
61
- if (includePortExport && port !== void 0) {
62
- commands.push(`export PORT=${port}`);
63
- }
64
- if (command) {
65
- commands.push(command);
66
- }
67
- const fullCommand = commands.join(" && ");
68
- const historyFreeCommand = ` ${fullCommand}`;
69
- let script = `tell application "Terminal"
70
- `;
71
- script += ` set newTab to do script "${escapeForAppleScript(historyFreeCommand)}"
72
- `;
73
- if (backgroundColor) {
74
- const { r, g, b } = backgroundColor;
75
- script += ` set background color of newTab to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
76
- `;
77
- }
78
- script += `end tell`;
79
- return script;
80
- }
81
- function escapePathForAppleScript(path) {
82
- return path.replace(/'/g, "'\\''");
83
- }
84
- function escapeForAppleScript(command) {
85
- return command.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
86
- }
87
- async function buildITerm2SingleTabScript(options) {
88
- const command = await buildCommandSequence(options);
89
- let script = 'tell application id "com.googlecode.iterm2"\n';
90
- script += " create window with default profile\n";
91
- script += " set s1 to current session of current window\n\n";
92
- if (options.backgroundColor) {
93
- const { r, g, b } = options.backgroundColor;
94
- script += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
95
- `;
96
- }
97
- script += ` tell s1 to write text "${escapeForAppleScript(command)}"
98
-
99
- `;
100
- if (options.title) {
101
- script += ` set name of s1 to "${escapeForAppleScript(options.title)}"
102
-
103
- `;
104
- }
105
- script += " activate\n";
106
- script += "end tell";
107
- return script;
108
- }
109
- async function buildCommandSequence(options) {
110
- const {
111
- workspacePath,
112
- command,
113
- port,
114
- includeEnvSetup,
115
- includePortExport
116
- } = options;
117
- const commands = [];
118
- if (workspacePath) {
119
- commands.push(`cd '${escapePathForAppleScript(workspacePath)}'`);
120
- }
121
- if (includeEnvSetup && workspacePath) {
122
- const sourceCommands = await buildEnvSourceCommands(
123
- workspacePath,
124
- async (p) => existsSync(p)
125
- );
126
- commands.push(...sourceCommands);
127
- }
128
- if (includePortExport && port !== void 0) {
129
- commands.push(`export PORT=${port}`);
130
- }
131
- if (command) {
132
- commands.push(command);
133
- }
134
- const fullCommand = commands.join(" && ");
135
- return ` ${fullCommand}`;
136
- }
137
- async function buildITerm2MultiTabScript(optionsArray) {
138
- if (optionsArray.length < 2) {
139
- throw new Error("buildITerm2MultiTabScript requires at least 2 terminal options");
140
- }
141
- let script = 'tell application id "com.googlecode.iterm2"\n';
142
- script += " create window with default profile\n";
143
- script += " set newWindow to current window\n";
144
- const options1 = optionsArray[0];
145
- if (!options1) {
146
- throw new Error("First terminal option is undefined");
147
- }
148
- const command1 = await buildCommandSequence(options1);
149
- script += " set s1 to current session of newWindow\n\n";
150
- if (options1.backgroundColor) {
151
- const { r, g, b } = options1.backgroundColor;
152
- script += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
153
- `;
154
- }
155
- script += ` tell s1 to write text "${escapeForAppleScript(command1)}"
156
-
157
- `;
158
- if (options1.title) {
159
- script += ` set name of s1 to "${escapeForAppleScript(options1.title)}"
160
-
161
- `;
162
- }
163
- for (let i = 1; i < optionsArray.length; i++) {
164
- const options = optionsArray[i];
165
- if (!options) {
166
- throw new Error(`Terminal option at index ${i} is undefined`);
167
- }
168
- const command = await buildCommandSequence(options);
169
- const sessionVar = `s${i + 1}`;
170
- script += " tell newWindow\n";
171
- script += ` set newTab${i} to (create tab with default profile)
172
- `;
173
- script += " end tell\n";
174
- script += ` set ${sessionVar} to current session of newTab${i}
175
-
176
- `;
177
- if (options.backgroundColor) {
178
- const { r, g, b } = options.backgroundColor;
179
- script += ` set background color of ${sessionVar} to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
180
- `;
181
- }
182
- script += ` tell ${sessionVar} to write text "${escapeForAppleScript(command)}"
183
-
184
- `;
185
- if (options.title) {
186
- script += ` set name of ${sessionVar} to "${escapeForAppleScript(options.title)}"
187
-
188
- `;
189
- }
190
- }
191
- script += " activate\n";
192
- script += "end tell";
193
- return script;
194
- }
195
- async function openMultipleTerminalWindows(optionsArray) {
196
- if (optionsArray.length < 2) {
197
- throw new Error("openMultipleTerminalWindows requires at least 2 terminal options. Use openTerminalWindow for single terminal.");
198
- }
199
- const platform = detectPlatform();
200
- if (platform !== "darwin") {
201
- throw new Error(
202
- `Terminal window launching not yet supported on ${platform}. Currently only macOS is supported.`
203
- );
204
- }
205
- const hasITerm2 = await detectITerm2();
206
- if (hasITerm2) {
207
- const applescript = await buildITerm2MultiTabScript(optionsArray);
208
- try {
209
- await execa("osascript", ["-e", applescript]);
210
- } catch (error) {
211
- throw new Error(
212
- `Failed to open iTerm2 window: ${error instanceof Error ? error.message : "Unknown error"}`
213
- );
214
- }
215
- } else {
216
- for (let i = 0; i < optionsArray.length; i++) {
217
- const options = optionsArray[i];
218
- if (!options) {
219
- throw new Error(`Terminal option at index ${i} is undefined`);
220
- }
221
- await openTerminalWindow(options);
222
- if (i < optionsArray.length - 1) {
223
- await new Promise((resolve) => setTimeout(resolve, 1e3));
224
- }
225
- }
226
- }
227
- }
228
-
229
- export {
230
- detectITerm2,
231
- openTerminalWindow,
232
- openMultipleTerminalWindows
233
- };
234
- //# sourceMappingURL=chunk-VAYGNQTE.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/terminal.ts"],"sourcesContent":["import { execa } from 'execa'\nimport { existsSync } from 'node:fs'\nimport type { Platform } from '../types/index.js'\nimport { buildEnvSourceCommands } from './env.js'\n\nexport interface TerminalWindowOptions {\n\tworkspacePath?: string\n\tcommand?: string\n\tbackgroundColor?: { r: number; g: number; b: number }\n\tport?: number\n\tincludeEnvSetup?: boolean // source .env\n\tincludePortExport?: boolean // export PORT=<port>\n\ttitle?: string // Terminal tab title\n}\n\n/**\n * Detect current platform\n */\nexport function detectPlatform(): Platform {\n\tconst platform = process.platform\n\tif (platform === 'darwin') return 'darwin'\n\tif (platform === 'linux') return 'linux'\n\tif (platform === 'win32') return 'win32'\n\treturn 'unsupported'\n}\n\n/**\n * Detect if iTerm2 is installed on macOS\n * Returns false on non-macOS platforms\n */\nexport async function detectITerm2(): Promise<boolean> {\n\tconst platform = detectPlatform()\n\tif (platform !== 'darwin') return false\n\n\t// Check if iTerm.app exists at standard location\n\treturn existsSync('/Applications/iTerm.app')\n}\n\n/**\n * Open new terminal window with specified options\n * Currently supports macOS only\n */\nexport async function openTerminalWindow(\n\toptions: TerminalWindowOptions\n): Promise<void> {\n\tconst platform = detectPlatform()\n\n\tif (platform !== 'darwin') {\n\t\tthrow new Error(\n\t\t\t`Terminal window launching not yet supported on ${platform}. ` +\n\t\t\t\t`Currently only macOS is supported.`\n\t\t)\n\t}\n\n\t// Detect if iTerm2 is available\n\tconst hasITerm2 = await detectITerm2()\n\n\t// Build appropriate AppleScript based on terminal availability\n\tconst applescript = hasITerm2\n\t\t? await buildITerm2SingleTabScript(options)\n\t\t: await buildAppleScript(options)\n\n\ttry {\n\t\tawait execa('osascript', ['-e', applescript])\n\n\t\t// Activate the appropriate terminal application (only needed for Terminal.app)\n\t\t// iTerm2 script includes its own activation\n\t\tif (!hasITerm2) {\n\t\t\tawait execa('osascript', ['-e', 'tell application \"Terminal\" to activate'])\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to open terminal window: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t)\n\t}\n}\n\n/**\n * Build AppleScript for macOS Terminal.app\n */\nasync function buildAppleScript(options: TerminalWindowOptions): Promise<string> {\n\tconst {\n\t\tworkspacePath,\n\t\tcommand,\n\t\tbackgroundColor,\n\t\tport,\n\t\tincludeEnvSetup,\n\t\tincludePortExport,\n\t} = options\n\n\t// Build command sequence\n\tconst commands: string[] = []\n\n\t// Navigate to workspace\n\tif (workspacePath) {\n\t\tcommands.push(`cd '${escapePathForAppleScript(workspacePath)}'`)\n\t}\n\n\t// Source all dotenv-flow files\n\tif (includeEnvSetup && workspacePath) {\n\t\tconst sourceCommands = await buildEnvSourceCommands(\n\t\t\tworkspacePath,\n\t\t\tasync (p) => existsSync(p)\n\t\t)\n\t\tcommands.push(...sourceCommands)\n\t}\n\n\t// Export PORT variable\n\tif (includePortExport && port !== undefined) {\n\t\tcommands.push(`export PORT=${port}`)\n\t}\n\n\t// Add custom command\n\tif (command) {\n\t\tcommands.push(command)\n\t}\n\n\t// Join with &&\n\tconst fullCommand = commands.join(' && ')\n\n\t// Prefix with space to prevent shell history pollution\n\t// Most shells (bash/zsh) ignore commands starting with space when HISTCONTROL=ignorespace\n\tconst historyFreeCommand = ` ${fullCommand}`\n\n\t// Build AppleScript\n\tlet script = `tell application \"Terminal\"\\n`\n\tscript += ` set newTab to do script \"${escapeForAppleScript(historyFreeCommand)}\"\\n`\n\n\t// Apply background color if provided\n\tif (backgroundColor) {\n\t\tconst { r, g, b } = backgroundColor\n\t\t// Convert 8-bit RGB (0-255) to 16-bit RGB (0-65535)\n\t\tscript += ` set background color of newTab to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\tscript += `end tell`\n\n\treturn script\n}\n\n/**\n * Escape path for AppleScript string\n * Single quotes in path need special escaping\n */\nfunction escapePathForAppleScript(path: string): string {\n\t// Replace single quote with '\\''\n\treturn path.replace(/'/g, \"'\\\\''\")\n}\n\n/**\n * Escape command for AppleScript do script\n * Must handle double quotes and backslashes\n */\nfunction escapeForAppleScript(command: string): string {\n\treturn (\n\t\tcommand\n\t\t\t.replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n\t\t\t.replace(/\"/g, '\\\\\"') // Escape double quotes\n\t)\n}\n\n/**\n * Build iTerm2 AppleScript for single tab\n */\nasync function buildITerm2SingleTabScript(options: TerminalWindowOptions): Promise<string> {\n\tconst command = await buildCommandSequence(options)\n\n\tlet script = 'tell application id \"com.googlecode.iterm2\"\\n'\n\tscript += ' create window with default profile\\n'\n\tscript += ' set s1 to current session of current window\\n\\n'\n\n\t// Set background color\n\tif (options.backgroundColor) {\n\t\tconst { r, g, b } = options.backgroundColor\n\t\tscript += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\t// Execute command\n\tscript += ` tell s1 to write text \"${escapeForAppleScript(command)}\"\\n\\n`\n\n\t// Set session name (tab title)\n\tif (options.title) {\n\t\tscript += ` set name of s1 to \"${escapeForAppleScript(options.title)}\"\\n\\n`\n\t}\n\n\t// Activate iTerm2\n\tscript += ' activate\\n'\n\tscript += 'end tell'\n\n\treturn script\n}\n\n/**\n * Build command sequence for terminal\n */\nasync function buildCommandSequence(options: TerminalWindowOptions): Promise<string> {\n\tconst {\n\t\tworkspacePath,\n\t\tcommand,\n\t\tport,\n\t\tincludeEnvSetup,\n\t\tincludePortExport,\n\t} = options\n\n\tconst commands: string[] = []\n\n\t// Navigate to workspace\n\tif (workspacePath) {\n\t\tcommands.push(`cd '${escapePathForAppleScript(workspacePath)}'`)\n\t}\n\n\t// Source all dotenv-flow files\n\tif (includeEnvSetup && workspacePath) {\n\t\tconst sourceCommands = await buildEnvSourceCommands(\n\t\t\tworkspacePath,\n\t\t\tasync (p) => existsSync(p)\n\t\t)\n\t\tcommands.push(...sourceCommands)\n\t}\n\n\t// Export PORT variable\n\tif (includePortExport && port !== undefined) {\n\t\tcommands.push(`export PORT=${port}`)\n\t}\n\n\t// Add custom command\n\tif (command) {\n\t\tcommands.push(command)\n\t}\n\n\t// Join with &&\n\tconst fullCommand = commands.join(' && ')\n\n\t// Prefix with space to prevent shell history pollution\n\treturn ` ${fullCommand}`\n}\n\n/**\n * Build iTerm2 AppleScript for multiple tabs (2+) in single window\n */\nasync function buildITerm2MultiTabScript(\n\toptionsArray: TerminalWindowOptions[]\n): Promise<string> {\n\tif (optionsArray.length < 2) {\n\t\tthrow new Error('buildITerm2MultiTabScript requires at least 2 terminal options')\n\t}\n\n\tlet script = 'tell application id \"com.googlecode.iterm2\"\\n'\n\tscript += ' create window with default profile\\n'\n\tscript += ' set newWindow to current window\\n'\n\n\t// First tab\n\tconst options1 = optionsArray[0]\n\tif (!options1) {\n\t\tthrow new Error('First terminal option is undefined')\n\t}\n\tconst command1 = await buildCommandSequence(options1)\n\n\tscript += ' set s1 to current session of newWindow\\n\\n'\n\n\t// Set background color for first tab\n\tif (options1.backgroundColor) {\n\t\tconst { r, g, b } = options1.backgroundColor\n\t\tscript += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\t// Execute command in first tab\n\tscript += ` tell s1 to write text \"${escapeForAppleScript(command1)}\"\\n\\n`\n\n\t// Set tab title for first tab\n\tif (options1.title) {\n\t\tscript += ` set name of s1 to \"${escapeForAppleScript(options1.title)}\"\\n\\n`\n\t}\n\n\t// Subsequent tabs (2, 3, ...)\n\tfor (let i = 1; i < optionsArray.length; i++) {\n\t\tconst options = optionsArray[i]\n\t\tif (!options) {\n\t\t\tthrow new Error(`Terminal option at index ${i} is undefined`)\n\t\t}\n\t\tconst command = await buildCommandSequence(options)\n\t\tconst sessionVar = `s${i + 1}`\n\n\t\t// Create tab\n\t\tscript += ' tell newWindow\\n'\n\t\tscript += ` set newTab${i} to (create tab with default profile)\\n`\n\t\tscript += ' end tell\\n'\n\t\tscript += ` set ${sessionVar} to current session of newTab${i}\\n\\n`\n\n\t\t// Set background color\n\t\tif (options.backgroundColor) {\n\t\t\tconst { r, g, b } = options.backgroundColor\n\t\t\tscript += ` set background color of ${sessionVar} to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t\t}\n\n\t\t// Execute command\n\t\tscript += ` tell ${sessionVar} to write text \"${escapeForAppleScript(command)}\"\\n\\n`\n\n\t\t// Set tab title\n\t\tif (options.title) {\n\t\t\tscript += ` set name of ${sessionVar} to \"${escapeForAppleScript(options.title)}\"\\n\\n`\n\t\t}\n\t}\n\n\t// Activate iTerm2\n\tscript += ' activate\\n'\n\tscript += 'end tell'\n\n\treturn script\n}\n\n/**\n * Open multiple terminal windows/tabs (2+) with specified options\n * If iTerm2 is available on macOS, creates single window with multiple tabs\n * Otherwise falls back to multiple separate Terminal.app windows\n */\nexport async function openMultipleTerminalWindows(\n\toptionsArray: TerminalWindowOptions[]\n): Promise<void> {\n\tif (optionsArray.length < 2) {\n\t\tthrow new Error('openMultipleTerminalWindows requires at least 2 terminal options. Use openTerminalWindow for single terminal.')\n\t}\n\n\tconst platform = detectPlatform()\n\n\tif (platform !== 'darwin') {\n\t\tthrow new Error(\n\t\t\t`Terminal window launching not yet supported on ${platform}. ` +\n\t\t\t\t`Currently only macOS is supported.`\n\t\t)\n\t}\n\n\t// Detect if iTerm2 is available\n\tconst hasITerm2 = await detectITerm2()\n\n\tif (hasITerm2) {\n\t\t// Use iTerm2 with multiple tabs in single window\n\t\tconst applescript = await buildITerm2MultiTabScript(optionsArray)\n\n\t\ttry {\n\t\t\tawait execa('osascript', ['-e', applescript])\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to open iTerm2 window: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t} else {\n\t\t// Fall back to multiple Terminal.app windows\n\t\tfor (let i = 0; i < optionsArray.length; i++) {\n\t\t\tconst options = optionsArray[i]\n\t\t\tif (!options) {\n\t\t\t\tthrow new Error(`Terminal option at index ${i} is undefined`)\n\t\t\t}\n\t\t\tawait openTerminalWindow(options)\n\n\t\t\t// Brief pause between terminals (except after last one)\n\t\t\tif (i < optionsArray.length - 1) {\n\t\t\t\t// eslint-disable-next-line no-undef\n\t\t\t\tawait new Promise<void>((resolve) => setTimeout(resolve, 1000))\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Open dual terminal windows/tabs with specified options\n * If iTerm2 is available on macOS, creates single window with two tabs\n * Otherwise falls back to two separate Terminal.app windows\n */\nexport async function openDualTerminalWindow(\n\toptions1: TerminalWindowOptions,\n\toptions2: TerminalWindowOptions\n): Promise<void> {\n\t// Delegate to openMultipleTerminalWindows for consistency\n\tawait openMultipleTerminalWindows([options1, options2])\n}\n"],"mappings":";;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAiBpB,SAAS,iBAA2B;AAC1C,QAAM,WAAW,QAAQ;AACzB,MAAI,aAAa,SAAU,QAAO;AAClC,MAAI,aAAa,QAAS,QAAO;AACjC,MAAI,aAAa,QAAS,QAAO;AACjC,SAAO;AACR;AAMA,eAAsB,eAAiC;AACtD,QAAM,WAAW,eAAe;AAChC,MAAI,aAAa,SAAU,QAAO;AAGlC,SAAO,WAAW,yBAAyB;AAC5C;AAMA,eAAsB,mBACrB,SACgB;AAChB,QAAM,WAAW,eAAe;AAEhC,MAAI,aAAa,UAAU;AAC1B,UAAM,IAAI;AAAA,MACT,kDAAkD,QAAQ;AAAA,IAE3D;AAAA,EACD;AAGA,QAAM,YAAY,MAAM,aAAa;AAGrC,QAAM,cAAc,YACjB,MAAM,2BAA2B,OAAO,IACxC,MAAM,iBAAiB,OAAO;AAEjC,MAAI;AACH,UAAM,MAAM,aAAa,CAAC,MAAM,WAAW,CAAC;AAI5C,QAAI,CAAC,WAAW;AACf,YAAM,MAAM,aAAa,CAAC,MAAM,yCAAyC,CAAC;AAAA,IAC3E;AAAA,EACD,SAAS,OAAO;AACf,UAAM,IAAI;AAAA,MACT,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IAC5F;AAAA,EACD;AACD;AAKA,eAAe,iBAAiB,SAAiD;AAChF,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,IAAI;AAGJ,QAAM,WAAqB,CAAC;AAG5B,MAAI,eAAe;AAClB,aAAS,KAAK,OAAO,yBAAyB,aAAa,CAAC,GAAG;AAAA,EAChE;AAGA,MAAI,mBAAmB,eAAe;AACrC,UAAM,iBAAiB,MAAM;AAAA,MAC5B;AAAA,MACA,OAAO,MAAM,WAAW,CAAC;AAAA,IAC1B;AACA,aAAS,KAAK,GAAG,cAAc;AAAA,EAChC;AAGA,MAAI,qBAAqB,SAAS,QAAW;AAC5C,aAAS,KAAK,eAAe,IAAI,EAAE;AAAA,EACpC;AAGA,MAAI,SAAS;AACZ,aAAS,KAAK,OAAO;AAAA,EACtB;AAGA,QAAM,cAAc,SAAS,KAAK,MAAM;AAIxC,QAAM,qBAAqB,IAAI,WAAW;AAG1C,MAAI,SAAS;AAAA;AACb,YAAU,8BAA8B,qBAAqB,kBAAkB,CAAC;AAAA;AAGhF,MAAI,iBAAiB;AACpB,UAAM,EAAE,GAAG,GAAG,EAAE,IAAI;AAEpB,cAAU,wCAAwC,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC;AAAA;AAAA,EACtH;AAEA,YAAU;AAEV,SAAO;AACR;AAMA,SAAS,yBAAyB,MAAsB;AAEvD,SAAO,KAAK,QAAQ,MAAM,OAAO;AAClC;AAMA,SAAS,qBAAqB,SAAyB;AACtD,SACC,QACE,QAAQ,OAAO,MAAM,EACrB,QAAQ,MAAM,KAAK;AAEvB;AAKA,eAAe,2BAA2B,SAAiD;AAC1F,QAAM,UAAU,MAAM,qBAAqB,OAAO;AAElD,MAAI,SAAS;AACb,YAAU;AACV,YAAU;AAGV,MAAI,QAAQ,iBAAiB;AAC5B,UAAM,EAAE,GAAG,GAAG,EAAE,IAAI,QAAQ;AAC5B,cAAU,oCAAoC,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC;AAAA;AAAA,EAClH;AAGA,YAAU,4BAA4B,qBAAqB,OAAO,CAAC;AAAA;AAAA;AAGnE,MAAI,QAAQ,OAAO;AAClB,cAAU,wBAAwB,qBAAqB,QAAQ,KAAK,CAAC;AAAA;AAAA;AAAA,EACtE;AAGA,YAAU;AACV,YAAU;AAEV,SAAO;AACR;AAKA,eAAe,qBAAqB,SAAiD;AACpF,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,IAAI;AAEJ,QAAM,WAAqB,CAAC;AAG5B,MAAI,eAAe;AAClB,aAAS,KAAK,OAAO,yBAAyB,aAAa,CAAC,GAAG;AAAA,EAChE;AAGA,MAAI,mBAAmB,eAAe;AACrC,UAAM,iBAAiB,MAAM;AAAA,MAC5B;AAAA,MACA,OAAO,MAAM,WAAW,CAAC;AAAA,IAC1B;AACA,aAAS,KAAK,GAAG,cAAc;AAAA,EAChC;AAGA,MAAI,qBAAqB,SAAS,QAAW;AAC5C,aAAS,KAAK,eAAe,IAAI,EAAE;AAAA,EACpC;AAGA,MAAI,SAAS;AACZ,aAAS,KAAK,OAAO;AAAA,EACtB;AAGA,QAAM,cAAc,SAAS,KAAK,MAAM;AAGxC,SAAO,IAAI,WAAW;AACvB;AAKA,eAAe,0BACd,cACkB;AAClB,MAAI,aAAa,SAAS,GAAG;AAC5B,UAAM,IAAI,MAAM,gEAAgE;AAAA,EACjF;AAEA,MAAI,SAAS;AACb,YAAU;AACV,YAAU;AAGV,QAAM,WAAW,aAAa,CAAC;AAC/B,MAAI,CAAC,UAAU;AACd,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACrD;AACA,QAAM,WAAW,MAAM,qBAAqB,QAAQ;AAEpD,YAAU;AAGV,MAAI,SAAS,iBAAiB;AAC7B,UAAM,EAAE,GAAG,GAAG,EAAE,IAAI,SAAS;AAC7B,cAAU,oCAAoC,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC;AAAA;AAAA,EAClH;AAGA,YAAU,4BAA4B,qBAAqB,QAAQ,CAAC;AAAA;AAAA;AAGpE,MAAI,SAAS,OAAO;AACnB,cAAU,wBAAwB,qBAAqB,SAAS,KAAK,CAAC;AAAA;AAAA;AAAA,EACvE;AAGA,WAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC7C,UAAM,UAAU,aAAa,CAAC;AAC9B,QAAI,CAAC,SAAS;AACb,YAAM,IAAI,MAAM,4BAA4B,CAAC,eAAe;AAAA,IAC7D;AACA,UAAM,UAAU,MAAM,qBAAqB,OAAO;AAClD,UAAM,aAAa,IAAI,IAAI,CAAC;AAG5B,cAAU;AACV,cAAU,iBAAiB,CAAC;AAAA;AAC5B,cAAU;AACV,cAAU,SAAS,UAAU,gCAAgC,CAAC;AAAA;AAAA;AAG9D,QAAI,QAAQ,iBAAiB;AAC5B,YAAM,EAAE,GAAG,GAAG,EAAE,IAAI,QAAQ;AAC5B,gBAAU,6BAA6B,UAAU,QAAQ,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC;AAAA;AAAA,IAC7H;AAGA,cAAU,UAAU,UAAU,mBAAmB,qBAAqB,OAAO,CAAC;AAAA;AAAA;AAG9E,QAAI,QAAQ,OAAO;AAClB,gBAAU,iBAAiB,UAAU,QAAQ,qBAAqB,QAAQ,KAAK,CAAC;AAAA;AAAA;AAAA,IACjF;AAAA,EACD;AAGA,YAAU;AACV,YAAU;AAEV,SAAO;AACR;AAOA,eAAsB,4BACrB,cACgB;AAChB,MAAI,aAAa,SAAS,GAAG;AAC5B,UAAM,IAAI,MAAM,+GAA+G;AAAA,EAChI;AAEA,QAAM,WAAW,eAAe;AAEhC,MAAI,aAAa,UAAU;AAC1B,UAAM,IAAI;AAAA,MACT,kDAAkD,QAAQ;AAAA,IAE3D;AAAA,EACD;AAGA,QAAM,YAAY,MAAM,aAAa;AAErC,MAAI,WAAW;AAEd,UAAM,cAAc,MAAM,0BAA0B,YAAY;AAEhE,QAAI;AACH,YAAM,MAAM,aAAa,CAAC,MAAM,WAAW,CAAC;AAAA,IAC7C,SAAS,OAAO;AACf,YAAM,IAAI;AAAA,QACT,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MAC1F;AAAA,IACD;AAAA,EACD,OAAO;AAEN,aAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC7C,YAAM,UAAU,aAAa,CAAC;AAC9B,UAAI,CAAC,SAAS;AACb,cAAM,IAAI,MAAM,4BAA4B,CAAC,eAAe;AAAA,MAC7D;AACA,YAAM,mBAAmB,OAAO;AAGhC,UAAI,IAAI,aAAa,SAAS,GAAG;AAEhC,cAAM,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,GAAI,CAAC;AAAA,MAC/D;AAAA,IACD;AAAA,EACD;AACD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/IssueEnhancementService.ts","../src/utils/text.ts"],"sourcesContent":["import type { IssueTracker } from './IssueTracker.js'\nimport type { AgentManager } from './AgentManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport { launchClaude } from '../utils/claude.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { waitForKeypress } from '../utils/prompt.js'\nimport { getLogger } from '../utils/logger-context.js'\n\n/**\n * Service for enhancing and creating issues with AI assistance.\n * Extracts reusable issue enhancement logic from StartCommand.\n */\nexport class IssueEnhancementService {\n\tconstructor(\n\t\tprivate issueTrackerService: IssueTracker,\n\t\tprivate agentManager: AgentManager,\n\t\tprivate settingsManager: SettingsManager\n\t) {\n\t\t// No-op - logger now uses AsyncLocalStorage context\n\t}\n\n\t/**\n\t * Expose issue tracker for provider checks\n\t */\n\tpublic get issueTracker(): IssueTracker {\n\t\treturn this.issueTrackerService\n\t}\n\n\t/**\n\t * Validates that a description meets minimum requirements.\n\t * Requirements: >30 characters AND >2 spaces\n\t */\n\tpublic validateDescription(description: string): boolean {\n\t\tconst trimmedDescription = description.trim()\n\t\tconst spaceCount = (trimmedDescription.match(/ /g) ?? []).length\n\n\t\treturn trimmedDescription.length > 30 && spaceCount > 2\n\t}\n\n\t/**\n\t * Enhances a description using Claude Code in headless mode.\n\t * Falls back to original description if enhancement fails.\n\t */\n\tpublic async enhanceDescription(description: string): Promise<string> {\n\t\ttry {\n\t\t\tgetLogger().info('Enhancing description with Claude Code. This may take a moment...')\n\n\t\t\t// Load agent configurations\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst loadedAgents = await this.agentManager.loadAgents(settings)\n\t\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t\t// Call Claude in headless mode with issue enhancer agent\n\t\t\tconst prompt = `@agent-iloom-issue-enhancer\n\nTASK: Enhance the following issue description for GitHub.\n\nINPUT:\n${description}\n\nOUTPUT REQUIREMENTS:\n- Return ONLY the enhanced description markdown text\n- NO meta-commentary (no \"Here is...\", \"The enhanced...\", \"I have...\", etc)\n- NO code block markers (\\`\\`\\`)\n- NO conversational framing or acknowledgments\n- NO explanations of your work\n- Start your response immediately with the enhanced content\n\nYour response should be the raw markdown that will become the GitHub issue body.`\n\n\t\t\tconst enhanced = await launchClaude(prompt, {\n\t\t\t\theadless: true,\n\t\t\t\tmodel: 'sonnet',\n\t\t\t\tagents,\n\t\t\t})\n\n\t\t\tif (enhanced && typeof enhanced === 'string') {\n\t\t\t\tgetLogger().success('Description enhanced successfully')\n\t\t\t\treturn enhanced\n\t\t\t}\n\n\t\t\t// Fallback to original description\n\t\t\tgetLogger().warn('Claude enhancement returned empty result, using original description')\n\t\t\treturn description\n\t\t} catch (error) {\n\t\t\tgetLogger().warn(`Failed to enhance description: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn description\n\t\t}\n\t}\n\n\t/**\n\t * Creates a GitHub issue with title and enhanced body.\n\t * @param originalDescription - Used as the issue title\n\t * @param enhancedDescription - Used as the issue body\n\t * @param repository - Optional repository override (format: \"owner/repo\")\n\t * @param labels - Optional array of label names to add to the issue\n\t * @returns Issue number and URL\n\t */\n\tpublic async createEnhancedIssue(\n\t\toriginalDescription: string,\n\t\tenhancedDescription: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\tgetLogger().info('Creating GitHub issue from description...')\n\n\t\tconst result = await this.issueTrackerService.createIssue(\n\t\t\toriginalDescription, // Use original description as title\n\t\t\tenhancedDescription, // Use enhanced description as body\n\t\t\trepository,\n\t\t\tlabels\n\t\t)\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Waits for user keypress and opens issue in browser for review.\n\t * @param issueNumber - Issue number to open for review\n\t * @param confirm - If true, wait for additional keypress after opening browser before returning\n\t * @param repository - Optional repository to fetch issue from (format: \"owner/repo\")\n\t */\n\tpublic async waitForReviewAndOpen(issueNumber: string | number, confirm = false, repository?: string): Promise<void> {\n\t\t// Check if running in non-interactive environment (CI or no TTY)\n\t\tconst isCI = process.env.CI === 'true'\n\t\tconst isNonInteractive = isCI || !process.stdin.isTTY\n\n\t\tif (isNonInteractive) {\n\t\t\t// In non-interactive environment: Skip all interactive operations\n\t\t\tgetLogger().info(`Running in non-interactive environment - skipping interactive prompts for issue #${issueNumber}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Get issue URL\n\t\tconst issueUrl = await this.issueTrackerService.getIssueUrl(issueNumber, repository)\n\n\t\t// Display message and wait for first keypress\n\t\tconst message = `Created issue #${issueNumber}.\nReview and edit the issue in your browser if needed.\nPress any key to open issue for editing...`\n\t\tawait waitForKeypress(message)\n\n\t\t// Open issue in browser\n\t\tawait openBrowser(issueUrl)\n\n\t\t// If confirmation required, wait for second keypress\n\t\tif (confirm) {\n\t\t\tawait waitForKeypress('Press any key to continue with loom creation...')\n\t\t}\n\t}\n}\n","/**\n * Capitalizes the first letter of a string.\n *\n * Override behavior: If the string starts with a space, it signals the user\n * wants to opt-out of auto-capitalization. In this case, the leading space\n * is stripped and the first letter is NOT capitalized.\n *\n * @param str - The string to process\n * @returns The processed string with first letter capitalized (or original if override)\n */\nexport function capitalizeFirstLetter(str: string): string {\n\t// Handle empty or whitespace-only strings\n\tif (!str || str.length === 0) {\n\t\treturn str\n\t}\n\n\t// Check for space-prefix override: strip leading space and return as-is\n\tif (str.startsWith(' ')) {\n\t\treturn str.slice(1)\n\t}\n\n\t// Find the first character that could be capitalized (a letter)\n\tconst firstChar = str.charAt(0)\n\n\t// If first character is a letter (including unicode), capitalize it\n\t// Check if toUpperCase() produces a different result (indicates it's a letter with case)\n\tconst upperChar = firstChar.toUpperCase()\n\tif (upperChar !== firstChar.toLowerCase() || /\\p{L}/u.test(firstChar)) {\n\t\t// Only capitalize if it actually changes (avoids issues with non-cased scripts)\n\t\tif (upperChar !== firstChar) {\n\t\t\treturn upperChar + str.slice(1)\n\t\t}\n\t}\n\n\t// Non-letter first character or no case transformation available: return unchanged\n\treturn str\n}\n"],"mappings":";;;;;;;;;;;;;;;AAYO,IAAM,0BAAN,MAA8B;AAAA,EACpC,YACS,qBACA,cACA,iBACP;AAHO;AACA;AACA;AAAA,EAGT;AAAA;AAAA;AAAA;AAAA,EAKA,IAAW,eAA6B;AACvC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,oBAAoB,aAA8B;AACxD,UAAM,qBAAqB,YAAY,KAAK;AAC5C,UAAM,cAAc,mBAAmB,MAAM,IAAI,KAAK,CAAC,GAAG;AAE1D,WAAO,mBAAmB,SAAS,MAAM,aAAa;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,mBAAmB,aAAsC;AACrE,QAAI;AACH,gBAAU,EAAE,KAAK,mEAAmE;AAGpF,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa;AACzD,YAAM,eAAe,MAAM,KAAK,aAAa,WAAW,QAAQ;AAChE,YAAM,SAAS,KAAK,aAAa,aAAa,YAAY;AAG1D,YAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYV,YAAM,WAAW,MAAM,aAAa,QAAQ;AAAA,QAC3C,UAAU;AAAA,QACV,OAAO;AAAA,QACP;AAAA,MACD,CAAC;AAED,UAAI,YAAY,OAAO,aAAa,UAAU;AAC7C,kBAAU,EAAE,QAAQ,mCAAmC;AACvD,eAAO;AAAA,MACR;AAGA,gBAAU,EAAE,KAAK,sEAAsE;AACvF,aAAO;AAAA,IACR,SAAS,OAAO;AACf,gBAAU,EAAE,KAAK,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AAC7G,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,oBACZ,qBACA,qBACA,YACA,QACoD;AACpD,cAAU,EAAE,KAAK,2CAA2C;AAE5D,UAAM,SAAS,MAAM,KAAK,oBAAoB;AAAA,MAC7C;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,qBAAqB,aAA8B,UAAU,OAAO,YAAoC;AAEpH,UAAM,OAAO,QAAQ,IAAI,OAAO;AAChC,UAAM,mBAAmB,QAAQ,CAAC,QAAQ,MAAM;AAEhD,QAAI,kBAAkB;AAErB,gBAAU,EAAE,KAAK,oFAAoF,WAAW,EAAE;AAClH;AAAA,IACD;AAGA,UAAM,WAAW,MAAM,KAAK,oBAAoB,YAAY,aAAa,UAAU;AAGnF,UAAM,UAAU,kBAAkB,WAAW;AAAA;AAAA;AAG7C,UAAM,gBAAgB,OAAO;AAG7B,UAAM,YAAY,QAAQ;AAG1B,QAAI,SAAS;AACZ,YAAM,gBAAgB,iDAAiD;AAAA,IACxE;AAAA,EACD;AACD;;;AC5IO,SAAS,sBAAsB,KAAqB;AAE1D,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG;AAC7B,WAAO;AAAA,EACR;AAGA,MAAI,IAAI,WAAW,GAAG,GAAG;AACxB,WAAO,IAAI,MAAM,CAAC;AAAA,EACnB;AAGA,QAAM,YAAY,IAAI,OAAO,CAAC;AAI9B,QAAM,YAAY,UAAU,YAAY;AACxC,MAAI,cAAc,UAAU,YAAY,KAAK,WAAC,UAAM,GAAC,EAAC,KAAK,SAAS,GAAG;AAEtE,QAAI,cAAc,WAAW;AAC5B,aAAO,YAAY,IAAI,MAAM,CAAC;AAAA,IAC/B;AAAA,EACD;AAGA,SAAO;AACR;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/color.ts"],"sourcesContent":["import { createHash } from 'crypto'\nimport logger from './logger'\n\n/**\n * RGB color representation\n */\nexport interface RgbColor {\n\tr: number\n\tg: number\n\tb: number\n}\n\n/**\n * Complete color data with RGB, hex, and palette index\n */\nexport interface ColorData {\n\trgb: RgbColor\n\thex: string\n\tindex: number\n}\n\n/**\n * Get the predefined color palette (16 visually distinct colors)\n * Reduced from 40 colors to ensure minimum euclidean distance >= 30 between all pairs\n * This prevents near-identical colors being assigned to different looms\n *\n * @returns Array of 16 RGB colors\n */\nexport function getColorPalette(): RgbColor[] {\n\treturn [\n\t\t{ r: 220, g: 235, b: 255 }, // 0: Soft blue\n\t\t{ r: 255, g: 220, b: 235 }, // 1: Soft pink\n\t\t{ r: 220, g: 255, b: 235 }, // 2: Soft green\n\t\t{ r: 255, g: 245, b: 220 }, // 3: Soft cream\n\t\t{ r: 245, g: 220, b: 255 }, // 4: Soft lavender\n\t\t{ r: 220, g: 245, b: 255 }, // 5: Soft cyan\n\t\t{ r: 235, g: 235, b: 235 }, // 6: Soft grey\n\t\t{ r: 255, g: 230, b: 230 }, // 7: Soft coral\n\t\t{ r: 230, g: 255, b: 230 }, // 8: Soft mint\n\t\t{ r: 255, g: 245, b: 230 }, // 9: Soft peach\n\t\t{ r: 220, g: 255, b: 255 }, // 10: Soft aqua\n\t\t{ r: 255, g: 220, b: 255 }, // 11: Soft magenta\n\t\t{ r: 255, g: 255, b: 220 }, // 12: Soft yellow\n\t\t{ r: 235, g: 220, b: 255 }, // 13: Soft violet\n\t\t{ r: 220, g: 255, b: 245 }, // 14: Soft sea green\n\t\t{ r: 255, g: 235, b: 220 }, // 15: Soft salmon\n\t]\n}\n\n/**\n * Convert RGB values to hex color format\n *\n * @param r - Red value (0-255)\n * @param g - Green value (0-255)\n * @param b - Blue value (0-255)\n * @returns Hex color string (e.g., \"#dcebf8\")\n * @throws Error if RGB values are out of range\n */\nexport function rgbToHex(r: number, g: number, b: number): string {\n\t// Validate RGB values\n\tif (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {\n\t\tthrow new Error('RGB values must be between 0 and 255')\n\t}\n\n\t// Convert to hex and pad with zeros\n\tconst rHex = r.toString(16).padStart(2, '0')\n\tconst gHex = g.toString(16).padStart(2, '0')\n\tconst bHex = b.toString(16).padStart(2, '0')\n\n\treturn `#${rHex}${gHex}${bHex}`\n}\n\n/**\n * Convert hex color format to RGB values\n *\n * @param hex - Hex color string (with or without # prefix)\n * @returns RGB color object\n * @throws Error if hex format is invalid\n */\nexport function hexToRgb(hex: string): RgbColor {\n\t// Remove # prefix if present\n\tconst cleanHex = hex.startsWith('#') ? hex.slice(1) : hex\n\n\t// Validate format (must be exactly 6 hex characters)\n\tif (cleanHex.length !== 6 || !/^[0-9a-fA-F]{6}$/.test(cleanHex)) {\n\t\tthrow new Error('Invalid hex color format. Expected format: #RRGGBB or RRGGBB')\n\t}\n\n\t// Parse hex values\n\tconst r = parseInt(cleanHex.slice(0, 2), 16)\n\tconst g = parseInt(cleanHex.slice(2, 4), 16)\n\tconst b = parseInt(cleanHex.slice(4, 6), 16)\n\n\treturn { r, g, b }\n}\n\n/**\n * Generate deterministic color from branch name using SHA256 hash\n * Matches the bash implementation in bash/new-branch-workflow.sh\n *\n * @param branchName - Branch name to generate color from\n * @returns ColorData with RGB, hex, and palette index\n */\nexport function generateColorFromBranchName(branchName: string): ColorData {\n\t// Generate SHA256 hash of branch name\n\tconst hash = createHash('sha256').update(branchName).digest('hex')\n\n\t// Take first 8 hex characters and convert to index (0-39)\n\t// Matches bash: local index=$(( 0x$hash % ${#colors[@]} ))\n\tconst hashPrefix = hash.slice(0, 8)\n\tconst palette = getColorPalette()\n\tconst hashAsInt = parseInt(hashPrefix, 16)\n\tconst index = hashAsInt % palette.length\n\tlogger.debug(`[generateColorFromBranchName] Branch name: ${branchName}, Hash: ${hash}, Hash prefix: ${hashPrefix}, Hash as int: ${hashAsInt}, Index: ${index}`)\n\n\t// Get color from palette\n\tconst rgb = palette[index]\n\n\t// This should never happen as index is always in range [0, palette.length)\n\tif (!rgb) {\n\t\tthrow new Error(`Invalid color index: ${index}`)\n\t}\n\n\t// Convert to hex format\n\tconst hex = rgbToHex(rgb.r, rgb.g, rgb.b)\n\n\treturn {\n\t\trgb,\n\t\thex,\n\t\tindex,\n\t}\n}\n\n/**\n * Calculate euclidean distance between two RGB colors\n */\nexport function colorDistance(a: RgbColor, b: RgbColor): number {\n\treturn Math.sqrt(\n\t\tMath.pow(a.r - b.r, 2) +\n\t\tMath.pow(a.g - b.g, 2) +\n\t\tMath.pow(a.b - b.b, 2)\n\t)\n}\n\n/**\n * Minimum distance threshold for colors to be considered \"distinct\"\n * Note: With RGB constrained to 220-255 range (subtle backgrounds),\n * the maximum possible distance between any two colors is ~60.6\n * A threshold of 20 ensures colors are visually distinguishable\n * (vs the original palette minimum of 3.61) while allowing enough\n * palette diversity for typical concurrent loom counts (5-10).\n */\nexport const MIN_COLOR_DISTANCE = 20\n\n/**\n * Select a color for a branch, avoiding colors that are too similar to hex colors in use\n * This function is robust against palette changes since it compares hex colors directly.\n *\n * @param branchName - Branch name to generate base color from\n * @param usedHexColors - Array of hex colors (e.g., \"#dcebff\") already in use by active looms\n * @returns ColorData with the selected color\n */\nexport function selectDistinctColor(branchName: string, usedHexColors: string[]): ColorData {\n\tconst palette = getColorPalette()\n\tconst hashBasedColor = generateColorFromBranchName(branchName)\n\n\t// If no colors in use, return hash-based selection\n\tif (usedHexColors.length === 0) {\n\t\treturn hashBasedColor\n\t}\n\n\t// Convert used hex colors to RGB for distance calculation\n\tconst usedRgbColors: RgbColor[] = []\n\tfor (const hex of usedHexColors) {\n\t\ttry {\n\t\t\tusedRgbColors.push(hexToRgb(hex))\n\t\t} catch {\n\t\t\t// Skip invalid hex colors\n\t\t\tlogger.debug(`[selectDistinctColor] Skipping invalid hex color: ${hex}`)\n\t\t}\n\t}\n\n\t// If all hex colors were invalid, return hash-based selection\n\tif (usedRgbColors.length === 0) {\n\t\treturn hashBasedColor\n\t}\n\n\t// Check if hash-based color is distinct enough from all used colors\n\tconst isTooSimilar = usedRgbColors.some(usedRgb =>\n\t\tcolorDistance(hashBasedColor.rgb, usedRgb) < MIN_COLOR_DISTANCE\n\t)\n\n\tif (!isTooSimilar) {\n\t\treturn hashBasedColor\n\t}\n\n\t// Find the first available color that's distinct from all used colors\n\tfor (let i = 0; i < palette.length; i++) {\n\t\tconst candidateRgb = palette[i]\n\t\tif (!candidateRgb) continue\n\n\t\tconst isDistinct = usedRgbColors.every(usedRgb =>\n\t\t\tcolorDistance(candidateRgb, usedRgb) >= MIN_COLOR_DISTANCE\n\t\t)\n\n\t\tif (isDistinct) {\n\t\t\treturn {\n\t\t\t\trgb: candidateRgb,\n\t\t\t\thex: rgbToHex(candidateRgb.r, candidateRgb.g, candidateRgb.b),\n\t\t\t\tindex: i,\n\t\t\t}\n\t\t}\n\t}\n\n\t// Fallback: all colors too similar, return hash-based (best effort)\n\tlogger.debug(`[selectDistinctColor] No distinct color found, falling back to hash-based for ${branchName}`)\n\treturn hashBasedColor\n}\n\n/**\n * Lighten a color by a given amount\n * Useful for creating slightly lighter variants for hover states\n *\n * @param rgb - RGB color to lighten\n * @param amount - Amount to lighten (0-1, where 0.1 = 10% lighter)\n * @returns Lightened RGB color\n */\nexport function lightenColor(rgb: RgbColor, amount: number): RgbColor {\n\tconst clamp = (value: number): number => Math.min(255, Math.max(0, Math.round(value)))\n\n\treturn {\n\t\tr: clamp(rgb.r + (255 - rgb.r) * amount),\n\t\tg: clamp(rgb.g + (255 - rgb.g) * amount),\n\t\tb: clamp(rgb.b + (255 - rgb.b) * amount),\n\t}\n}\n\n/**\n * Saturate a color by pushing it away from grey towards its dominant hue\n * Makes subtle colors more vivid while maintaining their hue\n *\n * @param rgb - RGB color to saturate\n * @param amount - Amount to saturate (0-1, where 0.4 = 40% more saturated)\n * @returns Saturated RGB color\n */\nexport function saturateColor(rgb: RgbColor, amount: number): RgbColor {\n\tconst clamp = (value: number): number => Math.min(255, Math.max(0, Math.round(value)))\n\n\t// Calculate average (grey point)\n\tconst avg = (rgb.r + rgb.g + rgb.b) / 3\n\n\t// Push each channel away from grey\n\treturn {\n\t\tr: clamp(rgb.r + (rgb.r - avg) * amount),\n\t\tg: clamp(rgb.g + (rgb.g - avg) * amount),\n\t\tb: clamp(rgb.b + (rgb.b - avg) * amount),\n\t}\n}\n\n/**\n * Calculate appropriate foreground color (black or white) for a given background\n * Uses relative luminance formula from WCAG 2.0\n *\n * @param rgb - Background RGB color\n * @returns '#000000' for light backgrounds, '#ffffff' for dark backgrounds\n */\nexport function calculateForegroundColor(rgb: RgbColor): string {\n\t// Convert RGB to relative luminance (WCAG 2.0 formula)\n\tconst toLinear = (channel: number): number => {\n\t\tconst c = channel / 255\n\t\treturn c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n\t}\n\n\tconst r = toLinear(rgb.r)\n\tconst g = toLinear(rgb.g)\n\tconst b = toLinear(rgb.b)\n\n\tconst luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b\n\n\t// Use black text for light backgrounds (luminance > 0.5)\n\t// Use white text for dark backgrounds\n\treturn luminance > 0.5 ? '#000000' : '#ffffff'\n}\n"],"mappings":"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","names":[]}
@@ -1,207 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- logger
4
- } from "./chunk-UYVWLISQ.js";
5
-
6
- // src/utils/env.ts
7
- import path from "path";
8
- import dotenvFlow from "dotenv-flow";
9
- function parseEnvFile(content) {
10
- const envMap = /* @__PURE__ */ new Map();
11
- const lines = content.split("\n");
12
- for (const line of lines) {
13
- const trimmedLine = line.trim();
14
- if (!trimmedLine || trimmedLine.startsWith("#")) {
15
- continue;
16
- }
17
- const cleanLine = trimmedLine.startsWith("export ") ? trimmedLine.substring(7) : trimmedLine;
18
- const equalsIndex = cleanLine.indexOf("=");
19
- if (equalsIndex === -1) {
20
- continue;
21
- }
22
- const key = cleanLine.substring(0, equalsIndex).trim();
23
- let value = cleanLine.substring(equalsIndex + 1);
24
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
25
- value = value.substring(1, value.length - 1);
26
- value = value.replace(/\\"/g, '"').replace(/\\'/g, "'");
27
- value = value.replace(/\\n/g, "\n");
28
- }
29
- if (key) {
30
- envMap.set(key, value);
31
- }
32
- }
33
- return envMap;
34
- }
35
- function formatEnvLine(key, value) {
36
- const escapedValue = value.replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
37
- return `${key}="${escapedValue}"`;
38
- }
39
- function validateEnvVariable(key, _value) {
40
- if (!key || key.length === 0) {
41
- return {
42
- valid: false,
43
- error: "Environment variable key cannot be empty"
44
- };
45
- }
46
- if (!isValidEnvKey(key)) {
47
- return {
48
- valid: false,
49
- error: `Invalid environment variable name: ${key}. Must start with a letter or underscore and contain only letters, numbers, and underscores.`
50
- };
51
- }
52
- return { valid: true };
53
- }
54
- function extractPort(envContent) {
55
- const portValue = envContent.get("PORT");
56
- if (!portValue) {
57
- return null;
58
- }
59
- const port = parseInt(portValue, 10);
60
- if (isNaN(port)) {
61
- return null;
62
- }
63
- return port;
64
- }
65
- function isValidEnvKey(key) {
66
- if (!key || key.length === 0) {
67
- return false;
68
- }
69
- const validKeyRegex = /^[A-Za-z_][A-Za-z0-9_]*$/;
70
- return validKeyRegex.test(key);
71
- }
72
- function loadEnvIntoProcess(options) {
73
- logger.debug("Loading environment variables with dotenv-flow", {
74
- options: {
75
- path: (options == null ? void 0 : options.path) ?? "current working directory",
76
- nodeEnv: (options == null ? void 0 : options.nodeEnv) ?? "not specified",
77
- defaultNodeEnv: (options == null ? void 0 : options.defaultNodeEnv) ?? "development (default)"
78
- }
79
- });
80
- const configOptions = {
81
- silent: true
82
- // Don't throw errors if .env files are missing
83
- };
84
- if ((options == null ? void 0 : options.path) !== void 0) {
85
- configOptions.path = options.path;
86
- logger.debug(`Using custom path: ${options.path}`);
87
- }
88
- if ((options == null ? void 0 : options.nodeEnv) !== void 0) {
89
- configOptions.node_env = options.nodeEnv;
90
- logger.debug(`Using NODE_ENV: ${options.nodeEnv}`);
91
- }
92
- if ((options == null ? void 0 : options.defaultNodeEnv) !== void 0) {
93
- configOptions.default_node_env = options.defaultNodeEnv;
94
- logger.debug(`Using default NODE_ENV: ${options.defaultNodeEnv}`);
95
- } else {
96
- configOptions.default_node_env = "development";
97
- logger.debug("Using default NODE_ENV: development");
98
- }
99
- logger.debug("dotenv-flow config options:", configOptions);
100
- const result = dotenvFlow.config(configOptions);
101
- const returnValue = {};
102
- if (result.parsed) {
103
- returnValue.parsed = result.parsed;
104
- const variableCount = Object.keys(result.parsed).length;
105
- logger.debug(`Successfully loaded ${variableCount} environment variables`);
106
- } else {
107
- logger.debug("No environment variables were parsed");
108
- }
109
- if (result.error) {
110
- returnValue.error = result.error;
111
- logger.debug("dotenv-flow returned an error", {
112
- error: result.error.message,
113
- name: result.error.name
114
- });
115
- } else {
116
- logger.debug("dotenv-flow completed without errors");
117
- }
118
- return returnValue;
119
- }
120
- function isNoEnvFilesFoundError(error) {
121
- return error.message.startsWith('no ".env*" files matching pattern');
122
- }
123
- function loadWorkspaceEnv(workspacePath) {
124
- const nodeEnv = process.env.NODE_ENV ?? "development";
125
- logger.debug("Loading workspace environment variables", {
126
- workspacePath,
127
- detectedNodeEnv: nodeEnv,
128
- processNodeEnv: process.env.NODE_ENV ?? "not set"
129
- });
130
- return loadEnvIntoProcess({
131
- path: workspacePath,
132
- nodeEnv,
133
- defaultNodeEnv: "development"
134
- });
135
- }
136
- var DOTENV_FLOW_NODE_ENV = process.env.DOTENV_FLOW_NODE_ENV ?? "development";
137
- function getDotenvFlowFiles() {
138
- return [
139
- ".env",
140
- ".env.local",
141
- `.env.${DOTENV_FLOW_NODE_ENV}`,
142
- `.env.${DOTENV_FLOW_NODE_ENV}.local`
143
- ];
144
- }
145
- function getLocalEquivalent(filename) {
146
- if (filename.endsWith(".local")) {
147
- return filename;
148
- }
149
- return `${filename}.local`;
150
- }
151
- async function findEnvFileForDatabaseUrl(workspacePath, variableName, isFileTracked, fileExists, getEnvVariable) {
152
- const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable);
153
- if (file === null) {
154
- return ".env.local";
155
- }
156
- const isTracked = await isFileTracked(file, workspacePath);
157
- if (isTracked) {
158
- return getLocalEquivalent(file);
159
- }
160
- return file;
161
- }
162
- async function buildEnvSourceCommands(workspacePath, fileExists) {
163
- const files = getDotenvFlowFiles();
164
- const commands = [];
165
- for (const file of files) {
166
- const fullPath = path.join(workspacePath, file);
167
- const exists = await fileExists(fullPath);
168
- if (exists) {
169
- commands.push(`source ${file}`);
170
- }
171
- }
172
- return commands;
173
- }
174
- async function findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable) {
175
- const files = getDotenvFlowFiles().reverse();
176
- for (const file of files) {
177
- const fullPath = path.join(workspacePath, file);
178
- if (!await fileExists(fullPath)) {
179
- continue;
180
- }
181
- const value = await getEnvVariable(fullPath, variableName);
182
- if (value !== null) {
183
- return file;
184
- }
185
- }
186
- return null;
187
- }
188
- async function hasVariableInAnyEnvFile(workspacePath, variableName, fileExists, getEnvVariable) {
189
- const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable);
190
- return file !== null;
191
- }
192
-
193
- export {
194
- parseEnvFile,
195
- formatEnvLine,
196
- validateEnvVariable,
197
- extractPort,
198
- loadEnvIntoProcess,
199
- isNoEnvFilesFoundError,
200
- loadWorkspaceEnv,
201
- getDotenvFlowFiles,
202
- findEnvFileForDatabaseUrl,
203
- buildEnvSourceCommands,
204
- findEnvFileContainingVariable,
205
- hasVariableInAnyEnvFile
206
- };
207
- //# sourceMappingURL=chunk-Z5NXYJIG.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/env.ts"],"sourcesContent":["import path from 'path'\nimport dotenvFlow, { type DotenvFlowConfigOptions } from 'dotenv-flow'\nimport { logger } from './logger.js'\n\n/**\n * Parse .env file content into key-value map\n * Handles comments, empty lines, quoted/unquoted values, multiline values\n */\nexport function parseEnvFile(content: string): Map<string, string> {\n const envMap = new Map<string, string>()\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const trimmedLine = line.trim()\n\n // Skip empty lines and comments\n if (!trimmedLine || trimmedLine.startsWith('#')) {\n continue\n }\n\n // Remove 'export ' prefix if present\n const cleanLine = trimmedLine.startsWith('export ')\n ? trimmedLine.substring(7)\n : trimmedLine\n\n // Find the first equals sign\n const equalsIndex = cleanLine.indexOf('=')\n if (equalsIndex === -1) {\n continue\n }\n\n const key = cleanLine.substring(0, equalsIndex).trim()\n let value = cleanLine.substring(equalsIndex + 1)\n\n // Handle quoted values\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.substring(1, value.length - 1)\n // Unescape quotes\n value = value.replace(/\\\\\"/g, '\"').replace(/\\\\'/g, \"'\")\n // Unescape newlines\n value = value.replace(/\\\\n/g, '\\n')\n }\n\n if (key) {\n envMap.set(key, value)\n }\n }\n\n return envMap\n}\n\n/**\n * Format environment variable as line for .env file\n * Always quotes values and escapes internal quotes\n */\nexport function formatEnvLine(key: string, value: string): string {\n // Escape quotes and newlines in the value\n const escapedValue = value\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n\n return `${key}=\"${escapedValue}\"`\n}\n\n/**\n * Validate environment variable name and value\n */\nexport function validateEnvVariable(\n key: string,\n _value?: string\n): { valid: boolean; error?: string } {\n if (!key || key.length === 0) {\n return {\n valid: false,\n error: 'Environment variable key cannot be empty',\n }\n }\n\n if (!isValidEnvKey(key)) {\n return {\n valid: false,\n error: `Invalid environment variable name: ${key}. Must start with a letter or underscore and contain only letters, numbers, and underscores.`,\n }\n }\n\n // Values can be any string, including empty\n return { valid: true }\n}\n\n/**\n * Normalize line endings for cross-platform compatibility\n */\nexport function normalizeLineEndings(content: string): string {\n return content.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n}\n\n/**\n * Extract port from .env file if present\n */\nexport function extractPort(envContent: Map<string, string>): number | null {\n const portValue = envContent.get('PORT')\n if (!portValue) {\n return null\n }\n\n const port = parseInt(portValue, 10)\n if (isNaN(port)) {\n return null\n }\n\n return port\n}\n\n/**\n * Check if environment variable key is valid\n */\nexport function isValidEnvKey(key: string): boolean {\n if (!key || key.length === 0) {\n return false\n }\n\n // Must start with letter or underscore, followed by letters, numbers, or underscores\n const validKeyRegex = /^[A-Za-z_][A-Za-z0-9_]*$/\n return validKeyRegex.test(key)\n}\n\n/**\n * Load environment variables using dotenv-flow\n * Supports environment-specific files (.env.development, .env.production, etc.)\n * and local overrides (.env.local, .env.development.local)\n */\nexport function loadEnvIntoProcess(options?: {\n path?: string\n nodeEnv?: string\n defaultNodeEnv?: string\n}): { parsed?: Record<string, string>; error?: Error } {\n logger.debug('Loading environment variables with dotenv-flow', {\n options: {\n path: options?.path ?? 'current working directory',\n nodeEnv: options?.nodeEnv ?? 'not specified',\n defaultNodeEnv: options?.defaultNodeEnv ?? 'development (default)'\n }\n })\n\n const configOptions: Partial<DotenvFlowConfigOptions> = {\n silent: true, // Don't throw errors if .env files are missing\n }\n\n // Only add defined values to avoid TypeScript strict type issues\n if (options?.path !== undefined) {\n configOptions.path = options.path\n logger.debug(`Using custom path: ${options.path}`)\n }\n if (options?.nodeEnv !== undefined) {\n configOptions.node_env = options.nodeEnv\n logger.debug(`Using NODE_ENV: ${options.nodeEnv}`)\n }\n if (options?.defaultNodeEnv !== undefined) {\n configOptions.default_node_env = options.defaultNodeEnv\n logger.debug(`Using default NODE_ENV: ${options.defaultNodeEnv}`)\n } else {\n configOptions.default_node_env = 'development'\n logger.debug('Using default NODE_ENV: development')\n }\n\n logger.debug('dotenv-flow config options:', configOptions)\n\n const result = dotenvFlow.config(configOptions)\n\n const returnValue: { parsed?: Record<string, string>; error?: Error } = {}\n\n if (result.parsed) {\n returnValue.parsed = result.parsed as Record<string, string>\n const variableCount = Object.keys(result.parsed).length\n logger.debug(`Successfully loaded ${variableCount} environment variables`)\n } else {\n logger.debug('No environment variables were parsed')\n }\n\n if (result.error) {\n returnValue.error = result.error\n logger.debug('dotenv-flow returned an error', {\n error: result.error.message,\n name: result.error.name\n })\n } else {\n logger.debug('dotenv-flow completed without errors')\n }\n\n return returnValue\n}\n\n/**\n * Check if an error from loadEnvIntoProcess indicates no .env files were found\n * This is a harmless condition that shouldn't be logged as a warning\n */\nexport function isNoEnvFilesFoundError(error: Error): boolean {\n return error.message.startsWith('no \".env*\" files matching pattern')\n}\n\n/**\n * Load environment variables for a specific workspace\n * Automatically determines environment based on NODE_ENV or defaults to development\n */\nexport function loadWorkspaceEnv(workspacePath: string): {\n parsed?: Record<string, string>\n error?: Error\n} {\n const nodeEnv = process.env.NODE_ENV ?? 'development'\n\n logger.debug('Loading workspace environment variables', {\n workspacePath,\n detectedNodeEnv: nodeEnv,\n processNodeEnv: process.env.NODE_ENV ?? 'not set'\n })\n\n return loadEnvIntoProcess({\n path: workspacePath,\n nodeEnv: nodeEnv,\n defaultNodeEnv: 'development'\n })\n}\n\n// CONSTANT: Always use 'development' per critical constraint, unless overridden\nconst DOTENV_FLOW_NODE_ENV = process.env.DOTENV_FLOW_NODE_ENV ?? 'development'\n\n/**\n * Get dotenv-flow files in precedence order (lowest to highest)\n * Always uses 'development' as NODE_ENV per constraint\n */\nexport function getDotenvFlowFiles(): string[] {\n return [\n '.env',\n '.env.local',\n `.env.${DOTENV_FLOW_NODE_ENV}`,\n `.env.${DOTENV_FLOW_NODE_ENV}.local`\n ]\n}\n\n/**\n * Map a file to its \"local\" equivalent for git-safe writes\n * .env -> .env.local\n * .env.{NODE_ENV} -> .env.{NODE_ENV}.local\n * Already local files return unchanged\n */\nexport function getLocalEquivalent(filename: string): string {\n // Already a .local file\n if (filename.endsWith('.local')) {\n return filename\n }\n return `${filename}.local`\n}\n\n/**\n * Find the appropriate env file to write a database URL variable to\n * Considers dotenv-flow precedence and git tracking status\n * Returns path relative to workspacePath\n *\n * Algorithm:\n * 1. Search files in reverse precedence order (highest first)\n * 2. Find first file containing the variable\n * 3. If tracked by git, return its .local equivalent\n * 4. If not tracked, return the file itself\n * 5. If not found anywhere, return '.env.local' (safe default)\n */\nexport async function findEnvFileForDatabaseUrl(\n workspacePath: string,\n variableName: string,\n isFileTracked: (filePath: string, cwd: string) => Promise<boolean>,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<string> {\n // Find the highest-precedence file containing the variable\n const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable)\n\n if (file === null) {\n // Variable not found anywhere - use safe default\n return '.env.local'\n }\n\n // Found the variable - check git tracking\n const isTracked = await isFileTracked(file, workspacePath)\n if (isTracked) {\n // Return .local equivalent for git safety\n return getLocalEquivalent(file)\n }\n\n return file\n}\n\n/**\n * Build shell source commands for all existing dotenv-flow files\n * Returns commands in precedence order (later overrides earlier)\n */\nexport async function buildEnvSourceCommands(\n workspacePath: string,\n fileExists: (filePath: string) => Promise<boolean>\n): Promise<string[]> {\n const files = getDotenvFlowFiles()\n const commands: string[] = []\n\n for (const file of files) {\n const fullPath = path.join(workspacePath, file)\n const exists = await fileExists(fullPath)\n if (exists) {\n commands.push(`source ${file}`)\n }\n }\n\n return commands\n}\n\n/**\n * Find the highest-precedence env file containing a variable\n * Searches all dotenv-flow files in reverse precedence order (highest first)\n * Returns the relative filename if found, null otherwise\n */\nexport async function findEnvFileContainingVariable(\n workspacePath: string,\n variableName: string,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<string | null> {\n const files = getDotenvFlowFiles().reverse() // highest precedence first\n\n for (const file of files) {\n const fullPath = path.join(workspacePath, file)\n\n // Skip if file doesn't exist\n if (!(await fileExists(fullPath))) {\n continue\n }\n\n // Check if file contains the variable\n const value = await getEnvVariable(fullPath, variableName)\n if (value !== null) {\n return file\n }\n }\n\n return null\n}\n\n/**\n * Check if a variable exists in any dotenv-flow file\n * Searches all dotenv-flow files (.env, .env.local, .env.{NODE_ENV}, .env.{NODE_ENV}.local)\n * Returns true if variable is found in any file, false otherwise\n */\nexport async function hasVariableInAnyEnvFile(\n workspacePath: string,\n variableName: string,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<boolean> {\n const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable)\n return file !== null\n}\n"],"mappings":"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","names":[]}
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- InitCommand
4
- } from "./chunk-4YTILIIH.js";
5
- import "./chunk-UYWAESOT.js";
6
- import "./chunk-3CMGCRB5.js";
7
- import "./chunk-PSFVTBM7.js";
8
- import "./chunk-DKQ4SUII.js";
9
- import "./chunk-LN4H3A6A.js";
10
- import "./chunk-OOU3DKNT.js";
11
- import "./chunk-YZTDGPFB.js";
12
- import "./chunk-SJ2GZ6RF.js";
13
- import "./chunk-RUC7OULH.js";
14
- import "./chunk-VAYGNQTE.js";
15
- import "./chunk-Z5NXYJIG.js";
16
- import "./chunk-6UIGZD2N.js";
17
- import "./chunk-UYVWLISQ.js";
18
- export {
19
- InitCommand
20
- };
21
- //# sourceMappingURL=init-F6PFMSU5.js.map
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- createNeonProviderFromSettings
4
- } from "./chunk-UNXRACJ7.js";
5
- import "./chunk-SJ2GZ6RF.js";
6
- import "./chunk-6UIGZD2N.js";
7
- import "./chunk-UYVWLISQ.js";
8
- export {
9
- createNeonProviderFromSettings
10
- };
11
- //# sourceMappingURL=neon-helpers-L5CXQ5CT.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/recap.ts"],"sourcesContent":["/**\n * RecapCommand - Fast read-only command for VS Code extension\n *\n * Reads ~/.config/iloom-ai/recaps/{current-loom}.json and outputs it.\n * Skips config validation for fast startup.\n * Includes filePath in output so extension can set up file watcher.\n */\nimport path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport type { RecapFile, RecapOutput } from '../mcp/recap-types.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\n\nconst RECAPS_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\n\n/**\n * Reuse MetadataManager.slugifyPath() algorithm\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with ___ (triple underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n */\nfunction slugifyPath(loomPath: string): string {\n\tlet slug = loomPath.replace(/[/\\\\]+$/, '')\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\treturn `${slug}.json`\n}\n\nexport interface RecapCommandInput {\n\tidentifier?: string | undefined // Optional identifier (issue number, PR number, branch name)\n\tjson?: boolean | undefined\n}\n\nexport class RecapCommand {\n\t/**\n\t * Execute the recap command\n\t * Returns RecapOutput in JSON mode, void otherwise\n\t */\n\tasync execute(input: RecapCommandInput): Promise<RecapOutput | void> {\n\t\t// Resolve loom path from identifier or fall back to cwd\n\t\tconst loomPath = await this.resolveLoomPath(input.identifier)\n\t\tconst filePath = path.join(RECAPS_DIR, slugifyPath(loomPath))\n\n\t\t// Read recap file (return empty object if not found)\n\t\tlet recap: RecapFile = {}\n\t\ttry {\n\t\t\tif (await fs.pathExists(filePath)) {\n\t\t\t\tconst content = await fs.readFile(filePath, 'utf8')\n\t\t\t\trecap = JSON.parse(content) as RecapFile\n\t\t\t}\n\t\t} catch {\n\t\t\t// Graceful degradation - return empty recap on read error\n\t\t\t// This is intentional for fast startup\n\t\t}\n\n\t\t// Build output with filePath for file watching (provide defaults for optional fields)\n\t\tconst goal = recap.goal ?? null\n\t\tconst complexity = recap.complexity ?? null\n\t\tconst entries = recap.entries ?? []\n\t\tconst artifacts = recap.artifacts ?? []\n\t\tconst result: RecapOutput = { filePath, goal, complexity, entries, artifacts }\n\n\t\tif (input.json) {\n\t\t\treturn result\n\t\t}\n\n\t\t// Non-JSON mode: print human-readable format (intentionally using console.log for piping/redirection)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Recap file: ${filePath}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Goal: ${goal ?? '(not set)'}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Complexity: ${complexity ? `${complexity.level}${complexity.reason ? ` - ${complexity.reason}` : ''}` : '(not set)'}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Entries: ${entries.length}`)\n\t\tfor (const entry of entries) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(` [${entry.type}] ${entry.content}`)\n\t\t}\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Artifacts: ${artifacts.length}`)\n\t\tfor (const artifact of artifacts) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(` [${artifact.type}] ${artifact.description} - ${artifact.primaryUrl}`)\n\t\t}\n\t}\n\n\t/**\n\t * Resolve identifier to loom path\n\t * Falls back to cwd when no identifier is provided (backward compatible)\n\t */\n\tprivate async resolveLoomPath(identifier: string | undefined): Promise<string> {\n\t\t// Default: use current working directory\n\t\tif (!identifier?.trim()) {\n\t\t\treturn process.cwd()\n\t\t}\n\n\t\tconst trimmedId = identifier.trim()\n\t\tconst gitWorktreeManager = new GitWorktreeManager()\n\t\tconst identifierParser = new IdentifierParser(gitWorktreeManager)\n\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123\n\t\tconst prPattern = /^(?:pr|PR)[/-](\\d+)$/\n\t\tconst prMatch = trimmedId.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\treturn worktree.path\n\t\t\t}\n\t\t\tthrow new Error(`No worktree found for PR #${prNumber}`)\n\t\t}\n\n\t\t// Use IdentifierParser for pattern-based detection\n\t\ttry {\n\t\t\tconst parsed = await identifierParser.parseForPatternDetection(trimmedId)\n\n\t\t\t// Find worktree based on parsed type\n\t\t\tif (parsed.type === 'pr' && typeof parsed.number === 'number') {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(parsed.number, '')\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for PR #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for issue #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'branch' && parsed.branchName) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForBranch(parsed.branchName)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for branch: ${parsed.branchName}`)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Re-throw IdentifierParser errors with context\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new Error(`Could not resolve identifier '${identifier}': ${error.message}`)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\t// Should not reach here, but provide a fallback error\n\t\tthrow new Error(`Could not resolve identifier: ${identifier}`)\n\t}\n}\n"],"mappings":"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","names":[]}