@iloom/cli 0.2.0 → 0.3.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 (169) hide show
  1. package/README.md +274 -30
  2. package/dist/BranchNamingService-3OQPRSWT.js +13 -0
  3. package/dist/ClaudeContextManager-MUQSDY2E.js +13 -0
  4. package/dist/ClaudeService-HG4VQ7AW.js +12 -0
  5. package/dist/GitHubService-EBOETDIW.js +11 -0
  6. package/dist/{LoomLauncher-CTSWJL35.js → LoomLauncher-FLEMBCSQ.js} +63 -32
  7. package/dist/LoomLauncher-FLEMBCSQ.js.map +1 -0
  8. package/dist/ProjectCapabilityDetector-34LU7JJ4.js +9 -0
  9. package/dist/{PromptTemplateManager-WII75TKH.js → PromptTemplateManager-A52RUAMS.js} +2 -2
  10. package/dist/README.md +274 -30
  11. package/dist/{SettingsManager-XOYCLH3D.js → SettingsManager-WHHFGSL7.js} +12 -4
  12. package/dist/SettingsMigrationManager-AGIIIPDQ.js +10 -0
  13. package/dist/agents/iloom-issue-analyze-and-plan.md +125 -35
  14. package/dist/agents/iloom-issue-analyzer.md +284 -32
  15. package/dist/agents/iloom-issue-complexity-evaluator.md +40 -21
  16. package/dist/agents/iloom-issue-enhancer.md +69 -48
  17. package/dist/agents/iloom-issue-implementer.md +36 -25
  18. package/dist/agents/iloom-issue-planner.md +35 -24
  19. package/dist/agents/iloom-issue-reviewer.md +62 -9
  20. package/dist/chunk-3KATJIKO.js +55 -0
  21. package/dist/chunk-3KATJIKO.js.map +1 -0
  22. package/dist/{chunk-SWCRXDZC.js → chunk-3RUPPQRG.js} +1 -18
  23. package/dist/chunk-3RUPPQRG.js.map +1 -0
  24. package/dist/{chunk-RF2YI2XJ.js → chunk-47KSHUCR.js} +3 -3
  25. package/dist/chunk-47KSHUCR.js.map +1 -0
  26. package/dist/{chunk-VETG35MF.js → chunk-4HHRTA7Q.js} +3 -3
  27. package/dist/{chunk-VETG35MF.js.map → chunk-4HHRTA7Q.js.map} +1 -1
  28. package/dist/chunk-5EF7Z346.js +1987 -0
  29. package/dist/chunk-5EF7Z346.js.map +1 -0
  30. package/dist/{chunk-4IV6W4U5.js → chunk-AWOFAD5O.js} +12 -12
  31. package/dist/chunk-AWOFAD5O.js.map +1 -0
  32. package/dist/{chunk-2PLUQT6J.js → chunk-C5QCTEQK.js} +2 -2
  33. package/dist/{chunk-CWR2SANQ.js → chunk-EBISESAP.js} +1 -1
  34. package/dist/{chunk-LHP6ROUM.js → chunk-FIAT22G7.js} +4 -16
  35. package/dist/chunk-FIAT22G7.js.map +1 -0
  36. package/dist/{chunk-TS6DL67T.js → chunk-G2IEYOLQ.js} +11 -38
  37. package/dist/chunk-G2IEYOLQ.js.map +1 -0
  38. package/dist/{chunk-ZMNQBJUI.js → chunk-IP7SMKIF.js} +61 -22
  39. package/dist/chunk-IP7SMKIF.js.map +1 -0
  40. package/dist/{chunk-JNKJ7NJV.js → chunk-JKXJ7BGL.js} +6 -2
  41. package/dist/{chunk-JNKJ7NJV.js.map → chunk-JKXJ7BGL.js.map} +1 -1
  42. package/dist/{chunk-LAPY6NAE.js → chunk-JQFO7QQN.js} +68 -12
  43. package/dist/{chunk-LAPY6NAE.js.map → chunk-JQFO7QQN.js.map} +1 -1
  44. package/dist/{SettingsMigrationManager-MTQIMI54.js → chunk-KLBYVHPK.js} +3 -2
  45. package/dist/{chunk-HBVFXN7R.js → chunk-MAVL6PJF.js} +26 -3
  46. package/dist/chunk-MAVL6PJF.js.map +1 -0
  47. package/dist/{chunk-USVVV3FP.js → chunk-MKWYLDFK.js} +5 -5
  48. package/dist/chunk-ML3NRPNB.js +396 -0
  49. package/dist/chunk-ML3NRPNB.js.map +1 -0
  50. package/dist/{chunk-DJUGYNQE.js → chunk-PA6Q6AWM.js} +16 -3
  51. package/dist/chunk-PA6Q6AWM.js.map +1 -0
  52. package/dist/chunk-RO26VS3W.js +444 -0
  53. package/dist/chunk-RO26VS3W.js.map +1 -0
  54. package/dist/{chunk-6LEQW46Y.js → chunk-VAYCCUXW.js} +72 -2
  55. package/dist/{chunk-6LEQW46Y.js.map → chunk-VAYCCUXW.js.map} +1 -1
  56. package/dist/{chunk-SPYPLHMK.js → chunk-VU3QMIP2.js} +34 -2
  57. package/dist/chunk-VU3QMIP2.js.map +1 -0
  58. package/dist/{chunk-PVAVNJKS.js → chunk-WEN5C5DM.js} +10 -1
  59. package/dist/chunk-WEN5C5DM.js.map +1 -0
  60. package/dist/{chunk-MFU53H6J.js → chunk-XXV3UFZL.js} +3 -3
  61. package/dist/{chunk-MFU53H6J.js.map → chunk-XXV3UFZL.js.map} +1 -1
  62. package/dist/{chunk-GZP4UGGM.js → chunk-ZM3CFL5L.js} +2 -2
  63. package/dist/{chunk-BLCTGFZN.js → chunk-ZT3YZB4K.js} +3 -4
  64. package/dist/chunk-ZT3YZB4K.js.map +1 -0
  65. package/dist/{claude-ZIWDG4XG.js → claude-GOP6PFC7.js} +2 -2
  66. package/dist/{cleanup-FEIVZSIV.js → cleanup-7RWLBSLE.js} +86 -25
  67. package/dist/cleanup-7RWLBSLE.js.map +1 -0
  68. package/dist/cli.js +2511 -62
  69. package/dist/cli.js.map +1 -1
  70. package/dist/{contribute-EMZKCAC6.js → contribute-BS2L4FZR.js} +6 -6
  71. package/dist/{feedback-LFNMQBAZ.js → feedback-N4ECWIPF.js} +15 -14
  72. package/dist/{feedback-LFNMQBAZ.js.map → feedback-N4ECWIPF.js.map} +1 -1
  73. package/dist/{git-WC6HZLOT.js → git-TDXKRTXM.js} +4 -2
  74. package/dist/{ignite-MQWVJEAB.js → ignite-VM64QO3J.js} +32 -27
  75. package/dist/ignite-VM64QO3J.js.map +1 -0
  76. package/dist/index.d.ts +359 -45
  77. package/dist/index.js +1266 -502
  78. package/dist/index.js.map +1 -1
  79. package/dist/{init-GJDYN2IK.js → init-G3T64SC4.js} +104 -40
  80. package/dist/init-G3T64SC4.js.map +1 -0
  81. package/dist/mcp/issue-management-server.js +934 -0
  82. package/dist/mcp/issue-management-server.js.map +1 -0
  83. package/dist/{neon-helpers-ZVIRPKCI.js → neon-helpers-WPUACUVC.js} +3 -3
  84. package/dist/neon-helpers-WPUACUVC.js.map +1 -0
  85. package/dist/{open-NXSN7XOC.js → open-KXDXEKRZ.js} +39 -36
  86. package/dist/open-KXDXEKRZ.js.map +1 -0
  87. package/dist/{prompt-ANTQWHUF.js → prompt-7INJ7YRU.js} +4 -2
  88. package/dist/prompt-7INJ7YRU.js.map +1 -0
  89. package/dist/prompts/init-prompt.txt +538 -95
  90. package/dist/prompts/issue-prompt.txt +27 -27
  91. package/dist/{rebase-DUNFOJVS.js → rebase-Q7GMM7EI.js} +6 -6
  92. package/dist/{remote-ZCXJVVNW.js → remote-VUNCQZ6J.js} +3 -2
  93. package/dist/remote-VUNCQZ6J.js.map +1 -0
  94. package/dist/{run-O7ZK7CKA.js → run-PAWJJCSX.js} +39 -36
  95. package/dist/run-PAWJJCSX.js.map +1 -0
  96. package/dist/schema/settings.schema.json +56 -0
  97. package/dist/{test-git-T76HOTIA.js → test-git-3WDLNQCA.js} +3 -3
  98. package/dist/{test-prefix-6HJUVQMH.js → test-prefix-EVGAWAJW.js} +3 -3
  99. package/dist/{test-webserver-M2I3EV4J.js → test-webserver-DAHONWCS.js} +4 -4
  100. package/dist/test-webserver-DAHONWCS.js.map +1 -0
  101. package/package.json +2 -1
  102. package/dist/ClaudeContextManager-LVCYRM6Q.js +0 -13
  103. package/dist/ClaudeService-WVTWB3DK.js +0 -12
  104. package/dist/GitHubService-7E2S5NNZ.js +0 -11
  105. package/dist/LoomLauncher-CTSWJL35.js.map +0 -1
  106. package/dist/add-issue-OBI325W7.js +0 -69
  107. package/dist/add-issue-OBI325W7.js.map +0 -1
  108. package/dist/chunk-4IV6W4U5.js.map +0 -1
  109. package/dist/chunk-BLCTGFZN.js.map +0 -1
  110. package/dist/chunk-CVLAZRNB.js +0 -54
  111. package/dist/chunk-CVLAZRNB.js.map +0 -1
  112. package/dist/chunk-DJUGYNQE.js.map +0 -1
  113. package/dist/chunk-H4E4THUZ.js +0 -55
  114. package/dist/chunk-H4E4THUZ.js.map +0 -1
  115. package/dist/chunk-H5LDRGVK.js +0 -642
  116. package/dist/chunk-H5LDRGVK.js.map +0 -1
  117. package/dist/chunk-HBVFXN7R.js.map +0 -1
  118. package/dist/chunk-LHP6ROUM.js.map +0 -1
  119. package/dist/chunk-PVAVNJKS.js.map +0 -1
  120. package/dist/chunk-RF2YI2XJ.js.map +0 -1
  121. package/dist/chunk-SPYPLHMK.js.map +0 -1
  122. package/dist/chunk-SWCRXDZC.js.map +0 -1
  123. package/dist/chunk-SYOSCMIT.js +0 -545
  124. package/dist/chunk-SYOSCMIT.js.map +0 -1
  125. package/dist/chunk-T3KEIB4D.js +0 -243
  126. package/dist/chunk-T3KEIB4D.js.map +0 -1
  127. package/dist/chunk-TS6DL67T.js.map +0 -1
  128. package/dist/chunk-ZMNQBJUI.js.map +0 -1
  129. package/dist/cleanup-FEIVZSIV.js.map +0 -1
  130. package/dist/enhance-MNA4ZGXW.js +0 -176
  131. package/dist/enhance-MNA4ZGXW.js.map +0 -1
  132. package/dist/finish-TX5CJICB.js +0 -1749
  133. package/dist/finish-TX5CJICB.js.map +0 -1
  134. package/dist/ignite-MQWVJEAB.js.map +0 -1
  135. package/dist/init-GJDYN2IK.js.map +0 -1
  136. package/dist/mcp/chunk-6SDFJ42P.js +0 -62
  137. package/dist/mcp/chunk-6SDFJ42P.js.map +0 -1
  138. package/dist/mcp/claude-NDFOCQQQ.js +0 -249
  139. package/dist/mcp/claude-NDFOCQQQ.js.map +0 -1
  140. package/dist/mcp/color-QS5BFCNN.js +0 -168
  141. package/dist/mcp/color-QS5BFCNN.js.map +0 -1
  142. package/dist/mcp/github-comment-server.js +0 -168
  143. package/dist/mcp/github-comment-server.js.map +0 -1
  144. package/dist/mcp/terminal-OMNRFWB3.js +0 -227
  145. package/dist/mcp/terminal-OMNRFWB3.js.map +0 -1
  146. package/dist/open-NXSN7XOC.js.map +0 -1
  147. package/dist/run-O7ZK7CKA.js.map +0 -1
  148. package/dist/start-73I5W7WW.js +0 -983
  149. package/dist/start-73I5W7WW.js.map +0 -1
  150. package/dist/test-webserver-M2I3EV4J.js.map +0 -1
  151. /package/dist/{ClaudeContextManager-LVCYRM6Q.js.map → BranchNamingService-3OQPRSWT.js.map} +0 -0
  152. /package/dist/{ClaudeService-WVTWB3DK.js.map → ClaudeContextManager-MUQSDY2E.js.map} +0 -0
  153. /package/dist/{GitHubService-7E2S5NNZ.js.map → ClaudeService-HG4VQ7AW.js.map} +0 -0
  154. /package/dist/{PromptTemplateManager-WII75TKH.js.map → GitHubService-EBOETDIW.js.map} +0 -0
  155. /package/dist/{SettingsManager-XOYCLH3D.js.map → ProjectCapabilityDetector-34LU7JJ4.js.map} +0 -0
  156. /package/dist/{claude-ZIWDG4XG.js.map → PromptTemplateManager-A52RUAMS.js.map} +0 -0
  157. /package/dist/{git-WC6HZLOT.js.map → SettingsManager-WHHFGSL7.js.map} +0 -0
  158. /package/dist/{neon-helpers-ZVIRPKCI.js.map → SettingsMigrationManager-AGIIIPDQ.js.map} +0 -0
  159. /package/dist/{chunk-2PLUQT6J.js.map → chunk-C5QCTEQK.js.map} +0 -0
  160. /package/dist/{chunk-CWR2SANQ.js.map → chunk-EBISESAP.js.map} +0 -0
  161. /package/dist/{SettingsMigrationManager-MTQIMI54.js.map → chunk-KLBYVHPK.js.map} +0 -0
  162. /package/dist/{chunk-USVVV3FP.js.map → chunk-MKWYLDFK.js.map} +0 -0
  163. /package/dist/{chunk-GZP4UGGM.js.map → chunk-ZM3CFL5L.js.map} +0 -0
  164. /package/dist/{prompt-ANTQWHUF.js.map → claude-GOP6PFC7.js.map} +0 -0
  165. /package/dist/{contribute-EMZKCAC6.js.map → contribute-BS2L4FZR.js.map} +0 -0
  166. /package/dist/{remote-ZCXJVVNW.js.map → git-TDXKRTXM.js.map} +0 -0
  167. /package/dist/{rebase-DUNFOJVS.js.map → rebase-Q7GMM7EI.js.map} +0 -0
  168. /package/dist/{test-git-T76HOTIA.js.map → test-git-3WDLNQCA.js.map} +0 -0
  169. /package/dist/{test-prefix-6HJUVQMH.js.map → test-prefix-EVGAWAJW.js.map} +0 -0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger
