@iloom/cli 0.1.19 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +16 -0
  2. package/dist/ClaudeContextManager-LVCYRM6Q.js +13 -0
  3. package/dist/ClaudeService-WVTWB3DK.js +12 -0
  4. package/dist/{GitHubService-LWP4GKGH.js → GitHubService-7E2S5NNZ.js} +3 -3
  5. package/dist/{LoomLauncher-UMMLPIZO.js → LoomLauncher-CTSWJL35.js} +6 -6
  6. package/dist/README.md +16 -0
  7. package/dist/{SettingsManager-SKLUVE3K.js → SettingsManager-XOYCLH3D.js} +2 -2
  8. package/dist/{add-issue-X56V3XPB.js → add-issue-OBI325W7.js} +7 -7
  9. package/dist/{chunk-DEPYQRRB.js → chunk-2PLUQT6J.js} +2 -2
  10. package/dist/{chunk-VVH3ANF2.js → chunk-4IV6W4U5.js} +4 -4
  11. package/dist/{chunk-PV3GAXQO.js → chunk-6LEQW46Y.js} +2 -2
  12. package/dist/{chunk-VCMMAFXQ.js → chunk-CVLAZRNB.js} +2 -2
  13. package/dist/{chunk-FXV24OYZ.js → chunk-DJUGYNQE.js} +9 -2
  14. package/dist/{chunk-FXV24OYZ.js.map → chunk-DJUGYNQE.js.map} +1 -1
  15. package/dist/{chunk-KOCQAD2E.js → chunk-HBVFXN7R.js} +3 -3
  16. package/dist/{chunk-ELFT36PV.js → chunk-LHP6ROUM.js} +3 -3
  17. package/dist/{chunk-PXZBAC2M.js → chunk-MFU53H6J.js} +2 -2
  18. package/dist/{chunk-PR7FKQBG.js → chunk-RF2YI2XJ.js} +2 -2
  19. package/dist/{chunk-JXQXSC45.js → chunk-SWCRXDZC.js} +2 -2
  20. package/dist/{chunk-Q2KYPAH2.js → chunk-SYOSCMIT.js} +6 -6
  21. package/dist/{chunk-VYQLLHZ7.js → chunk-T3KEIB4D.js} +6 -2
  22. package/dist/{chunk-VYQLLHZ7.js.map → chunk-T3KEIB4D.js.map} +1 -1
  23. package/dist/{chunk-ZWXJBSUW.js → chunk-TS6DL67T.js} +2 -2
  24. package/dist/{chunk-IO4WFTL2.js → chunk-VETG35MF.js} +2 -2
  25. package/dist/{chunk-RSRO7564.js → chunk-ZE74H5BR.js} +28 -3
  26. package/dist/chunk-ZE74H5BR.js.map +1 -0
  27. package/dist/{claude-7LUVDZZ4.js → claude-ZIWDG4XG.js} +2 -2
  28. package/dist/{cleanup-ZHROIBSQ.js → cleanup-FEIVZSIV.js} +5 -5
  29. package/dist/cli.js +29 -29
  30. package/dist/cli.js.map +1 -1
  31. package/dist/{contribute-3MQJ3XAQ.js → contribute-EMZKCAC6.js} +6 -3
  32. package/dist/{contribute-3MQJ3XAQ.js.map → contribute-EMZKCAC6.js.map} +1 -1
  33. package/dist/{enhance-VGWUX474.js → enhance-MNA4ZGXW.js} +7 -7
  34. package/dist/{feedback-ZOUCCHN4.js → feedback-LFNMQBAZ.js} +6 -6
  35. package/dist/{finish-QJSK6Z7J.js → finish-TX5CJICB.js} +411 -17
  36. package/dist/finish-TX5CJICB.js.map +1 -0
  37. package/dist/{git-OUYMVYJX.js → git-WC6HZLOT.js} +2 -2
  38. package/dist/{ignite-HICLZEYU.js → ignite-MQWVJEAB.js} +7 -7
  39. package/dist/index.d.ts +20 -0
  40. package/dist/index.js +32 -3
  41. package/dist/index.js.map +1 -1
  42. package/dist/{init-UMKNHNV5.js → init-GJDYN2IK.js} +6 -6
  43. package/dist/mcp/{claude-YHHHLSXH.js → claude-NDFOCQQQ.js} +2 -2
  44. package/dist/mcp/{terminal-SDCMDVD7.js → terminal-OMNRFWB3.js} +28 -3
  45. package/dist/mcp/terminal-OMNRFWB3.js.map +1 -0
  46. package/dist/{open-ETZUFSE4.js → open-NXSN7XOC.js} +4 -4
  47. package/dist/prompts/init-prompt.txt +29 -0
  48. package/dist/{rebase-KBWFDZCN.js → rebase-DUNFOJVS.js} +6 -6
  49. package/dist/{remote-GJEZWRCC.js → remote-ZCXJVVNW.js} +4 -2
  50. package/dist/{run-4SVQ3WEU.js → run-O7ZK7CKA.js} +4 -4
  51. package/dist/schema/settings.schema.json +18 -0
  52. package/dist/{start-CT2ZEFP2.js → start-73I5W7WW.js} +15 -15
  53. package/dist/{terminal-3D6TUAKJ.js → terminal-BIRBZ4AZ.js} +2 -2
  54. package/dist/{test-git-MKZATGZN.js → test-git-T76HOTIA.js} +3 -3
  55. package/dist/{test-prefix-ZNLWDI3K.js → test-prefix-6HJUVQMH.js} +3 -3
  56. package/dist/{test-tabs-JRKY3QMM.js → test-tabs-RXDBZ6J7.js} +2 -2
  57. package/package.json +1 -1
  58. package/dist/ClaudeContextManager-JKR4WGNU.js +0 -13
  59. package/dist/ClaudeService-55DQGB7T.js +0 -12
  60. package/dist/chunk-RSRO7564.js.map +0 -1
  61. package/dist/finish-QJSK6Z7J.js.map +0 -1
  62. package/dist/mcp/terminal-SDCMDVD7.js.map +0 -1
  63. /package/dist/{ClaudeContextManager-JKR4WGNU.js.map → ClaudeContextManager-LVCYRM6Q.js.map} +0 -0
  64. /package/dist/{ClaudeService-55DQGB7T.js.map → ClaudeService-WVTWB3DK.js.map} +0 -0
  65. /package/dist/{GitHubService-LWP4GKGH.js.map → GitHubService-7E2S5NNZ.js.map} +0 -0
  66. /package/dist/{LoomLauncher-UMMLPIZO.js.map → LoomLauncher-CTSWJL35.js.map} +0 -0
  67. /package/dist/{SettingsManager-SKLUVE3K.js.map → SettingsManager-XOYCLH3D.js.map} +0 -0
  68. /package/dist/{add-issue-X56V3XPB.js.map → add-issue-OBI325W7.js.map} +0 -0
  69. /package/dist/{chunk-DEPYQRRB.js.map → chunk-2PLUQT6J.js.map} +0 -0
  70. /package/dist/{chunk-VVH3ANF2.js.map → chunk-4IV6W4U5.js.map} +0 -0
  71. /package/dist/{chunk-PV3GAXQO.js.map → chunk-6LEQW46Y.js.map} +0 -0
  72. /package/dist/{chunk-VCMMAFXQ.js.map → chunk-CVLAZRNB.js.map} +0 -0
  73. /package/dist/{chunk-KOCQAD2E.js.map → chunk-HBVFXN7R.js.map} +0 -0
  74. /package/dist/{chunk-ELFT36PV.js.map → chunk-LHP6ROUM.js.map} +0 -0
  75. /package/dist/{chunk-PXZBAC2M.js.map → chunk-MFU53H6J.js.map} +0 -0
  76. /package/dist/{chunk-PR7FKQBG.js.map → chunk-RF2YI2XJ.js.map} +0 -0
  77. /package/dist/{chunk-JXQXSC45.js.map → chunk-SWCRXDZC.js.map} +0 -0
  78. /package/dist/{chunk-Q2KYPAH2.js.map → chunk-SYOSCMIT.js.map} +0 -0
  79. /package/dist/{chunk-ZWXJBSUW.js.map → chunk-TS6DL67T.js.map} +0 -0
  80. /package/dist/{chunk-IO4WFTL2.js.map → chunk-VETG35MF.js.map} +0 -0
  81. /package/dist/{claude-7LUVDZZ4.js.map → claude-ZIWDG4XG.js.map} +0 -0
  82. /package/dist/{cleanup-ZHROIBSQ.js.map → cleanup-FEIVZSIV.js.map} +0 -0
  83. /package/dist/{enhance-VGWUX474.js.map → enhance-MNA4ZGXW.js.map} +0 -0
  84. /package/dist/{feedback-ZOUCCHN4.js.map → feedback-LFNMQBAZ.js.map} +0 -0
  85. /package/dist/{git-OUYMVYJX.js.map → git-WC6HZLOT.js.map} +0 -0
  86. /package/dist/{ignite-HICLZEYU.js.map → ignite-MQWVJEAB.js.map} +0 -0
  87. /package/dist/{init-UMKNHNV5.js.map → init-GJDYN2IK.js.map} +0 -0
  88. /package/dist/mcp/{claude-YHHHLSXH.js.map → claude-NDFOCQQQ.js.map} +0 -0
  89. /package/dist/{open-ETZUFSE4.js.map → open-NXSN7XOC.js.map} +0 -0
  90. /package/dist/{rebase-KBWFDZCN.js.map → rebase-DUNFOJVS.js.map} +0 -0
  91. /package/dist/{remote-GJEZWRCC.js.map → remote-ZCXJVVNW.js.map} +0 -0
  92. /package/dist/{run-4SVQ3WEU.js.map → run-O7ZK7CKA.js.map} +0 -0
  93. /package/dist/{start-CT2ZEFP2.js.map → start-73I5W7WW.js.map} +0 -0
  94. /package/dist/{terminal-3D6TUAKJ.js.map → terminal-BIRBZ4AZ.js.map} +0 -0
  95. /package/dist/{test-git-MKZATGZN.js.map → test-git-T76HOTIA.js.map} +0 -0
  96. /package/dist/{test-prefix-ZNLWDI3K.js.map → test-prefix-6HJUVQMH.js.map} +0 -0
  97. /package/dist/{test-tabs-JRKY3QMM.js.map → test-tabs-RXDBZ6J7.js.map} +0 -0
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MergeManager
4
- } from "./chunk-VVH3ANF2.js";
4
+ } from "./chunk-4IV6W4U5.js";
5
5
  import {
6
6
  ResourceCleanup
7
- } from "./chunk-Q2KYPAH2.js";
7
+ } from "./chunk-SYOSCMIT.js";
8
8
  import {
9
9
  IdentifierParser
10
10
  } from "./chunk-H4E4THUZ.js";
