@iloom/cli 0.4.0 → 0.5.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 (135) hide show
  1. package/README.md +24 -0
  2. package/dist/{BranchNamingService-TOM2KAUT.js → BranchNamingService-GCCWB3LK.js} +2 -2
  3. package/dist/ClaudeContextManager-DQFKIMEP.js +16 -0
  4. package/dist/ClaudeService-CJS32WG2.js +15 -0
  5. package/dist/{LoomLauncher-SJBZFZXE.js → LoomLauncher-4UG2E4CD.js} +40 -32
  6. package/dist/LoomLauncher-4UG2E4CD.js.map +1 -0
  7. package/dist/MetadataManager-WXUVXKUS.js +10 -0
  8. package/dist/PRManager-7DSIMCAD.js +16 -0
  9. package/dist/{PromptTemplateManager-2TDZAUC6.js → PromptTemplateManager-72FEOGT6.js} +2 -2
  10. package/dist/README.md +24 -0
  11. package/dist/{SettingsManager-FJFU6JJD.js → SettingsManager-XPR4TEQL.js} +2 -2
  12. package/dist/agents/iloom-issue-analyze-and-plan.md +41 -7
  13. package/dist/agents/iloom-issue-analyzer.md +38 -8
  14. package/dist/agents/iloom-issue-complexity-evaluator.md +45 -15
  15. package/dist/agents/iloom-issue-enhancer.md +60 -18
  16. package/dist/agents/iloom-issue-implementer.md +29 -7
  17. package/dist/agents/iloom-issue-planner.md +36 -7
  18. package/dist/agents/iloom-issue-reviewer.md +30 -7
  19. package/dist/{chunk-M5XUCTTJ.js → chunk-3CMGCRB5.js} +2 -2
  20. package/dist/{chunk-KM3W7YQX.js → chunk-4YTILIIH.js} +8 -9
  21. package/dist/chunk-4YTILIIH.js.map +1 -0
  22. package/dist/{chunk-HVGQP44L.js → chunk-AS2IRKLU.js} +2 -2
  23. package/dist/{chunk-ADDNFQJ4.js → chunk-CDQEK2WD.js} +6 -6
  24. package/dist/{chunk-74VMN2KC.js → chunk-DKQ4SUII.js} +16 -1
  25. package/dist/chunk-DKQ4SUII.js.map +1 -0
  26. package/dist/{chunk-LTNDJMTH.js → chunk-GVRO4PWE.js} +13 -9
  27. package/dist/chunk-GVRO4PWE.js.map +1 -0
  28. package/dist/{chunk-HHDSIE72.js → chunk-HABINPX2.js} +72 -16
  29. package/dist/{chunk-HHDSIE72.js.map → chunk-HABINPX2.js.map} +1 -1
  30. package/dist/{chunk-TR5MC2U6.js → chunk-LN4H3A6A.js} +66 -7
  31. package/dist/chunk-LN4H3A6A.js.map +1 -0
  32. package/dist/{chunk-VWNS6DH5.js → chunk-OOU3DKNT.js} +13 -7
  33. package/dist/chunk-OOU3DKNT.js.map +1 -0
  34. package/dist/chunk-P2ZQ5LKB.js +347 -0
  35. package/dist/chunk-P2ZQ5LKB.js.map +1 -0
  36. package/dist/{chunk-P2WZIDF3.js → chunk-QIUJPPJQ.js} +2 -2
  37. package/dist/{chunk-HD5SUKI2.js → chunk-RFUOIUQF.js} +49 -6
  38. package/dist/{chunk-HD5SUKI2.js.map → chunk-RFUOIUQF.js.map} +1 -1
  39. package/dist/{chunk-OF7BNW4D.js → chunk-RJKMF6BC.js} +30 -4
  40. package/dist/chunk-RJKMF6BC.js.map +1 -0
  41. package/dist/{chunk-O7WHXLCB.js → chunk-RNZMHJK7.js} +18 -4
  42. package/dist/chunk-RNZMHJK7.js.map +1 -0
  43. package/dist/{chunk-75B2HZZ5.js → chunk-RUC7OULH.js} +2 -2
  44. package/dist/{chunk-F4J6KEL6.js → chunk-S65T4O6I.js} +2 -2
  45. package/dist/{chunk-NFVFVYAP.js → chunk-T5IIUG4Z.js} +109 -20
  46. package/dist/chunk-T5IIUG4Z.js.map +1 -0
  47. package/dist/{chunk-JJUPY5MM.js → chunk-VAYGNQTE.js} +2 -2
  48. package/dist/{chunk-S44CHE3G.js → chunk-VTXCGKV5.js} +2 -2
  49. package/dist/{chunk-MLS5FAV7.js → chunk-YZTDGPFB.js} +21 -1
  50. package/dist/chunk-YZTDGPFB.js.map +1 -0
  51. package/dist/{chunk-3NFBZRPR.js → chunk-Z5NXYJIG.js} +19 -1
  52. package/dist/chunk-Z5NXYJIG.js.map +1 -0
  53. package/dist/{claude-X7EBJRB2.js → claude-ACVXNB6N.js} +4 -4
  54. package/dist/{cleanup-7QVPYBJJ.js → cleanup-MIDJVSIU.js} +18 -18
  55. package/dist/cli.js +293 -372
  56. package/dist/cli.js.map +1 -1
  57. package/dist/{contribute-RZYCYUDX.js → contribute-RS3DO3WP.js} +4 -4
  58. package/dist/{dev-server-LOY7YWCP.js → dev-server-ASH7HJVI.js} +28 -14
  59. package/dist/dev-server-ASH7HJVI.js.map +1 -0
  60. package/dist/{feedback-562KPG5U.js → feedback-RVIGHBJG.js} +9 -8
  61. package/dist/{feedback-562KPG5U.js.map → feedback-RVIGHBJG.js.map} +1 -1
  62. package/dist/{git-OXJACVAU.js → git-OQAPUPLP.js} +16 -6
  63. package/dist/git-OQAPUPLP.js.map +1 -0
  64. package/dist/{ignite-VSIPGKKG.js → ignite-XJALWFAT.js} +60 -25
  65. package/dist/ignite-XJALWFAT.js.map +1 -0
  66. package/dist/index.d.ts +60 -7
  67. package/dist/index.js +107 -7
  68. package/dist/index.js.map +1 -1
  69. package/dist/init-F6PFMSU5.js +21 -0
  70. package/dist/init-F6PFMSU5.js.map +1 -0
  71. package/dist/mcp/recap-server.js +264 -0
  72. package/dist/mcp/recap-server.js.map +1 -0
  73. package/dist/{open-CX7HUE26.js → open-KW4NTLXH.js} +15 -16
  74. package/dist/{open-CX7HUE26.js.map → open-KW4NTLXH.js.map} +1 -1
  75. package/dist/{projects-6DTNDVLH.js → projects-QEAEBAT2.js} +2 -2
  76. package/dist/prompts/init-prompt.txt +31 -72
  77. package/dist/prompts/issue-prompt.txt +115 -15
  78. package/dist/prompts/pr-prompt.txt +49 -1
  79. package/dist/prompts/regular-prompt.txt +80 -20
  80. package/dist/{rebase-55URTXZC.js → rebase-WZHHE5LU.js} +9 -9
  81. package/dist/recap-33NPZ3ZO.js +117 -0
  82. package/dist/recap-33NPZ3ZO.js.map +1 -0
  83. package/dist/{run-DP2U2CA2.js → run-HRYQ7TR7.js} +15 -16
  84. package/dist/{run-DP2U2CA2.js.map → run-HRYQ7TR7.js.map} +1 -1
  85. package/dist/schema/settings.schema.json +13 -2
  86. package/dist/shell-JMU5XTHW.js +240 -0
  87. package/dist/shell-JMU5XTHW.js.map +1 -0
  88. package/dist/{summary-J3CJSM7L.js → summary-4SSGGH7N.js} +20 -12
  89. package/dist/summary-4SSGGH7N.js.map +1 -0
  90. package/dist/{test-git-QLAIBJLX.js → test-git-6SAIRBUD.js} +4 -4
  91. package/dist/{test-prefix-6YM2ZOON.js → test-prefix-RLVRK5ZD.js} +4 -4
  92. package/dist/{test-tabs-JGO3VOXJ.js → test-tabs-3SCJWRKT.js} +3 -3
  93. package/package.json +1 -1
  94. package/dist/ClaudeContextManager-VEGJTS5E.js +0 -16
  95. package/dist/ClaudeService-ICSHJMQ5.js +0 -15
  96. package/dist/LoomLauncher-SJBZFZXE.js.map +0 -1
  97. package/dist/chunk-3NFBZRPR.js.map +0 -1
  98. package/dist/chunk-74VMN2KC.js.map +0 -1
  99. package/dist/chunk-KM3W7YQX.js.map +0 -1
  100. package/dist/chunk-LTNDJMTH.js.map +0 -1
  101. package/dist/chunk-MLS5FAV7.js.map +0 -1
  102. package/dist/chunk-NFVFVYAP.js.map +0 -1
  103. package/dist/chunk-O7WHXLCB.js.map +0 -1
  104. package/dist/chunk-OF7BNW4D.js.map +0 -1
  105. package/dist/chunk-QRBOPFAA.js +0 -48
  106. package/dist/chunk-QRBOPFAA.js.map +0 -1
  107. package/dist/chunk-TR5MC2U6.js.map +0 -1
  108. package/dist/chunk-VWNS6DH5.js.map +0 -1
  109. package/dist/dev-server-LOY7YWCP.js.map +0 -1
  110. package/dist/ignite-VSIPGKKG.js.map +0 -1
  111. package/dist/init-SCR2LQ4A.js +0 -21
  112. package/dist/summary-J3CJSM7L.js.map +0 -1
  113. /package/dist/{BranchNamingService-TOM2KAUT.js.map → BranchNamingService-GCCWB3LK.js.map} +0 -0
  114. /package/dist/{ClaudeContextManager-VEGJTS5E.js.map → ClaudeContextManager-DQFKIMEP.js.map} +0 -0
  115. /package/dist/{ClaudeService-ICSHJMQ5.js.map → ClaudeService-CJS32WG2.js.map} +0 -0
  116. /package/dist/{PromptTemplateManager-2TDZAUC6.js.map → MetadataManager-WXUVXKUS.js.map} +0 -0
  117. /package/dist/{SettingsManager-FJFU6JJD.js.map → PRManager-7DSIMCAD.js.map} +0 -0
  118. /package/dist/{claude-X7EBJRB2.js.map → PromptTemplateManager-72FEOGT6.js.map} +0 -0
  119. /package/dist/{git-OXJACVAU.js.map → SettingsManager-XPR4TEQL.js.map} +0 -0
  120. /package/dist/{chunk-M5XUCTTJ.js.map → chunk-3CMGCRB5.js.map} +0 -0
  121. /package/dist/{chunk-HVGQP44L.js.map → chunk-AS2IRKLU.js.map} +0 -0
  122. /package/dist/{chunk-ADDNFQJ4.js.map → chunk-CDQEK2WD.js.map} +0 -0
  123. /package/dist/{chunk-P2WZIDF3.js.map → chunk-QIUJPPJQ.js.map} +0 -0
  124. /package/dist/{chunk-75B2HZZ5.js.map → chunk-RUC7OULH.js.map} +0 -0
  125. /package/dist/{chunk-F4J6KEL6.js.map → chunk-S65T4O6I.js.map} +0 -0
  126. /package/dist/{chunk-JJUPY5MM.js.map → chunk-VAYGNQTE.js.map} +0 -0
  127. /package/dist/{chunk-S44CHE3G.js.map → chunk-VTXCGKV5.js.map} +0 -0
  128. /package/dist/{init-SCR2LQ4A.js.map → claude-ACVXNB6N.js.map} +0 -0
  129. /package/dist/{cleanup-7QVPYBJJ.js.map → cleanup-MIDJVSIU.js.map} +0 -0
  130. /package/dist/{contribute-RZYCYUDX.js.map → contribute-RS3DO3WP.js.map} +0 -0
  131. /package/dist/{projects-6DTNDVLH.js.map → projects-QEAEBAT2.js.map} +0 -0
  132. /package/dist/{rebase-55URTXZC.js.map → rebase-WZHHE5LU.js.map} +0 -0
  133. /package/dist/{test-git-QLAIBJLX.js.map → test-git-6SAIRBUD.js.map} +0 -0
  134. /package/dist/{test-prefix-6YM2ZOON.js.map → test-prefix-RLVRK5ZD.js.map} +0 -0
  135. /package/dist/{test-tabs-JGO3VOXJ.js.map → test-tabs-3SCJWRKT.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,97 +1,102 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SessionSummaryService
4
- } from "./chunk-HHDSIE72.js";
4
+ } from "./chunk-HABINPX2.js";
5
+ import {
6
+ FirstRunManager,
7
+ IssueTrackerFactory,
8
+ generateIssueManagementMcpConfig
9
+ } from "./chunk-RFUOIUQF.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-NFVFVYAP.js";
17
+ } from "./chunk-T5IIUG4Z.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-KM3W7YQX.js";
35
+ } from "./chunk-4YTILIIH.js";
16
36
  import "./chunk-UYWAESOT.js";