4
+ } from "./chunk-GEHQXLEI.js";
5
+
6
+ // src/lib/BranchNamingService.ts
7
+ var SimpleBranchNameStrategy = class {
8
+ async generate(issueNumber, title) {
9
+ const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").substring(0, 20);
10
+ return `feat/issue-${issueNumber}__${slug}`;
11
+ }
12
+ };
13
+ var ClaudeBranchNameStrategy = class {
14
+ constructor(claudeModel = "haiku") {
15
+ this.claudeModel = claudeModel;
16
+ }
17
+ async generate(issueNumber, title) {
18
+ const { generateBranchName } = await import("./claude-GOP6PFC7.js");
19
+ return generateBranchName(title, issueNumber, this.claudeModel);
20
+ }
21
+ };
22
+ var DefaultBranchNamingService = class {
23
+ constructor(options) {
24
+ if (options == null ? void 0 : options.strategy) {
25
+ this.defaultStrategy = options.strategy;
26
+ } else if ((options == null ? void 0 : options.useClaude) !== false) {
27
+ this.defaultStrategy = new ClaudeBranchNameStrategy(options == null ? void 0 : options.claudeModel);
28
+ } else {
29
+ this.defaultStrategy = new SimpleBranchNameStrategy();
30
+ }
31
+ }
32
+ async generateBranchName(options) {
33
+ const { issueNumber, title, strategy } = options;
34
+ const nameStrategy = strategy ?? this.defaultStrategy;
35
+ logger.debug("Generating branch name", {
36
+ issueNumber,
37
+ title,
38
+ strategy: nameStrategy.constructor.name
39
+ });
40
+ return nameStrategy.generate(issueNumber, title);
41
+ }
42
+ setDefaultStrategy(strategy) {
43
+ this.defaultStrategy = strategy;
44
+ }
45
+ getDefaultStrategy() {
46
+ return this.defaultStrategy;
47
+ }
48
+ };
49
+
50
+ export {
51
+ SimpleBranchNameStrategy,
52
+ ClaudeBranchNameStrategy,
53
+ DefaultBranchNamingService
54
+ };
55
+ //# sourceMappingURL=chunk-3KATJIKO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/BranchNamingService.ts"],"sourcesContent":["import type { BranchNameStrategy, BranchGenerationOptions } from '../types/branch-naming.js'\nimport { logger } from '../utils/logger.js'\n\n// ============================================\n// Strategy Classes\n// ============================================\n\n/**\n * Simple branch naming strategy\n * Format: feat/issue-{number}__{slug}\n */\nexport class SimpleBranchNameStrategy implements BranchNameStrategy {\n\tasync generate(issueNumber: string | number, title: string): Promise<string> {\n\t\t// Create a simple slug from the title\n\t\tconst slug = title\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t.replace(/^-|-$/g, '')\n\t\t\t.substring(0, 20) // Keep it short for the simple strategy\n\n\t\treturn `feat/issue-${issueNumber}__${slug}`\n\t}\n}\n\n/**\n * Claude AI-powered branch naming strategy\n * Uses Claude CLI to generate semantic branch names\n */\nexport class ClaudeBranchNameStrategy implements BranchNameStrategy {\n\tconstructor(private claudeModel = 'haiku') {}\n\n\tasync generate(issueNumber: string | number, title: string): Promise<string> {\n\t\t// Dynamic import to allow mocking in tests\n\t\tconst { generateBranchName } = await import('../utils/claude.js')\n\t\treturn generateBranchName(title, issueNumber, this.claudeModel)\n\t}\n}\n\n// ============================================\n// Service Interface and Implementation\n// ============================================\n\n/**\n * Service interface for branch name generation\n * Provides strategy management and generation capabilities\n */\nexport interface BranchNamingService {\n\tgenerateBranchName(options: BranchGenerationOptions): Promise<string>\n\tsetDefaultStrategy(strategy: BranchNameStrategy): void\n\tgetDefaultStrategy(): BranchNameStrategy\n}\n\n/**\n * Default implementation of BranchNamingService\n * Supports multiple naming strategies with configurable defaults\n */\nexport class DefaultBranchNamingService implements BranchNamingService {\n\tprivate defaultStrategy: BranchNameStrategy\n\n\tconstructor(options?: {\n\t\tstrategy?: BranchNameStrategy\n\t\tuseClaude?: boolean\n\t\tclaudeModel?: string\n\t}) {\n\t\t// Set up default strategy based on options\n\t\tif (options?.strategy) {\n\t\t\tthis.defaultStrategy = options.strategy\n\t\t} else if (options?.useClaude !== false) {\n\t\t\tthis.defaultStrategy = new ClaudeBranchNameStrategy(options?.claudeModel)\n\t\t} else {\n\t\t\tthis.defaultStrategy = new SimpleBranchNameStrategy()\n\t\t}\n\t}\n\n\tasync generateBranchName(options: BranchGenerationOptions): Promise<string> {\n\t\tconst { issueNumber, title, strategy } = options\n\n\t\t// Use provided strategy or fall back to default\n\t\tconst nameStrategy = strategy ?? this.defaultStrategy\n\n\t\tlogger.debug('Generating branch name', {\n\t\t\tissueNumber,\n\t\t\ttitle,\n\t\t\tstrategy: nameStrategy.constructor.name,\n\t\t})\n\n\t\treturn nameStrategy.generate(issueNumber, title)\n\t}\n\n\tsetDefaultStrategy(strategy: BranchNameStrategy): void {\n\t\tthis.defaultStrategy = strategy\n\t}\n\n\tgetDefaultStrategy(): BranchNameStrategy {\n\t\treturn this.defaultStrategy\n\t}\n}\n"],"mappings":";;;;;;AAWO,IAAM,2BAAN,MAA6D;AAAA,EACnE,MAAM,SAAS,aAA8B,OAAgC;AAE5E,UAAM,OAAO,MACX,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,UAAU,EAAE,EACpB,UAAU,GAAG,EAAE;AAEjB,WAAO,cAAc,WAAW,KAAK,IAAI;AAAA,EAC1C;AACD;AAMO,IAAM,2BAAN,MAA6D;AAAA,EACnE,YAAoB,cAAc,SAAS;AAAvB;AAAA,EAAwB;AAAA,EAE5C,MAAM,SAAS,aAA8B,OAAgC;AAE5E,UAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,sBAAoB;AAChE,WAAO,mBAAmB,OAAO,aAAa,KAAK,WAAW;AAAA,EAC/D;AACD;AAoBO,IAAM,6BAAN,MAAgE;AAAA,EAGtE,YAAY,SAIT;AAEF,QAAI,mCAAS,UAAU;AACtB,WAAK,kBAAkB,QAAQ;AAAA,IAChC,YAAW,mCAAS,eAAc,OAAO;AACxC,WAAK,kBAAkB,IAAI,yBAAyB,mCAAS,WAAW;AAAA,IACzE,OAAO;AACN,WAAK,kBAAkB,IAAI,yBAAyB;AAAA,IACrD;AAAA,EACD;AAAA,EAEA,MAAM,mBAAmB,SAAmD;AAC3E,UAAM,EAAE,aAAa,OAAO,SAAS,IAAI;AAGzC,UAAM,eAAe,YAAY,KAAK;AAEtC,WAAO,MAAM,0BAA0B;AAAA,MACtC;AAAA,MACA;AAAA,MACA,UAAU,aAAa,YAAY;AAAA,IACpC,CAAC;AAED,WAAO,aAAa,SAAS,aAAa,KAAK;AAAA,EAChD;AAAA,EAEA,mBAAmB,UAAoC;AACtD,SAAK,kBAAkB;AAAA,EACxB;AAAA,EAEA,qBAAyC;AACxC,WAAO,KAAK;AAAA,EACb;AACD;","names":[]}
@@ -148,21 +148,6 @@ async function updateProjectItemField(itemId, projectId, fieldId, optionId) {
148
148
  "json"
149
149
  ]);
