@iloom/cli 0.6.1 → 0.7.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 (178) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +35 -18
  3. package/dist/{BranchNamingService-B5PVRR7F.js → BranchNamingService-FLPUUFOB.js} +2 -2
  4. package/dist/ClaudeContextManager-KE5TBZVZ.js +14 -0
  5. package/dist/ClaudeService-CRSETT3A.js +13 -0
  6. package/dist/{GitHubService-S2OGUTDR.js → GitHubService-O7U4UQ7N.js} +3 -3
  7. package/dist/{LoomLauncher-5LFM4LXB.js → LoomLauncher-NL65LSKP.js} +6 -6
  8. package/dist/{MetadataManager-DFI73J3G.js → MetadataManager-XJ2YB762.js} +2 -2
  9. package/dist/PRManager-2ABCWXHW.js +16 -0
  10. package/dist/{ProjectCapabilityDetector-S5FLNCFI.js → ProjectCapabilityDetector-IA56AUE6.js} +3 -3
  11. package/dist/{PromptTemplateManager-C3DK6XZL.js → PromptTemplateManager-7L3HJQQU.js} +2 -2
  12. package/dist/README.md +35 -18
  13. package/dist/{SettingsManager-35F5RUJH.js → SettingsManager-YU4VYPTW.js} +2 -2
  14. package/dist/agents/iloom-framework-detector.md +78 -9
  15. package/dist/agents/iloom-issue-analyze-and-plan.md +42 -17
  16. package/dist/agents/iloom-issue-analyzer.md +14 -14
  17. package/dist/agents/iloom-issue-complexity-evaluator.md +38 -15
  18. package/dist/agents/iloom-issue-enhancer.md +15 -15
  19. package/dist/agents/iloom-issue-implementer.md +44 -15
  20. package/dist/agents/iloom-issue-planner.md +121 -17
  21. package/dist/agents/iloom-issue-reviewer.md +15 -15
  22. package/dist/{build-FJVYP7EV.js → build-HQ5HGA3T.js} +9 -9
  23. package/dist/{chunk-VU3QMIP2.js → chunk-453NC377.js} +91 -15
  24. package/dist/chunk-453NC377.js.map +1 -0
  25. package/dist/{chunk-UQIXZ3BA.js → chunk-5V74K5ZA.js} +2 -2
  26. package/dist/{chunk-7WANFUIK.js → chunk-6TL3BYH6.js} +2 -2
  27. package/dist/{chunk-ZPSTA5PR.js → chunk-7GLZVDPQ.js} +2 -2
  28. package/dist/{chunk-64O2UIWO.js → chunk-AFRICMSW.js} +4 -4
  29. package/dist/{chunk-5TXLVEXT.js → chunk-C3AKFAIR.js} +2 -2
  30. package/dist/{chunk-K7SEEHKO.js → chunk-CNSTXBJ3.js} +7 -419
  31. package/dist/chunk-CNSTXBJ3.js.map +1 -0
  32. package/dist/{chunk-2A7WQKBE.js → chunk-DAOS6EC3.js} +96 -6
  33. package/dist/chunk-DAOS6EC3.js.map +1 -0
  34. package/dist/{chunk-TRQ76ISK.js → chunk-ELJKYFSH.js} +9 -9
  35. package/dist/{chunk-VDA5JMB4.js → chunk-EPPPDVHD.js} +21 -8
  36. package/dist/chunk-EPPPDVHD.js.map +1 -0
  37. package/dist/{chunk-LVBRMTE6.js → chunk-FEAJR6PN.js} +6 -6
  38. package/dist/{chunk-6YSFTPKW.js → chunk-FM4KBPVA.js} +18 -13
  39. package/dist/chunk-FM4KBPVA.js.map +1 -0
  40. package/dist/{chunk-AEIMYF4P.js → chunk-FP7G7DG3.js} +6 -2
  41. package/dist/chunk-FP7G7DG3.js.map +1 -0
  42. package/dist/{chunk-LT3SGBR7.js → chunk-GCPAZSGV.js} +36 -2
  43. package/dist/{chunk-LT3SGBR7.js.map → chunk-GCPAZSGV.js.map} +1 -1
  44. package/dist/chunk-GJMEKEI5.js +517 -0
  45. package/dist/chunk-GJMEKEI5.js.map +1 -0
  46. package/dist/{chunk-7Q66W4OH.js → chunk-HBJITKSZ.js} +37 -1
  47. package/dist/chunk-HBJITKSZ.js.map +1 -0
  48. package/dist/{chunk-7HIRPCKU.js → chunk-HVQNVRAF.js} +2 -2
  49. package/dist/{chunk-6U6VI4SZ.js → chunk-KVS4XGBQ.js} +4 -4
  50. package/dist/{chunk-SN3Z6EZO.js → chunk-N7FVXZNI.js} +2 -2
  51. package/dist/{chunk-I75JMBNB.js → chunk-QQFBMCAH.js} +54 -43
  52. package/dist/chunk-QQFBMCAH.js.map +1 -0
  53. package/dist/{chunk-AXX3QIKK.js → chunk-RD7I2Q2F.js} +2 -2
  54. package/dist/chunk-TIYJEEVO.js +79 -0
  55. package/dist/chunk-TIYJEEVO.js.map +1 -0
  56. package/dist/{chunk-EK3XCAAS.js → chunk-UDRZY65Y.js} +2 -2
  57. package/dist/{chunk-3PT7RKL5.js → chunk-USJSNHGG.js} +2 -2
  58. package/dist/{chunk-CFUWQHCJ.js → chunk-VWGKGNJP.js} +114 -35
  59. package/dist/chunk-VWGKGNJP.js.map +1 -0
  60. package/dist/{chunk-F6WVM437.js → chunk-WFQ5CLTR.js} +6 -3
  61. package/dist/chunk-WFQ5CLTR.js.map +1 -0
  62. package/dist/{chunk-BXCPJJYM.js → chunk-XPKN3QWY.js} +24 -6
  63. package/dist/chunk-XPKN3QWY.js.map +1 -0
  64. package/dist/chunk-YQNSZKKT.js +822 -0
  65. package/dist/chunk-YQNSZKKT.js.map +1 -0
  66. package/dist/{chunk-GEXP5IOF.js → chunk-ZA575VLF.js} +21 -8
  67. package/dist/chunk-ZA575VLF.js.map +1 -0
  68. package/dist/{claude-H33OQMXO.js → claude-6H36IBHO.js} +4 -2
  69. package/dist/{cleanup-BRUAINKE.js → cleanup-77U5ATYI.js} +20 -16
  70. package/dist/cleanup-77U5ATYI.js.map +1 -0
  71. package/dist/cli.js +361 -954
  72. package/dist/cli.js.map +1 -1
  73. package/dist/commit-ONRXU67O.js +237 -0
  74. package/dist/commit-ONRXU67O.js.map +1 -0
  75. package/dist/{compile-ULNO5F7Q.js → compile-CT7IR7O2.js} +9 -9
  76. package/dist/{contribute-Q6GX6AXK.js → contribute-GXKOIA42.js} +5 -5
  77. package/dist/{dev-server-4RCDJ5MU.js → dev-server-UKAPBGUR.js} +22 -74
  78. package/dist/dev-server-UKAPBGUR.js.map +1 -0
  79. package/dist/{feedback-O4Q55SVS.js → feedback-K3A4QUSG.js} +10 -10
  80. package/dist/{git-FVMGBHC2.js → git-ENLT2VNI.js} +6 -4
  81. package/dist/hooks/iloom-hook.js +30 -2
  82. package/dist/{ignite-VHV65WEZ.js → ignite-YUAOJ5PP.js} +20 -20
  83. package/dist/ignite-YUAOJ5PP.js.map +1 -0
  84. package/dist/index.d.ts +71 -27
  85. package/dist/index.js +196 -266
  86. package/dist/index.js.map +1 -1
  87. package/dist/init-XQQMFDM6.js +21 -0
  88. package/dist/{lint-5JMCWE4Y.js → lint-HAVU4U34.js} +9 -9
  89. package/dist/mcp/issue-management-server.js +359 -13
  90. package/dist/mcp/issue-management-server.js.map +1 -1
  91. package/dist/mcp/recap-server.js +13 -4
  92. package/dist/mcp/recap-server.js.map +1 -1
  93. package/dist/{open-WHVUYGPY.js → open-QI63XQ4F.js} +25 -76
  94. package/dist/open-QI63XQ4F.js.map +1 -0
  95. package/dist/{projects-SA76I4TZ.js → projects-TWY4RT2Z.js} +11 -4
  96. package/dist/projects-TWY4RT2Z.js.map +1 -0
  97. package/dist/prompts/init-prompt.txt +119 -51
  98. package/dist/prompts/issue-prompt.txt +132 -63
  99. package/dist/prompts/pr-prompt.txt +3 -3
  100. package/dist/prompts/regular-prompt.txt +16 -18
  101. package/dist/prompts/session-summary-prompt.txt +13 -13
  102. package/dist/{rebase-Y4AS6LQW.js → rebase-QYCRF7JG.js} +53 -8
  103. package/dist/rebase-QYCRF7JG.js.map +1 -0
  104. package/dist/{recap-VOOUXOGP.js → recap-ZKGHZCX6.js} +6 -6
  105. package/dist/{run-NCRK5NPR.js → run-YDVYORT2.js} +25 -76
  106. package/dist/run-YDVYORT2.js.map +1 -0
  107. package/dist/schema/settings.schema.json +14 -3
  108. package/dist/{shell-SBLXVOVJ.js → shell-2NNSIU34.js} +6 -6
  109. package/dist/{summary-CVFAMDOJ.js → summary-G6L3VAKK.js} +11 -10
  110. package/dist/{summary-CVFAMDOJ.js.map → summary-G6L3VAKK.js.map} +1 -1
  111. package/dist/{test-3KIVXI6J.js → test-75WAA6DU.js} +9 -9
  112. package/dist/{test-git-ZB6AGGRW.js → test-git-E2BLXR6M.js} +4 -4
  113. package/dist/{test-prefix-FBGXKMPA.js → test-prefix-A7JGGYAA.js} +4 -4
  114. package/dist/{test-webserver-YVQD42W6.js → test-webserver-NRMGT2HB.js} +29 -8
  115. package/dist/test-webserver-NRMGT2HB.js.map +1 -0
  116. package/package.json +3 -1
  117. package/dist/ClaudeContextManager-6J2EB4QU.js +0 -14
  118. package/dist/ClaudeService-O2PB22GX.js +0 -13
  119. package/dist/PRManager-GB3FOJ2W.js +0 -14
  120. package/dist/chunk-2A7WQKBE.js.map +0 -1
  121. package/dist/chunk-6YSFTPKW.js.map +0 -1
  122. package/dist/chunk-7Q66W4OH.js.map +0 -1
  123. package/dist/chunk-AEIMYF4P.js.map +0 -1
  124. package/dist/chunk-BXCPJJYM.js.map +0 -1
  125. package/dist/chunk-CFUWQHCJ.js.map +0 -1
  126. package/dist/chunk-F6WVM437.js.map +0 -1
  127. package/dist/chunk-GEXP5IOF.js.map +0 -1
  128. package/dist/chunk-I75JMBNB.js.map +0 -1
  129. package/dist/chunk-K7SEEHKO.js.map +0 -1
  130. package/dist/chunk-VDA5JMB4.js.map +0 -1
  131. package/dist/chunk-VU3QMIP2.js.map +0 -1
  132. package/dist/chunk-W6WVRHJ6.js +0 -251
  133. package/dist/chunk-W6WVRHJ6.js.map +0 -1
  134. package/dist/cleanup-BRUAINKE.js.map +0 -1
  135. package/dist/dev-server-4RCDJ5MU.js.map +0 -1
  136. package/dist/ignite-VHV65WEZ.js.map +0 -1
  137. package/dist/init-UTYRHNJJ.js +0 -21
  138. package/dist/open-WHVUYGPY.js.map +0 -1
  139. package/dist/projects-SA76I4TZ.js.map +0 -1
  140. package/dist/rebase-Y4AS6LQW.js.map +0 -1
  141. package/dist/run-NCRK5NPR.js.map +0 -1
  142. package/dist/test-webserver-YVQD42W6.js.map +0 -1
  143. /package/dist/{BranchNamingService-B5PVRR7F.js.map → BranchNamingService-FLPUUFOB.js.map} +0 -0
  144. /package/dist/{ClaudeContextManager-6J2EB4QU.js.map → ClaudeContextManager-KE5TBZVZ.js.map} +0 -0
  145. /package/dist/{ClaudeService-O2PB22GX.js.map → ClaudeService-CRSETT3A.js.map} +0 -0
  146. /package/dist/{GitHubService-S2OGUTDR.js.map → GitHubService-O7U4UQ7N.js.map} +0 -0
  147. /package/dist/{LoomLauncher-5LFM4LXB.js.map → LoomLauncher-NL65LSKP.js.map} +0 -0
  148. /package/dist/{MetadataManager-DFI73J3G.js.map → MetadataManager-XJ2YB762.js.map} +0 -0
  149. /package/dist/{PRManager-GB3FOJ2W.js.map → PRManager-2ABCWXHW.js.map} +0 -0
  150. /package/dist/{ProjectCapabilityDetector-S5FLNCFI.js.map → ProjectCapabilityDetector-IA56AUE6.js.map} +0 -0
  151. /package/dist/{PromptTemplateManager-C3DK6XZL.js.map → PromptTemplateManager-7L3HJQQU.js.map} +0 -0
  152. /package/dist/{SettingsManager-35F5RUJH.js.map → SettingsManager-YU4VYPTW.js.map} +0 -0
  153. /package/dist/{build-FJVYP7EV.js.map → build-HQ5HGA3T.js.map} +0 -0
  154. /package/dist/{chunk-UQIXZ3BA.js.map → chunk-5V74K5ZA.js.map} +0 -0
  155. /package/dist/{chunk-7WANFUIK.js.map → chunk-6TL3BYH6.js.map} +0 -0
  156. /package/dist/{chunk-ZPSTA5PR.js.map → chunk-7GLZVDPQ.js.map} +0 -0
  157. /package/dist/{chunk-64O2UIWO.js.map → chunk-AFRICMSW.js.map} +0 -0
  158. /package/dist/{chunk-5TXLVEXT.js.map → chunk-C3AKFAIR.js.map} +0 -0
  159. /package/dist/{chunk-TRQ76ISK.js.map → chunk-ELJKYFSH.js.map} +0 -0
  160. /package/dist/{chunk-LVBRMTE6.js.map → chunk-FEAJR6PN.js.map} +0 -0
  161. /package/dist/{chunk-7HIRPCKU.js.map → chunk-HVQNVRAF.js.map} +0 -0
  162. /package/dist/{chunk-6U6VI4SZ.js.map → chunk-KVS4XGBQ.js.map} +0 -0
  163. /package/dist/{chunk-SN3Z6EZO.js.map → chunk-N7FVXZNI.js.map} +0 -0
  164. /package/dist/{chunk-AXX3QIKK.js.map → chunk-RD7I2Q2F.js.map} +0 -0
  165. /package/dist/{chunk-EK3XCAAS.js.map → chunk-UDRZY65Y.js.map} +0 -0
  166. /package/dist/{chunk-3PT7RKL5.js.map → chunk-USJSNHGG.js.map} +0 -0
  167. /package/dist/{claude-H33OQMXO.js.map → claude-6H36IBHO.js.map} +0 -0
  168. /package/dist/{compile-ULNO5F7Q.js.map → compile-CT7IR7O2.js.map} +0 -0
  169. /package/dist/{contribute-Q6GX6AXK.js.map → contribute-GXKOIA42.js.map} +0 -0
  170. /package/dist/{feedback-O4Q55SVS.js.map → feedback-K3A4QUSG.js.map} +0 -0
  171. /package/dist/{git-FVMGBHC2.js.map → git-ENLT2VNI.js.map} +0 -0
  172. /package/dist/{init-UTYRHNJJ.js.map → init-XQQMFDM6.js.map} +0 -0
  173. /package/dist/{lint-5JMCWE4Y.js.map → lint-HAVU4U34.js.map} +0 -0
  174. /package/dist/{recap-VOOUXOGP.js.map → recap-ZKGHZCX6.js.map} +0 -0
  175. /package/dist/{shell-SBLXVOVJ.js.map → shell-2NNSIU34.js.map} +0 -0
  176. /package/dist/{test-3KIVXI6J.js.map → test-75WAA6DU.js.map} +0 -0
  177. /package/dist/{test-git-ZB6AGGRW.js.map → test-git-E2BLXR6M.js.map} +0 -0
  178. /package/dist/{test-prefix-FBGXKMPA.js.map → test-prefix-A7JGGYAA.js.map} +0 -0
