@iloom/cli 0.9.2 → 0.10.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 (231) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +160 -41
  3. package/dist/{BranchNamingService-K6XNWQ6C.js → BranchNamingService-25KSZAEM.js} +2 -2
  4. package/dist/ClaudeContextManager-66GR4BGM.js +14 -0
  5. package/dist/ClaudeService-7KM5NA5Z.js +13 -0
  6. package/dist/{GitHubService-TGWJN4V4.js → GitHubService-MEHKHUQP.js} +4 -4
  7. package/dist/IssueTrackerFactory-NG53YX5S.js +14 -0
  8. package/dist/{LoomLauncher-73NXL2CL.js → LoomLauncher-TDLZSYG2.js} +9 -9
  9. package/dist/{MetadataManager-W3C54UYT.js → MetadataManager-5QZSTKNN.js} +2 -2
  10. package/dist/{ProjectCapabilityDetector-N5L7T4IY.js → ProjectCapabilityDetector-5KSYUTBJ.js} +3 -3
  11. package/dist/{PromptTemplateManager-36YLQRHP.js → PromptTemplateManager-YOE2SIPG.js} +2 -2
  12. package/dist/README.md +160 -41
  13. package/dist/{SettingsManager-AW3JTJHD.js → SettingsManager-FNKCOZMQ.js} +4 -2
  14. package/dist/agents/iloom-artifact-reviewer.md +11 -0
  15. package/dist/agents/iloom-code-reviewer.md +14 -0
  16. package/dist/agents/iloom-issue-analyze-and-plan.md +55 -12
  17. package/dist/agents/iloom-issue-analyzer.md +49 -6
  18. package/dist/agents/iloom-issue-complexity-evaluator.md +47 -6
  19. package/dist/agents/iloom-issue-enhancer.md +86 -7
  20. package/dist/agents/iloom-issue-implementer.md +48 -7
  21. package/dist/agents/iloom-issue-planner.md +115 -62
  22. package/dist/{build-THZI572G.js → build-VHGEMXBA.js} +9 -9
  23. package/dist/chunk-4232AHNQ.js +35 -0
  24. package/dist/chunk-4232AHNQ.js.map +1 -0
  25. package/dist/chunk-4E7LCFUG.js +24 -0
  26. package/dist/chunk-4E7LCFUG.js.map +1 -0
  27. package/dist/{chunk-AR5QKYNE.js → chunk-4FGEGQW4.js} +4 -4
  28. package/dist/{chunk-R4YWBGY6.js → chunk-5FJWO4IT.js} +67 -22
  29. package/dist/chunk-5FJWO4IT.js.map +1 -0
  30. package/dist/{chunk-VPTAX5TR.js → chunk-5RPBYK5Q.js} +35 -30
  31. package/dist/chunk-5RPBYK5Q.js.map +1 -0
  32. package/dist/{chunk-YKFCCV6S.js → chunk-63QWFWH3.js} +7 -7
  33. package/dist/chunk-63QWFWH3.js.map +1 -0
  34. package/dist/{chunk-RI2YL6TK.js → chunk-7VHJNVLF.js} +80 -23
  35. package/dist/chunk-7VHJNVLF.js.map +1 -0
  36. package/dist/{chunk-B7U6OKUR.js → chunk-C6HNNJIV.js} +11 -3
  37. package/dist/chunk-C6HNNJIV.js.map +1 -0
  38. package/dist/{chunk-A7NJF73J.js → chunk-CVCTIDDK.js} +4 -4
  39. package/dist/{chunk-Z2TWEXR7.js → chunk-E6KOWMKA.js} +6 -6
  40. package/dist/chunk-E6KOWMKA.js.map +1 -0
  41. package/dist/{chunk-3I4ONZRT.js → chunk-EVPZFV3K.js} +10 -10
  42. package/dist/chunk-EVPZFV3K.js.map +1 -0
  43. package/dist/{chunk-IZIYLYPK.js → chunk-G5V75JD5.js} +2 -2
  44. package/dist/chunk-GRISNU6G.js +651 -0
  45. package/dist/chunk-GRISNU6G.js.map +1 -0
  46. package/dist/chunk-HEXKPKCK.js +1396 -0
  47. package/dist/chunk-HEXKPKCK.js.map +1 -0
  48. package/dist/{chunk-TC7APDKU.js → chunk-I5T677EA.js} +2 -2
  49. package/dist/{chunk-KBEIQP4G.js → chunk-KB64WNBZ.js} +43 -3
  50. package/dist/chunk-KB64WNBZ.js.map +1 -0
  51. package/dist/{chunk-NWMORW3U.js → chunk-KIK2ZFAL.js} +2 -2
  52. package/dist/{chunk-CWRI4JC3.js → chunk-KKV5WH5M.js} +30 -31
  53. package/dist/chunk-KKV5WH5M.js.map +1 -0
  54. package/dist/{chunk-DGG2VY7B.js → chunk-KVHIAWVT.js} +9 -9
  55. package/dist/chunk-KVHIAWVT.js.map +1 -0
  56. package/dist/{chunk-OFDN5NKS.js → chunk-KXDRI47U.js} +69 -12
  57. package/dist/chunk-KXDRI47U.js.map +1 -0
  58. package/dist/{chunk-NUACL52E.js → chunk-LLHXQS3C.js} +2 -2
  59. package/dist/chunk-LUKXJSRI.js +73 -0
  60. package/dist/chunk-LUKXJSRI.js.map +1 -0
  61. package/dist/{chunk-TL72BGP6.js → chunk-MORRVYPT.js} +2 -2
  62. package/dist/chunk-OTGH2HRS.js +1427 -0
  63. package/dist/chunk-OTGH2HRS.js.map +1 -0
  64. package/dist/{chunk-7ZEHSSUP.js → chunk-P4O6EH46.js} +4 -4
  65. package/dist/{chunk-KAYXR544.js → chunk-QVLPWNE3.js} +2 -2
  66. package/dist/chunk-QZWEJVWV.js +207 -0
  67. package/dist/chunk-QZWEJVWV.js.map +1 -0
  68. package/dist/chunk-RJ3VBUFK.js +781 -0
  69. package/dist/chunk-RJ3VBUFK.js.map +1 -0
  70. package/dist/chunk-RSYT7MVI.js +202 -0
  71. package/dist/chunk-RSYT7MVI.js.map +1 -0
  72. package/dist/{chunk-6IIL5M2L.js → chunk-S7PZA6IV.js} +10 -8
  73. package/dist/{chunk-6IIL5M2L.js.map → chunk-S7PZA6IV.js.map} +1 -1
  74. package/dist/chunk-SKSYYBCU.js +229 -0
  75. package/dist/chunk-SKSYYBCU.js.map +1 -0
  76. package/dist/{chunk-ULSWCPQG.js → chunk-SWSJWA2S.js} +476 -5
  77. package/dist/chunk-SWSJWA2S.js.map +1 -0
  78. package/dist/{chunk-KXGQYLFZ.js → chunk-UKBAJ2QQ.js} +61 -7
  79. package/dist/chunk-UKBAJ2QQ.js.map +1 -0
  80. package/dist/{chunk-FO5GGFOV.js → chunk-UR5DGNUO.js} +71 -9
  81. package/dist/chunk-UR5DGNUO.js.map +1 -0
  82. package/dist/{chunk-QN47QVBX.js → chunk-UUEW5KWB.js} +1 -1
  83. package/dist/chunk-UUEW5KWB.js.map +1 -0
  84. package/dist/{chunk-4CO6KG5S.js → chunk-VG45TUYK.js} +53 -7
  85. package/dist/{chunk-4CO6KG5S.js.map → chunk-VG45TUYK.js.map} +1 -1
  86. package/dist/{chunk-4LKGCFGG.js → chunk-WWKOVDWC.js} +2 -2
  87. package/dist/{chunk-KJTVU3HZ.js → chunk-WXIM2WS7.js} +8 -8
  88. package/dist/chunk-WXIM2WS7.js.map +1 -0
  89. package/dist/{chunk-VOGGLPG5.js → chunk-YQ57ORTV.js} +14 -1
  90. package/dist/chunk-YQ57ORTV.js.map +1 -0
  91. package/dist/{chunk-SOSQILHO.js → chunk-ZNMPGMHY.js} +44 -797
  92. package/dist/chunk-ZNMPGMHY.js.map +1 -0
  93. package/dist/{claude-TP2QO3BU.js → claude-7GGEWVEM.js} +2 -2
  94. package/dist/{cleanup-PJRIFFU4.js → cleanup-6PVAC4NI.js} +85 -34
  95. package/dist/cleanup-6PVAC4NI.js.map +1 -0
  96. package/dist/cli.js +630 -801
  97. package/dist/cli.js.map +1 -1
  98. package/dist/{commit-IVP3M4HG.js → commit-FZR5XDQG.js} +26 -23
  99. package/dist/commit-FZR5XDQG.js.map +1 -0
  100. package/dist/{compile-R2J65HBQ.js → compile-7ALJHZ4N.js} +9 -9
  101. package/dist/{contribute-VDZXHK5Y.js → contribute-5GKLK3BQ.js} +14 -6
  102. package/dist/contribute-5GKLK3BQ.js.map +1 -0
  103. package/dist/{dev-server-7F622OEO.js → dev-server-7SMIB7OF.js} +29 -15
  104. package/dist/dev-server-7SMIB7OF.js.map +1 -0
  105. package/dist/{feedback-E7VET7CL.js → feedback-G2GJFN2F.js} +18 -16
  106. package/dist/{feedback-E7VET7CL.js.map → feedback-G2GJFN2F.js.map} +1 -1
  107. package/dist/{git-2QDQ2X2S.js → git-GTLKAZRJ.js} +4 -4
  108. package/dist/hooks/iloom-hook.js +15 -0
  109. package/dist/ignite-H2O5Y5A2.js +34 -0
  110. package/dist/ignite-H2O5Y5A2.js.map +1 -0
  111. package/dist/index.d.ts +482 -58
  112. package/dist/index.js +1340 -44
  113. package/dist/index.js.map +1 -1
  114. package/dist/{init-676DHF6R.js → init-32YOKXRL.js} +57 -21
  115. package/dist/init-32YOKXRL.js.map +1 -0
  116. package/dist/{issues-PJSOLOBJ.js → issues-4UUAQ5K6.js} +61 -20
  117. package/dist/issues-4UUAQ5K6.js.map +1 -0
  118. package/dist/{lint-CJM7BAIM.js → lint-AAN2NZWG.js} +9 -9
  119. package/dist/mcp/harness-server.js +140 -0
  120. package/dist/mcp/harness-server.js.map +1 -0
  121. package/dist/mcp/issue-management-server.js +2599 -262
  122. package/dist/mcp/issue-management-server.js.map +1 -1
  123. package/dist/mcp/recap-server.js +144 -21
  124. package/dist/mcp/recap-server.js.map +1 -1
  125. package/dist/{neon-helpers-VVFFTLXE.js → neon-helpers-CQN2PB4S.js} +3 -3
  126. package/dist/neon-helpers-CQN2PB4S.js.map +1 -0
  127. package/dist/{open-544H7JF5.js → open-FXWW3VI4.js} +15 -15
  128. package/dist/open-FXWW3VI4.js.map +1 -0
  129. package/dist/{plan-Q7ELXDLC.js → plan-RQ5FPIGF.js} +358 -40
  130. package/dist/plan-RQ5FPIGF.js.map +1 -0
  131. package/dist/{projects-LH362JZQ.js → projects-2UOXFLNZ.js} +4 -4
  132. package/dist/prompts/CLAUDE.md +62 -0
  133. package/dist/prompts/init-prompt.txt +430 -34
  134. package/dist/prompts/issue-prompt.txt +473 -54
  135. package/dist/prompts/plan-prompt.txt +140 -19
  136. package/dist/prompts/pr-prompt.txt +44 -1
  137. package/dist/prompts/regular-prompt.txt +42 -1
  138. package/dist/prompts/session-summary-prompt.txt +14 -0
  139. package/dist/prompts/swarm-orchestrator-prompt.txt +464 -0
  140. package/dist/{rebase-YND35CIE.js → rebase-6NVLX5V7.js} +21 -12
  141. package/dist/rebase-6NVLX5V7.js.map +1 -0
  142. package/dist/{recap-3W7COH7D.js → recap-OMBOKJST.js} +47 -19
  143. package/dist/recap-OMBOKJST.js.map +1 -0
  144. package/dist/{run-QUXJKDQQ.js → run-BBXLRIZB.js} +15 -15
  145. package/dist/run-BBXLRIZB.js.map +1 -0
  146. package/dist/schema/package-iloom.schema.json +58 -0
  147. package/dist/schema/settings.schema.json +149 -15
  148. package/dist/{shell-QGECBLST.js → shell-RF7LTND5.js} +14 -7
  149. package/dist/shell-RF7LTND5.js.map +1 -0
  150. package/dist/{summary-G2T4452H.js → summary-WTQZ7XG2.js} +27 -25
  151. package/dist/summary-WTQZ7XG2.js.map +1 -0
  152. package/dist/{test-EA5NQFDC.js → test-SGO6I5Z7.js} +9 -9
  153. package/dist/{test-git-M7LSLEFL.js → test-git-XM4TM65W.js} +4 -4
  154. package/dist/test-jira-LDTOYFSD.js +96 -0
  155. package/dist/test-jira-LDTOYFSD.js.map +1 -0
  156. package/dist/{test-prefix-64NAAUON.js → test-prefix-GBO37XCN.js} +4 -4
  157. package/dist/{test-webserver-OK6Z5FJM.js → test-webserver-NZ3JTVLL.js} +6 -6
  158. package/dist/{vscode-AR5NNXXI.js → vscode-6XUGHJKL.js} +7 -7
  159. package/package.json +5 -1
  160. package/dist/ClaudeContextManager-HR5JQKAI.js +0 -14
  161. package/dist/ClaudeService-TK7FMC2X.js +0 -13
  162. package/dist/chunk-3I4ONZRT.js.map +0 -1
  163. package/dist/chunk-B7U6OKUR.js.map +0 -1
  164. package/dist/chunk-CWRI4JC3.js.map +0 -1
  165. package/dist/chunk-DGG2VY7B.js.map +0 -1
  166. package/dist/chunk-FJDRTVJX.js +0 -520
  167. package/dist/chunk-FJDRTVJX.js.map +0 -1
  168. package/dist/chunk-FO5GGFOV.js.map +0 -1
  169. package/dist/chunk-KBEIQP4G.js.map +0 -1
  170. package/dist/chunk-KJTVU3HZ.js.map +0 -1
  171. package/dist/chunk-KXGQYLFZ.js.map +0 -1
  172. package/dist/chunk-OFDN5NKS.js.map +0 -1
  173. package/dist/chunk-QN47QVBX.js.map +0 -1
  174. package/dist/chunk-R4YWBGY6.js.map +0 -1
  175. package/dist/chunk-RI2YL6TK.js.map +0 -1
  176. package/dist/chunk-SOSQILHO.js.map +0 -1
  177. package/dist/chunk-ULSWCPQG.js.map +0 -1
  178. package/dist/chunk-VOGGLPG5.js.map +0 -1
  179. package/dist/chunk-VPTAX5TR.js.map +0 -1
  180. package/dist/chunk-W6DP5RVR.js +0 -101
  181. package/dist/chunk-W6DP5RVR.js.map +0 -1
  182. package/dist/chunk-WHI5KEOX.js +0 -121
  183. package/dist/chunk-WHI5KEOX.js.map +0 -1
  184. package/dist/chunk-YKFCCV6S.js.map +0 -1
  185. package/dist/chunk-Z2TWEXR7.js.map +0 -1
  186. package/dist/cleanup-PJRIFFU4.js.map +0 -1
  187. package/dist/commit-IVP3M4HG.js.map +0 -1
  188. package/dist/contribute-VDZXHK5Y.js.map +0 -1
  189. package/dist/dev-server-7F622OEO.js.map +0 -1
  190. package/dist/ignite-IW35CDBD.js +0 -784
  191. package/dist/ignite-IW35CDBD.js.map +0 -1
  192. package/dist/init-676DHF6R.js.map +0 -1
  193. package/dist/issues-PJSOLOBJ.js.map +0 -1
  194. package/dist/open-544H7JF5.js.map +0 -1
  195. package/dist/plan-Q7ELXDLC.js.map +0 -1
  196. package/dist/rebase-YND35CIE.js.map +0 -1
  197. package/dist/recap-3W7COH7D.js.map +0 -1
  198. package/dist/run-QUXJKDQQ.js.map +0 -1
  199. package/dist/shell-QGECBLST.js.map +0 -1
  200. package/dist/summary-G2T4452H.js.map +0 -1
  201. /package/dist/{BranchNamingService-K6XNWQ6C.js.map → BranchNamingService-25KSZAEM.js.map} +0 -0
  202. /package/dist/{ClaudeContextManager-HR5JQKAI.js.map → ClaudeContextManager-66GR4BGM.js.map} +0 -0
  203. /package/dist/{ClaudeService-TK7FMC2X.js.map → ClaudeService-7KM5NA5Z.js.map} +0 -0
  204. /package/dist/{GitHubService-TGWJN4V4.js.map → GitHubService-MEHKHUQP.js.map} +0 -0
  205. /package/dist/{MetadataManager-W3C54UYT.js.map → IssueTrackerFactory-NG53YX5S.js.map} +0 -0
  206. /package/dist/{LoomLauncher-73NXL2CL.js.map → LoomLauncher-TDLZSYG2.js.map} +0 -0
  207. /package/dist/{ProjectCapabilityDetector-N5L7T4IY.js.map → MetadataManager-5QZSTKNN.js.map} +0 -0
  208. /package/dist/{PromptTemplateManager-36YLQRHP.js.map → ProjectCapabilityDetector-5KSYUTBJ.js.map} +0 -0
  209. /package/dist/{SettingsManager-AW3JTJHD.js.map → PromptTemplateManager-YOE2SIPG.js.map} +0 -0
  210. /package/dist/{claude-TP2QO3BU.js.map → SettingsManager-FNKCOZMQ.js.map} +0 -0
  211. /package/dist/{build-THZI572G.js.map → build-VHGEMXBA.js.map} +0 -0
  212. /package/dist/{chunk-AR5QKYNE.js.map → chunk-4FGEGQW4.js.map} +0 -0
  213. /package/dist/{chunk-A7NJF73J.js.map → chunk-CVCTIDDK.js.map} +0 -0
  214. /package/dist/{chunk-IZIYLYPK.js.map → chunk-G5V75JD5.js.map} +0 -0
  215. /package/dist/{chunk-TC7APDKU.js.map → chunk-I5T677EA.js.map} +0 -0
  216. /package/dist/{chunk-NWMORW3U.js.map → chunk-KIK2ZFAL.js.map} +0 -0
  217. /package/dist/{chunk-NUACL52E.js.map → chunk-LLHXQS3C.js.map} +0 -0
  218. /package/dist/{chunk-TL72BGP6.js.map → chunk-MORRVYPT.js.map} +0 -0
  219. /package/dist/{chunk-7ZEHSSUP.js.map → chunk-P4O6EH46.js.map} +0 -0
  220. /package/dist/{chunk-KAYXR544.js.map → chunk-QVLPWNE3.js.map} +0 -0
  221. /package/dist/{chunk-4LKGCFGG.js.map → chunk-WWKOVDWC.js.map} +0 -0
  222. /package/dist/{git-2QDQ2X2S.js.map → claude-7GGEWVEM.js.map} +0 -0
  223. /package/dist/{compile-R2J65HBQ.js.map → compile-7ALJHZ4N.js.map} +0 -0
  224. /package/dist/{neon-helpers-VVFFTLXE.js.map → git-GTLKAZRJ.js.map} +0 -0
  225. /package/dist/{lint-CJM7BAIM.js.map → lint-AAN2NZWG.js.map} +0 -0
  226. /package/dist/{projects-LH362JZQ.js.map → projects-2UOXFLNZ.js.map} +0 -0
  227. /package/dist/{test-EA5NQFDC.js.map → test-SGO6I5Z7.js.map} +0 -0
  228. /package/dist/{test-git-M7LSLEFL.js.map → test-git-XM4TM65W.js.map} +0 -0
  229. /package/dist/{test-prefix-64NAAUON.js.map → test-prefix-GBO37XCN.js.map} +0 -0
  230. /package/dist/{test-webserver-OK6Z5FJM.js.map → test-webserver-NZ3JTVLL.js.map} +0 -0
  231. /package/dist/{vscode-AR5NNXXI.js.map → vscode-6XUGHJKL.js.map} +0 -0
