@iloom/cli 0.4.1 → 0.5.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 (121) hide show
  1. package/README.md +24 -0
  2. package/dist/{ClaudeContextManager-DK77227F.js → ClaudeContextManager-DQFKIMEP.js} +5 -5
  3. package/dist/{ClaudeService-W3SA7HVG.js → ClaudeService-CJS32WG2.js} +4 -4
  4. package/dist/{LoomLauncher-S3YGJRJQ.js → LoomLauncher-JNWBMHES.js} +22 -88
  5. package/dist/LoomLauncher-JNWBMHES.js.map +1 -0
  6. package/dist/MetadataManager-WXUVXKUS.js +10 -0
  7. package/dist/PRManager-7DSIMCAD.js +16 -0
  8. package/dist/{PromptTemplateManager-2TDZAUC6.js → PromptTemplateManager-72FEOGT6.js} +2 -2
  9. package/dist/README.md +24 -0
  10. package/dist/{SettingsManager-FJFU6JJD.js → SettingsManager-XPR4TEQL.js} +2 -2
  11. package/dist/agents/iloom-issue-analyze-and-plan.md +41 -7
  12. package/dist/agents/iloom-issue-analyzer.md +38 -8
  13. package/dist/agents/iloom-issue-complexity-evaluator.md +45 -15
  14. package/dist/agents/iloom-issue-enhancer.md +60 -18
  15. package/dist/agents/iloom-issue-implementer.md +29 -7
  16. package/dist/agents/iloom-issue-planner.md +36 -7
  17. package/dist/agents/iloom-issue-reviewer.md +30 -7
  18. package/dist/{chunk-JC5HXN75.js → chunk-3CMGCRB5.js} +2 -2
  19. package/dist/{chunk-G6CIIJLT.js → chunk-4YTILIIH.js} +7 -8
  20. package/dist/chunk-4YTILIIH.js.map +1 -0
  21. package/dist/{chunk-IARWMDAX.js → chunk-6KB7R22U.js} +98 -16
  22. package/dist/chunk-6KB7R22U.js.map +1 -0
  23. package/dist/{chunk-55TB3FSG.js → chunk-AS2IRKLU.js} +2 -2
  24. package/dist/{chunk-VTXCGKV5.js → chunk-BVIK2P6P.js} +11 -3
  25. package/dist/chunk-BVIK2P6P.js.map +1 -0
  26. package/dist/chunk-CDF7ZX2B.js +72 -0
  27. package/dist/chunk-CDF7ZX2B.js.map +1 -0
  28. package/dist/{chunk-POI7KLBH.js → chunk-CDQEK2WD.js} +5 -5
  29. package/dist/{chunk-74VMN2KC.js → chunk-DKQ4SUII.js} +16 -1
  30. package/dist/chunk-DKQ4SUII.js.map +1 -0
  31. package/dist/{chunk-BIIQHEXJ.js → chunk-GVRO4PWE.js} +12 -8
  32. package/dist/chunk-GVRO4PWE.js.map +1 -0
  33. package/dist/{chunk-TMZAVPGF.js → chunk-HABINPX2.js} +71 -15
  34. package/dist/{chunk-TMZAVPGF.js.map → chunk-HABINPX2.js.map} +1 -1
  35. package/dist/{chunk-2W2FBL5G.js → chunk-LN4H3A6A.js} +66 -7
  36. package/dist/chunk-LN4H3A6A.js.map +1 -0
  37. package/dist/{chunk-VWNS6DH5.js → chunk-OOU3DKNT.js} +13 -7
  38. package/dist/chunk-OOU3DKNT.js.map +1 -0
  39. package/dist/chunk-P2ZQ5LKB.js +347 -0
  40. package/dist/chunk-P2ZQ5LKB.js.map +1 -0
  41. package/dist/{chunk-OF7BNW4D.js → chunk-RJKMF6BC.js} +30 -4
  42. package/dist/chunk-RJKMF6BC.js.map +1 -0
  43. package/dist/{chunk-O7WHXLCB.js → chunk-RNZMHJK7.js} +18 -4
  44. package/dist/chunk-RNZMHJK7.js.map +1 -0
  45. package/dist/{chunk-UPUAQYAW.js → chunk-S65T4O6I.js} +2 -2
  46. package/dist/{chunk-HD5SUKI2.js → chunk-TSLKDFAF.js} +55 -6
  47. package/dist/chunk-TSLKDFAF.js.map +1 -0
  48. package/dist/{chunk-IJ7IGJT3.js → chunk-YZTDGPFB.js} +18 -1
  49. package/dist/chunk-YZTDGPFB.js.map +1 -0
  50. package/dist/{cleanup-KDLVTT7M.js → cleanup-LU6NU2NZ.js} +14 -14
  51. package/dist/cli.js +283 -363
  52. package/dist/cli.js.map +1 -1
  53. package/dist/{contribute-HY372S6F.js → contribute-RS3DO3WP.js} +4 -4
  54. package/dist/{dev-server-JCJGQ3PV.js → dev-server-ASH7HJVI.js} +30 -16
  55. package/dist/dev-server-ASH7HJVI.js.map +1 -0
  56. package/dist/{feedback-7PVBQNLJ.js → feedback-OFVW22UW.js} +11 -6
  57. package/dist/{feedback-7PVBQNLJ.js.map → feedback-OFVW22UW.js.map} +1 -1
  58. package/dist/{git-4BVOOOOV.js → git-OQAPUPLP.js} +16 -6
  59. package/dist/git-OQAPUPLP.js.map +1 -0
  60. package/dist/{ignite-3B264M7K.js → ignite-NREQ3JRM.js} +57 -22
  61. package/dist/ignite-NREQ3JRM.js.map +1 -0
  62. package/dist/index.d.ts +58 -7
  63. package/dist/index.js +110 -7
  64. package/dist/index.js.map +1 -1
  65. package/dist/{init-LBA6NUK2.js → init-F6PFMSU5.js} +7 -7
  66. package/dist/init-F6PFMSU5.js.map +1 -0
  67. package/dist/mcp/recap-server.js +264 -0
  68. package/dist/mcp/recap-server.js.map +1 -0
  69. package/dist/{open-OGCV32Z4.js → open-KW4NTLXH.js} +16 -17
  70. package/dist/{open-OGCV32Z4.js.map → open-KW4NTLXH.js.map} +1 -1
  71. package/dist/{projects-P55273AB.js → projects-QEAEBAT2.js} +2 -2
  72. package/dist/prompts/init-prompt.txt +31 -72
  73. package/dist/prompts/issue-prompt.txt +115 -15
  74. package/dist/prompts/pr-prompt.txt +49 -1
  75. package/dist/prompts/regular-prompt.txt +80 -20
  76. package/dist/{rebase-4T5FQHNH.js → rebase-WZHHE5LU.js} +6 -6
  77. package/dist/recap-33NPZ3ZO.js +117 -0
  78. package/dist/recap-33NPZ3ZO.js.map +1 -0
  79. package/dist/{run-HNOP6WE2.js → run-HRYQ7TR7.js} +16 -17
  80. package/dist/{run-HNOP6WE2.js.map → run-HRYQ7TR7.js.map} +1 -1
  81. package/dist/schema/settings.schema.json +13 -2
  82. package/dist/{shell-DE3HKJSM.js → shell-JMU5XTHW.js} +6 -6
  83. package/dist/{summary-GDT7DTRI.js → summary-4SSGGH7N.js} +17 -9
  84. package/dist/summary-4SSGGH7N.js.map +1 -0
  85. package/dist/{test-git-YMAE57UP.js → test-git-6SAIRBUD.js} +4 -4
  86. package/dist/{test-prefix-YCKL6CMT.js → test-prefix-RLVRK5ZD.js} +4 -4
  87. package/package.json +1 -1
  88. package/dist/LoomLauncher-S3YGJRJQ.js.map +0 -1
  89. package/dist/chunk-2W2FBL5G.js.map +0 -1
  90. package/dist/chunk-74VMN2KC.js.map +0 -1
  91. package/dist/chunk-BIIQHEXJ.js.map +0 -1
  92. package/dist/chunk-G6CIIJLT.js.map +0 -1
  93. package/dist/chunk-HD5SUKI2.js.map +0 -1
  94. package/dist/chunk-IARWMDAX.js.map +0 -1
  95. package/dist/chunk-IJ7IGJT3.js.map +0 -1
  96. package/dist/chunk-O7WHXLCB.js.map +0 -1
  97. package/dist/chunk-OF7BNW4D.js.map +0 -1
  98. package/dist/chunk-QRBOPFAA.js +0 -48
  99. package/dist/chunk-QRBOPFAA.js.map +0 -1
  100. package/dist/chunk-VTXCGKV5.js.map +0 -1
  101. package/dist/chunk-VWNS6DH5.js.map +0 -1
  102. package/dist/dev-server-JCJGQ3PV.js.map +0 -1
  103. package/dist/ignite-3B264M7K.js.map +0 -1
  104. package/dist/summary-GDT7DTRI.js.map +0 -1
  105. /package/dist/{ClaudeContextManager-DK77227F.js.map → ClaudeContextManager-DQFKIMEP.js.map} +0 -0
  106. /package/dist/{ClaudeService-W3SA7HVG.js.map → ClaudeService-CJS32WG2.js.map} +0 -0
  107. /package/dist/{PromptTemplateManager-2TDZAUC6.js.map → MetadataManager-WXUVXKUS.js.map} +0 -0
  108. /package/dist/{SettingsManager-FJFU6JJD.js.map → PRManager-7DSIMCAD.js.map} +0 -0
  109. /package/dist/{git-4BVOOOOV.js.map → PromptTemplateManager-72FEOGT6.js.map} +0 -0
  110. /package/dist/{init-LBA6NUK2.js.map → SettingsManager-XPR4TEQL.js.map} +0 -0
  111. /package/dist/{chunk-JC5HXN75.js.map → chunk-3CMGCRB5.js.map} +0 -0
  112. /package/dist/{chunk-55TB3FSG.js.map → chunk-AS2IRKLU.js.map} +0 -0
  113. /package/dist/{chunk-POI7KLBH.js.map → chunk-CDQEK2WD.js.map} +0 -0
  114. /package/dist/{chunk-UPUAQYAW.js.map → chunk-S65T4O6I.js.map} +0 -0
  115. /package/dist/{cleanup-KDLVTT7M.js.map → cleanup-LU6NU2NZ.js.map} +0 -0
  116. /package/dist/{contribute-HY372S6F.js.map → contribute-RS3DO3WP.js.map} +0 -0
  117. /package/dist/{projects-P55273AB.js.map → projects-QEAEBAT2.js.map} +0 -0
  118. /package/dist/{rebase-4T5FQHNH.js.map → rebase-WZHHE5LU.js.map} +0 -0
  119. /package/dist/{shell-DE3HKJSM.js.map → shell-JMU5XTHW.js.map} +0 -0
  120. /package/dist/{test-git-YMAE57UP.js.map → test-git-6SAIRBUD.js.map} +0 -0
  121. /package/dist/{test-prefix-YCKL6CMT.js.map → test-prefix-RLVRK5ZD.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,98 +1,107 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SessionSummaryService
4
- } from "./chunk-TMZAVPGF.js";
4
+ } from "./chunk-HABINPX2.js";
5
+ import {
6
+ FirstRunManager,
7
+ IssueTrackerFactory,
8
+ generateIssueManagementMcpConfig
9
+ } from "./chunk-TSLKDFAF.js";
10
+ import "./chunk-QHA67Q7A.js";
5
11
  import {
6
12
  CLIIsolationManager,
7
13
  DatabaseManager,
8
14
  EnvironmentManager,
9
15
  LoomManager,
10
16
  ResourceCleanup
11
- } from "./chunk-IARWMDAX.js";
17
+ } from "./chunk-6KB7R22U.js";
18
+ import {
19
+ detectPackageManager,
20
+ installDependencies,
21
+ runScript
22
+ } from "./chunk-VBFDVGAE.js";
23
+ import {
24
+ ProcessManager
25
+ } from "./chunk-VU3QMIP2.js";
26
+ import {
27
+ IdentifierParser
28
+ } from "./chunk-AS2IRKLU.js";
29
+ import {
30
+ createNeonProviderFromSettings
31
+ } from "./chunk-UNXRACJ7.js";
12
32
  import {
13
33
  InitCommand,
14
34
  ShellCompletion
15
- } from "./chunk-G6CIIJLT.js";
35
+ } from "./chunk-4YTILIIH.js";
16
36
  import "./chunk-UYWAESOT.js";