@@ -0,0 +1,822 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ detectPackageManager,
4
+ runScript
5
+ } from "./chunk-RD7I2Q2F.js";
6
+ import {
7
+ getPackageConfig,
8
+ hasScript
9
+ } from "./chunk-XPKN3QWY.js";
10
+ import {
11
+ executeGitCommand
12
+ } from "./chunk-ZA575VLF.js";
13
+ import {
14
+ promptCommitAction
15
+ } from "./chunk-ZX3GTM7O.js";
16
+ import {
17
+ detectClaudeCli,
18
+ launchClaude
19
+ } from "./chunk-FP7G7DG3.js";
20
+ import {
21
+ getLogger
22
+ } from "./chunk-6MLEBAYZ.js";
23
+ import {
24
+ logger
25
+ } from "./chunk-VT4PDUYT.js";
26
+
27
+ // src/lib/ValidationRunner.ts
28
+ var ValidationRunner = class {
29
+ constructor() {
30
+ }
31
+ /**
32
+ * Run all validations in sequence: typecheck → lint → test
33
+ * Fails fast on first error
34
+ */
35
+ async runValidations(worktreePath, options = {}) {
36
+ const startTime = Date.now();
37
+ const steps = [];
38
+ if (!options.skipTypecheck) {
39
+ const typecheckResult = await this.runTypecheck(
40
+ worktreePath,
41
+ options.dryRun ?? false
42
+ );
43
+ steps.push(typecheckResult);
44
+ if (!typecheckResult.passed && !typecheckResult.skipped) {
45
+ return {
46
+ success: false,
47
+ steps,
48
+ totalDuration: Date.now() - startTime
49
+ };
50
+ }
51
+ }
52
+ if (!options.skipLint) {
53
+ const lintResult = await this.runLint(worktreePath, options.dryRun ?? false);
54
+ steps.push(lintResult);
55
+ if (!lintResult.passed && !lintResult.skipped) {
56
+ return { success: false, steps, totalDuration: Date.now() - startTime };
57
+ }
58
+ }
59
+ if (!options.skipTests) {
60
+ const testResult = await this.runTests(
61
+ worktreePath,
62
+ options.dryRun ?? false
63
+ );
64
+ steps.push(testResult);
65
+ if (!testResult.passed && !testResult.skipped) {
66
+ return { success: false, steps, totalDuration: Date.now() - startTime };
67
+ }
68
+ }
69
+ return { success: true, steps, totalDuration: Date.now() - startTime };
70
+ }
71
+ /**
72
+ * Run typecheck validation
73
+ * Prefers 'compile' script over 'typecheck' if both exist
74
+ */
75
+ async runTypecheck(worktreePath, dryRun) {
76
+ const stepStartTime = Date.now();
77
+ let scriptToRun = null;
78
+ try {
79
+ const pkgJson = await getPackageConfig(worktreePath);
80
+ const hasCompileScript = hasScript(pkgJson, "compile");
81
+ const hasTypecheckScript = hasScript(pkgJson, "typecheck");
82
+ if (hasCompileScript) {
83
+ scriptToRun = "compile";
84
+ } else if (hasTypecheckScript) {
85
+ scriptToRun = "typecheck";
86
+ }
87
+ if (!scriptToRun) {
88
+ getLogger().debug("Skipping typecheck - no compile or typecheck script found");
89
+ return {
90
+ step: "typecheck",
91
+ passed: true,
92
+ skipped: true,
93
+ duration: Date.now() - stepStartTime
94
+ };
95
+ }
96
+ } catch (error) {
97
+ if (error instanceof Error && error.message.includes("package.json not found")) {
98
+ getLogger().debug("Skipping typecheck - no package.json found (non-Node.js project)");
99
+ return {
100
+ step: "typecheck",
101
+ passed: true,
102
+ skipped: true,
103
+ duration: Date.now() - stepStartTime
104
+ };
105
+ }
106
+ throw error;
107
+ }
108
+ const packageManager = await detectPackageManager(worktreePath);
109
+ if (dryRun) {
110
+ const command = packageManager === "npm" ? `npm run ${scriptToRun}` : `${packageManager} ${scriptToRun}`;
111
+ getLogger().info(`[DRY RUN] Would run: ${command}`);
112
+ return {
113
+ step: scriptToRun,
114
+ passed: true,
115
+ skipped: false,
116
+ duration: Date.now() - stepStartTime
117
+ };
118
+ }
119
+ getLogger().info(`Running ${scriptToRun}...`);
120
+ try {
121
+ await runScript(scriptToRun, worktreePath, [], { quiet: true });
122
+ getLogger().success(`${scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1)} passed`);
123
+ return {
124
+ step: scriptToRun,
125
+ passed: true,
126
+ skipped: false,
127
+ duration: Date.now() - stepStartTime
128
+ };
129
+ } catch {
130
+ const fixed = await this.attemptClaudeFix(
131
+ scriptToRun,
132
+ worktreePath,
133
+ packageManager
134
+ );
135
+ if (fixed) {
136
+ return {
137
+ step: scriptToRun,
138
+ passed: true,
139
+ skipped: false,
140
+ duration: Date.now() - stepStartTime
141
+ };
142
+ }
143
+ const runCommand = packageManager === "npm" ? `npm run ${scriptToRun}` : `${packageManager} ${scriptToRun}`;
144
+ const stepLabel = scriptToRun.charAt(0).toUpperCase() + scriptToRun.slice(1);
145
+ throw new Error(
146
+ `Error: ${stepLabel} failed.
147
+ Fix type errors before merging.
148
+
149
+ Run '${runCommand}' to see detailed errors.`
150
+ );
151
+ }
152
+ }
153
+ /**
154
+ * Run lint validation
155
+ */
156
+ async runLint(worktreePath, dryRun) {
157
+ const stepStartTime = Date.now();
158
+ try {
159
+ const pkgJson = await getPackageConfig(worktreePath);
160
+ const hasLintScript = hasScript(pkgJson, "lint");
161
+ if (!hasLintScript) {
162
+ getLogger().debug("Skipping lint - no lint script found");
163
+ return {
164
+ step: "lint",
165
+ passed: true,
166
+ skipped: true,
167
+ duration: Date.now() - stepStartTime
168
+ };
169
+ }
170
+ } catch (error) {
171
+ if (error instanceof Error && error.message.includes("package.json not found")) {
172
+ getLogger().debug("Skipping lint - no package.json found (non-Node.js project)");
173
+ return {
174
+ step: "lint",
175
+ passed: true,
176
+ skipped: true,
177
+ duration: Date.now() - stepStartTime
178
+ };
179
+ }
180
+ throw error;
181
+ }
182
+ const packageManager = await detectPackageManager(worktreePath);
183
+ if (dryRun) {
184
+ const command = packageManager === "npm" ? "npm run lint" : `${packageManager} lint`;
185
+ getLogger().info(`[DRY RUN] Would run: ${command}`);
186
+ return {
187
+ step: "lint",
188
+ passed: true,
189
+ skipped: false,
190
+ duration: Date.now() - stepStartTime
191
+ };
192
+ }
193
+ getLogger().info("Running lint...");
194
+ try {
195
+ await runScript("lint", worktreePath, [], { quiet: true });
196
+ getLogger().success("Linting passed");
197
+ return {
198
+ step: "lint",
199
+ passed: true,
200
+ skipped: false,
201
+ duration: Date.now() - stepStartTime
202
+ };
203
+ } catch {
204
+ const fixed = await this.attemptClaudeFix(
205
+ "lint",
206
+ worktreePath,
207
+ packageManager
208
+ );
209
+ if (fixed) {
210
+ return {
211
+ step: "lint",
212
+ passed: true,
213
+ skipped: false,
214
+ duration: Date.now() - stepStartTime
215
+ };
216
+ }
217
+ const runCommand = packageManager === "npm" ? "npm run lint" : `${packageManager} lint`;
218
+ throw new Error(
219
+ `Error: Linting failed.
220
+ Fix linting errors before merging.
221
+
222
+ Run '${runCommand}' to see detailed errors.`
223
+ );
224
+ }
225
+ }
226
+ /**
227
+ * Run test validation
228
+ */
229
+ async runTests(worktreePath, dryRun) {
230
+ const stepStartTime = Date.now();
231
+ try {
232
+ const pkgJson = await getPackageConfig(worktreePath);
233
+ const hasTestScript = hasScript(pkgJson, "test");
234
+ if (!hasTestScript) {
235
+ getLogger().debug("Skipping tests - no test script found");
236
+ return {
237
+ step: "test",
238
+ passed: true,
239
+ skipped: true,
240
+ duration: Date.now() - stepStartTime
241
+ };
242
+ }
243
+ } catch (error) {
244
+ if (error instanceof Error && error.message.includes("package.json not found")) {
245
+ getLogger().debug("Skipping tests - no package.json found (non-Node.js project)");
246
+ return {
247
+ step: "test",
248
+ passed: true,
249
+ skipped: true,
250
+ duration: Date.now() - stepStartTime
251
+ };
252
+ }
253
+ throw error;
254
+ }
255
+ const packageManager = await detectPackageManager(worktreePath);
256
+ if (dryRun) {
257
+ const command = packageManager === "npm" ? "npm run test" : `${packageManager} test`;
258
+ getLogger().info(`[DRY RUN] Would run: ${command}`);
259
+ return {
260
+ step: "test",
261
+ passed: true,
262
+ skipped: false,
263
+ duration: Date.now() - stepStartTime
264
+ };
265
+ }
266
+ getLogger().info("Running tests...");
267
+ try {
268
+ await runScript("test", worktreePath, [], { quiet: true });
269
+ getLogger().success("Tests passed");
270
+ return {
271
+ step: "test",
272
+ passed: true,
273
+ skipped: false,
274
+ duration: Date.now() - stepStartTime
275
+ };
276
+ } catch {
277
+ const fixed = await this.attemptClaudeFix(
278
+ "test",
279
+ worktreePath,
280
+ packageManager
281
+ );
282
+ if (fixed) {
283
+ return {
284
+ step: "test",
285
+ passed: true,
286
+ skipped: false,
287
+ duration: Date.now() - stepStartTime
288
+ };
289
+ }
290
+ const runCommand = packageManager === "npm" ? "npm run test" : `${packageManager} test`;
291
+ throw new Error(
292
+ `Error: Tests failed.
293
+ Fix test failures before merging.
294
+
295
+ Run '${runCommand}' to see detailed errors.`
296
+ );
297
+ }
298
+ }
299
+ /**
300
+ * Attempt to fix validation errors using Claude
301
+ * Pattern based on MergeManager.attemptClaudeConflictResolution
302
+ *
303
+ * @param validationType - Type of validation that failed ('compile' | 'typecheck' | 'lint' | 'test')
304
+ * @param worktreePath - Path to the worktree
305
+ * @param packageManager - Detected package manager
306
+ * @returns true if Claude fixed the issue, false otherwise
307
+ */
308
+ async attemptClaudeFix(validationType, worktreePath, packageManager) {
309
+ const isClaudeAvailable = await detectClaudeCli();
310
+ if (!isClaudeAvailable) {
311
+ getLogger().debug("Claude CLI not available, skipping auto-fix");
312
+ return false;
313
+ }
314
+ const validationCommand = this.getValidationCommand(validationType, packageManager);
315
+ const prompt = this.getClaudePrompt(validationType, validationCommand);
316
+ const validationTypeCapitalized = validationType.charAt(0).toUpperCase() + validationType.slice(1);
317
+ getLogger().info(`Launching Claude to help fix ${validationTypeCapitalized} errors...`);
318
+ try {
319
+ await launchClaude(prompt, {
320
+ addDir: worktreePath,
321
+ headless: false,
322
+ // Interactive mode
323
+ permissionMode: "acceptEdits",
324
+ // Auto-accept edits
325
+ model: "sonnet"
326
+ // Use Sonnet model
327
+ });
328
+ getLogger().info(`Re-running ${validationTypeCapitalized} after Claude's fixes...`);
329
+ try {
330
+ await runScript(validationType, worktreePath, [], { quiet: true });
331
+ getLogger().success(`${validationTypeCapitalized} passed after Claude auto-fix`);
332
+ return true;
333
+ } catch {
334
+ getLogger().warn(`${validationTypeCapitalized} still failing after Claude's help`);
335
+ return false;
336
+ }
337
+ } catch (error) {
338
+ getLogger().warn("Claude auto-fix failed", {
339
+ error: error instanceof Error ? error.message : String(error)
340
+ });
341
+ return false;
342
+ }
343
+ }
344
+ /**
345
+ * Get validation command string for prompts
346
+ * Uses il commands for multi-language project support
347
+ */
348
+ getValidationCommand(validationType, _packageManager) {
349
+ return `il ${validationType}`;
350
+ }
351
+ /**
352
+ * Get Claude prompt for specific validation type
353
+ * Matches bash script prompts exactly
354
+ */
355
+ getClaudePrompt(validationType, validationCommand) {
356
+ switch (validationType) {
357
+ case "compile":
358
+ case "typecheck":
359
+ return `There are TypeScript errors in this codebase. Please analyze the ${validationType} output, identify all type errors, and fix them. Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all type issues. When you are done, tell the user to quit using /exit to continue the validation process.`;
360
+ case "lint":
361
+ return `There are ESLint errors in this codebase. Please analyze the linting output, identify all linting issues, and fix them. Run '${validationCommand}' to see the errors, then make the necessary code changes to resolve all linting issues. Focus on code quality, consistency, and following the project's linting rules. When you are done, tell the user to quit using /exit to continue the validation process.`;
362
+ case "test":
363
+ return `There are unit test failures in this codebase. Please analyze the test output to understand what's failing, then fix the issues. This might involve updating test code, fixing bugs in the source code, or updating tests to match new behavior. Run '${validationCommand}' to see the detailed test failures, then make the necessary changes to get all tests passing. When you are done, tell the user to quit using /exit to continue the validation process.`;
364
+ }
365
+ }
366
+ };
367
+
368
+ // src/types/index.ts
369
+ var UserAbortedCommitError = class extends Error {
370
+ constructor(message = "User aborted the commit") {
371
+ super(message);
372
+ this.name = "UserAbortedCommitError";
373
+ }
374
+ };
375
+
376
+ // src/utils/vscode.ts
377
+ import { execa } from "execa";
378
+ function isRunningInVSCode() {
379
+ return process.env.TERM_PROGRAM === "vscode";
380
+ }
381
+ function isRunningInCursor() {
382
+ return !!process.env.CURSOR_TRACE_ID;
383
+ }
384
+ function isRunningInAntigravity() {
385
+ return !!process.env.ANTIGRAVITY_CLI_ALIAS;
386
+ }
387
+ async function isVSCodeAvailable() {
388
+ try {
389
+ await execa("command", ["-v", "code"], {
390
+ shell: true,
391
+ timeout: 5e3
392
+ });
393
+ return true;
394
+ } catch (error) {
395
+ logger.debug("VSCode CLI not available", { error });
396
+ return false;
397
+ }
398
+ }
399
+ async function isCursorAvailable() {
400
+ try {
401
+ await execa("command", ["-v", "cursor"], {
402
+ shell: true,
403
+ timeout: 5e3
404
+ });
405
+ return true;
406
+ } catch (error) {
407
+ logger.debug("Cursor CLI not available", { error });
408
+ return false;
409
+ }
410
+ }
411
+ async function isAntigravityAvailable() {
412
+ try {
413
+ await execa("command", ["-v", "agy"], {
414
+ shell: true,
415
+ timeout: 5e3
416
+ });
417
+ return true;
418
+ } catch (error) {
419
+ logger.debug("Antigravity CLI not available", { error });
420
+ return false;
421
+ }
422
+ }
423
+
424
+ // src/lib/CommitManager.ts
425
+ import { writeFile, readFile, unlink } from "fs/promises";
426
+ import { join } from "path";
427
+ import { execa as execa2 } from "execa";
428
+ var CommitManager = class {
429
+ constructor() {
430
+ }
431
+ /**
432
+ * Detect uncommitted changes in a worktree
433
+ * Parses git status --porcelain output into structured GitStatus
434
+ */
435
+ async detectUncommittedChanges(worktreePath) {
436
+ const porcelainOutput = await executeGitCommand(["status", "--porcelain"], {
437
+ cwd: worktreePath
438
+ });
439
+ const { stagedFiles, unstagedFiles } = this.parseGitStatus(porcelainOutput);
440
+ const currentBranch = await executeGitCommand(["branch", "--show-current"], {
441
+ cwd: worktreePath
442
+ });
443
+ return {
444
+ hasUncommittedChanges: stagedFiles.length > 0 || unstagedFiles.length > 0,
445
+ unstagedFiles,
446
+ stagedFiles,
447
+ currentBranch: currentBranch.trim(),
448
+ // Defer these to future enhancement
449
+ isAheadOfRemote: false,
450
+ isBehindRemote: false
451
+ };
452
+ }
453
+ /**
454
+ * Stage all changes and commit with Claude-generated or simple message
455
+ * Tries Claude first, falls back to simple message if Claude unavailable or fails
456
+ * Returns the commit message that was used
457
+ */
458
+ async commitChanges(worktreePath, options) {
459
+ if (options.dryRun) {
460
+ getLogger().info("[DRY RUN] Would run: git add -A");
461
+ getLogger().info("[DRY RUN] Would generate commit message with Claude (if available)");
462
+ const fallbackMessage = this.generateFallbackMessage(options);
463
+ const verifyFlag = options.skipVerify ? " --no-verify" : "";
464
+ getLogger().info(`[DRY RUN] Would commit with message${verifyFlag}: ${fallbackMessage}`);
465
+ return { message: fallbackMessage };
466
+ }
467
+ await executeGitCommand(["add", "-A"], { cwd: worktreePath });
468
+ let message = null;
469
+ if (!options.message) {
470
+ try {
471
+ message = await this.generateClaudeCommitMessage(worktreePath, options.issueNumber, options.issuePrefix, options.trailerType);
472
+ } catch (error) {
473
+ getLogger().debug("Claude commit message generation failed, using fallback", { error });
474
+ }
475
+ }
476
+ message ??= this.generateFallbackMessage(options);
477
+ if (options.skipVerify && !options.skipVerifySilent) {
478
+ getLogger().warn("Skipping pre-commit hooks (--no-verify configured in settings)");
479
+ }
480
+ try {
481
+ if (options.noReview || options.message) {
482
+ const commitArgs = ["commit", "-m", message];
483
+ if (options.skipVerify) {
484
+ commitArgs.push("--no-verify");
485
+ }
486
+ await executeGitCommand(commitArgs, { cwd: worktreePath });
487
+ } else {
488
+ const action = await promptCommitAction(message);
489
+ if (action === "abort") {
490
+ throw new UserAbortedCommitError();
491
+ }
492
+ if (action === "accept") {
493
+ const commitArgs = ["commit", "-m", message];
494
+ if (options.skipVerify) {
495
+ commitArgs.push("--no-verify");
496
+ }
497
+ await executeGitCommand(commitArgs, { cwd: worktreePath });
498
+ } else {
499
+ getLogger().info("Opening editor for commit message review...");
500
+ if (isRunningInAntigravity() && await isAntigravityAvailable()) {
501
+ await this.commitWithExternalEditor(worktreePath, message, options, "agy", "Antigravity");
502
+ } else if (isRunningInCursor() && await isCursorAvailable()) {
503
+ await this.commitWithExternalEditor(worktreePath, message, options, "cursor", "Cursor");
504
+ } else if (isRunningInVSCode() && await isVSCodeAvailable()) {
505
+ await this.commitWithExternalEditor(worktreePath, message, options, "code", "VSCode");
506
+ } else {
507
+ const commitArgs = ["commit", "-e", "-m", message];
508
+ if (options.skipVerify) {
509
+ commitArgs.push("--no-verify");
510
+ }
511
+ await executeGitCommand(commitArgs, {
512
+ cwd: worktreePath,
513
+ stdio: "inherit",
514
+ timeout: 3e5
515
+ // 5 minutes for interactive editing
516
+ });
517
+ }
518
+ }
519
+ }
520
+ return { message };
521
+ } catch (error) {
522
+ if (error instanceof UserAbortedCommitError) {
523
+ throw error;
524
+ }
525
+ if (error instanceof Error && error.message.includes("nothing to commit")) {
526
+ getLogger().info("No changes to commit");
527
+ return { message: "" };
528
+ }
529
+ throw error;
530
+ }
531
+ }
532
+ /**
533
+ * Commit with external editor CLI (VSCode, Cursor, Antigravity, etc.)
534
+ * Handles file creation, editing, and commit to ensure the file opens
535
+ * in the current editor window (preserves IPC context)
536
+ */
537
+ async commitWithExternalEditor(worktreePath, message, options, cliCommand, editorName) {
538
+ const commitMsgPath = join(worktreePath, ".COMMIT_EDITMSG");
539
+ const initialContent = `${message}
540
+
541
+ # Please enter the commit message for your changes. Lines starting
542
+ # with '#' will be ignored, and an empty message aborts the commit.
543
+ #
544
+ # Save and close the file to complete the commit.
545
+ `;
546
+ await writeFile(commitMsgPath, initialContent, "utf-8");
547
+ try {
548
+ getLogger().debug(`Opening commit message in ${editorName}: ${commitMsgPath}`);
549
+ await execa2(cliCommand, ["--wait", commitMsgPath], {
550
+ cwd: worktreePath,
551
+ stdio: "inherit"
552
+ });
553
+ const editedContent = await readFile(commitMsgPath, "utf-8");
554
+ const finalMessage = editedContent.split("\n").filter((line) => !line.startsWith("#")).join("\n").trim();
555
+ if (!finalMessage) {
556
+ throw new UserAbortedCommitError();
557
+ }
558
+ const commitArgs = ["commit", "-F", commitMsgPath];
559
+ if (options.skipVerify) {
560
+ commitArgs.push("--no-verify");
561
+ }
562
+ await writeFile(commitMsgPath, finalMessage, "utf-8");
563
+ await executeGitCommand(commitArgs, { cwd: worktreePath });
564
+ } finally {
565
+ try {
566
+ await unlink(commitMsgPath);
567
+ } catch {
568
+ }
569
+ }
570
+ }
571
+ /**
572
+ * Generate simple fallback commit message when Claude unavailable
573
+ * Used as fallback for Claude-powered commit messages
574
+ */
575
+ generateFallbackMessage(options) {
576
+ if (options.message) {
577
+ return options.message;
578
+ }
579
+ if (options.issueNumber) {
580
+ const trailer = options.trailerType ?? "Fixes";
581
+ return `WIP: Auto-commit for issue ${options.issuePrefix}${options.issueNumber}
582
+
583
+ ${trailer} ${options.issuePrefix}${options.issueNumber}`;
584
+ } else {
585
+ return "WIP: Auto-commit uncommitted changes";
586
+ }
587
+ }
588
+ /**
589
+ * Parse git status --porcelain output
590
+ * Format: "XY filename" where X=index, Y=worktree
591
+ * Examples:
592
+ * "M file.ts" - staged modification
593
+ * " M file.ts" - unstaged modification
594
+ * "MM file.ts" - both staged and unstaged
595
+ * "?? file.ts" - untracked
596
+ */
597
+ parseGitStatus(porcelainOutput) {
598
+ const stagedFiles = [];
599
+ const unstagedFiles = [];
600
+ if (!porcelainOutput.trim()) {
601
+ return { stagedFiles, unstagedFiles };
602
+ }
603
+ const lines = porcelainOutput.split("\n").filter((line) => line.trim());
604
+ for (const line of lines) {
605
+ if (line.length < 3) continue;
606
+ const indexStatus = line[0];
607
+ const worktreeStatus = line[1];
608
+ const filename = line.substring(3);
609
+ if (indexStatus !== " " && indexStatus !== "?") {
610
+ stagedFiles.push(filename);
611
+ }
612
+ if (worktreeStatus !== " " || line.startsWith("??")) {
613
+ unstagedFiles.push(filename);
614
+ }
615
+ }
616
+ return { stagedFiles, unstagedFiles };
617
+ }
618
+ /**
619
+ * Generate commit message using Claude Code
620
+ * Claude examines the git repository directly via --add-dir option
621
+ * Returns null if Claude unavailable or fails validation
622
+ */
623
+ async generateClaudeCommitMessage(worktreePath, issueNumber, issuePrefix, trailerType) {
624
+ const startTime = Date.now();
625
+ if (getLogger().isDebugEnabled()) {
626
+ getLogger().debug("Claude commit message generation started", {
627
+ worktreePath: worktreePath.split("/").pop(),
628
+ // Just show the folder name for privacy
629
+ issueNumber
630
+ });
631
+ } else {
632
+ getLogger().info("Generating commit message with Claude...");
633
+ }
634
+ getLogger().debug("Checking Claude CLI availability...");
635
+ const isClaudeAvailable = await detectClaudeCli();
636
+ if (!isClaudeAvailable) {
637
+ getLogger().info("Claude CLI not available, skipping Claude commit message generation");
638
+ return null;
639
+ }
640
+ getLogger().debug("Claude CLI is available");
641
+ getLogger().debug("Building commit message prompt...");
642
+ const prompt = this.buildCommitMessagePrompt(issueNumber, issuePrefix, trailerType);
643
+ getLogger().debug("Prompt built", { promptLength: prompt.length });
644
+ getLogger().debug("Claude prompt content:", {
645
+ prompt,
646
+ truncatedPreview: prompt.substring(0, 500) + (prompt.length > 500 ? "...[truncated]" : "")
647
+ });
648
+ try {
649
+ const claudeStartTime = Date.now();
650
+ const claudeOptions = {
651
+ headless: true,
652
+ addDir: worktreePath,
653
+ model: "claude-haiku-4-5-20251001",
654
+ // Fast, cost-effective model
655
+ timeout: 12e4,
656
+ // 120 second timeout
657
+ appendSystemPrompt: "Output only the requested content. Never include preamble, analysis, or meta-commentary. Your response is used verbatim."
658
+ };
659
+ getLogger().debug("Claude CLI call parameters:", {
660
+ options: claudeOptions,
661
+ worktreePathForAnalysis: worktreePath,
662
+ addDirContents: "Will include entire worktree directory for analysis"
663
+ });
664
+ const result = await launchClaude(prompt, claudeOptions);
665
+ const claudeDuration = Date.now() - claudeStartTime;
666
+ getLogger().debug("Claude API call completed", { duration: `${claudeDuration}ms` });
667
+ if (typeof result !== "string") {
668
+ getLogger().warn("Claude returned non-string result", { resultType: typeof result });
669
+ return null;
670
+ }
671
+ getLogger().debug("Raw Claude output received", {
672
+ outputLength: result.length,
673
+ preview: result.substring(0, 200) + (result.length > 200 ? "..." : "")
674
+ });
675
+ getLogger().debug("Sanitizing Claude output...");
676
+ const sanitized = this.sanitizeClaudeOutput(result);
677
+ getLogger().debug("Output sanitized", {
678
+ originalLength: result.length,
679
+ sanitizedLength: sanitized.length,
680
+ sanitized: sanitized.substring(0, 200) + (sanitized.length > 200 ? "..." : "")
681
+ });
682
+ if (!sanitized) {
683
+ getLogger().warn("Claude returned empty message after sanitization");
684
+ return null;
685
+ }
686
+ let finalMessage = sanitized;
687
+ if (issueNumber) {
688
+ const trailer = trailerType ?? "Fixes";
689
+ const trailerRef = `${trailer} ${issuePrefix}${issueNumber}`;
690
+ if (!finalMessage.includes(trailerRef)) {
691
+ finalMessage = `${finalMessage}
692
+
693
+ ${trailerRef}`;
694
+ getLogger().debug(`Added "${trailerRef}" trailer to commit message`);
695
+ } else {
696
+ getLogger().debug(`"${trailerRef}" already present in commit message`);
697
+ }
698
+ }
699
+ const totalDuration = Date.now() - startTime;
700
+ if (getLogger().isDebugEnabled()) {
701
+ getLogger().debug("Claude commit message generated", {
702
+ message: finalMessage,
703
+ totalDuration: `${totalDuration}ms`,
704
+ claudeApiDuration: `${claudeDuration}ms`
705
+ });
706
+ } else {
707
+ getLogger().info("Commit message generated");
708
+ }
709
+ return finalMessage;
710
+ } catch (error) {
711
+ const totalDuration = Date.now() - startTime;
712
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
713
+ if (errorMessage.includes("timed out") || errorMessage.includes("timeout")) {
714
+ getLogger().warn("Claude commit message generation timed out after 45 seconds", {
715
+ totalDuration: `${totalDuration}ms`,
716
+ worktreePath: worktreePath.split("/").pop()
717
+ });
718
+ } else {
719
+ getLogger().warn("Failed to generate commit message with Claude", {
720
+ error: errorMessage,
721
+ totalDuration: `${totalDuration}ms`,
722
+ worktreePath: worktreePath.split("/").pop()
723
+ });
724
+ }
725
+ return null;
726
+ }
727
+ }
728
+ /**
729
+ * Build structured XML prompt for commit message generation
730
+ * Uses XML format for clear task definition and output expectations
731
+ */
732
+ buildCommitMessagePrompt(issueNumber, issuePrefix, trailerType) {
733
+ const trailer = trailerType ?? "Fixes";
734
+ const issueContext = issueNumber ? `
735
+ <IssueContext>
736
+ This commit is associated with issue ${issuePrefix}${issueNumber}.
737
+ ${trailer === "Fixes" ? "If the changes appear to resolve the issue, include" : "Include"} "${trailer} ${issuePrefix}${issueNumber}" at the end of the first line of commit message.
738
+ </IssueContext>` : "";
739
+ const examplePrefix = issuePrefix || "";
740
+ return `<Task>
741
+ You are a software engineer writing a commit message for this repository.
742
+ Examine the staged changes in the git repository and generate a concise, meaningful commit message.
743
+ </Task>
744
+
745
+ <Requirements>
746
+ <Format>The first line must be a brief summary of the changes made as a full sentence. If it references an issue, include "${trailer} ${examplePrefix}N" at the end of this line.
747
+
748
+ Add 2 newlines, then add a bullet-point form description of the changes made, each change on a new line.</Format>
749
+ <Mood>Use imperative mood (e.g., "Add feature" not "Added feature")</Mood>
750
+ <Focus>Be specific about what was changed and why</Focus>
751
+ <Conciseness>Keep message under 72 characters for subject line when possible</Conciseness>
752
+ <NoMeta>CRITICAL: Do NOT include ANY explanatory text, analysis, or meta-commentary. Output ONLY the raw commit message.</NoMeta>
753
+ <Examples>
754
+ Good: "Add user authentication with JWT tokens. ${trailer} ${examplePrefix}42
755
+
756
+ - Implement login and registration endpoints
757
+ - Secure routes with JWT middleware
758
+ - Update user model to store hashed passwords"
759
+ Good: "Fix navigation bug in sidebar menu."
760
+ Bad: "Based on the changes, I'll create: Add user authentication"
761
+ Bad: "Looking at the files, this commit should be: Fix navigation bug"
762
+ </Examples>
763
+ ${issueContext}
764
+ </Requirements>
765
+
766
+ <Output>
767
+ IMPORTANT: Your entire response will be used directly as the git commit message.
768
+ Do not include any explanatory text before or after the commit message.
769
+ Start your response immediately with the commit message text.
770
+ </Output>`;
771
+ }
772
+ /**
773
+ * Sanitize Claude output to remove meta-commentary and clean formatting
774
+ * Handles cases where Claude includes explanatory text despite instructions
775
+ */
776
+ sanitizeClaudeOutput(rawOutput) {
777
+ let cleaned = rawOutput.trim();
778
+ const metaPatterns = [
779
+ /^.*?based on.*?changes.*?:/i,
780
+ /^.*?looking at.*?files.*?:/i,
781
+ /^.*?examining.*?:/i,
782
+ /^.*?analyzing.*?:/i,
783
+ /^.*?i'll.*?generate.*?:/i,
784
+ /^.*?let me.*?:/i,
785
+ /^.*?the commit message.*?should be.*?:/i,
786
+ /^.*?here.*?is.*?commit.*?message.*?:/i
787
+ ];
788
+ for (const pattern of metaPatterns) {
789
+ cleaned = cleaned.replace(pattern, "").trim();
790
+ }
791
+ if (cleaned.includes(":")) {
792
+ const colonIndex = cleaned.indexOf(":");
793
+ const beforeColon = cleaned.substring(0, colonIndex).trim().toLowerCase();
794
+ const metaIndicators = [
795
+ "here is the commit message",
796
+ "commit message",
797
+ "here is",
798
+ "the message should be",
799
+ "i suggest",
800
+ "my suggestion"
801
+ ];
802
+ const isMetaCommentary = metaIndicators.some((indicator) => beforeColon.includes(indicator));
803
+ if (isMetaCommentary) {
804
+ const afterColon = cleaned.substring(colonIndex + 1).trim();
805
+ if (afterColon && afterColon.length > 10) {
806
+ cleaned = afterColon;
807
+ }
808
+ }
809
+ }
810
+ if (cleaned.startsWith('"') && cleaned.endsWith('"') || cleaned.startsWith("'") && cleaned.endsWith("'")) {
811
+ cleaned = cleaned.slice(1, -1).trim();
812
+ }
813
+ return cleaned;
814
+ }
815
+ };
816
+
817
+ export {
818
+ ValidationRunner,
819
+ UserAbortedCommitError,
820
+ CommitManager
821
+ };
822
+ //# sourceMappingURL=chunk-YQNSZKKT.js.map