@iloom/cli 0.13.0-beta.0 → 0.13.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 (173) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -18
  3. package/dist/{ClaudeContextManager-RRGREEZQ.js → ClaudeContextManager-ZH6LEA5I.js} +5 -5
  4. package/dist/{ClaudeService-LEPW6QAC.js → ClaudeService-YR66WXZN.js} +4 -4
  5. package/dist/{IssueTrackerFactory-KE2BDCLC.js → IssueTrackerFactory-O2ZBA666.js} +3 -3
  6. package/dist/{LoomLauncher-GKQMR5E6.js → LoomLauncher-V54ENBEF.js} +5 -5
  7. package/dist/{MetadataManager-V4LSJ2PB.js → MetadataManager-HHE6LQF2.js} +2 -2
  8. package/dist/{PromptTemplateManager-I75WKXM4.js → PromptTemplateManager-4RFELNYY.js} +2 -2
  9. package/dist/README.md +3 -18
  10. package/dist/{SettingsManager-KQU7OX7G.js → SettingsManager-SLSYEYDZ.js} +4 -4
  11. package/dist/agents/iloom-artifact-reviewer.md +1 -0
  12. package/dist/agents/iloom-code-reviewer.md +21 -0
  13. package/dist/agents/iloom-issue-analyze-and-plan.md +30 -12
  14. package/dist/agents/iloom-issue-analyzer.md +32 -7
  15. package/dist/agents/iloom-issue-complexity-evaluator.md +32 -12
  16. package/dist/agents/iloom-issue-implementer.md +31 -12
  17. package/dist/agents/iloom-issue-planner.md +30 -12
  18. package/dist/agents/iloom-wave-verifier.md +177 -4
  19. package/dist/{build-V3KADFMO.js → build-ZTGWDHWU.js} +8 -8
  20. package/dist/{chunk-VVQQIG64.js → chunk-55NTREIU.js} +33 -30
  21. package/dist/chunk-55NTREIU.js.map +1 -0
  22. package/dist/{chunk-AYLC633W.js → chunk-7TN5VW4I.js} +65 -7
  23. package/dist/chunk-7TN5VW4I.js.map +1 -0
  24. package/dist/{chunk-RFCAPHL5.js → chunk-C2BVNJW5.js} +2 -2
  25. package/dist/{chunk-3XEXT35Z.js → chunk-E5OM25WK.js} +3 -3
  26. package/dist/{chunk-Q7VXHJP6.js → chunk-EHAITKLS.js} +10 -6
  27. package/dist/{chunk-Q7VXHJP6.js.map → chunk-EHAITKLS.js.map} +1 -1
  28. package/dist/{chunk-ZUIFO7B4.js → chunk-ERMEYFT6.js} +7 -2
  29. package/dist/chunk-ERMEYFT6.js.map +1 -0
  30. package/dist/{chunk-WGUGB54H.js → chunk-F5NKWLMQ.js} +21 -24
  31. package/dist/chunk-F5NKWLMQ.js.map +1 -0
  32. package/dist/{chunk-TN2D2RX7.js → chunk-G2DGDCDP.js} +33 -224
  33. package/dist/chunk-G2DGDCDP.js.map +1 -0
  34. package/dist/{chunk-NUUFP53X.js → chunk-GPBX2BY2.js} +2 -2
  35. package/dist/{chunk-SN4S5CWL.js → chunk-GQDVH6FA.js} +2 -2
  36. package/dist/{chunk-YUOVWWJX.js → chunk-HKEXRZMU.js} +5 -310
  37. package/dist/chunk-HKEXRZMU.js.map +1 -0
  38. package/dist/{chunk-TAEVA4QR.js → chunk-HWDQRW3O.js} +3 -3
  39. package/dist/chunk-HWDQRW3O.js.map +1 -0
  40. package/dist/{chunk-KQSV7FOG.js → chunk-J5JOJPK3.js} +2 -2
  41. package/dist/{chunk-PD75ZCFT.js → chunk-KCAWSZUO.js} +18 -17
  42. package/dist/chunk-KCAWSZUO.js.map +1 -0
  43. package/dist/{chunk-QNPJXO53.js → chunk-KGOBNC5A.js} +4 -4
  44. package/dist/{chunk-H3T3EPF3.js → chunk-LNY2Y32V.js} +2 -2
  45. package/dist/{chunk-7RCUWU3I.js → chunk-MRPIDNZU.js} +1 -1
  46. package/dist/chunk-MRPIDNZU.js.map +1 -0
  47. package/dist/{chunk-VIQOQ463.js → chunk-OLJ54WGW.js} +15 -10
  48. package/dist/chunk-OLJ54WGW.js.map +1 -0
  49. package/dist/{chunk-QQULYI2S.js → chunk-P5MNWBLH.js} +108 -47
  50. package/dist/chunk-P5MNWBLH.js.map +1 -0
  51. package/dist/{chunk-4VQXMEEP.js → chunk-PPQ5LV7U.js} +3 -3
  52. package/dist/{chunk-4VQXMEEP.js.map → chunk-PPQ5LV7U.js.map} +1 -1
  53. package/dist/{chunk-QED2WB2D.js → chunk-PS6K2AOV.js} +5 -5
  54. package/dist/{chunk-JD3K2344.js → chunk-QNRXRSKC.js} +36 -3
  55. package/dist/chunk-QNRXRSKC.js.map +1 -0
  56. package/dist/{chunk-SA446KA2.js → chunk-T4KFKKEB.js} +7 -7
  57. package/dist/{chunk-XCP2WDYA.js → chunk-T4NESGYB.js} +3 -3
  58. package/dist/{chunk-QXGM32TO.js → chunk-TJDKGKQV.js} +2 -2
  59. package/dist/{chunk-X5DRLONY.js → chunk-UXBVDD7U.js} +6 -6
  60. package/dist/{chunk-JDN4SPV3.js → chunk-WYDLOQYO.js} +2 -2
  61. package/dist/{chunk-4JZEQBWV.js → chunk-XIVLGWUX.js} +3 -1
  62. package/dist/chunk-XIVLGWUX.js.map +1 -0
  63. package/dist/{chunk-NTDY5AMO.js → chunk-ZEFTWM5Z.js} +2 -2
  64. package/dist/{cleanup-RJKLI47I.js → cleanup-BCVY7PEF.js} +22 -22
  65. package/dist/cleanup-BCVY7PEF.js.map +1 -0
  66. package/dist/cli.js +136 -105
  67. package/dist/cli.js.map +1 -1
  68. package/dist/{commit-SUHRUMDE.js → commit-L5JNBU4U.js} +8 -8
  69. package/dist/{compile-2MD346PO.js → compile-GPJOHXH4.js} +8 -8
  70. package/dist/{contribute-P4BMRY7C.js → contribute-QEGCI4PS.js} +4 -4
  71. package/dist/{dev-server-ZNTLWOL5.js → dev-server-UQKNKU2S.js} +249 -31
  72. package/dist/dev-server-UQKNKU2S.js.map +1 -0
  73. package/dist/{feedback-Q6WG2WX4.js → feedback-2LWXKLQZ.js} +4 -4
  74. package/dist/{git-TX2IEMB3.js → git-IS7AV3ED.js} +4 -4
  75. package/dist/hooks/iloom-hook.js +40 -2
  76. package/dist/{ignite-P644W2PK.js → ignite-VQDJQ37S.js} +12 -14
  77. package/dist/index.d.ts +73 -75
  78. package/dist/index.js +32 -32
  79. package/dist/index.js.map +1 -1
  80. package/dist/{init-5HFY7JG6.js → init-7SDJUAEZ.js} +8 -8
  81. package/dist/{install-deps-J4ALTM27.js → install-deps-NGSFDNUW.js} +8 -8
  82. package/dist/{issues-LZMIF22U.js → issues-4HQKEUP7.js} +5 -5
  83. package/dist/{lint-XIXKU22H.js → lint-C5FOVRXY.js} +8 -8
  84. package/dist/mcp/issue-management-server.js +19 -22
  85. package/dist/mcp/issue-management-server.js.map +1 -1
  86. package/dist/neon-helpers-LCZAN4U4.js +11 -0
  87. package/dist/{open-KUO35JIJ.js → open-2HL6GV5F.js} +19 -15
  88. package/dist/open-2HL6GV5F.js.map +1 -0
  89. package/dist/{plan-7CF56OIR.js → plan-GC3HF73T.js} +86 -66
  90. package/dist/plan-GC3HF73T.js.map +1 -0
  91. package/dist/{projects-L5AHUBGA.js → projects-3F6T3KZL.js} +2 -2
  92. package/dist/prompts/init-prompt.txt +40 -36
  93. package/dist/prompts/issue-prompt.txt +4 -1
  94. package/dist/prompts/plan-prompt.txt +97 -16
  95. package/dist/prompts/regular-prompt.txt +1 -1
  96. package/dist/prompts/swarm-orchestrator-prompt.txt +25 -12
  97. package/dist/{rebase-MAMWPA2L.js → rebase-MLIN572O.js} +7 -7
  98. package/dist/{recap-IDBO3KM5.js → recap-CKGKFDJL.js} +7 -7
  99. package/dist/{run-RGZHCQ6M.js → run-CUNRQNZS.js} +19 -15
  100. package/dist/run-CUNRQNZS.js.map +1 -0
  101. package/dist/schema/settings.schema.json +35 -31
  102. package/dist/{shell-7ADCDFIV.js → shell-M2YYPNGV.js} +6 -6
  103. package/dist/{summary-7J2HORFD.js → summary-XR4CBJEG.js} +9 -9
  104. package/dist/{test-SRB7EWU6.js → test-ESDAHEVE.js} +8 -8
  105. package/dist/{test-git-G7ATVIXG.js → test-git-KWPLHYSI.js} +4 -4
  106. package/dist/{test-jira-Q2HPA522.js → test-jira-6NK7UHSV.js} +3 -3
  107. package/dist/{test-prefix-JMDGXR5A.js → test-prefix-VVODGHXP.js} +4 -4
  108. package/dist/{test-webserver-GZFVXBGD.js → test-webserver-AHXKC6H4.js} +6 -6
  109. package/dist/{vscode-3I7ISHUU.js → vscode-OY7HOVRO.js} +6 -6
  110. package/package.json +1 -1
  111. package/dist/chunk-4JZEQBWV.js.map +0 -1
  112. package/dist/chunk-7RCUWU3I.js.map +0 -1
  113. package/dist/chunk-AYLC633W.js.map +0 -1
  114. package/dist/chunk-JD3K2344.js.map +0 -1
  115. package/dist/chunk-PD75ZCFT.js.map +0 -1
  116. package/dist/chunk-QQULYI2S.js.map +0 -1
  117. package/dist/chunk-TAEVA4QR.js.map +0 -1
  118. package/dist/chunk-TN2D2RX7.js.map +0 -1
  119. package/dist/chunk-VIQOQ463.js.map +0 -1
  120. package/dist/chunk-VVQQIG64.js.map +0 -1
  121. package/dist/chunk-WGUGB54H.js.map +0 -1
  122. package/dist/chunk-YUOVWWJX.js.map +0 -1
  123. package/dist/chunk-ZUIFO7B4.js.map +0 -1
  124. package/dist/cleanup-RJKLI47I.js.map +0 -1
  125. package/dist/database-helpers-PRDFNDRO.js +0 -11
  126. package/dist/dev-server-ZNTLWOL5.js.map +0 -1
  127. package/dist/open-KUO35JIJ.js.map +0 -1
  128. package/dist/plan-7CF56OIR.js.map +0 -1
  129. package/dist/run-RGZHCQ6M.js.map +0 -1
  130. /package/dist/{ClaudeContextManager-RRGREEZQ.js.map → ClaudeContextManager-ZH6LEA5I.js.map} +0 -0
  131. /package/dist/{ClaudeService-LEPW6QAC.js.map → ClaudeService-YR66WXZN.js.map} +0 -0
  132. /package/dist/{IssueTrackerFactory-KE2BDCLC.js.map → IssueTrackerFactory-O2ZBA666.js.map} +0 -0
  133. /package/dist/{LoomLauncher-GKQMR5E6.js.map → LoomLauncher-V54ENBEF.js.map} +0 -0
  134. /package/dist/{MetadataManager-V4LSJ2PB.js.map → MetadataManager-HHE6LQF2.js.map} +0 -0
  135. /package/dist/{PromptTemplateManager-I75WKXM4.js.map → PromptTemplateManager-4RFELNYY.js.map} +0 -0
  136. /package/dist/{SettingsManager-KQU7OX7G.js.map → SettingsManager-SLSYEYDZ.js.map} +0 -0
  137. /package/dist/{build-V3KADFMO.js.map → build-ZTGWDHWU.js.map} +0 -0
  138. /package/dist/{chunk-RFCAPHL5.js.map → chunk-C2BVNJW5.js.map} +0 -0
  139. /package/dist/{chunk-3XEXT35Z.js.map → chunk-E5OM25WK.js.map} +0 -0
  140. /package/dist/{chunk-NUUFP53X.js.map → chunk-GPBX2BY2.js.map} +0 -0
  141. /package/dist/{chunk-SN4S5CWL.js.map → chunk-GQDVH6FA.js.map} +0 -0
  142. /package/dist/{chunk-KQSV7FOG.js.map → chunk-J5JOJPK3.js.map} +0 -0
  143. /package/dist/{chunk-QNPJXO53.js.map → chunk-KGOBNC5A.js.map} +0 -0
  144. /package/dist/{chunk-H3T3EPF3.js.map → chunk-LNY2Y32V.js.map} +0 -0
  145. /package/dist/{chunk-QED2WB2D.js.map → chunk-PS6K2AOV.js.map} +0 -0
  146. /package/dist/{chunk-SA446KA2.js.map → chunk-T4KFKKEB.js.map} +0 -0
  147. /package/dist/{chunk-XCP2WDYA.js.map → chunk-T4NESGYB.js.map} +0 -0
  148. /package/dist/{chunk-QXGM32TO.js.map → chunk-TJDKGKQV.js.map} +0 -0
  149. /package/dist/{chunk-X5DRLONY.js.map → chunk-UXBVDD7U.js.map} +0 -0
  150. /package/dist/{chunk-JDN4SPV3.js.map → chunk-WYDLOQYO.js.map} +0 -0
  151. /package/dist/{chunk-NTDY5AMO.js.map → chunk-ZEFTWM5Z.js.map} +0 -0
  152. /package/dist/{commit-SUHRUMDE.js.map → commit-L5JNBU4U.js.map} +0 -0
  153. /package/dist/{compile-2MD346PO.js.map → compile-GPJOHXH4.js.map} +0 -0
  154. /package/dist/{contribute-P4BMRY7C.js.map → contribute-QEGCI4PS.js.map} +0 -0
  155. /package/dist/{feedback-Q6WG2WX4.js.map → feedback-2LWXKLQZ.js.map} +0 -0
  156. /package/dist/{database-helpers-PRDFNDRO.js.map → git-IS7AV3ED.js.map} +0 -0
  157. /package/dist/{git-TX2IEMB3.js.map → ignite-VQDJQ37S.js.map} +0 -0
  158. /package/dist/{init-5HFY7JG6.js.map → init-7SDJUAEZ.js.map} +0 -0
  159. /package/dist/{install-deps-J4ALTM27.js.map → install-deps-NGSFDNUW.js.map} +0 -0
  160. /package/dist/{issues-LZMIF22U.js.map → issues-4HQKEUP7.js.map} +0 -0
  161. /package/dist/{lint-XIXKU22H.js.map → lint-C5FOVRXY.js.map} +0 -0
  162. /package/dist/{ignite-P644W2PK.js.map → neon-helpers-LCZAN4U4.js.map} +0 -0
  163. /package/dist/{projects-L5AHUBGA.js.map → projects-3F6T3KZL.js.map} +0 -0
  164. /package/dist/{rebase-MAMWPA2L.js.map → rebase-MLIN572O.js.map} +0 -0
  165. /package/dist/{recap-IDBO3KM5.js.map → recap-CKGKFDJL.js.map} +0 -0
  166. /package/dist/{shell-7ADCDFIV.js.map → shell-M2YYPNGV.js.map} +0 -0
  167. /package/dist/{summary-7J2HORFD.js.map → summary-XR4CBJEG.js.map} +0 -0
  168. /package/dist/{test-SRB7EWU6.js.map → test-ESDAHEVE.js.map} +0 -0
  169. /package/dist/{test-git-G7ATVIXG.js.map → test-git-KWPLHYSI.js.map} +0 -0
  170. /package/dist/{test-jira-Q2HPA522.js.map → test-jira-6NK7UHSV.js.map} +0 -0
  171. /package/dist/{test-prefix-JMDGXR5A.js.map → test-prefix-VVODGHXP.js.map} +0 -0
  172. /package/dist/{test-webserver-GZFVXBGD.js.map → test-webserver-AHXKC6H4.js.map} +0 -0
  173. /package/dist/{vscode-3I7ISHUU.js.map → vscode-OY7HOVRO.js.map} +0 -0