17
37
  import {
18
38
  IssueEnhancementService,
19
39
  capitalizeFirstLetter
20
- } from "./chunk-S44CHE3G.js";
21
- import {
22
- MergeManager
23
- } from "./chunk-LTNDJMTH.js";
24
- import {
25
- FirstRunManager,
26
- IssueTrackerFactory,
27
- generateIssueManagementMcpConfig
28
- } from "./chunk-HD5SUKI2.js";
29
- import "./chunk-QHA67Q7A.js";
40
+ } from "./chunk-VTXCGKV5.js";
30
41
  import {
31
42
  AgentManager
32
- } from "./chunk-O7WHXLCB.js";
43
+ } from "./chunk-RNZMHJK7.js";
33
44
  import {
34
- IdentifierParser
35
- } from "./chunk-HVGQP44L.js";
45
+ ProjectCapabilityDetector
46
+ } from "./chunk-EBISESAP.js";
36
47
  import {
37
- ProcessManager
38
- } from "./chunk-VU3QMIP2.js";
48
+ hasScript,
49
+ readPackageJson
50
+ } from "./chunk-2ZPFJQ3B.js";
39
51
  import {
40
- openBrowser
41
- } from "./chunk-YETJNRQM.js";
52
+ MergeManager
53
+ } from "./chunk-GVRO4PWE.js";
42
54
  import {
43
55
  GitWorktreeManager
44
- } from "./chunk-M5XUCTTJ.js";
45
- import {
46
- detectPackageManager,
47
- installDependencies,
48
- runScript
49
- } from "./chunk-VBFDVGAE.js";
56
+ } from "./chunk-3CMGCRB5.js";
50
57
  import {
51
- ClaudeContextManager
52
- } from "./chunk-F4J6KEL6.js";
53
- import "./chunk-ADDNFQJ4.js";
58
+ PRManager
59
+ } from "./chunk-P2ZQ5LKB.js";
54
60
  import {
55
- DefaultBranchNamingService
56
- } from "./chunk-P2WZIDF3.js";
61
+ openBrowser
62
+ } from "./chunk-YETJNRQM.js";
57
63
  import {
58
- ProjectCapabilityDetector
59
- } from "./chunk-EBISESAP.js";
64
+ getConfiguredRepoFromSettings,
65
+ hasMultipleRemotes
66
+ } from "./chunk-PSFVTBM7.js";
60
67
  import {
61
- hasScript,
62
- readPackageJson
63
- } from "./chunk-2ZPFJQ3B.js";
68
+ ClaudeContextManager
69
+ } from "./chunk-S65T4O6I.js";
70
+ import "./chunk-CDQEK2WD.js";
71
+ import "./chunk-DKQ4SUII.js";
64
72
  import {
65
73
  extractSettingsOverrides
66
74
  } from "./chunk-GYCR2LOU.js";
