@iloom/cli 0.4.0 → 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 (82) hide show
  1. package/dist/{BranchNamingService-TOM2KAUT.js → BranchNamingService-GCCWB3LK.js} +2 -2
  2. package/dist/{ClaudeContextManager-VEGJTS5E.js → ClaudeContextManager-DK77227F.js} +6 -6
  3. package/dist/{ClaudeService-ICSHJMQ5.js → ClaudeService-W3SA7HVG.js} +5 -5
  4. package/dist/{LoomLauncher-SJBZFZXE.js → LoomLauncher-S3YGJRJQ.js} +24 -11
  5. package/dist/LoomLauncher-S3YGJRJQ.js.map +1 -0
  6. package/dist/{chunk-TR5MC2U6.js → chunk-2W2FBL5G.js} +2 -2
  7. package/dist/{chunk-HVGQP44L.js → chunk-55TB3FSG.js} +2 -2
  8. package/dist/{chunk-LTNDJMTH.js → chunk-BIIQHEXJ.js} +4 -4
  9. package/dist/{chunk-KM3W7YQX.js → chunk-G6CIIJLT.js} +4 -4
  10. package/dist/{chunk-NFVFVYAP.js → chunk-IARWMDAX.js} +19 -12
  11. package/dist/chunk-IARWMDAX.js.map +1 -0
  12. package/dist/{chunk-MLS5FAV7.js → chunk-IJ7IGJT3.js} +4 -1
  13. package/dist/chunk-IJ7IGJT3.js.map +1 -0
  14. package/dist/{chunk-M5XUCTTJ.js → chunk-JC5HXN75.js} +2 -2
  15. package/dist/{chunk-ADDNFQJ4.js → chunk-POI7KLBH.js} +2 -2
  16. package/dist/{chunk-P2WZIDF3.js → chunk-QIUJPPJQ.js} +2 -2
  17. package/dist/{chunk-75B2HZZ5.js → chunk-RUC7OULH.js} +2 -2
  18. package/dist/{chunk-HHDSIE72.js → chunk-TMZAVPGF.js} +3 -3
  19. package/dist/{chunk-F4J6KEL6.js → chunk-UPUAQYAW.js} +2 -2
  20. package/dist/{chunk-JJUPY5MM.js → chunk-VAYGNQTE.js} +2 -2
  21. package/dist/{chunk-S44CHE3G.js → chunk-VTXCGKV5.js} +2 -2
  22. package/dist/{chunk-3NFBZRPR.js → chunk-Z5NXYJIG.js} +19 -1
  23. package/dist/chunk-Z5NXYJIG.js.map +1 -0
  24. package/dist/{claude-X7EBJRB2.js → claude-ACVXNB6N.js} +4 -4
  25. package/dist/{cleanup-7QVPYBJJ.js → cleanup-KDLVTT7M.js} +12 -12
  26. package/dist/cli.js +95 -48
  27. package/dist/cli.js.map +1 -1
  28. package/dist/{contribute-RZYCYUDX.js → contribute-HY372S6F.js} +3 -3
  29. package/dist/{dev-server-LOY7YWCP.js → dev-server-JCJGQ3PV.js} +9 -9
  30. package/dist/{feedback-562KPG5U.js → feedback-7PVBQNLJ.js} +5 -5
  31. package/dist/{git-OXJACVAU.js → git-4BVOOOOV.js} +3 -3
  32. package/dist/{ignite-VSIPGKKG.js → ignite-3B264M7K.js} +7 -7
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +3 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/{init-SCR2LQ4A.js → init-LBA6NUK2.js} +8 -8
  37. package/dist/{open-CX7HUE26.js → open-OGCV32Z4.js} +9 -9
  38. package/dist/{projects-6DTNDVLH.js → projects-P55273AB.js} +2 -2
  39. package/dist/{rebase-55URTXZC.js → rebase-4T5FQHNH.js} +8 -8
  40. package/dist/{run-DP2U2CA2.js → run-HNOP6WE2.js} +9 -9
  41. package/dist/shell-DE3HKJSM.js +240 -0
  42. package/dist/shell-DE3HKJSM.js.map +1 -0
  43. package/dist/{summary-J3CJSM7L.js → summary-GDT7DTRI.js} +8 -8
  44. package/dist/{test-git-QLAIBJLX.js → test-git-YMAE57UP.js} +3 -3
  45. package/dist/{test-prefix-6YM2ZOON.js → test-prefix-YCKL6CMT.js} +3 -3
  46. package/dist/{test-tabs-JGO3VOXJ.js → test-tabs-3SCJWRKT.js} +3 -3
  47. package/package.json +1 -1
  48. package/dist/LoomLauncher-SJBZFZXE.js.map +0 -1
  49. package/dist/chunk-3NFBZRPR.js.map +0 -1
  50. package/dist/chunk-MLS5FAV7.js.map +0 -1
  51. package/dist/chunk-NFVFVYAP.js.map +0 -1
  52. /package/dist/{BranchNamingService-TOM2KAUT.js.map → BranchNamingService-GCCWB3LK.js.map} +0 -0
  53. /package/dist/{ClaudeContextManager-VEGJTS5E.js.map → ClaudeContextManager-DK77227F.js.map} +0 -0
  54. /package/dist/{ClaudeService-ICSHJMQ5.js.map → ClaudeService-W3SA7HVG.js.map} +0 -0
  55. /package/dist/{chunk-TR5MC2U6.js.map → chunk-2W2FBL5G.js.map} +0 -0
  56. /package/dist/{chunk-HVGQP44L.js.map → chunk-55TB3FSG.js.map} +0 -0
  57. /package/dist/{chunk-LTNDJMTH.js.map → chunk-BIIQHEXJ.js.map} +0 -0
  58. /package/dist/{chunk-KM3W7YQX.js.map → chunk-G6CIIJLT.js.map} +0 -0
  59. /package/dist/{chunk-M5XUCTTJ.js.map → chunk-JC5HXN75.js.map} +0 -0
  60. /package/dist/{chunk-ADDNFQJ4.js.map → chunk-POI7KLBH.js.map} +0 -0
  61. /package/dist/{chunk-P2WZIDF3.js.map → chunk-QIUJPPJQ.js.map} +0 -0
  62. /package/dist/{chunk-75B2HZZ5.js.map → chunk-RUC7OULH.js.map} +0 -0
  63. /package/dist/{chunk-HHDSIE72.js.map → chunk-TMZAVPGF.js.map} +0 -0
  64. /package/dist/{chunk-F4J6KEL6.js.map → chunk-UPUAQYAW.js.map} +0 -0
  65. /package/dist/{chunk-JJUPY5MM.js.map → chunk-VAYGNQTE.js.map} +0 -0
  66. /package/dist/{chunk-S44CHE3G.js.map → chunk-VTXCGKV5.js.map} +0 -0
  67. /package/dist/{claude-X7EBJRB2.js.map → claude-ACVXNB6N.js.map} +0 -0
  68. /package/dist/{cleanup-7QVPYBJJ.js.map → cleanup-KDLVTT7M.js.map} +0 -0
  69. /package/dist/{contribute-RZYCYUDX.js.map → contribute-HY372S6F.js.map} +0 -0
  70. /package/dist/{dev-server-LOY7YWCP.js.map → dev-server-JCJGQ3PV.js.map} +0 -0
  71. /package/dist/{feedback-562KPG5U.js.map → feedback-7PVBQNLJ.js.map} +0 -0
  72. /package/dist/{git-OXJACVAU.js.map → git-4BVOOOOV.js.map} +0 -0
  73. /package/dist/{ignite-VSIPGKKG.js.map → ignite-3B264M7K.js.map} +0 -0
  74. /package/dist/{init-SCR2LQ4A.js.map → init-LBA6NUK2.js.map} +0 -0
  75. /package/dist/{open-CX7HUE26.js.map → open-OGCV32Z4.js.map} +0 -0
  76. /package/dist/{projects-6DTNDVLH.js.map → projects-P55273AB.js.map} +0 -0
  77. /package/dist/{rebase-55URTXZC.js.map → rebase-4T5FQHNH.js.map} +0 -0
  78. /package/dist/{run-DP2U2CA2.js.map → run-HNOP6WE2.js.map} +0 -0
  79. /package/dist/{summary-J3CJSM7L.js.map → summary-GDT7DTRI.js.map} +0 -0
  80. /package/dist/{test-git-QLAIBJLX.js.map → test-git-YMAE57UP.js.map} +0 -0
  81. /package/dist/{test-prefix-6YM2ZOON.js.map → test-prefix-YCKL6CMT.js.map} +0 -0
  82. /package/dist/{test-tabs-JGO3VOXJ.js.map → test-tabs-3SCJWRKT.js.map} +0 -0
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  InitCommand
4
- } from "./chunk-KM3W7YQX.js";
4
+ } from "./chunk-G6CIIJLT.js";
5
5
  import "./chunk-UYWAESOT.js";