@@ -2,15 +2,15 @@
2
2
  import {
3
3
  CommitManager,
4
4
  ValidationRunner
5
- } from "./chunk-TAEVA4QR.js";
5
+ } from "./chunk-HWDQRW3O.js";
6
6
  import {
7
7
  IssueManagementProviderFactory
8
- } from "./chunk-NTDY5AMO.js";
8
+ } from "./chunk-ZEFTWM5Z.js";
9
9
  import "./chunk-4232AHNQ.js";
10
- import "./chunk-VIQOQ463.js";
10
+ import "./chunk-OLJ54WGW.js";
11
11
  import {
12
12
  GitWorktreeManager
13
- } from "./chunk-4VQXMEEP.js";
13
+ } from "./chunk-PPQ5LV7U.js";
14
14
  import "./chunk-K3QGG4O2.js";
15
15
  import "./chunk-DMSL5BAP.js";
16
16
  import "./chunk-KV4NU3RP.js";
@@ -20,13 +20,13 @@ import {
20
20
  extractIssueNumber,
21
21
  getWorktreeRoot,
22
22
  isValidGitRepo
23
- } from "./chunk-QNPJXO53.js";
23
+ } from "./chunk-KGOBNC5A.js";
24
24
  import {
25
25
  SettingsManager
26
- } from "./chunk-WGUGB54H.js";
26
+ } from "./chunk-F5NKWLMQ.js";
27
27
  import {
28
28
  MetadataManager
29
- } from "./chunk-4JZEQBWV.js";
29
+ } from "./chunk-XIVLGWUX.js";
30
30
  import {
31
31
  getLogger
32
32
  } from "./chunk-FTYWGQFM.js";