@@ -4,32 +4,35 @@ import {
4
4
  } from "./chunk-BYUMEDDD.js";
5
5
  import {
6
6
  ShellCompletion
7
- } from "./chunk-NWMORW3U.js";
7
+ } from "./chunk-KIK2ZFAL.js";
8
+ import {
9
+ TelemetryService
10
+ } from "./chunk-RSYT7MVI.js";
11
+ import "./chunk-I5T677EA.js";
8
12
  import {
9
13
  FirstRunManager
10
14
  } from "./chunk-Q7POFB5Q.js";
11
15
  import {
12
16
  AgentManager
13
- } from "./chunk-B7U6OKUR.js";
14
- import "./chunk-TC7APDKU.js";
17
+ } from "./chunk-C6HNNJIV.js";
15
18
  import {
16
19
  parseGitRemotes
17
20
  } from "./chunk-FXDYIV3K.js";
21
+ import {
22
+ detectClaudeCli,
23
+ launchClaude
24
+ } from "./chunk-UR5DGNUO.js";
18
25
  import {
19
26
  PromptTemplateManager
20
- } from "./chunk-QN47QVBX.js";
27
+ } from "./chunk-UUEW5KWB.js";
21
28
  import {
22
29
  getRepoRoot,
23
30
  isFileGitignored
24
- } from "./chunk-AR5QKYNE.js";
25
- import "./chunk-RI2YL6TK.js";
26
- import "./chunk-KBEIQP4G.js";
27
- import "./chunk-7JDMYTFZ.js";
28
- import {
29
- detectClaudeCli,
30
- launchClaude
31
- } from "./chunk-FO5GGFOV.js";
31
+ } from "./chunk-4FGEGQW4.js";
32
+ import "./chunk-7VHJNVLF.js";
33
+ import "./chunk-KB64WNBZ.js";
32
34
  import "./chunk-6MLEBAYZ.js";
35
+ import "./chunk-7JDMYTFZ.js";
33
36
  import {
34
37
  logger
35
38
  } from "./chunk-VT4PDUYT.js";