17
37
  import {
18
38
  IssueEnhancementService,
19
39
  capitalizeFirstLetter
20
- } from "./chunk-VTXCGKV5.js";
40
+ } from "./chunk-BVIK2P6P.js";
21
41
  import {
22
- MergeManager
23
- } from "./chunk-BIIQHEXJ.js";
42
+ AgentManager
43
+ } from "./chunk-RNZMHJK7.js";
24
44
  import {
25
- FirstRunManager,
26
- IssueTrackerFactory,
27
- generateIssueManagementMcpConfig
28
- } from "./chunk-HD5SUKI2.js";
29
- import "./chunk-QHA67Q7A.js";
45
+ ProjectCapabilityDetector
46
+ } from "./chunk-EBISESAP.js";
30
47
  import {
31
- AgentManager
32
- } from "./chunk-O7WHXLCB.js";
48
+ hasScript,
49
+ readPackageJson
50
+ } from "./chunk-2ZPFJQ3B.js";
33
51
  import {
34
- IdentifierParser
35
- } from "./chunk-55TB3FSG.js";
52
+ MergeManager
53
+ } from "./chunk-GVRO4PWE.js";
36
54
  import {
37
- ProcessManager
38
- } from "./chunk-VU3QMIP2.js";
55
+ GitWorktreeManager
56
+ } from "./chunk-3CMGCRB5.js";
57
+ import {
58
+ PRManager
59
+ } from "./chunk-P2ZQ5LKB.js";
39
60
  import {
40
61
  openBrowser
41
62
  } from "./chunk-YETJNRQM.js";
42
63
  import {
43
- GitWorktreeManager
44
- } from "./chunk-JC5HXN75.js";
64
+ getConfiguredRepoFromSettings,
65
+ hasMultipleRemotes
66
+ } from "./chunk-PSFVTBM7.js";
45
67
  import {
46
- detectPackageManager,
47
- installDependencies,
48
- runScript
49
- } from "./chunk-VBFDVGAE.js";
68
+ getIdeConfig,
69
+ getInstallHint,
70
+ isIdeAvailable
71
+ } from "./chunk-CDF7ZX2B.js";
50
72
  import {
51
73
  ClaudeContextManager
52
- } from "./chunk-UPUAQYAW.js";
53
- import "./chunk-POI7KLBH.js";
74
+ } from "./chunk-S65T4O6I.js";
75
+ import "./chunk-CDQEK2WD.js";
76
+ import "./chunk-DKQ4SUII.js";
54
77
  import {
55
78
  extractSettingsOverrides
56
79
  } from "./chunk-GYCR2LOU.js";
57
80
  import {
58
81
  DefaultBranchNamingService
59
82
  } from "./chunk-QIUJPPJQ.js";