@@ -238,4 +238,4 @@ export {
238
238
  CommitCommand,
239
239
  WorktreeValidationError
240
240
  };
241
- //# sourceMappingURL=commit-SUHRUMDE.js.map
241
+ //# sourceMappingURL=commit-L5JNBU4U.js.map
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ScriptCommandBase
4
- } from "./chunk-QED2WB2D.js";
5
- import "./chunk-SN4S5CWL.js";
4
+ } from "./chunk-PS6K2AOV.js";
5
+ import "./chunk-GQDVH6FA.js";
6
6
  import {
7
7
  runScript
8
- } from "./chunk-VIQOQ463.js";
9
- import "./chunk-4VQXMEEP.js";
8
+ } from "./chunk-OLJ54WGW.js";
9
+ import "./chunk-PPQ5LV7U.js";
10
10
  import {
11
11
  getPackageScripts
12
12
  } from "./chunk-K3QGG4O2.js";
13
- import "./chunk-QNPJXO53.js";
14
- import "./chunk-WGUGB54H.js";
15
- import "./chunk-4JZEQBWV.js";
13
+ import "./chunk-KGOBNC5A.js";
14
+ import "./chunk-F5NKWLMQ.js";
15
+ import "./chunk-XIVLGWUX.js";
16
16
  import "./chunk-FTYWGQFM.js";
17
17
  import {
18
18
  logger
@@ -54,4 +54,4 @@ var CompileCommand = class extends ScriptCommandBase {
54
54
  export {
55
55
  CompileCommand
56
56
  };
57
- //# sourceMappingURL=compile-2MD346PO.js.map
57
+ //# sourceMappingURL=compile-GPJOHXH4.js.map
@@ -14,9 +14,9 @@ import {
14
14
  } from "./chunk-NPVA65KS.js";
15
15
  import {
16
16
  executeGitCommand
17
- } from "./chunk-QNPJXO53.js";
18
- import "./chunk-WGUGB54H.js";
19
- import "./chunk-4JZEQBWV.js";
17
+ } from "./chunk-KGOBNC5A.js";
18
+ import "./chunk-F5NKWLMQ.js";
19
+ import "./chunk-XIVLGWUX.js";
20
20
  import "./chunk-FTYWGQFM.js";
21
21
  import {
22
22
  logger
@@ -335,4 +335,4 @@ export {
335
335
  validateDirectoryPath,
336
336
  validateRepoExists
337
337
  };
338
- //# sourceMappingURL=contribute-P4BMRY7C.js.map
338
+ //# sourceMappingURL=contribute-QEGCI4PS.js.map
@@ -1,21 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- DevServerManager
4
- } from "./chunk-QQULYI2S.js";
3
+ DevServerManager,
4
+ buildDevServerUrl
5
+ } from "./chunk-P5MNWBLH.js";
5
6
  import {
6
7
  DockerManager
7
- } from "./chunk-AYLC633W.js";
8
- import "./chunk-H3T3EPF3.js";
8
+ } from "./chunk-7TN5VW4I.js";
9
+ import "./chunk-LNY2Y32V.js";
9
10
  import {
10
11
  getWorkspacePort
11
- } from "./chunk-ZUIFO7B4.js";
12
+ } from "./chunk-ERMEYFT6.js";
12
13
  import {
13
14
  IdentifierParser
14
- } from "./chunk-SN4S5CWL.js";
15
- import "./chunk-VIQOQ463.js";
15
+ } from "./chunk-GQDVH6FA.js";
16
+ import "./chunk-OLJ54WGW.js";
16
17
  import {
17
18
  GitWorktreeManager
18
- } from "./chunk-4VQXMEEP.js";
19
+ } from "./chunk-PPQ5LV7U.js";
20
+ import {
21
+ openBrowser
22
+ } from "./chunk-WEBMMJKL.js";
19
23
  import {
20
24
  extractSettingsOverrides
21
25
  } from "./chunk-GYCR2LOU.js";
@@ -25,22 +29,210 @@ import {
25
29
  import "./chunk-K3QGG4O2.js";
26
30
  import {
27
31
  extractIssueNumber
28
- } from "./chunk-QNPJXO53.js";
32
+ } from "./chunk-KGOBNC5A.js";
29
33
  import {
30
34
  SettingsManager
31
- } from "./chunk-WGUGB54H.js";
35
+ } from "./chunk-F5NKWLMQ.js";
32
36
  import {
33
37
  MetadataManager
34
- } from "./chunk-4JZEQBWV.js";
38
+ } from "./chunk-XIVLGWUX.js";
35
39
  import "./chunk-FTYWGQFM.js";
36
40
  import {
37
41
  isNoEnvFilesFoundError,
38
42
  loadWorkspaceEnv,
39
- logger
43
+ logger,
44
+ restoreTerminalState
40
45
  } from "./chunk-VRPPI6GU.js";
41
46
 
42
47
  // src/commands/dev-server.ts
43
48
  import path from "path";