@@ -50,27 +53,42 @@ var InitCommand = class {
50
53
  /**
51
54
  * Main entry point for the init command
52
55
  * @param customInitialMessage Optional custom initial message to send to Claude (defaults to "Help me configure iloom settings.")
56
+ * @param acceptDefaults If true, skip interactive prompts and mark project as configured with defaults
53
57
  */
54
- async execute(customInitialMessage) {
58
+ async execute(customInitialMessage, acceptDefaults) {
55
59
  try {
56
60
  logger.debug("InitCommand.execute() starting", {
57
61
  cwd: process.cwd(),
58
62
  nodeVersion: process.version,
59
- hasCustomInitialMessage: !!customInitialMessage
63
+ hasCustomInitialMessage: !!customInitialMessage,
64
+ acceptDefaults: !!acceptDefaults
60
65
  });
61
66
  logger.info(chalk.bold("Welcome to iloom setup"));
62
67
  logger.info(chalk.bold("Verifying current setup..."));
63
68
  await this.setupProjectConfiguration();
69
+ const mode = acceptDefaults ? "accept-defaults" : customInitialMessage ? "guided-custom-prompt" : "guided";
70
+ try {
71
+ TelemetryService.getInstance().track("init.started", { mode });
72
+ } catch (e) {
73
+ logger.debug("Telemetry tracking failed", { error: e });
74
+ }
75
+ if (acceptDefaults) {
76
+ await this.markProjectConfigured();
77
+ try {
78
+ TelemetryService.getInstance().track("init.completed", { mode });
79
+ } catch (e) {
80
+ logger.debug("Telemetry tracking failed", { error: e });
81
+ }
82
+ logger.info(chalk.green("Setup complete! Enjoy using iloom CLI."));
83
+ return;
84
+ }
64
85
  const guidedInitSucceeded = await this.launchGuidedInit(customInitialMessage);
65
86
  if (guidedInitSucceeded) {
66
- const projectRoot = await getRepoRoot() ?? process.cwd();
67
- const firstRunManager = new FirstRunManager();
68
- const alreadyConfigured = await firstRunManager.isProjectConfigured(projectRoot);
87
+ const alreadyConfigured = await this.isProjectConfigured();
69
88
  if (!alreadyConfigured) {
70
- await firstRunManager.markProjectAsConfigured(projectRoot);
71
- logger.debug("Project marked as configured", { projectRoot });
89
+ await this.markProjectConfigured();
72
90
  } else {
73
- logger.debug("Project already marked as configured, skipping", { projectRoot });
91
+ logger.debug("Project already marked as configured, skipping");
74
92
  }
75
93
  } else {
76
94
  logger.debug("Skipping project marker - guided init did not complete successfully");
@@ -397,6 +415,24 @@ var InitCommand = class {
397
415
  return false;
398
416
  }
399
417
  }
418
+ /**
419
+ * Check if the project is already marked as configured.
420
+ */
421
+ async isProjectConfigured() {
422
+ const projectRoot = await getRepoRoot() ?? process.cwd();
423
+ const firstRunManager = new FirstRunManager();
424
+ return firstRunManager.isProjectConfigured(projectRoot);
425
+ }
426
+ /**
427
+ * Mark the project as configured.
428
+ * Used by both accept-defaults mode and after guided init succeeds.
429
+ */
430
+ async markProjectConfigured() {
431
+ const projectRoot = await getRepoRoot() ?? process.cwd();
432
+ const firstRunManager = new FirstRunManager();
433
+ await firstRunManager.markProjectAsConfigured(projectRoot);
434
+ logger.debug("Project marked as configured", { projectRoot });
435
+ }
400
436
  /**
401
437
  * Load README.md content for init prompt
402
438
  * Walks up from dist directory to find README.md in project root
@@ -425,4 +461,4 @@ var InitCommand = class {
425
461
  export {
426
462
  InitCommand
427
463
  };
428
- //# sourceMappingURL=init-676DHF6R.js.map
464
+ //# sourceMappingURL=init-32YOKXRL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { ShellCompletion } from '../lib/ShellCompletion.js'\nimport chalk from 'chalk'\nimport { mkdir, readFile } from 'fs/promises'\nimport { existsSync } from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { PromptTemplateManager } from '../lib/PromptTemplateManager.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { fileURLToPath } from 'url'\nimport { GitRemote, parseGitRemotes } from '../utils/remote.js'\nimport { SettingsMigrationManager } from '../lib/SettingsMigrationManager.js'\nimport { getRepoRoot, isFileGitignored } from '../utils/git.js'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\nimport { TelemetryService } from '../lib/TelemetryService.js'\n\n/**\n * Initialize iloom configuration\n * Implements the `il init` command requested in issue #94\n */\nexport class InitCommand {\n private readonly shellCompletion: ShellCompletion\n private readonly templateManager: PromptTemplateManager\n private readonly agentManager: AgentManager\n\n constructor(\n shellCompletion?: ShellCompletion,\n templateManager?: PromptTemplateManager,\n agentManager?: AgentManager\n ) {\n this.shellCompletion = shellCompletion ?? new ShellCompletion()\n this.templateManager = templateManager ?? new PromptTemplateManager()\n this.agentManager = agentManager ?? new AgentManager()\n }\n\n /**\n * Main entry point for the init command\n * @param customInitialMessage Optional custom initial message to send to Claude (defaults to \"Help me configure iloom settings.\")\n * @param acceptDefaults If true, skip interactive prompts and mark project as configured with defaults\n */\n public async execute(customInitialMessage?: string, acceptDefaults?: boolean): Promise<void> {\n try {\n logger.debug('InitCommand.execute() starting', {\n cwd: process.cwd(),\n nodeVersion: process.version,\n hasCustomInitialMessage: !!customInitialMessage,\n acceptDefaults: !!acceptDefaults\n })\n\n logger.info(chalk.bold('Welcome to iloom setup'))\n\n // Setup project configuration\n logger.info(chalk.bold('Verifying current setup...'))\n\n await this.setupProjectConfiguration()\n\n // Determine mode for telemetry\n const mode = acceptDefaults ? 'accept-defaults' as const : customInitialMessage ? 'guided-custom-prompt' as const : 'guided' as const\n\n try {\n TelemetryService.getInstance().track('init.started', { mode })\n } catch (e) {\n logger.debug('Telemetry tracking failed', { error: e })\n }\n\n // If accept-defaults mode, mark project as configured and return early\n if (acceptDefaults) {\n await this.markProjectConfigured()\n try {\n TelemetryService.getInstance().track('init.completed', { mode })\n } catch (e) {\n logger.debug('Telemetry tracking failed', { error: e })\n }\n logger.info(chalk.green('Setup complete! Enjoy using iloom CLI.'))\n return\n }\n\n // Launch guided Claude configuration if available\n const guidedInitSucceeded = await this.launchGuidedInit(customInitialMessage)\n\n // Only mark project as configured if guided init succeeded and not already marked\n // This enables VSCode extension detection and ensures project appears in `il projects` list\n if (guidedInitSucceeded) {\n const alreadyConfigured = await this.isProjectConfigured()\n if (!alreadyConfigured) {\n await this.markProjectConfigured()\n } else {\n logger.debug('Project already marked as configured, skipping')\n }\n } else {\n logger.debug('Skipping project marker - guided init did not complete successfully')\n }\n\n logger.info(chalk.green('Setup complete! Enjoy using iloom CLI.'))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.error(`Initialization failed: ${message}`)\n throw error\n }\n }\n\n /**\n * Setup project configuration files\n * Ensures .iloom directory exists and runs legacy migrations\n */\n private async setupProjectConfiguration(): Promise<void> {\n logger.debug('setupProjectConfiguration() starting')\n\n // Migrate legacy .hatchbox settings to .iloom (BEFORE creating new files)\n try {\n logger.debug('Loading SettingsMigrationManager for legacy migration')\n const migrationManager = new SettingsMigrationManager()\n logger.debug('Running settings migration check')\n await migrationManager.migrateSettingsIfNeeded()\n logger.debug('Settings migration check completed')\n } catch (error) {\n // Log warning but don't fail\n logger.warn(`Settings migration failed: ${error instanceof Error ? error.message : 'Unknown'}`)\n logger.debug('Settings migration error details', { error })\n }\n\n // Ensure .iloom directory exists\n const iloomDir = path.join(process.cwd(), '.iloom')\n logger.debug('Creating .iloom directory', { iloomDir })\n await mkdir(iloomDir, { recursive: true })\n logger.debug('.iloom directory created/verified')\n logger.debug('setupProjectConfiguration() completed')\n }\n\n /**\n * Launch interactive Claude-guided configuration\n * @param customInitialMessage Optional custom initial message to send to Claude\n * @returns true if Claude session completed successfully, false otherwise\n */\n private async launchGuidedInit(customInitialMessage?: string): Promise<boolean> {\n logger.debug('launchGuidedInit() starting', { hasCustomInitialMessage: !!customInitialMessage })\n logger.info(chalk.bold('Starting interactive Claude-guided configuration...'))\n\n // Check if Claude CLI is available\n logger.debug('Checking Claude CLI availability')\n const claudeAvailable = await detectClaudeCli()\n logger.debug('Claude CLI availability check result', { claudeAvailable })\n\n if (!claudeAvailable) {\n logger.warn('Claude Code not detected. Skipping guided configuration.')\n logger.info('iloom won\\'t be able to help you much without Claude Code, so please install it: npm install -g @anthropic-ai/claude-code')\n logger.debug('Exiting launchGuidedInit() due to missing Claude CLI')\n return false\n }\n\n try {\n // Load schema from dist/schema/settings.schema.json\n // Use similar approach to PromptTemplateManager for path resolution\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = path.dirname(__filename)\n\n // Walk up to find the schema directory (in case of chunked files)\n let schemaPath = path.join(__dirname, 'schema', 'settings.schema.json')\n\n logger.debug('Loading settings schema', {\n __filename,\n __dirname,\n schemaPath,\n schemaExists: existsSync(schemaPath)\n })\n\n let schemaContent = ''\n if (existsSync(schemaPath)) {\n logger.debug('Reading schema file')\n schemaContent = await readFile(schemaPath, 'utf-8')\n logger.debug('Schema file loaded', {\n contentLength: schemaContent.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(schemaContent)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.warn('Schema file not found - Claude will work without schema validation')\n logger.debug('Schema file not found at expected path', { schemaPath })\n }\n\n // Check for existing settings - read ALL three files if they exist (global, project, local)\n const settingsGlobalPath = path.join(os.homedir(), '.config', 'iloom-ai', 'settings.json')\n const settingsLocalPath = path.join(process.cwd(), '.iloom', 'settings.local.json')\n const settingsCommittedPath = path.join(process.cwd(), '.iloom', 'settings.json')\n\n let settingsGlobalJson = ''\n let settingsJson = ''\n let settingsLocalJson = ''\n\n logger.debug('Checking for settings files', {\n settingsGlobalPath,\n settingsLocalPath,\n settingsCommittedPath,\n globalExists: existsSync(settingsGlobalPath),\n localExists: existsSync(settingsLocalPath),\n committedExists: existsSync(settingsCommittedPath)\n })\n\n // Read global settings.json if it exists\n if (existsSync(settingsGlobalPath)) {\n logger.debug('Reading global settings.json')\n const content = await readFile(settingsGlobalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsGlobalJson = content\n logger.debug('global settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('global settings.json is empty, skipping')\n }\n } else {\n logger.debug('global settings.json does not exist')\n }\n\n // Read settings.json if it exists\n if (existsSync(settingsCommittedPath)) {\n logger.debug('Reading settings.json')\n const content = await readFile(settingsCommittedPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsJson = content\n logger.debug('settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.json is empty, skipping')\n }\n } else {\n logger.debug('settings.json does not exist')\n }\n\n // Read settings.local.json if it exists\n if (existsSync(settingsLocalPath)) {\n logger.debug('Reading settings.local.json')\n const content = await readFile(settingsLocalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsLocalJson = content\n logger.debug('settings.local.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.local.json is empty, skipping')\n }\n } else {\n logger.debug('settings.local.json does not exist')\n }\n\n // Log summary\n logger.debug('Settings files summary', {\n hasSettingsGlobalJson: !!settingsGlobalJson,\n hasSettingsJson: !!settingsJson,\n hasSettingsLocalJson: !!settingsLocalJson,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Detect shell and read config\n logger.debug('Detecting user shell')\n const shell = this.shellCompletion.detectShell()\n logger.debug('Shell detection result', { shell })\n\n let shellConfigPath = ''\n let shellConfigContent = ''\n\n if (shell !== 'unknown') {\n logger.debug('Grepping shell config for completion setup')\n const shellConfig = await this.shellCompletion.grepCompletionConfig(shell)\n if (shellConfig) {\n shellConfigPath = shellConfig.path\n shellConfigContent = shellConfig.content\n logger.debug('Shell config completion grep completed', {\n path: shellConfigPath,\n contentLength: shellConfigContent.length,\n configExists: existsSync(shellConfigPath),\n hasMatches: shellConfigContent.trim().length > 0\n })\n } else {\n logger.debug('Could not read shell config')\n }\n } else {\n logger.debug('Unknown shell detected, skipping config read')\n }\n\n let remotes: GitRemote[] = []\n try {\n // Detect git remotes for GitHub configuration\n logger.debug('Detecting git remotes for GitHub configuration')\n remotes = await parseGitRemotes()\n logger.debug('Git remotes detected', { count: remotes.length, remotes })\n } catch (error) {\n const message = error instanceof Error ? error.stack : 'Unknown error'\n logger.debug(\"Error occured while getting remote info: \", message)\n }\n\n // Detect if .vscode/settings.json is gitignored\n let vscodeSettingsGitignored = false\n try {\n vscodeSettingsGitignored = await isFileGitignored('.vscode/settings.json')\n logger.debug('VSCode settings gitignore status', { vscodeSettingsGitignored })\n } catch (error) {\n logger.debug('Could not detect gitignore status for .vscode/settings.json', { error })\n }\n\n let remotesInfo = ''\n let multipleRemotes = false\n let singleRemote = false\n let singleRemoteName = ''\n let singleRemoteUrl = ''\n let noRemotes = false\n\n if (remotes.length === 0) {\n noRemotes = true\n remotesInfo = 'No git remotes detected in this repository.'\n } else if (remotes.length === 1 && remotes[0]) {\n singleRemote = true\n singleRemoteName = remotes[0].name\n singleRemoteUrl = remotes[0].url\n remotesInfo = `Detected Remote:\\n- **${remotes[0].name}**: ${remotes[0].url} (${remotes[0].owner}/${remotes[0].repo})`\n } else {\n multipleRemotes = true\n remotesInfo = `Detected Remotes (${remotes.length}):\\n` +\n remotes.map(r => `- **${r.name}**: ${r.url} (${r.owner}/${r.repo})`).join('\\n')\n }\n\n // Load README content for comprehensive documentation\n logger.debug('README content loading...')\n const readmeContent = await this.loadReadmeContent()\n logger.debug('README content loaded', {\n readmeContentLength: readmeContent.length,\n })\n\n // Detect if project has package.json for multi-language support\n const packageJsonPath = path.join(process.cwd(), 'package.json')\n const hasPackageJson = existsSync(packageJsonPath)\n logger.debug('Package.json detection', { packageJsonPath, hasPackageJson })\n\n // Build template variables\n const variables = {\n SETTINGS_SCHEMA: schemaContent,\n SETTINGS_GLOBAL_JSON: settingsGlobalJson,\n SETTINGS_JSON: settingsJson,\n SETTINGS_LOCAL_JSON: settingsLocalJson,\n SHELL_TYPE: shell,\n SHELL_CONFIG_PATH: shellConfigPath,\n SHELL_CONFIG_CONTENT: shellConfigContent,\n REMOTES_INFO: remotesInfo,\n MULTIPLE_REMOTES: multipleRemotes.toString(),\n SINGLE_REMOTE: singleRemote.toString(),\n SINGLE_REMOTE_NAME: singleRemoteName,\n SINGLE_REMOTE_URL: singleRemoteUrl,\n NO_REMOTES: noRemotes.toString(),\n README_CONTENT: readmeContent,\n VSCODE_SETTINGS_GITIGNORED: vscodeSettingsGitignored.toString(),\n // Multi-language support - mutually exclusive booleans\n HAS_PACKAGE_JSON: hasPackageJson,\n NO_PACKAGE_JSON: !hasPackageJson,\n }\n\n logger.debug('Building template variables', {\n variableKeys: Object.keys(variables),\n schemaContentLength: schemaContent.length,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Get init prompt\n logger.debug('Loading init prompt template')\n const prompt = await this.templateManager.getPrompt('init', variables)\n\n logger.debug('Init prompt loaded', {\n promptLength: prompt.length,\n containsSchema: prompt.includes('SETTINGS_SCHEMA'),\n containsExistingSettings: prompt.includes('EXISTING_SETTINGS')\n })\n\n // Load framework-detector agent for non-Node.js project setup\n let agents: Record<string, unknown> | undefined\n try {\n const loadedAgents = await this.agentManager.loadAgents(\n undefined, // No settings overrides for init\n variables,\n ['iloom-framework-detector.md']\n )\n agents = this.agentManager.formatForCli(loadedAgents)\n logger.debug('Loaded framework-detector agent for init', {\n agentCount: Object.keys(agents).length,\n agentNames: Object.keys(agents),\n })\n } catch (error) {\n // Log warning but continue without agents\n logger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n }\n\n // Pre-approved tools for init workflow to reduce permission prompts\n const initAllowedTools = [\n 'Bash(git rev-parse:*)',\n 'Bash(git init:*)',\n 'Bash(git status:*)',\n 'Bash(git add:*)',\n 'Bash(git commit:*)',\n 'Read',\n 'Write',\n 'Edit',\n ]\n\n const claudeOptions = {\n model: 'opus',\n headless: false,\n appendSystemPrompt: prompt,\n addDir: process.cwd(),\n allowedTools: initAllowedTools,\n ...(agents && { agents }),\n }\n\n logger.debug('Launching Claude with options', {\n optionKeys: Object.keys(claudeOptions),\n headless: claudeOptions.headless,\n hasSystemPrompt: !!claudeOptions.appendSystemPrompt,\n addDir: claudeOptions.addDir,\n promptLength: prompt.length,\n hasCustomInitialMessage: !!customInitialMessage,\n hasAgents: !!agents,\n })\n\n // Launch Claude in interactive mode with custom initial message if provided\n const initialMessage = customInitialMessage ?? 'Help me configure iloom settings.'\n await launchClaude(initialMessage, claudeOptions)\n logger.debug('Claude session completed successfully')\n return true\n\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.warn(`Guided configuration failed: ${message}`)\n logger.debug('launchGuidedInit() error details', error instanceof Error ? error.stack : {error})\n logger.info('You can manually edit .iloom/settings.json to configure iloom.')\n return false\n }\n }\n\n /**\n * Check if the project is already marked as configured.\n */\n private async isProjectConfigured(): Promise<boolean> {\n const projectRoot = await getRepoRoot() ?? process.cwd()\n const firstRunManager = new FirstRunManager()\n return firstRunManager.isProjectConfigured(projectRoot)\n }\n\n /**\n * Mark the project as configured.\n * Used by both accept-defaults mode and after guided init succeeds.\n */\n private async markProjectConfigured(): Promise<void> {\n const projectRoot = await getRepoRoot() ?? process.cwd()\n const firstRunManager = new FirstRunManager()\n await firstRunManager.markProjectAsConfigured(projectRoot)\n logger.debug('Project marked as configured', { projectRoot })\n }\n\n /**\n * Load README.md content for init prompt\n * Walks up from dist directory to find README.md in project root\n */\n private async loadReadmeContent(): Promise<string> {\n try {\n // Walk up from current file location to find README.md\n // Use same pattern as PromptTemplateManager for finding files\n let currentDir = path.dirname(fileURLToPath(import.meta.url))\n\n // Walk up to find README.md\n while (currentDir !== path.dirname(currentDir)) {\n const readmePath = path.join(currentDir, 'README.md')\n try {\n const content = await readFile(readmePath, 'utf-8')\n logger.debug('Loaded README.md for init prompt', { readmePath })\n return content\n } catch {\n currentDir = path.dirname(currentDir)\n }\n }\n\n logger.debug('README.md not found, returning empty string')\n return ''\n } catch (error) {\n // Graceful degradation - return empty string on error\n logger.debug(`Failed to load README.md: ${error}`)\n return ''\n }\n }\n\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,OAAO,WAAW;AAClB,SAAS,OAAO,gBAAgB;AAChC,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AACjB,OAAO,QAAQ;AAIf,SAAS,qBAAqB;AAWvB,IAAM,cAAN,MAAkB;AAAA,EAKvB,YACE,iBACA,iBACA,cACA;AACA,SAAK,kBAAkB,mBAAmB,IAAI,gBAAgB;AAC9D,SAAK,kBAAkB,mBAAmB,IAAI,sBAAsB;AACpE,SAAK,eAAe,gBAAgB,IAAI,aAAa;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,QAAQ,sBAA+B,gBAAyC;AAC3F,QAAI;AACF,aAAO,MAAM,kCAAkC;AAAA,QAC7C,KAAK,QAAQ,IAAI;AAAA,QACjB,aAAa,QAAQ;AAAA,QACrB,yBAAyB,CAAC,CAAC;AAAA,QAC3B,gBAAgB,CAAC,CAAC;AAAA,MACpB,CAAC;AAED,aAAO,KAAK,MAAM,KAAK,wBAAwB,CAAC;AAGhD,aAAO,KAAK,MAAM,KAAK,4BAA4B,CAAC;AAEpD,YAAM,KAAK,0BAA0B;AAGrC,YAAM,OAAO,iBAAiB,oBAA6B,uBAAuB,yBAAkC;AAEpH,UAAI;AACF,yBAAiB,YAAY,EAAE,MAAM,gBAAgB,EAAE,KAAK,CAAC;AAAA,MAC/D,SAAS,GAAG;AACV,eAAO,MAAM,6BAA6B,EAAE,OAAO,EAAE,CAAC;AAAA,MACxD;AAGA,UAAI,gBAAgB;AAClB,cAAM,KAAK,sBAAsB;AACjC,YAAI;AACF,2BAAiB,YAAY,EAAE,MAAM,kBAAkB,EAAE,KAAK,CAAC;AAAA,QACjE,SAAS,GAAG;AACV,iBAAO,MAAM,6BAA6B,EAAE,OAAO,EAAE,CAAC;AAAA,QACxD;AACA,eAAO,KAAK,MAAM,MAAM,wCAAwC,CAAC;AACjE;AAAA,MACF;AAGA,YAAM,sBAAsB,MAAM,KAAK,iBAAiB,oBAAoB;AAI5E,UAAI,qBAAqB;AACvB,cAAM,oBAAoB,MAAM,KAAK,oBAAoB;AACzD,YAAI,CAAC,mBAAmB;AACtB,gBAAM,KAAK,sBAAsB;AAAA,QACnC,OAAO;AACL,iBAAO,MAAM,gDAAgD;AAAA,QAC/D;AAAA,MACF,OAAO;AACL,eAAO,MAAM,qEAAqE;AAAA,MACpF;AAEA,aAAO,KAAK,MAAM,MAAM,wCAAwC,CAAC;AAAA,IACnE,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,aAAO,MAAM,0BAA0B,OAAO,EAAE;AAChD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,4BAA2C;AACvD,WAAO,MAAM,sCAAsC;AAGnD,QAAI;AACF,aAAO,MAAM,uDAAuD;AACpE,YAAM,mBAAmB,IAAI,yBAAyB;AACtD,aAAO,MAAM,kCAAkC;AAC/C,YAAM,iBAAiB,wBAAwB;AAC/C,aAAO,MAAM,oCAAoC;AAAA,IACnD,SAAS,OAAO;AAEd,aAAO,KAAK,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,SAAS,EAAE;AAC9F,aAAO,MAAM,oCAAoC,EAAE,MAAM,CAAC;AAAA,IAC5D;AAGA,UAAM,WAAW,KAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ;AAClD,WAAO,MAAM,6BAA6B,EAAE,SAAS,CAAC;AACtD,UAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AACzC,WAAO,MAAM,mCAAmC;AAChD,WAAO,MAAM,uCAAuC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,iBAAiB,sBAAiD;AAC9E,WAAO,MAAM,+BAA+B,EAAE,yBAAyB,CAAC,CAAC,qBAAqB,CAAC;AAC/F,WAAO,KAAK,MAAM,KAAK,qDAAqD,CAAC;AAG7E,WAAO,MAAM,kCAAkC;AAC/C,UAAM,kBAAkB,MAAM,gBAAgB;AAC9C,WAAO,MAAM,wCAAwC,EAAE,gBAAgB,CAAC;AAExE,QAAI,CAAC,iBAAiB;AACpB,aAAO,KAAK,0DAA0D;AACtE,aAAO,KAAK,0HAA2H;AACvI,aAAO,MAAM,sDAAsD;AACnE,aAAO;AAAA,IACT;AAEA,QAAI;AAGF,YAAM,aAAa,cAAc,YAAY,GAAG;AAChD,YAAM,YAAY,KAAK,QAAQ,UAAU;AAGzC,UAAI,aAAa,KAAK,KAAK,WAAW,UAAU,sBAAsB;AAEtE,aAAO,MAAM,2BAA2B;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,WAAW,UAAU;AAAA,MACrC,CAAC;AAED,UAAI,gBAAgB;AACpB,UAAI,WAAW,UAAU,GAAG;AAC1B,eAAO,MAAM,qBAAqB;AAClC,wBAAgB,MAAM,SAAS,YAAY,OAAO;AAClD,eAAO,MAAM,sBAAsB;AAAA,UACjC,eAAe,cAAc;AAAA,UAC7B,cAAc,MAAe;AAC3B,gBAAI;AACF,mBAAK,MAAM,aAAa;AACxB,qBAAO;AAAA,YACT,QAAQ;AACN,qBAAO;AAAA,YACT;AAAA,UACF,GAAG;AAAA,QACL,CAAC;AAAA,MACH,OAAO;AACL,eAAO,KAAK,oEAAoE;AAChF,eAAO,MAAM,0CAA0C,EAAE,WAAW,CAAC;AAAA,MACvE;AAGA,YAAM,qBAAqB,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY,eAAe;AACzF,YAAM,oBAAoB,KAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,qBAAqB;AAClF,YAAM,wBAAwB,KAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,eAAe;AAEhF,UAAI,qBAAqB;AACzB,UAAI,eAAe;AACnB,UAAI,oBAAoB;AAExB,aAAO,MAAM,+BAA+B;AAAA,QAC1C;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,WAAW,kBAAkB;AAAA,QAC3C,aAAa,WAAW,iBAAiB;AAAA,QACzC,iBAAiB,WAAW,qBAAqB;AAAA,MACnD,CAAC;AAGD,UAAI,WAAW,kBAAkB,GAAG;AAClC,eAAO,MAAM,8BAA8B;AAC3C,cAAM,UAAU,MAAM,SAAS,oBAAoB,OAAO;AAC1D,cAAM,UAAU,QAAQ,KAAK;AAC7B,YAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,+BAAqB;AACrB,iBAAO,MAAM,+BAA+B;AAAA,YAC1C,eAAe,QAAQ;AAAA,YACvB,cAAc,MAAe;AAC3B,kBAAI;AACF,qBAAK,MAAM,OAAO;AAClB,uBAAO;AAAA,cACT,QAAQ;AACN,uBAAO;AAAA,cACT;AAAA,YACF,GAAG;AAAA,UACL,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,yCAAyC;AAAA,QACxD;AAAA,MACF,OAAO;AACL,eAAO,MAAM,qCAAqC;AAAA,MACpD;AAGA,UAAI,WAAW,qBAAqB,GAAG;AACrC,eAAO,MAAM,uBAAuB;AACpC,cAAM,UAAU,MAAM,SAAS,uBAAuB,OAAO;AAC7D,cAAM,UAAU,QAAQ,KAAK;AAC7B,YAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,yBAAe;AACf,iBAAO,MAAM,wBAAwB;AAAA,YACnC,eAAe,QAAQ;AAAA,YACvB,cAAc,MAAe;AAC3B,kBAAI;AACF,qBAAK,MAAM,OAAO;AAClB,uBAAO;AAAA,cACT,QAAQ;AACN,uBAAO;AAAA,cACT;AAAA,YACF,GAAG;AAAA,UACL,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,kCAAkC;AAAA,QACjD;AAAA,MACF,OAAO;AACL,eAAO,MAAM,8BAA8B;AAAA,MAC7C;AAGA,UAAI,WAAW,iBAAiB,GAAG;AACjC,eAAO,MAAM,6BAA6B;AAC1C,cAAM,UAAU,MAAM,SAAS,mBAAmB,OAAO;AACzD,cAAM,UAAU,QAAQ,KAAK;AAC7B,YAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,8BAAoB;AACpB,iBAAO,MAAM,8BAA8B;AAAA,YACzC,eAAe,QAAQ;AAAA,YACvB,cAAc,MAAe;AAC3B,kBAAI;AACF,qBAAK,MAAM,OAAO;AAClB,uBAAO;AAAA,cACT,QAAQ;AACN,uBAAO;AAAA,cACT;AAAA,YACF,GAAG;AAAA,UACL,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,wCAAwC;AAAA,QACvD;AAAA,MACF,OAAO;AACL,eAAO,MAAM,oCAAoC;AAAA,MACnD;AAGA,aAAO,MAAM,0BAA0B;AAAA,QACrC,uBAAuB,CAAC,CAAC;AAAA,QACzB,iBAAiB,CAAC,CAAC;AAAA,QACnB,sBAAsB,CAAC,CAAC;AAAA,QACxB,0BAA0B,mBAAmB;AAAA,QAC7C,oBAAoB,aAAa;AAAA,QACjC,yBAAyB,kBAAkB;AAAA,MAC7C,CAAC;AAGD,aAAO,MAAM,sBAAsB;AACnC,YAAM,QAAQ,KAAK,gBAAgB,YAAY;AAC/C,aAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC;AAEhD,UAAI,kBAAkB;AACtB,UAAI,qBAAqB;AAEzB,UAAI,UAAU,WAAW;AACvB,eAAO,MAAM,4CAA4C;AACzD,cAAM,cAAc,MAAM,KAAK,gBAAgB,qBAAqB,KAAK;AACzE,YAAI,aAAa;AACf,4BAAkB,YAAY;AAC9B,+BAAqB,YAAY;AACjC,iBAAO,MAAM,0CAA0C;AAAA,YACrD,MAAM;AAAA,YACN,eAAe,mBAAmB;AAAA,YAClC,cAAc,WAAW,eAAe;AAAA,YACxC,YAAY,mBAAmB,KAAK,EAAE,SAAS;AAAA,UACjD,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,6BAA6B;AAAA,QAC5C;AAAA,MACF,OAAO;AACL,eAAO,MAAM,8CAA8C;AAAA,MAC7D;AAEA,UAAI,UAAuB,CAAC;AAC5B,UAAI;AAEF,eAAO,MAAM,gDAAgD;AAC7D,kBAAU,MAAM,gBAAgB;AAChC,eAAO,MAAM,wBAAwB,EAAE,OAAO,QAAQ,QAAQ,QAAQ,CAAC;AAAA,MACzE,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,QAAQ;AACvD,eAAO,MAAM,6CAA6C,OAAO;AAAA,MACnE;AAGA,UAAI,2BAA2B;AAC/B,UAAI;AACF,mCAA2B,MAAM,iBAAiB,uBAAuB;AACzE,eAAO,MAAM,oCAAoC,EAAE,yBAAyB,CAAC;AAAA,MAC/E,SAAS,OAAO;AACd,eAAO,MAAM,+DAA+D,EAAE,MAAM,CAAC;AAAA,MACvF;AAEA,UAAI,cAAc;AAClB,UAAI,kBAAkB;AACtB,UAAI,eAAe;AACnB,UAAI,mBAAmB;AACvB,UAAI,kBAAkB;AACtB,UAAI,YAAY;AAEhB,UAAI,QAAQ,WAAW,GAAG;AACxB,oBAAY;AACZ,sBAAc;AAAA,MAChB,WAAW,QAAQ,WAAW,KAAK,QAAQ,CAAC,GAAG;AAC7C,uBAAe;AACf,2BAAmB,QAAQ,CAAC,EAAE;AAC9B,0BAAkB,QAAQ,CAAC,EAAE;AAC7B,sBAAc;AAAA,MAAyB,QAAQ,CAAC,EAAE,IAAI,OAAO,QAAQ,CAAC,EAAE,GAAG,KAAK,QAAQ,CAAC,EAAE,KAAK,IAAI,QAAQ,CAAC,EAAE,IAAI;AAAA,MACrH,OAAO;AACL,0BAAkB;AAClB,sBAAc,qBAAqB,QAAQ,MAAM;AAAA,IAC/C,QAAQ,IAAI,OAAK,OAAO,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,IAAI,GAAG,EAAE,KAAK,IAAI;AAAA,MAClF;AAGA,aAAO,MAAM,2BAA2B;AACxC,YAAM,gBAAgB,MAAM,KAAK,kBAAkB;AACnD,aAAO,MAAM,yBAAyB;AAAA,QACpC,qBAAqB,cAAc;AAAA,MACrC,CAAC;AAGD,YAAM,kBAAkB,KAAK,KAAK,QAAQ,IAAI,GAAG,cAAc;AAC/D,YAAM,iBAAiB,WAAW,eAAe;AACjD,aAAO,MAAM,0BAA0B,EAAE,iBAAiB,eAAe,CAAC;AAG1E,YAAM,YAAY;AAAA,QAChB,iBAAiB;AAAA,QACjB,sBAAsB;AAAA,QACtB,eAAe;AAAA,QACf,qBAAqB;AAAA,QACrB,YAAY;AAAA,QACZ,mBAAmB;AAAA,QACnB,sBAAsB;AAAA,QACtB,cAAc;AAAA,QACd,kBAAkB,gBAAgB,SAAS;AAAA,QAC3C,eAAe,aAAa,SAAS;AAAA,QACrC,oBAAoB;AAAA,QACpB,mBAAmB;AAAA,QACnB,YAAY,UAAU,SAAS;AAAA,QAC/B,gBAAgB;AAAA,QAChB,4BAA4B,yBAAyB,SAAS;AAAA;AAAA,QAE9D,kBAAkB;AAAA,QAClB,iBAAiB,CAAC;AAAA,MACpB;AAEA,aAAO,MAAM,+BAA+B;AAAA,QAC1C,cAAc,OAAO,KAAK,SAAS;AAAA,QACnC,qBAAqB,cAAc;AAAA,QACnC,0BAA0B,mBAAmB;AAAA,QAC7C,oBAAoB,aAAa;AAAA,QACjC,yBAAyB,kBAAkB;AAAA,MAC7C,CAAC;AAGD,aAAO,MAAM,8BAA8B;AAC3C,YAAM,SAAS,MAAM,KAAK,gBAAgB,UAAU,QAAQ,SAAS;AAErE,aAAO,MAAM,sBAAsB;AAAA,QACjC,cAAc,OAAO;AAAA,QACrB,gBAAgB,OAAO,SAAS,iBAAiB;AAAA,QACjD,0BAA0B,OAAO,SAAS,mBAAmB;AAAA,MAC/D,CAAC;AAGD,UAAI;AACJ,UAAI;AACF,cAAM,eAAe,MAAM,KAAK,aAAa;AAAA,UAC3C;AAAA;AAAA,UACA;AAAA,UACA,CAAC,6BAA6B;AAAA,QAChC;AACA,iBAAS,KAAK,aAAa,aAAa,YAAY;AACpD,eAAO,MAAM,4CAA4C;AAAA,UACvD,YAAY,OAAO,KAAK,MAAM,EAAE;AAAA,UAChC,YAAY,OAAO,KAAK,MAAM;AAAA,QAChC,CAAC;AAAA,MACH,SAAS,OAAO;AAEd,eAAO,KAAK,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AAAA,MAClG;AAGA,YAAM,mBAAmB;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,gBAAgB;AAAA,QACpB,OAAO;AAAA,QACP,UAAU;AAAA,QACV,oBAAoB;AAAA,QACpB,QAAQ,QAAQ,IAAI;AAAA,QACpB,cAAc;AAAA,QACd,GAAI,UAAU,EAAE,OAAO;AAAA,MACzB;AAEA,aAAO,MAAM,iCAAiC;AAAA,QAC5C,YAAY,OAAO,KAAK,aAAa;AAAA,QACrC,UAAU,cAAc;AAAA,QACxB,iBAAiB,CAAC,CAAC,cAAc;AAAA,QACjC,QAAQ,cAAc;AAAA,QACtB,cAAc,OAAO;AAAA,QACrB,yBAAyB,CAAC,CAAC;AAAA,QAC3B,WAAW,CAAC,CAAC;AAAA,MACf,CAAC;AAGD,YAAM,iBAAiB,wBAAwB;AAC/C,YAAM,aAAa,gBAAgB,aAAa;AAChD,aAAO,MAAM,uCAAuC;AACpD,aAAO;AAAA,IAET,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,aAAO,KAAK,gCAAgC,OAAO,EAAE;AACrD,aAAO,MAAM,oCAAoC,iBAAiB,QAAQ,MAAM,QAAQ,EAAC,MAAK,CAAC;AAC/F,aAAO,KAAK,gEAAgE;AAC5E,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,sBAAwC;AACpD,UAAM,cAAc,MAAM,YAAY,KAAK,QAAQ,IAAI;AACvD,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,WAAO,gBAAgB,oBAAoB,WAAW;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,wBAAuC;AACnD,UAAM,cAAc,MAAM,YAAY,KAAK,QAAQ,IAAI;AACvD,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,gBAAgB,wBAAwB,WAAW;AACzD,WAAO,MAAM,gCAAgC,EAAE,YAAY,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,oBAAqC;AACjD,QAAI;AAGF,UAAI,aAAa,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG5D,aAAO,eAAe,KAAK,QAAQ,UAAU,GAAG;AAC9C,cAAM,aAAa,KAAK,KAAK,YAAY,WAAW;AACpD,YAAI;AACF,gBAAM,UAAU,MAAM,SAAS,YAAY,OAAO;AAClD,iBAAO,MAAM,oCAAoC,EAAE,WAAW,CAAC;AAC/D,iBAAO;AAAA,QACT,QAAQ;AACN,uBAAa,KAAK,QAAQ,UAAU;AAAA,QACtC;AAAA,MACF;AAEA,aAAO,MAAM,6CAA6C;AAC1D,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,aAAO,MAAM,6BAA6B,KAAK,EAAE;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AAEF;","names":[]}
@@ -1,26 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- IssueTrackerFactory
4
- } from "./chunk-KXGQYLFZ.js";
5
- import {
6
- fetchLinearIssueList
7
- } from "./chunk-FJDRTVJX.js";
3
+ fetchJiraIssueList
4
+ } from "./chunk-4232AHNQ.js";
8
5
  import {
9
6
  findMainWorktreePathWithSettings
10
- } from "./chunk-AR5QKYNE.js";
7
+ } from "./chunk-4FGEGQW4.js";
11
8
  import {
12
9
  SettingsManager
13
- } from "./chunk-RI2YL6TK.js";
14
- import "./chunk-KBEIQP4G.js";
15
- import "./chunk-OFDN5NKS.js";
10
+ } from "./chunk-7VHJNVLF.js";
11
+ import "./chunk-KB64WNBZ.js";
12
+ import {
13
+ IssueTrackerFactory
14
+ } from "./chunk-UKBAJ2QQ.js";
15
+ import {
16
+ JiraApiClient,
17
+ fetchLinearIssueList
18
+ } from "./chunk-HEXKPKCK.js";
19
+ import "./chunk-KXDRI47U.js";
16
20
  import {
17
21
  fetchGitHubIssueList,
18
22
  fetchGitHubPRList
19
- } from "./chunk-4CO6KG5S.js";
20
- import "./chunk-7JDMYTFZ.js";
23
+ } from "./chunk-VG45TUYK.js";
21
24
  import {
22
25
  getLogger
23
26
  } from "./chunk-6MLEBAYZ.js";
27
+ import "./chunk-7JDMYTFZ.js";
24
28
  import "./chunk-VT4PDUYT.js";
25
29
 
26
30
  // src/commands/issues.ts
@@ -30,8 +34,8 @@ import fs from "fs-extra";
30
34
  import crypto from "crypto";
31
35
  var CACHE_TTL_MS = 2 * 60 * 1e3;
32
36
  var CACHE_DIR = path.join(os.homedir(), ".config", "iloom-ai", "cache");
33
- function getCacheFilePath(projectPath, provider, limit) {
34
- const hash = crypto.createHash("md5").update(`${projectPath}:${provider}:${limit}`).digest("hex").slice(0, 12);
37
+ function getCacheFilePath(projectPath, provider, limit, sprint, mine) {
38
+ const hash = crypto.createHash("md5").update(`${projectPath}:${provider}:${limit}:${sprint ?? ""}:${mine ? "mine" : ""}`).digest("hex").slice(0, 12);
35
39
  return path.join(CACHE_DIR, `issues-${hash}.json`);
36
40
  }
37
41
  async function readCacheFile(cacheFilePath) {
@@ -64,9 +68,11 @@ var IssuesCommand = class {
64
68
  * @returns Array of issue list items
65
69
  */
66
70
  async execute(options) {
67
- var _a, _b, _c, _d;
71
+ var _a, _b, _c, _d, _e;
68
72
  const logger = getLogger();
69
73
  const limit = (options == null ? void 0 : options.limit) ?? 100;
74
+ const sprint = options == null ? void 0 : options.sprint;
75
+ const mine = options == null ? void 0 : options.mine;
70
76
  let resolvedProjectPath;
71
77
  if (options == null ? void 0 : options.projectPath) {
72
78
  resolvedProjectPath = options.projectPath;
@@ -80,7 +86,10 @@ var IssuesCommand = class {
80
86
  }
81
87
  const settings = await this.settingsManager.loadSettings(resolvedProjectPath);
82
88
  const provider = IssueTrackerFactory.getProviderName(settings);
83
- const cacheFilePath = getCacheFilePath(resolvedProjectPath, provider, limit);
89
+ if (provider !== "jira" && sprint) {
90
+ logger.warn("--sprint flag is only supported with the Jira issue tracker. Ignoring.");
91
+ }
92
+ const cacheFilePath = getCacheFilePath(resolvedProjectPath, provider, limit, sprint, mine);
84
93
  const cached = await readCacheFile(cacheFilePath);
85
94
  if (cached !== null) {
86
95
  logger.debug(`Returning cached issues (${cached.length} items)`);
@@ -90,7 +99,8 @@ var IssuesCommand = class {
90
99
  if (provider === "github") {
91
100
  results = await fetchGitHubIssueList({
92
101
  limit,
93
- cwd: resolvedProjectPath
102
+ cwd: resolvedProjectPath,
103
+ ...mine ? { mine } : {}
94
104
  });
95
105
  } else if (provider === "linear") {
96
106
  const teamId = (_b = (_a = settings.issueManagement) == null ? void 0 : _a.linear) == null ? void 0 : _b.teamId;
@@ -102,8 +112,38 @@ var IssuesCommand = class {
102
112
  const apiToken = ((_d = (_c = settings.issueManagement) == null ? void 0 : _c.linear) == null ? void 0 : _d.apiToken) ?? process.env.LINEAR_API_TOKEN;
103
113
  results = await fetchLinearIssueList(teamId, {
104
114
  limit,
105
- ...apiToken ? { apiToken } : {}
115
+ ...apiToken ? { apiToken } : {},
116
+ ...mine ? { mine } : {}
106
117
  });
118
+ } else if (provider === "jira") {
119
+ const jiraSettings = (_e = settings.issueManagement) == null ? void 0 : _e.jira;
120
+ const host = jiraSettings == null ? void 0 : jiraSettings.host;
121
+ if (!host) {
122
+ throw new Error(
123
+ "Jira host not configured. Set issueManagement.jira.host in your settings.json."
124
+ );
125
+ }
126
+ const username = jiraSettings == null ? void 0 : jiraSettings.username;
127
+ if (!username) {
128
+ throw new Error(
129
+ "Jira username not configured. Set issueManagement.jira.username in your settings.json."
130
+ );
131
+ }
132
+ const apiToken = jiraSettings == null ? void 0 : jiraSettings.apiToken;
133
+ if (!apiToken) {
134
+ throw new Error(
135
+ "Jira API token not configured. Set issueManagement.jira.apiToken in your settings.json or settings.local.json."
136
+ );
137
+ }
138
+ const projectKey = jiraSettings == null ? void 0 : jiraSettings.projectKey;
139
+ if (!projectKey) {
140
+ throw new Error(
141
+ "Jira project key not configured. Set issueManagement.jira.projectKey in your settings.json."
142
+ );
143
+ }
144
+ const doneStatuses = jiraSettings == null ? void 0 : jiraSettings.doneStatuses;
145
+ const client = new JiraApiClient({ host, username, apiToken });
146
+ results = await fetchJiraIssueList(client, { host, projectKey, doneStatuses, limit, sprint, mine });
107
147
  } else {
108
148
  throw new Error(`Unsupported issue tracker provider: ${provider}`);
109
149
  }
@@ -113,13 +153,14 @@ var IssuesCommand = class {
113
153
  try {
114
154
  const prs = await fetchGitHubPRList({
115
155
  limit,
116
- cwd: resolvedProjectPath
156
+ cwd: resolvedProjectPath,
157
+ ...mine ? { mine } : {}
117
158
  });
118
159
  const prItems = prs.map((pr) => ({ ...pr, type: "pr" }));
119
160
  results = [...results, ...prItems];
120
161
  } catch (error) {
121
162
  const stderr = error.stderr ?? "";
122
- const isExpectedError = error instanceof Error && (error.message.includes("not logged in") || error.message.includes("auth login") || error.message.includes("rate limit") || error.message.includes("ETIMEDOUT") || error.message.includes("ECONNREFUSED") || stderr.includes("not logged in") || stderr.includes("rate limit"));
163
+ const isExpectedError = error instanceof Error && (error.message.includes("not logged in") || error.message.includes("auth login") || error.message.includes("rate limit") || error.message.includes("ETIMEDOUT") || error.message.includes("ECONNREFUSED") || error.message.includes("no git remotes found") || stderr.includes("not logged in") || stderr.includes("rate limit"));
123
164
  if (isExpectedError) {
124
165
  logger.warn(`PR fetch failed (non-fatal), continuing with issues only: ${error.message}`);
125
166
  } else {
@@ -135,4 +176,4 @@ var IssuesCommand = class {
135
176
  export {
136
177
  IssuesCommand
137
178
  };
138
- //# sourceMappingURL=issues-PJSOLOBJ.js.map
179
+ //# sourceMappingURL=issues-4UUAQ5K6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/issues.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport crypto from 'crypto'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { fetchGitHubIssueList, fetchGitHubPRList } from '../utils/github.js'\nimport { fetchLinearIssueList } from '../utils/linear.js'\nimport { fetchJiraIssueList } from '../utils/jira.js'\nimport { JiraApiClient } from '../lib/providers/jira/index.js'\nimport { getLogger } from '../utils/logger-context.js'\n\n/**\n * Unified output interface for issues from any provider\n */\nexport interface IssueListItem {\n id: string\n title: string\n updatedAt: string\n url: string\n state: string\n type?: 'issue' | 'pr'\n}\n\n/**\n * File-based cache structure (follows UpdateNotifier pattern)\n */\ninterface IssuesCacheFile {\n timestamp: number // Date.now() when cached\n projectPath: string // for verification\n provider: string // 'github' | 'linear' | 'jira'\n data: IssueListItem[]\n}\n\n// Cache configuration\nconst CACHE_TTL_MS = 2 * 60 * 1000 // 2 minutes\nconst CACHE_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'cache')\n\n/**\n * Generate a deterministic cache file path from project path + provider\n */\nfunction getCacheFilePath(projectPath: string, provider: string, limit: number, sprint?: string, mine?: boolean): string {\n const hash = crypto.createHash('md5').update(`${projectPath}:${provider}:${limit}:${sprint ?? ''}:${mine ? 'mine' : ''}`).digest('hex').slice(0, 12)\n return path.join(CACHE_DIR, `issues-${hash}.json`)\n}\n\n/**\n * Read cache file, return null if missing/expired/corrupted\n * Follows UpdateNotifier.getCachedCheck pattern\n */\nasync function readCacheFile(cacheFilePath: string): Promise<IssueListItem[] | null> {\n try {\n if (!fs.existsSync(cacheFilePath)) return null\n const content = await fs.readFile(cacheFilePath, 'utf8')\n const cache = JSON.parse(content) as IssuesCacheFile\n if (Date.now() - cache.timestamp < CACHE_TTL_MS) return cache.data\n return null // expired\n } catch {\n return null // corrupted or unreadable, treat as cache miss\n }\n}\n\n/**\n * Write cache file following UpdateNotifier.saveCacheFile pattern\n */\nasync function writeCacheFile(\n cacheFilePath: string,\n data: IssueListItem[],\n projectPath: string,\n provider: string,\n): Promise<void> {\n try {\n await fs.ensureDir(CACHE_DIR)\n const cache: IssuesCacheFile = { timestamp: Date.now(), projectPath, provider, data }\n await fs.writeFile(cacheFilePath, JSON.stringify(cache, null, 2), 'utf8')\n } catch {\n // Cache write failure is non-fatal, just log debug\n getLogger().debug(`Failed to write issues cache to ${cacheFilePath}`)\n }\n}\n\nexport interface IssuesCommandOptions {\n projectPath?: string | undefined\n limit?: number | undefined\n sprint?: string | undefined\n mine?: boolean | undefined\n}\n\n/**\n * IssuesCommand: List open issues from the configured issue tracker\n *\n * Returns JSON array of issues. Uses file-based caching with ~2 minute TTL.\n * Follows the ProjectsCommand pattern for structure.\n */\nexport class IssuesCommand {\n private readonly settingsManager: SettingsManager\n\n constructor(settingsManager?: SettingsManager) {\n this.settingsManager = settingsManager ?? new SettingsManager()\n }\n\n /**\n * Execute the issues command\n * @param options - Command options\n * @returns Array of issue list items\n */\n async execute(options?: IssuesCommandOptions): Promise<IssueListItem[]> {\n const logger = getLogger()\n const limit = options?.limit ?? 100\n const sprint = options?.sprint\n const mine = options?.mine\n\n // 1. Resolve project root\n let resolvedProjectPath: string\n if (options?.projectPath) {\n resolvedProjectPath = options.projectPath\n } else {\n try {\n resolvedProjectPath = await findMainWorktreePathWithSettings()\n } catch {\n logger.debug('Failed to resolve worktree path, falling back to cwd')\n resolvedProjectPath = process.cwd()\n }\n }\n\n // 2. Load settings from resolved root\n const settings = await this.settingsManager.loadSettings(resolvedProjectPath)\n\n // 3. Determine provider\n const provider = IssueTrackerFactory.getProviderName(settings)\n\n // Warn if Jira-only flags used with non-Jira provider (--sprint is Jira-only; --mine works across all providers)\n if (provider !== 'jira' && sprint) {\n logger.warn('--sprint flag is only supported with the Jira issue tracker. Ignoring.')\n }\n\n // 4. Check file-based cache\n const cacheFilePath = getCacheFilePath(resolvedProjectPath, provider, limit, sprint, mine)\n const cached = await readCacheFile(cacheFilePath)\n if (cached !== null) {\n logger.debug(`Returning cached issues (${cached.length} items)`)\n // Backfill type field for cache entries from before PR support was added\n return cached.map(item => ({ type: 'issue' as const, ...item }))\n }\n\n // 5. Fetch issues based on provider\n let results: IssueListItem[]\n\n if (provider === 'github') {\n results = await fetchGitHubIssueList({\n limit,\n cwd: resolvedProjectPath,\n ...(mine ? { mine } : {}),\n })\n } else if (provider === 'linear') {\n const teamId = settings.issueManagement?.linear?.teamId\n if (!teamId) {\n throw new Error(\n 'Linear team ID not configured. Set issueManagement.linear.teamId in your settings.json.',\n )\n }\n const apiToken = settings.issueManagement?.linear?.apiToken ?? process.env.LINEAR_API_TOKEN\n results = await fetchLinearIssueList(teamId, {\n limit,\n ...(apiToken ? { apiToken } : {}),\n ...(mine ? { mine } : {}),\n })\n } else if (provider === 'jira') {\n const jiraSettings = settings.issueManagement?.jira\n const host = jiraSettings?.host\n if (!host) {\n throw new Error(\n 'Jira host not configured. Set issueManagement.jira.host in your settings.json.',\n )\n }\n const username = jiraSettings?.username\n if (!username) {\n throw new Error(\n 'Jira username not configured. Set issueManagement.jira.username in your settings.json.',\n )\n }\n const apiToken = jiraSettings?.apiToken\n if (!apiToken) {\n throw new Error(\n 'Jira API token not configured. Set issueManagement.jira.apiToken in your settings.json or settings.local.json.',\n )\n }\n const projectKey = jiraSettings?.projectKey\n if (!projectKey) {\n throw new Error(\n 'Jira project key not configured. Set issueManagement.jira.projectKey in your settings.json.',\n )\n }\n const doneStatuses = jiraSettings?.doneStatuses\n const client = new JiraApiClient({ host, username, apiToken })\n results = await fetchJiraIssueList(client, { host, projectKey, doneStatuses, limit, sprint, mine })\n } else {\n throw new Error(`Unsupported issue tracker provider: ${provider}`)\n }\n\n // Tag issues with type\n results.forEach(item => { item.type = 'issue' })\n\n // 6. Fetch PRs from GitHub (PRs are a GitHub concept regardless of issue tracker)\n // TODO(bitbucket): detect bitbucket configuration and fetch PRs from Bitbucket instead of GitHub when relevant\n try {\n const prs = await fetchGitHubPRList({\n limit,\n cwd: resolvedProjectPath,\n ...(mine ? { mine } : {}),\n })\n const prItems: IssueListItem[] = prs.map(pr => ({ ...pr, type: 'pr' as const }))\n results = [...results, ...prItems]\n } catch (error) {\n // Only catch expected, non-fatal errors from gh CLI\n // Per CLAUDE.md: \"DO NOT SWALLOW ERRORS\" -- must check specifically\n const stderr = (error as NodeJS.ErrnoException & { stderr?: string }).stderr ?? ''\n const isExpectedError = error instanceof Error && (\n error.message.includes('not logged in') ||\n error.message.includes('auth login') ||\n error.message.includes('rate limit') ||\n error.message.includes('ETIMEDOUT') ||\n error.message.includes('ECONNREFUSED') ||\n error.message.includes('no git remotes found') ||\n stderr.includes('not logged in') ||\n stderr.includes('rate limit')\n )\n if (isExpectedError) {\n logger.warn(`PR fetch failed (non-fatal), continuing with issues only: ${error.message}`)\n } else {\n throw error // Re-throw unexpected errors -- do not swallow\n }\n }\n\n // 7. Sort by updatedAt descending and apply limit\n results.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n results = results.slice(0, limit)\n\n // 8. Write results to cache file\n await writeCacheFile(cacheFilePath, results, resolvedProjectPath, provider)\n\n return results\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ScriptCommandBase
4
- } from "./chunk-KJTVU3HZ.js";
5
- import "./chunk-YKFCCV6S.js";
6
- import "./chunk-4LKGCFGG.js";
7
- import "./chunk-TC7APDKU.js";
8
- import "./chunk-VOGGLPG5.js";
9
- import "./chunk-AR5QKYNE.js";
10
- import "./chunk-RI2YL6TK.js";
11
- import "./chunk-KBEIQP4G.js";
4
+ } from "./chunk-WXIM2WS7.js";
5
+ import "./chunk-WWKOVDWC.js";
6
+ import "./chunk-63QWFWH3.js";
7
+ import "./chunk-I5T677EA.js";
8
+ import "./chunk-YQ57ORTV.js";
9
+ import "./chunk-4FGEGQW4.js";
10
+ import "./chunk-7VHJNVLF.js";
11
+ import "./chunk-KB64WNBZ.js";
12
12
  import "./chunk-6MLEBAYZ.js";
13
13
  import "./chunk-VT4PDUYT.js";
14
14
 
@@ -24,4 +24,4 @@ var LintCommand = class extends ScriptCommandBase {
24
24
  export {
25
25
  LintCommand
26
26
  };
27
- //# sourceMappingURL=lint-CJM7BAIM.js.map
27
+ //# sourceMappingURL=lint-AAN2NZWG.js.map
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/mcp/harness-server.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ import { fileURLToPath } from "url";
8
+ import net from "net";
9
+ var SIGNAL_TIMEOUT_MS = 3e4;
10
+ function validateEnvironment() {
11
+ const socketPath = process.env.ILOOM_HARNESS_SOCKET;
12
+ if (!socketPath) {
13
+ console.error("Missing required environment variable: ILOOM_HARNESS_SOCKET");
14
+ process.exit(1);
15
+ }
16
+ return socketPath;
17
+ }
18
+ var validatedSocketPath = null;
19
+ function getSocketPath() {
20
+ if (!validatedSocketPath) {
21
+ throw new Error("ILOOM_HARNESS_SOCKET not validated - validateEnvironment() must be called first");
22
+ }
23
+ return validatedSocketPath;
24
+ }
25
+ function sendSignalToHarness(socketPath, message) {
26
+ return new Promise((resolve, reject) => {
27
+ const socket = net.createConnection(socketPath);
28
+ let responseData = "";
29
+ let settled = false;
30
+ const timeoutHandle = globalThis.setTimeout(() => {
31
+ if (!settled) {
32
+ settled = true;
33
+ socket.destroy();
34
+ reject(new Error("Harness did not respond within 30s."));
35
+ }
36
+ }, SIGNAL_TIMEOUT_MS);
37
+ socket.on("connect", () => {
38
+ const payload = JSON.stringify(message) + "\n";
39
+ socket.write(payload);
40
+ });
41
+ socket.on("data", (chunk) => {
42
+ responseData += chunk.toString();
43
+ const newlineIndex = responseData.indexOf("\n");
44
+ if (newlineIndex !== -1) {
45
+ const line = responseData.slice(0, newlineIndex).trim();
46
+ if (!settled) {
47
+ settled = true;
48
+ globalThis.clearTimeout(timeoutHandle);
49
+ socket.destroy();
50
+ try {
51
+ const parsed = JSON.parse(line);
52
+ resolve(parsed);
53
+ } catch {
54
+ reject(new Error(`Harness returned invalid JSON: ${line}`));
55
+ }
56
+ }
57
+ }
58
+ });
59
+ socket.on("error", (err) => {
60
+ if (!settled) {
61
+ settled = true;
62
+ globalThis.clearTimeout(timeoutHandle);
63
+ if (err.code === "EPIPE" || err.code === "ECONNRESET") {
64
+ reject(new Error("Harness closed connection before responding."));
65
+ } else {
66
+ reject(err);
67
+ }
68
+ }
69
+ });
70
+ socket.on("close", () => {
71
+ if (!settled) {
72
+ settled = true;
73
+ globalThis.clearTimeout(timeoutHandle);
74
+ reject(new Error("Harness closed connection before responding."));
75
+ }
76
+ });
77
+ });
78
+ }
79
+ var server = new McpServer({
80
+ name: "iloom-harness",
81
+ version: "0.1.0"
82
+ });
83
+ server.registerTool(
84
+ "signal",
85
+ {
86
+ title: "Signal",
87
+ description: "Send a structured signal to the iloom harness process and return the response. Use this to notify the harness of workflow events (e.g., done, status update).",
88
+ inputSchema: {
89
+ type: z.string().describe('Signal type (e.g., "done", "status")'),
90
+ data: z.record(z.unknown()).optional().describe("Optional payload data for the signal")
91
+ }
92
+ },
93
+ async ({ type, data }) => {
94
+ const socketPath = getSocketPath();
95
+ const message = { type };
96
+ if (data !== void 0) {
97
+ message.data = data;
98
+ }
99
+ let response;
100
+ try {
101
+ response = await sendSignalToHarness(socketPath, message);
102
+ } catch (err) {
103
+ const errorMessage = err instanceof Error ? err.message : String(err);
104
+ const isTimeout = errorMessage.includes("within 30s");
105
+ const text = isTimeout ? "Error: Harness did not respond within 30s." : `Error: ${errorMessage}`;
106
+ return {
107
+ content: [{ type: "text", text }],
108
+ isError: true
109
+ };
110
+ }
111
+ return {
112
+ content: [{ type: "text", text: JSON.stringify(response) }]
113
+ };
114
+ }
115
+ );
116
+ async function main() {
117
+ console.error("=== Iloom Harness MCP Server Starting ===");
118
+ console.error(`PID: ${process.pid}`);
119
+ console.error(`Node version: ${process.version}`);
120
+ console.error(`CWD: ${process.cwd()}`);
121
+ console.error(`Script: ${fileURLToPath(import.meta.url)}`);
122
+ console.error("Environment variables:");
123
+ console.error(` ILOOM_HARNESS_SOCKET=${process.env.ILOOM_HARNESS_SOCKET ?? "<not set>"}`);
124
+ validatedSocketPath = validateEnvironment();
125
+ console.error(`Harness socket path: ${validatedSocketPath}`);
126
+ const transport = new StdioServerTransport();
127
+ await server.connect(transport);
128
+ console.error("=== Iloom Harness MCP Server READY (stdio transport) ===");
129
+ }
130
+ var isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
131
+ if (isMain) {
132
+ main().catch((error) => {
133
+ console.error("Fatal error starting MCP server:", error);
134
+ process.exit(1);
135
+ });
136
+ }
137
+ export {
138
+ sendSignalToHarness
139
+ };
140
+ //# sourceMappingURL=harness-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/mcp/harness-server.ts"],"sourcesContent":["/**\n * Harness MCP Server\n *\n * Provides Claude with a `signal` tool to send structured messages to the iloom\n * harness process via Unix domain socket. This is the Claude-side counterpart to\n * the HarnessServer (#762).\n *\n * Environment variables:\n * - ILOOM_HARNESS_SOCKET: Path to the harness Unix domain socket (required)\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { fileURLToPath } from 'node:url'\nimport net from 'net'\n\nconst SIGNAL_TIMEOUT_MS = 30_000\n\n/**\n * Validate required environment variables\n * Exits with error if missing\n */\nfunction validateEnvironment(): string {\n\tconst socketPath = process.env.ILOOM_HARNESS_SOCKET\n\n\tif (!socketPath) {\n\t\tconsole.error('Missing required environment variable: ILOOM_HARNESS_SOCKET')\n\t\tprocess.exit(1)\n\t}\n\n\treturn socketPath\n}\n\nlet validatedSocketPath: string | null = null\n\n/**\n * Get the validated socket path\n * Throws if called before validateEnvironment()\n */\nfunction getSocketPath(): string {\n\tif (!validatedSocketPath) {\n\t\tthrow new Error('ILOOM_HARNESS_SOCKET not validated - validateEnvironment() must be called first')\n\t}\n\treturn validatedSocketPath\n}\n\n/**\n * Send a signal message to the harness via Unix domain socket and wait for response.\n * Returns the parsed response object, or throws on error/timeout.\n */\nexport function sendSignalToHarness(\n\tsocketPath: string,\n\tmessage: { type: string; data?: Record<string, unknown> }\n): Promise<Record<string, unknown>> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst socket = net.createConnection(socketPath)\n\n\t\tlet responseData = ''\n\t\tlet settled = false\n\n\t\tconst timeoutHandle = globalThis.setTimeout(() => {\n\t\t\tif (!settled) {\n\t\t\t\tsettled = true\n\t\t\t\tsocket.destroy()\n\t\t\t\treject(new Error('Harness did not respond within 30s.'))\n\t\t\t}\n\t\t}, SIGNAL_TIMEOUT_MS)\n\n\t\tsocket.on('connect', () => {\n\t\t\tconst payload = JSON.stringify(message) + '\\n'\n\t\t\tsocket.write(payload)\n\t\t})\n\n\t\tsocket.on('data', (chunk: Buffer) => {\n\t\t\tresponseData += chunk.toString()\n\n\t\t\tconst newlineIndex = responseData.indexOf('\\n')\n\t\t\tif (newlineIndex !== -1) {\n\t\t\t\tconst line = responseData.slice(0, newlineIndex).trim()\n\t\t\t\tif (!settled) {\n\t\t\t\t\tsettled = true\n\t\t\t\t\tglobalThis.clearTimeout(timeoutHandle)\n\t\t\t\t\tsocket.destroy()\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst parsed = JSON.parse(line) as Record<string, unknown>\n\t\t\t\t\t\tresolve(parsed)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treject(new Error(`Harness returned invalid JSON: ${line}`))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\tsocket.on('error', (err: NodeJS.ErrnoException) => {\n\t\t\tif (!settled) {\n\t\t\t\tsettled = true\n\t\t\t\tglobalThis.clearTimeout(timeoutHandle)\n\t\t\t\t// EPIPE and ECONNRESET mean the connection was closed before we could write/read\n\t\t\t\tif (err.code === 'EPIPE' || err.code === 'ECONNRESET') {\n\t\t\t\t\treject(new Error('Harness closed connection before responding.'))\n\t\t\t\t} else {\n\t\t\t\t\treject(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\tsocket.on('close', () => {\n\t\t\tif (!settled) {\n\t\t\t\tsettled = true\n\t\t\t\tglobalThis.clearTimeout(timeoutHandle)\n\t\t\t\treject(new Error('Harness closed connection before responding.'))\n\t\t\t}\n\t\t})\n\t})\n}\n\n// Initialize MCP server\nconst server = new McpServer({\n\tname: 'iloom-harness',\n\tversion: '0.1.0',\n})\n\n// Register signal tool\nserver.registerTool(\n\t'signal',\n\t{\n\t\ttitle: 'Signal',\n\t\tdescription:\n\t\t\t'Send a structured signal to the iloom harness process and return the response. ' +\n\t\t\t'Use this to notify the harness of workflow events (e.g., done, status update).',\n\t\tinputSchema: {\n\t\t\ttype: z.string().describe('Signal type (e.g., \"done\", \"status\")'),\n\t\t\tdata: z.record(z.unknown()).optional().describe('Optional payload data for the signal'),\n\t\t},\n\t},\n\tasync ({ type, data }) => {\n\t\tconst socketPath = getSocketPath()\n\t\tconst message: { type: string; data?: Record<string, unknown> } = { type }\n\t\tif (data !== undefined) {\n\t\t\tmessage.data = data as Record<string, unknown>\n\t\t}\n\n\t\tlet response: Record<string, unknown>\n\t\ttry {\n\t\t\tresponse = await sendSignalToHarness(socketPath, message)\n\t\t} catch (err) {\n\t\t\tconst errorMessage = err instanceof Error ? err.message : String(err)\n\t\t\tconst isTimeout = errorMessage.includes('within 30s')\n\t\t\tconst text = isTimeout\n\t\t\t\t? 'Error: Harness did not respond within 30s.'\n\t\t\t\t: `Error: ${errorMessage}`\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: 'text' as const, text }],\n\t\t\t\tisError: true,\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tcontent: [{ type: 'text' as const, text: JSON.stringify(response) }],\n\t\t}\n\t}\n)\n\n// Main server startup\nasync function main(): Promise<void> {\n\tconsole.error('=== Iloom Harness MCP Server Starting ===')\n\tconsole.error(`PID: ${process.pid}`)\n\tconsole.error(`Node version: ${process.version}`)\n\tconsole.error(`CWD: ${process.cwd()}`)\n\tconsole.error(`Script: ${fileURLToPath(import.meta.url)}`)\n\n\tconsole.error('Environment variables:')\n\tconsole.error(` ILOOM_HARNESS_SOCKET=${process.env.ILOOM_HARNESS_SOCKET ?? '<not set>'}`)\n\n\tvalidatedSocketPath = validateEnvironment()\n\tconsole.error(`Harness socket path: ${validatedSocketPath}`)\n\n\tconst transport = new StdioServerTransport()\n\tawait server.connect(transport)\n\tconsole.error('=== Iloom Harness MCP Server READY (stdio transport) ===')\n}\n\n// Only run main when executed directly (not when imported in tests)\nconst isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]\nif (isMain) {\n\tmain().catch((error) => {\n\t\tconsole.error('Fatal error starting MCP server:', error)\n\t\tprocess.exit(1)\n\t})\n}\n"],"mappings":"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","names":[]}