60
- import {
61
- ProjectCapabilityDetector
62
- } from "./chunk-EBISESAP.js";
63
- import {
64
- hasScript,
65
- readPackageJson
66
- } from "./chunk-2ZPFJQ3B.js";
67
- import {
68
- createNeonProviderFromSettings
69
- } from "./chunk-UNXRACJ7.js";
70
- import {
71
- getConfiguredRepoFromSettings,
72
- getEffectivePRTargetRemote,
73
- hasMultipleRemotes,
74
- parseGitRemotes
75
- } from "./chunk-PSFVTBM7.js";
76
83
  import {
77
84
  executeGitCommand,
78
85
  extractIssueNumber,
79
86
  findMainWorktreePathWithSettings,
87
+ findPlaceholderCommitSha,
80
88
  getMergeTargetBranch,
81
89
  getRepoRoot,
82
- pushBranchToRemote
83
- } from "./chunk-2W2FBL5G.js";
84
- import {
85
- MetadataManager
86
- } from "./chunk-IJ7IGJT3.js";
90
+ isPlaceholderCommit,
91
+ pushBranchToRemote,
92
+ removePlaceholderCommitFromHead,
93
+ removePlaceholderCommitFromHistory
94
+ } from "./chunk-LN4H3A6A.js";
87
95
  import {
88
96
  SettingsManager
89
- } from "./chunk-VWNS6DH5.js";
97
+ } from "./chunk-OOU3DKNT.js";
98
+ import {
99
+ MetadataManager
100
+ } from "./chunk-YZTDGPFB.js";
90
101
  import {
91
102
  GitHubService
92
103
  } from "./chunk-OEGECBFS.js";
93
- import {
94
- executeGhCommand
95
- } from "./chunk-KO2FOMHL.js";
104
+ import "./chunk-KO2FOMHL.js";
96
105
  import {
97
106
  promptCommitAction,
98
107
  promptConfirmation,
@@ -111,7 +120,6 @@ import {
111
120
  getLogger,
112
121
  withLogger
113
122
  } from "./chunk-6UIGZD2N.js";
114
- import "./chunk-74VMN2KC.js";
115
123
  import {
116
124
  createStderrLogger,
117
125
  logger
@@ -605,7 +613,11 @@ var AddIssueCommand = class {
605
613
  getLogger().info(`Using GitHub repository: ${repo}`);
606
614
  }
607
615
  }
608
- if (!description || !this.enhancementService.validateDescription(description)) {
616
+ const hasBody = !!body;
617
+ if (!description || !this.enhancementService.validateDescription(description, hasBody)) {
618
+ if (hasBody) {
619
+ throw new Error("Description is required and cannot be empty");
620
+ }
609
621
  throw new Error("Description is required and must be more than 30 characters with at least 3 words");
610
622
  }
611
623
  const issueBody = body ?? await this.enhancementService.enhanceDescription(description);
@@ -1664,267 +1676,6 @@ Run '${runCommand}' to see detailed errors.`
1664
1676
  }
1665
1677
  };
1666
1678
 
1667
- // src/lib/PRManager.ts
1668
- var PRManager = class {
1669
- constructor(settings) {
1670
- this.settings = settings;
1671
- }
1672
- /**
1673
- * Check if a PR already exists for the given branch
1674
- * @param branchName - Branch to check
1675
- * @param cwd - Working directory
1676
- * @returns Existing PR info or null if none found
1677
- */
1678
- async checkForExistingPR(branchName, cwd) {
1679
- try {
1680
- const prList = await executeGhCommand(
1681
- ["pr", "list", "--head", branchName, "--state", "open", "--json", "number,url"],
1682
- cwd ? { cwd } : void 0
1683
- );
1684
- if (prList.length > 0) {
1685
- return prList[0] ?? null;
1686
- }
1687
- return null;
1688
- } catch (error) {
1689
- getLogger().debug("Error checking for existing PR", { error });
1690
- return null;
1691
- }
1692
- }
1693
- /**
1694
- * Generate PR body using Claude if available, otherwise use simple template
1695
- * @param issueNumber - Issue number to include in body
1696
- * @param worktreePath - Path to worktree for context
1697
- * @returns PR body markdown
1698
- */
1699
- async generatePRBody(issueNumber, worktreePath) {
1700
- const hasClaudeCli = await detectClaudeCli();
1701
- if (hasClaudeCli) {
1702
- try {
1703
- const prompt = this.buildPRBodyPrompt(issueNumber);
1704
- const body2 = await launchClaude(prompt, {
1705
- headless: true,
1706
- addDir: worktreePath,
1707
- timeout: 3e4
1708
- });
1709
- if (body2 && typeof body2 === "string" && body2.trim()) {
1710
- const sanitized = this.sanitizeClaudeOutput(body2);
1711
- if (sanitized) {
1712
- return sanitized;
1713
- }
1714
- }
1715
- } catch (error) {
1716
- getLogger().debug("Claude PR body generation failed, using template", { error });
1717
- }
1718
- }
1719
- let body = "This PR contains changes from the iloom workflow.\n\n";
1720
- if (issueNumber) {
1721
- body += `Fixes #${issueNumber}`;
1722
- }
1723
- return body;
1724
- }
1725
- /**
1726
- * Build structured XML prompt for PR body generation
1727
- * Uses XML format for clear task definition and output expectations
1728
- */
1729
- buildPRBodyPrompt(issueNumber) {
1730
- const issueContext = issueNumber ? `
1731
- <IssueContext>
1732
- This PR is associated with GitHub issue #${issueNumber}.
1733
- Include "Fixes #${issueNumber}" at the end of the body on its own line.
1734
- </IssueContext>` : "";
1735
- return `<Task>
1736
- You are a software engineer writing a pull request body for this repository.
1737
- Examine the changes in the git repository and generate a concise, professional PR description.
1738
- </Task>
1739
-
1740
- <Requirements>
1741
- <Format>Write 2-3 sentences summarizing what was changed and why.${issueNumber ? `
1742
-
1743
- End with "Fixes #${issueNumber}" on its own line.` : ""}</Format>
1744
- <Tone>Professional and concise</Tone>
1745
- <Focus>Summarize the changes and their purpose</Focus>
1746
- <NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw PR body text.</NoMeta>
1747
- <Examples>
1748
- Good: "Add user authentication with JWT tokens to secure the API endpoints. This includes login and registration endpoints with proper password hashing.
1749
-
1750
- Fixes #42"
1751
- Good: "Fix navigation bug in sidebar menu that caused incorrect highlighting on nested routes."
1752
- Bad: "Here's the PR body:
1753
-
1754
- ---
1755
-
1756
- Add user authentication..."
1757
- Bad: "Based on the changes, I'll write: Fix navigation bug..."
1758
- </Examples>
1759
- ${issueContext}
1760
- </Requirements>
1761
-
1762
- <Output>
1763
- IMPORTANT: Your entire response will be used directly as the GitHub pull request body.
1764
- Do not include any explanatory text, headers, or separators before or after the body.
1765
- Start your response immediately with the PR body text.
1766
- </Output>`;
1767
- }
1768
- /**
1769
- * Sanitize Claude output to remove meta-commentary and clean formatting
1770
- * Handles cases where Claude includes explanatory text despite instructions
1771
- */
1772
- sanitizeClaudeOutput(rawOutput) {
1773
- let cleaned = rawOutput.trim();
1774
- const metaPatterns = [
1775
- /^.*?based on.*?changes.*?:/i,
1776
- /^.*?looking at.*?files.*?:/i,
1777
- /^.*?examining.*?:/i,
1778
- /^.*?analyzing.*?:/i,
1779
- /^.*?i'll.*?generate.*?:/i,
1780
- /^.*?let me.*?:/i,
1781
- /^.*?here.*?is.*?(?:the\s+)?(?:pr|pull request).*?body.*?:/i,
1782
- /^.*?here's.*?(?:the\s+)?(?:pr|pull request).*?body.*?:/i
1783
- ];
1784
- for (const pattern of metaPatterns) {
1785
- cleaned = cleaned.replace(pattern, "").trim();
1786
- }
1787
- cleaned = cleaned.replace(/^[-=]{3,}\s*/m, "").trim();
1788
- if (cleaned.includes(":")) {
1789
- const colonIndex = cleaned.indexOf(":");
1790
- const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase();
1791
- const metaIndicators = [
1792
- "here is the pr body",
1793
- "here is the pull request body",
1794
- "pr body",
1795
- "pull request body",
1796
- "here is",
1797
- "here's",
1798
- "the body should be",
1799
- "i suggest",
1800
- "my suggestion"
1801
- ];
1802
- const isMetaCommentary = metaIndicators.some((indicator) => beforeColon.includes(indicator));
1803
- if (isMetaCommentary) {
1804
- const afterColon = cleaned.substring(colonIndex + 1).trim();
1805
- const afterSeparator = afterColon.replace(/^[-=]{3,}\s*/m, "").trim();
1806
- if (afterSeparator && afterSeparator.length > 10) {
1807
- cleaned = afterSeparator;
1808
- }
1809
- }
1810
- }
1811
- if (cleaned.startsWith('"') && cleaned.endsWith('"') || cleaned.startsWith("'") && cleaned.endsWith("'")) {
1812
- cleaned = cleaned.slice(1, -1).trim();
1813
- }
1814
- return cleaned;
1815
- }
1816
- /**
1817
- * Create a GitHub PR for the branch
1818
- * @param branchName - Branch to create PR from (used as --head)
1819
- * @param title - PR title
1820
- * @param body - PR body
1821
- * @param baseBranch - Base branch to target (usually main/master)
1822
- * @param cwd - Working directory
1823
- * @returns PR URL
1824
- */
1825
- async createPR(branchName, title, body, baseBranch, cwd) {
1826
- try {
1827
- const targetRemote = await getEffectivePRTargetRemote(this.settings, cwd);
1828
- let headValue = branchName;
1829
- if (targetRemote !== "origin") {
1830
- const remotes = await parseGitRemotes(cwd);
1831
- const originRemote = remotes.find((r) => r.name === "origin");
1832
- if (originRemote) {
1833
- headValue = `${originRemote.owner}:${branchName}`;
1834
- getLogger().debug(`Fork workflow detected, using head: ${headValue}`);
1835
- }
1836
- }
1837
- const args = ["pr", "create", "--head", headValue, "--title", title, "--body", body, "--base", baseBranch];
1838
- if (targetRemote !== "origin") {
1839
- const repo = await getConfiguredRepoFromSettings(this.settings, cwd);
1840
- args.push("--repo", repo);
1841
- }
1842
- const result = await executeGhCommand(args, cwd ? { cwd } : void 0);
1843
- const url = typeof result === "string" ? result.trim() : String(result).trim();
1844
- if (!url.includes("github.com") || !url.includes("/pull/")) {
1845
- throw new Error(`Unexpected response from gh pr create: ${url}`);
1846
- }
1847
- return url;
1848
- } catch (error) {
1849
- const errorMessage = error instanceof Error ? error.message : String(error);
1850
- if (errorMessage.includes("Head sha can't be blank") || errorMessage.includes("No commits between")) {
1851
- throw new Error(
1852
- `Failed to create pull request: ${errorMessage}
1853
-
1854
- This error typically occurs when:
1855
- - The branch was not fully pushed to the remote
1856
- - There's a race condition between push and PR creation
1857
- - The branch has no commits ahead of the base branch
1858
-
1859
- Try running: git push -u origin ${branchName}
1860
- Then retry: il finish`
1861
- );
1862
- }
1863
- throw new Error(`Failed to create pull request: ${errorMessage}`);
1864
- }
1865
- }
1866
- /**
1867
- * Open PR URL in browser
1868
- * @param url - PR URL to open
1869
- */
1870
- async openPRInBrowser(url) {
1871
- try {
1872
- await openBrowser(url);
1873
- getLogger().debug("Opened PR in browser", { url });
1874
- } catch (error) {
1875
- getLogger().warn("Failed to open PR in browser", { error });
1876
- }
1877
- }
1878
- /**
1879
- * Complete PR workflow: check for existing, create if needed, optionally open in browser
1880
- * @param branchName - Branch to create PR from
1881
- * @param title - PR title
1882
- * @param issueNumber - Optional issue number for body generation
1883
- * @param baseBranch - Base branch to target
1884
- * @param worktreePath - Path to worktree
1885
- * @param openInBrowser - Whether to open PR in browser
1886
- * @returns PR creation result
1887
- */
1888
- async createOrOpenPR(branchName, title, issueNumber, baseBranch, worktreePath, openInBrowser) {
1889
- const existingPR = await this.checkForExistingPR(branchName, worktreePath);
1890
- if (existingPR) {
1891
- getLogger().info(`Pull request already exists: ${existingPR.url}`);
1892
- if (openInBrowser) {
1893
- await this.openPRInBrowser(existingPR.url);
1894
- }
1895
- return {
1896
- url: existingPR.url,
1897
- number: existingPR.number,
1898
- wasExisting: true
1899
- };
1900
- }
1901
- const body = await this.generatePRBody(issueNumber, worktreePath);
1902
- getLogger().info("Creating pull request...");
1903
- const url = await this.createPR(branchName, title, body, baseBranch, worktreePath);
1904
- const prNumber = this.extractPRNumberFromUrl(url);
1905
- if (openInBrowser) {
1906
- await this.openPRInBrowser(url);
1907
- }
1908
- return {
1909
- url,
1910
- number: prNumber,
1911
- wasExisting: false
1912
- };
1913
- }
1914
- /**
1915
- * Extract PR number from GitHub PR URL
1916
- * @param url - PR URL (e.g., https://github.com/owner/repo/pull/123)
1917
- * @returns PR number
1918
- */
1919
- extractPRNumberFromUrl(url) {
1920
- const match = url.match(/\/pull\/(\d+)/);
1921
- if (match == null ? void 0 : match[1]) {
1922
- return parseInt(match[1], 10);
1923
- }
1924
- throw new Error(`Could not extract PR number from URL: ${url}`);
1925
- }
1926
- };
1927
-
1928
1679
  // src/commands/finish.ts