@@ -34,29 +34,39 @@ import {
34
34
  hasScript,
35
35
  readPackageJson
36
36
  } from "./chunk-2ZPFJQ3B.js";
37
+ import {
38
+ openBrowser
39
+ } from "./chunk-YETJNRQM.js";
37
40
  import {
38
41
  getConfiguredRepoFromSettings,
39
- hasMultipleRemotes
40
- } from "./chunk-FXV24OYZ.js";
42
+ getEffectivePRTargetRemote,
43
+ hasMultipleRemotes,
44
+ parseGitRemotes
45
+ } from "./chunk-DJUGYNQE.js";
41
46
  import {
42
47
  GitHubService
43
- } from "./chunk-ZWXJBSUW.js";
44
- import "./chunk-JXQXSC45.js";
48
+ } from "./chunk-TS6DL67T.js";
49
+ import {
50
+ executeGhCommand
51
+ } from "./chunk-SWCRXDZC.js";
45
52
  import {
46
53
  detectClaudeCli,
47
54
  launchClaude
48
- } from "./chunk-PXZBAC2M.js";
55
+ } from "./chunk-MFU53H6J.js";
49
56
  import {
50
57
  GitWorktreeManager
51
- } from "./chunk-IO4WFTL2.js";
58
+ } from "./chunk-VETG35MF.js";
52
59
  import {
53
60
  SettingsManager
54
- } from "./chunk-VYQLLHZ7.js";
61
+ } from "./chunk-T3KEIB4D.js";
55
62
  import {
56
63
  executeGitCommand,
57
- findMainWorktreePathWithSettings
58
- } from "./chunk-KOCQAD2E.js";
59
- import "./chunk-JNKJ7NJV.js";
64
+ findMainWorktreePathWithSettings,
65
+ pushBranchToRemote
66
+ } from "./chunk-HBVFXN7R.js";
67
+ import {
68
+ promptConfirmation
69
+ } from "./chunk-JNKJ7NJV.js";
60
70
  import {
61
71
  logger
62
72
  } from "./chunk-GEHQXLEI.js";