67
75
  import {
68
- createNeonProviderFromSettings
69
- } from "./chunk-UNXRACJ7.js";
70
- import {
71
- getConfiguredRepoFromSettings,
72
- getEffectivePRTargetRemote,
73
- hasMultipleRemotes,
74
- parseGitRemotes
75
- } from "./chunk-PSFVTBM7.js";
76
+ DefaultBranchNamingService
77
+ } from "./chunk-QIUJPPJQ.js";
76
78
  import {
77
79
  executeGitCommand,
78
80
  extractIssueNumber,
79
81
  findMainWorktreePathWithSettings,
82
+ findPlaceholderCommitSha,
83
+ getMergeTargetBranch,
80
84
  getRepoRoot,
81
- pushBranchToRemote
82
- } from "./chunk-TR5MC2U6.js";
83
- import {
84
- MetadataManager
85
- } from "./chunk-MLS5FAV7.js";
85
+ isPlaceholderCommit,
86
+ pushBranchToRemote,
87
+ removePlaceholderCommitFromHead,
88
+ removePlaceholderCommitFromHistory
89
+ } from "./chunk-LN4H3A6A.js";
86
90
  import {
87
91
  SettingsManager
88
- } from "./chunk-VWNS6DH5.js";
92
+ } from "./chunk-OOU3DKNT.js";
93
+ import {
94
+ MetadataManager
95
+ } from "./chunk-YZTDGPFB.js";
89
96
  import {
90
97
  GitHubService
91
98
  } from "./chunk-OEGECBFS.js";
92
- import {
93
- executeGhCommand
94
- } from "./chunk-KO2FOMHL.js";
99
+ import "./chunk-KO2FOMHL.js";
95
100
  import {
96
101
  promptCommitAction,
97
102
  promptConfirmation,
@@ -101,16 +106,15 @@ import "./chunk-WUQQNE63.js";
101
106
  import {
102
107
  detectClaudeCli,
103
108
  launchClaude
104
- } from "./chunk-75B2HZZ5.js";
105
- import "./chunk-JJUPY5MM.js";
109
+ } from "./chunk-RUC7OULH.js";
110
+ import "./chunk-VAYGNQTE.js";
106
111
  import {
107
112
  loadEnvIntoProcess
108
- } from "./chunk-3NFBZRPR.js";
113
+ } from "./chunk-Z5NXYJIG.js";
109
114
  import {
110
115
  getLogger,
111
116
  withLogger
112
117
  } from "./chunk-6UIGZD2N.js";