6
- import "./chunk-M5XUCTTJ.js";
6
+ import "./chunk-JC5HXN75.js";
7
7
  import "./chunk-PSFVTBM7.js";
8
- import "./chunk-TR5MC2U6.js";
9
- import "./chunk-MLS5FAV7.js";
8
+ import "./chunk-2W2FBL5G.js";
9
+ import "./chunk-IJ7IGJT3.js";
10
10
  import "./chunk-VWNS6DH5.js";
11
11
  import "./chunk-SJ2GZ6RF.js";
12
- import "./chunk-75B2HZZ5.js";
13
- import "./chunk-JJUPY5MM.js";
14
- import "./chunk-3NFBZRPR.js";
12
+ import "./chunk-RUC7OULH.js";
13
+ import "./chunk-VAYGNQTE.js";
14
+ import "./chunk-Z5NXYJIG.js";
15
15
  import "./chunk-6UIGZD2N.js";
16
16
  import "./chunk-74VMN2KC.js";
17
17
  import "./chunk-UYVWLISQ.js";
18
18
  export {
19
19
  InitCommand
20
20
  };
21
- //# sourceMappingURL=init-SCR2LQ4A.js.map
21
+ //# sourceMappingURL=init-LBA6NUK2.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import "./chunk-QRBOPFAA.js";
6
6
  import {
7
7
  IdentifierParser
8
- } from "./chunk-HVGQP44L.js";
8
+ } from "./chunk-55TB3FSG.js";
9
9
  import {
10
10
  calculatePortForBranch
11
11
  } from "./chunk-VU3QMIP2.js";
@@ -14,19 +14,19 @@ import {
14
14
  } from "./chunk-YETJNRQM.js";
15
15
  import {
16
16
  GitWorktreeManager
17
- } from "./chunk-M5XUCTTJ.js";
17
+ } from "./chunk-JC5HXN75.js";
18
18
  import "./chunk-VBFDVGAE.js";
19
+ import {
20
+ extractSettingsOverrides
21
+ } from "./chunk-GYCR2LOU.js";
19
22
  import {
20
23
  ProjectCapabilityDetector
21
24
  } from "./chunk-EBISESAP.js";
22
25
  import "./chunk-2ZPFJQ3B.js";
23
- import {
24
- extractSettingsOverrides
25
- } from "./chunk-GYCR2LOU.js";
26
26
  import {
27
27
  extractIssueNumber
28
- } from "./chunk-TR5MC2U6.js";
29
- import "./chunk-MLS5FAV7.js";
28
+ } from "./chunk-2W2FBL5G.js";
29
+ import "./chunk-IJ7IGJT3.js";
30
30
  import {
31
31
  SettingsManager
32
32
  } from "./chunk-VWNS6DH5.js";
@@ -34,7 +34,7 @@ import {
34
34
  extractPort,
35
35
  findEnvFileContainingVariable,
36
36
  parseEnvFile
37
- } from "./chunk-3NFBZRPR.js";
37
+ } from "./chunk-Z5NXYJIG.js";
38
38
  import "./chunk-6UIGZD2N.js";