@@ -781,6 +791,267 @@ Run '${runCommand}' to see detailed errors.`
781
791
  }
782
792
  };
783
793
 
794
+ // src/lib/PRManager.ts
795
+ var PRManager = class {
796
+ constructor(settings) {
797
+ this.settings = settings;
798
+ }
799
+ /**
800
+ * Check if a PR already exists for the given branch
801
+ * @param branchName - Branch to check
802
+ * @param cwd - Working directory
803
+ * @returns Existing PR info or null if none found
804
+ */
805
+ async checkForExistingPR(branchName, cwd) {
806
+ try {
807
+ const prList = await executeGhCommand(
808
+ ["pr", "list", "--head", branchName, "--state", "open", "--json", "number,url"],
809
+ cwd ? { cwd } : void 0
810
+ );
811
+ if (prList.length > 0) {
812
+ return prList[0] ?? null;
813
+ }
814
+ return null;
815
+ } catch (error) {
816
+ logger.debug("Error checking for existing PR", { error });
817
+ return null;
818
+ }
819
+ }
820
+ /**
821
+ * Generate PR body using Claude if available, otherwise use simple template
822
+ * @param issueNumber - Issue number to include in body
823
+ * @param worktreePath - Path to worktree for context
824
+ * @returns PR body markdown
825
+ */
826
+ async generatePRBody(issueNumber, worktreePath) {
827
+ const hasClaudeCli = await detectClaudeCli();
828
+ if (hasClaudeCli) {
829
+ try {
830
+ const prompt = this.buildPRBodyPrompt(issueNumber);
831
+ const body2 = await launchClaude(prompt, {
832
+ headless: true,
833
+ addDir: worktreePath,
834
+ timeout: 3e4
835
+ });
836
+ if (body2 && typeof body2 === "string" && body2.trim()) {
837
+ const sanitized = this.sanitizeClaudeOutput(body2);
838
+ if (sanitized) {
839
+ return sanitized;
840
+ }
841
+ }
842
+ } catch (error) {
843
+ logger.debug("Claude PR body generation failed, using template", { error });
844
+ }
845
+ }
846
+ let body = "This PR contains changes from the iloom workflow.\n\n";
847
+ if (issueNumber) {
848
+ body += `Fixes #${issueNumber}`;
849
+ }
850
+ return body;
851
+ }
852
+ /**
853
+ * Build structured XML prompt for PR body generation
854
+ * Uses XML format for clear task definition and output expectations
855
+ */
856
+ buildPRBodyPrompt(issueNumber) {
857
+ const issueContext = issueNumber ? `
858
+ <IssueContext>
859
+ This PR is associated with GitHub issue #${issueNumber}.
860
+ Include "Fixes #${issueNumber}" at the end of the body on its own line.
861
+ </IssueContext>` : "";
862
+ return `<Task>
863
+ You are a software engineer writing a pull request body for this repository.
864
+ Examine the changes in the git repository and generate a concise, professional PR description.
865
+ </Task>
866
+
867
+ <Requirements>
868
+ <Format>Write 2-3 sentences summarizing what was changed and why.${issueNumber ? `
869
+
870
+ End with "Fixes #${issueNumber}" on its own line.` : ""}</Format>
871
+ <Tone>Professional and concise</Tone>
872
+ <Focus>Summarize the changes and their purpose</Focus>
873
+ <NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw PR body text.</NoMeta>
874
+ <Examples>
875
+ Good: "Add user authentication with JWT tokens to secure the API endpoints. This includes login and registration endpoints with proper password hashing.
876
+
877
+ Fixes #42"
878
+ Good: "Fix navigation bug in sidebar menu that caused incorrect highlighting on nested routes."
879
+ Bad: "Here's the PR body:
880
+
881
+ ---
882
+
883
+ Add user authentication..."
884
+ Bad: "Based on the changes, I'll write: Fix navigation bug..."
885
+ </Examples>
886
+ ${issueContext}
887
+ </Requirements>
888
+
889
+ <Output>
890
+ IMPORTANT: Your entire response will be used directly as the GitHub pull request body.
891
+ Do not include any explanatory text, headers, or separators before or after the body.
892
+ Start your response immediately with the PR body text.
893
+ </Output>`;
894
+ }
895
+ /**
896
+ * Sanitize Claude output to remove meta-commentary and clean formatting
897
+ * Handles cases where Claude includes explanatory text despite instructions
898
+ */
899
+ sanitizeClaudeOutput(rawOutput) {
900
+ let cleaned = rawOutput.trim();
901
+ const metaPatterns = [
902
+ /^.*?based on.*?changes.*?:/i,
903
+ /^.*?looking at.*?files.*?:/i,
904
+ /^.*?examining.*?:/i,
905
+ /^.*?analyzing.*?:/i,
906
+ /^.*?i'll.*?generate.*?:/i,
907
+ /^.*?let me.*?:/i,
908
+ /^.*?here.*?is.*?(?:the\s+)?(?:pr|pull request).*?body.*?:/i,
909
+ /^.*?here's.*?(?:the\s+)?(?:pr|pull request).*?body.*?:/i
910
+ ];
911
+ for (const pattern of metaPatterns) {
912
+ cleaned = cleaned.replace(pattern, "").trim();
913
+ }
914
+ cleaned = cleaned.replace(/^[-=]{3,}\s*/m, "").trim();
915
+ if (cleaned.includes(":")) {
916
+ const colonIndex = cleaned.indexOf(":");
917
+ const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase();
918
+ const metaIndicators = [
919
+ "here is the pr body",
920
+ "here is the pull request body",
921
+ "pr body",
922
+ "pull request body",
923
+ "here is",
924
+ "here's",
925
+ "the body should be",
926
+ "i suggest",
927
+ "my suggestion"
928
+ ];
929
+ const isMetaCommentary = metaIndicators.some((indicator) => beforeColon.includes(indicator));
930
+ if (isMetaCommentary) {
931
+ const afterColon = cleaned.substring(colonIndex + 1).trim();
932
+ const afterSeparator = afterColon.replace(/^[-=]{3,}\s*/m, "").trim();
933
+ if (afterSeparator && afterSeparator.length > 10) {
934
+ cleaned = afterSeparator;
935
+ }
936
+ }
937
+ }
938
+ if (cleaned.startsWith('"') && cleaned.endsWith('"') || cleaned.startsWith("'") && cleaned.endsWith("'")) {
939
+ cleaned = cleaned.slice(1, -1).trim();
940
+ }
941
+ return cleaned;
942
+ }
943
+ /**
944
+ * Create a GitHub PR for the branch
945
+ * @param branchName - Branch to create PR from (used as --head)
946
+ * @param title - PR title
947
+ * @param body - PR body
948
+ * @param baseBranch - Base branch to target (usually main/master)
949
+ * @param cwd - Working directory
950
+ * @returns PR URL
951
+ */
952
+ async createPR(branchName, title, body, baseBranch, cwd) {
953
+ try {
954
+ const targetRemote = await getEffectivePRTargetRemote(this.settings, cwd);
955
+ let headValue = branchName;
956
+ if (targetRemote !== "origin") {
957
+ const remotes = await parseGitRemotes(cwd);
958
+ const originRemote = remotes.find((r) => r.name === "origin");
959
+ if (originRemote) {
960
+ headValue = `${originRemote.owner}:${branchName}`;
961
+ logger.debug(`Fork workflow detected, using head: ${headValue}`);
962
+ }
963
+ }
964
+ const args = ["pr", "create", "--head", headValue, "--title", title, "--body", body, "--base", baseBranch];
965
+ if (targetRemote !== "origin") {
966
+ const repo = await getConfiguredRepoFromSettings(this.settings, cwd);
967
+ args.push("--repo", repo);
968
+ }
969
+ const result = await executeGhCommand(args, cwd ? { cwd } : void 0);
970
+ const url = typeof result === "string" ? result.trim() : String(result).trim();
971
+ if (!url.includes("github.com") || !url.includes("/pull/")) {
972
+ throw new Error(`Unexpected response from gh pr create: ${url}`);
973
+ }
974
+ return url;
975
+ } catch (error) {
976
+ const errorMessage = error instanceof Error ? error.message : String(error);
977
+ if (errorMessage.includes("Head sha can't be blank") || errorMessage.includes("No commits between")) {
978
+ throw new Error(
979
+ `Failed to create pull request: ${errorMessage}
980
+
981
+ This error typically occurs when:
982
+ - The branch was not fully pushed to the remote
983
+ - There's a race condition between push and PR creation
984
+ - The branch has no commits ahead of the base branch
985
+
986
+ Try running: git push -u origin ${branchName}
987
+ Then retry: il finish`
988
+ );
989
+ }
990
+ throw new Error(`Failed to create pull request: ${errorMessage}`);
991
+ }
992
+ }
993
+ /**
994
+ * Open PR URL in browser
995
+ * @param url - PR URL to open
996
+ */
997
+ async openPRInBrowser(url) {
998
+ try {
999
+ await openBrowser(url);
1000
+ logger.debug("Opened PR in browser", { url });
1001
+ } catch (error) {
1002
+ logger.warn("Failed to open PR in browser", { error });
1003
+ }
1004
+ }
1005
+ /**
1006
+ * Complete PR workflow: check for existing, create if needed, optionally open in browser
1007
+ * @param branchName - Branch to create PR from
1008
+ * @param title - PR title
1009
+ * @param issueNumber - Optional issue number for body generation
1010
+ * @param baseBranch - Base branch to target
1011
+ * @param worktreePath - Path to worktree
1012
+ * @param openInBrowser - Whether to open PR in browser
1013
+ * @returns PR creation result
1014
+ */
1015
+ async createOrOpenPR(branchName, title, issueNumber, baseBranch, worktreePath, openInBrowser) {
1016
+ const existingPR = await this.checkForExistingPR(branchName, worktreePath);
1017
+ if (existingPR) {
1018
+ logger.info(`Pull request already exists: ${existingPR.url}`);
1019
+ if (openInBrowser) {
1020
+ await this.openPRInBrowser(existingPR.url);
1021
+ }
1022
+ return {
1023
+ url: existingPR.url,
1024
+ number: existingPR.number,
1025
+ wasExisting: true
1026
+ };
1027
+ }
1028
+ const body = await this.generatePRBody(issueNumber, worktreePath);
1029
+ logger.info("Creating pull request...");
1030
+ const url = await this.createPR(branchName, title, body, baseBranch, worktreePath);
1031
+ const prNumber = this.extractPRNumberFromUrl(url);
1032
+ if (openInBrowser) {
1033
+ await this.openPRInBrowser(url);
1034
+ }
1035
+ return {
1036
+ url,
1037
+ number: prNumber,
1038
+ wasExisting: false
1039
+ };
1040
+ }
1041
+ /**
1042
+ * Extract PR number from GitHub PR URL
1043
+ * @param url - PR URL (e.g., https://github.com/owner/repo/pull/123)
1044
+ * @returns PR number
1045
+ */
1046
+ extractPRNumberFromUrl(url) {
1047
+ const match = url.match(/\/pull\/(\d+)/);
1048
+ if (match == null ? void 0 : match[1]) {
1049
+ return parseInt(match[1], 10);
1050
+ }
1051
+ throw new Error(`Could not extract PR number from URL: ${url}`);
1052
+ }
1053
+ };
1054
+
784
1055
  // src/commands/finish.ts