1929
1680
  import path3 from "path";
1930
1681
  var FinishCommand = class {
@@ -2302,10 +2053,22 @@ var FinishCommand = class {
2302
2053
  }
2303
2054
  /**
2304
2055
  * Execute workflow for issues and branches (merge into main)
2305
- * This is the traditional workflow: validatecommitrebase → merge → cleanup
2056
+ * This is the workflow: rebasevalidatecommit → merge → cleanup
2306
2057
  */
2307
2058
  async executeIssueWorkflow(parsed, options, worktree, result) {
2308
- var _a, _b;
2059
+ var _a, _b, _c;
2060
+ getLogger().info("Rebasing branch on main...");
2061
+ const mergeOptions = {
2062
+ dryRun: options.dryRun ?? false,
2063
+ force: options.force ?? false
2064
+ };
2065
+ await this.mergeManager.rebaseOnMain(worktree.path, mergeOptions);
2066
+ getLogger().success("Branch rebased successfully");
2067
+ result.operations.push({
2068
+ type: "rebase",
2069
+ message: "Branch rebased on main",
2070
+ success: true
2071
+ });
2309
2072
  if (!options.dryRun) {
2310
2073
  getLogger().info("Running pre-merge validations...");
2311
2074
  await this.validationRunner.runValidations(worktree.path, {
@@ -2380,18 +2143,92 @@ var FinishCommand = class {
2380
2143
  await this.executeGitHubPRWorkflow(parsed, options, worktree, settings, result);
2381
2144
  return;
2382
2145
  }
2383
- getLogger().info("Rebasing branch on main...");
2384
- const mergeOptions = {
2385
- dryRun: options.dryRun ?? false,
2386
- force: options.force ?? false
2387
- };
2388
- await this.mergeManager.rebaseOnMain(worktree.path, mergeOptions);
2389
- getLogger().success("Branch rebased successfully");
2390
- result.operations.push({
2391
- type: "rebase",
2392
- message: "Branch rebased on main",
2393
- success: true
2394
- });
2146
+ if (mergeBehavior.mode === "github-draft-pr") {
2147
+ if (!this.issueTracker.supportsPullRequests) {
2148
+ throw new Error(
2149
+ `The 'github-draft-pr' merge mode requires a GitHub-compatible issue tracker. Your provider (${this.issueTracker.providerName}) does not support pull requests.`
2150
+ );
2151
+ }
2152
+ const { MetadataManager: MetadataManager2 } = await import("./MetadataManager-WXUVXKUS.js");
2153
+ const metadataManager = new MetadataManager2();
2154
+ const metadata = await metadataManager.readMetadata(worktree.path);
2155
+ getLogger().debug(`Draft PR mode: worktree=${worktree.path}, draftPrNumber=${(metadata == null ? void 0 : metadata.draftPrNumber) ?? "none"}`);
2156
+ if (!(metadata == null ? void 0 : metadata.draftPrNumber)) {
2157
+ getLogger().warn("No draft PR found in metadata, creating new PR...");
2158
+ await this.executeGitHubPRWorkflow(parsed, options, worktree, settings, result);
2159
+ return;
2160
+ }
2161
+ const isHeadPlaceholder = await isPlaceholderCommit(worktree.path);
2162
+ const placeholderSha = await findPlaceholderCommitSha(worktree.path);
2163
+ getLogger().debug(`Placeholder detection: isHead=${isHeadPlaceholder}, sha=${placeholderSha ?? "none"}`);
2164
+ if (isHeadPlaceholder) {
2165
+ const commitCount = await executeGitCommand(
2166
+ ["rev-list", "--count", "HEAD"],
2167
+ { cwd: worktree.path }
2168
+ );
2169
+ if (parseInt(commitCount.trim(), 10) <= 1) {
2170
+ throw new Error(
2171
+ "Cannot finish draft PR: no changes have been committed.\nPlease make at least one commit before finishing."
2172
+ );
2173
+ }
2174
+ if (!options.dryRun) {
2175
+ getLogger().info("Removing placeholder commit from HEAD...");
2176
+ await removePlaceholderCommitFromHead(worktree.path);
2177
+ } else {
2178
+ getLogger().info("[DRY RUN] Would remove placeholder commit from HEAD");
2179
+ }
2180
+ } else if (placeholderSha) {
2181
+ const commitsAfterPlaceholder = await executeGitCommand(
2182
+ ["rev-list", "--count", `${placeholderSha}..HEAD`],
2183
+ { cwd: worktree.path }
2184
+ );
2185
+ if (parseInt(commitsAfterPlaceholder.trim(), 10) === 0) {
2186
+ throw new Error(
2187
+ "Cannot finish draft PR: no changes have been committed after the placeholder.\nPlease make at least one commit before finishing."
2188
+ );
2189
+ }
2190
+ if (!options.dryRun) {
2191
+ getLogger().info("Removing placeholder commit from history...");
2192
+ await removePlaceholderCommitFromHistory(worktree.path, placeholderSha);
2193
+ } else {
2194
+ getLogger().info("[DRY RUN] Would remove placeholder commit from history");
2195
+ }
2196
+ }
2197
+ const needsForceWithLease = isHeadPlaceholder || placeholderSha;
2198
+ if (!options.dryRun) {
2199
+ getLogger().info("Pushing final commits to remote...");
2200
+ if (needsForceWithLease) {
2201
+ await executeGitCommand(["push", "--force-with-lease", "origin", worktree.branch], { cwd: worktree.path });
2202
+ } else {
2203
+ await pushBranchToRemote(worktree.branch, worktree.path, { dryRun: false });
2204
+ }
2205
+ } else {
2206
+ if (needsForceWithLease) {
2207
+ getLogger().info("[DRY RUN] Would force push final commits to remote (history rewritten)");
2208
+ } else {
2209
+ getLogger().info("[DRY RUN] Would push final commits to remote");
2210
+ }
2211
+ }
2212
+ const prManager = new PRManager(settings);
2213
+ if (!options.dryRun) {
2214
+ await prManager.markPRReady(metadata.draftPrNumber, worktree.path);
2215
+ getLogger().success(`PR #${metadata.draftPrNumber} marked as ready for review`);
2216
+ } else {
2217
+ getLogger().info(`[DRY RUN] Would mark PR #${metadata.draftPrNumber} as ready for review`);
2218
+ }
2219
+ const prUrl = (_c = metadata.prUrls) == null ? void 0 : _c[String(metadata.draftPrNumber)];
2220
+ if (prUrl) {
2221
+ result.prUrl = prUrl;
2222
+ }
2223
+ result.operations.push({
2224
+ type: "pr-ready",
2225
+ message: `PR #${metadata.draftPrNumber} marked as ready for review`,
2226
+ success: true
2227
+ });
2228
+ await this.generateSessionSummaryIfConfigured(parsed, worktree, options, metadata.draftPrNumber);
2229
+ await this.handlePRCleanupPrompt(parsed, options, worktree, result);
2230
+ return;
2231
+ }
2395
2232
  getLogger().info("Performing fast-forward merge...");
2396
2233
  await this.mergeManager.performFastForwardMerge(worktree.branch, worktree.path, mergeOptions);
2397
2234
  getLogger().success("Fast-forward merge completed successfully");
@@ -2555,7 +2392,7 @@ var FinishCommand = class {
2555
2392
  });
2556
2393
  }
2557
2394
  finishResult.prUrl = prResult.url;
2558
- await this.generateSessionSummaryIfConfigured(parsed, worktree, options);
2395
+ await this.generateSessionSummaryIfConfigured(parsed, worktree, options, prResult.number);
2559
2396
  await this.handlePRCleanupPrompt(parsed, options, worktree, finishResult);
2560
2397
  }
2561
2398
  }
@@ -2578,8 +2415,8 @@ var FinishCommand = class {
2578
2415
  getLogger().info("");
2579
2416
  const shouldCleanup = await promptConfirmation(
2580
2417
  "Clean up worktree now?",
2581
- false
2582
- // Default to keeping worktree (safer option)
2418
+ true
2419
+ // Default to keeping worktree - won't delete if unmerged changes
2583
2420
  );
2584
2421
  if (shouldCleanup) {
2585
2422
  await this.performWorktreeCleanup(parsed, options, worktree, finishResult);
@@ -2720,8 +2557,13 @@ var FinishCommand = class {
2720
2557
  * This ensures the finish workflow continues even if summary generation fails
2721
2558
  *
2722
2559
  * In dry-run mode: generates summary and shows preview, but doesn't post
2560
+ *
2561
+ * @param parsed - The parsed input identifying the issue/PR being finished
2562
+ * @param worktree - The worktree being finished
2563
+ * @param options - Finish options (including dryRun flag)
2564
+ * @param prNumber - Optional PR number - when provided, summary is posted to the PR instead of the issue
2723
2565
  */
2724
- async generateSessionSummaryIfConfigured(parsed, worktree, options) {
2566
+ async generateSessionSummaryIfConfigured(parsed, worktree, options, prNumber) {
2725
2567
  if (parsed.type === "branch") {
2726
2568
  return;
2727
2569
  }
@@ -2752,7 +2594,8 @@ var FinishCommand = class {
2752
2594
  worktreePath: worktree.path,
2753
2595
  issueNumber: parsed.number ?? 0,
2754
2596
  branchName: worktree.branch,
2755
- loomType: parsed.type
2597
+ loomType: parsed.type,
2598
+ ...prNumber !== void 0 && { prNumber }
2756
2599
  });
2757
2600
  }
2758
2601
  /**
@@ -2992,7 +2835,9 @@ function formatLoomForJson(worktree, mainWorktreePath, metadata) {
2992
2835
  created_at: (metadata == null ? void 0 : metadata.created_at) ?? null,
2993
2836
  issueTracker: (metadata == null ? void 0 : metadata.issueTracker) ?? null,
2994
2837
  colorHex: (metadata == null ? void 0 : metadata.colorHex) ?? null,
2995
- projectPath: (metadata == null ? void 0 : metadata.projectPath) ?? null
2838
+ projectPath: (metadata == null ? void 0 : metadata.projectPath) ?? null,
2839
+ issueUrls: (metadata == null ? void 0 : metadata.issueUrls) ?? {},
2840
+ prUrls: (metadata == null ? void 0 : metadata.prUrls) ?? {}
2996
2841
  };
2997
2842
  }
2998
2843
  function formatLoomsForJson(worktrees, mainWorktreePath, metadata) {
@@ -3006,7 +2851,7 @@ function parseIssueIdentifier(value) {
3006
2851
  const parsed = parseInt(value, 10);
3007
2852
  return !isNaN(parsed) && String(parsed) === value ? parsed : value;
3008
2853
  }
3009
- program.name("iloom").description(packageJson.description).version(packageJson.version).option("--debug", "Enable debug output (default: based on ILOOM_DEBUG env var)").option("--completion", "Output shell completion script for current shell").option("--set <key=value>", "Override any setting using dot notation (repeatable, e.g., --set workflows.issue.startIde=false)").allowUnknownOption().hook("preAction", async (thisCommand) => {
2854
+ program.name("iloom").description(packageJson.description).version(packageJson.version).option("--debug", "Enable debug output (default: based on ILOOM_DEBUG env var)").option("--completion", "Output shell completion script for current shell").option("--set <key=value>", "Override any setting using dot notation (repeatable, e.g., --set workflows.issue.startIde=false)").allowUnknownOption().hook("preAction", async (thisCommand, actionCommand) => {
3010
2855
  const options = thisCommand.opts();
3011
2856
  const envDebug = process.env.ILOOM_DEBUG === "true";
3012
2857
  const debugEnabled = options.debug !== void 0 ? options.debug : envDebug;
@@ -3030,16 +2875,18 @@ program.name("iloom").description(packageJson.description).version(packageJson.v
3030
2875
  } catch (error) {
3031
2876
  logger.debug(`Settings migration failed: ${error instanceof Error ? error.message : "Unknown"}`);
3032
2877
  }
3033
- await validateSettingsForCommand(thisCommand);
3034
- await validateGhCliForCommand(thisCommand);
2878
+ await validateSettingsForCommand(actionCommand);
2879
+ await validateGhCliForCommand(actionCommand);
2880
+ await validateIdeForStartCommand(thisCommand);
3035
2881
  });
3036
2882
  async function validateSettingsForCommand(command) {
3037
2883
  var _a, _b;
3038
- const commandName = command.args[0] ?? "";
2884
+ const commandName = command.name();
3039
2885
  const bypassCommands = ["help", "init", "update", "contribute"];
3040
2886
  if (bypassCommands.includes(commandName)) {
3041
2887
  return;
3042
2888
  }
2889
+ const warnOnlyCommands = ["list", "projects"];
3043
2890
  try {
3044
2891
  const settingsManager = new SettingsManager();
3045
2892
  const settings = await settingsManager.loadSettings();
@@ -3049,6 +2896,10 @@ async function validateSettingsForCommand(command) {
3049
2896
  return;
3050
2897
  }
3051
2898
  } catch (error) {
2899
+ if (warnOnlyCommands.includes(commandName)) {
2900
+ logger.warn(`Configuration warning: ${error instanceof Error ? error.message : "Unknown error"}`);
2901
+ return;
2902
+ }
3052
2903
  logger.error(`Configuration error: ${error instanceof Error ? error.message : "Unknown error"}`);
3053
2904
  logger.info("Please fix your .iloom/settings.json file and try again.");
3054
2905
  process.exit(1);
@@ -3056,7 +2907,7 @@ async function validateSettingsForCommand(command) {
3056
2907
  }
3057
2908
  async function validateGhCliForCommand(command) {
3058
2909
  var _a, _b;
3059
- const commandName = command.args[0] ?? "";
2910
+ const commandName = command.name();
3060
2911
  const alwaysRequireGh = ["feedback", "contribute"];
3061
2912
  const conditionallyRequireGh = ["start", "finish", "enhance", "add-issue", "ignite", "spin"];
3062
2913
  const warnOnly = ["init", "list", "rebase", "cleanup", "run", "update", "open"];
@@ -3107,6 +2958,41 @@ async function validateGhCliForCommand(command) {
3107
2958
  }
3108
2959
  }
3109
2960
  }
2961
+ async function validateIdeForStartCommand(command) {
2962
+ var _a, _b;
2963
+ const commandName = command.args[0] ?? "";
2964
+ if (commandName !== "start") {
2965
+ return;
2966
+ }
2967
+ const codeOption = command.opts()["code"];
2968
+ if (codeOption === false) {
2969
+ return;
2970
+ }
2971
+ const settingsManager = new SettingsManager();
2972
+ let settings;
2973
+ try {
2974
+ settings = await settingsManager.loadSettings();
2975
+ } catch {
2976
+ return;
2977
+ }
2978
+ const workflowConfig = (_a = settings.workflows) == null ? void 0 : _a.issue;
2979
+ if ((workflowConfig == null ? void 0 : workflowConfig.startIde) === false && codeOption !== true) {
2980
+ return;
2981
+ }
2982
+ const ideConfig = getIdeConfig(settings.ide);
2983
+ const available = await isIdeAvailable(ideConfig.command);
2984
+ if (!available) {
2985
+ const hint = getInstallHint(((_b = settings.ide) == null ? void 0 : _b.type) ?? "vscode");
2986
+ logger.error(
2987
+ `${ideConfig.name} is configured as your IDE but "${ideConfig.command}" command was not found.`
2988
+ );
2989
+ logger.info("");
2990
+ logger.info(hint);
2991
+ logger.info("");
2992
+ logger.info("Alternatively, use --no-code to skip IDE launch or configure a different IDE in settings.");
2993
+ process.exit(1);
2994
+ }
2995
+ }
3110
2996
  async function autoLaunchInitForMultipleRemotes() {
3111
2997
  var _a, _b;
3112
2998
  logger.info("Multiple git remotes detected, but no GitHub remote is configured.");
@@ -3118,14 +3004,14 @@ async function autoLaunchInitForMultipleRemotes() {
3118
3004
  await waitForKeypress2("Press any key to start configuration...");
3119
3005
  logger.info("");
3120
3006
  try {
3121
- const { InitCommand: InitCommand2 } = await import("./init-LBA6NUK2.js");
3007
+ const { InitCommand: InitCommand2 } = await import("./init-F6PFMSU5.js");
3122
3008
  const initCommand = new InitCommand2();
3123
3009
  const customInitialMessage = "Help me configure which git remote iloom should use for GitHub operations. I have multiple remotes and need to select the correct one.";
3124
3010
  await initCommand.execute(customInitialMessage);
3125
3011
  logger.info("");
3126
3012
  logger.info("Configuration complete! Continuing with your original command...");
3127
3013
  logger.info("");
3128
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-FJFU6JJD.js");
3014
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-XPR4TEQL.js");
3129
3015
  const settingsManager = new SettingsManager2();
3130
3016
  const settings = await settingsManager.loadSettings();
3131
3017
  const { hasMultipleRemotes: hasMultipleRemotes2 } = await import("./remote-73TZ2ADI.js");
@@ -3183,7 +3069,7 @@ program.command("start").alias("new").alias("create").alias("up").description("C
3183
3069
  await executeAction();
3184
3070
  }
3185
3071
  });
3186
- program.command("add-issue").alias("a").description("Create and enhance GitHub issue without starting workspace").argument("<description>", "Natural language description of the issue (>50 chars, >2 spaces)").option("--body <text>", "Body text for issue (skips AI enhancement)").option("--json", "Output result as JSON").action(async (description, options) => {
3072
+ program.command("add-issue").alias("a").description("Create and enhance GitHub issue without starting workspace").argument("<description>", "Issue title (>30 chars, >2 spaces; or any non-empty text when --body provided)").option("--body <text>", "Body text for issue (skips AI enhancement)").option("--json", "Output result as JSON").action(async (description, options) => {
3187
3073
  const executeAction = async () => {
3188
3074
  try {
3189
3075
  const settingsManager = new SettingsManager();
@@ -3217,9 +3103,9 @@ program.command("add-issue").alias("a").description("Create and enhance GitHub i
3217
3103
  await executeAction();
3218
3104
  }
3219
3105
  });
3220
- program.command("feedback").alias("f").description("Submit feedback/bug report to iloom-cli repository").argument("<description>", "Natural language description of feedback (>50 chars, >2 spaces)").option("--body <text>", "Body text for feedback (added after diagnostics)").action(async (description, options) => {
3106
+ program.command("feedback").alias("f").description("Submit feedback/bug report to iloom-cli repository").argument("<description>", "Feedback title (>30 chars, >2 spaces; or any non-empty text when --body provided)").option("--body <text>", "Body text for feedback (added after diagnostics)").action(async (description, options) => {
3221
3107
  try {
3222
- const { FeedbackCommand } = await import("./feedback-7PVBQNLJ.js");
3108
+ const { FeedbackCommand } = await import("./feedback-OFVW22UW.js");
3223
3109
  const command = new FeedbackCommand();
3224
3110
  const feedbackOptions = {};
3225
3111
  if (options.body !== void 0) {
@@ -3299,7 +3185,7 @@ program.command("finish").alias("dn").description("Merge work and cleanup worksp
3299
3185
  });
3300
3186
  program.command("rebase").description("Rebase current branch on main with Claude-assisted conflict resolution").option("-f, --force", "Skip confirmation prompts").option("-n, --dry-run", "Preview actions without executing").action(async (options) => {
3301
3187
  try {
3302
- const { RebaseCommand } = await import("./rebase-4T5FQHNH.js");
3188
+ const { RebaseCommand } = await import("./rebase-WZHHE5LU.js");
3303
3189
  const command = new RebaseCommand();
3304
3190
  await command.execute(options);
3305
3191
  } catch (error) {
@@ -3311,7 +3197,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3311
3197
  new Option("--one-shot <mode>", "One-shot automation mode").choices(["default", "noReview", "bypassPermissions"]).default("default")
3312
3198
  ).action(async (options) => {
3313
3199
  try {
3314
- const { IgniteCommand } = await import("./ignite-3B264M7K.js");
3200
+ const { IgniteCommand } = await import("./ignite-NREQ3JRM.js");
3315
3201
  const command = new IgniteCommand();
3316
3202
  await command.execute(options.oneShot ?? "default");
3317
3203
  } catch (error) {
@@ -3322,7 +3208,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3322
3208
  program.command("open").description("Open workspace in browser or run CLI tool").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").allowUnknownOption().action(async (identifier, _options, command) => {
3323
3209
  try {
3324
3210
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3325
- const { OpenCommand } = await import("./open-OGCV32Z4.js");
3211
+ const { OpenCommand } = await import("./open-KW4NTLXH.js");
3326
3212
  const cmd = new OpenCommand();
3327
3213
  const input = identifier ? { identifier, args } : { args };
3328
3214
  await cmd.execute(input);
@@ -3334,7 +3220,7 @@ program.command("open").description("Open workspace in browser or run CLI tool")
3334
3220
  program.command("run").description("Run CLI tool or open workspace in browser").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").allowUnknownOption().action(async (identifier, _options, command) => {
3335
3221
  try {
3336
3222
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3337
- const { RunCommand } = await import("./run-HNOP6WE2.js");
3223
+ const { RunCommand } = await import("./run-HRYQ7TR7.js");
3338
3224
  const cmd = new RunCommand();
3339
3225
  const input = identifier ? { identifier, args } : { args };
3340
3226
  await cmd.execute(input);
@@ -3345,7 +3231,7 @@ program.command("run").description("Run CLI tool or open workspace in browser").
3345
3231
  });
3346
3232
  program.command("dev-server").alias("dev").description("Start dev server for workspace (foreground)").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").option("--json", "Output as JSON").action(async (identifier, options) => {
3347
3233
  try {
3348
- const { DevServerCommand } = await import("./dev-server-JCJGQ3PV.js");
3234
+ const { DevServerCommand } = await import("./dev-server-ASH7HJVI.js");
3349
3235
  const cmd = new DevServerCommand();
3350
3236
  await cmd.execute({ identifier, json: options == null ? void 0 : options.json });
3351
3237
  } catch (error) {
@@ -3355,7 +3241,7 @@ program.command("dev-server").alias("dev").description("Start dev server for wor
3355
3241
  });
3356
3242
  program.command("shell").alias("terminal").description("Open interactive shell with workspace environment").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3357
3243
  try {
3358
- const { ShellCommand } = await import("./shell-DE3HKJSM.js");
3244
+ const { ShellCommand } = await import("./shell-JMU5XTHW.js");
3359
3245
  const cmd = new ShellCommand();
3360
3246
  await cmd.execute({ identifier });
3361
3247
  } catch (error) {
@@ -3366,7 +3252,7 @@ program.command("shell").alias("terminal").description("Open interactive shell w
3366
3252
  program.command("cleanup").alias("remove").alias("clean").description("Remove workspaces").argument("[identifier]", "Branch name or issue number to cleanup (auto-detected)").option("-l, --list", "List all worktrees").option("-a, --all", "Remove all worktrees (interactive confirmation)").option("-i, --issue <number>", "Cleanup by issue number", parseInt).option("-f, --force", "Skip confirmations and force removal").option("--dry-run", "Show what would be done without doing it").option("--json", "Output result as JSON").action(async (identifier, options) => {
3367
3253
  const executeAction = async () => {
3368
3254
  try {
3369
- const { CleanupCommand } = await import("./cleanup-KDLVTT7M.js");
3255
+ const { CleanupCommand } = await import("./cleanup-LU6NU2NZ.js");
3370
3256
  const command = new CleanupCommand();
3371
3257
  const input = {
3372
3258
  options: options ?? {}
@@ -3406,7 +3292,11 @@ program.command("list").description("Show active workspaces").option("--json", "
3406
3292
  metadata.set(worktree.path, loomMetadata);
3407
3293
  }
3408
3294
  if (options.json) {
3409
- const mainWorktreePath = await findMainWorktreePathWithSettings();
3295
+ let mainWorktreePath;
3296
+ try {
3297
+ mainWorktreePath = await findMainWorktreePathWithSettings();
3298
+ } catch {
3299
+ }
3410
3300
  console.log(JSON.stringify(formatLoomsForJson(worktrees, mainWorktreePath, metadata), null, 2));
3411
3301
  return;
3412
3302
  }
@@ -3440,7 +3330,7 @@ program.command("list").description("Show active workspaces").option("--json", "
3440
3330
  });
3441
3331
  program.command("projects").description("List configured iloom projects").option("--json", "Output as JSON (default behavior)").action(async (options) => {
3442
3332
  try {
3443
- const { ProjectsCommand } = await import("./projects-P55273AB.js");
3333
+ const { ProjectsCommand } = await import("./projects-QEAEBAT2.js");
3444
3334
  const command = new ProjectsCommand();
3445
3335
  const result = await command.execute(options);
3446
3336
  console.log(JSON.stringify(result, null, 2));
@@ -3449,9 +3339,9 @@ program.command("projects").description("List configured iloom projects").option
3449
3339
  process.exit(1);
3450
3340
  }
3451
3341
  });
3452
- program.command("init").alias("config").description("Initialize iloom configuration and setup shell autocomplete").argument("[prompt]", 'Custom initial message to send to Claude (defaults to "Help me configure iloom settings.")').action(async (prompt) => {
3342
+ program.command("init").alias("config").description("Initialize iloom configuration").argument("[prompt]", 'Custom initial message to send to Claude (defaults to "Help me configure iloom settings.")').action(async (prompt) => {
3453
3343
  try {
3454
- const { InitCommand: InitCommand2 } = await import("./init-LBA6NUK2.js");
3344
+ const { InitCommand: InitCommand2 } = await import("./init-F6PFMSU5.js");
3455
3345
  const command = new InitCommand2();
3456
3346
  const trimmedPrompt = prompt == null ? void 0 : prompt.trim();
3457
3347
  const customPrompt = trimmedPrompt && trimmedPrompt.length > 0 ? trimmedPrompt : void 0;
@@ -3463,7 +3353,7 @@ program.command("init").alias("config").description("Initialize iloom configurat
3463
3353
  });
3464
3354
  program.command("contribute").description("Set up local development environment for contributing to iloom").action(async () => {
3465
3355
  try {
3466
- const { ContributeCommand } = await import("./contribute-HY372S6F.js");
3356
+ const { ContributeCommand } = await import("./contribute-RS3DO3WP.js");
3467
3357
  const command = new ContributeCommand();
3468
3358
  await command.execute();
3469
3359
  } catch (error) {
@@ -3543,9 +3433,9 @@ program.command("test-github").description("Test GitHub integration (Issue #3)")
3543
3433
  program.command("test-claude").description("Test Claude integration (Issue #10)").option("--detect", "Test Claude CLI detection").option("--version", "Get Claude CLI version").option("--branch <title>", "Test branch name generation with given title").option("--issue <number>", "Issue number for branch generation", "123").option("--launch <prompt>", "Launch Claude with a prompt (headless)").option("--interactive", "Launch Claude interactively (requires --launch)").option("--template <name>", "Test template loading").action(async (options) => {
3544
3434
  try {
3545
3435
  const { detectClaudeCli: detectClaudeCli2, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-ACVXNB6N.js");
3546
- const { PromptTemplateManager } = await import("./PromptTemplateManager-2TDZAUC6.js");
3547
- const { ClaudeService } = await import("./ClaudeService-W3SA7HVG.js");
3548
- const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-DK77227F.js");
3436
+ const { PromptTemplateManager } = await import("./PromptTemplateManager-72FEOGT6.js");
3437
+ const { ClaudeService } = await import("./ClaudeService-CJS32WG2.js");
3438
+ const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-DQFKIMEP.js");
3549
3439
  logger.info("Testing Claude Integration\n");
3550
3440
  if (options.detect) {
3551
3441
  logger.info("Detecting Claude CLI...");
@@ -3693,7 +3583,7 @@ program.command("test-webserver").description("Test if a web server is running o
3693
3583
  });
3694
3584
  program.command("test-git").description("Test Git integration - findMainWorktreePath() function (reads .iloom/settings.json)").action(async () => {
3695
3585
  try {
3696
- const { TestGitCommand } = await import("./test-git-YMAE57UP.js");
3586
+ const { TestGitCommand } = await import("./test-git-6SAIRBUD.js");
3697
3587
  const command = new TestGitCommand();
3698
3588
  await command.execute();
3699
3589
  } catch (error) {
@@ -3719,7 +3609,7 @@ program.command("test-tabs").description("Test iTerm2 dual tab functionality - o
3719
3609
  });
3720
3610
  program.command("test-prefix").description("Test worktree prefix configuration - preview worktree paths (reads .iloom/settings.json)").action(async () => {
3721
3611
  try {
3722
- const { TestPrefixCommand } = await import("./test-prefix-YCKL6CMT.js");
3612
+ const { TestPrefixCommand } = await import("./test-prefix-RLVRK5ZD.js");
3723
3613
  const command = new TestPrefixCommand();
3724
3614
  await command.execute();
3725
3615
  } catch (error) {
@@ -3733,7 +3623,7 @@ program.command("test-prefix").description("Test worktree prefix configuration -
3733
3623
  program.command("summary").description("Generate Claude session summary for a loom").argument("[identifier]", "Issue number, PR number (pr/123), or branch name (auto-detected if omitted)").option("--with-comment", "Post summary as a comment to the issue/PR").option("--json", "Output result as JSON").action(async (identifier, options) => {
3734
3624
  const executeAction = async () => {
3735
3625
  try {
3736
- const { SummaryCommand } = await import("./summary-GDT7DTRI.js");
3626
+ const { SummaryCommand } = await import("./summary-4SSGGH7N.js");
3737
3627
  const command = new SummaryCommand();
3738
3628
  const result = await command.execute({ identifier, options });
3739
3629
  if (options.json && result) {
@@ -3759,10 +3649,39 @@ program.command("summary").description("Generate Claude session summary for a lo
3759
3649
  await executeAction();
3760
3650
  }
3761
3651
  });
3652
+ program.command("recap").description("Get recap for a loom (defaults to current directory)").argument("[identifier]", "Issue number, PR number (pr/123), or branch name (auto-detected if omitted)").option("--json", "Output as JSON with filePath for file watching").action(async (identifier, options) => {
3653
+ const executeAction = async () => {
3654
+ try {
3655
+ const { RecapCommand } = await import("./recap-33NPZ3ZO.js");
3656
+ const command = new RecapCommand();
3657
+ const result = await command.execute({ identifier, json: options.json });
3658
+ if (options.json && result) {
3659
+ console.log(JSON.stringify(result, null, 2));
3660
+ }
3661
+ process.exit(0);
3662
+ } catch (error) {
3663
+ if (options.json) {
3664
+ console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" }, null, 2));
3665
+ } else {
3666
+ logger.error(`Recap failed: ${error instanceof Error ? error.message : "Unknown error"}`);
3667
+ if (error instanceof Error && error.stack) {
3668
+ logger.debug(error.stack);
3669
+ }
3670
+ }
3671
+ process.exit(1);
3672
+ }
3673
+ };
3674
+ if (options.json) {
3675
+ const jsonLogger = createStderrLogger();
3676
+ await withLogger(jsonLogger, executeAction);
3677
+ } else {
3678
+ await executeAction();
3679
+ }
3680
+ });
3762
3681
  program.command("test-neon").description("Test Neon integration and debug configuration").action(async () => {
3763
3682
  var _a;
3764
3683
  try {
3765
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-FJFU6JJD.js");
3684
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-XPR4TEQL.js");
3766
3685
  const { createNeonProviderFromSettings: createNeonProviderFromSettings2 } = await import("./neon-helpers-L5CXQ5CT.js");
3767
3686
  logger.info("Testing Neon Integration\n");
3768
3687
  logger.info("1. Settings Configuration:");
@@ -3861,6 +3780,7 @@ if (isRunDirectly) {
3861
3780
  }
3862
3781
  }
3863
3782
  export {
3864
- validateGhCliForCommand
3783
+ validateGhCliForCommand,
3784
+ validateIdeForStartCommand
3865
3785
  };
3866
3786
  //# sourceMappingURL=cli.js.map