39
39
  import {
40
40
  logger
@@ -291,4 +291,4 @@ Make sure the project is built (run 'il start' first)`
291
291
  export {
292
292
  OpenCommand
293
293
  };
294
- //# sourceMappingURL=open-CX7HUE26.js.map
294
+ //# sourceMappingURL=open-OGCV32Z4.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MetadataManager
4
- } from "./chunk-MLS5FAV7.js";
4
+ } from "./chunk-IJ7IGJT3.js";
5
5
  import {
6
6
  getLogger
7
7
  } from "./chunk-6UIGZD2N.js";
@@ -70,4 +70,4 @@ var ProjectsCommand = class {
70
70
  export {
71
71
  ProjectsCommand
72
72
  };
73
- //# sourceMappingURL=projects-6DTNDVLH.js.map
73
+ //# sourceMappingURL=projects-P55273AB.js.map
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MergeManager
4
- } from "./chunk-LTNDJMTH.js";
4
+ } from "./chunk-BIIQHEXJ.js";
5
5
  import {
6
6
  GitWorktreeManager
7
- } from "./chunk-M5XUCTTJ.js";
7
+ } from "./chunk-JC5HXN75.js";
8
8
  import {
9
9
  getWorktreeRoot,
10
10
  isValidGitRepo
11
- } from "./chunk-TR5MC2U6.js";
12
- import "./chunk-MLS5FAV7.js";
11
+ } from "./chunk-2W2FBL5G.js";
12
+ import "./chunk-IJ7IGJT3.js";
13
13
  import {
14
14
  SettingsManager
15
15
  } from "./chunk-VWNS6DH5.js";
16
- import "./chunk-75B2HZZ5.js";
17
- import "./chunk-JJUPY5MM.js";
18
- import "./chunk-3NFBZRPR.js";
16
+ import "./chunk-RUC7OULH.js";
17
+ import "./chunk-VAYGNQTE.js";
18
+ import "./chunk-Z5NXYJIG.js";
19
19
  import "./chunk-6UIGZD2N.js";
20
20
  import {
21
21
  logger
@@ -96,4 +96,4 @@ export {
96
96
  RebaseCommand,
97
97
  WorktreeValidationError
98
98
  };
99
- //# sourceMappingURL=rebase-55URTXZC.js.map
99
+ //# sourceMappingURL=rebase-4T5FQHNH.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import "./chunk-QRBOPFAA.js";
6
6
  import {
7
7
  IdentifierParser
8
- } from "./chunk-HVGQP44L.js";
8
+ } from "./chunk-55TB3FSG.js";
9
9
  import {
10
10
  calculatePortForBranch
11
11
  } from "./chunk-VU3QMIP2.js";
@@ -14,19 +14,19 @@ import {
14
14
  } from "./chunk-YETJNRQM.js";
15
15
  import {
16
16
  GitWorktreeManager
17
- } from "./chunk-M5XUCTTJ.js";
17
+ } from "./chunk-JC5HXN75.js";
18
18
  import "./chunk-VBFDVGAE.js";
19
+ import {
20
+ extractSettingsOverrides
21
+ } from "./chunk-GYCR2LOU.js";
19
22
  import {
20
23
  ProjectCapabilityDetector
21
24
  } from "./chunk-EBISESAP.js";
22
25
  import "./chunk-2ZPFJQ3B.js";
23
- import {
24
- extractSettingsOverrides
25
- } from "./chunk-GYCR2LOU.js";
26
26
  import {
27
27
  extractIssueNumber
28
- } from "./chunk-TR5MC2U6.js";
29
- import "./chunk-MLS5FAV7.js";
28
+ } from "./chunk-2W2FBL5G.js";
29
+ import "./chunk-IJ7IGJT3.js";
30
30
  import {
31
31
  SettingsManager
32
32
  } from "./chunk-VWNS6DH5.js";
@@ -34,7 +34,7 @@ import {
34
34
  extractPort,
35
35
  findEnvFileContainingVariable,
36
36
  parseEnvFile
37
- } from "./chunk-3NFBZRPR.js";
37
+ } from "./chunk-Z5NXYJIG.js";
38
38
  import "./chunk-6UIGZD2N.js";
39
39
  import {
40
40
  logger
@@ -291,4 +291,4 @@ Make sure the project is built (run 'il start' first)`
291
291
  export {
292
292
  RunCommand
293
293
  };
294
- //# sourceMappingURL=run-DP2U2CA2.js.map
294
+ //# sourceMappingURL=run-HNOP6WE2.js.map
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ IdentifierParser
4
+ } from "./chunk-55TB3FSG.js";
5
+ import {
6
+ GitWorktreeManager
7
+ } from "./chunk-JC5HXN75.js";
8
+ import {
9
+ extractIssueNumber
10
+ } from "./chunk-2W2FBL5G.js";
11
+ import "./chunk-IJ7IGJT3.js";
12
+ import {
13
+ SettingsManager
14
+ } from "./chunk-VWNS6DH5.js";
15
+ import {
16
+ getDotenvFlowFiles,
17
+ loadWorkspaceEnv
18
+ } from "./chunk-Z5NXYJIG.js";
19
+ import "./chunk-6UIGZD2N.js";
20
+ import {
21
+ logger
22
+ } from "./chunk-UYVWLISQ.js";
23
+
24
+ // src/commands/shell.ts
25
+ import path from "path";
26
+ import { execa } from "execa";
27
+ import fs from "fs-extra";
28
+ var ShellCommand = class {
29
+ constructor(gitWorktreeManager = new GitWorktreeManager(), identifierParser = new IdentifierParser(new GitWorktreeManager()), settingsManager = new SettingsManager()) {
30
+ this.gitWorktreeManager = gitWorktreeManager;
31
+ this.identifierParser = identifierParser;
32
+ this.settingsManager = settingsManager;
33
+ }
34
+ async execute(input) {
35
+ const parsed = input.identifier ? await this.parseExplicitInput(input.identifier) : await this.autoDetectFromCurrentDirectory();
36
+ logger.debug(`Parsed input: ${JSON.stringify(parsed)}`);
37
+ const worktree = await this.findWorktreeForIdentifier(parsed);
38
+ logger.debug(`Found worktree at: ${worktree.path}`);
39
+ const settings = await this.settingsManager.loadSettings();
40
+ const shouldLoadEnv = settings.sourceEnvOnStart ?? false;
41
+ let envVars = { ...process.env };
42
+ let loadedEnvFiles = [];
43
+ if (shouldLoadEnv) {
44
+ const envResult = loadWorkspaceEnv(worktree.path);
45
+ if (envResult.parsed) {
46
+ envVars = { ...envVars, ...envResult.parsed };
47
+ }
48
+ loadedEnvFiles = await this.getExistingEnvFiles(worktree.path);
49
+ }
50
+ const loomIdentifier = this.formatLoomIdentifier(parsed);
51
+ envVars.ILOOM_LOOM = loomIdentifier;
52
+ const shell = this.detectShell();
53
+ this.printSummary(worktree.path, shell, loadedEnvFiles, envVars, shouldLoadEnv);
54
+ await execa(shell, [], {
55
+ cwd: worktree.path,
56
+ env: envVars,
57
+ stdio: "inherit"
58
+ });
59
+ }
60
+ /**
61
+ * Parse explicit identifier input
62
+ */
63
+ async parseExplicitInput(identifier) {
64
+ const parsed = await this.identifierParser.parseForPatternDetection(identifier);
65
+ if (parsed.type === "description") {
66
+ throw new Error("Description input type is not supported in terminal command");
67
+ }
68
+ const result = {
69
+ type: parsed.type,
70
+ originalInput: parsed.originalInput,
71
+ autoDetected: false
72
+ };
73
+ if (parsed.number !== void 0) {
74
+ result.number = parsed.number;
75
+ }
76
+ if (parsed.branchName !== void 0) {
77
+ result.branchName = parsed.branchName;
78
+ }
79
+ return result;
80
+ }
81
+ /**
82
+ * Auto-detect identifier from current directory
83
+ */
84
+ async autoDetectFromCurrentDirectory() {
85
+ const currentDir = path.basename(process.cwd());
86
+ const prPattern = /_pr_(\d+)$/;
87
+ const prMatch = currentDir.match(prPattern);
88
+ if (prMatch == null ? void 0 : prMatch[1]) {
89
+ const prNumber = parseInt(prMatch[1], 10);
90
+ logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
91
+ return {
92
+ type: "pr",
93
+ number: prNumber,
94
+ originalInput: currentDir,
95
+ autoDetected: true
96
+ };
97
+ }
98
+ const issueNumber = extractIssueNumber(currentDir);
99
+ if (issueNumber !== null) {
100
+ logger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`);
101
+ return {
102
+ type: "issue",
103
+ number: issueNumber,
104
+ originalInput: currentDir,
105
+ autoDetected: true
106
+ };
107
+ }
108
+ const repoInfo = await this.gitWorktreeManager.getRepoInfo();
109
+ const currentBranch = repoInfo.currentBranch;
110
+ if (!currentBranch) {
111
+ throw new Error(
112
+ "Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\nExpected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix"
113
+ );
114
+ }
115
+ const branchIssueNumber = extractIssueNumber(currentBranch);
116
+ if (branchIssueNumber !== null) {
117
+ logger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`);
118
+ return {
119
+ type: "issue",
120
+ number: branchIssueNumber,
121
+ originalInput: currentBranch,
122
+ autoDetected: true
123
+ };
124
+ }
125
+ return {
126
+ type: "branch",
127
+ branchName: currentBranch,
128
+ originalInput: currentBranch,
129
+ autoDetected: true
130
+ };
131
+ }
132
+ /**
133
+ * Find worktree for the given identifier
134
+ */
135
+ async findWorktreeForIdentifier(parsed) {
136
+ let worktree = null;
137
+ if (parsed.type === "issue" && parsed.number !== void 0) {
138
+ worktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number);
139
+ } else if (parsed.type === "pr" && parsed.number !== void 0) {
140
+ const prNumber = typeof parsed.number === "number" ? parsed.number : Number(parsed.number);
141
+ if (isNaN(prNumber) || !isFinite(prNumber)) {
142
+ throw new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`);
143
+ }
144
+ worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, "");
145
+ } else if (parsed.type === "branch" && parsed.branchName) {
146
+ worktree = await this.gitWorktreeManager.findWorktreeForBranch(
147
+ parsed.branchName
148
+ );
149
+ }
150
+ if (!worktree) {
151
+ throw new Error(
152
+ `No worktree found for ${this.formatParsedInput(parsed)}. Run 'il start ${parsed.originalInput}' to create one.`
153
+ );
154
+ }
155
+ return worktree;
156
+ }
157
+ /**
158
+ * Format parsed input for display
159
+ */
160
+ formatParsedInput(parsed) {
161
+ const autoLabel = parsed.autoDetected ? " (auto-detected)" : "";
162
+ if (parsed.type === "issue") {
163
+ return `issue #${parsed.number}${autoLabel}`;
164
+ }
165
+ if (parsed.type === "pr") {
166
+ return `PR #${parsed.number}${autoLabel}`;
167
+ }
168
+ return `branch "${parsed.branchName}"${autoLabel}`;
169
+ }
170
+ /**
171
+ * Format loom identifier for ILOOM_LOOM env var
172
+ */
173
+ formatLoomIdentifier(parsed) {
174
+ if (parsed.type === "issue") {
175
+ return `issue-${parsed.number}`;
176
+ }
177
+ if (parsed.type === "pr") {
178
+ return `pr-${parsed.number}`;
179
+ }
180
+ return parsed.branchName ?? parsed.originalInput;
181
+ }
182
+ /**
183
+ * Detect shell based on platform and environment variables
184
+ */
185
+ detectShell() {
186
+ if (process.env.ILOOM_SHELL) {
187
+ return process.env.ILOOM_SHELL;
188
+ }
189
+ if (process.platform === "win32") {
190
+ return process.env.COMSPEC ?? "cmd.exe";
191
+ }
192
+ return process.env.SHELL ?? "/bin/bash";
193
+ }
194
+ /**
195
+ * Get list of existing dotenv-flow files in workspace
196
+ */
197
+ async getExistingEnvFiles(workspacePath) {
198
+ const files = getDotenvFlowFiles();
199
+ const existing = [];
200
+ for (const file of files) {
201
+ const fullPath = path.join(workspacePath, file);
202
+ if (await fs.pathExists(fullPath)) {
203
+ existing.push(file);
204
+ }
205
+ }
206
+ return existing;
207
+ }
208
+ /**
209
+ * Print summary of shell session
210
+ */
211
+ printSummary(workspacePath, shell, loadedEnvFiles, envVars, envEnabled) {
212
+ logger.info("Opening interactive shell");
213
+ logger.info(` Workspace: ${workspacePath}`);
214
+ logger.info(` Shell: ${shell}`);
215
+ if (envEnabled) {
216
+ if (loadedEnvFiles.length > 0) {
217
+ logger.info(` Env files: ${loadedEnvFiles.join(", ")}`);
218
+ } else {
219
+ logger.info(" Env files: (none found)");
220
+ }
221
+ const keyVars = ["PORT", "DATABASE_URL", "NODE_ENV"];
222
+ const presentVars = keyVars.filter((v) => envVars[v]);
223
+ if (presentVars.length > 0) {
224
+ const varSummary = presentVars.map((v) => {
225
+ const value = envVars[v];
226
+ const displayValue = value && value.length > 40 ? value.substring(0, 37) + "..." : value;
227
+ return `${v}=${displayValue}`;
228
+ }).join(", ");
229
+ logger.info(` Key vars: ${varSummary}`);
230
+ }
231
+ } else {
232
+ logger.info(" Env loading: disabled (set sourceEnvOnStart in settings to enable)");
233
+ }
234
+ logger.info("");
235
+ }
236
+ };
237
+ export {
238
+ ShellCommand
239
+ };
240
+ //# sourceMappingURL=shell-DE3HKJSM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/shell.ts"],"sourcesContent":["import path from 'path'\nimport { execa } from 'execa'\nimport fs from 'fs-extra'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { loadWorkspaceEnv, getDotenvFlowFiles } from '../utils/env.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface ShellCommandInput {\n\tidentifier?: string | undefined\n}\n\ninterface ParsedShellInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * ShellCommand - Open interactive shell with workspace environment\n * Loads dotenv-flow files when sourceEnvOnStart is configured\n */\nexport class ShellCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: ShellCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.debug(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Load settings to check sourceEnvOnStart\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\n\n\t\t// 4. Build environment variables\n\t\tlet envVars: Record<string, string> = { ...process.env as Record<string, string> }\n\t\tlet loadedEnvFiles: string[] = []\n\n\t\tif (shouldLoadEnv) {\n\t\t\tconst envResult = loadWorkspaceEnv(worktree.path)\n\t\t\tif (envResult.parsed) {\n\t\t\t\tenvVars = { ...envVars, ...envResult.parsed }\n\t\t\t}\n\t\t\t// Determine which files were actually loaded\n\t\t\tloadedEnvFiles = await this.getExistingEnvFiles(worktree.path)\n\t\t}\n\n\t\t// 5. Set ILOOM_LOOM for PS1 customization\n\t\tconst loomIdentifier = this.formatLoomIdentifier(parsed)\n\t\tenvVars.ILOOM_LOOM = loomIdentifier\n\n\t\t// 6. Detect shell\n\t\tconst shell = this.detectShell()\n\n\t\t// 7. Print summary\n\t\tthis.printSummary(worktree.path, shell, loadedEnvFiles, envVars, shouldLoadEnv)\n\n\t\t// 8. Launch interactive shell\n\t\tawait execa(shell, [], {\n\t\t\tcwd: worktree.path,\n\t\t\tenv: envVars,\n\t\t\tstdio: 'inherit',\n\t\t})\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedShellInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach terminal command\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in terminal command')\n\t\t}\n\n\t\tconst result: ParsedShellInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedShellInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedShellInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedShellInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Format loom identifier for ILOOM_LOOM env var\n\t */\n\tprivate formatLoomIdentifier(parsed: ParsedShellInput): string {\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue-${parsed.number}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `pr-${parsed.number}`\n\t\t}\n\t\treturn parsed.branchName ?? parsed.originalInput\n\t}\n\n\t/**\n\t * Detect shell based on platform and environment variables\n\t */\n\tprivate detectShell(): string {\n\t\t// ILOOM_SHELL takes highest priority\n\t\tif (process.env.ILOOM_SHELL) {\n\t\t\treturn process.env.ILOOM_SHELL\n\t\t}\n\n\t\t// Platform-specific detection\n\t\tif (process.platform === 'win32') {\n\t\t\t// Windows: prefer PowerShell, fall back to COMSPEC (cmd.exe)\n\t\t\treturn process.env.COMSPEC ?? 'cmd.exe'\n\t\t}\n\n\t\t// Unix/macOS: use SHELL or fall back to /bin/bash\n\t\treturn process.env.SHELL ?? '/bin/bash'\n\t}\n\n\t/**\n\t * Get list of existing dotenv-flow files in workspace\n\t */\n\tprivate async getExistingEnvFiles(workspacePath: string): Promise<string[]> {\n\t\tconst files = getDotenvFlowFiles()\n\t\tconst existing: string[] = []\n\n\t\tfor (const file of files) {\n\t\t\tconst fullPath = path.join(workspacePath, file)\n\t\t\tif (await fs.pathExists(fullPath)) {\n\t\t\t\texisting.push(file)\n\t\t\t}\n\t\t}\n\n\t\treturn existing\n\t}\n\n\t/**\n\t * Print summary of shell session\n\t */\n\tprivate printSummary(\n\t\tworkspacePath: string,\n\t\tshell: string,\n\t\tloadedEnvFiles: string[],\n\t\tenvVars: Record<string, string>,\n\t\tenvEnabled: boolean\n\t): void {\n\t\tlogger.info('Opening interactive shell')\n\t\tlogger.info(` Workspace: ${workspacePath}`)\n\t\tlogger.info(` Shell: ${shell}`)\n\n\t\tif (envEnabled) {\n\t\t\tif (loadedEnvFiles.length > 0) {\n\t\t\t\tlogger.info(` Env files: ${loadedEnvFiles.join(', ')}`)\n\t\t\t} else {\n\t\t\t\tlogger.info(' Env files: (none found)')\n\t\t\t}\n\n\t\t\t// Print key environment variables if present\n\t\t\tconst keyVars = ['PORT', 'DATABASE_URL', 'NODE_ENV']\n\t\t\tconst presentVars = keyVars.filter(v => envVars[v])\n\t\t\tif (presentVars.length > 0) {\n\t\t\t\tconst varSummary = presentVars.map(v => {\n\t\t\t\t\tconst value = envVars[v]\n\t\t\t\t\t// Truncate long values like DATABASE_URL\n\t\t\t\t\tconst displayValue = value && value.length > 40\n\t\t\t\t\t\t? value.substring(0, 37) + '...'\n\t\t\t\t\t\t: value\n\t\t\t\t\treturn `${v}=${displayValue}`\n\t\t\t\t}).join(', ')\n\t\t\t\tlogger.info(` Key vars: ${varSummary}`)\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.info(' Env loading: disabled (set sourceEnvOnStart in settings to enable)')\n\t\t}\n\n\t\tlogger.info('')\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SessionSummaryService
4
- } from "./chunk-HHDSIE72.js";
4
+ } from "./chunk-TMZAVPGF.js";
5
5
  import "./chunk-QHA67Q7A.js";