785
1056
  import path from "path";
786
1057
  var FinishCommand = class {
@@ -1107,8 +1378,8 @@ var FinishCommand = class {
1107
1378
  logger.info("[DRY RUN] Would auto-commit uncommitted changes (validation passed)");
1108
1379
  } else {
1109
1380
  logger.info("Validation passed, auto-committing uncommitted changes...");
1110
- const settings = await this.settingsManager.loadSettings(worktree.path);
1111
- const skipVerify = ((_b = (_a = settings.workflows) == null ? void 0 : _a.issue) == null ? void 0 : _b.noVerify) ?? false;
1381
+ const settings2 = await this.settingsManager.loadSettings(worktree.path);
1382
+ const skipVerify = ((_b = (_a = settings2.workflows) == null ? void 0 : _a.issue) == null ? void 0 : _b.noVerify) ?? false;
1112
1383
  const commitOptions = {
1113
1384
  dryRun: options.dryRun ?? false,
1114
1385
  skipVerify
@@ -1122,6 +1393,12 @@ var FinishCommand = class {
1122
1393
  } else {
1123
1394
  logger.debug("No uncommitted changes found");
1124
1395
  }
1396
+ const settings = await this.settingsManager.loadSettings(worktree.path);
1397
+ const mergeBehavior = settings.mergeBehavior ?? { mode: "local" };
1398
+ if (mergeBehavior.mode === "github-pr") {
1399
+ await this.executeGitHubPRWorkflow(parsed, options, worktree, settings);
1400
+ return;
1401
+ }
1125
1402
  logger.info("Rebasing branch on main...");
1126
1403
  const mergeOptions = {
1127
1404
  dryRun: options.dryRun ?? false,
@@ -1189,8 +1466,8 @@ var FinishCommand = class {
1189
1466
  logger.info(`[DRY RUN] Would push changes to origin/${pr.branch}`);
1190
1467
  } else {
1191
1468
  logger.info("Pushing changes to remote...");
1192
- const { pushBranchToRemote } = await import("./git-OUYMVYJX.js");
1193
- await pushBranchToRemote(pr.branch, worktree.path, {
1469
+ const { pushBranchToRemote: pushBranchToRemote2 } = await import("./git-WC6HZLOT.js");
1470
+ await pushBranchToRemote2(pr.branch, worktree.path, {
1194
1471
  dryRun: false
1195
1472
  });
1196
1473
  logger.success(`Changes pushed to PR #${parsed.number}`);
@@ -1200,6 +1477,123 @@ var FinishCommand = class {
1200
1477
  logger.info(`To cleanup when done: il cleanup ${parsed.number}`);
1201
1478
  }
1202
1479
  }
1480
+ /**
1481
+ * Execute workflow for GitHub PR creation (github-pr merge mode)
1482
+ * Validates → Commits → Pushes → Creates PR → Prompts for cleanup
1483
+ */
1484
+ async executeGitHubPRWorkflow(parsed, options, worktree, settings) {
1485
+ if (options.dryRun) {
1486
+ logger.info("[DRY RUN] Would push branch to origin");
1487
+ } else {
1488
+ logger.info("Pushing branch to origin...");
1489
+ await pushBranchToRemote(worktree.branch, worktree.path, { dryRun: false });
1490
+ logger.success("Branch pushed successfully");
1491
+ }
1492
+ const prManager = new PRManager(settings);
1493
+ let prTitle = `Work from ${worktree.branch}`;
1494
+ if (parsed.type === "issue" && parsed.number) {
1495
+ try {
1496
+ const issue = await this.gitHubService.fetchIssue(parsed.number);
1497
+ prTitle = issue.title;
1498
+ } catch (error) {
1499
+ logger.debug("Could not fetch issue title, using branch name", { error });
1500
+ }
1501
+ }
1502
+ if (options.dryRun) {
1503
+ logger.info("[DRY RUN] Would create GitHub PR");
1504
+ logger.info(` Title: ${prTitle}`);
1505
+ logger.info(` Base: ${settings.mainBranch ?? "main"}`);
1506
+ } else {
1507
+ const baseBranch = settings.mainBranch ?? "main";
1508
+ const openInBrowser = options.noBrowser !== true;
1509
+ const result = await prManager.createOrOpenPR(
1510
+ worktree.branch,
1511
+ prTitle,
1512
+ parsed.type === "issue" ? parsed.number : void 0,
1513
+ baseBranch,
1514
+ worktree.path,
1515
+ openInBrowser
1516
+ );
1517
+ if (result.wasExisting) {
1518
+ logger.success(`Existing pull request: ${result.url}`);
1519
+ } else {
1520
+ logger.success(`Pull request created: ${result.url}`);
1521
+ }
1522
+ await this.handlePRCleanupPrompt(parsed, options, worktree);
1523
+ }
1524
+ }
1525
+ /**
1526
+ * Handle cleanup prompt after PR creation
1527
+ * Respects --cleanup and --no-cleanup flags, otherwise prompts user
1528
+ */
1529
+ async handlePRCleanupPrompt(parsed, options, worktree) {
1530
+ if (options.cleanup === true) {
1531
+ logger.info("Cleaning up worktree (--cleanup flag)...");
1532
+ await this.performWorktreeCleanup(parsed, options, worktree);
1533
+ } else if (options.cleanup === false) {
1534
+ logger.info("Worktree kept active for continued work (--no-cleanup flag)");
1535
+ logger.info(`To cleanup later: il cleanup ${parsed.originalInput}`);
1536
+ } else {
1537
+ logger.info("");
1538
+ logger.info("PR created successfully. Would you like to clean up the worktree?");
1539
+ logger.info(` Worktree: ${worktree.path}`);
1540
+ logger.info(` Branch: ${worktree.branch}`);
1541
+ logger.info("");
1542
+ const shouldCleanup = await promptConfirmation(
1543
+ "Clean up worktree now?",
1544
+ false
1545
+ // Default to keeping worktree (safer option)
1546
+ );
1547
+ if (shouldCleanup) {
1548
+ await this.performWorktreeCleanup(parsed, options, worktree);
1549
+ } else {
1550
+ logger.info("Worktree kept active. Run `il cleanup` when ready.");
1551
+ }
1552
+ }
1553
+ }
1554
+ /**
1555
+ * Perform worktree cleanup (used by GitHub PR workflow)
1556
+ * Similar to performPostMergeCleanup but for PR workflow
1557
+ */
1558
+ async performWorktreeCleanup(parsed, options, worktree) {
1559
+ const cleanupInput = {
1560
+ type: parsed.type,
1561
+ originalInput: parsed.originalInput,
1562
+ ...parsed.number !== void 0 && { number: parsed.number },
1563
+ ...parsed.branchName !== void 0 && { branchName: parsed.branchName }
1564
+ };
1565
+ const cleanupOptions = {
1566
+ dryRun: options.dryRun ?? false,
1567
+ deleteBranch: false,
1568
+ // Don't delete branch - PR still needs it
1569
+ keepDatabase: false,
1570
+ // Clean up database
1571
+ force: options.force ?? false
1572
+ };
1573
+ try {
1574
+ logger.info("Starting worktree cleanup...");
1575
+ await this.ensureResourceCleanup();
1576
+ if (!this.resourceCleanup) {
1577
+ throw new Error("Failed to initialize ResourceCleanup");
1578
+ }
1579
+ const result = await this.resourceCleanup.cleanupWorktree(cleanupInput, cleanupOptions);
1580
+ this.reportCleanupResults(result);
1581
+ if (!result.success) {
1582
+ logger.warn("Some cleanup operations failed - manual cleanup may be required");
1583
+ this.showManualCleanupInstructions(worktree);
1584
+ } else {
1585
+ logger.success("Worktree cleanup completed successfully");
1586
+ }
1587
+ if (this.isRunningFromWithinWorktree(worktree.path)) {
1588
+ this.showTerminalCloseWarning(worktree);
1589
+ }
1590
+ } catch (error) {
1591
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
1592
+ logger.warn(`Cleanup failed: ${errorMessage}`);
1593
+ logger.warn("Manual cleanup may be required");
1594
+ this.showManualCleanupInstructions(worktree);
1595
+ }
1596
+ }
1203
1597
  /**
1204
1598
  * Perform cleanup for closed/merged PRs
1205
1599
  * Similar to performPostMergeCleanup but with different messaging
@@ -1352,4 +1746,4 @@ var FinishCommand = class {
1352
1746
  export {
1353
1747
  FinishCommand
1354
1748
  };
1355
- //# sourceMappingURL=finish-QJSK6Z7J.js.map
1749
+ //# sourceMappingURL=finish-TX5CJICB.js.map