113
- import "./chunk-74VMN2KC.js";
114
118
  import {
115
119
  createStderrLogger,
116
120
  logger
@@ -1155,6 +1159,12 @@ import { execa } from "execa";
1155
1159
  function isRunningInVSCode() {
1156
1160
  return process.env.TERM_PROGRAM === "vscode";
1157
1161
  }
1162
+ function isRunningInCursor() {
1163
+ return !!process.env.CURSOR_TRACE_ID;
1164
+ }
1165
+ function isRunningInAntigravity() {
1166
+ return !!process.env.ANTIGRAVITY_CLI_ALIAS;
1167
+ }
1158
1168
  async function isVSCodeAvailable() {
1159
1169
  try {
1160
1170
  await execa("command", ["-v", "code"], {
@@ -1167,6 +1177,30 @@ async function isVSCodeAvailable() {
1167
1177
  return false;
1168
1178
  }
1169
1179
  }
1180
+ async function isCursorAvailable() {
1181
+ try {
1182
+ await execa("command", ["-v", "cursor"], {
1183
+ shell: true,
1184
+ timeout: 5e3
1185
+ });
1186
+ return true;
1187
+ } catch (error) {
1188
+ logger.debug("Cursor CLI not available", { error });
1189
+ return false;
1190
+ }
1191
+ }
1192
+ async function isAntigravityAvailable() {
1193
+ try {
1194
+ await execa("command", ["-v", "agy"], {
1195
+ shell: true,
1196
+ timeout: 5e3
1197
+ });
1198
+ return true;
1199
+ } catch (error) {
1200
+ logger.debug("Antigravity CLI not available", { error });
1201
+ return false;
1202
+ }
1203
+ }
1170
1204
 
1171
1205
  // src/types/index.ts
1172
1206
  var UserAbortedCommitError = class extends Error {
@@ -1251,8 +1285,12 @@ var CommitManager = class {
1251
1285
  await executeGitCommand(commitArgs, { cwd: worktreePath });
1252
1286
  } else {
1253
1287
  getLogger().info("Opening editor for commit message review...");
1254
- if (isRunningInVSCode() && await isVSCodeAvailable()) {
1255
- await this.commitWithVSCodeEditor(worktreePath, message, options);
1288
+ if (isRunningInAntigravity() && await isAntigravityAvailable()) {
1289
+ await this.commitWithExternalEditor(worktreePath, message, options, "agy", "Antigravity");
1290
+ } else if (isRunningInCursor() && await isCursorAvailable()) {
1291
+ await this.commitWithExternalEditor(worktreePath, message, options, "cursor", "Cursor");
1292
+ } else if (isRunningInVSCode() && await isVSCodeAvailable()) {
1293
+ await this.commitWithExternalEditor(worktreePath, message, options, "code", "VSCode");
1256
1294
  } else {
1257
1295
  const commitArgs = ["commit", "-e", "-m", message];
1258
1296
  if (options.skipVerify) {
@@ -1279,10 +1317,11 @@ var CommitManager = class {
1279
1317
  }
1280
1318
  }
1281
1319
  /**
1282
- * Commit with VSCode editor - handles file creation, editing, and commit ourselves
1283
- * to ensure the file opens in the current VSCode window (preserves IPC context)
1320
+ * Commit with external editor CLI (VSCode, Cursor, Antigravity, etc.)
1321
+ * Handles file creation, editing, and commit to ensure the file opens
1322
+ * in the current editor window (preserves IPC context)
1284
1323
  */
1285
- async commitWithVSCodeEditor(worktreePath, message, options) {
1324
+ async commitWithExternalEditor(worktreePath, message, options, cliCommand, editorName) {
1286
1325
  const commitMsgPath = join(worktreePath, ".COMMIT_EDITMSG");
1287
1326
  const initialContent = `${message}
1288
1327
 
@@ -1293,8 +1332,8 @@ var CommitManager = class {
1293
1332
  `;
1294
1333
  await writeFile(commitMsgPath, initialContent, "utf-8");
1295
1334
  try {
1296
- getLogger().debug(`Opening commit message in VSCode: ${commitMsgPath}`);
1297
- await execa2("code", ["--wait", commitMsgPath], {
1335
+ getLogger().debug(`Opening commit message in ${editorName}: ${commitMsgPath}`);
1336
+ await execa2(cliCommand, ["--wait", commitMsgPath], {
1298
1337
  cwd: worktreePath,
1299
1338
  stdio: "inherit"
1300
1339
  });
@@ -1628,267 +1667,6 @@ Run '${runCommand}' to see detailed errors.`
1628
1667
  }
1629
1668
  };
1630
1669
 
1631
- // src/lib/PRManager.ts
1632
- var PRManager = class {
1633
- constructor(settings) {
1634
- this.settings = settings;
1635
- }
1636
- /**
1637
- * Check if a PR already exists for the given branch
1638
- * @param branchName - Branch to check
1639
- * @param cwd - Working directory
1640
- * @returns Existing PR info or null if none found
1641
- */
1642
- async checkForExistingPR(branchName, cwd) {
1643
- try {
1644
- const prList = await executeGhCommand(
1645
- ["pr", "list", "--head", branchName, "--state", "open", "--json", "number,url"],
1646
- cwd ? { cwd } : void 0
1647
- );
1648
- if (prList.length > 0) {
1649
- return prList[0] ?? null;
1650
- }
1651
- return null;
1652
- } catch (error) {
1653
- getLogger().debug("Error checking for existing PR", { error });
1654
- return null;
1655
- }
1656
- }
1657
- /**
1658
- * Generate PR body using Claude if available, otherwise use simple template
1659
- * @param issueNumber - Issue number to include in body
1660
- * @param worktreePath - Path to worktree for context
1661
- * @returns PR body markdown
1662
- */
1663
- async generatePRBody(issueNumber, worktreePath) {
1664
- const hasClaudeCli = await detectClaudeCli();
1665
- if (hasClaudeCli) {
1666
- try {
1667
- const prompt = this.buildPRBodyPrompt(issueNumber);
1668
- const body2 = await launchClaude(prompt, {
1669
- headless: true,
1670
- addDir: worktreePath,
1671
- timeout: 3e4
1672
- });
1673
- if (body2 && typeof body2 === "string" && body2.trim()) {
1674
- const sanitized = this.sanitizeClaudeOutput(body2);
1675
- if (sanitized) {
1676
- return sanitized;
1677
- }
1678
- }
1679
- } catch (error) {
1680
- getLogger().debug("Claude PR body generation failed, using template", { error });
1681
- }
1682
- }
1683
- let body = "This PR contains changes from the iloom workflow.\n\n";
1684
- if (issueNumber) {
1685
- body += `Fixes #${issueNumber}`;
1686
- }
1687
- return body;
1688
- }
1689
- /**
1690
- * Build structured XML prompt for PR body generation
1691
- * Uses XML format for clear task definition and output expectations
1692
- */
1693
- buildPRBodyPrompt(issueNumber) {
1694
- const issueContext = issueNumber ? `
1695
- <IssueContext>
1696
- This PR is associated with GitHub issue #${issueNumber}.
1697
- Include "Fixes #${issueNumber}" at the end of the body on its own line.
1698
- </IssueContext>` : "";
1699
- return `<Task>
1700
- You are a software engineer writing a pull request body for this repository.
1701
- Examine the changes in the git repository and generate a concise, professional PR description.
1702
- </Task>
1703
-
1704
- <Requirements>
1705
- <Format>Write 2-3 sentences summarizing what was changed and why.${issueNumber ? `
1706
-
1707
- End with "Fixes #${issueNumber}" on its own line.` : ""}</Format>
1708
- <Tone>Professional and concise</Tone>
1709
- <Focus>Summarize the changes and their purpose</Focus>
1710
- <NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw PR body text.</NoMeta>
1711
- <Examples>
1712
- Good: "Add user authentication with JWT tokens to secure the API endpoints. This includes login and registration endpoints with proper password hashing.
1713
-
1714
- Fixes #42"
1715
- Good: "Fix navigation bug in sidebar menu that caused incorrect highlighting on nested routes."
1716
- Bad: "Here's the PR body:
1717
-
1718
- ---
1719
-
1720
- Add user authentication..."
1721
- Bad: "Based on the changes, I'll write: Fix navigation bug..."
1722
- </Examples>
1723
- ${issueContext}
1724
- </Requirements>
1725
-
1726
- <Output>
1727
- IMPORTANT: Your entire response will be used directly as the GitHub pull request body.
1728
- Do not include any explanatory text, headers, or separators before or after the body.
1729
- Start your response immediately with the PR body text.
1730
- </Output>`;
1731
- }
1732
- /**
1733
- * Sanitize Claude output to remove meta-commentary and clean formatting
1734
- * Handles cases where Claude includes explanatory text despite instructions
1735
- */
1736
- sanitizeClaudeOutput(rawOutput) {
1737
- let cleaned = rawOutput.trim();
1738
- const metaPatterns = [
1739
- /^.*?based on.*?changes.*?:/i,
1740
- /^.*?looking at.*?files.*?:/i,
1741
- /^.*?examining.*?:/i,
1742
- /^.*?analyzing.*?:/i,
1743
- /^.*?i'll.*?generate.*?:/i,
1744
- /^.*?let me.*?:/i,
1745
- /^.*?here.*?is.*?(?:the\s+)?(?:pr|pull request).*?body.*?:/i,
1746
- /^.*?here's.*?(?:the\s+)?(?:pr|pull request).*?body.*?:/i
1747
- ];
1748
- for (const pattern of metaPatterns) {
1749
- cleaned = cleaned.replace(pattern, "").trim();
1750
- }
1751
- cleaned = cleaned.replace(/^[-=]{3,}\s*/m, "").trim();
1752
- if (cleaned.includes(":")) {
1753
- const colonIndex = cleaned.indexOf(":");
1754
- const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase();
1755
- const metaIndicators = [
1756
- "here is the pr body",
1757
- "here is the pull request body",
1758
- "pr body",
1759
- "pull request body",
1760
- "here is",
1761
- "here's",
1762
- "the body should be",
1763
- "i suggest",
1764
- "my suggestion"
1765
- ];
1766
- const isMetaCommentary = metaIndicators.some((indicator) => beforeColon.includes(indicator));
1767
- if (isMetaCommentary) {
1768
- const afterColon = cleaned.substring(colonIndex + 1).trim();
1769
- const afterSeparator = afterColon.replace(/^[-=]{3,}\s*/m, "").trim();
1770
- if (afterSeparator && afterSeparator.length > 10) {
1771
- cleaned = afterSeparator;
1772
- }
1773
- }
1774
- }
1775
- if (cleaned.startsWith('"') && cleaned.endsWith('"') || cleaned.startsWith("'") && cleaned.endsWith("'")) {
1776
- cleaned = cleaned.slice(1, -1).trim();
1777
- }
1778
- return cleaned;
1779
- }
1780
- /**
1781
- * Create a GitHub PR for the branch
1782
- * @param branchName - Branch to create PR from (used as --head)
1783
- * @param title - PR title
1784
- * @param body - PR body
1785
- * @param baseBranch - Base branch to target (usually main/master)
1786
- * @param cwd - Working directory
1787
- * @returns PR URL
1788
- */
1789
- async createPR(branchName, title, body, baseBranch, cwd) {
1790
- try {
1791
- const targetRemote = await getEffectivePRTargetRemote(this.settings, cwd);
1792
- let headValue = branchName;
1793
- if (targetRemote !== "origin") {
1794
- const remotes = await parseGitRemotes(cwd);
1795
- const originRemote = remotes.find((r) => r.name === "origin");
1796
- if (originRemote) {
1797
- headValue = `${originRemote.owner}:${branchName}`;
1798
- getLogger().debug(`Fork workflow detected, using head: ${headValue}`);
1799
- }
1800
- }
1801
- const args = ["pr", "create", "--head", headValue, "--title", title, "--body", body, "--base", baseBranch];
1802
- if (targetRemote !== "origin") {
1803
- const repo = await getConfiguredRepoFromSettings(this.settings, cwd);
1804
- args.push("--repo", repo);
1805
- }
1806
- const result = await executeGhCommand(args, cwd ? { cwd } : void 0);
1807
- const url = typeof result === "string" ? result.trim() : String(result).trim();
1808
- if (!url.includes("github.com") || !url.includes("/pull/")) {
1809
- throw new Error(`Unexpected response from gh pr create: ${url}`);
1810
- }
1811
- return url;
1812
- } catch (error) {
1813
- const errorMessage = error instanceof Error ? error.message : String(error);
1814
- if (errorMessage.includes("Head sha can't be blank") || errorMessage.includes("No commits between")) {
1815
- throw new Error(
1816
- `Failed to create pull request: ${errorMessage}
1817
-
1818
- This error typically occurs when:
1819
- - The branch was not fully pushed to the remote
1820
- - There's a race condition between push and PR creation
1821
- - The branch has no commits ahead of the base branch
1822
-
1823
- Try running: git push -u origin ${branchName}
1824
- Then retry: il finish`
1825
- );
1826
- }
1827
- throw new Error(`Failed to create pull request: ${errorMessage}`);
1828
- }
1829
- }
1830
- /**
1831
- * Open PR URL in browser
1832
- * @param url - PR URL to open
1833
- */
1834
- async openPRInBrowser(url) {
1835
- try {
1836
- await openBrowser(url);
1837
- getLogger().debug("Opened PR in browser", { url });
1838
- } catch (error) {
1839
- getLogger().warn("Failed to open PR in browser", { error });
1840
- }
1841
- }
1842
- /**
1843
- * Complete PR workflow: check for existing, create if needed, optionally open in browser
1844
- * @param branchName - Branch to create PR from
1845
- * @param title - PR title
1846
- * @param issueNumber - Optional issue number for body generation
1847
- * @param baseBranch - Base branch to target
1848
- * @param worktreePath - Path to worktree
1849
- * @param openInBrowser - Whether to open PR in browser
1850
- * @returns PR creation result
1851
- */
1852
- async createOrOpenPR(branchName, title, issueNumber, baseBranch, worktreePath, openInBrowser) {
1853
- const existingPR = await this.checkForExistingPR(branchName, worktreePath);
1854
- if (existingPR) {
1855
- getLogger().info(`Pull request already exists: ${existingPR.url}`);
1856
- if (openInBrowser) {
1857
- await this.openPRInBrowser(existingPR.url);
1858
- }
1859
- return {
1860
- url: existingPR.url,
1861
- number: existingPR.number,
1862
- wasExisting: true
1863
- };
1864
- }
1865
- const body = await this.generatePRBody(issueNumber, worktreePath);
1866
- getLogger().info("Creating pull request...");
1867
- const url = await this.createPR(branchName, title, body, baseBranch, worktreePath);
1868
- const prNumber = this.extractPRNumberFromUrl(url);
1869
- if (openInBrowser) {
1870
- await this.openPRInBrowser(url);
1871
- }
1872
- return {
1873
- url,
1874
- number: prNumber,
1875
- wasExisting: false
1876
- };
1877
- }
1878
- /**
1879
- * Extract PR number from GitHub PR URL
1880
- * @param url - PR URL (e.g., https://github.com/owner/repo/pull/123)
1881
- * @returns PR number
1882
- */
1883
- extractPRNumberFromUrl(url) {
1884
- const match = url.match(/\/pull\/(\d+)/);
1885
- if (match == null ? void 0 : match[1]) {
1886
- return parseInt(match[1], 10);
1887
- }
1888
- throw new Error(`Could not extract PR number from URL: ${url}`);
1889
- }
1890
- };
1891
-
1892
1670
  // src/commands/finish.ts
1893
1671
  import path3 from "path";
1894
1672
  var FinishCommand = class {
@@ -1929,7 +1707,7 @@ var FinishCommand = class {
1929
1707
  const neonProvider = createNeonProviderFromSettings(settings);
1930
1708
  const databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName);
1931
1709
  const cliIsolationManager = new CLIIsolationManager();
1932
- const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-TOM2KAUT.js");
1710
+ const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-GCCWB3LK.js");
1933
1711
  this.loomManager ??= new LoomManager(
1934
1712
  this.gitWorktreeManager,
1935
1713
  this.issueTracker,
@@ -2266,10 +2044,22 @@ var FinishCommand = class {
2266
2044
  }
2267
2045
  /**
2268
2046
  * Execute workflow for issues and branches (merge into main)
2269
- * This is the traditional workflow: validatecommitrebase → merge → cleanup
2047
+ * This is the workflow: rebasevalidatecommit → merge → cleanup
2270
2048
  */
2271
2049
  async executeIssueWorkflow(parsed, options, worktree, result) {
2272
- var _a, _b;
2050
+ var _a, _b, _c;
2051
+ getLogger().info("Rebasing branch on main...");
2052
+ const mergeOptions = {
2053
+ dryRun: options.dryRun ?? false,
2054
+ force: options.force ?? false
2055
+ };
2056
+ await this.mergeManager.rebaseOnMain(worktree.path, mergeOptions);
2057
+ getLogger().success("Branch rebased successfully");
2058
+ result.operations.push({
2059
+ type: "rebase",
2060
+ message: "Branch rebased on main",
2061
+ success: true
2062
+ });
2273
2063
  if (!options.dryRun) {
2274
2064
  getLogger().info("Running pre-merge validations...");
2275
2065
  await this.validationRunner.runValidations(worktree.path, {
@@ -2344,18 +2134,92 @@ var FinishCommand = class {
2344
2134
  await this.executeGitHubPRWorkflow(parsed, options, worktree, settings, result);
2345
2135
  return;
2346
2136
  }
2347
- getLogger().info("Rebasing branch on main...");
2348
- const mergeOptions = {
2349
- dryRun: options.dryRun ?? false,
2350
- force: options.force ?? false
2351
- };
2352
- await this.mergeManager.rebaseOnMain(worktree.path, mergeOptions);
2353
- getLogger().success("Branch rebased successfully");
2354
- result.operations.push({
2355
- type: "rebase",
2356
- message: "Branch rebased on main",
2357
- success: true
2358
- });
2137
+ if (mergeBehavior.mode === "github-draft-pr") {
2138
+ if (!this.issueTracker.supportsPullRequests) {
2139
+ throw new Error(
2140
+ `The 'github-draft-pr' merge mode requires a GitHub-compatible issue tracker. Your provider (${this.issueTracker.providerName}) does not support pull requests.`
2141
+ );
2142
+ }
2143
+ const { MetadataManager: MetadataManager2 } = await import("./MetadataManager-WXUVXKUS.js");
2144
+ const metadataManager = new MetadataManager2();
2145
+ const metadata = await metadataManager.readMetadata(worktree.path);
2146
+ getLogger().debug(`Draft PR mode: worktree=${worktree.path}, draftPrNumber=${(metadata == null ? void 0 : metadata.draftPrNumber) ?? "none"}`);
2147
+ if (!(metadata == null ? void 0 : metadata.draftPrNumber)) {
2148
+ getLogger().warn("No draft PR found in metadata, creating new PR...");
2149
+ await this.executeGitHubPRWorkflow(parsed, options, worktree, settings, result);
2150
+ return;
2151
+ }
2152
+ const isHeadPlaceholder = await isPlaceholderCommit(worktree.path);
2153
+ const placeholderSha = await findPlaceholderCommitSha(worktree.path);
2154
+ getLogger().debug(`Placeholder detection: isHead=${isHeadPlaceholder}, sha=${placeholderSha ?? "none"}`);
2155
+ if (isHeadPlaceholder) {
2156
+ const commitCount = await executeGitCommand(
2157
+ ["rev-list", "--count", "HEAD"],
2158
+ { cwd: worktree.path }
2159
+ );
2160
+ if (parseInt(commitCount.trim(), 10) <= 1) {
2161
+ throw new Error(
2162
+ "Cannot finish draft PR: no changes have been committed.\nPlease make at least one commit before finishing."
2163
+ );
2164
+ }
2165
+ if (!options.dryRun) {
2166
+ getLogger().info("Removing placeholder commit from HEAD...");
2167
+ await removePlaceholderCommitFromHead(worktree.path);
2168
+ } else {
2169
+ getLogger().info("[DRY RUN] Would remove placeholder commit from HEAD");
2170
+ }
2171
+ } else if (placeholderSha) {
2172
+ const commitsAfterPlaceholder = await executeGitCommand(
2173
+ ["rev-list", "--count", `${placeholderSha}..HEAD`],
2174
+ { cwd: worktree.path }
2175
+ );
2176
+ if (parseInt(commitsAfterPlaceholder.trim(), 10) === 0) {
2177
+ throw new Error(
2178
+ "Cannot finish draft PR: no changes have been committed after the placeholder.\nPlease make at least one commit before finishing."
2179
+ );
2180
+ }
2181
+ if (!options.dryRun) {
2182
+ getLogger().info("Removing placeholder commit from history...");
2183
+ await removePlaceholderCommitFromHistory(worktree.path, placeholderSha);
2184
+ } else {
2185
+ getLogger().info("[DRY RUN] Would remove placeholder commit from history");
2186
+ }
2187
+ }
2188
+ const needsForceWithLease = isHeadPlaceholder || placeholderSha;
2189
+ if (!options.dryRun) {
2190
+ getLogger().info("Pushing final commits to remote...");
2191
+ if (needsForceWithLease) {
2192
+ await executeGitCommand(["push", "--force-with-lease", "origin", worktree.branch], { cwd: worktree.path });
2193
+ } else {
2194
+ await pushBranchToRemote(worktree.branch, worktree.path, { dryRun: false });
2195
+ }
2196
+ } else {
2197
+ if (needsForceWithLease) {
2198
+ getLogger().info("[DRY RUN] Would force push final commits to remote (history rewritten)");
2199
+ } else {
2200
+ getLogger().info("[DRY RUN] Would push final commits to remote");
2201
+ }
2202
+ }
2203
+ const prManager = new PRManager(settings);
2204
+ if (!options.dryRun) {
2205
+ await prManager.markPRReady(metadata.draftPrNumber, worktree.path);
2206
+ getLogger().success(`PR #${metadata.draftPrNumber} marked as ready for review`);
2207
+ } else {
2208
+ getLogger().info(`[DRY RUN] Would mark PR #${metadata.draftPrNumber} as ready for review`);
2209
+ }
2210
+ const prUrl = (_c = metadata.prUrls) == null ? void 0 : _c[String(metadata.draftPrNumber)];
2211
+ if (prUrl) {
2212
+ result.prUrl = prUrl;
2213
+ }
2214
+ result.operations.push({
2215
+ type: "pr-ready",
2216
+ message: `PR #${metadata.draftPrNumber} marked as ready for review`,
2217
+ success: true
2218
+ });
2219
+ await this.generateSessionSummaryIfConfigured(parsed, worktree, options, metadata.draftPrNumber);
2220
+ await this.handlePRCleanupPrompt(parsed, options, worktree, result);
2221
+ return;
2222
+ }
2359
2223
  getLogger().info("Performing fast-forward merge...");
2360
2224
  await this.mergeManager.performFastForwardMerge(worktree.branch, worktree.path, mergeOptions);
2361
2225
  getLogger().success("Fast-forward merge completed successfully");
@@ -2483,17 +2347,17 @@ var FinishCommand = class {
2483
2347
  getLogger().debug("Could not fetch issue title, using branch name", { error });
2484
2348
  }
2485
2349
  }
2350
+ const baseBranch = await getMergeTargetBranch(worktree.path);
2486
2351
  if (options.dryRun) {
2487
2352
  getLogger().info("[DRY RUN] Would create GitHub PR");
2488
2353
  getLogger().info(` Title: ${prTitle}`);
2489
- getLogger().info(` Base: ${settings.mainBranch ?? "main"}`);
2354
+ getLogger().info(` Base: ${baseBranch}`);
2490
2355
  finishResult.operations.push({
2491
2356
  type: "pr-creation",
2492
2357
  message: "Would create GitHub PR (dry-run)",
2493
2358
  success: true
2494
2359
  });
2495
2360
  } else {
2496
- const baseBranch = settings.mainBranch ?? "main";
2497
2361
  const openInBrowser = options.noBrowser !== true;
2498
2362
  const prResult = await prManager.createOrOpenPR(
2499
2363
  worktree.branch,
@@ -2519,7 +2383,7 @@ var FinishCommand = class {
2519
2383
  });
2520
2384
  }
2521
2385
  finishResult.prUrl = prResult.url;
2522
- await this.generateSessionSummaryIfConfigured(parsed, worktree, options);
2386
+ await this.generateSessionSummaryIfConfigured(parsed, worktree, options, prResult.number);
2523
2387
  await this.handlePRCleanupPrompt(parsed, options, worktree, finishResult);
2524
2388
  }
2525
2389
  }
@@ -2542,8 +2406,8 @@ var FinishCommand = class {
2542
2406
  getLogger().info("");
2543
2407
  const shouldCleanup = await promptConfirmation(
2544
2408
  "Clean up worktree now?",
2545
- false
2546
- // Default to keeping worktree (safer option)
2409
+ true
2410
+ // Default to keeping worktree - won't delete if unmerged changes
2547
2411
  );
2548
2412
  if (shouldCleanup) {
2549
2413
  await this.performWorktreeCleanup(parsed, options, worktree, finishResult);
@@ -2684,8 +2548,13 @@ var FinishCommand = class {
2684
2548
  * This ensures the finish workflow continues even if summary generation fails
2685
2549
  *
2686
2550
  * In dry-run mode: generates summary and shows preview, but doesn't post
2551
+ *
2552
+ * @param parsed - The parsed input identifying the issue/PR being finished
2553
+ * @param worktree - The worktree being finished
2554
+ * @param options - Finish options (including dryRun flag)
2555
+ * @param prNumber - Optional PR number - when provided, summary is posted to the PR instead of the issue
2687
2556
  */
2688
- async generateSessionSummaryIfConfigured(parsed, worktree, options) {
2557
+ async generateSessionSummaryIfConfigured(parsed, worktree, options, prNumber) {
2689
2558
  if (parsed.type === "branch") {
2690
2559
  return;
2691
2560
  }
@@ -2716,7 +2585,8 @@ var FinishCommand = class {
2716
2585
  worktreePath: worktree.path,
2717
2586
  issueNumber: parsed.number ?? 0,
2718
2587
  branchName: worktree.branch,
2719
- loomType: parsed.type
2588
+ loomType: parsed.type,
2589
+ ...prNumber !== void 0 && { prNumber }
2720
2590
  });
2721
2591
  }
2722
2592
  /**
@@ -2955,7 +2825,10 @@ function formatLoomForJson(worktree, mainWorktreePath, metadata) {
2955
2825
  description: (metadata == null ? void 0 : metadata.description) ?? null,
2956
2826
  created_at: (metadata == null ? void 0 : metadata.created_at) ?? null,
2957
2827
  issueTracker: (metadata == null ? void 0 : metadata.issueTracker) ?? null,
2958
- colorHex: (metadata == null ? void 0 : metadata.colorHex) ?? null
2828
+ colorHex: (metadata == null ? void 0 : metadata.colorHex) ?? null,
2829
+ projectPath: (metadata == null ? void 0 : metadata.projectPath) ?? null,
2830
+ issueUrls: (metadata == null ? void 0 : metadata.issueUrls) ?? {},
2831
+ prUrls: (metadata == null ? void 0 : metadata.prUrls) ?? {}
2959
2832
  };
2960
2833
  }
2961
2834
  function formatLoomsForJson(worktrees, mainWorktreePath, metadata) {
@@ -3003,6 +2876,7 @@ async function validateSettingsForCommand(command) {
3003
2876
  if (bypassCommands.includes(commandName)) {
3004
2877
  return;
3005
2878
  }
2879
+ const warnOnlyCommands = ["list", "projects"];
3006
2880
  try {
3007
2881
  const settingsManager = new SettingsManager();
3008
2882
  const settings = await settingsManager.loadSettings();
@@ -3012,6 +2886,10 @@ async function validateSettingsForCommand(command) {
3012
2886
  return;
3013
2887
  }
3014
2888
  } catch (error) {
2889
+ if (warnOnlyCommands.includes(commandName)) {
2890
+ logger.warn(`Configuration warning: ${error instanceof Error ? error.message : "Unknown error"}`);
2891
+ return;
2892
+ }
3015
2893
  logger.error(`Configuration error: ${error instanceof Error ? error.message : "Unknown error"}`);
3016
2894
  logger.info("Please fix your .iloom/settings.json file and try again.");
3017
2895
  process.exit(1);
@@ -3081,14 +2959,14 @@ async function autoLaunchInitForMultipleRemotes() {
3081
2959
  await waitForKeypress2("Press any key to start configuration...");
3082
2960
  logger.info("");
3083
2961
  try {
3084
- const { InitCommand: InitCommand2 } = await import("./init-SCR2LQ4A.js");
2962
+ const { InitCommand: InitCommand2 } = await import("./init-F6PFMSU5.js");
3085
2963
  const initCommand = new InitCommand2();
3086
2964
  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.";
3087
2965
  await initCommand.execute(customInitialMessage);
3088
2966
  logger.info("");
3089
2967
  logger.info("Configuration complete! Continuing with your original command...");
3090
2968
  logger.info("");
3091
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-FJFU6JJD.js");
2969
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-XPR4TEQL.js");
3092
2970
  const settingsManager = new SettingsManager2();
3093
2971
  const settings = await settingsManager.loadSettings();
3094
2972
  const { hasMultipleRemotes: hasMultipleRemotes2 } = await import("./remote-73TZ2ADI.js");
@@ -3182,7 +3060,7 @@ program.command("add-issue").alias("a").description("Create and enhance GitHub i
3182
3060
  });
3183
3061
  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) => {
3184
3062
  try {
3185
- const { FeedbackCommand } = await import("./feedback-562KPG5U.js");
3063
+ const { FeedbackCommand } = await import("./feedback-RVIGHBJG.js");
3186
3064
  const command = new FeedbackCommand();
3187
3065
  const feedbackOptions = {};
3188
3066
  if (options.body !== void 0) {
@@ -3262,7 +3140,7 @@ program.command("finish").alias("dn").description("Merge work and cleanup worksp
3262
3140
  });
3263
3141
  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) => {
3264
3142
  try {
3265
- const { RebaseCommand } = await import("./rebase-55URTXZC.js");
3143
+ const { RebaseCommand } = await import("./rebase-WZHHE5LU.js");
3266
3144
  const command = new RebaseCommand();
3267
3145
  await command.execute(options);
3268
3146
  } catch (error) {
@@ -3274,7 +3152,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3274
3152
  new Option("--one-shot <mode>", "One-shot automation mode").choices(["default", "noReview", "bypassPermissions"]).default("default")
3275
3153
  ).action(async (options) => {
3276
3154
  try {
3277
- const { IgniteCommand } = await import("./ignite-VSIPGKKG.js");
3155
+ const { IgniteCommand } = await import("./ignite-XJALWFAT.js");
3278
3156
  const command = new IgniteCommand();
3279
3157
  await command.execute(options.oneShot ?? "default");
3280
3158
  } catch (error) {
@@ -3285,7 +3163,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3285
3163
  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) => {
3286
3164
  try {
3287
3165
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3288
- const { OpenCommand } = await import("./open-CX7HUE26.js");
3166
+ const { OpenCommand } = await import("./open-KW4NTLXH.js");
3289
3167
  const cmd = new OpenCommand();
3290
3168
  const input = identifier ? { identifier, args } : { args };
3291
3169
  await cmd.execute(input);
@@ -3297,7 +3175,7 @@ program.command("open").description("Open workspace in browser or run CLI tool")
3297
3175
  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) => {
3298
3176
  try {
3299
3177
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3300
- const { RunCommand } = await import("./run-DP2U2CA2.js");
3178
+ const { RunCommand } = await import("./run-HRYQ7TR7.js");
3301
3179
  const cmd = new RunCommand();
3302
3180
  const input = identifier ? { identifier, args } : { args };
3303
3181
  await cmd.execute(input);
@@ -3308,7 +3186,7 @@ program.command("run").description("Run CLI tool or open workspace in browser").
3308
3186
  });
3309
3187
  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) => {
3310
3188
  try {
3311
- const { DevServerCommand } = await import("./dev-server-LOY7YWCP.js");
3189
+ const { DevServerCommand } = await import("./dev-server-ASH7HJVI.js");
3312
3190
  const cmd = new DevServerCommand();
3313
3191
  await cmd.execute({ identifier, json: options == null ? void 0 : options.json });
3314
3192
  } catch (error) {
@@ -3316,10 +3194,20 @@ program.command("dev-server").alias("dev").description("Start dev server for wor
3316
3194
  process.exit(1);
3317
3195
  }
3318
3196
  });
3197
+ 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) => {
3198
+ try {
3199
+ const { ShellCommand } = await import("./shell-JMU5XTHW.js");
3200
+ const cmd = new ShellCommand();
3201
+ await cmd.execute({ identifier });
3202
+ } catch (error) {
3203
+ logger.error(`Failed to open shell: ${error instanceof Error ? error.message : "Unknown error"}`);
3204
+ process.exit(1);
3205
+ }
3206
+ });
3319
3207
  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) => {
3320
3208
  const executeAction = async () => {
3321
3209
  try {
3322
- const { CleanupCommand } = await import("./cleanup-7QVPYBJJ.js");
3210
+ const { CleanupCommand } = await import("./cleanup-MIDJVSIU.js");
3323
3211
  const command = new CleanupCommand();
3324
3212
  const input = {
3325
3213
  options: options ?? {}
@@ -3359,7 +3247,11 @@ program.command("list").description("Show active workspaces").option("--json", "
3359
3247
  metadata.set(worktree.path, loomMetadata);
3360
3248
  }
3361
3249
  if (options.json) {
3362
- const mainWorktreePath = await findMainWorktreePathWithSettings();
3250
+ let mainWorktreePath;
3251
+ try {
3252
+ mainWorktreePath = await findMainWorktreePathWithSettings();
3253
+ } catch {
3254
+ }
3363
3255
  console.log(JSON.stringify(formatLoomsForJson(worktrees, mainWorktreePath, metadata), null, 2));
3364
3256
  return;
3365
3257
  }
@@ -3393,7 +3285,7 @@ program.command("list").description("Show active workspaces").option("--json", "
3393
3285
  });
3394
3286
  program.command("projects").description("List configured iloom projects").option("--json", "Output as JSON (default behavior)").action(async (options) => {
3395
3287
  try {
3396
- const { ProjectsCommand } = await import("./projects-6DTNDVLH.js");
3288
+ const { ProjectsCommand } = await import("./projects-QEAEBAT2.js");
3397
3289
  const command = new ProjectsCommand();
3398
3290
  const result = await command.execute(options);
3399
3291
  console.log(JSON.stringify(result, null, 2));
@@ -3402,9 +3294,9 @@ program.command("projects").description("List configured iloom projects").option
3402
3294
  process.exit(1);
3403
3295
  }
3404
3296
  });
3405
- 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) => {
3297
+ 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) => {
3406
3298
  try {
3407
- const { InitCommand: InitCommand2 } = await import("./init-SCR2LQ4A.js");
3299
+ const { InitCommand: InitCommand2 } = await import("./init-F6PFMSU5.js");
3408
3300
  const command = new InitCommand2();
3409
3301
  const trimmedPrompt = prompt == null ? void 0 : prompt.trim();
3410
3302
  const customPrompt = trimmedPrompt && trimmedPrompt.length > 0 ? trimmedPrompt : void 0;
@@ -3416,7 +3308,7 @@ program.command("init").alias("config").description("Initialize iloom configurat
3416
3308
  });
3417
3309
  program.command("contribute").description("Set up local development environment for contributing to iloom").action(async () => {
3418
3310
  try {
3419
- const { ContributeCommand } = await import("./contribute-RZYCYUDX.js");
3311
+ const { ContributeCommand } = await import("./contribute-RS3DO3WP.js");
3420
3312
  const command = new ContributeCommand();
3421
3313
  await command.execute();
3422
3314
  } catch (error) {
@@ -3437,7 +3329,7 @@ program.command("update").description("Update iloom-cli to the latest version").
3437
3329
  program.command("test-github").description("Test GitHub integration (Issue #3)").argument("<identifier>", "Issue number or PR number").option("--no-claude", "Skip Claude for branch name generation").action(async (identifier, options) => {
3438
3330
  try {
3439
3331
  const { GitHubService: GitHubService2 } = await import("./GitHubService-RPM27GWD.js");
3440
- const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-TOM2KAUT.js");
3332
+ const { DefaultBranchNamingService: DefaultBranchNamingService2 } = await import("./BranchNamingService-GCCWB3LK.js");
3441
3333
  logger.info("Testing GitHub Integration\n");
3442
3334
  const service = new GitHubService2();
3443
3335
  const branchNaming = new DefaultBranchNamingService2({ useClaude: options.claude !== false });
@@ -3495,10 +3387,10 @@ program.command("test-github").description("Test GitHub integration (Issue #3)")
3495
3387
  });
3496
3388
  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) => {
3497
3389
  try {
3498
- const { detectClaudeCli: detectClaudeCli2, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-X7EBJRB2.js");
3499
- const { PromptTemplateManager } = await import("./PromptTemplateManager-2TDZAUC6.js");
3500
- const { ClaudeService } = await import("./ClaudeService-ICSHJMQ5.js");
3501
- const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-VEGJTS5E.js");
3390
+ const { detectClaudeCli: detectClaudeCli2, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-ACVXNB6N.js");
3391
+ const { PromptTemplateManager } = await import("./PromptTemplateManager-72FEOGT6.js");
3392
+ const { ClaudeService } = await import("./ClaudeService-CJS32WG2.js");
3393
+ const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-DQFKIMEP.js");
3502
3394
  logger.info("Testing Claude Integration\n");
3503
3395
  if (options.detect) {
3504
3396
  logger.info("Detecting Claude CLI...");
@@ -3646,7 +3538,7 @@ program.command("test-webserver").description("Test if a web server is running o
3646
3538
  });
3647
3539
  program.command("test-git").description("Test Git integration - findMainWorktreePath() function (reads .iloom/settings.json)").action(async () => {
3648
3540
  try {
3649
- const { TestGitCommand } = await import("./test-git-QLAIBJLX.js");
3541
+ const { TestGitCommand } = await import("./test-git-6SAIRBUD.js");
3650
3542
  const command = new TestGitCommand();
3651
3543
  await command.execute();
3652
3544
  } catch (error) {
@@ -3659,7 +3551,7 @@ program.command("test-git").description("Test Git integration - findMainWorktree
3659
3551
  });
3660
3552
  program.command("test-tabs").description("Test iTerm2 dual tab functionality - opens two tabs with test commands").action(async () => {
3661
3553
  try {
3662
- const { TestTabsCommand } = await import("./test-tabs-JGO3VOXJ.js");
3554
+ const { TestTabsCommand } = await import("./test-tabs-3SCJWRKT.js");
3663
3555
  const command = new TestTabsCommand();
3664
3556
  await command.execute();
3665
3557
  } catch (error) {
@@ -3672,7 +3564,7 @@ program.command("test-tabs").description("Test iTerm2 dual tab functionality - o
3672
3564
  });
3673
3565
  program.command("test-prefix").description("Test worktree prefix configuration - preview worktree paths (reads .iloom/settings.json)").action(async () => {
3674
3566
  try {
3675
- const { TestPrefixCommand } = await import("./test-prefix-6YM2ZOON.js");
3567
+ const { TestPrefixCommand } = await import("./test-prefix-RLVRK5ZD.js");
3676
3568
  const command = new TestPrefixCommand();
3677
3569
  await command.execute();
3678
3570
  } catch (error) {
@@ -3686,7 +3578,7 @@ program.command("test-prefix").description("Test worktree prefix configuration -
3686
3578
  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) => {
3687
3579
  const executeAction = async () => {
3688
3580
  try {
3689
- const { SummaryCommand } = await import("./summary-J3CJSM7L.js");
3581
+ const { SummaryCommand } = await import("./summary-4SSGGH7N.js");
3690
3582
  const command = new SummaryCommand();
3691
3583
  const result = await command.execute({ identifier, options });
3692
3584
  if (options.json && result) {
@@ -3712,10 +3604,39 @@ program.command("summary").description("Generate Claude session summary for a lo
3712
3604
  await executeAction();
3713
3605
  }
3714
3606
  });
3607
+ 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) => {
3608
+ const executeAction = async () => {
3609
+ try {
3610
+ const { RecapCommand } = await import("./recap-33NPZ3ZO.js");
3611
+ const command = new RecapCommand();
3612
+ const result = await command.execute({ identifier, json: options.json });
3613
+ if (options.json && result) {
3614
+ console.log(JSON.stringify(result, null, 2));
3615
+ }
3616
+ process.exit(0);
3617
+ } catch (error) {
3618
+ if (options.json) {
3619
+ console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" }, null, 2));
3620
+ } else {
3621
+ logger.error(`Recap failed: ${error instanceof Error ? error.message : "Unknown error"}`);
3622
+ if (error instanceof Error && error.stack) {
3623
+ logger.debug(error.stack);
3624
+ }
3625
+ }
3626
+ process.exit(1);
3627
+ }
3628
+ };
3629
+ if (options.json) {
3630
+ const jsonLogger = createStderrLogger();
3631
+ await withLogger(jsonLogger, executeAction);
3632
+ } else {
3633
+ await executeAction();
3634
+ }
3635
+ });
3715
3636
  program.command("test-neon").description("Test Neon integration and debug configuration").action(async () => {
3716
3637
  var _a;
3717
3638
  try {
3718
- const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-FJFU6JJD.js");
3639
+ const { SettingsManager: SettingsManager2 } = await import("./SettingsManager-XPR4TEQL.js");
3719
3640
  const { createNeonProviderFromSettings: createNeonProviderFromSettings2 } = await import("./neon-helpers-L5CXQ5CT.js");
3720
3641
  logger.info("Testing Neon Integration\n");
3721
3642
  logger.info("1. Settings Configuration:");