6
6
  import {
7
7
  GitWorktreeManager
8
- } from "./chunk-M5XUCTTJ.js";
8
+ } from "./chunk-JC5HXN75.js";
9
9
  import {
10
10
  extractIssueNumber
11
- } from "./chunk-TR5MC2U6.js";
11
+ } from "./chunk-2W2FBL5G.js";
12
12
  import {
13
13
  MetadataManager
14
- } from "./chunk-MLS5FAV7.js";
14
+ } from "./chunk-IJ7IGJT3.js";
15
15
  import "./chunk-VWNS6DH5.js";
16
16
  import "./chunk-KO2FOMHL.js";
17
- import "./chunk-75B2HZZ5.js";
18
- import "./chunk-JJUPY5MM.js";
19
- import "./chunk-3NFBZRPR.js";
17
+ import "./chunk-RUC7OULH.js";
18
+ import "./chunk-VAYGNQTE.js";
19
+ import "./chunk-Z5NXYJIG.js";
20
20
  import {
21
21
  getLogger
22
22
  } from "./chunk-6UIGZD2N.js";
@@ -241,4 +241,4 @@ Please provide an issue number, PR number, or branch name.`
241
241
  export {
242
242
  SummaryCommand
243
243
  };
244
- //# sourceMappingURL=summary-J3CJSM7L.js.map
244
+ //# sourceMappingURL=summary-GDT7DTRI.js.map
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  findMainWorktreePath
4
- } from "./chunk-TR5MC2U6.js";
5
- import "./chunk-MLS5FAV7.js";
4
+ } from "./chunk-2W2FBL5G.js";
5
+ import "./chunk-IJ7IGJT3.js";
6
6
  import {
7
7
  SettingsManager
8
8
  } from "./chunk-VWNS6DH5.js";
@@ -51,4 +51,4 @@ var TestGitCommand = class {
51
51
  export {
52
52
  TestGitCommand
53
53
  };
54
- //# sourceMappingURL=test-git-QLAIBJLX.js.map
54
+ //# sourceMappingURL=test-git-YMAE57UP.js.map
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  generateWorktreePath
4
- } from "./chunk-TR5MC2U6.js";
5
- import "./chunk-MLS5FAV7.js";
4
+ } from "./chunk-2W2FBL5G.js";
5
+ import "./chunk-IJ7IGJT3.js";
6
6
  import {
7
7
  SettingsManager
8
8
  } from "./chunk-VWNS6DH5.js";
@@ -67,4 +67,4 @@ var TestPrefixCommand = class {
67
67
  export {
68
68
  TestPrefixCommand
69
69
  };
70
- //# sourceMappingURL=test-prefix-6YM2ZOON.js.map
70
+ //# sourceMappingURL=test-prefix-YCKL6CMT.js.map
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  detectITerm2,
4
4
  openMultipleTerminalWindows
5
- } from "./chunk-JJUPY5MM.js";
6
- import "./chunk-3NFBZRPR.js";
5
+ } from "./chunk-VAYGNQTE.js";
6
+ import "./chunk-Z5NXYJIG.js";
7
7
  import {
8
8
  logger
9
9
  } from "./chunk-UYVWLISQ.js";
@@ -67,4 +67,4 @@ var TestTabsCommand = class {
67
67
  export {
68
68
  TestTabsCommand
69
69
  };
70
- //# sourceMappingURL=test-tabs-JGO3VOXJ.js.map
70
+ //# sourceMappingURL=test-tabs-3SCJWRKT.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloom/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Control plane for maintaining alignment between you and Claude Code as you work across multiple issues using isolated environments, visible context, and multi-agent workflows to scale understanding, not just output",
5
5
  "keywords": [
6
6
  "ai",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/LoomLauncher.ts","../src/utils/ide.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { openTerminalWindow, openMultipleTerminalWindows } from '../utils/terminal.js'\nimport type { TerminalWindowOptions } from '../utils/terminal.js'\nimport { openIdeWindow } from '../utils/ide.js'\nimport { getDevServerLaunchCommand } from '../utils/dev-server.js'\nimport { generateColorFromBranchName, hexToRgb } from '../utils/color.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { ClaudeContextManager } from './ClaudeContextManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport type { Capability } from '../types/loom.js'\nimport { getDotenvFlowFiles } from '../utils/env.js'\n\nexport interface LaunchLoomOptions {\n\tenableClaude: boolean\n\tenableCode: boolean\n\tenableDevServer: boolean\n\tenableTerminal: boolean\n\tworktreePath: string\n\tbranchName: string\n\tport?: number\n\tcapabilities: Capability[]\n\tworkflowType: 'issue' | 'pr' | 'regular'\n\tidentifier: string | number\n\ttitle?: string\n\toneShot?: import('../types/index.js').OneShotMode\n\tsetArguments?: string[] // Raw --set arguments to forward\n\texecutablePath?: string // Executable path to use for spin command\n\tsourceEnvOnStart?: boolean // defaults to false if undefined\n\tcolorTerminal?: boolean // defaults to true if undefined\n\tcolorHex?: string // Pre-calculated hex color from metadata, avoids recalculation\n}\n\n/**\n * LoomLauncher orchestrates opening loom components\n */\nexport class LoomLauncher {\n\tprivate claudeContext: ClaudeContextManager\n\tprivate settings?: SettingsManager\n\n\tconstructor(claudeContext?: ClaudeContextManager, settings?: SettingsManager) {\n\t\tthis.claudeContext = claudeContext ?? new ClaudeContextManager()\n\t\tif (settings !== undefined) {\n\t\t\tthis.settings = settings\n\t\t}\n\t}\n\n\t/**\n\t * Launch loom components based on individual flags\n\t */\n\tasync launchLoom(options: LaunchLoomOptions): Promise<void> {\n\t\tconst { enableClaude, enableCode, enableDevServer, enableTerminal } = options\n\n\t\tgetLogger().debug(`Launching loom components: Claude=${enableClaude}, Code=${enableCode}, DevServer=${enableDevServer}, Terminal=${enableTerminal}`)\n\n\t\tconst launchPromises: Promise<void>[] = []\n\n\t\t// Launch VSCode if enabled\n\t\tif (enableCode) {\n\t\t\tgetLogger().debug('Launching VSCode')\n\t\t\tlaunchPromises.push(this.launchVSCode(options))\n\t\t}\n\n\t\t// Build array of terminals to launch\n\t\tconst terminalsToLaunch: Array<{\n\t\t\ttype: 'claude' | 'devServer' | 'terminal'\n\t\t\toptions: TerminalWindowOptions\n\t\t}> = []\n\n\t\tif (enableDevServer) {\n\t\t\tterminalsToLaunch.push({\n\t\t\t\ttype: 'devServer',\n\t\t\t\toptions: await this.buildDevServerTerminalOptions(options),\n\t\t\t})\n\t\t}\n\n\t\tif (enableTerminal) {\n\t\t\tterminalsToLaunch.push({\n\t\t\t\ttype: 'terminal',\n\t\t\t\toptions: this.buildStandaloneTerminalOptions(options),\n\t\t\t})\n\t\t}\n\n\t\tif (enableClaude) {\n\t\t\tterminalsToLaunch.push({\n\t\t\t\ttype: 'claude',\n\t\t\t\toptions: await this.buildClaudeTerminalOptions(options),\n\t\t\t})\n\t\t}\n\n\t\t// Launch terminals based on count\n\t\tif (terminalsToLaunch.length > 1) {\n\t\t\t// Multiple terminals - launch as tabs in single window\n\t\t\tgetLogger().debug(`Launching ${terminalsToLaunch.length} terminals in single window`)\n\t\t\tlaunchPromises.push(this.launchMultipleTerminals(terminalsToLaunch, options))\n\t\t} else if (terminalsToLaunch.length === 1) {\n\t\t\t// Single terminal - launch standalone\n\t\t\tconst terminal = terminalsToLaunch[0]\n\t\t\tif (!terminal) {\n\t\t\t\tthrow new Error('Terminal configuration is undefined')\n\t\t\t}\n\t\t\tconst terminalType = terminal.type\n\t\t\tgetLogger().debug(`Launching single ${terminalType} terminal`)\n\n\t\t\tif (terminalType === 'claude') {\n\t\t\t\tlaunchPromises.push(this.launchClaudeTerminal(options))\n\t\t\t} else if (terminalType === 'devServer') {\n\t\t\t\tlaunchPromises.push(this.launchDevServerTerminal(options))\n\t\t\t} else {\n\t\t\t\tlaunchPromises.push(this.launchStandaloneTerminal(options))\n\t\t\t}\n\t\t}\n\n\t\t// Wait for all components to launch\n\t\tawait Promise.all(launchPromises)\n\n\t\tgetLogger().success('loom launched successfully')\n\t}\n\n\t/**\n\t * Launch IDE (VSCode or configured alternative)\n\t */\n\tprivate async launchVSCode(options: LaunchLoomOptions): Promise<void> {\n\t\tconst ideConfig = await this.settings?.loadSettings().then((s) => s.ide)\n\t\tawait openIdeWindow(options.worktreePath, ideConfig)\n\t\tgetLogger().info('IDE opened')\n\t}\n\n\t/**\n\t * Launch Claude terminal\n\t */\n\tprivate async launchClaudeTerminal(options: LaunchLoomOptions): Promise<void> {\n\t\tawait this.claudeContext.launchWithContext({\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\ttype: options.workflowType,\n\t\t\tidentifier: options.identifier,\n\t\t\tbranchName: options.branchName,\n\t\t\t...(options.title && { title: options.title }),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t\toneShot: options.oneShot ?? 'default',\n\t\t\t...(options.setArguments && { setArguments: options.setArguments }),\n\t\t\t...(options.executablePath && { executablePath: options.executablePath }),\n\t\t})\n\t\tgetLogger().info('Claude terminal opened')\n\t}\n\n\t/**\n\t * Launch dev server terminal\n\t */\n\tprivate async launchDevServerTerminal(options: LaunchLoomOptions): Promise<void> {\n\t\tconst devServerCommand = await getDevServerLaunchCommand(\n\t\t\toptions.worktreePath,\n\t\t\toptions.port,\n\t\t\toptions.capabilities\n\t\t)\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\tawait openTerminalWindow({\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: devServerCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && this.hasAnyEnvFiles(options.worktreePath),\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t})\n\t\tgetLogger().info('Dev server terminal opened')\n\t}\n\n\t/**\n\t * Launch standalone terminal (no command, just workspace with env vars)\n\t */\n\tprivate async launchStandaloneTerminal(options: LaunchLoomOptions): Promise<void> {\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\tawait openTerminalWindow({\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && this.hasAnyEnvFiles(options.worktreePath),\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t})\n\t\tgetLogger().info('Standalone terminal opened')\n\t}\n\n\t/**\n\t * Build terminal options for Claude\n\t */\n\tprivate async buildClaudeTerminalOptions(\n\t\toptions: LaunchLoomOptions\n\t): Promise<TerminalWindowOptions> {\n\t\tconst hasEnvFile = this.hasAnyEnvFiles(options.worktreePath)\n\t\tconst claudeTitle = `Claude - ${this.formatIdentifier(options.workflowType, options.identifier)}`\n\n\t\tconst executable = options.executablePath ?? 'iloom'\n\t\tlet claudeCommand = `${executable} spin`\n\t\tif (options.oneShot !== undefined && options.oneShot !== 'default') {\n\t\t\tclaudeCommand += ` --one-shot=${options.oneShot}`\n\t\t}\n\t\tif (options.setArguments && options.setArguments.length > 0) {\n\t\t\tfor (const setArg of options.setArguments) {\n\t\t\t\tclaudeCommand += ` --set ${setArg}`\n\t\t\t}\n\t\t}\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\treturn {\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: claudeCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\ttitle: claudeTitle,\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && hasEnvFile,\n\t\t\t...(options.port !== undefined && { port: options.port, includePortExport: true }),\n\t\t}\n\t}\n\n\t/**\n\t * Build terminal options for dev server\n\t */\n\tprivate async buildDevServerTerminalOptions(\n\t\toptions: LaunchLoomOptions\n\t): Promise<TerminalWindowOptions> {\n\t\tconst devServerCommand = await getDevServerLaunchCommand(\n\t\t\toptions.worktreePath,\n\t\t\toptions.port,\n\t\t\toptions.capabilities\n\t\t)\n\t\tconst hasEnvFile = this.hasAnyEnvFiles(options.worktreePath)\n\t\tconst devServerTitle = `Dev Server - ${this.formatIdentifier(options.workflowType, options.identifier)}`\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\treturn {\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\tcommand: devServerCommand,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\ttitle: devServerTitle,\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && hasEnvFile,\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t}\n\t}\n\n\t/**\n\t * Build terminal options for standalone terminal (no command)\n\t */\n\tprivate buildStandaloneTerminalOptions(\n\t\toptions: LaunchLoomOptions\n\t): TerminalWindowOptions {\n\t\tconst hasEnvFile = this.hasAnyEnvFiles(options.worktreePath)\n\t\tconst terminalTitle = `Terminal - ${this.formatIdentifier(options.workflowType, options.identifier)}`\n\n\t\t// Only generate color if terminal coloring is enabled (default: true)\n\t\tconst backgroundColor = (options.colorTerminal ?? true)\n\t\t\t? options.colorHex\n\t\t\t\t? hexToRgb(options.colorHex)\n\t\t\t\t: generateColorFromBranchName(options.branchName).rgb\n\t\t\t: undefined\n\n\t\treturn {\n\t\t\tworkspacePath: options.worktreePath,\n\t\t\t...(backgroundColor && { backgroundColor }),\n\t\t\ttitle: terminalTitle,\n\t\t\tincludeEnvSetup: (options.sourceEnvOnStart ?? false) && hasEnvFile,\n\t\t\tincludePortExport: options.capabilities.includes('web'),\n\t\t\t...(options.port !== undefined && { port: options.port }),\n\t\t}\n\t}\n\n\t/**\n\t * Launch multiple terminals (2+) as tabs in single window\n\t */\n\tprivate async launchMultipleTerminals(\n\t\tterminals: Array<{ type: string; options: TerminalWindowOptions }>,\n\t\t_options: LaunchLoomOptions\n\t): Promise<void> {\n\t\tconst terminalOptions = terminals.map((t) => t.options)\n\n\t\tawait openMultipleTerminalWindows(terminalOptions)\n\n\t\tconst terminalTypes = terminals.map((t) => t.type).join(' + ')\n\t\tgetLogger().info(`Multiple terminals opened: ${terminalTypes}`)\n\t}\n\n\t/**\n\t * Check if any dotenv-flow files exist in the workspace\n\t * Checks all files: .env, .env.local, .env.{NODE_ENV}, .env.{NODE_ENV}.local\n\t */\n\tprivate hasAnyEnvFiles(workspacePath: string): boolean {\n\t\tconst envFiles = getDotenvFlowFiles()\n\t\treturn envFiles.some(file => existsSync(join(workspacePath, file)))\n\t}\n\n\t/**\n\t * Format identifier for terminal tab titles\n\t */\n\tprivate formatIdentifier(workflowType: 'issue' | 'pr' | 'regular', identifier: string | number): string {\n\t\tif (workflowType === 'issue') {\n\t\t\treturn `Issue #${identifier}`\n\t\t} else if (workflowType === 'pr') {\n\t\t\treturn `PR #${identifier}`\n\t\t} else {\n\t\t\treturn `Branch: ${identifier}`\n\t\t}\n\t}\n}\n","import { execa } from 'execa'\nimport { logger } from './logger.js'\nimport type { IdeSettings } from '../lib/SettingsManager.js'\n\n// IDE preset configuration\nconst IDE_PRESETS = {\n\tvscode: { command: 'code', name: 'Visual Studio Code', args: [] },\n\tcursor: { command: 'cursor', name: 'Cursor', args: [] },\n\twebstorm: { command: 'webstorm', name: 'WebStorm', args: ['--nosplash'] },\n\tsublime: { command: 'subl', name: 'Sublime Text', args: [] },\n\tintellij: { command: 'idea', name: 'IntelliJ IDEA', args: ['--nosplash'] },\n\twindsurf: { command: 'surf', name: 'Windsurf', args: [] },\n\tantigravity: { command: 'agy', name: 'Antigravity', args: [] },\n} as const\n\ntype IdePreset = keyof typeof IDE_PRESETS\n\n// Resolve IDE configuration to command and args\nexport function getIdeConfig(ideSettings?: IdeSettings): {\n\tcommand: string\n\targs: string[]\n\tname: string\n} {\n\t// Default to vscode if not configured\n\tconst type = ideSettings?.type ?? 'vscode'\n\n\tconst preset = IDE_PRESETS[type as IdePreset]\n\treturn {\n\t\tcommand: preset.command,\n\t\targs: [...preset.args],\n\t\tname: preset.name,\n\t}\n}\n\n// Check if IDE is available\nexport async function isIdeAvailable(command: string): Promise<boolean> {\n\ttry {\n\t\tawait execa('command', ['-v', command], { shell: true, timeout: 5000 })\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// Get installation hint for IDE\nfunction getInstallHint(type: string): string {\n\tconst hints: Record<string, string> = {\n\t\tvscode:\n\t\t\t'Install command-line tools: Open VSCode > Command Palette > \"Shell Command: Install \\'code\\' command in PATH\"',\n\t\tcursor:\n\t\t\t'Install command-line tools: Open Cursor > Command Palette > \"Install \\'cursor\\' command in PATH\"',\n\t\twebstorm: 'Install via JetBrains Toolbox > Settings > Shell Scripts > Enable',\n\t\tsublime:\n\t\t\t'Create symlink: ln -s \"/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl\" /usr/local/bin/subl',\n\t\tintellij: 'Install via JetBrains Toolbox > Settings > Shell Scripts > Enable',\n\t\twindsurf:\n\t\t\t'Install command-line tools during Windsurf installation or create symlink manually',\n\t\tantigravity:\n\t\t\t'Install command-line tools during Antigravity installation or create symlink manually',\n\t}\n\treturn hints[type] ?? `Ensure the IDE command is available in your PATH`\n}\n\n// Open IDE window for workspace\nexport async function openIdeWindow(\n\tworkspacePath: string,\n\tideSettings?: IdeSettings\n): Promise<void> {\n\tconst config = getIdeConfig(ideSettings)\n\tconst available = await isIdeAvailable(config.command)\n\n\tif (!available) {\n\t\tconst type = ideSettings?.type ?? 'vscode'\n\t\tthrow new Error(\n\t\t\t`${config.name} is not available. The \"${config.command}\" command was not found in PATH.\\n` +\n\t\t\t\tgetInstallHint(type)\n\t\t)\n\t}\n\n\ttry {\n\t\tawait execa(config.command, [...config.args, workspacePath])\n\t\tlogger.debug(`Opened ${config.name} for workspace: ${workspacePath}`)\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to open ${config.name}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t)\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -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 * 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":[]}