49
+
50
+ // src/lib/DevServerTUI.ts
51
+ import { execa } from "execa";
52
+ var ESC = "\x1B";
53
+ var CSI = `${ESC}[`;
54
+ var CURSOR_SAVE = `${ESC}7`;
55
+ var CURSOR_RESTORE = `${ESC}8`;
56
+ var CURSOR_HIDE = `${CSI}?25l`;
57
+ var CURSOR_SHOW = `${CSI}?25h`;
58
+ var CLEAR_LINE = `${CSI}K`;
59
+ var CLEAR_SCREEN = `${CSI}2J${CSI}H`;
60
+ var SCROLL_REGION_RESET = `${CSI}r`;
61
+ function moveTo(row, col) {
62
+ return `${CSI}${row};${col}H`;
63
+ }
64
+ function setScrollRegion(top, bottom) {
65
+ return `${CSI}${top};${bottom}r`;
66
+ }
67
+ var STATUS_BAR_HEIGHT = 4;
68
+ var DevServerTUI = class {
69
+ constructor(options) {
70
+ this.status = "Running";
71
+ this.started = false;
72
+ this.cleanedUp = false;
73
+ this.url = options.url;
74
+ this.port = options.port;
75
+ this.containerPort = options.containerPort;
76
+ this.stdout = options.stdout ?? process.stdout;
77
+ this.stdin = options.stdin ?? process.stdin;
78
+ this.onQuit = options.onQuit;
79
+ this.onRestart = options.onRestart;
80
+ this.onData = (data) => this.handleKeypress(data);
81
+ this.onResize = () => this.handleResize();
82
+ this.onProcessExit = () => this.cleanup();
83
+ }
84
+ /**
85
+ * Start the TUI - sets up scroll region, renders status bar, starts keyboard listener.
86
+ */
87
+ start() {
88
+ if (this.started) return;
89
+ this.started = true;
90
+ const rows = this.stdout.rows ?? 24;
91
+ this.stdout.write(CURSOR_HIDE);
92
+ this.stdout.write(CLEAR_SCREEN);
93
+ const scrollBottom = Math.max(1, rows - STATUS_BAR_HEIGHT);
94
+ this.stdout.write(setScrollRegion(1, scrollBottom));
95
+ this.stdout.write(moveTo(1, 1));
96
+ this.renderStatusBar();
97
+ if (this.stdin.isTTY && typeof this.stdin.setRawMode === "function") {
98
+ this.stdin.setRawMode(true);
99
+ this.stdin.resume();
100
+ this.stdin.on("data", this.onData);
101
+ }
102
+ this.stdout.on("resize", this.onResize);
103
+ process.on("exit", this.onProcessExit);
104
+ }
105
+ /**
106
+ * Write server output into the scroll region.
107
+ * The scroll region constrains the cursor naturally, so output stays
108
+ * above the status bar without explicit cursor repositioning.
109
+ */
110
+ handleOutput(data) {
111
+ if (!this.started || this.cleanedUp) return;
112
+ this.stdout.write(data.toString());
113
+ }
114
+ /**
115
+ * Update the status displayed in the status bar.
116
+ */
117
+ updateStatus(status) {
118
+ this.status = status;
119
+ if (this.started && !this.cleanedUp) {
120
+ this.renderStatusBar();
121
+ }
122
+ }
123
+ /**
124
+ * Render the status bar in the fixed area below the scroll region.
125
+ */
126
+ renderStatusBar() {
127
+ const rows = this.stdout.rows ?? 24;
128
+ const cols = this.stdout.columns ?? 80;
129
+ if (rows < STATUS_BAR_HEIGHT + 2) return;
130
+ const barStartRow = rows - STATUS_BAR_HEIGHT + 1;
131
+ const urlSegment = ` ${this.url} `;
132
+ const statusIcon = this.status === "Running" ? "\u25A0" : this.status === "Stopped" ? "\u25A1" : "\u25C6";
133
+ const statusSegment = ` ${statusIcon} ${this.status} `;
134
+ const portSegment = this.containerPort ? ` Port ${this.containerPort} \u2192 ${this.port} ` : ` Port ${this.port} `;
135
+ const contentParts = `\u2502${urlSegment}\u2502${statusSegment}\u2502${portSegment}\u2502`;
136
+ const contentLine = contentParts.length < cols ? contentParts + " ".repeat(cols - contentParts.length) : contentParts.substring(0, cols);
137
+ const innerWidth = Math.max(0, cols - 2);
138
+ const topBorder = `\u250C${"\u2500".repeat(innerWidth)}\u2510`;
139
+ const bottomBorder = `\u2514${"\u2500".repeat(innerWidth)}\u2518`;
140
+ const hintLine = " [o] Open [c] Copy URL [r] Restart [q] Quit";
141
+ const paddedHint = hintLine.length < cols ? hintLine + " ".repeat(cols - hintLine.length) : hintLine.substring(0, cols);
142
+ this.stdout.write(CURSOR_SAVE);
143
+ this.stdout.write(moveTo(barStartRow, 1) + CLEAR_LINE + topBorder);
144
+ this.stdout.write(moveTo(barStartRow + 1, 1) + CLEAR_LINE + contentLine);
145
+ this.stdout.write(moveTo(barStartRow + 2, 1) + CLEAR_LINE + bottomBorder);
146
+ this.stdout.write(moveTo(barStartRow + 3, 1) + CLEAR_LINE + paddedHint);
147
+ this.stdout.write(CURSOR_RESTORE);
148
+ }
149
+ /**
150
+ * Handle keyboard input.
151
+ */
152
+ handleKeypress(data) {
153
+ const key = data.toString("utf8");
154
+ switch (key) {
155
+ case "o":
156
+ case "O":
157
+ void openBrowser(this.url).catch((err) => {
158
+ logger.warn(`Failed to open browser: ${err instanceof Error ? err.message : "Unknown error"}`);
159
+ });
160
+ break;
161
+ case "c":
162
+ case "C":
163
+ void this.copyToClipboard(this.url).catch((err) => {
164
+ logger.warn(`Failed to copy to clipboard: ${err instanceof Error ? err.message : "Unknown error"}`);
165
+ });
166
+ break;
167
+ case "q":
168
+ case "Q":
169
+ case "":
170
+ if (this.onQuit) {
171
+ this.onQuit();
172
+ }
173
+ break;
174
+ case "r":
175
+ case "R":
176
+ if (this.onRestart) {
177
+ this.onRestart();
178
+ }
179
+ break;
180
+ }
181
+ }
182
+ /**
183
+ * Copy text to clipboard using platform-specific commands.
184
+ */
185
+ async copyToClipboard(text) {
186
+ const platform = process.platform;
187
+ let command;
188
+ let args;
189
+ if (platform === "darwin") {
190
+ command = "pbcopy";
191
+ args = [];
192
+ } else if (platform === "win32") {
193
+ command = "clip";
194
+ args = [];
195
+ } else {
196
+ command = "xclip";
197
+ args = ["-selection", "clipboard"];
198
+ }
199
+ await execa(command, args, { input: text });
200
+ }
201
+ /**
202
+ * Handle terminal resize - re-establish scroll region and re-render status bar.
203
+ */
204
+ handleResize() {
205
+ if (!this.started || this.cleanedUp) return;
206
+ const rows = this.stdout.rows ?? 24;
207
+ if (rows < STATUS_BAR_HEIGHT + 2) return;
208
+ const scrollBottom = Math.max(1, rows - STATUS_BAR_HEIGHT);
209
+ this.stdout.write(setScrollRegion(1, scrollBottom));
210
+ this.renderStatusBar();
211
+ this.stdout.write(moveTo(scrollBottom, 1));
212
+ }
213
+ /**
214
+ * Clean up: restore full scroll region, disable raw mode, restore terminal.
215
+ */
216
+ cleanup() {
217
+ if (this.cleanedUp) return;
218
+ this.cleanedUp = true;
219
+ if (this.stdin.isTTY && typeof this.stdin.setRawMode === "function") {
220
+ this.stdin.removeListener("data", this.onData);
221
+ this.stdin.setRawMode(false);
222
+ this.stdin.pause();
223
+ }
224
+ this.stdout.removeListener("resize", this.onResize);
225
+ process.removeListener("exit", this.onProcessExit);
226
+ this.stdout.write(SCROLL_REGION_RESET);
227
+ this.stdout.write(CURSOR_SHOW);
228
+ const rows = this.stdout.rows ?? 24;
229
+ this.stdout.write(moveTo(rows, 1));
230
+ this.stdout.write("\n");
231
+ restoreTerminalState();
232
+ }
233
+ };
234
+
235
+ // src/commands/dev-server.ts
44
236
  var DevServerCommand = class {
45
237
  constructor(gitWorktreeManager = new GitWorktreeManager(), capabilityDetector = new ProjectCapabilityDetector(), identifierParser = new IdentifierParser(new GitWorktreeManager()), devServerManager = new DevServerManager(), settingsManager = new SettingsManager(), metadataManager = new MetadataManager()) {
46
238
  this.gitWorktreeManager = gitWorktreeManager;
@@ -57,7 +249,7 @@ var DevServerCommand = class {
57
249
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
58
250
  }
59
251
  async execute(input) {
60
- var _a, _b, _c, _d;
252
+ var _a, _b, _c, _d, _e, _f;
61
253
  const parsed = input.identifier ? await this.parseExplicitInput(input.identifier) : await this.autoDetectFromCurrentDirectory();
62
254
  logger.debug(`Parsed input: ${JSON.stringify(parsed)}`);
63
255
  const worktree = await this.findWorktreeForIdentifier(parsed);
@@ -108,13 +300,16 @@ var DevServerCommand = class {
108
300
  }
109
301
  const cliOverrides = extractSettingsOverrides();
110
302
  const settingsForPort = await this.settingsManager.loadSettings(void 0, cliOverrides);
303
+ const isMainWorktree = await this.gitWorktreeManager.isMainWorktree(worktree, this.settingsManager);
111
304
  const port = await getWorkspacePort({
112
305
  worktreePath: worktree.path,
113
306
  worktreeBranch: worktree.branch,
114
307
  basePort: (_d = (_c = settingsForPort.capabilities) == null ? void 0 : _c.web) == null ? void 0 : _d.basePort,
115
- checkEnvFile: true
308
+ checkEnvFile: true,
309
+ isMainWorktree
116
310
  });
117
- const url = `http://localhost:${port}`;
311
+ const protocol = ((_f = (_e = settingsForPort.capabilities) == null ? void 0 : _e.web) == null ? void 0 : _f.protocol) ?? "http";
312
+ const url = buildDevServerUrl(port, protocol);
118
313
  const isRunning = await this.devServerManager.isServerRunning(port, dockerConfig);
119
314
  if (isRunning) {
120
315
  const message2 = `Dev server already running at ${url}`;
@@ -145,22 +340,45 @@ var DevServerCommand = class {
145
340
  port,
146
341
  message
147
342
  };
148
- const processInfo = await this.devServerManager.runServerForeground(
149
- worktree.path,
150
- port,
151
- !!input.json,
152
- // Callback called immediately when process starts (for JSON output)
153
- (pid) => {
154
- if (input.json && pid) {
155
- finalResult.pid = pid;
156
- this.outputJson(finalResult);
343
+ const useTui = !input.json && process.stdout.isTTY === true && process.stdin.isTTY === true;
344
+ let tui;
345
+ if (useTui) {
346
+ tui = new DevServerTUI({
347
+ url,
348
+ port,
349
+ containerPort: dockerConfig == null ? void 0 : dockerConfig.containerPort,
350
+ onQuit: () => {
351
+ process.kill(process.pid, "SIGINT");
157
352
  }
158
- },
159
- envOverrides,
160
- dockerConfig
161
- );
162
- if (processInfo.pid) {
163
- finalResult.pid = processInfo.pid;
353
+ });
354
+ tui.start();
355
+ }
356
+ const onOutput = tui ? (data) => {
357
+ tui == null ? void 0 : tui.handleOutput(data);
358
+ } : void 0;
359
+ try {
360
+ const processInfo = await this.devServerManager.runServerForeground(
361
+ worktree.path,
362
+ port,
363
+ !!input.json,
364
+ // Callback called immediately when process starts (for JSON output)
365
+ (pid) => {
366
+ if (input.json && pid) {
367
+ finalResult.pid = pid;
368
+ this.outputJson(finalResult);
369
+ }
370
+ },
371
+ envOverrides,
372
+ dockerConfig,
373
+ onOutput
374
+ );
375
+ if (processInfo.pid) {
376
+ finalResult.pid = processInfo.pid;
377
+ }
378
+ } finally {
379
+ if (tui) {
380
+ tui.cleanup();
381
+ }
164
382
  }
165
383
  return finalResult;
166
384
  }
@@ -284,4 +502,4 @@ var DevServerCommand = class {
284
502
  export {
285
503
  DevServerCommand
286
504
  };
287
- //# sourceMappingURL=dev-server-ZNTLWOL5.js.map
505
+ //# sourceMappingURL=dev-server-UQKNKU2S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/dev-server.ts","../src/lib/DevServerTUI.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { DevServerTUI } from '../lib/DevServerTUI.js'\nimport { DockerManager } from '../lib/DockerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { loadWorkspaceEnv, isNoEnvFilesFoundError } from '../utils/env.js'\nimport { getWorkspacePort } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { buildDevServerUrl } from '../utils/dev-server.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface DevServerCommandInput {\n\tidentifier?: string | undefined\n\tjson?: boolean | undefined\n}\n\nexport interface DevServerResult {\n\tstatus: 'started' | 'already_running' | 'no_web_capability'\n\turl?: string\n\tport?: number\n\tpid?: number\n\tmessage: string\n}\n\ninterface ParsedDevServerInput {\n\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * DevServerCommand - Start dev server for workspace in foreground mode\n * Runs in foreground (blocking terminal until user stops it)\n */\nexport class DevServerCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager(),\n\t\tprivate metadataManager = new MetadataManager()\n\t) {}\n\n\t/**\n\t * Output JSON to stdout (used for --json flag)\n\t */\n\tprivate outputJson(data: DevServerResult | Record<string, unknown>): void {\n\t\tprocess.stdout.write(JSON.stringify(data, null, 2) + '\\n')\n\t}\n\n\tasync execute(input: DevServerCommandInput): Promise<DevServerResult> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.debug(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Load settings to check sourceEnvOnStart and Docker config\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\n\n\t\t// 3a. Extract Docker configuration if Docker mode is enabled\n\t\tconst identifier = parsed.number?.toString() ?? parsed.branchName ?? parsed.originalInput\n\t\tconst dockerConfig = DockerManager.buildDockerConfigFromSettings(\n\t\t\tsettings.capabilities?.web,\n\t\t\tidentifier\n\t\t)\n\n\t\tif (dockerConfig) {\n\t\t\tawait DockerManager.assertAvailable()\n\t\t\tconst { dockerFile, containerPort, identifier } = dockerConfig\n\t\t\tlogger.debug(`Docker mode enabled with config: ${JSON.stringify({ dockerFile, containerPort, identifier })}`)\n\t\t}\n\n\t\t// Build environment variables\n\t\tlet envOverrides: Record<string, string> = {}\n\n\t\tif (shouldLoadEnv) {\n\t\t\tconst envResult = loadWorkspaceEnv(worktree.path)\n\t\t\tif (envResult.parsed) {\n\t\t\t\tenvOverrides = envResult.parsed\n\t\t\t}\n\t\t\tif (envResult.error && !isNoEnvFilesFoundError(envResult.error)) {\n\t\t\t\tlogger.warn(`Failed to load env files: ${envResult.error.message}`)\n\t\t\t}\n\t\t}\n\n\t\t// 3b. Set ILOOM_LOOM for loom identification\n\t\tenvOverrides.ILOOM_LOOM = this.formatLoomIdentifier(parsed)\n\n\t\t// 3c. Set ILOOM_COLOR_HEX from loom metadata if available\n\t\tconst metadata = await this.metadataManager.readMetadata(worktree.path)\n\t\tif (metadata?.colorHex) {\n\t\t\tenvOverrides.ILOOM_COLOR_HEX = metadata.colorHex\n\t\t}\n\n\t\t// 4. Detect project capabilities\n\t\tconst { capabilities } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. If no web capability, return gracefully with info message\n\t\tif (!capabilities.includes('web')) {\n\t\t\tconst message = 'No web capability detected in this workspace. Dev server not started.'\n\t\t\tif (input.json) {\n\t\t\t\tthis.outputJson({\n\t\t\t\t\tstatus: 'no_web_capability',\n\t\t\t\t\tmessage,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlogger.info(message)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: 'no_web_capability',\n\t\t\t\tmessage,\n\t\t\t}\n\t\t}\n\n\t\t// 5. Get port for workspace\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settingsForPort = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst isMainWorktree = await this.gitWorktreeManager.isMainWorktree(worktree, this.settingsManager)\n\t\tconst port = await getWorkspacePort({\n\t\t\tworktreePath: worktree.path,\n\t\t\tworktreeBranch: worktree.branch,\n\t\t\tbasePort: settingsForPort.capabilities?.web?.basePort,\n\t\t\tcheckEnvFile: true,\n\t\t\tisMainWorktree,\n\t\t})\n\t\tconst protocol = settingsForPort.capabilities?.web?.protocol ?? 'http'\n\t\tconst url = buildDevServerUrl(port, protocol)\n\n\t\t// 6. Check if server already running\n\t\tconst isRunning = await this.devServerManager.isServerRunning(port, dockerConfig)\n\n\t\tif (isRunning) {\n\t\t\tconst message = `Dev server already running at ${url}`\n\t\t\tif (input.json) {\n\t\t\t\tthis.outputJson({\n\t\t\t\t\tstatus: 'already_running',\n\t\t\t\t\turl,\n\t\t\t\t\tport,\n\t\t\t\t\tmessage,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlogger.info(message)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: 'already_running',\n\t\t\t\turl,\n\t\t\t\tport,\n\t\t\t\tmessage,\n\t\t\t}\n\t\t}\n\n\t\t// 7. Start server in foreground\n\t\tconst message = `Starting dev server at ${url}`\n\t\tif (!input.json) {\n\t\t\tlogger.info(message)\n\t\t}\n\n\t\tlet finalResult: DevServerResult = {\n\t\t\tstatus: 'started',\n\t\t\turl,\n\t\t\tport,\n\t\t\tmessage,\n\t\t}\n\n\t\t// Determine if TUI should be used:\n\t\t// - Only when TTY is detected on both stdout and stdin\n\t\t// - Disabled in JSON mode (JSON output goes to stdout)\n\t\tconst useTui = !input.json && process.stdout.isTTY === true && process.stdin.isTTY === true\n\n\t\tlet tui: DevServerTUI | undefined\n\n\t\tif (useTui) {\n\t\t\ttui = new DevServerTUI({\n\t\t\t\turl,\n\t\t\t\tport,\n\t\t\t\tcontainerPort: dockerConfig?.containerPort,\n\t\t\t\tonQuit: (): void => {\n\t\t\t\t\t// Send SIGINT to self to trigger normal cleanup flow\n\t\t\t\t\tprocess.kill(process.pid, 'SIGINT')\n\t\t\t\t},\n\t\t\t})\n\t\t\ttui.start()\n\t\t}\n\n\t\tconst onOutput = tui\n\t\t\t? (data: Buffer): void => { tui?.handleOutput(data) }\n\t\t\t: undefined\n\n\t\ttry {\n\t\t\t// This will block until user stops the server (Ctrl+C)\n\t\t\t// In JSON mode, redirect npm output to stderr so JSON can go to stdout\n\t\t\tconst processInfo = await this.devServerManager.runServerForeground(\n\t\t\t\tworktree.path,\n\t\t\t\tport,\n\t\t\t\t!!input.json,\n\t\t\t\t// Callback called immediately when process starts (for JSON output)\n\t\t\t\t(pid) => {\n\t\t\t\t\tif (input.json && pid) {\n\t\t\t\t\t\tfinalResult.pid = pid\n\t\t\t\t\t\tthis.outputJson(finalResult)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tenvOverrides,\n\t\t\t\tdockerConfig,\n\t\t\t\tonOutput\n\t\t\t)\n\n\t\t\tif (processInfo.pid) {\n\t\t\t\tfinalResult.pid = processInfo.pid\n\t\t\t}\n\t\t} finally {\n\t\t\tif (tui) {\n\t\t\t\ttui.cleanup()\n\t\t\t}\n\t\t}\n\n\t\treturn finalResult\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedDevServerInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach dev-server command\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in dev-server command')\n\t\t}\n\n\t\tconst result: ParsedDevServerInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedDevServerInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedDevServerInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedDevServerInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Format loom identifier for ILOOM_LOOM env var\n\t */\n\tprivate formatLoomIdentifier(parsed: ParsedDevServerInput): string {\n\t\treturn parsed.originalInput\n\t}\n}\n","import { execa } from 'execa'\nimport { openBrowser } from '../utils/browser.js'\nimport { restoreTerminalState } from '../utils/terminal.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * ANSI escape sequences used for TUI rendering.\n */\nconst ESC = '\\x1b'\nconst CSI = `${ESC}[`\n\n/** Save cursor position */\nconst CURSOR_SAVE = `${ESC}7`\n/** Restore cursor position */\nconst CURSOR_RESTORE = `${ESC}8`\n/** Hide cursor */\nconst CURSOR_HIDE = `${CSI}?25l`\n/** Show cursor */\nconst CURSOR_SHOW = `${CSI}?25h`\n/** Clear from cursor to end of line */\nconst CLEAR_LINE = `${CSI}K`\n/** Clear entire screen and reset cursor to top-left */\nconst CLEAR_SCREEN = `${CSI}2J${CSI}H`\n/** Reset scroll region to full terminal */\nconst SCROLL_REGION_RESET = `${CSI}r`\n\n/** Move cursor to row,col (1-based) */\nfunction moveTo(row: number, col: number): string {\n\treturn `${CSI}${row};${col}H`\n}\n\n/** Set scroll region to rows top..bottom (1-based, inclusive) */\nfunction setScrollRegion(top: number, bottom: number): string {\n\treturn `${CSI}${top};${bottom}r`\n}\n\nexport type DevServerStatus = 'Running' | 'Stopped' | 'Restarting'\n\nexport interface DevServerTUIOptions {\n\turl: string\n\tport: number\n\tcontainerPort?: number | undefined\n\tstdout?: NodeJS.WriteStream | undefined\n\tstdin?: NodeJS.ReadStream | undefined\n\tonQuit?: (() => void) | undefined\n\tonRestart?: (() => void) | undefined\n}\n\n/**\n * Height of the status bar area (top border + content + bottom border + hint line).\n */\nconst STATUS_BAR_HEIGHT = 4\n\n/**\n * DevServerTUI - Lightweight terminal UI for dev server.\n *\n * Uses ANSI escape sequences to set a scroll region that restricts output\n * to the upper portion of the terminal, keeping a fixed status bar at the\n * bottom showing the external URL, server status, and port mapping.\n *\n * Keyboard shortcuts:\n * o - open URL in browser\n * c - copy URL to clipboard\n * q - quit (stop container and exit)\n * r - restart (if callback provided)\n */\nexport class DevServerTUI {\n\tprivate readonly url: string\n\tprivate readonly port: number\n\tprivate readonly containerPort: number | undefined\n\tprivate readonly stdout: NodeJS.WriteStream\n\tprivate readonly stdin: NodeJS.ReadStream\n\tprivate readonly onQuit: (() => void) | undefined\n\tprivate readonly onRestart: (() => void) | undefined\n\tprivate status: DevServerStatus = 'Running'\n\tprivate started = false\n\tprivate cleanedUp = false\n\tprivate readonly onData: (data: Buffer) => void\n\tprivate readonly onResize: () => void\n\tprivate readonly onProcessExit: () => void\n\n\tconstructor(options: DevServerTUIOptions) {\n\t\tthis.url = options.url\n\t\tthis.port = options.port\n\t\tthis.containerPort = options.containerPort\n\t\tthis.stdout = options.stdout ?? process.stdout\n\t\tthis.stdin = options.stdin ?? process.stdin\n\t\tthis.onQuit = options.onQuit\n\t\tthis.onRestart = options.onRestart\n\n\t\t// Bind handlers so they can be removed later\n\t\tthis.onData = (data: Buffer): void => this.handleKeypress(data)\n\t\tthis.onResize = (): void => this.handleResize()\n\t\tthis.onProcessExit = (): void => this.cleanup()\n\t}\n\n\t/**\n\t * Start the TUI - sets up scroll region, renders status bar, starts keyboard listener.\n\t */\n\tstart(): void {\n\t\tif (this.started) return\n\t\tthis.started = true\n\n\t\tconst rows = this.stdout.rows ?? 24\n\n\t\t// Hide cursor during TUI operation\n\t\tthis.stdout.write(CURSOR_HIDE)\n\n\t\t// Clear the screen so previous output (build logs, startup messages) doesn't\n\t\t// bleed through the scroll region and create a confusing mix of old and new text\n\t\tthis.stdout.write(CLEAR_SCREEN)\n\n\t\t// Set scroll region: top of terminal to (total rows - status bar height)\n\t\tconst scrollBottom = Math.max(1, rows - STATUS_BAR_HEIGHT)\n\t\tthis.stdout.write(setScrollRegion(1, scrollBottom))\n\n\t\t// Move cursor to top-left of scroll region\n\t\tthis.stdout.write(moveTo(1, 1))\n\n\t\t// Render status bar in the fixed area below scroll region\n\t\tthis.renderStatusBar()\n\n\t\t// Start keyboard listener (raw mode)\n\t\tif (this.stdin.isTTY && typeof this.stdin.setRawMode === 'function') {\n\t\t\tthis.stdin.setRawMode(true)\n\t\t\tthis.stdin.resume()\n\t\t\tthis.stdin.on('data', this.onData)\n\t\t}\n\n\t\t// Listen for terminal resize\n\t\tthis.stdout.on('resize', this.onResize)\n\n\t\t// Ensure terminal state is restored on unexpected exit (uncaught exceptions, etc.)\n\t\tprocess.on('exit', this.onProcessExit)\n\t}\n\n\t/**\n\t * Write server output into the scroll region.\n\t * The scroll region constrains the cursor naturally, so output stays\n\t * above the status bar without explicit cursor repositioning.\n\t */\n\thandleOutput(data: Buffer | string): void {\n\t\tif (!this.started || this.cleanedUp) return\n\n\t\tthis.stdout.write(data.toString())\n\t}\n\n\t/**\n\t * Update the status displayed in the status bar.\n\t */\n\tupdateStatus(status: DevServerStatus): void {\n\t\tthis.status = status\n\t\tif (this.started && !this.cleanedUp) {\n\t\t\tthis.renderStatusBar()\n\t\t}\n\t}\n\n\t/**\n\t * Render the status bar in the fixed area below the scroll region.\n\t */\n\tprivate renderStatusBar(): void {\n\t\tconst rows = this.stdout.rows ?? 24\n\t\tconst cols = this.stdout.columns ?? 80\n\n\t\t// Guard: terminal too small for status bar\n\t\tif (rows < STATUS_BAR_HEIGHT + 2) return\n\n\t\t// Status bar starts at row (rows - STATUS_BAR_HEIGHT + 1)\n\t\tconst barStartRow = rows - STATUS_BAR_HEIGHT + 1\n\n\t\t// Build content segments\n\t\tconst urlSegment = ` ${this.url} `\n\t\tconst statusIcon = this.status === 'Running' ? '\\u25A0' : this.status === 'Stopped' ? '\\u25A1' : '\\u25C6'\n\t\tconst statusSegment = ` ${statusIcon} ${this.status} `\n\t\tconst portSegment = this.containerPort\n\t\t\t? ` Port ${this.containerPort} \\u2192 ${this.port} `\n\t\t\t: ` Port ${this.port} `\n\n\t\t// Build content line with separators\n\t\tconst contentParts = `\\u2502${urlSegment}\\u2502${statusSegment}\\u2502${portSegment}\\u2502`\n\t\t// Pad content to fill width\n\t\tconst contentLine = contentParts.length < cols\n\t\t\t? contentParts + ' '.repeat(cols - contentParts.length)\n\t\t\t: contentParts.substring(0, cols)\n\n\t\t// Horizontal border line\n\t\tconst innerWidth = Math.max(0, cols - 2)\n\t\tconst topBorder = `\\u250C${'\\u2500'.repeat(innerWidth)}\\u2510`\n\t\tconst bottomBorder = `\\u2514${'\\u2500'.repeat(innerWidth)}\\u2518`\n\n\t\t// Hint line showing keyboard shortcuts\n\t\tconst hintLine = ' [o] Open [c] Copy URL [r] Restart [q] Quit'\n\t\tconst paddedHint = hintLine.length < cols\n\t\t\t? hintLine + ' '.repeat(cols - hintLine.length)\n\t\t\t: hintLine.substring(0, cols)\n\n\t\t// Save cursor, write status bar, restore cursor\n\t\tthis.stdout.write(CURSOR_SAVE)\n\n\t\t// Row 1: top border\n\t\tthis.stdout.write(moveTo(barStartRow, 1) + CLEAR_LINE + topBorder)\n\t\t// Row 2: content\n\t\tthis.stdout.write(moveTo(barStartRow + 1, 1) + CLEAR_LINE + contentLine)\n\t\t// Row 3: bottom border\n\t\tthis.stdout.write(moveTo(barStartRow + 2, 1) + CLEAR_LINE + bottomBorder)\n\t\t// Row 4: hint line\n\t\tthis.stdout.write(moveTo(barStartRow + 3, 1) + CLEAR_LINE + paddedHint)\n\n\t\tthis.stdout.write(CURSOR_RESTORE)\n\t}\n\n\t/**\n\t * Handle keyboard input.\n\t */\n\tprivate handleKeypress(data: Buffer): void {\n\t\tconst key = data.toString('utf8')\n\n\t\tswitch (key) {\n\t\t\tcase 'o':\n\t\t\tcase 'O':\n\t\t\t\tvoid openBrowser(this.url).catch((err: unknown) => {\n\t\t\t\t\tlogger.warn(`Failed to open browser: ${err instanceof Error ? err.message : 'Unknown error'}`)\n\t\t\t\t})\n\t\t\t\tbreak\n\n\t\t\tcase 'c':\n\t\t\tcase 'C':\n\t\t\t\tvoid this.copyToClipboard(this.url).catch((err: unknown) => {\n\t\t\t\t\tlogger.warn(`Failed to copy to clipboard: ${err instanceof Error ? err.message : 'Unknown error'}`)\n\t\t\t\t})\n\t\t\t\tbreak\n\n\t\t\tcase 'q':\n\t\t\tcase 'Q':\n\t\t\tcase '\\x03': // Ctrl+C\n\t\t\t\tif (this.onQuit) {\n\t\t\t\t\tthis.onQuit()\n\t\t\t\t}\n\t\t\t\tbreak\n\n\t\t\tcase 'r':\n\t\t\tcase 'R':\n\t\t\t\tif (this.onRestart) {\n\t\t\t\t\tthis.onRestart()\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/**\n\t * Copy text to clipboard using platform-specific commands.\n\t */\n\tprivate async copyToClipboard(text: string): Promise<void> {\n\t\tconst platform = process.platform\n\n\t\tlet command: string\n\t\tlet args: string[]\n\n\t\tif (platform === 'darwin') {\n\t\t\tcommand = 'pbcopy'\n\t\t\targs = []\n\t\t} else if (platform === 'win32') {\n\t\t\tcommand = 'clip'\n\t\t\targs = []\n\t\t} else {\n\t\t\t// Linux\n\t\t\tcommand = 'xclip'\n\t\t\targs = ['-selection', 'clipboard']\n\t\t}\n\n\t\tawait execa(command, args, { input: text })\n\t}\n\n\t/**\n\t * Handle terminal resize - re-establish scroll region and re-render status bar.\n\t */\n\tprivate handleResize(): void {\n\t\tif (!this.started || this.cleanedUp) return\n\n\t\tconst rows = this.stdout.rows ?? 24\n\n\t\t// Guard: terminal too small for status bar\n\t\tif (rows < STATUS_BAR_HEIGHT + 2) return\n\n\t\tconst scrollBottom = Math.max(1, rows - STATUS_BAR_HEIGHT)\n\n\t\t// Re-establish scroll region for new size\n\t\tthis.stdout.write(setScrollRegion(1, scrollBottom))\n\n\t\t// Re-render status bar at new position\n\t\tthis.renderStatusBar()\n\n\t\t// Move cursor back into scroll region\n\t\tthis.stdout.write(moveTo(scrollBottom, 1))\n\t}\n\n\t/**\n\t * Clean up: restore full scroll region, disable raw mode, restore terminal.\n\t */\n\tcleanup(): void {\n\t\tif (this.cleanedUp) return\n\t\tthis.cleanedUp = true\n\n\t\t// Remove event listeners\n\t\tif (this.stdin.isTTY && typeof this.stdin.setRawMode === 'function') {\n\t\t\tthis.stdin.removeListener('data', this.onData)\n\t\t\tthis.stdin.setRawMode(false)\n\t\t\tthis.stdin.pause()\n\t\t}\n\n\t\tthis.stdout.removeListener('resize', this.onResize)\n\t\tprocess.removeListener('exit', this.onProcessExit)\n\n\t\t// Reset scroll region to full terminal\n\t\tthis.stdout.write(SCROLL_REGION_RESET)\n\n\t\t// Show cursor\n\t\tthis.stdout.write(CURSOR_SHOW)\n\n\t\t// Move cursor to bottom of terminal\n\t\tconst rows = this.stdout.rows ?? 24\n\t\tthis.stdout.write(moveTo(rows, 1))\n\t\tthis.stdout.write('\\n')\n\n\t\t// Restore terminal state\n\t\trestoreTerminalState()\n\t}\n}\n"],"mappings":"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","names":["identifier","message"]}
@@ -8,9 +8,9 @@ import {
8
8
  import "./chunk-CQHHEW2M.js";
9
9
  import {
10
10
  AgentManager
11
- } from "./chunk-JD3K2344.js";
11
+ } from "./chunk-QNRXRSKC.js";
12
12
  import "./chunk-WEBMMJKL.js";
13
- import "./chunk-7RCUWU3I.js";
13
+ import "./chunk-MRPIDNZU.js";
14
14
  import {
15
15
  ProjectCapabilityDetector
16
16
  } from "./chunk-772N5WCA.js";
@@ -25,7 +25,7 @@ import {
25
25
  } from "./chunk-DDHWZNGL.js";
26
26
  import {
27
27
  SettingsManager
28
- } from "./chunk-WGUGB54H.js";
28
+ } from "./chunk-F5NKWLMQ.js";
29
29
  import "./chunk-FTYWGQFM.js";
30
30
  import {
31
31
  logger
@@ -167,4 +167,4 @@ ${userBody}`;
167
167
  export {
168
168
  FeedbackCommand
169
169
  };
170
- //# sourceMappingURL=feedback-Q6WG2WX4.js.map
170
+ //# sourceMappingURL=feedback-2LWXKLQZ.js.map
@@ -34,9 +34,9 @@ import {
34
34
  pushBranchToRemote,
35
35
  removePlaceholderCommitFromHead,
36
36
  removePlaceholderCommitFromHistory
37
- } from "./chunk-QNPJXO53.js";
38
- import "./chunk-WGUGB54H.js";
39
- import "./chunk-4JZEQBWV.js";
37
+ } from "./chunk-KGOBNC5A.js";
38
+ import "./chunk-F5NKWLMQ.js";
39
+ import "./chunk-XIVLGWUX.js";
40
40
  import "./chunk-FTYWGQFM.js";
41
41
  import "./chunk-VRPPI6GU.js";
42
42
  export {
@@ -75,4 +75,4 @@ export {
75
75
  removePlaceholderCommitFromHead,
76
76
  removePlaceholderCommitFromHistory
77
77
  };
78
- //# sourceMappingURL=git-TX2IEMB3.js.map
78
+ //# sourceMappingURL=git-IS7AV3ED.js.map
@@ -308,8 +308,46 @@ async function main() {
308
308
 
309
309
  // Special handling for UserPromptSubmit - output JSON additionalContext instead of broadcasting
310
310
  if (status === 'user_prompt_submit') {
311
- // In swarm mode, agents handle their own workflow only remind about code reviewer
311
+ // In swarm mode, check if swarm has completed to show appropriate reminder
312
312
  if (process.env.ILOOM_SWARM === '1') {
313
+ // Read epic metadata to check if swarm has finished
314
+ let swarmCompleted = false;
315
+ try {
316
+ const metadataPath = getMetadataFilePath(cwd);
317
+ if (fs.existsSync(metadataPath)) {
318
+ const content = fs.readFileSync(metadataPath, 'utf8');
319
+ const metadata = JSON.parse(content);
320
+ swarmCompleted = metadata.state === 'done' || metadata.state === 'failed';
321
+ }
322
+ } catch {
323
+ // If we can't read metadata, assume swarm is still running → use minimal reminder
324
+ }
325
+
326
+ if (swarmCompleted) {
327
+ // Swarm finished — show full routing table with swarm-prefixed agents
328
+ const reminder = `**REMINDER**: You MUST USE subagents to preserve your context window for ongoing conversation.
329
+
330
+ | Request Type | Action |
331
+ |--------------|--------|
332
+ | Trivial (quick answer, single-line fix) | Handle directly |
333
+ | Bug investigation / analysis - ESPECIALLY INVOLVING 3rd PARTY APIs/LIBRARIES | \`@agent-iloom-swarm-issue-analyzer\` → present findings → offer to fix |
334
+ | Code changes | \`@agent-iloom-swarm-issue-implementer\` - TELL THE AGENT NOT TO MAKE/UPDATE ISSUE COMMENTS TO AVOID POLLUTION |
335
+ | On 3rd repeated attempt at fixing the same problem | \`@agent-iloom-swarm-issue-analyze-and-plan\` → if approved, \`@agent-iloom-swarm-issue-implementer\` - DO NOT PROVIDE ADDITIONAL GUIDANCE ABOUT ISSUE COMMENTS |
336
+ | New features / complex changes | \`@agent-iloom-swarm-issue-analyze-and-plan\` → if approved, \`@agent-iloom-swarm-issue-implementer\` - IN THIS CASE IT'S OK TO CREATE/UPDATE ISSUE COMMENTS |
337
+ | Deep questions (how/why something works) | \`@agent-iloom-swarm-issue-analyzer\` |
338
+ | Code review request | \`@agent-iloom-swarm-code-reviewer\` |`;
339
+ const output = {
340
+ hookSpecificOutput: {
341
+ hookEventName: 'UserPromptSubmit',
342
+ additionalContext: reminder
343
+ }
344
+ };
345
+ console.log(JSON.stringify(output));
346
+ debug('UserPromptSubmit: swarm completed, output full swarm routing reminder');
347
+ process.exit(0);
348
+ }
349
+
350
+ // Swarm still in progress — agents handle their own workflow, only remind about code reviewer
313
351
  const swarmReminder = `**REMINDER**: When the user requests a code review, use \`@agent-iloom-code-reviewer\`.`;
314
352
  const output = {
315
353
  hookSpecificOutput: {
@@ -318,7 +356,7 @@ async function main() {
318
356
  }
319
357
  };
320
358
  console.log(JSON.stringify(output));
321
- debug('UserPromptSubmit: swarm mode, output code reviewer reminder');
359
+ debug('UserPromptSubmit: swarm in progress, output code reviewer reminder');
322
360
  process.exit(0);
323
361
  }
324
362
 
@@ -2,34 +2,32 @@
2
2
  import {
3
3
  IgniteCommand,
4
4
  WorktreeValidationError
5
- } from "./chunk-TN2D2RX7.js";
6
- import "./chunk-RFCAPHL5.js";
5
+ } from "./chunk-G2DGDCDP.js";
6
+ import "./chunk-C2BVNJW5.js";
7
7
  import "./chunk-7UBEHQTP.js";
8
- import "./chunk-ZUIFO7B4.js";
8
+ import "./chunk-ERMEYFT6.js";
9
9
  import "./chunk-MY2Q3FJ3.js";
10
10
  import "./chunk-EGNUOALL.js";
11
11
  import "./chunk-CQHHEW2M.js";
12
- import "./chunk-JD3K2344.js";
13
- import "./chunk-NTDY5AMO.js";
12
+ import "./chunk-QNRXRSKC.js";
13
+ import "./chunk-ZEFTWM5Z.js";
14
14
  import "./chunk-4232AHNQ.js";
15
- import "./chunk-VIQOQ463.js";
16
- import "./chunk-4VQXMEEP.js";
17
- import "./chunk-7RCUWU3I.js";
15
+ import "./chunk-PPQ5LV7U.js";
16
+ import "./chunk-MRPIDNZU.js";
18
17
  import "./chunk-GYCR2LOU.js";
19
- import "./chunk-K3QGG4O2.js";
20
- import "./chunk-NUUFP53X.js";
18
+ import "./chunk-GPBX2BY2.js";
21
19
  import "./chunk-DMSL5BAP.js";
22
20
  import "./chunk-D4Q7T5KD.js";
23
21
  import "./chunk-KV4NU3RP.js";
24
22
  import "./chunk-NPVA65KS.js";
25
23
  import "./chunk-DDHWZNGL.js";
26
- import "./chunk-QNPJXO53.js";
27
- import "./chunk-WGUGB54H.js";
28
- import "./chunk-4JZEQBWV.js";
24
+ import "./chunk-KGOBNC5A.js";
25
+ import "./chunk-F5NKWLMQ.js";
26
+ import "./chunk-XIVLGWUX.js";
29
27
  import "./chunk-FTYWGQFM.js";
30
28
  import "./chunk-VRPPI6GU.js";
31
29
  export {
32
30
  IgniteCommand,
33
31
  WorktreeValidationError
34
32
  };
35
- //# sourceMappingURL=ignite-P644W2PK.js.map
33
+ //# sourceMappingURL=ignite-VQDJQ37S.js.map