150
150
  }
151
- var SimpleBranchNameStrategy = class {
152
- async generate(issueNumber, title) {
153
- const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").substring(0, 20);
154
- return `feat/issue-${issueNumber}-${slug}`;
155
- }
156
- };
157
- var ClaudeBranchNameStrategy = class {
158
- constructor(claudeModel = "haiku") {
159
- this.claudeModel = claudeModel;
160
- }
161
- async generate(issueNumber, title) {
162
- const { generateBranchName } = await import("./claude-ZIWDG4XG.js");
163
- return generateBranchName(title, issueNumber, this.claudeModel);
164
- }
165
- };
166
151
  async function createIssue(title, body, options) {
167
152
  const { repo, labels } = options ?? {};
168
153
  logger.debug("Creating GitHub issue", { title, repo, labels });
@@ -223,9 +208,7 @@ export {
223
208
  fetchProjectItems,
224
209
  fetchProjectFields,
225
210
  updateProjectItemField,
226
- SimpleBranchNameStrategy,
227
- ClaudeBranchNameStrategy,
228
211
  createIssue,
229
212
  getRepoInfo
230
213
  };
231
- //# sourceMappingURL=chunk-SWCRXDZC.js.map
214
+ //# sourceMappingURL=chunk-3RUPPQRG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/github.ts"],"sourcesContent":["import { execa } from 'execa'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tGitHubAuthStatus,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { logger } from './logger.js'\n\n// Core GitHub CLI execution wrapper\nexport async function executeGhCommand<T = unknown>(\n\targs: string[],\n\toptions?: { cwd?: string; timeout?: number }\n): Promise<T> {\n\tconst result = await execa('gh', args, {\n\t\tcwd: options?.cwd ?? process.cwd(),\n\t\ttimeout: options?.timeout ?? 30000,\n\t\tencoding: 'utf8',\n\t})\n\n\t// Parse JSON output if --json flag, --format json, or --jq was used\n\tconst isJson =\n\t\targs.includes('--json') ||\n\t\targs.includes('--jq') ||\n\t\t(args.includes('--format') && args[args.indexOf('--format') + 1] === 'json')\n\tconst data = isJson ? JSON.parse(result.stdout) : result.stdout\n\n\treturn data as T\n}\n\n// Authentication checking\nexport async function checkGhAuth(): Promise<GitHubAuthStatus> {\n\ttry {\n\t\tconst output = await executeGhCommand<string>(['auth', 'status'])\n\n\t\t// Parse auth status output - handle both old and new formats\n\t\t// Old format: \"Logged in to github.com as username\"\n\t\t// New format: \"✓ Logged in to github.com account username (keyring)\"\n\n\t\t// Split output into lines to find the active account\n\t\tconst lines = output.split('\\n')\n\t\tlet username: string | undefined\n\t\tlet scopes: string[] = []\n\n\t\t// Find the active account (look for \"Active account: true\" or first account if none marked)\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst line = lines[i]\n\n\t\t\t// Match new format: \"✓ Logged in to github.com account username\"\n\t\t\tconst newFormatMatch = line?.match(/Logged in to github\\.com account ([^\\s(]+)/)\n\t\t\tif (newFormatMatch) {\n\t\t\t\tconst accountName = newFormatMatch[1]\n\n\t\t\t\t// Check if this is the active account\n\t\t\t\tconst nextFewLines = lines.slice(i + 1, i + 5).join('\\n')\n\t\t\t\tconst isActive = nextFewLines.includes('Active account: true')\n\n\t\t\t\t// If this is the active account, or we haven't found one yet and there's no \"Active account\" marker\n\t\t\t\tif (isActive || (!username && !output.includes('Active account:'))) {\n\t\t\t\t\tusername = accountName\n\n\t\t\t\t\t// Find scopes for this account\n\t\t\t\t\tconst scopeMatch = nextFewLines.match(/Token scopes: (.+)/)\n\t\t\t\t\tif (scopeMatch?.[1]) {\n\t\t\t\t\t\tscopes = scopeMatch[1].split(', ').map(scope => scope.replace(/^'|'$/g, ''))\n\t\t\t\t\t}\n\n\t\t\t\t\t// If this is the active account, we're done\n\t\t\t\t\tif (isActive) break\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fallback: match old format\n\t\t\tif (!username) {\n\t\t\t\tconst oldFormatMatch = line?.match(/Logged in to github\\.com as ([^\\s]+)/)\n\t\t\t\tif (oldFormatMatch) {\n\t\t\t\t\tusername = oldFormatMatch[1]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If scopes not yet extracted, try the old \"Token scopes\" format\n\t\tif (scopes.length === 0) {\n\t\t\tconst scopeMatch = output.match(/Token scopes: (.+)/)\n\t\t\tscopes = scopeMatch?.[1]?.split(', ').map(scope => scope.replace(/^'|'$/g, '')) ?? []\n\t\t}\n\n\t\treturn {\n\t\t\thasAuth: true,\n\t\t\tscopes,\n\t\t\t...(username && { username }),\n\t\t}\n\t} catch (error) {\n\t\t// Only return \"no auth\" for specific authentication errors\n\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('You are not logged into any GitHub hosts')) {\n\t\t\treturn { hasAuth: false, scopes: [] }\n\t\t}\n\t\t// Re-throw unexpected errors\n\t\tthrow error\n\t}\n}\n\nexport async function hasProjectScope(): Promise<boolean> {\n\tconst auth = await checkGhAuth()\n\treturn auth.scopes.includes('project')\n}\n\n// Issue fetching\nexport async function fetchGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<GitHubIssue> {\n\tlogger.debug('Fetching GitHub issue', { issueNumber, repo })\n\n\tconst args = [\n\t\t'issue',\n\t\t'view',\n\t\tString(issueNumber),\n\t\t'--json',\n\t\t'number,title,body,state,labels,assignees,url,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubIssue>(args)\n}\n\n// PR fetching\nexport async function fetchGhPR(\n\tprNumber: number,\n\trepo?: string\n): Promise<GitHubPullRequest> {\n\tlogger.debug('Fetching GitHub PR', { prNumber, repo })\n\n\tconst args = [\n\t\t'pr',\n\t\t'view',\n\t\tString(prNumber),\n\t\t'--json',\n\t\t'number,title,body,state,headRefName,baseRefName,url,isDraft,mergeable,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubPullRequest>(args)\n}\n\n// Project operations\nexport async function fetchProjectList(\n\towner: string\n): Promise<GitHubProject[]> {\n\tconst result = await executeGhCommand<{ projects: GitHubProject[] }>([\n\t\t'project',\n\t\t'list',\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'100',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.projects ?? []\n}\n\nexport async function fetchProjectItems(\n\tprojectNumber: number,\n\towner: string\n): Promise<ProjectItem[]> {\n\tconst result = await executeGhCommand<{ items: ProjectItem[] }>([\n\t\t'project',\n\t\t'item-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'10000',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.items ?? []\n}\n\nexport async function fetchProjectFields(\n\tprojectNumber: number,\n\towner: string\n): Promise<{ fields: ProjectField[] }> {\n\tconst result = await executeGhCommand<{ fields: ProjectField[] }>([\n\t\t'project',\n\t\t'field-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result ?? { fields: [] }\n}\n\nexport async function updateProjectItemField(\n\titemId: string,\n\tprojectId: string,\n\tfieldId: string,\n\toptionId: string\n): Promise<void> {\n\tawait executeGhCommand([\n\t\t'project',\n\t\t'item-edit',\n\t\t'--id',\n\t\titemId,\n\t\t'--project-id',\n\t\tprojectId,\n\t\t'--field-id',\n\t\tfieldId,\n\t\t'--single-select-option-id',\n\t\toptionId,\n\t\t'--format',\n\t\t'json',\n\t])\n}\n\n// GitHub Issue Operations\n\ninterface IssueCreateResponse {\n\tnumber: string | number\n\turl: string\n}\n\n/**\n * Create a new GitHub issue\n * @param title - The issue title\n * @param body - The issue body (markdown supported)\n * @param options - Optional configuration\n * @param options.repo - Repository in format \"owner/repo\" (uses current repo if not provided)\n * @param options.labels - Array of label names to add to the issue\n * @returns Issue metadata including number and URL\n */\nexport async function createIssue(\n\ttitle: string,\n\tbody: string,\n\toptions?: { repo?: string | undefined; labels?: string[] | undefined }\n): Promise<IssueCreateResponse> {\n\tconst { repo, labels } = options ?? {}\n\n\tlogger.debug('Creating GitHub issue', { title, repo, labels })\n\n\tconst args = [\n\t\t'issue',\n\t\t'create',\n\t\t'--title',\n\t\ttitle,\n\t\t'--body',\n\t\tbody,\n\t]\n\n\t// Add repo if provided\n\tif (repo) {\n\t\targs.splice(2, 0, '--repo', repo)\n\t}\n\n\t// Add labels if provided\n\tif (labels && labels.length > 0) {\n\t\targs.push('--label', labels.join(','))\n\t}\n\n\tconst execaOptions: { timeout: number; encoding: 'utf8'; cwd?: string } = {\n\t\ttimeout: 30000,\n\t\tencoding: 'utf8',\n\t}\n\n\tif (!repo) {\n\t\texecaOptions.cwd = process.cwd()\n\t}\n\n\tconst result = await execa('gh', args, execaOptions)\n\n\t// Parse the URL from the output (format: \"https://github.com/owner/repo/issues/123\")\n\tconst urlMatch = result.stdout.trim().match(/https:\\/\\/github\\.com\\/[^/]+\\/[^/]+\\/issues\\/(\\d+)/)\n\tif (!urlMatch?.[1]) {\n\t\tthrow new Error(`Failed to parse issue URL from gh output: ${result.stdout}`)\n\t}\n\n\tconst issueNumber = parseInt(urlMatch[1], 10)\n\tconst issueUrl = urlMatch[0]\n\n\treturn {\n\t\tnumber: issueNumber,\n\t\turl: issueUrl,\n\t}\n}\n\n/**\n * @deprecated Use createIssue with options.repo instead\n * Create a new GitHub issue in a specific repository\n * @param title - Issue title\n * @param body - Issue body (markdown)\n * @param repository - Repository in format \"owner/repo\"\n * @param labels - Optional array of label names to add to the issue\n * @returns Issue number and URL\n */\nexport async function createIssueInRepo(\n\ttitle: string,\n\tbody: string,\n\trepository: string,\n\tlabels?: string[]\n): Promise<IssueCreateResponse> {\n\treturn createIssue(title, body, { repo: repository, labels })\n}\n\n// GitHub Comment Operations\n\ninterface CommentResponse {\n\tid: number\n\turl: string\n\tcreated_at?: string\n\tupdated_at?: string\n}\n\ninterface RepoInfo {\n\towner: string\n\tname: string\n}\n\n/**\n * Create a comment on a GitHub issue\n * @param issueNumber - The issue number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createIssueComment(\n\tissueNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating issue comment', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/comments`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Update an existing GitHub comment\n * @param commentId - The comment ID\n * @param body - The updated comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Updated comment metadata\n */\nexport async function updateIssueComment(\n\tcommentId: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Updating issue comment', { commentId, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/comments/${commentId}`\n\t\t: `repos/:owner/:repo/issues/comments/${commentId}`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-X',\n\t\t'PATCH',\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, updated_at: .updated_at}',\n\t])\n}\n\n/**\n * Create a comment on a GitHub pull request\n * Note: PR comments use the same endpoint as issue comments\n * @param prNumber - The PR number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createPRComment(\n\tprNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating PR comment', { prNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${prNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${prNumber}/comments`\n\n\t// PR comments use the issues endpoint\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Get repository owner and name from current directory\n * @returns Repository owner and name\n */\nexport async function getRepoInfo(): Promise<RepoInfo> {\n\tlogger.debug('Fetching repository info')\n\n\tconst result = await executeGhCommand<{ owner: { login: string }; name: string }>([\n\t\t'repo',\n\t\t'view',\n\t\t'--json',\n\t\t'owner,name',\n\t])\n\n\treturn {\n\t\towner: result.owner.login,\n\t\tname: result.name,\n\t}\n}"],"mappings":"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","names":[]}
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-YETJNRQM.js";
5
5
  import {
6
6
  launchClaude
7
- } from "./chunk-MFU53H6J.js";
7
+ } from "./chunk-XXV3UFZL.js";
8
8
  import {
9
9
  waitForKeypress
10
- } from "./chunk-JNKJ7NJV.js";
10
+ } from "./chunk-JKXJ7BGL.js";
11
11
  import {
12
12
  logger
13
13
  } from "./chunk-GEHQXLEI.js";
