@iloom/cli 0.3.1 → 0.3.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.
- package/README.md +5 -3
- package/dist/{BranchNamingService-OMWKUYMM.js → BranchNamingService-JYO746H7.js} +2 -2
- package/dist/ClaudeContextManager-5WPRJIIW.js +15 -0
- package/dist/ClaudeService-Z4KA7QOW.js +14 -0
- package/dist/{GitHubService-EBOETDIW.js → GitHubService-UAMH7DMF.js} +2 -2
- package/dist/{LoomLauncher-JF7JZMTZ.js → LoomLauncher-FVZECY3C.js} +24 -13
- package/dist/LoomLauncher-FVZECY3C.js.map +1 -0
- package/dist/README.md +5 -3
- package/dist/{SettingsManager-ZCWJ56WP.js → SettingsManager-MTVX57WR.js} +2 -2
- package/dist/{chunk-4BGK7T6X.js → chunk-5NTD4MCZ.js} +23 -7
- package/dist/chunk-5NTD4MCZ.js.map +1 -0
- package/dist/{chunk-U5QDY7ZD.js → chunk-A2P7NZTB.js} +7 -7
- package/dist/{chunk-XPKDPZ5D.js → chunk-C7ASXK6J.js} +2 -2
- package/dist/{chunk-G2IEYOLQ.js → chunk-F4ENT6AC.js} +16 -1
- package/dist/chunk-F4ENT6AC.js.map +1 -0
- package/dist/{chunk-IXKLYTWO.js → chunk-FOV7RRQ2.js} +8 -8
- package/dist/{chunk-RO26VS3W.js → chunk-FXMLNKLT.js} +2 -2
- package/dist/{chunk-ZBQVSHVT.js → chunk-GOG3ZB7O.js} +14 -8
- package/dist/chunk-GOG3ZB7O.js.map +1 -0
- package/dist/{chunk-JQFO7QQN.js → chunk-GVICXJHW.js} +2 -2
- package/dist/{chunk-JQFO7QQN.js.map → chunk-GVICXJHW.js.map} +1 -1
- package/dist/{chunk-HBYZH6GD.js → chunk-KG343GSG.js} +90 -47
- package/dist/chunk-KG343GSG.js.map +1 -0
- package/dist/{chunk-TSKY3JI7.js → chunk-OAP6SASD.js} +2 -2
- package/dist/chunk-OXAM2WVC.js +68 -0
- package/dist/chunk-OXAM2WVC.js.map +1 -0
- package/dist/{chunk-4E4LD3QR.js → chunk-PRE7KTM4.js} +2 -2
- package/dist/{chunk-ZE74H5BR.js → chunk-RW54ZMBM.js} +26 -20
- package/dist/chunk-RW54ZMBM.js.map +1 -0
- package/dist/{chunk-O5OH5MRX.js → chunk-UCZ24SUE.js} +3 -3
- package/dist/chunk-UCZ24SUE.js.map +1 -0
- package/dist/{chunk-INW24J2W.js → chunk-UERERX6M.js} +2 -2
- package/dist/{chunk-IP7SMKIF.js → chunk-UJL4HI2R.js} +59 -60
- package/dist/chunk-UJL4HI2R.js.map +1 -0
- package/dist/{chunk-ZWFBBPJI.js → chunk-ZLIHIUDQ.js} +4 -2
- package/dist/chunk-ZLIHIUDQ.js.map +1 -0
- package/dist/{claude-LUZ35IMK.js → claude-KIZYXTSG.js} +4 -2
- package/dist/{cleanup-3MONU4PU.js → cleanup-6WYUD5SN.js} +17 -15
- package/dist/{cleanup-3MONU4PU.js.map → cleanup-6WYUD5SN.js.map} +1 -1
- package/dist/cli.js +207 -71
- package/dist/cli.js.map +1 -1
- package/dist/{contribute-UWJAGIG7.js → contribute-7YJHZTO7.js} +3 -2
- package/dist/{contribute-UWJAGIG7.js.map → contribute-7YJHZTO7.js.map} +1 -1
- package/dist/{feedback-W3BXTGIM.js → feedback-752MGDPG.js} +10 -8
- package/dist/{feedback-W3BXTGIM.js.map → feedback-752MGDPG.js.map} +1 -1
- package/dist/{git-34Z6QVDS.js → git-TAFVDB3J.js} +5 -2
- package/dist/{ignite-KVJEFXNO.js → ignite-OAMDUN27.js} +13 -11
- package/dist/{ignite-KVJEFXNO.js.map → ignite-OAMDUN27.js.map} +1 -1
- package/dist/index.d.ts +20 -7
- package/dist/index.js +276 -294
- package/dist/index.js.map +1 -1
- package/dist/{init-L55Q73H4.js → init-7IIM35LQ.js} +12 -9
- package/dist/{init-L55Q73H4.js.map → init-7IIM35LQ.js.map} +1 -1
- package/dist/mcp/issue-management-server.js +8 -1
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/{neon-helpers-WPUACUVC.js → neon-helpers-5HBYO2VP.js} +2 -2
- package/dist/{open-LNRZL3UU.js → open-FCHKQ77R.js} +27 -14
- package/dist/open-FCHKQ77R.js.map +1 -0
- package/dist/prompts/init-prompt.txt +7 -1
- package/dist/{rebase-C4WNCVGM.js → rebase-Q7WXK566.js} +10 -8
- package/dist/{rebase-C4WNCVGM.js.map → rebase-Q7WXK566.js.map} +1 -1
- package/dist/{run-IOGNIOYN.js → run-SJPM6YRI.js} +27 -14
- package/dist/run-SJPM6YRI.js.map +1 -0
- package/dist/schema/settings.schema.json +1 -1
- package/dist/{test-git-J7I5MFYH.js → test-git-DEUE656D.js} +5 -5
- package/dist/{test-prefix-ZCONBCBX.js → test-prefix-Y6Z6ZHSF.js} +5 -5
- package/dist/{test-tabs-RXDBZ6J7.js → test-tabs-PRMRSHKI.js} +3 -2
- package/dist/{test-tabs-RXDBZ6J7.js.map → test-tabs-PRMRSHKI.js.map} +1 -1
- package/package.json +1 -1
- package/dist/ClaudeContextManager-3VXA6UPR.js +0 -13
- package/dist/ClaudeService-6CPK43N4.js +0 -12
- package/dist/LoomLauncher-JF7JZMTZ.js.map +0 -1
- package/dist/chunk-4BGK7T6X.js.map +0 -1
- package/dist/chunk-G2IEYOLQ.js.map +0 -1
- package/dist/chunk-HBYZH6GD.js.map +0 -1
- package/dist/chunk-IP7SMKIF.js.map +0 -1
- package/dist/chunk-O5OH5MRX.js.map +0 -1
- package/dist/chunk-ZBQVSHVT.js.map +0 -1
- package/dist/chunk-ZE74H5BR.js.map +0 -1
- package/dist/chunk-ZWFBBPJI.js.map +0 -1
- package/dist/open-LNRZL3UU.js.map +0 -1
- package/dist/run-IOGNIOYN.js.map +0 -1
- package/dist/terminal-BIRBZ4AZ.js +0 -16
- package/dist/terminal-BIRBZ4AZ.js.map +0 -1
- /package/dist/{BranchNamingService-OMWKUYMM.js.map → BranchNamingService-JYO746H7.js.map} +0 -0
- /package/dist/{ClaudeContextManager-3VXA6UPR.js.map → ClaudeContextManager-5WPRJIIW.js.map} +0 -0
- /package/dist/{ClaudeService-6CPK43N4.js.map → ClaudeService-Z4KA7QOW.js.map} +0 -0
- /package/dist/{GitHubService-EBOETDIW.js.map → GitHubService-UAMH7DMF.js.map} +0 -0
- /package/dist/{SettingsManager-ZCWJ56WP.js.map → SettingsManager-MTVX57WR.js.map} +0 -0
- /package/dist/{chunk-U5QDY7ZD.js.map → chunk-A2P7NZTB.js.map} +0 -0
- /package/dist/{chunk-XPKDPZ5D.js.map → chunk-C7ASXK6J.js.map} +0 -0
- /package/dist/{chunk-IXKLYTWO.js.map → chunk-FOV7RRQ2.js.map} +0 -0
- /package/dist/{chunk-RO26VS3W.js.map → chunk-FXMLNKLT.js.map} +0 -0
- /package/dist/{chunk-TSKY3JI7.js.map → chunk-OAP6SASD.js.map} +0 -0
- /package/dist/{chunk-4E4LD3QR.js.map → chunk-PRE7KTM4.js.map} +0 -0
- /package/dist/{chunk-INW24J2W.js.map → chunk-UERERX6M.js.map} +0 -0
- /package/dist/{claude-LUZ35IMK.js.map → claude-KIZYXTSG.js.map} +0 -0
- /package/dist/{git-34Z6QVDS.js.map → git-TAFVDB3J.js.map} +0 -0
- /package/dist/{neon-helpers-WPUACUVC.js.map → neon-helpers-5HBYO2VP.js.map} +0 -0
- /package/dist/{test-git-J7I5MFYH.js.map → test-git-DEUE656D.js.map} +0 -0
- /package/dist/{test-prefix-ZCONBCBX.js.map → test-prefix-Y6Z6ZHSF.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -226,7 +226,7 @@ var init_SettingsManager = __esm({
|
|
|
226
226
|
IloomSettingsSchema = z.object({
|
|
227
227
|
mainBranch: z.string().min(1, "Settings 'mainBranch' cannot be empty").optional().describe("Name of the main/primary branch for the repository"),
|
|
228
228
|
sourceEnvOnStart: z.boolean().default(false).describe(
|
|
229
|
-
"Source .env
|
|
229
|
+
"Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). Files are sourced in precedence order so later files override earlier ones. NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env file compatibility. WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. Before enabling, verify ALL your .env.* files do not contain unquoted special characters (e.g., database URLs with ?, &, or other shell metacharacters). Shell compatibility issues may cause processes to fail or behave unexpectedly."
|
|
230
230
|
),
|
|
231
231
|
worktreePrefix: z.string().optional().refine(
|
|
232
232
|
(val) => {
|
|
@@ -282,7 +282,7 @@ var init_SettingsManager = __esm({
|
|
|
282
282
|
IloomSettingsSchemaNoDefaults = z.object({
|
|
283
283
|
mainBranch: z.string().min(1, "Settings 'mainBranch' cannot be empty").optional().describe("Name of the main/primary branch for the repository"),
|
|
284
284
|
sourceEnvOnStart: z.boolean().optional().describe(
|
|
285
|
-
"Source .env
|
|
285
|
+
"Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). Files are sourced in precedence order so later files override earlier ones. NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env compatibility. WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. Before enabling, verify ALL your .env.* files do not contain unquoted special characters (e.g., database URLs with ?, &, or other shell metacharacters). Shell compatibility issues may cause processes to fail or behave unexpectedly."
|
|
286
286
|
),
|
|
287
287
|
worktreePrefix: z.string().optional().refine(
|
|
288
288
|
(val) => {
|
|
@@ -433,8 +433,8 @@ Note: CLI overrides were applied. Check your --set arguments.`);
|
|
|
433
433
|
*/
|
|
434
434
|
formatAllZodErrors(error, settingsPath) {
|
|
435
435
|
const errorMessages = error.issues.map((issue) => {
|
|
436
|
-
const
|
|
437
|
-
return ` - ${
|
|
436
|
+
const path6 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
437
|
+
return ` - ${path6}: ${issue.message}`;
|
|
438
438
|
});
|
|
439
439
|
return new Error(
|
|
440
440
|
`Settings validation failed at ${settingsPath}:
|
|
@@ -540,237 +540,6 @@ ${errorMessages.join("\n")}`
|
|
|
540
540
|
}
|
|
541
541
|
});
|
|
542
542
|
|
|
543
|
-
// src/utils/terminal.ts
|
|
544
|
-
var terminal_exports = {};
|
|
545
|
-
__export(terminal_exports, {
|
|
546
|
-
detectITerm2: () => detectITerm2,
|
|
547
|
-
detectPlatform: () => detectPlatform,
|
|
548
|
-
openDualTerminalWindow: () => openDualTerminalWindow,
|
|
549
|
-
openMultipleTerminalWindows: () => openMultipleTerminalWindows,
|
|
550
|
-
openTerminalWindow: () => openTerminalWindow
|
|
551
|
-
});
|
|
552
|
-
import { execa as execa3 } from "execa";
|
|
553
|
-
import { existsSync } from "fs";
|
|
554
|
-
function detectPlatform() {
|
|
555
|
-
const platform = process.platform;
|
|
556
|
-
if (platform === "darwin") return "darwin";
|
|
557
|
-
if (platform === "linux") return "linux";
|
|
558
|
-
if (platform === "win32") return "win32";
|
|
559
|
-
return "unsupported";
|
|
560
|
-
}
|
|
561
|
-
async function detectITerm2() {
|
|
562
|
-
const platform = detectPlatform();
|
|
563
|
-
if (platform !== "darwin") return false;
|
|
564
|
-
return existsSync("/Applications/iTerm.app");
|
|
565
|
-
}
|
|
566
|
-
async function openTerminalWindow(options) {
|
|
567
|
-
const platform = detectPlatform();
|
|
568
|
-
if (platform !== "darwin") {
|
|
569
|
-
throw new Error(
|
|
570
|
-
`Terminal window launching not yet supported on ${platform}. Currently only macOS is supported.`
|
|
571
|
-
);
|
|
572
|
-
}
|
|
573
|
-
const hasITerm2 = await detectITerm2();
|
|
574
|
-
const applescript = hasITerm2 ? buildITerm2SingleTabScript(options) : buildAppleScript(options);
|
|
575
|
-
try {
|
|
576
|
-
await execa3("osascript", ["-e", applescript]);
|
|
577
|
-
if (!hasITerm2) {
|
|
578
|
-
await execa3("osascript", ["-e", 'tell application "Terminal" to activate']);
|
|
579
|
-
}
|
|
580
|
-
} catch (error) {
|
|
581
|
-
throw new Error(
|
|
582
|
-
`Failed to open terminal window: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
583
|
-
);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
function buildAppleScript(options) {
|
|
587
|
-
const {
|
|
588
|
-
workspacePath,
|
|
589
|
-
command,
|
|
590
|
-
backgroundColor,
|
|
591
|
-
port,
|
|
592
|
-
includeEnvSetup,
|
|
593
|
-
includePortExport
|
|
594
|
-
} = options;
|
|
595
|
-
const commands = [];
|
|
596
|
-
if (workspacePath) {
|
|
597
|
-
commands.push(`cd '${escapePathForAppleScript(workspacePath)}'`);
|
|
598
|
-
}
|
|
599
|
-
if (includeEnvSetup) {
|
|
600
|
-
commands.push("source .env");
|
|
601
|
-
}
|
|
602
|
-
if (includePortExport && port !== void 0) {
|
|
603
|
-
commands.push(`export PORT=${port}`);
|
|
604
|
-
}
|
|
605
|
-
if (command) {
|
|
606
|
-
commands.push(command);
|
|
607
|
-
}
|
|
608
|
-
const fullCommand = commands.join(" && ");
|
|
609
|
-
const historyFreeCommand = ` ${fullCommand}`;
|
|
610
|
-
let script = `tell application "Terminal"
|
|
611
|
-
`;
|
|
612
|
-
script += ` set newTab to do script "${escapeForAppleScript(historyFreeCommand)}"
|
|
613
|
-
`;
|
|
614
|
-
if (backgroundColor) {
|
|
615
|
-
const { r, g, b } = backgroundColor;
|
|
616
|
-
script += ` set background color of newTab to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
|
|
617
|
-
`;
|
|
618
|
-
}
|
|
619
|
-
script += `end tell`;
|
|
620
|
-
return script;
|
|
621
|
-
}
|
|
622
|
-
function escapePathForAppleScript(path5) {
|
|
623
|
-
return path5.replace(/'/g, "'\\''");
|
|
624
|
-
}
|
|
625
|
-
function escapeForAppleScript(command) {
|
|
626
|
-
return command.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
627
|
-
}
|
|
628
|
-
function buildITerm2SingleTabScript(options) {
|
|
629
|
-
const command = buildCommandSequence(options);
|
|
630
|
-
let script = 'tell application id "com.googlecode.iterm2"\n';
|
|
631
|
-
script += " create window with default profile\n";
|
|
632
|
-
script += " set s1 to current session of current window\n\n";
|
|
633
|
-
if (options.backgroundColor) {
|
|
634
|
-
const { r, g, b } = options.backgroundColor;
|
|
635
|
-
script += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
|
|
636
|
-
`;
|
|
637
|
-
}
|
|
638
|
-
script += ` tell s1 to write text "${escapeForAppleScript(command)}"
|
|
639
|
-
|
|
640
|
-
`;
|
|
641
|
-
if (options.title) {
|
|
642
|
-
script += ` set name of s1 to "${escapeForAppleScript(options.title)}"
|
|
643
|
-
|
|
644
|
-
`;
|
|
645
|
-
}
|
|
646
|
-
script += " activate\n";
|
|
647
|
-
script += "end tell";
|
|
648
|
-
return script;
|
|
649
|
-
}
|
|
650
|
-
function buildCommandSequence(options) {
|
|
651
|
-
const {
|
|
652
|
-
workspacePath,
|
|
653
|
-
command,
|
|
654
|
-
port,
|
|
655
|
-
includeEnvSetup,
|
|
656
|
-
includePortExport
|
|
657
|
-
} = options;
|
|
658
|
-
const commands = [];
|
|
659
|
-
if (workspacePath) {
|
|
660
|
-
commands.push(`cd '${escapePathForAppleScript(workspacePath)}'`);
|
|
661
|
-
}
|
|
662
|
-
if (includeEnvSetup) {
|
|
663
|
-
commands.push("source .env");
|
|
664
|
-
}
|
|
665
|
-
if (includePortExport && port !== void 0) {
|
|
666
|
-
commands.push(`export PORT=${port}`);
|
|
667
|
-
}
|
|
668
|
-
if (command) {
|
|
669
|
-
commands.push(command);
|
|
670
|
-
}
|
|
671
|
-
const fullCommand = commands.join(" && ");
|
|
672
|
-
return ` ${fullCommand}`;
|
|
673
|
-
}
|
|
674
|
-
function buildITerm2MultiTabScript(optionsArray) {
|
|
675
|
-
if (optionsArray.length < 2) {
|
|
676
|
-
throw new Error("buildITerm2MultiTabScript requires at least 2 terminal options");
|
|
677
|
-
}
|
|
678
|
-
let script = 'tell application id "com.googlecode.iterm2"\n';
|
|
679
|
-
script += " create window with default profile\n";
|
|
680
|
-
script += " set newWindow to current window\n";
|
|
681
|
-
const options1 = optionsArray[0];
|
|
682
|
-
if (!options1) {
|
|
683
|
-
throw new Error("First terminal option is undefined");
|
|
684
|
-
}
|
|
685
|
-
const command1 = buildCommandSequence(options1);
|
|
686
|
-
script += " set s1 to current session of newWindow\n\n";
|
|
687
|
-
if (options1.backgroundColor) {
|
|
688
|
-
const { r, g, b } = options1.backgroundColor;
|
|
689
|
-
script += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
|
|
690
|
-
`;
|
|
691
|
-
}
|
|
692
|
-
script += ` tell s1 to write text "${escapeForAppleScript(command1)}"
|
|
693
|
-
|
|
694
|
-
`;
|
|
695
|
-
if (options1.title) {
|
|
696
|
-
script += ` set name of s1 to "${escapeForAppleScript(options1.title)}"
|
|
697
|
-
|
|
698
|
-
`;
|
|
699
|
-
}
|
|
700
|
-
for (let i = 1; i < optionsArray.length; i++) {
|
|
701
|
-
const options = optionsArray[i];
|
|
702
|
-
if (!options) {
|
|
703
|
-
throw new Error(`Terminal option at index ${i} is undefined`);
|
|
704
|
-
}
|
|
705
|
-
const command = buildCommandSequence(options);
|
|
706
|
-
const sessionVar = `s${i + 1}`;
|
|
707
|
-
script += " tell newWindow\n";
|
|
708
|
-
script += ` set newTab${i} to (create tab with default profile)
|
|
709
|
-
`;
|
|
710
|
-
script += " end tell\n";
|
|
711
|
-
script += ` set ${sessionVar} to current session of newTab${i}
|
|
712
|
-
|
|
713
|
-
`;
|
|
714
|
-
if (options.backgroundColor) {
|
|
715
|
-
const { r, g, b } = options.backgroundColor;
|
|
716
|
-
script += ` set background color of ${sessionVar} to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
|
|
717
|
-
`;
|
|
718
|
-
}
|
|
719
|
-
script += ` tell ${sessionVar} to write text "${escapeForAppleScript(command)}"
|
|
720
|
-
|
|
721
|
-
`;
|
|
722
|
-
if (options.title) {
|
|
723
|
-
script += ` set name of ${sessionVar} to "${escapeForAppleScript(options.title)}"
|
|
724
|
-
|
|
725
|
-
`;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
script += " activate\n";
|
|
729
|
-
script += "end tell";
|
|
730
|
-
return script;
|
|
731
|
-
}
|
|
732
|
-
async function openMultipleTerminalWindows(optionsArray) {
|
|
733
|
-
if (optionsArray.length < 2) {
|
|
734
|
-
throw new Error("openMultipleTerminalWindows requires at least 2 terminal options. Use openTerminalWindow for single terminal.");
|
|
735
|
-
}
|
|
736
|
-
const platform = detectPlatform();
|
|
737
|
-
if (platform !== "darwin") {
|
|
738
|
-
throw new Error(
|
|
739
|
-
`Terminal window launching not yet supported on ${platform}. Currently only macOS is supported.`
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
const hasITerm2 = await detectITerm2();
|
|
743
|
-
if (hasITerm2) {
|
|
744
|
-
const applescript = buildITerm2MultiTabScript(optionsArray);
|
|
745
|
-
try {
|
|
746
|
-
await execa3("osascript", ["-e", applescript]);
|
|
747
|
-
} catch (error) {
|
|
748
|
-
throw new Error(
|
|
749
|
-
`Failed to open iTerm2 window: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
750
|
-
);
|
|
751
|
-
}
|
|
752
|
-
} else {
|
|
753
|
-
for (let i = 0; i < optionsArray.length; i++) {
|
|
754
|
-
const options = optionsArray[i];
|
|
755
|
-
if (!options) {
|
|
756
|
-
throw new Error(`Terminal option at index ${i} is undefined`);
|
|
757
|
-
}
|
|
758
|
-
await openTerminalWindow(options);
|
|
759
|
-
if (i < optionsArray.length - 1) {
|
|
760
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
async function openDualTerminalWindow(options1, options2) {
|
|
766
|
-
await openMultipleTerminalWindows([options1, options2]);
|
|
767
|
-
}
|
|
768
|
-
var init_terminal = __esm({
|
|
769
|
-
"src/utils/terminal.ts"() {
|
|
770
|
-
"use strict";
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
|
|
774
543
|
// src/utils/color.ts
|
|
775
544
|
var color_exports = {};
|
|
776
545
|
__export(color_exports, {
|
|
@@ -951,6 +720,7 @@ import path3 from "path";
|
|
|
951
720
|
import fs from "fs-extra";
|
|
952
721
|
|
|
953
722
|
// src/utils/git.ts
|
|
723
|
+
init_SettingsManager();
|
|
954
724
|
init_logger();
|
|
955
725
|
import path2 from "path";
|
|
956
726
|
import { execa } from "execa";
|
|
@@ -1097,7 +867,7 @@ function extractIssueNumber(branchName) {
|
|
|
1097
867
|
}
|
|
1098
868
|
return null;
|
|
1099
869
|
}
|
|
1100
|
-
function isWorktreePath(
|
|
870
|
+
function isWorktreePath(path6) {
|
|
1101
871
|
const worktreePatterns = [
|
|
1102
872
|
/\/worktrees?\//i,
|
|
1103
873
|
// Contains /worktree/ or /worktrees/
|
|
@@ -1112,7 +882,7 @@ function isWorktreePath(path5) {
|
|
|
1112
882
|
/\.worktree$/i
|
|
1113
883
|
// ends with .worktree
|
|
1114
884
|
];
|
|
1115
|
-
return worktreePatterns.some((pattern) => pattern.test(
|
|
885
|
+
return worktreePatterns.some((pattern) => pattern.test(path6));
|
|
1116
886
|
}
|
|
1117
887
|
function generateWorktreePath(branchName, rootDir = process.cwd(), options) {
|
|
1118
888
|
let sanitized = branchName.replace(/\//g, "-");
|
|
@@ -1154,31 +924,31 @@ function generateWorktreePath(branchName, rootDir = process.cwd(), options) {
|
|
|
1154
924
|
return path2.join(parentDir, `${prefix}${sanitized}`);
|
|
1155
925
|
}
|
|
1156
926
|
}
|
|
1157
|
-
async function isValidGitRepo(
|
|
927
|
+
async function isValidGitRepo(path6) {
|
|
1158
928
|
try {
|
|
1159
|
-
await executeGitCommand(["rev-parse", "--git-dir"], { cwd:
|
|
929
|
+
await executeGitCommand(["rev-parse", "--git-dir"], { cwd: path6 });
|
|
1160
930
|
return true;
|
|
1161
931
|
} catch {
|
|
1162
932
|
return false;
|
|
1163
933
|
}
|
|
1164
934
|
}
|
|
1165
|
-
async function getCurrentBranch(
|
|
935
|
+
async function getCurrentBranch(path6 = process.cwd()) {
|
|
1166
936
|
try {
|
|
1167
|
-
const result = await executeGitCommand(["branch", "--show-current"], { cwd:
|
|
937
|
+
const result = await executeGitCommand(["branch", "--show-current"], { cwd: path6 });
|
|
1168
938
|
return result.trim();
|
|
1169
939
|
} catch {
|
|
1170
940
|
return null;
|
|
1171
941
|
}
|
|
1172
942
|
}
|
|
1173
|
-
async function branchExists(branchName,
|
|
943
|
+
async function branchExists(branchName, path6 = process.cwd(), includeRemote = true) {
|
|
1174
944
|
try {
|
|
1175
|
-
const localResult = await executeGitCommand(["branch", "--list", branchName], { cwd:
|
|
945
|
+
const localResult = await executeGitCommand(["branch", "--list", branchName], { cwd: path6 });
|
|
1176
946
|
if (localResult.trim()) {
|
|
1177
947
|
return true;
|
|
1178
948
|
}
|
|
1179
949
|
if (includeRemote) {
|
|
1180
950
|
const remoteResult = await executeGitCommand(["branch", "-r", "--list", `*/${branchName}`], {
|
|
1181
|
-
cwd:
|
|
951
|
+
cwd: path6
|
|
1182
952
|
});
|
|
1183
953
|
if (remoteResult.trim()) {
|
|
1184
954
|
return true;
|
|
@@ -1189,17 +959,17 @@ async function branchExists(branchName, path5 = process.cwd(), includeRemote = t
|
|
|
1189
959
|
return false;
|
|
1190
960
|
}
|
|
1191
961
|
}
|
|
1192
|
-
async function getRepoRoot(
|
|
962
|
+
async function getRepoRoot(path6 = process.cwd()) {
|
|
1193
963
|
try {
|
|
1194
|
-
const result = await executeGitCommand(["rev-parse", "--show-toplevel"], { cwd:
|
|
964
|
+
const result = await executeGitCommand(["rev-parse", "--show-toplevel"], { cwd: path6 });
|
|
1195
965
|
return result.trim();
|
|
1196
966
|
} catch {
|
|
1197
967
|
return null;
|
|
1198
968
|
}
|
|
1199
969
|
}
|
|
1200
|
-
async function findMainWorktreePath(
|
|
970
|
+
async function findMainWorktreePath(path6 = process.cwd(), options) {
|
|
1201
971
|
try {
|
|
1202
|
-
const output = await executeGitCommand(["worktree", "list", "--porcelain"], { cwd:
|
|
972
|
+
const output = await executeGitCommand(["worktree", "list", "--porcelain"], { cwd: path6 });
|
|
1203
973
|
const worktrees = parseWorktreeList(output, options == null ? void 0 : options.mainBranch);
|
|
1204
974
|
if (worktrees.length === 0) {
|
|
1205
975
|
throw new Error("No worktrees found in repository");
|
|
@@ -1229,33 +999,30 @@ async function findMainWorktreePath(path5 = process.cwd(), options) {
|
|
|
1229
999
|
throw new Error(`Failed to find main worktree: ${error instanceof Error ? error.message : String(error)}`);
|
|
1230
1000
|
}
|
|
1231
1001
|
}
|
|
1232
|
-
async function findMainWorktreePathWithSettings(
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
settingsManager = new SM();
|
|
1236
|
-
}
|
|
1237
|
-
const settings = await settingsManager.loadSettings(path5);
|
|
1002
|
+
async function findMainWorktreePathWithSettings(path6, settingsManager) {
|
|
1003
|
+
settingsManager ??= new SettingsManager();
|
|
1004
|
+
const settings = await settingsManager.loadSettings(path6);
|
|
1238
1005
|
const findOptions = settings.mainBranch ? { mainBranch: settings.mainBranch } : void 0;
|
|
1239
|
-
return findMainWorktreePath(
|
|
1006
|
+
return findMainWorktreePath(path6, findOptions);
|
|
1240
1007
|
}
|
|
1241
|
-
async function hasUncommittedChanges(
|
|
1008
|
+
async function hasUncommittedChanges(path6 = process.cwd()) {
|
|
1242
1009
|
try {
|
|
1243
|
-
const result = await executeGitCommand(["status", "--porcelain"], { cwd:
|
|
1010
|
+
const result = await executeGitCommand(["status", "--porcelain"], { cwd: path6 });
|
|
1244
1011
|
return result.trim().length > 0;
|
|
1245
1012
|
} catch {
|
|
1246
1013
|
return false;
|
|
1247
1014
|
}
|
|
1248
1015
|
}
|
|
1249
|
-
async function getDefaultBranch(
|
|
1016
|
+
async function getDefaultBranch(path6 = process.cwd()) {
|
|
1250
1017
|
try {
|
|
1251
1018
|
const remoteResult = await executeGitCommand(["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
1252
|
-
cwd:
|
|
1019
|
+
cwd: path6
|
|
1253
1020
|
});
|
|
1254
1021
|
const match = remoteResult.match(/refs\/remotes\/origin\/(.+)/);
|
|
1255
1022
|
if (match) return match[1] ?? "main";
|
|
1256
1023
|
const commonDefaults = ["main", "master", "develop"];
|
|
1257
1024
|
for (const branch of commonDefaults) {
|
|
1258
|
-
if (await branchExists(branch,
|
|
1025
|
+
if (await branchExists(branch, path6)) {
|
|
1259
1026
|
return branch;
|
|
1260
1027
|
}
|
|
1261
1028
|
}
|
|
@@ -1264,13 +1031,13 @@ async function getDefaultBranch(path5 = process.cwd()) {
|
|
|
1264
1031
|
return "main";
|
|
1265
1032
|
}
|
|
1266
1033
|
}
|
|
1267
|
-
async function findAllBranchesForIssue(issueNumber,
|
|
1034
|
+
async function findAllBranchesForIssue(issueNumber, path6 = process.cwd(), settingsManager) {
|
|
1268
1035
|
if (!settingsManager) {
|
|
1269
1036
|
const { SettingsManager: SM } = await Promise.resolve().then(() => (init_SettingsManager(), SettingsManager_exports));
|
|
1270
1037
|
settingsManager = new SM();
|
|
1271
1038
|
}
|
|
1272
|
-
const protectedBranches = await settingsManager.getProtectedBranches(
|
|
1273
|
-
const output = await executeGitCommand(["branch", "-a"], { cwd:
|
|
1039
|
+
const protectedBranches = await settingsManager.getProtectedBranches(path6);
|
|
1040
|
+
const output = await executeGitCommand(["branch", "-a"], { cwd: path6 });
|
|
1274
1041
|
const branches = [];
|
|
1275
1042
|
const lines = output.split("\n").filter(Boolean);
|
|
1276
1043
|
for (const line of lines) {
|
|
@@ -1327,18 +1094,18 @@ async function findAllBranchesForIssue(issueNumber, path5 = process.cwd(), setti
|
|
|
1327
1094
|
}
|
|
1328
1095
|
return branches;
|
|
1329
1096
|
}
|
|
1330
|
-
async function isEmptyRepository(
|
|
1097
|
+
async function isEmptyRepository(path6 = process.cwd()) {
|
|
1331
1098
|
try {
|
|
1332
|
-
await executeGitCommand(["rev-parse", "--verify", "HEAD"], { cwd:
|
|
1099
|
+
await executeGitCommand(["rev-parse", "--verify", "HEAD"], { cwd: path6 });
|
|
1333
1100
|
return false;
|
|
1334
1101
|
} catch {
|
|
1335
1102
|
return true;
|
|
1336
1103
|
}
|
|
1337
1104
|
}
|
|
1338
|
-
async function ensureRepositoryHasCommits(
|
|
1339
|
-
const isEmpty = await isEmptyRepository(
|
|
1105
|
+
async function ensureRepositoryHasCommits(path6 = process.cwd()) {
|
|
1106
|
+
const isEmpty = await isEmptyRepository(path6);
|
|
1340
1107
|
if (isEmpty) {
|
|
1341
|
-
await executeGitCommand(["commit", "--no-verify", "--allow-empty", "-m", "Initial commit"], { cwd:
|
|
1108
|
+
await executeGitCommand(["commit", "--no-verify", "--allow-empty", "-m", "Initial commit"], { cwd: path6 });
|
|
1342
1109
|
}
|
|
1343
1110
|
}
|
|
1344
1111
|
async function pushBranchToRemote(branchName, worktreePath, options) {
|
|
@@ -1383,6 +1150,21 @@ async function pushBranchToRemote(branchName, worktreePath, options) {
|
|
|
1383
1150
|
throw new Error(`Failed to push to remote: ${errorMessage}`);
|
|
1384
1151
|
}
|
|
1385
1152
|
}
|
|
1153
|
+
async function isFileTrackedByGit(filePath, cwd = process.cwd()) {
|
|
1154
|
+
try {
|
|
1155
|
+
const result = await executeGitCommand(
|
|
1156
|
+
["ls-files", "--error-unmatch", filePath],
|
|
1157
|
+
{ cwd }
|
|
1158
|
+
);
|
|
1159
|
+
return result.trim().length > 0;
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1162
|
+
if (errorMessage.includes("pathspec") && errorMessage.includes("did not match")) {
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
throw error;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1386
1168
|
|
|
1387
1169
|
// src/lib/GitWorktreeManager.ts
|
|
1388
1170
|
var GitWorktreeManager = class {
|
|
@@ -1752,6 +1534,9 @@ var GitWorktreeManager = class {
|
|
|
1752
1534
|
}
|
|
1753
1535
|
};
|
|
1754
1536
|
|
|
1537
|
+
// src/lib/GitHubService.ts
|
|
1538
|
+
import { execSync } from "child_process";
|
|
1539
|
+
|
|
1755
1540
|
// src/types/github.ts
|
|
1756
1541
|
var GitHubError = class extends Error {
|
|
1757
1542
|
constructor(code, message, details) {
|
|
@@ -1991,6 +1776,18 @@ var GitHubService = class {
|
|
|
1991
1776
|
this.supportsPullRequests = true;
|
|
1992
1777
|
this.prompter = (options == null ? void 0 : options.prompter) ?? promptConfirmation;
|
|
1993
1778
|
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Check if GitHub CLI (gh) is available on the system
|
|
1781
|
+
* @returns true if gh CLI is installed and accessible, false otherwise
|
|
1782
|
+
*/
|
|
1783
|
+
static isCliAvailable() {
|
|
1784
|
+
try {
|
|
1785
|
+
execSync("gh --version", { stdio: "ignore" });
|
|
1786
|
+
return true;
|
|
1787
|
+
} catch {
|
|
1788
|
+
return false;
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1994
1791
|
// Input detection - IssueTracker interface implementation
|
|
1995
1792
|
async detectInputType(input, repo) {
|
|
1996
1793
|
const numberMatch = input.match(/^#?(\d+)$/);
|
|
@@ -2611,6 +2408,7 @@ import fs2 from "fs-extra";
|
|
|
2611
2408
|
|
|
2612
2409
|
// src/utils/env.ts
|
|
2613
2410
|
init_logger();
|
|
2411
|
+
import path4 from "path";
|
|
2614
2412
|
import dotenvFlow from "dotenv-flow";
|
|
2615
2413
|
function parseEnvFile(content) {
|
|
2616
2414
|
const envMap = /* @__PURE__ */ new Map();
|
|
@@ -2664,6 +2462,45 @@ function isValidEnvKey(key) {
|
|
|
2664
2462
|
const validKeyRegex = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2665
2463
|
return validKeyRegex.test(key);
|
|
2666
2464
|
}
|
|
2465
|
+
var DOTENV_FLOW_NODE_ENV = process.env.DOTENV_FLOW_NODE_ENV ?? "development";
|
|
2466
|
+
function getDotenvFlowFiles() {
|
|
2467
|
+
return [
|
|
2468
|
+
".env",
|
|
2469
|
+
".env.local",
|
|
2470
|
+
`.env.${DOTENV_FLOW_NODE_ENV}`,
|
|
2471
|
+
`.env.${DOTENV_FLOW_NODE_ENV}.local`
|
|
2472
|
+
];
|
|
2473
|
+
}
|
|
2474
|
+
async function buildEnvSourceCommands(workspacePath, fileExists) {
|
|
2475
|
+
const files = getDotenvFlowFiles();
|
|
2476
|
+
const commands = [];
|
|
2477
|
+
for (const file of files) {
|
|
2478
|
+
const fullPath = path4.join(workspacePath, file);
|
|
2479
|
+
const exists = await fileExists(fullPath);
|
|
2480
|
+
if (exists) {
|
|
2481
|
+
commands.push(`source ${file}`);
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
return commands;
|
|
2485
|
+
}
|
|
2486
|
+
async function findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable) {
|
|
2487
|
+
const files = getDotenvFlowFiles().reverse();
|
|
2488
|
+
for (const file of files) {
|
|
2489
|
+
const fullPath = path4.join(workspacePath, file);
|
|
2490
|
+
if (!await fileExists(fullPath)) {
|
|
2491
|
+
continue;
|
|
2492
|
+
}
|
|
2493
|
+
const value = await getEnvVariable(fullPath, variableName);
|
|
2494
|
+
if (value !== null) {
|
|
2495
|
+
return file;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
return null;
|
|
2499
|
+
}
|
|
2500
|
+
async function hasVariableInAnyEnvFile(workspacePath, variableName, fileExists, getEnvVariable) {
|
|
2501
|
+
const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable);
|
|
2502
|
+
return file !== null;
|
|
2503
|
+
}
|
|
2667
2504
|
|
|
2668
2505
|
// src/utils/port.ts
|
|
2669
2506
|
import { createHash } from "crypto";
|
|
@@ -2882,6 +2719,7 @@ var EnvironmentManager = class {
|
|
|
2882
2719
|
|
|
2883
2720
|
// src/lib/DatabaseManager.ts
|
|
2884
2721
|
init_logger();
|
|
2722
|
+
import fs3 from "fs-extra";
|
|
2885
2723
|
var logger3 = createLogger({ prefix: "\u{1F5C2}\uFE0F" });
|
|
2886
2724
|
var DatabaseManager = class {
|
|
2887
2725
|
constructor(provider, environment, databaseUrlEnvVarName = "DATABASE_URL") {
|
|
@@ -2904,17 +2742,17 @@ var DatabaseManager = class {
|
|
|
2904
2742
|
* Check if database branching should be used
|
|
2905
2743
|
* Requires BOTH conditions:
|
|
2906
2744
|
* 1. Database provider is properly configured (checked via provider.isConfigured())
|
|
2907
|
-
* 2.
|
|
2745
|
+
* 2. Any dotenv-flow file contains the configured database URL variable
|
|
2908
2746
|
*/
|
|
2909
|
-
async shouldUseDatabaseBranching(
|
|
2747
|
+
async shouldUseDatabaseBranching(workspacePath) {
|
|
2910
2748
|
if (!this.provider.isConfigured()) {
|
|
2911
2749
|
logger3.debug("Skipping database branching: Database provider not configured");
|
|
2912
2750
|
return false;
|
|
2913
2751
|
}
|
|
2914
|
-
const hasDatabaseUrl = await this.hasDatabaseUrlInEnv(
|
|
2752
|
+
const hasDatabaseUrl = await this.hasDatabaseUrlInEnv(workspacePath);
|
|
2915
2753
|
if (!hasDatabaseUrl) {
|
|
2916
2754
|
logger3.debug(
|
|
2917
|
-
"Skipping database branching: configured database URL variable not found in
|
|
2755
|
+
"Skipping database branching: configured database URL variable not found in any env file"
|
|
2918
2756
|
);
|
|
2919
2757
|
return false;
|
|
2920
2758
|
}
|
|
@@ -2925,12 +2763,12 @@ var DatabaseManager = class {
|
|
|
2925
2763
|
* Returns connection string if branch was created, null if skipped
|
|
2926
2764
|
*
|
|
2927
2765
|
* @param branchName - Name of the branch to create
|
|
2928
|
-
* @param
|
|
2766
|
+
* @param workspacePath - Path to workspace for configuration checks (checks all dotenv-flow files)
|
|
2929
2767
|
* @param cwd - Optional working directory to run commands from
|
|
2930
2768
|
* @param fromBranch - Optional parent branch to create from (for child looms)
|
|
2931
2769
|
*/
|
|
2932
|
-
async createBranchIfConfigured(branchName,
|
|
2933
|
-
if (!await this.shouldUseDatabaseBranching(
|
|
2770
|
+
async createBranchIfConfigured(branchName, workspacePath, cwd, fromBranch) {
|
|
2771
|
+
if (!await this.shouldUseDatabaseBranching(workspacePath)) {
|
|
2934
2772
|
return null;
|
|
2935
2773
|
}
|
|
2936
2774
|
if (!await this.provider.isCliAvailable()) {
|
|
@@ -3057,19 +2895,24 @@ var DatabaseManager = class {
|
|
|
3057
2895
|
return null;
|
|
3058
2896
|
}
|
|
3059
2897
|
/**
|
|
3060
|
-
* Check if
|
|
2898
|
+
* Check if any dotenv-flow file has the configured database URL variable
|
|
3061
2899
|
* CRITICAL: If user explicitly configured a custom variable name (not default),
|
|
3062
|
-
* throw an error if it's missing from
|
|
2900
|
+
* throw an error if it's missing from all env files
|
|
3063
2901
|
*/
|
|
3064
|
-
async hasDatabaseUrlInEnv(
|
|
2902
|
+
async hasDatabaseUrlInEnv(workspacePath) {
|
|
3065
2903
|
try {
|
|
3066
|
-
const envMap = await this.environment.readEnvFile(envFilePath);
|
|
3067
2904
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
3068
2905
|
logger3.debug(`Looking for custom database URL variable: ${this.databaseUrlEnvVarName}`);
|
|
3069
2906
|
} else {
|
|
3070
2907
|
logger3.debug("Looking for default database URL variable: DATABASE_URL");
|
|
3071
2908
|
}
|
|
3072
|
-
|
|
2909
|
+
const hasConfiguredVar = await hasVariableInAnyEnvFile(
|
|
2910
|
+
workspacePath,
|
|
2911
|
+
this.databaseUrlEnvVarName,
|
|
2912
|
+
async (p) => fs3.pathExists(p),
|
|
2913
|
+
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
2914
|
+
);
|
|
2915
|
+
if (hasConfiguredVar) {
|
|
3073
2916
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
3074
2917
|
logger3.debug(`\u2705 Found custom database URL variable: ${this.databaseUrlEnvVarName}`);
|
|
3075
2918
|
} else {
|
|
@@ -3078,20 +2921,25 @@ var DatabaseManager = class {
|
|
|
3078
2921
|
return true;
|
|
3079
2922
|
}
|
|
3080
2923
|
if (this.databaseUrlEnvVarName !== "DATABASE_URL") {
|
|
3081
|
-
logger3.debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in
|
|
2924
|
+
logger3.debug(`\u274C Custom database URL variable '${this.databaseUrlEnvVarName}' not found in any env file`);
|
|
3082
2925
|
throw new Error(
|
|
3083
|
-
`Configured database URL environment variable '${this.databaseUrlEnvVarName}' not found in
|
|
2926
|
+
`Configured database URL environment variable '${this.databaseUrlEnvVarName}' not found in any dotenv-flow file. Please add it to an .env file or update your iloom configuration.`
|
|
3084
2927
|
);
|
|
3085
2928
|
}
|
|
3086
|
-
const hasDefaultVar =
|
|
2929
|
+
const hasDefaultVar = await hasVariableInAnyEnvFile(
|
|
2930
|
+
workspacePath,
|
|
2931
|
+
"DATABASE_URL",
|
|
2932
|
+
async (p) => fs3.pathExists(p),
|
|
2933
|
+
async (p, v) => this.environment.getEnvVariable(p, v)
|
|
2934
|
+
);
|
|
3087
2935
|
if (hasDefaultVar) {
|
|
3088
2936
|
logger3.debug("\u2705 Found fallback DATABASE_URL variable");
|
|
3089
2937
|
} else {
|
|
3090
|
-
logger3.debug("\u274C No DATABASE_URL variable found in
|
|
2938
|
+
logger3.debug("\u274C No DATABASE_URL variable found in any env file");
|
|
3091
2939
|
}
|
|
3092
2940
|
return hasDefaultVar;
|
|
3093
2941
|
} catch (error) {
|
|
3094
|
-
if (error instanceof Error && error.message.includes("not found in
|
|
2942
|
+
if (error instanceof Error && error.message.includes("not found in")) {
|
|
3095
2943
|
throw error;
|
|
3096
2944
|
}
|
|
3097
2945
|
return false;
|
|
@@ -3104,6 +2952,140 @@ init_logger();
|
|
|
3104
2952
|
import { execa as execa4 } from "execa";
|
|
3105
2953
|
import { existsSync as existsSync2 } from "fs";
|
|
3106
2954
|
import { join } from "path";
|
|
2955
|
+
|
|
2956
|
+
// src/utils/terminal.ts
|
|
2957
|
+
import { execa as execa3 } from "execa";
|
|
2958
|
+
import { existsSync } from "fs";
|
|
2959
|
+
function detectPlatform() {
|
|
2960
|
+
const platform = process.platform;
|
|
2961
|
+
if (platform === "darwin") return "darwin";
|
|
2962
|
+
if (platform === "linux") return "linux";
|
|
2963
|
+
if (platform === "win32") return "win32";
|
|
2964
|
+
return "unsupported";
|
|
2965
|
+
}
|
|
2966
|
+
async function detectITerm2() {
|
|
2967
|
+
const platform = detectPlatform();
|
|
2968
|
+
if (platform !== "darwin") return false;
|
|
2969
|
+
return existsSync("/Applications/iTerm.app");
|
|
2970
|
+
}
|
|
2971
|
+
async function openTerminalWindow(options) {
|
|
2972
|
+
const platform = detectPlatform();
|
|
2973
|
+
if (platform !== "darwin") {
|
|
2974
|
+
throw new Error(
|
|
2975
|
+
`Terminal window launching not yet supported on ${platform}. Currently only macOS is supported.`
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
const hasITerm2 = await detectITerm2();
|
|
2979
|
+
const applescript = hasITerm2 ? await buildITerm2SingleTabScript(options) : await buildAppleScript(options);
|
|
2980
|
+
try {
|
|
2981
|
+
await execa3("osascript", ["-e", applescript]);
|
|
2982
|
+
if (!hasITerm2) {
|
|
2983
|
+
await execa3("osascript", ["-e", 'tell application "Terminal" to activate']);
|
|
2984
|
+
}
|
|
2985
|
+
} catch (error) {
|
|
2986
|
+
throw new Error(
|
|
2987
|
+
`Failed to open terminal window: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
async function buildAppleScript(options) {
|
|
2992
|
+
const {
|
|
2993
|
+
workspacePath,
|
|
2994
|
+
command,
|
|
2995
|
+
backgroundColor,
|
|
2996
|
+
port,
|
|
2997
|
+
includeEnvSetup,
|
|
2998
|
+
includePortExport
|
|
2999
|
+
} = options;
|
|
3000
|
+
const commands = [];
|
|
3001
|
+
if (workspacePath) {
|
|
3002
|
+
commands.push(`cd '${escapePathForAppleScript(workspacePath)}'`);
|
|
3003
|
+
}
|
|
3004
|
+
if (includeEnvSetup && workspacePath) {
|
|
3005
|
+
const sourceCommands = await buildEnvSourceCommands(
|
|
3006
|
+
workspacePath,
|
|
3007
|
+
async (p) => existsSync(p)
|
|
3008
|
+
);
|
|
3009
|
+
commands.push(...sourceCommands);
|
|
3010
|
+
}
|
|
3011
|
+
if (includePortExport && port !== void 0) {
|
|
3012
|
+
commands.push(`export PORT=${port}`);
|
|
3013
|
+
}
|
|
3014
|
+
if (command) {
|
|
3015
|
+
commands.push(command);
|
|
3016
|
+
}
|
|
3017
|
+
const fullCommand = commands.join(" && ");
|
|
3018
|
+
const historyFreeCommand = ` ${fullCommand}`;
|
|
3019
|
+
let script = `tell application "Terminal"
|
|
3020
|
+
`;
|
|
3021
|
+
script += ` set newTab to do script "${escapeForAppleScript(historyFreeCommand)}"
|
|
3022
|
+
`;
|
|
3023
|
+
if (backgroundColor) {
|
|
3024
|
+
const { r, g, b } = backgroundColor;
|
|
3025
|
+
script += ` set background color of newTab to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
|
|
3026
|
+
`;
|
|
3027
|
+
}
|
|
3028
|
+
script += `end tell`;
|
|
3029
|
+
return script;
|
|
3030
|
+
}
|
|
3031
|
+
function escapePathForAppleScript(path6) {
|
|
3032
|
+
return path6.replace(/'/g, "'\\''");
|
|
3033
|
+
}
|
|
3034
|
+
function escapeForAppleScript(command) {
|
|
3035
|
+
return command.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
3036
|
+
}
|
|
3037
|
+
async function buildITerm2SingleTabScript(options) {
|
|
3038
|
+
const command = await buildCommandSequence(options);
|
|
3039
|
+
let script = 'tell application id "com.googlecode.iterm2"\n';
|
|
3040
|
+
script += " create window with default profile\n";
|
|
3041
|
+
script += " set s1 to current session of current window\n\n";
|
|
3042
|
+
if (options.backgroundColor) {
|
|
3043
|
+
const { r, g, b } = options.backgroundColor;
|
|
3044
|
+
script += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}
|
|
3045
|
+
`;
|
|
3046
|
+
}
|
|
3047
|
+
script += ` tell s1 to write text "${escapeForAppleScript(command)}"
|
|
3048
|
+
|
|
3049
|
+
`;
|
|
3050
|
+
if (options.title) {
|
|
3051
|
+
script += ` set name of s1 to "${escapeForAppleScript(options.title)}"
|
|
3052
|
+
|
|
3053
|
+
`;
|
|
3054
|
+
}
|
|
3055
|
+
script += " activate\n";
|
|
3056
|
+
script += "end tell";
|
|
3057
|
+
return script;
|
|
3058
|
+
}
|
|
3059
|
+
async function buildCommandSequence(options) {
|
|
3060
|
+
const {
|
|
3061
|
+
workspacePath,
|
|
3062
|
+
command,
|
|
3063
|
+
port,
|
|
3064
|
+
includeEnvSetup,
|
|
3065
|
+
includePortExport
|
|
3066
|
+
} = options;
|
|
3067
|
+
const commands = [];
|
|
3068
|
+
if (workspacePath) {
|
|
3069
|
+
commands.push(`cd '${escapePathForAppleScript(workspacePath)}'`);
|
|
3070
|
+
}
|
|
3071
|
+
if (includeEnvSetup && workspacePath) {
|
|
3072
|
+
const sourceCommands = await buildEnvSourceCommands(
|
|
3073
|
+
workspacePath,
|
|
3074
|
+
async (p) => existsSync(p)
|
|
3075
|
+
);
|
|
3076
|
+
commands.push(...sourceCommands);
|
|
3077
|
+
}
|
|
3078
|
+
if (includePortExport && port !== void 0) {
|
|
3079
|
+
commands.push(`export PORT=${port}`);
|
|
3080
|
+
}
|
|
3081
|
+
if (command) {
|
|
3082
|
+
commands.push(command);
|
|
3083
|
+
}
|
|
3084
|
+
const fullCommand = commands.join(" && ");
|
|
3085
|
+
return ` ${fullCommand}`;
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
// src/utils/claude.ts
|
|
3107
3089
|
async function detectClaudeCli() {
|
|
3108
3090
|
try {
|
|
3109
3091
|
await execa4("command", ["-v", "claude"], {
|
|
@@ -3247,7 +3229,6 @@ async function launchClaudeInNewTerminalWindow(_prompt, options) {
|
|
|
3247
3229
|
if (!workspacePath) {
|
|
3248
3230
|
throw new Error("workspacePath is required for terminal window launch");
|
|
3249
3231
|
}
|
|
3250
|
-
const { openTerminalWindow: openTerminalWindow2 } = await Promise.resolve().then(() => (init_terminal(), terminal_exports));
|
|
3251
3232
|
const executable = executablePath ?? "iloom";
|
|
3252
3233
|
let launchCommand = `${executable} spin`;
|
|
3253
3234
|
if (oneShot !== "default") {
|
|
@@ -3271,7 +3252,7 @@ async function launchClaudeInNewTerminalWindow(_prompt, options) {
|
|
|
3271
3252
|
}
|
|
3272
3253
|
}
|
|
3273
3254
|
const hasEnvFile = existsSync2(join(workspacePath, ".env"));
|
|
3274
|
-
await
|
|
3255
|
+
await openTerminalWindow({
|
|
3275
3256
|
workspacePath,
|
|
3276
3257
|
command: launchCommand,
|
|
3277
3258
|
...backgroundColor && { backgroundColor },
|
|
@@ -3285,7 +3266,7 @@ async function launchClaudeInNewTerminalWindow(_prompt, options) {
|
|
|
3285
3266
|
init_logger();
|
|
3286
3267
|
import { readFile as readFile2 } from "fs/promises";
|
|
3287
3268
|
import { accessSync } from "fs";
|
|
3288
|
-
import
|
|
3269
|
+
import path5 from "path";
|
|
3289
3270
|
import { fileURLToPath } from "url";
|
|
3290
3271
|
var PromptTemplateManager = class {
|
|
3291
3272
|
constructor(templateDir) {
|
|
@@ -3294,17 +3275,17 @@ var PromptTemplateManager = class {
|
|
|
3294
3275
|
} else {
|
|
3295
3276
|
const currentFileUrl = import.meta.url;
|
|
3296
3277
|
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
3297
|
-
const distDir =
|
|
3298
|
-
let templateDir2 =
|
|
3278
|
+
const distDir = path5.dirname(currentFilePath);
|
|
3279
|
+
let templateDir2 = path5.join(distDir, "prompts");
|
|
3299
3280
|
let currentDir = distDir;
|
|
3300
|
-
while (currentDir !==
|
|
3301
|
-
const candidatePath =
|
|
3281
|
+
while (currentDir !== path5.dirname(currentDir)) {
|
|
3282
|
+
const candidatePath = path5.join(currentDir, "prompts");
|
|
3302
3283
|
try {
|
|
3303
3284
|
accessSync(candidatePath);
|
|
3304
3285
|
templateDir2 = candidatePath;
|
|
3305
3286
|
break;
|
|
3306
3287
|
} catch {
|
|
3307
|
-
currentDir =
|
|
3288
|
+
currentDir = path5.dirname(currentDir);
|
|
3308
3289
|
}
|
|
3309
3290
|
}
|
|
3310
3291
|
this.templateDir = templateDir2;
|
|
@@ -3319,7 +3300,7 @@ var PromptTemplateManager = class {
|
|
|
3319
3300
|
* Load a template file by name
|
|
3320
3301
|
*/
|
|
3321
3302
|
async loadTemplate(templateName) {
|
|
3322
|
-
const templatePath =
|
|
3303
|
+
const templatePath = path5.join(this.templateDir, `${templateName}-prompt.txt`);
|
|
3323
3304
|
logger.debug("Loading template", {
|
|
3324
3305
|
templateName,
|
|
3325
3306
|
templateDir: this.templateDir,
|
|
@@ -3974,6 +3955,7 @@ export {
|
|
|
3974
3955
|
getRepoRoot,
|
|
3975
3956
|
hasUncommittedChanges,
|
|
3976
3957
|
isEmptyRepository,
|
|
3958
|
+
isFileTrackedByGit,
|
|
3977
3959
|
isPRBranch,
|
|
3978
3960
|
isValidGitRepo,
|
|
3979
3961
|
isWorktreePath,
|