@@ -117,4 +117,4 @@ Press any key to open issue for editing...`;
117
117
  export {
118
118
  IssueEnhancementService
119
119
  };
120
- //# sourceMappingURL=chunk-RF2YI2XJ.js.map
120
+ //# sourceMappingURL=chunk-47KSHUCR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/IssueEnhancementService.ts"],"sourcesContent":["import type { IssueTracker } from './IssueTracker.js'\nimport type { AgentManager } from './AgentManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport { launchClaude } from '../utils/claude.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { waitForKeypress } from '../utils/prompt.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Service for enhancing and creating GitHub issues with AI assistance.\n * Extracts reusable issue enhancement logic from StartCommand.\n */\nexport class IssueEnhancementService {\n\tconstructor(\n\t\tprivate gitHubService: IssueTracker,\n\t\tprivate agentManager: AgentManager,\n\t\tprivate settingsManager: SettingsManager\n\t) {}\n\n\t/**\n\t * Validates that a description meets minimum requirements.\n\t * Requirements: >30 characters AND >2 spaces\n\t */\n\tpublic validateDescription(description: string): boolean {\n\t\tconst trimmedDescription = description.trim()\n\t\tconst spaceCount = (trimmedDescription.match(/ /g) ?? []).length\n\n\t\treturn trimmedDescription.length > 30 && spaceCount > 2\n\t}\n\n\t/**\n\t * Enhances a description using Claude AI in headless mode.\n\t * Falls back to original description if enhancement fails.\n\t */\n\tpublic async enhanceDescription(description: string): Promise<string> {\n\t\ttry {\n\t\t\tlogger.info('Enhancing description with Claude AI. This may take a moment...')\n\n\t\t\t// Load agent configurations\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst loadedAgents = await this.agentManager.loadAgents(settings)\n\t\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t\t// Call Claude in headless mode with issue enhancer agent\n\t\t\tconst prompt = `@agent-iloom-issue-enhancer\n\nTASK: Enhance the following issue description for GitHub.\n\nINPUT:\n${description}\n\nOUTPUT REQUIREMENTS:\n- Return ONLY the enhanced description markdown text\n- NO meta-commentary (no \"Here is...\", \"The enhanced...\", \"I have...\", etc)\n- NO code block markers (\\`\\`\\`)\n- NO conversational framing or acknowledgments\n- NO explanations of your work\n- Start your response immediately with the enhanced content\n\nYour response should be the raw markdown that will become the GitHub issue body.`\n\n\t\t\tconst enhanced = await launchClaude(prompt, {\n\t\t\t\theadless: true,\n\t\t\t\tmodel: 'sonnet',\n\t\t\t\tagents,\n\t\t\t})\n\n\t\t\tif (enhanced && typeof enhanced === 'string') {\n\t\t\t\tlogger.success('Description enhanced successfully')\n\t\t\t\treturn enhanced\n\t\t\t}\n\n\t\t\t// Fallback to original description\n\t\t\tlogger.warn('Claude enhancement returned empty result, using original description')\n\t\t\treturn description\n\t\t} catch (error) {\n\t\t\tlogger.warn(`Failed to enhance description: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn description\n\t\t}\n\t}\n\n\t/**\n\t * Creates a GitHub issue with title and enhanced body.\n\t * @param originalDescription - Used as the issue title\n\t * @param enhancedDescription - Used as the issue body\n\t * @param repository - Optional repository override (format: \"owner/repo\")\n\t * @param labels - Optional array of label names to add to the issue\n\t * @returns Issue number and URL\n\t */\n\tpublic async createEnhancedIssue(\n\t\toriginalDescription: string,\n\t\tenhancedDescription: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\tlogger.info('Creating GitHub issue from description...')\n\n\t\tconst result = await this.gitHubService.createIssue(\n\t\t\toriginalDescription, // Use original description as title\n\t\t\tenhancedDescription, // Use enhanced description as body\n\t\t\trepository,\n\t\t\tlabels\n\t\t)\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Waits for user keypress and opens issue in browser for review.\n\t * @param issueNumber - Issue number to open for review\n\t * @param confirm - If true, wait for additional keypress after opening browser before returning\n\t * @param repository - Optional repository to fetch issue from (format: \"owner/repo\")\n\t */\n\tpublic async waitForReviewAndOpen(issueNumber: string | number, confirm = false, repository?: string): Promise<void> {\n\t\t// Check if running in CI environment\n\t\tconst isCI = process.env.CI === 'true'\n\n\t\tif (isCI) {\n\t\t\t// In CI: Skip all interactive operations\n\t\t\tlogger.info(`Running in CI environment - skipping interactive prompts for issue #${issueNumber}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Get issue URL\n\t\tconst issueUrl = await this.gitHubService.getIssueUrl(issueNumber, repository)\n\n\t\t// Display message and wait for first keypress\n\t\tconst message = `Created issue #${issueNumber}.\nReview and edit the issue in your browser if needed.\nPress any key to open issue for editing...`\n\t\tawait waitForKeypress(message)\n\n\t\t// Open issue in browser\n\t\tawait openBrowser(issueUrl)\n\n\t\t// If confirmation required, wait for second keypress\n\t\tif (confirm) {\n\t\t\tawait waitForKeypress('Press any key to continue with loom creation...')\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAYO,IAAM,0BAAN,MAA8B;AAAA,EACpC,YACS,eACA,cACA,iBACP;AAHO;AACA;AACA;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMI,oBAAoB,aAA8B;AACxD,UAAM,qBAAqB,YAAY,KAAK;AAC5C,UAAM,cAAc,mBAAmB,MAAM,IAAI,KAAK,CAAC,GAAG;AAE1D,WAAO,mBAAmB,SAAS,MAAM,aAAa;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,mBAAmB,aAAsC;AACrE,QAAI;AACH,aAAO,KAAK,iEAAiE;AAG7E,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa;AACzD,YAAM,eAAe,MAAM,KAAK,aAAa,WAAW,QAAQ;AAChE,YAAM,SAAS,KAAK,aAAa,aAAa,YAAY;AAG1D,YAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYV,YAAM,WAAW,MAAM,aAAa,QAAQ;AAAA,QAC3C,UAAU;AAAA,QACV,OAAO;AAAA,QACP;AAAA,MACD,CAAC;AAED,UAAI,YAAY,OAAO,aAAa,UAAU;AAC7C,eAAO,QAAQ,mCAAmC;AAClD,eAAO;AAAA,MACR;AAGA,aAAO,KAAK,sEAAsE;AAClF,aAAO;AAAA,IACR,SAAS,OAAO;AACf,aAAO,KAAK,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AACxG,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,oBACZ,qBACA,qBACA,YACA,QACoD;AACpD,WAAO,KAAK,2CAA2C;AAEvD,UAAM,SAAS,MAAM,KAAK,cAAc;AAAA,MACvC;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,qBAAqB,aAA8B,UAAU,OAAO,YAAoC;AAEpH,UAAM,OAAO,QAAQ,IAAI,OAAO;AAEhC,QAAI,MAAM;AAET,aAAO,KAAK,uEAAuE,WAAW,EAAE;AAChG;AAAA,IACD;AAGA,UAAM,WAAW,MAAM,KAAK,cAAc,YAAY,aAAa,UAAU;AAG7E,UAAM,UAAU,kBAAkB,WAAW;AAAA;AAAA;AAG7C,UAAM,gBAAgB,OAAO;AAG7B,UAAM,YAAY,QAAQ;AAG1B,QAAI,SAAS;AACZ,YAAM,gBAAgB,iDAAiD;AAAA,IACxE;AAAA,EACD;AACD;","names":[]}
@@ -11,7 +11,7 @@ import {
11
11
  isPRBranch,
12
12
  isValidGitRepo,
13
13
  parseWorktreeList
14
- } from "./chunk-HBVFXN7R.js";
14
+ } from "./chunk-MAVL6PJF.js";
15
15
 
16
16
  // src/lib/GitWorktreeManager.ts
17
17
  import path from "path";
@@ -321,7 +321,7 @@ var GitWorktreeManager = class {
321
321
  */
322
322
  async findWorktreeForIssue(issueNumber) {
323
323
  const worktrees = await this.listWorktrees({ porcelain: true });
324
- const pattern = new RegExp(`(?:^|[/_-])issue-${issueNumber}(?:-|$)`);
324
+ const pattern = new RegExp(`(?:^|[/_-])issue-${issueNumber}(?:-|__|$)`);
325
325
  return worktrees.find((wt) => pattern.test(wt.branch)) ?? null;
326
326
  }
327
327
  /**
@@ -386,4 +386,4 @@ var GitWorktreeManager = class {
386
386
  export {
387
387
  GitWorktreeManager
388
388
  };
389
- //# sourceMappingURL=chunk-VETG35MF.js.map
389
+ //# sourceMappingURL=chunk-4HHRTA7Q.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/GitWorktreeManager.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport {\n type GitWorktree,\n type WorktreeCreateOptions,\n type WorktreeListOptions,\n type WorktreeValidation,\n type WorktreeStatus,\n type WorktreeCleanupOptions,\n} from '../types/worktree.js'\nimport {\n executeGitCommand,\n parseWorktreeList,\n isPRBranch,\n extractPRNumber,\n generateWorktreePath,\n isValidGitRepo,\n getCurrentBranch,\n getRepoRoot,\n hasUncommittedChanges,\n getDefaultBranch,\n findMainWorktreePathWithSettings,\n} from '../utils/git.js'\nimport type { SettingsManager } from './SettingsManager.js'\n\n/**\n * Manages Git worktrees for the iloom CLI\n * Ports functionality from bash scripts into TypeScript\n */\nexport class GitWorktreeManager {\n private readonly _workingDirectory: string\n\n constructor(workingDirectory: string = process.cwd()) {\n this._workingDirectory = workingDirectory\n }\n\n /**\n * Get the working directory for git operations (main worktree path)\n */\n get workingDirectory(): string {\n return this._workingDirectory\n }\n\n /**\n * List all worktrees in the repository\n * Defaults to porcelain format for reliable machine parsing\n * Equivalent to: git worktree list --porcelain\n */\n async listWorktrees(options: WorktreeListOptions = {}): Promise<GitWorktree[]> {\n const args = ['worktree', 'list']\n // Default to porcelain format for consistent parsing (can be disabled with porcelain: false)\n if (options.porcelain !== false) args.push('--porcelain')\n if (options.verbose) args.push('-v')\n\n const output = await executeGitCommand(args, { cwd: this._workingDirectory })\n return parseWorktreeList(output)\n }\n\n /**\n * Find worktree for a specific branch\n * Ports: find_worktree_for_branch() from find-worktree-for-branch.sh\n */\n async findWorktreeForBranch(branchName: string): Promise<GitWorktree | null> {\n const worktrees = await this.listWorktrees()\n return worktrees.find(wt => wt.branch === branchName) ?? null\n }\n\n /**\n * Check if a worktree is the main repository worktree\n * Uses findMainWorktreePathWithSettings to determine the main worktree based on settings.\n *\n * @param worktree - The worktree to check\n * @param settingsManager - SettingsManager instance for loading settings\n * @returns true if the worktree is the main worktree\n */\n async isMainWorktree(worktree: GitWorktree, settingsManager: SettingsManager): Promise<boolean> {\n const mainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, settingsManager)\n return worktree.path === mainWorktreePath\n }\n\n /**\n * Check if a worktree is a PR worktree based on naming patterns\n * Ports: is_pr_worktree() from worktree-utils.sh\n */\n isPRWorktree(worktree: GitWorktree): boolean {\n return isPRBranch(worktree.branch)\n }\n\n /**\n * Get PR number from worktree branch name\n * Ports: get_pr_number_from_worktree() from worktree-utils.sh\n */\n getPRNumberFromWorktree(worktree: GitWorktree): number | null {\n return extractPRNumber(worktree.branch)\n }\n\n /**\n * Create a new worktree\n * Ports worktree creation logic from new-branch-workflow.sh\n * @returns The absolute path to the created worktree\n */\n async createWorktree(options: WorktreeCreateOptions): Promise<string> {\n // Validate inputs\n if (!options.branch) {\n throw new Error('Branch name is required')\n }\n\n // Ensure path is absolute\n const absolutePath = path.resolve(options.path)\n\n // Check if path already exists and handle force flag\n if (await fs.pathExists(absolutePath)) {\n if (!options.force) {\n throw new Error(`Path already exists: ${absolutePath}`)\n }\n // Remove existing directory if force is true\n await fs.remove(absolutePath)\n }\n\n // Build git worktree add command\n const args = ['worktree', 'add']\n\n if (options.createBranch) {\n args.push('-b', options.branch)\n }\n\n if (options.force) {\n args.push('--force')\n }\n\n args.push(absolutePath)\n\n // Add branch name if not creating new branch\n if (!options.createBranch) {\n args.push(options.branch)\n } else if (options.baseBranch) {\n args.push(options.baseBranch)\n }\n\n await executeGitCommand(args, { cwd: this._workingDirectory })\n return absolutePath\n }\n\n /**\n * Remove a worktree and optionally clean up associated files\n * Ports worktree removal logic from cleanup-worktree.sh\n * @returns A message describing what was done (for dry-run mode)\n */\n async removeWorktree(\n worktreePath: string,\n options: WorktreeCleanupOptions = {}\n ): Promise<string | void> {\n // Validate worktree exists - use porcelain format for consistent parsing\n const worktrees = await this.listWorktrees({ porcelain: true })\n const worktree = worktrees.find(wt => wt.path === worktreePath)\n\n if (!worktree) {\n // Add debug logging to help diagnose the issue\n const { logger } = await import('../utils/logger.js')\n logger.debug(`Looking for worktree path: ${worktreePath}`)\n logger.debug(`Found ${worktrees.length} worktrees:`)\n worktrees.forEach((wt, i) => {\n logger.debug(` ${i}: path=\"${wt.path}\", branch=\"${wt.branch}\"`)\n })\n throw new Error(`Worktree not found: ${worktreePath}`)\n }\n\n // Check for uncommitted changes unless force is specified\n if (!options.force && !options.dryRun) {\n const hasChanges = await hasUncommittedChanges(worktreePath)\n if (hasChanges) {\n throw new Error(`Worktree has uncommitted changes: ${worktreePath}. Use --force to override.`)\n }\n }\n\n if (options.dryRun) {\n const actions = ['Remove worktree registration']\n if (options.removeDirectory) actions.push('Remove directory from disk')\n if (options.removeBranch) actions.push(`Remove branch: ${worktree.branch}`)\n\n return `Would perform: ${actions.join(', ')}`\n }\n\n // Remove worktree registration\n const args = ['worktree', 'remove']\n if (options.force) args.push('--force')\n args.push(worktreePath)\n\n await executeGitCommand(args, { cwd: this._workingDirectory })\n\n // Remove directory if requested\n if (options.removeDirectory && (await fs.pathExists(worktreePath))) {\n await fs.remove(worktreePath)\n }\n\n // Remove branch if requested and safe to do so\n if (options.removeBranch && !worktree.bare) {\n try {\n await executeGitCommand(['branch', '-D', worktree.branch], {\n cwd: this._workingDirectory,\n })\n } catch (error) {\n // Don't fail the whole operation if branch deletion fails\n // Just log a warning (caller can handle this)\n throw new Error(\n `Worktree removed but failed to delete branch ${worktree.branch}: ${error instanceof Error ? error.message : 'Unknown error'}`\n )\n }\n }\n }\n\n /**\n * Validate worktree state and integrity\n */\n async validateWorktree(worktreePath: string): Promise<WorktreeValidation> {\n const issues: string[] = []\n let existsOnDisk = false\n let isValidRepo = false\n let hasValidBranch = false\n\n try {\n // Check if path exists on disk\n existsOnDisk = await fs.pathExists(worktreePath)\n if (!existsOnDisk) {\n issues.push('Worktree directory does not exist on disk')\n }\n\n // Check if it's a valid Git repository\n if (existsOnDisk) {\n isValidRepo = await isValidGitRepo(worktreePath)\n if (!isValidRepo) {\n issues.push('Directory is not a valid Git repository')\n }\n }\n\n // Check if branch reference is valid\n if (isValidRepo) {\n const currentBranch = await getCurrentBranch(worktreePath)\n hasValidBranch = currentBranch !== null\n if (!hasValidBranch) {\n issues.push('Could not determine current branch')\n }\n }\n\n // Check if worktree is registered with Git\n const worktrees = await this.listWorktrees()\n const isRegistered = worktrees.some(wt => wt.path === worktreePath)\n if (!isRegistered) {\n issues.push('Worktree is not registered with Git')\n }\n } catch (error) {\n issues.push(`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`)\n }\n\n return {\n isValid: issues.length === 0,\n issues,\n existsOnDisk,\n isValidRepo,\n hasValidBranch,\n }\n }\n\n /**\n * Get detailed status information for a worktree\n */\n async getWorktreeStatus(worktreePath: string): Promise<WorktreeStatus> {\n const statusOutput = await executeGitCommand(['status', '--porcelain=v1'], {\n cwd: worktreePath,\n })\n\n let modified = 0\n let staged = 0\n let deleted = 0\n let untracked = 0\n\n const lines = statusOutput.trim().split('\\n').filter(Boolean)\n for (const line of lines) {\n const status = line.substring(0, 2)\n if (status[0] === 'M' || status[1] === 'M') modified++\n if (status[0] === 'A' || status[0] === 'D' || status[0] === 'R') staged++\n if (status[0] === 'D' || status[1] === 'D') deleted++\n if (status === '??') untracked++\n }\n\n const currentBranch = (await getCurrentBranch(worktreePath)) ?? 'unknown'\n const detached = currentBranch === 'unknown'\n\n // Get ahead/behind information\n let ahead = 0\n let behind = 0\n try {\n const aheadBehindOutput = await executeGitCommand(\n ['rev-list', '--left-right', '--count', `origin/${currentBranch}...HEAD`],\n { cwd: worktreePath }\n )\n const parts = aheadBehindOutput.trim().split('\\t')\n const behindStr = parts[0]\n const aheadStr = parts[1]\n behind = behindStr ? parseInt(behindStr, 10) || 0 : 0\n ahead = aheadStr ? parseInt(aheadStr, 10) || 0 : 0\n } catch {\n // Ignore errors for ahead/behind calculation\n }\n\n return {\n modified,\n staged,\n deleted,\n untracked,\n hasChanges: modified + staged + deleted + untracked > 0,\n branch: currentBranch,\n detached,\n ahead,\n behind,\n }\n }\n\n /**\n * Generate a suggested worktree path for a branch\n */\n generateWorktreePath(\n branchName: string,\n customRoot?: string,\n options?: { isPR?: boolean; prNumber?: number; prefix?: string }\n ): string {\n const root = customRoot ?? this._workingDirectory\n return generateWorktreePath(branchName, root, options)\n }\n\n /**\n * Sanitize a branch name for use as a directory name\n * Replaces slashes with dashes and removes invalid filesystem characters\n * Ports logic from bash script line 593: ${BRANCH_NAME//\\\\//-}\n */\n sanitizeBranchName(branchName: string): string {\n return branchName\n .replace(/\\//g, '-') // Replace slashes with dashes\n .replace(/[^a-zA-Z0-9-]/g, '-') // Replace invalid chars (including underscores) with dashes\n .replace(/-+/g, '-') // Collapse multiple dashes\n .replace(/^-|-$/g, '') // Remove leading/trailing dashes\n .toLowerCase()\n }\n\n /**\n * Check if repository is in a valid state for worktree operations\n */\n async isRepoReady(): Promise<boolean> {\n try {\n const repoRoot = await getRepoRoot(this._workingDirectory)\n return repoRoot !== null\n } catch {\n return false\n }\n }\n\n /**\n * Get repository information\n */\n async getRepoInfo(): Promise<{\n root: string | null\n defaultBranch: string\n currentBranch: string | null\n }> {\n const root = await getRepoRoot(this._workingDirectory)\n const defaultBranch = await getDefaultBranch(this._workingDirectory)\n const currentBranch = await getCurrentBranch(this._workingDirectory)\n\n return {\n root,\n defaultBranch,\n currentBranch,\n }\n }\n\n /**\n * Prune stale worktree entries (worktrees that no longer exist on disk)\n */\n async pruneWorktrees(): Promise<void> {\n await executeGitCommand(['worktree', 'prune', '-v'], { cwd: this._workingDirectory })\n }\n\n /**\n * Lock a worktree to prevent it from being pruned or moved\n */\n async lockWorktree(worktreePath: string, reason?: string): Promise<void> {\n const args = ['worktree', 'lock', worktreePath]\n if (reason) args.push('--reason', reason)\n\n await executeGitCommand(args, { cwd: this._workingDirectory })\n }\n\n /**\n * Unlock a previously locked worktree\n */\n async unlockWorktree(worktreePath: string): Promise<void> {\n await executeGitCommand(['worktree', 'unlock', worktreePath], { cwd: this._workingDirectory })\n }\n\n /**\n * Find worktrees matching an identifier (branch name, path, or PR number)\n */\n async findWorktreesByIdentifier(identifier: string): Promise<GitWorktree[]> {\n const worktrees = await this.listWorktrees({ porcelain: true })\n return worktrees.filter(\n wt =>\n wt.branch.includes(identifier) ||\n wt.path.includes(identifier) ||\n this.getPRNumberFromWorktree(wt)?.toString() === identifier\n )\n }\n\n /**\n * Find worktree for a specific issue number using exact pattern matching\n * Matches: issue-{N} at start OR after /, -, _ (but NOT issue-{N}X where X is a digit)\n * Supports patterns like: issue-44, feat/issue-44-feature, feat-issue-44, bugfix_issue-44, etc.\n * Avoids false matches like: tissue-44, myissue-44\n * Ports: find_existing_worktree() from bash script lines 131-165\n */\n async findWorktreeForIssue(issueNumber: number): Promise<GitWorktree | null> {\n const worktrees = await this.listWorktrees({ porcelain: true })\n\n // Pattern: starts with 'issue-{N}' OR has '/issue-{N}', '-issue-{N}', '_issue-{N}' but not 'issue-{N}{digit}'\n const pattern = new RegExp(`(?:^|[/_-])issue-${issueNumber}(?:-|$)`)\n\n return worktrees.find(wt => pattern.test(wt.branch)) ?? null\n }\n\n /**\n * Find worktree for a specific PR by branch name\n * Ports: find_existing_worktree() for PR type from bash script lines 149-160\n */\n async findWorktreeForPR(prNumber: number, branchName: string): Promise<GitWorktree | null> {\n const worktrees = await this.listWorktrees({ porcelain: true })\n\n // Find by exact branch name match (prioritized)\n const byBranch = worktrees.find(wt => wt.branch === branchName)\n if (byBranch) return byBranch\n\n // Also check directory name pattern: *_pr_{N}\n const pathPattern = new RegExp(`_pr_${prNumber}$`)\n return worktrees.find(wt => pathPattern.test(wt.path)) ?? null\n }\n\n /**\n * Remove multiple worktrees\n * Returns a summary of successes and failures\n * Automatically filters out the main worktree\n *\n * @param worktrees - Array of worktrees to remove\n * @param settingsManager - SettingsManager instance for determining main worktree\n * @param options - Cleanup options\n */\n async removeWorktrees(\n worktrees: GitWorktree[],\n settingsManager: SettingsManager,\n options: WorktreeCleanupOptions = {}\n ): Promise<{\n successes: Array<{ worktree: GitWorktree }>\n failures: Array<{ worktree: GitWorktree; error: string }>\n skipped: Array<{ worktree: GitWorktree; reason: string }>\n }> {\n const successes: Array<{ worktree: GitWorktree }> = []\n const failures: Array<{ worktree: GitWorktree; error: string }> = []\n const skipped: Array<{ worktree: GitWorktree; reason: string }> = []\n\n for (const worktree of worktrees) {\n // Skip main worktree\n if (await this.isMainWorktree(worktree, settingsManager)) {\n skipped.push({ worktree, reason: 'Cannot remove main worktree' })\n continue\n }\n\n try {\n await this.removeWorktree(worktree.path, {\n ...options,\n removeDirectory: true,\n })\n successes.push({ worktree })\n } catch (error) {\n failures.push({\n worktree,\n error: error instanceof Error ? error.message : 'Unknown error',\n })\n }\n }\n\n return { successes, failures, skipped }\n }\n\n /**\n * Format worktree information for display\n */\n formatWorktree(worktree: GitWorktree): {\n title: string\n path: string\n commit: string\n } {\n const prNumber = this.getPRNumberFromWorktree(worktree)\n const prLabel = prNumber ? ` (PR #${prNumber})` : ''\n const bareLabel = worktree.bare ? ' [main]' : ''\n\n return {\n title: `${worktree.branch}${prLabel}${bareLabel}`,\n path: worktree.path,\n commit: worktree.commit.substring(0, 7),\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/lib/GitWorktreeManager.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport {\n type GitWorktree,\n type WorktreeCreateOptions,\n type WorktreeListOptions,\n type WorktreeValidation,\n type WorktreeStatus,\n type WorktreeCleanupOptions,\n} from '../types/worktree.js'\nimport {\n executeGitCommand,\n parseWorktreeList,\n isPRBranch,\n extractPRNumber,\n generateWorktreePath,\n isValidGitRepo,\n getCurrentBranch,\n getRepoRoot,\n hasUncommittedChanges,\n getDefaultBranch,\n findMainWorktreePathWithSettings,\n} from '../utils/git.js'\nimport type { SettingsManager } from './SettingsManager.js'\n\n/**\n * Manages Git worktrees for the iloom CLI\n * Ports functionality from bash scripts into TypeScript\n */\nexport class GitWorktreeManager {\n private readonly _workingDirectory: string\n\n constructor(workingDirectory: string = process.cwd()) {\n this._workingDirectory = workingDirectory\n }\n\n /**\n * Get the working directory for git operations (main worktree path)\n */\n get workingDirectory(): string {\n return this._workingDirectory\n }\n\n /**\n * List all worktrees in the repository\n * Defaults to porcelain format for reliable machine parsing\n * Equivalent to: git worktree list --porcelain\n */\n async listWorktrees(options: WorktreeListOptions = {}): Promise<GitWorktree[]> {\n const args = ['worktree', 'list']\n // Default to porcelain format for consistent parsing (can be disabled with porcelain: false)\n if (options.porcelain !== false) args.push('--porcelain')\n if (options.verbose) args.push('-v')\n\n const output = await executeGitCommand(args, { cwd: this._workingDirectory })\n return parseWorktreeList(output)\n }\n\n /**\n * Find worktree for a specific branch\n * Ports: find_worktree_for_branch() from find-worktree-for-branch.sh\n */\n async findWorktreeForBranch(branchName: string): Promise<GitWorktree | null> {\n const worktrees = await this.listWorktrees()\n return worktrees.find(wt => wt.branch === branchName) ?? null\n }\n\n /**\n * Check if a worktree is the main repository worktree\n * Uses findMainWorktreePathWithSettings to determine the main worktree based on settings.\n *\n * @param worktree - The worktree to check\n * @param settingsManager - SettingsManager instance for loading settings\n * @returns true if the worktree is the main worktree\n */\n async isMainWorktree(worktree: GitWorktree, settingsManager: SettingsManager): Promise<boolean> {\n const mainWorktreePath = await findMainWorktreePathWithSettings(worktree.path, settingsManager)\n return worktree.path === mainWorktreePath\n }\n\n /**\n * Check if a worktree is a PR worktree based on naming patterns\n * Ports: is_pr_worktree() from worktree-utils.sh\n */\n isPRWorktree(worktree: GitWorktree): boolean {\n return isPRBranch(worktree.branch)\n }\n\n /**\n * Get PR number from worktree branch name\n * Ports: get_pr_number_from_worktree() from worktree-utils.sh\n */\n getPRNumberFromWorktree(worktree: GitWorktree): number | null {\n return extractPRNumber(worktree.branch)\n }\n\n /**\n * Create a new worktree\n * Ports worktree creation logic from new-branch-workflow.sh\n * @returns The absolute path to the created worktree\n */\n async createWorktree(options: WorktreeCreateOptions): Promise<string> {\n // Validate inputs\n if (!options.branch) {\n throw new Error('Branch name is required')\n }\n\n // Ensure path is absolute\n const absolutePath = path.resolve(options.path)\n\n // Check if path already exists and handle force flag\n if (await fs.pathExists(absolutePath)) {\n if (!options.force) {\n throw new Error(`Path already exists: ${absolutePath}`)\n }\n // Remove existing directory if force is true\n await fs.remove(absolutePath)\n }\n\n // Build git worktree add command\n const args = ['worktree', 'add']\n\n if (options.createBranch) {\n args.push('-b', options.branch)\n }\n\n if (options.force) {\n args.push('--force')\n }\n\n args.push(absolutePath)\n\n // Add branch name if not creating new branch\n if (!options.createBranch) {\n args.push(options.branch)\n } else if (options.baseBranch) {\n args.push(options.baseBranch)\n }\n\n await executeGitCommand(args, { cwd: this._workingDirectory })\n return absolutePath\n }\n\n /**\n * Remove a worktree and optionally clean up associated files\n * Ports worktree removal logic from cleanup-worktree.sh\n * @returns A message describing what was done (for dry-run mode)\n */\n async removeWorktree(\n worktreePath: string,\n options: WorktreeCleanupOptions = {}\n ): Promise<string | void> {\n // Validate worktree exists - use porcelain format for consistent parsing\n const worktrees = await this.listWorktrees({ porcelain: true })\n const worktree = worktrees.find(wt => wt.path === worktreePath)\n\n if (!worktree) {\n // Add debug logging to help diagnose the issue\n const { logger } = await import('../utils/logger.js')\n logger.debug(`Looking for worktree path: ${worktreePath}`)\n logger.debug(`Found ${worktrees.length} worktrees:`)\n worktrees.forEach((wt, i) => {\n logger.debug(` ${i}: path=\"${wt.path}\", branch=\"${wt.branch}\"`)\n })\n throw new Error(`Worktree not found: ${worktreePath}`)\n }\n\n // Check for uncommitted changes unless force is specified\n if (!options.force && !options.dryRun) {\n const hasChanges = await hasUncommittedChanges(worktreePath)\n if (hasChanges) {\n throw new Error(`Worktree has uncommitted changes: ${worktreePath}. Use --force to override.`)\n }\n }\n\n if (options.dryRun) {\n const actions = ['Remove worktree registration']\n if (options.removeDirectory) actions.push('Remove directory from disk')\n if (options.removeBranch) actions.push(`Remove branch: ${worktree.branch}`)\n\n return `Would perform: ${actions.join(', ')}`\n }\n\n // Remove worktree registration\n const args = ['worktree', 'remove']\n if (options.force) args.push('--force')\n args.push(worktreePath)\n\n await executeGitCommand(args, { cwd: this._workingDirectory })\n\n // Remove directory if requested\n if (options.removeDirectory && (await fs.pathExists(worktreePath))) {\n await fs.remove(worktreePath)\n }\n\n // Remove branch if requested and safe to do so\n if (options.removeBranch && !worktree.bare) {\n try {\n await executeGitCommand(['branch', '-D', worktree.branch], {\n cwd: this._workingDirectory,\n })\n } catch (error) {\n // Don't fail the whole operation if branch deletion fails\n // Just log a warning (caller can handle this)\n throw new Error(\n `Worktree removed but failed to delete branch ${worktree.branch}: ${error instanceof Error ? error.message : 'Unknown error'}`\n )\n }\n }\n }\n\n /**\n * Validate worktree state and integrity\n */\n async validateWorktree(worktreePath: string): Promise<WorktreeValidation> {\n const issues: string[] = []\n let existsOnDisk = false\n let isValidRepo = false\n let hasValidBranch = false\n\n try {\n // Check if path exists on disk\n existsOnDisk = await fs.pathExists(worktreePath)\n if (!existsOnDisk) {\n issues.push('Worktree directory does not exist on disk')\n }\n\n // Check if it's a valid Git repository\n if (existsOnDisk) {\n isValidRepo = await isValidGitRepo(worktreePath)\n if (!isValidRepo) {\n issues.push('Directory is not a valid Git repository')\n }\n }\n\n // Check if branch reference is valid\n if (isValidRepo) {\n const currentBranch = await getCurrentBranch(worktreePath)\n hasValidBranch = currentBranch !== null\n if (!hasValidBranch) {\n issues.push('Could not determine current branch')\n }\n }\n\n // Check if worktree is registered with Git\n const worktrees = await this.listWorktrees()\n const isRegistered = worktrees.some(wt => wt.path === worktreePath)\n if (!isRegistered) {\n issues.push('Worktree is not registered with Git')\n }\n } catch (error) {\n issues.push(`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`)\n }\n\n return {\n isValid: issues.length === 0,\n issues,\n existsOnDisk,\n isValidRepo,\n hasValidBranch,\n }\n }\n\n /**\n * Get detailed status information for a worktree\n */\n async getWorktreeStatus(worktreePath: string): Promise<WorktreeStatus> {\n const statusOutput = await executeGitCommand(['status', '--porcelain=v1'], {\n cwd: worktreePath,\n })\n\n let modified = 0\n let staged = 0\n let deleted = 0\n let untracked = 0\n\n const lines = statusOutput.trim().split('\\n').filter(Boolean)\n for (const line of lines) {\n const status = line.substring(0, 2)\n if (status[0] === 'M' || status[1] === 'M') modified++\n if (status[0] === 'A' || status[0] === 'D' || status[0] === 'R') staged++\n if (status[0] === 'D' || status[1] === 'D') deleted++\n if (status === '??') untracked++\n }\n\n const currentBranch = (await getCurrentBranch(worktreePath)) ?? 'unknown'\n const detached = currentBranch === 'unknown'\n\n // Get ahead/behind information\n let ahead = 0\n let behind = 0\n try {\n const aheadBehindOutput = await executeGitCommand(\n ['rev-list', '--left-right', '--count', `origin/${currentBranch}...HEAD`],\n { cwd: worktreePath }\n )\n const parts = aheadBehindOutput.trim().split('\\t')\n const behindStr = parts[0]\n const aheadStr = parts[1]\n behind = behindStr ? parseInt(behindStr, 10) || 0 : 0\n ahead = aheadStr ? parseInt(aheadStr, 10) || 0 : 0\n } catch {\n // Ignore errors for ahead/behind calculation\n }\n\n return {\n modified,\n staged,\n deleted,\n untracked,\n hasChanges: modified + staged + deleted + untracked > 0,\n branch: currentBranch,\n detached,\n ahead,\n behind,\n }\n }\n\n /**\n * Generate a suggested worktree path for a branch\n */\n generateWorktreePath(\n branchName: string,\n customRoot?: string,\n options?: { isPR?: boolean; prNumber?: number; prefix?: string }\n ): string {\n const root = customRoot ?? this._workingDirectory\n return generateWorktreePath(branchName, root, options)\n }\n\n /**\n * Sanitize a branch name for use as a directory name\n * Replaces slashes with dashes and removes invalid filesystem characters\n * Ports logic from bash script line 593: ${BRANCH_NAME//\\\\//-}\n */\n sanitizeBranchName(branchName: string): string {\n return branchName\n .replace(/\\//g, '-') // Replace slashes with dashes\n .replace(/[^a-zA-Z0-9-]/g, '-') // Replace invalid chars (including underscores) with dashes\n .replace(/-+/g, '-') // Collapse multiple dashes\n .replace(/^-|-$/g, '') // Remove leading/trailing dashes\n .toLowerCase()\n }\n\n /**\n * Check if repository is in a valid state for worktree operations\n */\n async isRepoReady(): Promise<boolean> {\n try {\n const repoRoot = await getRepoRoot(this._workingDirectory)\n return repoRoot !== null\n } catch {\n return false\n }\n }\n\n /**\n * Get repository information\n */\n async getRepoInfo(): Promise<{\n root: string | null\n defaultBranch: string\n currentBranch: string | null\n }> {\n const root = await getRepoRoot(this._workingDirectory)\n const defaultBranch = await getDefaultBranch(this._workingDirectory)\n const currentBranch = await getCurrentBranch(this._workingDirectory)\n\n return {\n root,\n defaultBranch,\n currentBranch,\n }\n }\n\n /**\n * Prune stale worktree entries (worktrees that no longer exist on disk)\n */\n async pruneWorktrees(): Promise<void> {\n await executeGitCommand(['worktree', 'prune', '-v'], { cwd: this._workingDirectory })\n }\n\n /**\n * Lock a worktree to prevent it from being pruned or moved\n */\n async lockWorktree(worktreePath: string, reason?: string): Promise<void> {\n const args = ['worktree', 'lock', worktreePath]\n if (reason) args.push('--reason', reason)\n\n await executeGitCommand(args, { cwd: this._workingDirectory })\n }\n\n /**\n * Unlock a previously locked worktree\n */\n async unlockWorktree(worktreePath: string): Promise<void> {\n await executeGitCommand(['worktree', 'unlock', worktreePath], { cwd: this._workingDirectory })\n }\n\n /**\n * Find worktrees matching an identifier (branch name, path, or PR number)\n */\n async findWorktreesByIdentifier(identifier: string): Promise<GitWorktree[]> {\n const worktrees = await this.listWorktrees({ porcelain: true })\n return worktrees.filter(\n wt =>\n wt.branch.includes(identifier) ||\n wt.path.includes(identifier) ||\n this.getPRNumberFromWorktree(wt)?.toString() === identifier\n )\n }\n\n /**\n * Find worktree for a specific issue number using exact pattern matching\n * Matches: issue-{N} at start OR after /, -, _ (but NOT issue-{N}X where X is a digit)\n * Supports patterns like: issue-44, feat/issue-44-feature, feat-issue-44, bugfix_issue-44, etc.\n * Avoids false matches like: tissue-44, myissue-44\n * Ports: find_existing_worktree() from bash script lines 131-165\n */\n async findWorktreeForIssue(issueNumber: string | number): Promise<GitWorktree | null> {\n const worktrees = await this.listWorktrees({ porcelain: true })\n\n // Pattern: starts with 'issue-{N}' OR has '/issue-{N}', '-issue-{N}', '_issue-{N}' but not 'issue-{N}{digit}'\n const pattern = new RegExp(`(?:^|[/_-])issue-${issueNumber}(?:-|__|$)`)\n\n return worktrees.find(wt => pattern.test(wt.branch)) ?? null\n }\n\n /**\n * Find worktree for a specific PR by branch name\n * Ports: find_existing_worktree() for PR type from bash script lines 149-160\n */\n async findWorktreeForPR(prNumber: number, branchName: string): Promise<GitWorktree | null> {\n const worktrees = await this.listWorktrees({ porcelain: true })\n\n // Find by exact branch name match (prioritized)\n const byBranch = worktrees.find(wt => wt.branch === branchName)\n if (byBranch) return byBranch\n\n // Also check directory name pattern: *_pr_{N}\n const pathPattern = new RegExp(`_pr_${prNumber}$`)\n return worktrees.find(wt => pathPattern.test(wt.path)) ?? null\n }\n\n /**\n * Remove multiple worktrees\n * Returns a summary of successes and failures\n * Automatically filters out the main worktree\n *\n * @param worktrees - Array of worktrees to remove\n * @param settingsManager - SettingsManager instance for determining main worktree\n * @param options - Cleanup options\n */\n async removeWorktrees(\n worktrees: GitWorktree[],\n settingsManager: SettingsManager,\n options: WorktreeCleanupOptions = {}\n ): Promise<{\n successes: Array<{ worktree: GitWorktree }>\n failures: Array<{ worktree: GitWorktree; error: string }>\n skipped: Array<{ worktree: GitWorktree; reason: string }>\n }> {\n const successes: Array<{ worktree: GitWorktree }> = []\n const failures: Array<{ worktree: GitWorktree; error: string }> = []\n const skipped: Array<{ worktree: GitWorktree; reason: string }> = []\n\n for (const worktree of worktrees) {\n // Skip main worktree\n if (await this.isMainWorktree(worktree, settingsManager)) {\n skipped.push({ worktree, reason: 'Cannot remove main worktree' })\n continue\n }\n\n try {\n await this.removeWorktree(worktree.path, {\n ...options,\n removeDirectory: true,\n })\n successes.push({ worktree })\n } catch (error) {\n failures.push({\n worktree,\n error: error instanceof Error ? error.message : 'Unknown error',\n })\n }\n }\n\n return { successes, failures, skipped }\n }\n\n /**\n * Format worktree information for display\n */\n formatWorktree(worktree: GitWorktree): {\n title: string\n path: string\n commit: string\n } {\n const prNumber = this.getPRNumberFromWorktree(worktree)\n const prLabel = prNumber ? ` (PR #${prNumber})` : ''\n const bareLabel = worktree.bare ? ' [main]' : ''\n\n return {\n title: `${worktree.branch}${prLabel}${bareLabel}`,\n path: worktree.path,\n commit: worktree.commit.substring(0, 7),\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AA4BR,IAAM,qBAAN,MAAyB;AAAA,EAG9B,YAAY,mBAA2B,QAAQ,IAAI,GAAG;AACpD,SAAK,oBAAoB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,mBAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc,UAA+B,CAAC,GAA2B;AAC7E,UAAM,OAAO,CAAC,YAAY,MAAM;AAEhC,QAAI,QAAQ,cAAc,MAAO,MAAK,KAAK,aAAa;AACxD,QAAI,QAAQ,QAAS,MAAK,KAAK,IAAI;AAEnC,UAAM,SAAS,MAAM,kBAAkB,MAAM,EAAE,KAAK,KAAK,kBAAkB,CAAC;AAC5E,WAAO,kBAAkB,MAAM;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,sBAAsB,YAAiD;AAC3E,UAAM,YAAY,MAAM,KAAK,cAAc;AAC3C,WAAO,UAAU,KAAK,QAAM,GAAG,WAAW,UAAU,KAAK;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,eAAe,UAAuB,iBAAoD;AAC9F,UAAM,mBAAmB,MAAM,iCAAiC,SAAS,MAAM,eAAe;AAC9F,WAAO,SAAS,SAAS;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,UAAgC;AAC3C,WAAO,WAAW,SAAS,MAAM;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,wBAAwB,UAAsC;AAC5D,WAAO,gBAAgB,SAAS,MAAM;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAe,SAAiD;AAEpE,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAGA,UAAM,eAAe,KAAK,QAAQ,QAAQ,IAAI;AAG9C,QAAI,MAAM,GAAG,WAAW,YAAY,GAAG;AACrC,UAAI,CAAC,QAAQ,OAAO;AAClB,cAAM,IAAI,MAAM,wBAAwB,YAAY,EAAE;AAAA,MACxD;AAEA,YAAM,GAAG,OAAO,YAAY;AAAA,IAC9B;AAGA,UAAM,OAAO,CAAC,YAAY,KAAK;AAE/B,QAAI,QAAQ,cAAc;AACxB,WAAK,KAAK,MAAM,QAAQ,MAAM;AAAA,IAChC;AAEA,QAAI,QAAQ,OAAO;AACjB,WAAK,KAAK,SAAS;AAAA,IACrB;AAEA,SAAK,KAAK,YAAY;AAGtB,QAAI,CAAC,QAAQ,cAAc;AACzB,WAAK,KAAK,QAAQ,MAAM;AAAA,IAC1B,WAAW,QAAQ,YAAY;AAC7B,WAAK,KAAK,QAAQ,UAAU;AAAA,IAC9B;AAEA,UAAM,kBAAkB,MAAM,EAAE,KAAK,KAAK,kBAAkB,CAAC;AAC7D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eACJ,cACA,UAAkC,CAAC,GACX;AAExB,UAAM,YAAY,MAAM,KAAK,cAAc,EAAE,WAAW,KAAK,CAAC;AAC9D,UAAM,WAAW,UAAU,KAAK,QAAM,GAAG,SAAS,YAAY;AAE9D,QAAI,CAAC,UAAU;AAEb,YAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAoB;AACpD,aAAO,MAAM,8BAA8B,YAAY,EAAE;AACzD,aAAO,MAAM,SAAS,UAAU,MAAM,aAAa;AACnD,gBAAU,QAAQ,CAAC,IAAI,MAAM;AAC3B,eAAO,MAAM,KAAK,CAAC,WAAW,GAAG,IAAI,cAAc,GAAG,MAAM,GAAG;AAAA,MACjE,CAAC;AACD,YAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;AAAA,IACvD;AAGA,QAAI,CAAC,QAAQ,SAAS,CAAC,QAAQ,QAAQ;AACrC,YAAM,aAAa,MAAM,sBAAsB,YAAY;AAC3D,UAAI,YAAY;AACd,cAAM,IAAI,MAAM,qCAAqC,YAAY,4BAA4B;AAAA,MAC/F;AAAA,IACF;AAEA,QAAI,QAAQ,QAAQ;AAClB,YAAM,UAAU,CAAC,8BAA8B;AAC/C,UAAI,QAAQ,gBAAiB,SAAQ,KAAK,4BAA4B;AACtE,UAAI,QAAQ,aAAc,SAAQ,KAAK,kBAAkB,SAAS,MAAM,EAAE;AAE1E,aAAO,kBAAkB,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC7C;AAGA,UAAM,OAAO,CAAC,YAAY,QAAQ;AAClC,QAAI,QAAQ,MAAO,MAAK,KAAK,SAAS;AACtC,SAAK,KAAK,YAAY;AAEtB,UAAM,kBAAkB,MAAM,EAAE,KAAK,KAAK,kBAAkB,CAAC;AAG7D,QAAI,QAAQ,mBAAoB,MAAM,GAAG,WAAW,YAAY,GAAI;AAClE,YAAM,GAAG,OAAO,YAAY;AAAA,IAC9B;AAGA,QAAI,QAAQ,gBAAgB,CAAC,SAAS,MAAM;AAC1C,UAAI;AACF,cAAM,kBAAkB,CAAC,UAAU,MAAM,SAAS,MAAM,GAAG;AAAA,UACzD,KAAK,KAAK;AAAA,QACZ,CAAC;AAAA,MACH,SAAS,OAAO;AAGd,cAAM,IAAI;AAAA,UACR,gDAAgD,SAAS,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,QAC9H;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,cAAmD;AACxE,UAAM,SAAmB,CAAC;AAC1B,QAAI,eAAe;AACnB,QAAI,cAAc;AAClB,QAAI,iBAAiB;AAErB,QAAI;AAEF,qBAAe,MAAM,GAAG,WAAW,YAAY;AAC/C,UAAI,CAAC,cAAc;AACjB,eAAO,KAAK,2CAA2C;AAAA,MACzD;AAGA,UAAI,cAAc;AAChB,sBAAc,MAAM,eAAe,YAAY;AAC/C,YAAI,CAAC,aAAa;AAChB,iBAAO,KAAK,yCAAyC;AAAA,QACvD;AAAA,MACF;AAGA,UAAI,aAAa;AACf,cAAM,gBAAgB,MAAM,iBAAiB,YAAY;AACzD,yBAAiB,kBAAkB;AACnC,YAAI,CAAC,gBAAgB;AACnB,iBAAO,KAAK,oCAAoC;AAAA,QAClD;AAAA,MACF;AAGA,YAAM,YAAY,MAAM,KAAK,cAAc;AAC3C,YAAM,eAAe,UAAU,KAAK,QAAM,GAAG,SAAS,YAAY;AAClE,UAAI,CAAC,cAAc;AACjB,eAAO,KAAK,qCAAqC;AAAA,MACnD;AAAA,IACF,SAAS,OAAO;AACd,aAAO,KAAK,qBAAqB,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AAAA,IAC7F;AAEA,WAAO;AAAA,MACL,SAAS,OAAO,WAAW;AAAA,MAC3B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,cAA+C;AACrE,UAAM,eAAe,MAAM,kBAAkB,CAAC,UAAU,gBAAgB,GAAG;AAAA,MACzE,KAAK;AAAA,IACP,CAAC;AAED,QAAI,WAAW;AACf,QAAI,SAAS;AACb,QAAI,UAAU;AACd,QAAI,YAAY;AAEhB,UAAM,QAAQ,aAAa,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAC5D,eAAW,QAAQ,OAAO;AACxB,YAAM,SAAS,KAAK,UAAU,GAAG,CAAC;AAClC,UAAI,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,IAAK;AAC5C,UAAI,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,IAAK;AACjE,UAAI,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,IAAK;AAC5C,UAAI,WAAW,KAAM;AAAA,IACvB;AAEA,UAAM,gBAAiB,MAAM,iBAAiB,YAAY,KAAM;AAChE,UAAM,WAAW,kBAAkB;AAGnC,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,QAAI;AACF,YAAM,oBAAoB,MAAM;AAAA,QAC9B,CAAC,YAAY,gBAAgB,WAAW,UAAU,aAAa,SAAS;AAAA,QACxE,EAAE,KAAK,aAAa;AAAA,MACtB;AACA,YAAM,QAAQ,kBAAkB,KAAK,EAAE,MAAM,GAAI;AACjD,YAAM,YAAY,MAAM,CAAC;AACzB,YAAM,WAAW,MAAM,CAAC;AACxB,eAAS,YAAY,SAAS,WAAW,EAAE,KAAK,IAAI;AACpD,cAAQ,WAAW,SAAS,UAAU,EAAE,KAAK,IAAI;AAAA,IACnD,QAAQ;AAAA,IAER;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,WAAW,SAAS,UAAU,YAAY;AAAA,MACtD,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,qBACE,YACA,YACA,SACQ;AACR,UAAM,OAAO,cAAc,KAAK;AAChC,WAAO,qBAAqB,YAAY,MAAM,OAAO;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB,YAA4B;AAC7C,WAAO,WACJ,QAAQ,OAAO,GAAG,EAClB,QAAQ,kBAAkB,GAAG,EAC7B,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE,EACpB,YAAY;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAgC;AACpC,QAAI;AACF,YAAM,WAAW,MAAM,YAAY,KAAK,iBAAiB;AACzD,aAAO,aAAa;AAAA,IACtB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAIH;AACD,UAAM,OAAO,MAAM,YAAY,KAAK,iBAAiB;AACrD,UAAM,gBAAgB,MAAM,iBAAiB,KAAK,iBAAiB;AACnE,UAAM,gBAAgB,MAAM,iBAAiB,KAAK,iBAAiB;AAEnE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAgC;AACpC,UAAM,kBAAkB,CAAC,YAAY,SAAS,IAAI,GAAG,EAAE,KAAK,KAAK,kBAAkB,CAAC;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,cAAsB,QAAgC;AACvE,UAAM,OAAO,CAAC,YAAY,QAAQ,YAAY;AAC9C,QAAI,OAAQ,MAAK,KAAK,YAAY,MAAM;AAExC,UAAM,kBAAkB,MAAM,EAAE,KAAK,KAAK,kBAAkB,CAAC;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,cAAqC;AACxD,UAAM,kBAAkB,CAAC,YAAY,UAAU,YAAY,GAAG,EAAE,KAAK,KAAK,kBAAkB,CAAC;AAAA,EAC/F;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,0BAA0B,YAA4C;AAC1E,UAAM,YAAY,MAAM,KAAK,cAAc,EAAE,WAAW,KAAK,CAAC;AAC9D,WAAO,UAAU;AAAA,MACf,QAAG;AArZT;AAsZQ,kBAAG,OAAO,SAAS,UAAU,KAC7B,GAAG,KAAK,SAAS,UAAU,OAC3B,UAAK,wBAAwB,EAAE,MAA/B,mBAAkC,gBAAe;AAAA;AAAA,IACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,qBAAqB,aAA2D;AACpF,UAAM,YAAY,MAAM,KAAK,cAAc,EAAE,WAAW,KAAK,CAAC;AAG9D,UAAM,UAAU,IAAI,OAAO,oBAAoB,WAAW,YAAY;AAEtE,WAAO,UAAU,KAAK,QAAM,QAAQ,KAAK,GAAG,MAAM,CAAC,KAAK;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,UAAkB,YAAiD;AACzF,UAAM,YAAY,MAAM,KAAK,cAAc,EAAE,WAAW,KAAK,CAAC;AAG9D,UAAM,WAAW,UAAU,KAAK,QAAM,GAAG,WAAW,UAAU;AAC9D,QAAI,SAAU,QAAO;AAGrB,UAAM,cAAc,IAAI,OAAO,OAAO,QAAQ,GAAG;AACjD,WAAO,UAAU,KAAK,QAAM,YAAY,KAAK,GAAG,IAAI,CAAC,KAAK;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,gBACJ,WACA,iBACA,UAAkC,CAAC,GAKlC;AACD,UAAM,YAA8C,CAAC;AACrD,UAAM,WAA4D,CAAC;AACnE,UAAM,UAA4D,CAAC;AAEnE,eAAW,YAAY,WAAW;AAEhC,UAAI,MAAM,KAAK,eAAe,UAAU,eAAe,GAAG;AACxD,gBAAQ,KAAK,EAAE,UAAU,QAAQ,8BAA8B,CAAC;AAChE;AAAA,MACF;AAEA,UAAI;AACF,cAAM,KAAK,eAAe,SAAS,MAAM;AAAA,UACvC,GAAG;AAAA,UACH,iBAAiB;AAAA,QACnB,CAAC;AACD,kBAAU,KAAK,EAAE,SAAS,CAAC;AAAA,MAC7B,SAAS,OAAO;AACd,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAClD,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO,EAAE,WAAW,UAAU,QAAQ;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,UAIb;AACA,UAAM,WAAW,KAAK,wBAAwB,QAAQ;AACtD,UAAM,UAAU,WAAW,SAAS,QAAQ,MAAM;AAClD,UAAM,YAAY,SAAS,OAAO,YAAY;AAE9C,WAAO;AAAA,MACL,OAAO,GAAG,SAAS,MAAM,GAAG,OAAO,GAAG,SAAS;AAAA,MAC/C,MAAM,SAAS;AAAA,MACf,QAAQ,SAAS,OAAO,UAAU,GAAG,CAAC;AAAA,IACxC;AAAA,EACF;AACF;","names":[]}