@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/issue-management-server.ts","../../src/utils/github.ts","../../src/utils/logger.ts","../../src/utils/terminal.ts","../../src/utils/env.ts","../../src/utils/image-processor.ts","../../src/mcp/GitHubIssueManagementProvider.ts","../../src/utils/linear.ts","../../src/types/linear.ts","../../src/utils/linear-markup-converter.ts","../../src/mcp/LinearIssueManagementProvider.ts","../../src/mcp/IssueManagementProviderFactory.ts"],"sourcesContent":["/**\n * Issue Management MCP Server\n *\n * A Model Context Protocol server that enables Claude to interact with issue trackers\n * (GitHub, Linear, etc.) during workflows. Provides tools for fetching issues, reading\n * comments, and creating/updating comments.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { IssueManagementProviderFactory } from './IssueManagementProviderFactory.js'\nimport type {\n\tIssueProvider,\n\tGetIssueInput,\n\tGetPRInput,\n\tGetCommentInput,\n\tCreateCommentInput,\n\tUpdateCommentInput,\n\tCreateIssueInput,\n\tCreateChildIssueInput,\n\tCreateDependencyInput,\n\tGetDependenciesInput,\n\tGetChildIssuesInput,\n\tRemoveDependencyInput,\n} from './types.js'\n\n// Validate required environment variables\nfunction validateEnvironment(): IssueProvider {\n\tconst provider = process.env.ISSUE_PROVIDER as IssueProvider | undefined\n\tif (!provider) {\n\t\tconsole.error('Missing required environment variable: ISSUE_PROVIDER')\n\t\tprocess.exit(1)\n\t}\n\n\tif (provider !== 'github' && provider !== 'linear') {\n\t\tconsole.error(`Invalid ISSUE_PROVIDER: ${provider}. Must be 'github' or 'linear'`)\n\t\tprocess.exit(1)\n\t}\n\n\t// GitHub-specific validation\n\tif (provider === 'github') {\n\t\tconst required = ['REPO_OWNER', 'REPO_NAME']\n\t\tconst missing = required.filter((key) => !process.env[key])\n\n\t\tif (missing.length > 0) {\n\t\t\tconsole.error(\n\t\t\t\t`Missing required environment variables for GitHub provider: ${missing.join(', ')}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\t// Linear requires API token for SDK authentication\n\tif (provider === 'linear') {\n\t\tif (!process.env.LINEAR_API_TOKEN) {\n\t\t\tconsole.error('Missing required environment variable for Linear provider: LINEAR_API_TOKEN')\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn provider\n}\n\n// Initialize the MCP server\nconst server = new McpServer({\n\tname: 'issue-management-broker',\n\tversion: '0.1.0',\n})\n\n// Define flexible author schema\nconst flexibleAuthorSchema = z.object({\n\tid: z.string(),\n\tdisplayName: z.string(),\n}).passthrough()\n\n// Register get_issue tool\nserver.registerTool(\n\t'get_issue',\n\t{\n\t\ttitle: 'Get Issue',\n\t\tdescription:\n\t\t\t'Fetch issue details including body, title, comments, labels, assignees, and other metadata. ' +\n\t\t\t'Author fields vary by provider: GitHub uses { login }, Linear uses { name, displayName }, Jira uses { displayName, accountId }. ' +\n\t\t\t'All authors have normalized core fields: { id, displayName } plus provider-specific fields.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The issue identifier'),\n\t\t\tincludeComments: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Whether to include comments (default: true)'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\t// Core validated fields\n\t\t\tid: z.string().describe('Issue identifier'),\n\t\t\ttitle: z.string().describe('Issue title'),\n\t\t\tbody: z.string().describe('Issue body/description'),\n\t\t\tstate: z.string().describe('Issue state (open, closed, etc.)'),\n\t\t\turl: z.string().describe('Issue URL'),\n\t\t\tprovider: z.enum(['github', 'linear']).describe('Issue management provider'),\n\n\t\t\t// Flexible author - core fields + passthrough\n\t\t\tauthor: flexibleAuthorSchema.nullable().describe(\n\t\t\t\t'Issue author with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\n\t\t\t// Optional flexible arrays\n\t\t\tassignees: z.array(flexibleAuthorSchema).optional().describe(\n\t\t\t\t'Issue assignees with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\t\t\tlabels: z.array(\n\t\t\t\tz.object({ name: z.string() }).passthrough()\n\t\t\t).optional().describe('Issue labels'),\n\n\t\t\t// Comments with flexible author\n\t\t\tcomments: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\tbody: z.string(),\n\t\t\t\t\tauthor: flexibleAuthorSchema.nullable(),\n\t\t\t\t\tcreatedAt: z.string(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('Issue comments with flexible author structure'),\n\t\t},\n\t},\n\tasync ({ number, includeComments, repo }: GetIssueInput) => {\n\t\tconsole.error(`Fetching issue ${number}${repo ? ` from ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tconst result = await provider.getIssue({ number, includeComments, repo })\n\n\t\t\tconsole.error(`Issue fetched successfully: ${result.number} - ${result.title}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to fetch issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to fetch issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Import GitHubIssueManagementProvider for get_pr tool (PRs always use GitHub)\nimport { GitHubIssueManagementProvider } from './GitHubIssueManagementProvider.js'\n\n// Register get_pr tool\n// Note: PRs only exist on GitHub, so this tool always uses the GitHub provider\n// regardless of the configured issue tracker\nserver.registerTool(\n\t'get_pr',\n\t{\n\t\ttitle: 'Get Pull Request',\n\t\tdescription:\n\t\t\t'Fetch pull request details including title, body, comments, files, commits, and branch information. ' +\n\t\t\t'PRs only exist on GitHub, so this tool always uses GitHub regardless of configured issue tracker. ' +\n\t\t\t'Author fields have normalized core fields: { id, displayName } plus provider-specific fields.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The PR number'),\n\t\t\tincludeComments: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Whether to include comments (default: true)'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\t// Core validated fields\n\t\t\tid: z.string().describe('PR identifier'),\n\t\t\tnumber: z.number().describe('PR number'),\n\t\t\ttitle: z.string().describe('PR title'),\n\t\t\tbody: z.string().describe('PR body/description'),\n\t\t\tstate: z.string().describe('PR state (OPEN, CLOSED, MERGED)'),\n\t\t\turl: z.string().describe('PR URL'),\n\n\t\t\t// Branch info\n\t\t\theadRefName: z.string().describe('Source branch name'),\n\t\t\tbaseRefName: z.string().describe('Target branch name'),\n\n\t\t\t// Flexible author - core fields + passthrough\n\t\t\tauthor: flexibleAuthorSchema.nullable().describe(\n\t\t\t\t'PR author with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\n\t\t\t// Optional flexible arrays\n\t\t\tfiles: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tpath: z.string(),\n\t\t\t\t\tadditions: z.number(),\n\t\t\t\t\tdeletions: z.number(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('Changed files in the PR'),\n\t\t\tcommits: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\toid: z.string(),\n\t\t\t\t\tmessageHeadline: z.string(),\n\t\t\t\t\tauthor: flexibleAuthorSchema.nullable(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('Commits in the PR'),\n\t\t\tcomments: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\tbody: z.string(),\n\t\t\t\t\tauthor: flexibleAuthorSchema.nullable(),\n\t\t\t\t\tcreatedAt: z.string(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('PR comments'),\n\t\t},\n\t},\n\tasync ({ number, includeComments, repo }: GetPRInput) => {\n\t\tconsole.error(`Fetching PR ${number}${repo ? ` from ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\t// PRs always use GitHub provider regardless of configured issue tracker\n\t\t\tconst provider = new GitHubIssueManagementProvider()\n\t\t\tconst result = await provider.getPR({ number, includeComments, repo })\n\n\t\t\tconsole.error(`PR fetched successfully: #${result.number} - ${result.title}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to fetch PR: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to fetch PR: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register get_comment tool\nserver.registerTool(\n\t'get_comment',\n\t{\n\t\ttitle: 'Get Comment',\n\t\tdescription:\n\t\t\t'Fetch a specific comment by ID. Author has normalized core fields { id, displayName } plus provider-specific fields.',\n\t\tinputSchema: {\n\t\t\tcommentId: z.string().describe('The comment identifier to fetch'),\n\t\t\tnumber: z.string().describe('The issue or PR identifier (context for providers that need it)'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string().describe('Comment identifier'),\n\t\t\tbody: z.string().describe('Comment body content'),\n\t\t\tauthor: flexibleAuthorSchema.nullable().describe(\n\t\t\t\t'Comment author with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\t\t\tcreated_at: z.string().describe('Comment creation timestamp'),\n\t\t\tupdated_at: z.string().optional().describe('Comment last updated timestamp'),\n\t\t},\n\t},\n\tasync ({ commentId, number, repo }: GetCommentInput) => {\n\t\tconsole.error(`Fetching comment ${commentId} from issue ${number}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tconst result = await provider.getComment({ commentId, number, repo })\n\n\t\t\tconsole.error(`Comment fetched successfully: ${result.id}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to fetch comment: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to fetch comment: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register create_comment tool\nserver.registerTool(\n\t'create_comment',\n\t{\n\t\ttitle: 'Create Comment',\n\t\tdescription:\n\t\t\t'Create a new comment on an issue or pull request. Use this to start tracking a workflow phase.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The issue or PR identifier'),\n\t\t\tbody: z.string().describe('The comment body (markdown supported)'),\n\t\t\ttype: z\n\t\t\t\t.enum(['issue', 'pr'])\n\t\t\t\t.describe('Type of entity to comment on (issue or pr)'),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string(),\n\t\t\turl: z.string(),\n\t\t\tcreated_at: z.string().optional(),\n\t\t},\n\t},\n\tasync ({ number, body, type }: CreateCommentInput) => {\n\t\tconsole.error(`Creating ${type} comment on ${number}`)\n\n\t\ttry {\n\t\t\t// PR comments must always go to GitHub since PRs only exist on GitHub\n\t\t\tconst providerType = type === 'pr' ? 'github' : (process.env.ISSUE_PROVIDER as IssueProvider)\n\t\t\tconst provider = IssueManagementProviderFactory.create(providerType)\n\t\t\tconst result = await provider.createComment({ number, body, type })\n\n\t\t\tconsole.error(\n\t\t\t\t`Comment created successfully: ${result.id} at ${result.url}`\n\t\t\t)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create comment: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create ${type} comment: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register update_comment tool\nserver.registerTool(\n\t'update_comment',\n\t{\n\t\ttitle: 'Update Comment',\n\t\tdescription:\n\t\t\t'Update an existing comment. Use this to update progress during a workflow phase.',\n\t\tinputSchema: {\n\t\t\tcommentId: z.string().describe('The comment identifier to update'),\n\t\t\tnumber: z.string().describe('The issue or PR identifier (context for providers that need it)'),\n\t\t\tbody: z.string().describe('The updated comment body (markdown supported)'),\n\t\t\ttype: z.enum(['issue', 'pr']).optional().describe('Optional type to route PR comments to GitHub regardless of configured provider'),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string(),\n\t\t\turl: z.string(),\n\t\t\tupdated_at: z.string().optional(),\n\t\t},\n\t},\n\tasync ({ commentId, number, body, type }: UpdateCommentInput) => {\n\t\tconsole.error(`Updating comment ${commentId} on ${type === 'pr' ? 'PR' : 'issue'} ${number}`)\n\n\t\ttry {\n\t\t\t// PR comments must always go to GitHub since PRs only exist on GitHub\n\t\t\tconst providerType = type === 'pr' ? 'github' : (process.env.ISSUE_PROVIDER as IssueProvider)\n\t\t\tconst provider = IssueManagementProviderFactory.create(providerType)\n\t\t\tconst result = await provider.updateComment({ commentId, number, body })\n\n\t\t\tconsole.error(\n\t\t\t\t`Comment updated successfully: ${result.id} at ${result.url}`\n\t\t\t)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to update comment: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to update comment: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register create_issue tool\nserver.registerTool(\n\t'create_issue',\n\t{\n\t\ttitle: 'Create Issue',\n\t\tdescription:\n\t\t\t'Create a new issue in the configured issue tracker. ' +\n\t\t\t'For GitHub: creates issue in the configured repository. ' +\n\t\t\t'For Linear: requires teamKey parameter (e.g., \"ENG\", \"PLAT\"), or configure issueManagement.linear.teamId in settings, or call get_issue first to auto-detect the team.',\n\t\tinputSchema: {\n\t\t\ttitle: z.string().describe('The issue title'),\n\t\t\tbody: z.string().describe('The issue body/description (markdown supported)'),\n\t\t\tlabels: z.array(z.string()).optional().describe('Optional labels to apply to the issue'),\n\t\t\tteamKey: z.string().optional().describe('Team key for Linear (e.g., \"ENG\"). Falls back to settings or team extracted from previous get_issue call. Ignored for GitHub.'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string().describe('Issue identifier'),\n\t\t\turl: z.string().describe('Issue URL'),\n\t\t\tnumber: z.number().optional().describe('Issue number (GitHub only)'),\n\t\t},\n\t},\n\tasync ({ title, body, labels, teamKey, repo }: CreateIssueInput) => {\n\t\tconsole.error(`Creating issue: ${title}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tconst result = await provider.createIssue({ title, body, labels, teamKey, repo })\n\n\t\t\tconsole.error(`Issue created successfully: ${result.id} at ${result.url}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register create_child_issue tool\nserver.registerTool(\n\t'create_child_issue',\n\t{\n\t\ttitle: 'Create Child Issue',\n\t\tdescription:\n\t\t\t'Create a new child issue linked to a parent issue. ' +\n\t\t\t'For GitHub: creates issue and links via sub-issue API (requires two API calls). ' +\n\t\t\t'For Linear: creates issue atomically with parent relationship. ' +\n\t\t\t'The parentId should be the parent issue identifier (GitHub issue number or Linear identifier like \"ENG-123\").',\n\t\tinputSchema: {\n\t\t\tparentId: z.string().describe('Parent issue identifier (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\ttitle: z.string().describe('The child issue title'),\n\t\t\tbody: z.string().describe('The child issue body/description (markdown supported)'),\n\t\t\tlabels: z.array(z.string()).optional().describe('Optional labels to apply to the child issue'),\n\t\t\tteamKey: z.string().optional().describe('Team key for Linear (e.g., \"ENG\"). Falls back to parent team. Ignored for GitHub.'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string().describe('Issue identifier'),\n\t\t\turl: z.string().describe('Issue URL'),\n\t\t\tnumber: z.number().optional().describe('Issue number (GitHub only)'),\n\t\t},\n\t},\n\tasync ({ parentId, title, body, labels, teamKey, repo }: CreateChildIssueInput) => {\n\t\tconsole.error(`Creating child issue for parent ${parentId}: ${title}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tconst result = await provider.createChildIssue({ parentId, title, body, labels, teamKey, repo })\n\n\t\t\tconsole.error(`Child issue created successfully: ${result.id} at ${result.url}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create child issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create child issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Define dependency result schema\nconst dependencyResultSchema = z.object({\n\tid: z.string().describe('Issue identifier'),\n\ttitle: z.string().describe('Issue title'),\n\turl: z.string().describe('Issue URL'),\n\tstate: z.string().describe('Issue state'),\n})\n\n// Register create_dependency tool\nserver.registerTool(\n\t'create_dependency',\n\t{\n\t\ttitle: 'Create Dependency',\n\t\tdescription:\n\t\t\t'Create a blocking dependency between two issues. ' +\n\t\t\t'The blockingIssue will block the blockedIssue. ' +\n\t\t\t'For GitHub: uses the sub-issue API. ' +\n\t\t\t'For Linear: creates a \"blocks\" relation.',\n\t\tinputSchema: {\n\t\t\tblockingIssue: z.string().describe('The issue that blocks (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\tblockedIssue: z.string().describe('The issue being blocked (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tsuccess: z.boolean().describe('Whether the dependency was created successfully'),\n\t\t},\n\t},\n\tasync ({ blockingIssue, blockedIssue, repo }: CreateDependencyInput) => {\n\t\tconsole.error(`Creating dependency: ${blockingIssue} blocks ${blockedIssue}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tawait provider.createDependency({ blockingIssue, blockedIssue, repo })\n\n\t\t\tconsole.error(`Dependency created successfully: ${blockingIssue} -> ${blockedIssue}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ success: true }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { success: true },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create dependency: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create dependency: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register get_dependencies tool\nserver.registerTool(\n\t'get_dependencies',\n\t{\n\t\ttitle: 'Get Dependencies',\n\t\tdescription:\n\t\t\t'Get blocking/blocked_by dependencies for an issue. ' +\n\t\t\t'Returns lists of issues that this issue blocks and/or is blocked by.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('Issue identifier (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\tdirection: z\n\t\t\t\t.enum(['blocking', 'blocked_by', 'both'])\n\t\t\t\t.describe('Which dependencies to fetch: \"blocking\" for issues this blocks, \"blocked_by\" for issues blocking this, \"both\" for all'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tblocking: z.array(dependencyResultSchema).describe('Issues that this issue blocks'),\n\t\t\tblockedBy: z.array(dependencyResultSchema).describe('Issues that block this issue'),\n\t\t},\n\t},\n\tasync ({ number, direction, repo }: GetDependenciesInput) => {\n\t\tconsole.error(`Getting dependencies for ${number} (direction: ${direction})${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tconst result = await provider.getDependencies({ number, direction, repo })\n\n\t\t\tconsole.error(`Dependencies fetched: ${result.blocking.length} blocking, ${result.blockedBy.length} blocked_by`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to get dependencies: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to get dependencies: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register remove_dependency tool\nserver.registerTool(\n\t'remove_dependency',\n\t{\n\t\ttitle: 'Remove Dependency',\n\t\tdescription:\n\t\t\t'Remove a blocking dependency between two issues. ' +\n\t\t\t'The blockingIssue will no longer block the blockedIssue.',\n\t\tinputSchema: {\n\t\t\tblockingIssue: z.string().describe('The issue that blocks (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\tblockedIssue: z.string().describe('The issue being blocked (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tsuccess: z.boolean().describe('Whether the dependency was removed successfully'),\n\t\t},\n\t},\n\tasync ({ blockingIssue, blockedIssue, repo }: RemoveDependencyInput) => {\n\t\tconsole.error(`Removing dependency: ${blockingIssue} blocks ${blockedIssue}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tawait provider.removeDependency({ blockingIssue, blockedIssue, repo })\n\n\t\t\tconsole.error(`Dependency removed successfully: ${blockingIssue} -> ${blockedIssue}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ success: true }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { success: true },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to remove dependency: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to remove dependency: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Define child issue result schema (reuse dependencyResultSchema pattern)\nconst childIssueResultSchema = z.object({\n\tid: z.string().describe('Issue identifier'),\n\ttitle: z.string().describe('Issue title'),\n\turl: z.string().describe('Issue URL'),\n\tstate: z.string().describe('Issue state'),\n})\n\n// Register get_child_issues tool\nserver.registerTool(\n\t'get_child_issues',\n\t{\n\t\ttitle: 'Get Child Issues',\n\t\tdescription:\n\t\t\t'Get child issues (sub-issues) of a parent issue. ' +\n\t\t\t'Returns a list of issues that are children of the specified parent.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('Parent issue identifier (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tchildren: z.array(childIssueResultSchema).describe('Child issues of the parent'),\n\t\t},\n\t},\n\tasync ({ number, repo }: GetChildIssuesInput) => {\n\t\tconsole.error(`Getting child issues for ${number}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider\n\t\t\t)\n\t\t\tconst result = await provider.getChildIssues({ number, repo })\n\n\t\t\tconsole.error(`Child issues fetched: ${result.length} children`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ children: result }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { children: result } as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to get child issues: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to get child issues: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Main server startup\nasync function main(): Promise<void> {\n\tconsole.error('Starting Issue Management MCP Server...')\n\n\t// Validate environment and get provider\n\tconst provider = validateEnvironment()\n\tconsole.error('Environment validated')\n\tconsole.error(`Issue management provider: ${provider}`)\n\n\tif (provider === 'github') {\n\t\tconsole.error(`Repository: ${process.env.REPO_OWNER}/${process.env.REPO_NAME}`)\n\t\tconsole.error(`Event type: ${process.env.GITHUB_EVENT_NAME ?? 'not specified'}`)\n\t}\n\n\t// Connect stdio transport\n\tconst transport = new StdioServerTransport()\n\tawait server.connect(transport)\n\n\tconsole.error('Issue Management MCP Server running on stdio transport')\n}\n\n// Run the server\nmain().catch((error) => {\n\tconsole.error('Fatal error starting MCP server:', error)\n\tprocess.exit(1)\n})\n","import { execa } from 'execa'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tGitHubAuthStatus,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { logger } from './logger.js'\n\n// Core GitHub CLI execution wrapper\nexport async function executeGhCommand<T = unknown>(\n\targs: string[],\n\toptions?: { cwd?: string; timeout?: number }\n): Promise<T> {\n\tconst result = await execa('gh', args, {\n\t\tcwd: options?.cwd ?? process.cwd(),\n\t\ttimeout: options?.timeout ?? 30000,\n\t\tencoding: 'utf8',\n\t})\n\n\t// Parse JSON output if --json flag, --format json, --jq, or GraphQL was used\n\tconst isJson =\n\t\targs.includes('--json') ||\n\t\targs.includes('--jq') ||\n\t\t(args.includes('--format') && args[args.indexOf('--format') + 1] === 'json') ||\n\t\t(args[0] === 'api' && args[1] === 'graphql')\n\tconst data = isJson ? JSON.parse(result.stdout) : result.stdout\n\n\treturn data as T\n}\n\n// Authentication checking\nexport async function checkGhAuth(): Promise<GitHubAuthStatus> {\n\ttry {\n\t\tconst output = await executeGhCommand<string>(['auth', 'status'])\n\n\t\t// Parse auth status output - handle both old and new formats\n\t\t// Old format: \"Logged in to github.com as username\"\n\t\t// New format: \"✓ Logged in to github.com account username (keyring)\"\n\n\t\t// Split output into lines to find the active account\n\t\tconst lines = output.split('\\n')\n\t\tlet username: string | undefined\n\t\tlet scopes: string[] = []\n\n\t\t// Find the active account (look for \"Active account: true\" or first account if none marked)\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst line = lines[i]\n\n\t\t\t// Match new format: \"✓ Logged in to github.com account username\"\n\t\t\tconst newFormatMatch = line?.match(/Logged in to github\\.com account ([^\\s(]+)/)\n\t\t\tif (newFormatMatch) {\n\t\t\t\tconst accountName = newFormatMatch[1]\n\n\t\t\t\t// Check if this is the active account\n\t\t\t\tconst nextFewLines = lines.slice(i + 1, i + 5).join('\\n')\n\t\t\t\tconst isActive = nextFewLines.includes('Active account: true')\n\n\t\t\t\t// If this is the active account, or we haven't found one yet and there's no \"Active account\" marker\n\t\t\t\tif (isActive || (!username && !output.includes('Active account:'))) {\n\t\t\t\t\tusername = accountName\n\n\t\t\t\t\t// Find scopes for this account\n\t\t\t\t\tconst scopeMatch = nextFewLines.match(/Token scopes: (.+)/)\n\t\t\t\t\tif (scopeMatch?.[1]) {\n\t\t\t\t\t\tscopes = scopeMatch[1].split(', ').map(scope => scope.replace(/^'|'$/g, ''))\n\t\t\t\t\t}\n\n\t\t\t\t\t// If this is the active account, we're done\n\t\t\t\t\tif (isActive) break\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fallback: match old format\n\t\t\tif (!username) {\n\t\t\t\tconst oldFormatMatch = line?.match(/Logged in to github\\.com as ([^\\s]+)/)\n\t\t\t\tif (oldFormatMatch) {\n\t\t\t\t\tusername = oldFormatMatch[1]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If scopes not yet extracted, try the old \"Token scopes\" format\n\t\tif (scopes.length === 0) {\n\t\t\tconst scopeMatch = output.match(/Token scopes: (.+)/)\n\t\t\tscopes = scopeMatch?.[1]?.split(', ').map(scope => scope.replace(/^'|'$/g, '')) ?? []\n\t\t}\n\n\t\treturn {\n\t\t\thasAuth: true,\n\t\t\tscopes,\n\t\t\t...(username && { username }),\n\t\t}\n\t} catch (error) {\n\t\t// Only return \"no auth\" for specific authentication errors\n\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('You are not logged into any GitHub hosts')) {\n\t\t\treturn { hasAuth: false, scopes: [] }\n\t\t}\n\t\t// Re-throw unexpected errors\n\t\tthrow error\n\t}\n}\n\nexport async function hasProjectScope(): Promise<boolean> {\n\tconst auth = await checkGhAuth()\n\treturn auth.scopes.includes('project')\n}\n\n// Issue fetching\nexport async function fetchGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<GitHubIssue> {\n\tlogger.debug('Fetching GitHub issue', { issueNumber, repo })\n\n\tconst args = [\n\t\t'issue',\n\t\t'view',\n\t\tString(issueNumber),\n\t\t'--json',\n\t\t'number,title,body,state,labels,assignees,url,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubIssue>(args)\n}\n\n// PR fetching\nexport async function fetchGhPR(\n\tprNumber: number,\n\trepo?: string\n): Promise<GitHubPullRequest> {\n\tlogger.debug('Fetching GitHub PR', { prNumber, repo })\n\n\tconst args = [\n\t\t'pr',\n\t\t'view',\n\t\tString(prNumber),\n\t\t'--json',\n\t\t'number,title,body,state,headRefName,baseRefName,url,isDraft,isCrossRepository,mergeable,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubPullRequest>(args)\n}\n\n// Project operations\nexport async function fetchProjectList(\n\towner: string\n): Promise<GitHubProject[]> {\n\tconst result = await executeGhCommand<{ projects: GitHubProject[] }>([\n\t\t'project',\n\t\t'list',\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'100',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.projects ?? []\n}\n\nexport async function fetchProjectItems(\n\tprojectNumber: number,\n\towner: string\n): Promise<ProjectItem[]> {\n\tconst result = await executeGhCommand<{ items: ProjectItem[] }>([\n\t\t'project',\n\t\t'item-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'10000',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.items ?? []\n}\n\nexport async function fetchProjectFields(\n\tprojectNumber: number,\n\towner: string\n): Promise<{ fields: ProjectField[] }> {\n\tconst result = await executeGhCommand<{ fields: ProjectField[] }>([\n\t\t'project',\n\t\t'field-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result ?? { fields: [] }\n}\n\nexport async function updateProjectItemField(\n\titemId: string,\n\tprojectId: string,\n\tfieldId: string,\n\toptionId: string\n): Promise<void> {\n\tawait executeGhCommand([\n\t\t'project',\n\t\t'item-edit',\n\t\t'--id',\n\t\titemId,\n\t\t'--project-id',\n\t\tprojectId,\n\t\t'--field-id',\n\t\tfieldId,\n\t\t'--single-select-option-id',\n\t\toptionId,\n\t\t'--format',\n\t\t'json',\n\t])\n}\n\n// GitHub Issue Operations\n\ninterface IssueCreateResponse {\n\tnumber: string | number\n\turl: string\n}\n\n/**\n * Create a new GitHub issue\n * @param title - The issue title\n * @param body - The issue body (markdown supported)\n * @param options - Optional configuration\n * @param options.repo - Repository in format \"owner/repo\" (uses current repo if not provided)\n * @param options.labels - Array of label names to add to the issue\n * @returns Issue metadata including number and URL\n */\nexport async function createIssue(\n\ttitle: string,\n\tbody: string,\n\toptions?: { repo?: string | undefined; labels?: string[] | undefined }\n): Promise<IssueCreateResponse> {\n\tconst { repo, labels } = options ?? {}\n\n\tlogger.debug('Creating GitHub issue', { title, repo, labels })\n\n\tconst args = [\n\t\t'issue',\n\t\t'create',\n\t\t'--title',\n\t\ttitle,\n\t\t'--body',\n\t\tbody,\n\t]\n\n\t// Add repo if provided\n\tif (repo) {\n\t\targs.splice(2, 0, '--repo', repo)\n\t}\n\n\t// Add labels if provided\n\tif (labels && labels.length > 0) {\n\t\targs.push('--label', labels.join(','))\n\t}\n\n\tconst execaOptions: { timeout: number; encoding: 'utf8'; cwd?: string } = {\n\t\ttimeout: 30000,\n\t\tencoding: 'utf8',\n\t}\n\n\tif (!repo) {\n\t\texecaOptions.cwd = process.cwd()\n\t}\n\n\tconst result = await execa('gh', args, execaOptions)\n\n\t// Parse the URL from the output (format: \"https://github.com/owner/repo/issues/123\")\n\tconst urlMatch = result.stdout.trim().match(/https:\\/\\/github\\.com\\/[^/]+\\/[^/]+\\/issues\\/(\\d+)/)\n\tif (!urlMatch?.[1]) {\n\t\tthrow new Error(`Failed to parse issue URL from gh output: ${result.stdout}`)\n\t}\n\n\tconst issueNumber = parseInt(urlMatch[1], 10)\n\tconst issueUrl = urlMatch[0]\n\n\treturn {\n\t\tnumber: issueNumber,\n\t\turl: issueUrl,\n\t}\n}\n\n/**\n * @deprecated Use createIssue with options.repo instead\n * Create a new GitHub issue in a specific repository\n * @param title - Issue title\n * @param body - Issue body (markdown)\n * @param repository - Repository in format \"owner/repo\"\n * @param labels - Optional array of label names to add to the issue\n * @returns Issue number and URL\n */\nexport async function createIssueInRepo(\n\ttitle: string,\n\tbody: string,\n\trepository: string,\n\tlabels?: string[]\n): Promise<IssueCreateResponse> {\n\treturn createIssue(title, body, { repo: repository, labels })\n}\n\n// GitHub Comment Operations\n\ninterface CommentResponse {\n\tid: number\n\turl: string\n\tcreated_at?: string\n\tupdated_at?: string\n}\n\ninterface RepoInfo {\n\towner: string\n\tname: string\n}\n\n/**\n * Create a comment on a GitHub issue\n * @param issueNumber - The issue number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createIssueComment(\n\tissueNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating issue comment', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/comments`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Update an existing GitHub comment\n * @param commentId - The comment ID\n * @param body - The updated comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Updated comment metadata\n */\nexport async function updateIssueComment(\n\tcommentId: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Updating issue comment', { commentId, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/comments/${commentId}`\n\t\t: `repos/:owner/:repo/issues/comments/${commentId}`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-X',\n\t\t'PATCH',\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, updated_at: .updated_at}',\n\t])\n}\n\n/**\n * Create a comment on a GitHub pull request\n * Note: PR comments use the same endpoint as issue comments\n * @param prNumber - The PR number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createPRComment(\n\tprNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating PR comment', { prNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${prNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${prNumber}/comments`\n\n\t// PR comments use the issues endpoint\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Get repository owner and name from current directory\n * @returns Repository owner and name\n */\nexport async function getRepoInfo(): Promise<RepoInfo> {\n\tlogger.debug('Fetching repository info')\n\n\tconst result = await executeGhCommand<{ owner: { login: string }; name: string }>([\n\t\t'repo',\n\t\t'view',\n\t\t'--json',\n\t\t'owner,name',\n\t])\n\n\treturn {\n\t\towner: result.owner.login,\n\t\tname: result.name,\n\t}\n}\n\n// GitHub Sub-Issue Operations\n\n/**\n * Get the GraphQL node ID for a GitHub issue\n * Required for sub-issue API which uses node IDs, not issue numbers\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns GraphQL node ID (e.g., \"I_kwDOPvp_cc7...\")\n */\nexport async function getIssueNodeId(\n\tissueNumber: number,\n\trepo?: string\n): Promise<string> {\n\tlogger.debug('Fetching GitHub issue node ID', { issueNumber, repo })\n\n\tconst args = ['issue', 'view', String(issueNumber), '--json', 'id']\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tconst result = await executeGhCommand<{ id: string }>(args)\n\treturn result.id\n}\n\n/**\n * Link a child issue to a parent issue using GitHub's sub-issue API\n * Requires GraphQL-Features: sub_issues header\n * @param parentNodeId - GraphQL node ID of the parent issue\n * @param childNodeId - GraphQL node ID of the child issue\n */\nexport async function addSubIssue(\n\tparentNodeId: string,\n\tchildNodeId: string\n): Promise<void> {\n\tlogger.debug('Linking child issue to parent', { parentNodeId, childNodeId })\n\n\tconst mutation = `\n\t\tmutation addSubIssue($parentId: ID!, $subIssueId: ID!) {\n\t\t\taddSubIssue(input: { issueId: $parentId, subIssueId: $subIssueId }) {\n\t\t\t\tissue { id }\n\t\t\t\tsubIssue { id }\n\t\t\t}\n\t\t}\n\t`\n\n\tawait executeGhCommand([\n\t\t'api', 'graphql',\n\t\t'-H', 'GraphQL-Features: sub_issues',\n\t\t'-f', `query=${mutation}`,\n\t\t'-F', `parentId=${parentNodeId}`,\n\t\t'-F', `subIssueId=${childNodeId}`,\n\t])\n}\n\n/**\n * Get sub-issues (children) of a parent GitHub issue\n * Uses GraphQL to query the sub-issue relationship\n * @param issueNumber - The parent issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Array of child issues with id, title, url, and state\n */\nexport async function getSubIssues(\n\tissueNumber: number,\n\trepo?: string\n): Promise<Array<{ id: string; title: string; url: string; state: string }>> {\n\tlogger.debug('Fetching GitHub sub-issues', { issueNumber, repo })\n\n\t// Get the node ID for the parent issue\n\tconst parentNodeId = await getIssueNodeId(issueNumber, repo)\n\n\t// Query sub-issues using GraphQL\n\tconst query = `\n\t\tquery getSubIssues($parentId: ID!) {\n\t\t\tnode(id: $parentId) {\n\t\t\t\t... on Issue {\n\t\t\t\t\tsubIssues(first: 100) {\n\t\t\t\t\t\tnodes {\n\t\t\t\t\t\t\tnumber\n\t\t\t\t\t\t\ttitle\n\t\t\t\t\t\t\turl\n\t\t\t\t\t\t\tstate\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t`\n\n\tinterface SubIssueNode {\n\t\tnumber: number\n\t\ttitle: string\n\t\turl: string\n\t\tstate: string\n\t}\n\n\tinterface SubIssuesResponse {\n\t\tdata: {\n\t\t\tnode: {\n\t\t\t\tsubIssues: {\n\t\t\t\t\tnodes: SubIssueNode[]\n\t\t\t\t}\n\t\t\t} | null\n\t\t}\n\t}\n\n\ttry {\n\t\tconst result = await executeGhCommand<SubIssuesResponse>([\n\t\t\t'api', 'graphql',\n\t\t\t'-H', 'GraphQL-Features: sub_issues',\n\t\t\t'-f', `query=${query}`,\n\t\t\t'-F', `parentId=${parentNodeId}`,\n\t\t])\n\n\t\tconst subIssues = result.data.node?.subIssues?.nodes ?? []\n\n\t\treturn subIssues.map(issue => ({\n\t\t\tid: String(issue.number),\n\t\t\ttitle: issue.title,\n\t\t\turl: issue.url,\n\t\t\tstate: issue.state.toLowerCase(),\n\t\t}))\n\t} catch (error) {\n\t\t// Return empty array if sub-issues feature is not available or no children\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for feature not available or empty result\n\t\t\tif (combinedError.includes('sub_issues') || combinedError.includes('null')) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tthrow error\n\t}\n}\n\n// GitHub Issue Dependency Operations\n\n/**\n * GitHub dependency result from API\n */\ninterface GitHubDependency {\n\tid: number\n\tnumber: number\n\ttitle: string\n\tstate: string\n\thtml_url: string\n}\n\n/**\n * Get the internal database ID for a GitHub issue\n * Required for dependency API which uses database IDs, not node IDs\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Internal GitHub issue database ID\n */\nexport async function getIssueDatabaseId(\n\tissueNumber: number,\n\trepo?: string\n): Promise<number> {\n\tlogger.debug('Fetching GitHub issue database ID', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}`\n\n\tconst result = await executeGhCommand<{ id: number }>([\n\t\t'api',\n\t\tapiPath,\n\t\t'--jq',\n\t\t'{id: .id}',\n\t])\n\n\treturn result.id\n}\n\n/**\n * Get dependencies for a GitHub issue\n * Uses GitHub's issue dependencies API\n * @param issueNumber - The issue number\n * @param direction - 'blocking' for issues this blocks, 'blocked_by' for issues blocking this\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Array of dependency objects with id, title, url, state\n */\nexport async function getIssueDependencies(\n\tissueNumber: number,\n\tdirection: 'blocking' | 'blocked_by',\n\trepo?: string\n): Promise<Array<{ id: string; databaseId: number; title: string; url: string; state: string }>> {\n\tlogger.debug('Fetching GitHub issue dependencies', { issueNumber, direction, repo })\n\n\t// Use the dependencies API with the appropriate direction endpoint\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/dependencies/${direction}`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/dependencies/${direction}`\n\n\ttry {\n\t\tconst result = await executeGhCommand<GitHubDependency[]>([\n\t\t\t'api',\n\t\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\t\t'--jq', '.',\n\t\t\tapiPath,\n\t\t])\n\n\t\treturn (result ?? []).map(dep => ({\n\t\t\tid: String(dep.number),\n\t\t\tdatabaseId: dep.id,\n\t\t\ttitle: dep.title,\n\t\t\turl: dep.html_url,\n\t\t\tstate: dep.state,\n\t\t}))\n\t} catch (error) {\n\t\t// Return empty array for 404 on the dependencies endpoint\n\t\t// This indicates the issue exists but has no dependencies configured\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for 404 specifically on dependencies endpoint\n\t\t\tif (combinedError.includes('404') && combinedError.includes('dependencies')) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/**\n * Create a dependency between two issues (A blocks B)\n * Uses GitHub's issue dependencies API\n * @param blockedIssueNumber - The issue number that is blocked\n * @param blockingIssueDatabaseId - The database ID of the issue that blocks\n * @param repo - Optional repo in \"owner/repo\" format\n * @throws Error with specific message for: dependency already exists, issue not found, or dependencies feature not enabled\n */\nexport async function createIssueDependency(\n\tblockedIssueNumber: number,\n\tblockingIssueDatabaseId: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Creating GitHub issue dependency', { blockedIssueNumber, blockingIssueDatabaseId, repo })\n\n\t// POST to the blocked issue's blocked_by endpoint with the blocking issue's database ID\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by`\n\t\t: `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by`\n\n\ttry {\n\t\tawait executeGhCommand([\n\t\t\t'api',\n\t\t\t'-X', 'POST',\n\t\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\t\tapiPath,\n\t\t\t'-F', `issue_id=${blockingIssueDatabaseId}`,\n\t\t])\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for dependency already exists (422 Unprocessable Entity)\n\t\t\tif (combinedError.includes('422') || combinedError.includes('already exists') || combinedError.includes('Unprocessable Entity')) {\n\t\t\t\tthrow new Error(`Dependency already exists: issue #${blockedIssueNumber} is already blocked by the specified issue`)\n\t\t\t}\n\n\t\t\t// Check for issue not found (404)\n\t\t\tif (combinedError.includes('404') || combinedError.includes('Not Found')) {\n\t\t\t\tthrow new Error(`Issue not found: unable to create dependency for issue #${blockedIssueNumber}. The issue may not exist or you may not have access to it.`)\n\t\t\t}\n\n\t\t\t// Check for dependencies feature not enabled (403 or specific error message)\n\t\t\tif (combinedError.includes('403') || combinedError.includes('Forbidden') || combinedError.includes('not enabled')) {\n\t\t\t\tthrow new Error(`Dependencies feature not enabled: the repository may not have issue dependencies enabled. This feature requires GitHub Enterprise or specific repository settings.`)\n\t\t\t}\n\t\t}\n\n\t\t// Re-throw the original error if it doesn't match any known patterns\n\t\tthrow error\n\t}\n}\n\n/**\n * Remove a dependency between two issues (A blocks B)\n * Uses GitHub's issue dependencies API\n * @param blockedIssueNumber - The issue number that is blocked\n * @param blockingIssueDatabaseId - The database ID of the issue that blocks\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function removeIssueDependency(\n\tblockedIssueNumber: number,\n\tblockingIssueDatabaseId: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Removing GitHub issue dependency', { blockedIssueNumber, blockingIssueDatabaseId, repo })\n\n\t// DELETE from the blocked issue's blocked_by endpoint with the blocking issue's database ID\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}`\n\t\t: `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}`\n\n\tawait executeGhCommand([\n\t\t'api',\n\t\t'-X', 'DELETE',\n\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\tapiPath,\n\t])\n}\n\n// Issue List Operations (for il issues command)\n\nexport interface GitHubIssueListItem {\n\tid: string\n\ttitle: string\n\tupdatedAt: string\n\turl: string\n\tstate: string\n}\n\n/**\n * Fetch a list of open GitHub issues sorted by recently updated\n * @param options - Fetch options\n * @param options.limit - Maximum number of issues to return (default: 100)\n * @param options.cwd - Working directory for gh CLI (default: process.cwd())\n * @returns Array of issues\n */\nexport async function fetchGitHubIssueList(\n\toptions?: { limit?: number; cwd?: string }\n): Promise<GitHubIssueListItem[]> {\n\tconst limit = options?.limit ?? 100\n\n\tlogger.debug('Fetching GitHub issue list', { limit, cwd: options?.cwd })\n\n\tconst result = await executeGhCommand<Array<{\n\t\tnumber: number\n\t\ttitle: string\n\t\tupdatedAt: string\n\t\turl: string\n\t\tstate: string\n\t}>>([\n\t\t'issue',\n\t\t'list',\n\t\t'--state', 'open',\n\t\t'--json', 'number,title,updatedAt,url,state',\n\t\t'--limit', String(limit),\n\t\t'--search', 'sort:updated-desc',\n\t], options?.cwd ? { cwd: options.cwd } : undefined)\n\n\treturn (result ?? []).map(item => ({\n\t\tid: String(item.number),\n\t\ttitle: item.title,\n\t\tupdatedAt: item.updatedAt,\n\t\turl: item.url,\n\t\tstate: item.state.toLowerCase(),\n\t}))\n}\n\n/**\n * Fetch a list of open, non-draft GitHub PRs sorted by recently updated\n * @param options - Fetch options\n * @param options.limit - Maximum number of PRs to return (default: 100)\n * @param options.cwd - Working directory for gh CLI (default: process.cwd())\n * @returns Array of PRs mapped to GitHubIssueListItem (with [PR] title prefix)\n */\nexport async function fetchGitHubPRList(\n\toptions?: { limit?: number; cwd?: string }\n): Promise<GitHubIssueListItem[]> {\n\tconst limit = options?.limit ?? 100\n\t// Over-fetch to account for draft PRs that will be filtered out client-side\n\t// gh pr list has no --draft=false flag\n\tconst fetchLimit = Math.max(limit * 2, 50)\n\n\tlogger.debug('Fetching GitHub PR list', { limit, fetchLimit, cwd: options?.cwd })\n\n\tconst result = await executeGhCommand<Array<{\n\t\tnumber: number\n\t\ttitle: string\n\t\tupdatedAt: string\n\t\turl: string\n\t\tstate: string\n\t\tisDraft: boolean\n\t}>>([\n\t\t'pr', 'list',\n\t\t'--state', 'open',\n\t\t'--json', 'number,title,updatedAt,url,state,isDraft',\n\t\t'--limit', String(fetchLimit),\n\t], options?.cwd ? { cwd: options.cwd } : undefined)\n\n\treturn (result ?? [])\n\t\t.filter(item => !item.isDraft)\n\t\t.slice(0, limit)\n\t\t.map(item => ({\n\t\t\tid: String(item.number),\n\t\t\ttitle: `[PR] ${item.title}`,\n\t\t\tupdatedAt: item.updatedAt,\n\t\t\turl: item.url,\n\t\t\tstate: item.state.toLowerCase(),\n\t\t}))\n}\n","// Lines 1-5: Imports\nimport chalk, { Chalk } from 'chalk'\nimport { detectDarkMode, type ThemeMode } from './terminal.js'\n\n// Lines 7-17: Type definitions\nexport interface LoggerOptions {\n prefix?: string\n timestamp?: boolean\n silent?: boolean\n forceColor?: boolean | undefined | null\n debug?: boolean\n}\n\nexport interface Logger {\n info: (message: string, ...args: unknown[]) => void\n success: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n debug: (message: string, ...args: unknown[]) => void\n setDebug: (enabled: boolean) => void\n isDebugEnabled: () => boolean\n stdout: NodeJS.WriteStream // Stream for progress output (stdout normally, stderr in JSON mode)\n}\n\n// Lines 19-29: Stream-specific chalk instances\nconst stdoutChalk = new Chalk({ level: chalk.level })\nconst stderrChalk = new Chalk({ level: chalk.level })\n\n// Lines 31-60: Theme-aware color selection\nlet currentThemeMode: ThemeMode = 'light'\n\n/**\n * Initialize theme mode detection\n * This is called automatically on module load\n */\nasync function initializeThemeMode(): Promise<void> {\n try {\n currentThemeMode = await detectDarkMode()\n } catch {\n // Default to light mode on error\n currentThemeMode = 'light'\n }\n}\n\n// Initialize theme mode on module load (non-blocking)\nvoid initializeThemeMode()\n\n/**\n * Get color function based on current theme mode\n * Light mode uses standard colors, dark mode uses brighter/more saturated variants\n */\nfunction getInfoColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.cyan : chalkInstance.blue\n}\n\nfunction getSuccessColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.greenBright : chalkInstance.green\n}\n\nfunction getWarnColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.yellowBright : chalkInstance.yellow\n}\n\nfunction getErrorColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.redBright : chalkInstance.red\n}\n\nfunction getDebugColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.gray : chalkInstance.gray\n}\n\n// Lines 31-45: Helper functions\nfunction formatMessage(message: string, ...args: unknown[]): string {\n // Convert args to strings and append to message\n const formattedArgs = args.map(arg =>\n typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)\n )\n return formattedArgs.length > 0 ? `${message} ${formattedArgs.join(' ')}` : message\n}\n\nfunction formatWithEmoji(message: string, emoji: string, colorFn: (str: string) => string): string {\n if (message.trim()) {\n return colorFn(`${emoji} ${message}`)\n } else {\n return ''\n }\n}\n\nlet globalDebugEnabled = false\n\n// Lines 47-96: Main logger implementation\n/* eslint-disable no-console */\nexport const logger: Logger = {\n info: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '🗂️ ', getInfoColor(stdoutChalk))\n console.log(output)\n },\n\n success: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '✅', getSuccessColor(stdoutChalk))\n console.log(output)\n },\n\n warn: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '⚠️ ', getWarnColor(stderrChalk))\n console.error(output)\n },\n\n error: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '❌', getErrorColor(stderrChalk))\n console.error(output)\n },\n\n debug: (message: string, ...args: unknown[]): void => {\n if (globalDebugEnabled) {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '🔍', getDebugColor(stdoutChalk))\n console.log(output)\n }\n },\n\n setDebug: (enabled: boolean): void => {\n globalDebugEnabled = enabled\n },\n\n isDebugEnabled: (): boolean => {\n return globalDebugEnabled\n },\n\n stdout: process.stdout\n}\n/* eslint-enable no-console */\n\n// Lines 98-145: Factory function for custom logger instances\nexport function createLogger(options: LoggerOptions = {}): Logger {\n const { prefix = '', timestamp = false, silent = false, forceColor, debug = globalDebugEnabled } = options\n\n // Local debug flag for this logger instance\n let localDebugEnabled = debug\n\n // Create chalk instances with forced color if needed\n const customStdoutChalk = forceColor !== undefined\n ? new Chalk({ level: forceColor ? 3 : 0 })\n : stdoutChalk\n const customStderrChalk = forceColor !== undefined\n ? new Chalk({ level: forceColor ? 3 : 0 })\n : stderrChalk\n\n const prefixStr = prefix ? `[${prefix}] ` : ''\n const getTimestamp = (): string => timestamp ? `[${new Date().toISOString()}] ` : ''\n\n if (silent) {\n // Return no-op logger when silent\n return {\n info: (): void => {},\n success: (): void => {},\n warn: (): void => {},\n error: (): void => {},\n debug: (): void => {},\n setDebug: (): void => {},\n isDebugEnabled: (): boolean => {\n return false\n },\n stdout: process.stdout\n }\n }\n\n /* eslint-disable no-console */\n return {\n info: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🗂️ ', getInfoColor(customStdoutChalk))\n console.log(output)\n },\n success: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '✅', getSuccessColor(customStdoutChalk))\n console.log(output)\n },\n warn: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '⚠️ ', getWarnColor(customStderrChalk))\n console.error(output)\n },\n error: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '❌', getErrorColor(customStderrChalk))\n console.error(output)\n },\n debug: (message: string, ...args: unknown[]): void => {\n if (localDebugEnabled) {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🔍', getDebugColor(customStdoutChalk))\n console.log(output)\n }\n },\n setDebug: (enabled: boolean): void => {\n localDebugEnabled = enabled\n },\n isDebugEnabled: (): boolean => {\n return globalDebugEnabled\n },\n stdout: process.stdout\n }\n /* eslint-enable no-console */\n}\n\n// Lines 147-200: Factory function for stderr-only logger (for JSON mode)\n/**\n * Creates a logger that redirects all output to stderr.\n * Use this in JSON mode so progress messages don't pollute stdout.\n * The JSON output can then be cleanly piped.\n */\nexport function createStderrLogger(options: LoggerOptions = {}): Logger {\n const { prefix = '', timestamp = false, forceColor, debug = globalDebugEnabled } = options\n\n // Local debug flag for this logger instance\n let localDebugEnabled = debug\n\n // Create chalk instances with forced color if needed\n const customChalk = forceColor !== undefined\n ? new Chalk({ level: forceColor ? 3 : 0 })\n : stderrChalk\n\n const prefixStr = prefix ? `[${prefix}] ` : ''\n const getTimestamp = (): string => timestamp ? `[${new Date().toISOString()}] ` : ''\n\n return {\n info: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🗂️ ', getInfoColor(customChalk))\n console.error(output) // Redirect to stderr\n },\n success: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '✅', getSuccessColor(customChalk))\n console.error(output) // Redirect to stderr\n },\n warn: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '⚠️ ', getWarnColor(customChalk))\n console.error(output)\n },\n error: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '❌', getErrorColor(customChalk))\n console.error(output)\n },\n debug: (message: string, ...args: unknown[]): void => {\n if (localDebugEnabled) {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🔍', getDebugColor(customChalk))\n console.error(output) // Redirect to stderr\n }\n },\n setDebug: (enabled: boolean): void => {\n localDebugEnabled = enabled\n },\n isDebugEnabled: (): boolean => {\n return globalDebugEnabled\n },\n stdout: process.stderr // Use stderr for progress output in JSON mode\n }\n}\n\n/**\n * Set the theme mode for logger colors\n * This is useful for testing or overriding auto-detection\n *\n * @param mode - Theme mode to use ('light' or 'dark')\n */\nexport function setThemeMode(mode: ThemeMode): void {\n currentThemeMode = mode\n}\n\n/**\n * Get the current theme mode\n *\n * @returns Current theme mode\n */\nexport function getThemeMode(): ThemeMode {\n return currentThemeMode\n}\n\n// Default export\nexport default logger\n","import { execa } from 'execa'\nimport { existsSync } from 'node:fs'\nimport type { Platform } from '../types/index.js'\nimport { buildEnvSourceCommands } from './env.js'\n\nexport interface TerminalWindowOptions {\n\tworkspacePath?: string\n\tcommand?: string\n\tbackgroundColor?: { r: number; g: number; b: number }\n\tport?: number\n\tincludeEnvSetup?: boolean // source .env\n\tincludePortExport?: boolean // export PORT=<port>\n\ttitle?: string // Terminal tab title\n}\n\n/**\n * Detect current platform\n */\nexport function detectPlatform(): Platform {\n\tconst platform = process.platform\n\tif (platform === 'darwin') return 'darwin'\n\tif (platform === 'linux') return 'linux'\n\tif (platform === 'win32') return 'win32'\n\treturn 'unsupported'\n}\n\n/**\n * Theme mode for color palette selection\n */\nexport type ThemeMode = 'light' | 'dark'\n\n/**\n * Detect macOS dark mode using defaults command\n * Returns 'light' as default for non-macOS platforms or detection failures\n *\n * Uses `defaults read -g AppleInterfaceStyle` which returns \"Dark\" in dark mode\n * and errors (exit code 1) in light mode. This approach doesn't require\n * System Events permission unlike AppleScript.\n */\nexport async function detectDarkMode(): Promise<ThemeMode> {\n\tconst platform = detectPlatform()\n\tif (platform !== 'darwin') {\n\t\treturn 'light'\n\t}\n\n\ttry {\n\t\tconst result = await execa('defaults', ['read', '-g', 'AppleInterfaceStyle'])\n\t\treturn result.stdout.trim().toLowerCase() === 'dark' ? 'dark' : 'light'\n\t} catch {\n\t\t// defaults command errors when AppleInterfaceStyle is not set (light mode)\n\t\treturn 'light'\n\t}\n}\n\n/**\n * Detect if iTerm2 is installed on macOS\n * Returns false on non-macOS platforms\n */\nexport async function detectITerm2(): Promise<boolean> {\n\tconst platform = detectPlatform()\n\tif (platform !== 'darwin') return false\n\n\t// Check if iTerm.app exists at standard location\n\treturn existsSync('/Applications/iTerm.app')\n}\n\n/**\n * Open new terminal window with specified options\n * Currently supports macOS only\n */\nexport async function openTerminalWindow(\n\toptions: TerminalWindowOptions\n): Promise<void> {\n\tconst platform = detectPlatform()\n\n\tif (platform !== 'darwin') {\n\t\tthrow new Error(\n\t\t\t`Terminal window launching not yet supported on ${platform}. ` +\n\t\t\t\t`Currently only macOS is supported.`\n\t\t)\n\t}\n\n\t// Detect if iTerm2 is available\n\tconst hasITerm2 = await detectITerm2()\n\n\t// Build appropriate AppleScript based on terminal availability\n\tconst applescript = hasITerm2\n\t\t? await buildITerm2SingleTabScript(options)\n\t\t: await buildAppleScript(options)\n\n\ttry {\n\t\tawait execa('osascript', ['-e', applescript])\n\n\t\t// Activate the appropriate terminal application (only needed for Terminal.app)\n\t\t// iTerm2 script includes its own activation\n\t\tif (!hasITerm2) {\n\t\t\tawait execa('osascript', ['-e', 'tell application \"Terminal\" to activate'])\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to open terminal window: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t)\n\t}\n}\n\n/**\n * Build AppleScript for macOS Terminal.app\n */\nasync function buildAppleScript(options: TerminalWindowOptions): Promise<string> {\n\tconst {\n\t\tworkspacePath,\n\t\tcommand,\n\t\tbackgroundColor,\n\t\tport,\n\t\tincludeEnvSetup,\n\t\tincludePortExport,\n\t} = options\n\n\t// Build command sequence\n\tconst commands: string[] = []\n\n\t// Navigate to workspace\n\tif (workspacePath) {\n\t\tcommands.push(`cd '${escapePathForAppleScript(workspacePath)}'`)\n\t}\n\n\t// Source all dotenv-flow files\n\tif (includeEnvSetup && workspacePath) {\n\t\tconst sourceCommands = await buildEnvSourceCommands(\n\t\t\tworkspacePath,\n\t\t\tasync (p) => existsSync(p)\n\t\t)\n\t\tcommands.push(...sourceCommands)\n\t}\n\n\t// Export PORT variable\n\tif (includePortExport && port !== undefined) {\n\t\tcommands.push(`export PORT=${port}`)\n\t}\n\n\t// Add custom command\n\tif (command) {\n\t\tcommands.push(command)\n\t}\n\n\t// Join with &&\n\tconst fullCommand = commands.join(' && ')\n\n\t// Prefix with space to prevent shell history pollution\n\t// Most shells (bash/zsh) ignore commands starting with space when HISTCONTROL=ignorespace\n\tconst historyFreeCommand = ` ${fullCommand}`\n\n\t// Build AppleScript\n\tlet script = `tell application \"Terminal\"\\n`\n\tscript += ` set newTab to do script \"${escapeForAppleScript(historyFreeCommand)}\"\\n`\n\n\t// Apply background color if provided\n\tif (backgroundColor) {\n\t\tconst { r, g, b } = backgroundColor\n\t\t// Convert 8-bit RGB (0-255) to 16-bit RGB (0-65535)\n\t\tscript += ` set background color of newTab to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\tscript += `end tell`\n\n\treturn script\n}\n\n/**\n * Escape path for AppleScript string\n * Single quotes in path need special escaping\n */\nfunction escapePathForAppleScript(path: string): string {\n\t// Replace single quote with '\\''\n\treturn path.replace(/'/g, \"'\\\\''\")\n}\n\n/**\n * Escape command for AppleScript do script\n * Must handle double quotes and backslashes\n */\nfunction escapeForAppleScript(command: string): string {\n\treturn (\n\t\tcommand\n\t\t\t.replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n\t\t\t.replace(/\"/g, '\\\\\"') // Escape double quotes\n\t)\n}\n\n/**\n * Build iTerm2 AppleScript for single tab\n */\nasync function buildITerm2SingleTabScript(options: TerminalWindowOptions): Promise<string> {\n\tconst command = await buildCommandSequence(options)\n\n\tlet script = 'tell application id \"com.googlecode.iterm2\"\\n'\n\tscript += ' create window with default profile\\n'\n\tscript += ' set s1 to current session of current window\\n\\n'\n\n\t// Set background color\n\tif (options.backgroundColor) {\n\t\tconst { r, g, b } = options.backgroundColor\n\t\tscript += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\t// Execute command\n\tscript += ` tell s1 to write text \"${escapeForAppleScript(command)}\"\\n\\n`\n\n\t// Set session name (tab title)\n\tif (options.title) {\n\t\tscript += ` set name of s1 to \"${escapeForAppleScript(options.title)}\"\\n\\n`\n\t}\n\n\t// Activate iTerm2\n\tscript += ' activate\\n'\n\tscript += 'end tell'\n\n\treturn script\n}\n\n/**\n * Build command sequence for terminal\n */\nasync function buildCommandSequence(options: TerminalWindowOptions): Promise<string> {\n\tconst {\n\t\tworkspacePath,\n\t\tcommand,\n\t\tport,\n\t\tincludeEnvSetup,\n\t\tincludePortExport,\n\t} = options\n\n\tconst commands: string[] = []\n\n\t// Navigate to workspace\n\tif (workspacePath) {\n\t\tcommands.push(`cd '${escapePathForAppleScript(workspacePath)}'`)\n\t}\n\n\t// Source all dotenv-flow files\n\tif (includeEnvSetup && workspacePath) {\n\t\tconst sourceCommands = await buildEnvSourceCommands(\n\t\t\tworkspacePath,\n\t\t\tasync (p) => existsSync(p)\n\t\t)\n\t\tcommands.push(...sourceCommands)\n\t}\n\n\t// Export PORT variable\n\tif (includePortExport && port !== undefined) {\n\t\tcommands.push(`export PORT=${port}`)\n\t}\n\n\t// Add custom command\n\tif (command) {\n\t\tcommands.push(command)\n\t}\n\n\t// Join with &&\n\tconst fullCommand = commands.join(' && ')\n\n\t// Prefix with space to prevent shell history pollution\n\treturn ` ${fullCommand}`\n}\n\n/**\n * Build iTerm2 AppleScript for multiple tabs (2+) in single window\n */\nasync function buildITerm2MultiTabScript(\n\toptionsArray: TerminalWindowOptions[]\n): Promise<string> {\n\tif (optionsArray.length < 2) {\n\t\tthrow new Error('buildITerm2MultiTabScript requires at least 2 terminal options')\n\t}\n\n\tlet script = 'tell application id \"com.googlecode.iterm2\"\\n'\n\tscript += ' create window with default profile\\n'\n\tscript += ' set newWindow to current window\\n'\n\n\t// First tab\n\tconst options1 = optionsArray[0]\n\tif (!options1) {\n\t\tthrow new Error('First terminal option is undefined')\n\t}\n\tconst command1 = await buildCommandSequence(options1)\n\n\tscript += ' set s1 to current session of newWindow\\n\\n'\n\n\t// Set background color for first tab\n\tif (options1.backgroundColor) {\n\t\tconst { r, g, b } = options1.backgroundColor\n\t\tscript += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\t// Execute command in first tab\n\tscript += ` tell s1 to write text \"${escapeForAppleScript(command1)}\"\\n\\n`\n\n\t// Set tab title for first tab\n\tif (options1.title) {\n\t\tscript += ` set name of s1 to \"${escapeForAppleScript(options1.title)}\"\\n\\n`\n\t}\n\n\t// Subsequent tabs (2, 3, ...)\n\tfor (let i = 1; i < optionsArray.length; i++) {\n\t\tconst options = optionsArray[i]\n\t\tif (!options) {\n\t\t\tthrow new Error(`Terminal option at index ${i} is undefined`)\n\t\t}\n\t\tconst command = await buildCommandSequence(options)\n\t\tconst sessionVar = `s${i + 1}`\n\n\t\t// Create tab\n\t\tscript += ' tell newWindow\\n'\n\t\tscript += ` set newTab${i} to (create tab with default profile)\\n`\n\t\tscript += ' end tell\\n'\n\t\tscript += ` set ${sessionVar} to current session of newTab${i}\\n\\n`\n\n\t\t// Set background color\n\t\tif (options.backgroundColor) {\n\t\t\tconst { r, g, b } = options.backgroundColor\n\t\t\tscript += ` set background color of ${sessionVar} to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t\t}\n\n\t\t// Execute command\n\t\tscript += ` tell ${sessionVar} to write text \"${escapeForAppleScript(command)}\"\\n\\n`\n\n\t\t// Set tab title\n\t\tif (options.title) {\n\t\t\tscript += ` set name of ${sessionVar} to \"${escapeForAppleScript(options.title)}\"\\n\\n`\n\t\t}\n\t}\n\n\t// Activate iTerm2\n\tscript += ' activate\\n'\n\tscript += 'end tell'\n\n\treturn script\n}\n\n/**\n * Open multiple terminal windows/tabs (2+) with specified options\n * If iTerm2 is available on macOS, creates single window with multiple tabs\n * Otherwise falls back to multiple separate Terminal.app windows\n */\nexport async function openMultipleTerminalWindows(\n\toptionsArray: TerminalWindowOptions[]\n): Promise<void> {\n\tif (optionsArray.length < 2) {\n\t\tthrow new Error('openMultipleTerminalWindows requires at least 2 terminal options. Use openTerminalWindow for single terminal.')\n\t}\n\n\tconst platform = detectPlatform()\n\n\tif (platform !== 'darwin') {\n\t\tthrow new Error(\n\t\t\t`Terminal window launching not yet supported on ${platform}. ` +\n\t\t\t\t`Currently only macOS is supported.`\n\t\t)\n\t}\n\n\t// Detect if iTerm2 is available\n\tconst hasITerm2 = await detectITerm2()\n\n\tif (hasITerm2) {\n\t\t// Use iTerm2 with multiple tabs in single window\n\t\tconst applescript = await buildITerm2MultiTabScript(optionsArray)\n\n\t\ttry {\n\t\t\tawait execa('osascript', ['-e', applescript])\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to open iTerm2 window: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t} else {\n\t\t// Fall back to multiple Terminal.app windows\n\t\tfor (let i = 0; i < optionsArray.length; i++) {\n\t\t\tconst options = optionsArray[i]\n\t\t\tif (!options) {\n\t\t\t\tthrow new Error(`Terminal option at index ${i} is undefined`)\n\t\t\t}\n\t\t\tawait openTerminalWindow(options)\n\n\t\t\t// Brief pause between terminals (except after last one)\n\t\t\tif (i < optionsArray.length - 1) {\n\t\t\t\t// eslint-disable-next-line no-undef\n\t\t\t\tawait new Promise<void>((resolve) => setTimeout(resolve, 1000))\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Open dual terminal windows/tabs with specified options\n * If iTerm2 is available on macOS, creates single window with two tabs\n * Otherwise falls back to two separate Terminal.app windows\n */\nexport async function openDualTerminalWindow(\n\toptions1: TerminalWindowOptions,\n\toptions2: TerminalWindowOptions\n): Promise<void> {\n\t// Delegate to openMultipleTerminalWindows for consistency\n\tawait openMultipleTerminalWindows([options1, options2])\n}\n","import path from 'path'\nimport dotenvFlow, { type DotenvFlowConfigOptions } from 'dotenv-flow'\nimport { logger } from './logger.js'\n\n/**\n * Parse .env file content into key-value map\n * Handles comments, empty lines, quoted/unquoted values, multiline values\n */\nexport function parseEnvFile(content: string): Map<string, string> {\n const envMap = new Map<string, string>()\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const trimmedLine = line.trim()\n\n // Skip empty lines and comments\n if (!trimmedLine || trimmedLine.startsWith('#')) {\n continue\n }\n\n // Remove 'export ' prefix if present\n const cleanLine = trimmedLine.startsWith('export ')\n ? trimmedLine.substring(7)\n : trimmedLine\n\n // Find the first equals sign\n const equalsIndex = cleanLine.indexOf('=')\n if (equalsIndex === -1) {\n continue\n }\n\n const key = cleanLine.substring(0, equalsIndex).trim()\n let value = cleanLine.substring(equalsIndex + 1)\n\n // Handle quoted values\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.substring(1, value.length - 1)\n // Unescape quotes\n value = value.replace(/\\\\\"/g, '\"').replace(/\\\\'/g, \"'\")\n // Unescape newlines\n value = value.replace(/\\\\n/g, '\\n')\n }\n\n if (key) {\n envMap.set(key, value)\n }\n }\n\n return envMap\n}\n\n/**\n * Format environment variable as line for .env file\n * Always quotes values and escapes internal quotes\n */\nexport function formatEnvLine(key: string, value: string): string {\n // Escape quotes and newlines in the value\n const escapedValue = value\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n\n return `${key}=\"${escapedValue}\"`\n}\n\n/**\n * Validate environment variable name and value\n */\nexport function validateEnvVariable(\n key: string,\n _value?: string\n): { valid: boolean; error?: string } {\n if (!key || key.length === 0) {\n return {\n valid: false,\n error: 'Environment variable key cannot be empty',\n }\n }\n\n if (!isValidEnvKey(key)) {\n return {\n valid: false,\n error: `Invalid environment variable name: ${key}. Must start with a letter or underscore and contain only letters, numbers, and underscores.`,\n }\n }\n\n // Values can be any string, including empty\n return { valid: true }\n}\n\n/**\n * Normalize line endings for cross-platform compatibility\n */\nexport function normalizeLineEndings(content: string): string {\n return content.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n}\n\n/**\n * Extract port from .env file if present\n */\nexport function extractPort(envContent: Map<string, string>): number | null {\n const portValue = envContent.get('PORT')\n if (!portValue) {\n return null\n }\n\n const port = parseInt(portValue, 10)\n if (isNaN(port)) {\n return null\n }\n\n return port\n}\n\n/**\n * Check if environment variable key is valid\n */\nexport function isValidEnvKey(key: string): boolean {\n if (!key || key.length === 0) {\n return false\n }\n\n // Must start with letter or underscore, followed by letters, numbers, or underscores\n const validKeyRegex = /^[A-Za-z_][A-Za-z0-9_]*$/\n return validKeyRegex.test(key)\n}\n\n/**\n * Load environment variables using dotenv-flow\n * Supports environment-specific files (.env.development, .env.production, etc.)\n * and local overrides (.env.local, .env.development.local)\n */\nexport function loadEnvIntoProcess(options?: {\n path?: string\n nodeEnv?: string\n defaultNodeEnv?: string\n}): { parsed?: Record<string, string>; error?: Error } {\n logger.debug('Loading environment variables with dotenv-flow', {\n options: {\n path: options?.path ?? 'current working directory',\n nodeEnv: options?.nodeEnv ?? 'not specified',\n defaultNodeEnv: options?.defaultNodeEnv ?? 'development (default)'\n }\n })\n\n const configOptions: Partial<DotenvFlowConfigOptions> = {\n silent: true, // Don't throw errors if .env files are missing\n }\n\n // Only add defined values to avoid TypeScript strict type issues\n if (options?.path !== undefined) {\n configOptions.path = options.path\n logger.debug(`Using custom path: ${options.path}`)\n }\n if (options?.nodeEnv !== undefined) {\n configOptions.node_env = options.nodeEnv\n logger.debug(`Using NODE_ENV: ${options.nodeEnv}`)\n }\n if (options?.defaultNodeEnv !== undefined) {\n configOptions.default_node_env = options.defaultNodeEnv\n logger.debug(`Using default NODE_ENV: ${options.defaultNodeEnv}`)\n } else {\n configOptions.default_node_env = 'development'\n logger.debug('Using default NODE_ENV: development')\n }\n\n logger.debug('dotenv-flow config options:', configOptions)\n\n const result = dotenvFlow.config(configOptions)\n\n const returnValue: { parsed?: Record<string, string>; error?: Error } = {}\n\n if (result.parsed) {\n returnValue.parsed = result.parsed as Record<string, string>\n const variableCount = Object.keys(result.parsed).length\n logger.debug(`Successfully loaded ${variableCount} environment variables`)\n } else {\n logger.debug('No environment variables were parsed')\n }\n\n if (result.error) {\n returnValue.error = result.error\n logger.debug('dotenv-flow returned an error', {\n error: result.error.message,\n name: result.error.name\n })\n } else {\n logger.debug('dotenv-flow completed without errors')\n }\n\n return returnValue\n}\n\n/**\n * Check if an error from loadEnvIntoProcess indicates no .env files were found\n * This is a harmless condition that shouldn't be logged as a warning\n */\nexport function isNoEnvFilesFoundError(error: Error): boolean {\n return error.message.startsWith('no \".env*\" files matching pattern')\n}\n\n/**\n * Load environment variables for a specific workspace\n * Automatically determines environment based on NODE_ENV or defaults to development\n */\nexport function loadWorkspaceEnv(workspacePath: string): {\n parsed?: Record<string, string>\n error?: Error\n} {\n const nodeEnv = process.env.NODE_ENV ?? 'development'\n\n logger.debug('Loading workspace environment variables', {\n workspacePath,\n detectedNodeEnv: nodeEnv,\n processNodeEnv: process.env.NODE_ENV ?? 'not set'\n })\n\n return loadEnvIntoProcess({\n path: workspacePath,\n nodeEnv: nodeEnv,\n defaultNodeEnv: 'development'\n })\n}\n\n// CONSTANT: Always use 'development' per critical constraint, unless overridden\nconst DOTENV_FLOW_NODE_ENV = process.env.DOTENV_FLOW_NODE_ENV ?? 'development'\n\n/**\n * Get dotenv-flow files in precedence order (lowest to highest)\n * Always uses 'development' as NODE_ENV per constraint\n */\nexport function getDotenvFlowFiles(): string[] {\n return [\n '.env',\n '.env.local',\n `.env.${DOTENV_FLOW_NODE_ENV}`,\n `.env.${DOTENV_FLOW_NODE_ENV}.local`\n ]\n}\n\n/**\n * Map a file to its \"local\" equivalent for git-safe writes\n * .env -> .env.local\n * .env.{NODE_ENV} -> .env.{NODE_ENV}.local\n * Already local files return unchanged\n */\nexport function getLocalEquivalent(filename: string): string {\n // Already a .local file\n if (filename.endsWith('.local')) {\n return filename\n }\n return `${filename}.local`\n}\n\n/**\n * Find the appropriate env file to write a database URL variable to\n * Considers dotenv-flow precedence and git tracking status\n * Returns path relative to workspacePath\n *\n * Algorithm:\n * 1. Search files in reverse precedence order (highest first)\n * 2. Find first file containing the variable\n * 3. If tracked by git, return its .local equivalent\n * 4. If not tracked, return the file itself\n * 5. If not found anywhere, return '.env.local' (safe default)\n */\nexport async function findEnvFileForDatabaseUrl(\n workspacePath: string,\n variableName: string,\n isFileTracked: (filePath: string, cwd: string) => Promise<boolean>,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<string> {\n // Find the highest-precedence file containing the variable\n const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable)\n\n if (file === null) {\n // Variable not found anywhere - use safe default\n return '.env.local'\n }\n\n // Found the variable - check git tracking\n const isTracked = await isFileTracked(file, workspacePath)\n if (isTracked) {\n // Return .local equivalent for git safety\n return getLocalEquivalent(file)\n }\n\n return file\n}\n\n/**\n * Build shell source commands for all existing dotenv-flow files\n * Returns commands in precedence order (later overrides earlier)\n */\nexport async function buildEnvSourceCommands(\n workspacePath: string,\n fileExists: (filePath: string) => Promise<boolean>\n): Promise<string[]> {\n const files = getDotenvFlowFiles()\n const commands: string[] = []\n\n for (const file of files) {\n const fullPath = path.join(workspacePath, file)\n const exists = await fileExists(fullPath)\n if (exists) {\n commands.push(`source ${file}`)\n }\n }\n\n return commands\n}\n\n/**\n * Find the highest-precedence env file containing a variable\n * Searches all dotenv-flow files in reverse precedence order (highest first)\n * Returns the relative filename if found, null otherwise\n */\nexport async function findEnvFileContainingVariable(\n workspacePath: string,\n variableName: string,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<string | null> {\n const files = getDotenvFlowFiles().reverse() // highest precedence first\n\n for (const file of files) {\n const fullPath = path.join(workspacePath, file)\n\n // Skip if file doesn't exist\n if (!(await fileExists(fullPath))) {\n continue\n }\n\n // Check if file contains the variable\n const value = await getEnvVariable(fullPath, variableName)\n if (value !== null) {\n return file\n }\n }\n\n return null\n}\n\n/**\n * Check if a variable exists in any dotenv-flow file\n * Searches all dotenv-flow files (.env, .env.local, .env.{NODE_ENV}, .env.{NODE_ENV}.local)\n * Returns true if variable is found in any file, false otherwise\n */\nexport async function hasVariableInAnyEnvFile(\n workspacePath: string,\n variableName: string,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<boolean> {\n const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable)\n return file !== null\n}\n","/* global fetch, AbortController, setTimeout, clearTimeout */\nimport { tmpdir } from 'node:os'\nimport { join, extname } from 'node:path'\nimport { existsSync, mkdirSync, createWriteStream, unlinkSync } from 'node:fs'\nimport { pipeline } from 'node:stream/promises'\nimport { Readable } from 'node:stream'\nimport { createHash } from 'node:crypto'\nimport { execa } from 'execa'\nimport { logger } from './logger.js'\nimport type { IssueProvider } from '../mcp/types.js'\n\n/**\n * Represents a matched image in markdown content\n */\nexport interface ImageMatch {\n fullMatch: string\n url: string\n isMarkdown: boolean // true for ![](url), false for <img>\n}\n\n/**\n * Supported image extensions\n */\nconst SUPPORTED_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg']\n\n/**\n * Maximum allowed image size (10MB)\n */\nconst MAX_IMAGE_SIZE = 10 * 1024 * 1024\n\n/**\n * Request timeout in milliseconds (30 seconds)\n */\nconst REQUEST_TIMEOUT_MS = 30000\n\n/**\n * Cache directory path for downloaded images\n */\nexport const CACHE_DIR = join(tmpdir(), 'iloom-images')\n\n/**\n * Cached GitHub auth token (module-level to avoid repeated `gh auth token` calls)\n */\nlet cachedGitHubToken: string | undefined\n\n/**\n * Extract all image URLs from markdown content\n * Handles both ![alt](url) and <img src=\"url\"> formats\n *\n * @param content - Markdown content to parse\n * @returns Array of image matches with full match string and URL\n */\nexport function extractMarkdownImageUrls(content: string): ImageMatch[] {\n if (!content) {\n return []\n }\n\n const matches: ImageMatch[] = []\n\n // Regex for markdown images: ![alt](url)\n // Captures the entire match and the URL separately\n // Handles parentheses in URLs by matching balanced parens\n // The URL part matches: non-paren chars OR (balanced paren group)*, followed by non-paren/non-space chars\n const markdownRegex = /!\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\((?:[^()\\s]|\\([^()]*\\))*\\))+)\\)/g\n let match: RegExpExecArray | null\n\n while ((match = markdownRegex.exec(content)) !== null) {\n const url = match[2]\n if (url) {\n matches.push({\n fullMatch: match[0],\n url,\n isMarkdown: true\n })\n }\n }\n\n // Regex for HTML img tags: <img ... src=\"url\" ...>\n // Handles both double and single quotes, and self-closing tags\n const htmlImgRegex = /<img\\s+[^>]*src=[\"']([^\"']+)[\"'][^>]*\\/?>/gi\n\n while ((match = htmlImgRegex.exec(content)) !== null) {\n const url = match[1]\n if (url) {\n matches.push({\n fullMatch: match[0],\n url,\n isMarkdown: false\n })\n }\n }\n\n return matches\n}\n\n/**\n * Check if URL requires authentication to download\n * - Linear: uploads.linear.app\n * - GitHub: private-user-images.githubusercontent.com\n *\n * @param url - Image URL to check\n * @returns true if URL requires authentication\n */\nexport function isAuthenticatedImageUrl(url: string): boolean {\n try {\n const parsedUrl = new URL(url)\n const hostname = parsedUrl.hostname.toLowerCase()\n\n // Linear uploads require authentication\n if (hostname === 'uploads.linear.app') {\n return true\n }\n\n // GitHub private user images require authentication\n if (hostname === 'private-user-images.githubusercontent.com') {\n return true\n }\n\n // GitHub user-attachments (uploaded images in issues/PRs) require authentication\n if (hostname === 'github.com' && parsedUrl.pathname.startsWith('/user-attachments/assets/')) {\n return true\n }\n\n return false\n } catch {\n // Invalid URL - treat as not authenticated\n return false\n }\n}\n\n/**\n * Get extension from URL pathname\n *\n * @param url - URL to extract extension from\n * @returns Extension including dot (e.g., '.png') or null if not found\n */\nfunction getExtensionFromUrl(url: string): string | null {\n try {\n const parsedUrl = new URL(url)\n const pathname = parsedUrl.pathname\n const ext = extname(pathname).toLowerCase()\n\n if (SUPPORTED_EXTENSIONS.includes(ext)) {\n return ext\n }\n return null\n } catch {\n return null\n }\n}\n\n/**\n * Generate cache key from URL\n * For GitHub URLs, strips JWT query params to ensure consistent caching\n * Returns hash + original extension\n *\n * @param url - Image URL to generate cache key for\n * @returns Cache key (hash + extension)\n */\nexport function getCacheKey(url: string): string {\n const parsedUrl = new URL(url)\n\n // For GitHub private images, remove jwt query param to get stable cache key\n // The jwt changes each fetch but the base URL is the same for the same image\n if (parsedUrl.hostname === 'private-user-images.githubusercontent.com') {\n parsedUrl.searchParams.delete('jwt')\n }\n\n // Get URL without volatile params for hashing\n const stableUrl = parsedUrl.toString()\n\n // Generate SHA256 hash of the stable URL (first 16 chars for brevity)\n const hash = createHash('sha256').update(stableUrl).digest('hex').slice(0, 16)\n\n // Extract extension from URL pathname, default to .png\n const ext = getExtensionFromUrl(url) ?? '.png'\n\n return `${hash}${ext}`\n}\n\n/**\n * Check if image is already cached\n * Returns file path if exists, undefined otherwise\n *\n * @param url - Image URL to check cache for\n * @returns Cached file path or undefined\n */\nexport function getCachedImagePath(url: string): string | undefined {\n const cacheKey = getCacheKey(url)\n const cachedPath = join(CACHE_DIR, cacheKey)\n\n if (existsSync(cachedPath)) {\n return cachedPath\n }\n return undefined\n}\n\n/**\n * Get authentication token for the given provider\n *\n * @param provider - Provider type ('github' or 'linear')\n * @returns Authentication token or undefined\n */\nasync function getAuthToken(provider: IssueProvider): Promise<string | undefined> {\n if (provider === 'github') {\n // Return cached token if available\n if (cachedGitHubToken !== undefined) {\n return cachedGitHubToken\n }\n\n try {\n // Execute `gh auth token` to get GitHub token\n const result = await execa('gh', ['auth', 'token'])\n cachedGitHubToken = result.stdout.trim()\n return cachedGitHubToken\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.warn(`Failed to get GitHub auth token via gh CLI: ${message}`)\n return undefined\n }\n }\n\n if (provider === 'linear') {\n // Linear token from environment variable\n return process.env.LINEAR_API_TOKEN\n }\n\n return undefined\n}\n\n/**\n * Clear the cached GitHub auth token (for testing purposes)\n */\nexport function clearCachedGitHubToken(): void {\n cachedGitHubToken = undefined\n}\n\n/**\n * Download image from URL and stream it directly to a file\n *\n * @param url - Image URL to download\n * @param destPath - Destination file path\n * @param authHeader - Optional Authorization header value\n * @throws Error if download fails, times out, or exceeds size limit\n */\nexport async function downloadAndSaveImage(\n url: string,\n destPath: string,\n authHeader?: string\n): Promise<void> {\n const headers: Record<string, string> = {}\n if (authHeader) {\n headers['Authorization'] = authHeader\n }\n\n // Set up abort controller for timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS)\n\n try {\n const response = await fetch(url, { headers, signal: controller.signal })\n\n if (!response.ok) {\n throw new Error(`Failed to download image: ${response.status} ${response.statusText}`)\n }\n\n // Check Content-Length header if available\n const contentLength = response.headers.get('Content-Length')\n if (contentLength && parseInt(contentLength, 10) > MAX_IMAGE_SIZE) {\n throw new Error(`Image too large: ${contentLength} bytes exceeds ${MAX_IMAGE_SIZE} byte limit`)\n }\n\n if (!response.body) {\n throw new Error('Response body is null')\n }\n\n // Convert ReadableStream to Node.js Readable\n const reader = response.body.getReader()\n let bytesWritten = 0\n\n const nodeReadable = new Readable({\n async read(): Promise<void> {\n try {\n const { done, value } = await reader.read()\n if (done) {\n this.push(null)\n return\n }\n\n bytesWritten += value.byteLength\n if (bytesWritten > MAX_IMAGE_SIZE) {\n reader.cancel()\n this.destroy(new Error(`Image too large: ${bytesWritten} bytes exceeds ${MAX_IMAGE_SIZE} byte limit`))\n return\n }\n\n this.push(Buffer.from(value))\n } catch (err) {\n this.destroy(err instanceof Error ? err : new Error(String(err)))\n }\n }\n })\n\n // Ensure cache directory exists\n if (!existsSync(CACHE_DIR)) {\n mkdirSync(CACHE_DIR, { recursive: true })\n }\n\n // Stream to file\n const writeStream = createWriteStream(destPath)\n\n try {\n await pipeline(nodeReadable, writeStream)\n } catch (pipelineError) {\n // Clean up partial file on error\n try {\n if (existsSync(destPath)) {\n unlinkSync(destPath)\n }\n } catch {\n // Ignore cleanup errors\n }\n throw pipelineError\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Image download timed out after ${REQUEST_TIMEOUT_MS}ms`)\n }\n throw error\n } finally {\n clearTimeout(timeoutId)\n }\n}\n\n/**\n * Get the destination path for caching an image\n *\n * @param url - Original image URL (used to generate cache key)\n * @returns Local file path where image should be saved\n */\nexport function getCacheDestPath(url: string): string {\n // Ensure cache directory exists\n if (!existsSync(CACHE_DIR)) {\n mkdirSync(CACHE_DIR, { recursive: true })\n }\n\n // Generate cache key from URL\n const cacheKey = getCacheKey(url)\n return join(CACHE_DIR, cacheKey)\n}\n\n/**\n * Rewrite image URLs in markdown content\n *\n * @param content - Original markdown content\n * @param urlMap - Map of original URLs to local file paths\n * @returns Content with URLs replaced\n */\nexport function rewriteMarkdownUrls(\n content: string,\n urlMap: Map<string, string>\n): string {\n let result = content\n\n for (const [originalUrl, localPath] of urlMap) {\n // Escape special regex characters in the URL\n const escapedUrl = originalUrl.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const urlRegex = new RegExp(escapedUrl, 'g')\n result = result.replace(urlRegex, localPath)\n }\n\n return result\n}\n\n/**\n * Main entry point: process all images in markdown content\n * Downloads authenticated images (with caching), saves locally, rewrites URLs\n *\n * @param content - Markdown content to process\n * @param provider - Image provider for authentication ('github' or 'linear')\n * @returns Content with authenticated image URLs replaced with local file paths\n */\nexport async function processMarkdownImages(\n content: string,\n provider: IssueProvider\n): Promise<string> {\n // Early return if empty\n if (!content) {\n return ''\n }\n\n // Extract all image URLs\n const images = extractMarkdownImageUrls(content)\n if (images.length === 0) {\n return content\n }\n\n // Filter to only authenticated URLs\n const authImages = images.filter(img => isAuthenticatedImageUrl(img.url))\n if (authImages.length === 0) {\n return content\n }\n\n // Get auth token for provider\n const authToken = await getAuthToken(provider)\n\n // Deduplicate URLs (same image might appear multiple times)\n const uniqueUrls = [...new Set(authImages.map(img => img.url))]\n\n // Build URL map - process all unique URLs in parallel\n const urlMap = new Map<string, string>()\n\n // Download/cache images in parallel\n const downloadPromises = uniqueUrls.map(async (url) => {\n try {\n // Check cache first\n const cachedPath = getCachedImagePath(url)\n if (cachedPath) {\n logger.debug(`Using cached image: ${cachedPath}`)\n return { url, localPath: cachedPath }\n }\n\n // Cache miss - download and stream directly to file\n logger.debug(`Downloading image: ${url}`)\n const destPath = getCacheDestPath(url)\n await downloadAndSaveImage(\n url,\n destPath,\n authToken ? `Bearer ${authToken}` : undefined\n )\n return { url, localPath: destPath }\n } catch (error) {\n // Graceful degradation - log warning, return null to keep original URL\n const message = error instanceof Error ? error.message : String(error)\n logger.warn(`Failed to download image ${url}: ${message}`)\n return null\n }\n })\n\n const results = await Promise.all(downloadPromises)\n\n // Build URL map from results\n for (const result of results) {\n if (result !== null) {\n urlMap.set(result.url, result.localPath)\n }\n }\n\n // Rewrite and return\n return rewriteMarkdownUrls(content, urlMap)\n}\n","/**\n * GitHub implementation of Issue Management Provider\n * Uses GitHub CLI for all operations\n * Normalizes GitHub-specific fields (login) to provider-agnostic core fields (id, displayName)\n */\n\nimport type {\n\tIssueManagementProvider,\n\tGetIssueInput,\n\tGetPRInput,\n\tGetCommentInput,\n\tCreateCommentInput,\n\tUpdateCommentInput,\n\tCreateIssueInput,\n\tCreateChildIssueInput,\n\tCreateDependencyInput,\n\tGetDependenciesInput,\n\tRemoveDependencyInput,\n\tGetChildIssuesInput,\n\tCreateIssueResult,\n\tIssueResult,\n\tPRResult,\n\tCommentDetailResult,\n\tCommentResult,\n\tDependenciesResult,\n\tChildIssueResult,\n\tFlexibleAuthor,\n} from './types.js'\nimport {\n\texecuteGhCommand,\n\tcreateIssueComment,\n\tupdateIssueComment,\n\tcreatePRComment,\n\tcreateIssue,\n\tgetIssueNodeId,\n\taddSubIssue,\n\tgetIssueDatabaseId,\n\tgetIssueDependencies,\n\tcreateIssueDependency,\n\tremoveIssueDependency,\n\tgetSubIssues,\n} from '../utils/github.js'\nimport { processMarkdownImages } from '../utils/image-processor.js'\n\n/**\n * GitHub-specific author structure from API\n */\ninterface GitHubAuthor {\n\tlogin: string\n\tid?: number\n\tavatarUrl?: string\n\turl?: string\n}\n\n/**\n * Normalize GitHub author to FlexibleAuthor format\n */\nfunction normalizeAuthor(author: GitHubAuthor | null | undefined): FlexibleAuthor | null {\n\tif (!author) return null\n\n\treturn {\n\t\tid: author.id ? String(author.id) : author.login,\n\t\tdisplayName: author.login, // GitHub uses login as primary identifier\n\t\tlogin: author.login, // Preserve original GitHub field\n\t\t...(author.avatarUrl && { avatarUrl: author.avatarUrl }),\n\t\t...(author.url && { url: author.url }),\n\t}\n}\n\n/**\n * Extract numeric comment ID from GitHub comment URL\n * URL format: https://github.com/owner/repo/issues/123#issuecomment-3615239386\n */\nexport function extractNumericIdFromUrl(url: string): string {\n\tconst match = url.match(/#issuecomment-(\\d+)$/)\n\tif (!match?.[1]) {\n\t\tthrow new Error(`Cannot extract comment ID from URL: ${url}`)\n\t}\n\treturn match[1]\n}\n\n/**\n * GitHub-specific implementation of IssueManagementProvider\n */\nexport class GitHubIssueManagementProvider implements IssueManagementProvider {\n\treadonly providerName = 'github'\n\treadonly issuePrefix = '#'\n\n\t/**\n\t * Fetch issue details using gh CLI\n\t * Normalizes GitHub-specific fields to provider-agnostic format\n\t */\n\tasync getIssue(input: GetIssueInput): Promise<IssueResult> {\n\t\tconst { number, includeComments = true, repo } = input\n\n\t\t// Convert string ID to number for GitHub CLI\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Build fields list based on whether we need comments\n\t\tconst fields = includeComments\n\t\t\t? 'body,title,comments,labels,assignees,milestone,author,state,number,url'\n\t\t\t: 'body,title,labels,assignees,milestone,author,state,number,url'\n\n\t\t// Use gh issue view to fetch issue details\n\t\tinterface GitHubIssueResponse {\n\t\t\tnumber: number\n\t\t\ttitle: string\n\t\t\tbody: string\n\t\t\tstate: string\n\t\t\turl: string\n\t\t\tauthor?: GitHubAuthor\n\t\t\tlabels?: Array<{ name: string; color?: string; description?: string }>\n\t\t\tassignees?: Array<GitHubAuthor>\n\t\t\tmilestone?: { title: string; number?: number; state?: string }\n\t\t\tcomments?: Array<{\n\t\t\t\tid: number\n\t\t\t\tauthor: GitHubAuthor\n\t\t\t\tbody: string\n\t\t\t\tcreatedAt: string\n\t\t\t\tupdatedAt?: string\n\t\t\t\turl: string\n\t\t\t}>\n\t\t}\n\n\t\tconst args = [\n\t\t\t'issue',\n\t\t\t'view',\n\t\t\tString(issueNumber),\n\t\t\t'--json',\n\t\t\tfields,\n\t\t]\n\n\t\t// Add --repo flag if repo is provided (gh CLI handles both owner/repo and URL formats)\n\t\tif (repo) {\n\t\t\targs.push('--repo', repo)\n\t\t}\n\n\t\tconst raw = await executeGhCommand<GitHubIssueResponse>(args)\n\n\t\t// Normalize to IssueResult with core fields + passthrough\n\t\tconst result: IssueResult = {\n\t\t\t// Core fields\n\t\t\tid: String(raw.number),\n\t\t\ttitle: raw.title,\n\t\t\tbody: raw.body,\n\t\t\tstate: raw.state,\n\t\t\turl: raw.url,\n\t\t\tprovider: 'github',\n\n\t\t\t// Normalized author\n\t\t\tauthor: normalizeAuthor(raw.author),\n\n\t\t\t// Optional flexible fields\n\t\t\t...(raw.assignees && {\n\t\t\t\tassignees: raw.assignees.map(a => normalizeAuthor(a)).filter((a): a is FlexibleAuthor => a !== null),\n\t\t\t}),\n\t\t\t...(raw.labels && {\n\t\t\t\tlabels: raw.labels,\n\t\t\t}),\n\n\t\t\t// GitHub-specific passthrough fields\n\t\t\t...(raw.milestone && {\n\t\t\t\tmilestone: raw.milestone,\n\t\t\t}),\n\t\t}\n\n\t\t// Handle comments with normalized authors\n\t\t// Use extractNumericIdFromUrl to get REST API-compatible numeric IDs from comment URLs\n\t\t// (GitHub CLI returns GraphQL node IDs in the id field, but REST API expects numeric IDs)\n\t\tif (raw.comments !== undefined) {\n\t\t\tresult.comments = raw.comments.map(comment => ({\n\t\t\t\tid: extractNumericIdFromUrl(comment.url),\n\t\t\t\tbody: comment.body,\n\t\t\t\tcreatedAt: comment.createdAt,\n\t\t\t\tauthor: normalizeAuthor(comment.author),\n\t\t\t\t...(comment.updatedAt && { updatedAt: comment.updatedAt }),\n\t\t\t}))\n\t\t}\n\n\t\t// Process authenticated images in body and comments\n\t\tresult.body = await processMarkdownImages(result.body, 'github')\n\t\tif (result.comments) {\n\t\t\tfor (const comment of result.comments) {\n\t\t\t\tcomment.body = await processMarkdownImages(comment.body, 'github')\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch pull request details using gh CLI\n\t * Normalizes GitHub-specific fields to provider-agnostic format\n\t */\n\tasync getPR(input: GetPRInput): Promise<PRResult> {\n\t\tconst { number, includeComments = true, repo } = input\n\n\t\t// Convert string ID to number for GitHub CLI\n\t\tconst prNumber = parseInt(number, 10)\n\t\tif (isNaN(prNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub PR number: ${number}. GitHub PR IDs must be numeric.`)\n\t\t}\n\n\t\t// Build fields list based on whether we need comments\n\t\tconst baseFields = 'number,title,body,state,url,author,headRefName,baseRefName,files,commits'\n\t\tconst fields = includeComments\n\t\t\t? `${baseFields},comments`\n\t\t\t: baseFields\n\n\t\t// GitHub PR response structure\n\t\tinterface GitHubPRResponse {\n\t\t\tnumber: number\n\t\t\ttitle: string\n\t\t\tbody: string\n\t\t\tstate: string\n\t\t\turl: string\n\t\t\tauthor?: GitHubAuthor\n\t\t\theadRefName: string\n\t\t\tbaseRefName: string\n\t\t\tfiles?: Array<{\n\t\t\t\tpath: string\n\t\t\t\tadditions: number\n\t\t\t\tdeletions: number\n\t\t\t}>\n\t\t\tcommits?: Array<{\n\t\t\t\toid: string\n\t\t\t\tmessageHeadline: string\n\t\t\t\tauthors: Array<{ name: string; email: string }>\n\t\t\t}>\n\t\t\tcomments?: Array<{\n\t\t\t\tid: number\n\t\t\t\tauthor: GitHubAuthor\n\t\t\t\tbody: string\n\t\t\t\tcreatedAt: string\n\t\t\t\tupdatedAt?: string\n\t\t\t\turl: string\n\t\t\t}>\n\t\t}\n\n\t\tconst args = [\n\t\t\t'pr',\n\t\t\t'view',\n\t\t\tString(prNumber),\n\t\t\t'--json',\n\t\t\tfields,\n\t\t]\n\n\t\t// Add --repo flag if repo is provided\n\t\tif (repo) {\n\t\t\targs.push('--repo', repo)\n\t\t}\n\n\t\tconst raw = await executeGhCommand<GitHubPRResponse>(args)\n\n\t\t// Normalize to PRResult with core fields + passthrough\n\t\tconst result: PRResult = {\n\t\t\t// Core fields\n\t\t\tid: String(raw.number),\n\t\t\tnumber: raw.number,\n\t\t\ttitle: raw.title,\n\t\t\tbody: raw.body,\n\t\t\tstate: raw.state,\n\t\t\turl: raw.url,\n\n\t\t\t// Normalized author\n\t\t\tauthor: normalizeAuthor(raw.author),\n\n\t\t\t// PR-specific fields\n\t\t\theadRefName: raw.headRefName,\n\t\t\tbaseRefName: raw.baseRefName,\n\n\t\t\t// Optional files\n\t\t\t...(raw.files && {\n\t\t\t\tfiles: raw.files,\n\t\t\t}),\n\n\t\t\t// Optional commits - normalize author\n\t\t\t...(raw.commits && {\n\t\t\t\tcommits: raw.commits.map(commit => ({\n\t\t\t\t\toid: commit.oid,\n\t\t\t\t\tmessageHeadline: commit.messageHeadline,\n\t\t\t\t\tauthor: commit.authors?.[0]\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\tid: commit.authors[0].email,\n\t\t\t\t\t\t\tdisplayName: commit.authors[0].name,\n\t\t\t\t\t\t\tname: commit.authors[0].name,\n\t\t\t\t\t\t\temail: commit.authors[0].email,\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: null,\n\t\t\t\t})),\n\t\t\t}),\n\t\t}\n\n\t\t// Handle comments with normalized authors\n\t\t// Use extractNumericIdFromUrl to get REST API-compatible numeric IDs from comment URLs\n\t\tif (raw.comments !== undefined) {\n\t\t\tresult.comments = raw.comments.map(comment => ({\n\t\t\t\tid: extractNumericIdFromUrl(comment.url),\n\t\t\t\tbody: comment.body,\n\t\t\t\tcreatedAt: comment.createdAt,\n\t\t\t\tauthor: normalizeAuthor(comment.author),\n\t\t\t\t...(comment.updatedAt && { updatedAt: comment.updatedAt }),\n\t\t\t}))\n\t\t}\n\n\t\t// Process authenticated images in body and comments\n\t\tresult.body = await processMarkdownImages(result.body, 'github')\n\t\tif (result.comments) {\n\t\t\tfor (const comment of result.comments) {\n\t\t\t\tcomment.body = await processMarkdownImages(comment.body, 'github')\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch a specific comment by ID using gh API\n\t * Normalizes author to FlexibleAuthor format\n\t */\n\tasync getComment(input: GetCommentInput): Promise<CommentDetailResult> {\n\t\tconst { commentId, repo } = input\n\t\t// Note: GitHub doesn't need the issue number parameter - comment IDs are globally unique\n\t\t// But we accept it for interface compatibility with other providers\n\n\t\t// Convert string ID to number for GitHub API\n\t\tconst numericCommentId = parseInt(commentId, 10)\n\t\tif (isNaN(numericCommentId)) {\n\t\t\tthrow new Error(`Invalid GitHub comment ID: ${commentId}. GitHub comment IDs must be numeric.`)\n\t\t}\n\n\t\t// GitHub API response structure\n\t\tinterface GitHubCommentResponse {\n\t\t\tid: number\n\t\t\tbody: string\n\t\t\tuser: GitHubAuthor\n\t\t\tcreated_at: string\n\t\t\tupdated_at?: string\n\t\t\thtml_url?: string\n\t\t\treactions?: Record<string, unknown>\n\t\t}\n\n\t\t// Use explicit repo path if provided, otherwise use :owner/:repo placeholder\n\t\tconst apiPath = repo\n\t\t\t? `repos/${repo}/issues/comments/${numericCommentId}`\n\t\t\t: `repos/:owner/:repo/issues/comments/${numericCommentId}`\n\n\t\t// Use gh api to fetch specific comment\n\t\tconst raw = await executeGhCommand<GitHubCommentResponse>([\n\t\t\t'api',\n\t\t\tapiPath,\n\t\t\t'--jq',\n\t\t\t'{id: .id, body: .body, user: .user, created_at: .created_at, updated_at: .updated_at, html_url: .html_url, reactions: .reactions}',\n\t\t])\n\n\t\t// Process authenticated images in comment body\n\t\tconst processedBody = await processMarkdownImages(raw.body, 'github')\n\n\t\t// Normalize to CommentDetailResult\n\t\treturn {\n\t\t\tid: String(raw.id),\n\t\t\tbody: processedBody,\n\t\t\tauthor: normalizeAuthor(raw.user),\n\t\t\tcreated_at: raw.created_at,\n\t\t\t...(raw.updated_at && { updated_at: raw.updated_at }),\n\t\t\t// Passthrough GitHub-specific fields\n\t\t\t...(raw.html_url && { html_url: raw.html_url }),\n\t\t\t...(raw.reactions && { reactions: raw.reactions }),\n\t\t}\n\t}\n\n\t/**\n\t * Create a new comment on an issue or PR\n\t */\n\tasync createComment(input: CreateCommentInput): Promise<CommentResult> {\n\t\tconst { number, body, type } = input\n\n\t\t// Convert string ID to number for GitHub utilities\n\t\tconst numericId = parseInt(number, 10)\n\t\tif (isNaN(numericId)) {\n\t\t\tthrow new Error(`Invalid GitHub ${type} number: ${number}. GitHub IDs must be numeric.`)\n\t\t}\n\n\t\t// Delegate to existing GitHub utilities\n\t\tconst result =\n\t\t\ttype === 'issue'\n\t\t\t\t? await createIssueComment(numericId, body)\n\t\t\t\t: await createPRComment(numericId, body)\n\n\t\t// Convert numeric ID to string for the interface\n\t\treturn {\n\t\t\t...result,\n\t\t\tid: String(result.id),\n\t\t}\n\t}\n\n\t/**\n\t * Update an existing comment\n\t */\n\tasync updateComment(input: UpdateCommentInput): Promise<CommentResult> {\n\t\tconst { commentId, body } = input\n\t\t// Note: GitHub doesn't need the issue number parameter - comment IDs are globally unique\n\t\t// But we accept it for interface compatibility with other providers\n\n\t\t// Convert string ID to number for GitHub utility\n\t\tconst numericCommentId = parseInt(commentId, 10)\n\t\tif (isNaN(numericCommentId)) {\n\t\t\tthrow new Error(`Invalid GitHub comment ID: ${commentId}. GitHub comment IDs must be numeric.`)\n\t\t}\n\n\t\t// Delegate to existing GitHub utility\n\t\tconst result = await updateIssueComment(numericCommentId, body)\n\n\t\t// Convert numeric ID to string for the interface\n\t\treturn {\n\t\t\t...result,\n\t\t\tid: String(result.id),\n\t\t}\n\t}\n\n\t/**\n\t * Create a new issue\n\t */\n\tasync createIssue(input: CreateIssueInput): Promise<CreateIssueResult> {\n\t\tconst { title, body, labels, repo } = input\n\t\t// teamKey is ignored for GitHub\n\n\t\tconst result = await createIssue(title, body, { labels, repo })\n\n\t\t// Ensure number is numeric\n\t\tconst issueNumber = typeof result.number === 'number'\n\t\t\t? result.number\n\t\t\t: parseInt(String(result.number), 10)\n\n\t\treturn {\n\t\t\tid: String(issueNumber),\n\t\t\turl: result.url,\n\t\t\tnumber: issueNumber,\n\t\t}\n\t}\n\n\t/**\n\t * Create a child issue linked to a parent issue\n\t * GitHub requires two-step process: create issue, then link via GraphQL\n\t */\n\tasync createChildIssue(input: CreateChildIssueInput): Promise<CreateIssueResult> {\n\t\tconst { parentId, title, body, labels, repo } = input\n\t\t// teamKey is ignored for GitHub\n\n\t\t// Convert parent identifier to number\n\t\tconst parentNumber = parseInt(parentId, 10)\n\t\tif (isNaN(parentNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub parent issue number: ${parentId}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Step 1: Get parent issue's GraphQL node ID\n\t\tconst parentNodeId = await getIssueNodeId(parentNumber, repo)\n\n\t\t// Step 2: Create the child issue\n\t\tconst childResult = await createIssue(title, body, { labels, repo })\n\t\tconst childNumber = typeof childResult.number === 'number'\n\t\t\t? childResult.number\n\t\t\t: parseInt(String(childResult.number), 10)\n\n\t\t// Step 3: Get child issue's GraphQL node ID\n\t\tconst childNodeId = await getIssueNodeId(childNumber, repo)\n\n\t\t// Step 4: Link child to parent via GraphQL mutation\n\t\tawait addSubIssue(parentNodeId, childNodeId)\n\n\t\treturn {\n\t\t\tid: String(childNumber),\n\t\t\turl: childResult.url,\n\t\t\tnumber: childNumber,\n\t\t}\n\t}\n\n\t/**\n\t * Create a blocking dependency between two issues (A blocks B)\n\t * Uses GitHub's sub-issues API: blocking issue becomes parent, blocked issue becomes sub-issue\n\t */\n\tasync createDependency(input: CreateDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue, repo } = input\n\n\t\t// Convert string IDs to numbers\n\t\tconst blockingNumber = parseInt(blockingIssue, 10)\n\t\tif (isNaN(blockingNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockingIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tconst blockedNumber = parseInt(blockedIssue, 10)\n\t\tif (isNaN(blockedNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockedIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Get the database ID of the blocking issue\n\t\t// GitHub API: POST /issues/{blocked_issue_number}/dependencies/blocked_by with body issue_id={blocking_database_id}\n\t\tconst blockingDatabaseId = await getIssueDatabaseId(blockingNumber, repo)\n\n\t\t// Create the dependency: path uses blocked issue number, body uses blocking issue DB ID\n\t\tawait createIssueDependency(blockedNumber, blockingDatabaseId, repo)\n\t}\n\n\t/**\n\t * Get dependencies for an issue\n\t */\n\tasync getDependencies(input: GetDependenciesInput): Promise<DependenciesResult> {\n\t\tconst { number, direction, repo } = input\n\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tconst result: DependenciesResult = {\n\t\t\tblocking: [],\n\t\t\tblockedBy: [],\n\t\t}\n\n\t\t// Fetch dependencies based on direction\n\t\tif (direction === 'blocking' || direction === 'both') {\n\t\t\tresult.blocking = await getIssueDependencies(issueNumber, 'blocking', repo)\n\t\t}\n\n\t\tif (direction === 'blocked_by' || direction === 'both') {\n\t\t\tresult.blockedBy = await getIssueDependencies(issueNumber, 'blocked_by', repo)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Remove a blocking dependency between two issues (A blocks B)\n\t * Uses GitHub's sub-issues API: blocking issue is parent, blocked issue is sub-issue\n\t */\n\tasync removeDependency(input: RemoveDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue, repo } = input\n\n\t\t// Convert string IDs to numbers\n\t\tconst blockingNumber = parseInt(blockingIssue, 10)\n\t\tif (isNaN(blockingNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockingIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tconst blockedNumber = parseInt(blockedIssue, 10)\n\t\tif (isNaN(blockedNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockedIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Get the database ID of the blocking issue\n\t\t// GitHub API: DELETE /issues/{blocked_issue_number}/dependencies/blocked_by with body issue_id={blocking_database_id}\n\t\tconst blockingDatabaseId = await getIssueDatabaseId(blockingNumber, repo)\n\n\t\t// Remove the dependency: path uses blocked issue number, body uses blocking issue DB ID\n\t\tawait removeIssueDependency(blockedNumber, blockingDatabaseId, repo)\n\t}\n\n\t/**\n\t * Get child issues (sub-issues) of a parent issue\n\t */\n\tasync getChildIssues(input: GetChildIssuesInput): Promise<ChildIssueResult[]> {\n\t\tconst { number, repo } = input\n\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\treturn await getSubIssues(issueNumber, repo)\n\t}\n}\n","/**\n * Linear SDK utilities\n * Wrapper functions for the @linear/sdk\n */\n\nimport { LinearClient, IssueRelationType } from '@linear/sdk'\nimport type { LinearIssue, LinearComment } from '../types/linear.js'\nimport { LinearServiceError } from '../types/linear.js'\nimport { logger } from './logger.js'\n\n/**\n * Slugify a title for use in Linear URLs\n * Converts to lowercase, replaces non-alphanumeric with hyphens, truncates to reasonable length\n * @param title - Issue title\n * @param maxLength - Maximum slug length (default: 50)\n * @returns Slugified title\n */\nexport function slugifyTitle(title: string, maxLength: number = 50): string {\n // Convert to lowercase, replace non-alphanumeric chars with hyphens\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') // trim leading/trailing hyphens\n\n // If already short enough, return as-is\n if (slug.length <= maxLength) {\n return slug\n }\n\n // Split by hyphens and rebuild until we hit the limit\n const parts = slug.split('-')\n let result = ''\n for (const part of parts) {\n const candidate = result ? `${result}-${part}` : part\n if (candidate.length > maxLength) {\n break\n }\n result = candidate\n }\n\n return result || slug.slice(0, maxLength) // fallback if first part is too long\n}\n\n/**\n * Build a Linear issue URL with optional title slug\n * @param identifier - Issue identifier (e.g., \"ENG-123\")\n * @param title - Optional issue title for slug\n * @returns Linear URL\n */\nexport function buildLinearIssueUrl(identifier: string, title?: string): string {\n const base = `https://linear.app/issue/${identifier}`\n if (title) {\n const slug = slugifyTitle(title)\n return slug ? `${base}/${slug}` : base\n }\n return base\n}\n\n/**\n * Get Linear API token from environment\n * @returns API token\n * @throws LinearServiceError if token not found\n */\nfunction getLinearApiToken(): string {\n const token = process.env.LINEAR_API_TOKEN\n if (!token) {\n throw new LinearServiceError(\n 'UNAUTHORIZED',\n 'LINEAR_API_TOKEN not set. Configure in settings.local.json or set environment variable.',\n )\n }\n return token\n}\n\n/**\n * Create a Linear SDK client instance\n * @param apiToken - Optional API token (takes precedence over env var)\n * @returns Configured LinearClient\n */\nfunction createLinearClient(apiToken?: string): LinearClient {\n const token = apiToken ?? getLinearApiToken()\n return new LinearClient({ apiKey: token })\n}\n\n/**\n * Handle SDK errors and convert to LinearServiceError\n * @param error - Error from SDK\n * @param context - Context string for debugging\n * @throws LinearServiceError\n */\nfunction handleLinearError(error: unknown, context: string): never {\n logger.debug(`${context}: Handling error`, { error })\n\n // SDK errors typically have a message property\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Map common error patterns\n if (errorMessage.includes('not found') || errorMessage.includes('Not found')) {\n throw new LinearServiceError('NOT_FOUND', 'Linear issue or resource not found', { error })\n }\n\n if (\n errorMessage.includes('unauthorized') ||\n errorMessage.includes('Unauthorized') ||\n errorMessage.includes('Invalid API key')\n ) {\n throw new LinearServiceError(\n 'UNAUTHORIZED',\n 'Linear authentication failed. Check LINEAR_API_TOKEN.',\n { error },\n )\n }\n\n if (errorMessage.includes('rate limit')) {\n throw new LinearServiceError('RATE_LIMITED', 'Linear API rate limit exceeded', { error })\n }\n\n // Generic SDK error\n throw new LinearServiceError('CLI_ERROR', `Linear SDK error: ${errorMessage}`, { error })\n}\n\n/**\n * Fetch a Linear issue by identifier\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @returns Linear issue details\n * @throws LinearServiceError if issue not found or SDK error\n */\nexport async function fetchLinearIssue(identifier: string): Promise<LinearIssue> {\n try {\n logger.debug(`Fetching Linear issue: ${identifier}`)\n const client = createLinearClient()\n const issue = await client.issue(identifier)\n\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Convert SDK issue to our LinearIssue type\n const result: LinearIssue = {\n id: issue.id,\n identifier: issue.identifier,\n title: issue.title,\n url: issue.url,\n createdAt: issue.createdAt.toISOString(),\n updatedAt: issue.updatedAt.toISOString(),\n }\n\n // Add optional fields if present\n if (issue.description) {\n result.description = issue.description\n }\n\n if (issue.state) {\n const state = await issue.state\n if (state?.name) {\n result.state = state.name\n }\n }\n\n return result\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssue')\n }\n}\n\n/**\n * Create a new Linear issue\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param teamKey - Team key (e.g., \"ENG\", \"PLAT\")\n * @param labels - Optional label names to apply\n * @returns Created issue identifier and URL\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearIssue(\n title: string,\n body: string,\n teamKey: string,\n _labels?: string[],\n): Promise<{ identifier: string; url: string }> {\n try {\n logger.debug(`Creating Linear issue in team ${teamKey}: ${title}`)\n const client = createLinearClient()\n\n // Get team by key\n const teams = await client.teams()\n const team = teams.nodes.find((t) => t.key === teamKey)\n\n if (!team) {\n throw new LinearServiceError('NOT_FOUND', `Linear team ${teamKey} not found`)\n }\n\n // Create issue\n const issueInput: { teamId: string; title: string; description?: string } = {\n teamId: team.id,\n title,\n }\n\n if (body) {\n issueInput.description = body\n }\n\n const payload = await client.createIssue(issueInput)\n\n const issue = await payload.issue\n\n if (!issue) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear issue')\n }\n\n // Construct URL\n const url = issue.url ?? buildLinearIssueUrl(issue.identifier, title)\n\n return {\n identifier: issue.identifier,\n url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearIssue')\n }\n}\n\n/**\n * Create a child issue linked to a parent issue\n * Linear supports atomic creation with parentId field\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param teamKey - Team key (e.g., \"ENG\")\n * @param parentId - Parent issue UUID (from issue.id, not identifier)\n * @param labels - Optional label names to apply\n * @returns Created issue identifier and URL\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearChildIssue(\n title: string,\n body: string,\n teamKey: string,\n parentId: string,\n _labels?: string[],\n): Promise<{ identifier: string; url: string }> {\n try {\n logger.debug(`Creating Linear child issue in team ${teamKey}: ${title}`)\n const client = createLinearClient()\n\n // Get team by key\n const teams = await client.teams()\n const team = teams.nodes.find((t) => t.key === teamKey)\n\n if (!team) {\n throw new LinearServiceError('NOT_FOUND', `Linear team ${teamKey} not found`)\n }\n\n // Create issue with parentId for atomic parent-child relationship\n const issueInput: { teamId: string; title: string; description?: string; parentId: string } = {\n teamId: team.id,\n title,\n parentId, // UUID of parent issue\n }\n\n if (body) {\n issueInput.description = body\n }\n\n const payload = await client.createIssue(issueInput)\n\n const issue = await payload.issue\n\n if (!issue) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear child issue')\n }\n\n // Construct URL\n const url = issue.url ?? buildLinearIssueUrl(issue.identifier, title)\n\n return {\n identifier: issue.identifier,\n url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearChildIssue')\n }\n}\n\n/**\n * Create a comment on a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param body - Comment body (markdown)\n * @returns Created comment details\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearComment(\n identifier: string,\n body: string,\n): Promise<LinearComment> {\n try {\n logger.debug(`Creating comment on Linear issue ${identifier}`)\n const client = createLinearClient()\n\n // Get issue by identifier to get its ID\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Create comment using issue ID\n const payload = await client.createComment({\n issueId: issue.id,\n body,\n })\n\n const comment = await payload.comment\n\n if (!comment) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear comment')\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearComment')\n }\n}\n\n/**\n * Update a Linear issue's workflow state\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param stateName - Target state name (e.g., \"In Progress\", \"Done\")\n * @throws LinearServiceError on update failure\n */\nexport async function updateLinearIssueState(\n identifier: string,\n stateName: string,\n): Promise<void> {\n try {\n logger.debug(`Updating Linear issue ${identifier} state to: ${stateName}`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Get team to find state\n const team = await issue.team\n if (!team) {\n throw new LinearServiceError('CLI_ERROR', 'Issue has no team')\n }\n\n // Find state by name\n const states = await team.states()\n const state = states.nodes.find((s) => s.name === stateName)\n\n if (!state) {\n throw new LinearServiceError(\n 'NOT_FOUND',\n `State \"${stateName}\" not found in team ${team.key}`,\n )\n }\n\n // Update issue state\n await client.updateIssue(issue.id, {\n stateId: state.id,\n })\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'updateLinearIssueState')\n }\n}\n\n/**\n * Get a specific comment by ID\n * @param commentId - Linear comment UUID\n * @returns Comment details\n * @throws LinearServiceError if comment not found\n */\nexport async function getLinearComment(commentId: string): Promise<LinearComment> {\n try {\n logger.debug(`Fetching Linear comment: ${commentId}`)\n const client = createLinearClient()\n const comment = await client.comment({ id: commentId })\n\n if (!comment) {\n throw new LinearServiceError('NOT_FOUND', `Linear comment ${commentId} not found`)\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'getLinearComment')\n }\n}\n\n/**\n * Update an existing comment\n * @param commentId - Linear comment UUID\n * @param body - New comment body (markdown)\n * @returns Updated comment details\n * @throws LinearServiceError on update failure\n */\nexport async function updateLinearComment(\n commentId: string,\n body: string,\n): Promise<LinearComment> {\n try {\n logger.debug(`Updating Linear comment: ${commentId}`)\n const client = createLinearClient()\n\n const payload = await client.updateComment(commentId, { body })\n const comment = await payload.comment\n\n if (!comment) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to update Linear comment')\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'updateLinearComment')\n }\n}\n\n/**\n * Fetch all comments for a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @returns Array of comments\n * @throws LinearServiceError on fetch failure\n */\nexport async function fetchLinearIssueComments(identifier: string): Promise<LinearComment[]> {\n try {\n logger.debug(`Fetching comments for Linear issue: ${identifier}`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Fetch comments\n const comments = await issue.comments({ first: 100 })\n\n return comments.nodes.map((comment) => ({\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }))\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssueComments')\n }\n}\n\n/**\n * Get child issues of a parent Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param options - Optional settings\n * @param options.apiToken - Optional API token (takes precedence over env var)\n * @returns Array of child issues\n * @throws LinearServiceError on fetch failure\n */\nexport async function getLinearChildIssues(\n identifier: string,\n options?: { apiToken?: string },\n): Promise<Array<{ id: string; title: string; url: string; state: string }>> {\n try {\n logger.debug(`Fetching child issues for Linear issue: ${identifier}`)\n const client = createLinearClient(options?.apiToken)\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Fetch child issues\n const children = await issue.children({ first: 100 })\n\n // Build results, fetching state in parallel\n const results = await Promise.all(\n children.nodes.map(async (child) => {\n const stateObj = await child.state\n const state = stateObj?.name ?? 'unknown'\n\n return {\n id: child.identifier,\n title: child.title,\n url: child.url,\n state,\n }\n }),\n )\n\n return results\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'getLinearChildIssues')\n }\n}\n\n// Linear Issue Dependency Operations\n\n/**\n * Dependency result for Linear issues\n */\nexport interface LinearDependencyResult {\n id: string\n title: string\n url: string\n state: string\n}\n\n/**\n * Create a blocking relationship between two issues\n * @param blockingIssueId - UUID of the issue that blocks (the blocker)\n * @param blockedIssueId - UUID of the issue being blocked\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearIssueRelation(\n blockingIssueId: string,\n blockedIssueId: string,\n): Promise<void> {\n try {\n logger.debug(`Creating Linear issue relation: ${blockingIssueId} blocks ${blockedIssueId}`)\n const client = createLinearClient()\n\n // Create a \"blocks\" relation from blockingIssue to blockedIssue\n // In Linear, the relation is created on the blocking issue pointing to the blocked issue\n const payload = await client.createIssueRelation({\n issueId: blockingIssueId,\n relatedIssueId: blockedIssueId,\n type: IssueRelationType.Blocks,\n })\n\n if (!payload.success) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear issue relation')\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearIssueRelation')\n }\n}\n\n/**\n * Get dependencies for a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param direction - 'blocking' for issues this blocks, 'blocked_by' for blockers, 'both' for all\n * @returns Object with blocking and blockedBy arrays\n * @throws LinearServiceError on fetch failure\n */\nexport async function getLinearIssueDependencies(\n identifier: string,\n direction: 'blocking' | 'blocked_by' | 'both',\n): Promise<{ blocking: LinearDependencyResult[]; blockedBy: LinearDependencyResult[] }> {\n try {\n logger.debug(`Fetching Linear issue dependencies: ${identifier} (direction: ${direction})`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Fetch relations and inverse relations in parallel\n const [relations, inverseRelations] = await Promise.all([\n issue.relations(),\n issue.inverseRelations(),\n ])\n\n const blocking: LinearDependencyResult[] = []\n const blockedBy: LinearDependencyResult[] = []\n\n // Helper to build dependency result from a resolved issue\n const buildDependencyResult = async (\n relatedIssue: { identifier: string; title: string; url: string; state: unknown } | null | undefined,\n ): Promise<LinearDependencyResult | null> => {\n if (!relatedIssue) return null\n\n const stateObj = await (relatedIssue.state as Promise<{ name?: string } | null | undefined>)\n const state = stateObj?.name ?? 'unknown'\n\n return {\n id: relatedIssue.identifier,\n title: relatedIssue.title,\n url: relatedIssue.url,\n state,\n }\n }\n\n // Process blocking relations (this issue blocks the related issue)\n // relations with type 'blocks' mean this issue blocks the related issue\n if (direction === 'blocking' || direction === 'both') {\n const blockingRelations = relations.nodes.filter(\n (r) => r.type === IssueRelationType.Blocks,\n )\n // Resolve all related issues in parallel, then build results\n // Filter out undefined relatedIssue before Promise.all (LinearFetch<Issue> | undefined)\n const relatedIssuePromises = blockingRelations\n .map((r) => r.relatedIssue)\n .filter((p): p is NonNullable<typeof p> => p !== undefined)\n const relatedIssues = await Promise.all(relatedIssuePromises)\n const blockingResults = await Promise.all(\n relatedIssues.map((issue) => buildDependencyResult(issue)),\n )\n blocking.push(...blockingResults.filter((r): r is LinearDependencyResult => r !== null))\n }\n\n // Process blocked by relations (the related issue blocks this issue)\n // inverseRelations with type 'blocks' mean the related issue blocks this issue\n if (direction === 'blocked_by' || direction === 'both') {\n const blockedByRelations = inverseRelations.nodes.filter(\n (r) => r.type === IssueRelationType.Blocks,\n )\n // Resolve all source issues in parallel, then build results\n // Filter out undefined issue before Promise.all (LinearFetch<Issue> | undefined)\n const sourceIssuePromises = blockedByRelations\n .map((r) => r.issue)\n .filter((p): p is NonNullable<typeof p> => p !== undefined)\n const sourceIssues = await Promise.all(sourceIssuePromises)\n const blockedByResults = await Promise.all(\n sourceIssues.map((issue) => buildDependencyResult(issue)),\n )\n blockedBy.push(...blockedByResults.filter((r): r is LinearDependencyResult => r !== null))\n }\n\n return { blocking, blockedBy }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'getLinearIssueDependencies')\n }\n}\n\n/**\n * Delete an issue relation by ID\n * @param relationId - UUID of the relation to delete\n * @throws LinearServiceError on deletion failure\n */\nexport async function deleteLinearIssueRelation(relationId: string): Promise<void> {\n try {\n logger.debug(`Deleting Linear issue relation: ${relationId}`)\n const client = createLinearClient()\n\n const payload = await client.deleteIssueRelation(relationId)\n\n if (!payload.success) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to delete Linear issue relation')\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'deleteLinearIssueRelation')\n }\n}\n\n// Issue List Operations (for il issues command)\n\nexport interface LinearIssueListItem {\n id: string\n title: string\n updatedAt: string\n url: string\n state: string\n}\n\n/**\n * Fetch a list of active Linear issues for a team, sorted by recently updated\n * @param teamKey - Team key (e.g., \"ENG\", \"PLAT\")\n * @param options - Fetch options\n * @param options.limit - Maximum number of issues to return (default: 100)\n * @param options.apiToken - Optional API token (takes precedence over env var)\n * @returns Array of issues\n * @throws LinearServiceError on fetch failure\n */\nexport async function fetchLinearIssueList(\n teamKey: string,\n options?: { limit?: number; apiToken?: string },\n): Promise<LinearIssueListItem[]> {\n try {\n const limit = options?.limit ?? 100\n\n logger.debug(`Fetching Linear issue list for team ${teamKey}`, { limit })\n const client = createLinearClient(options?.apiToken)\n\n // Get team by key\n const teams = await client.teams()\n const team = teams.nodes.find((t) => t.key === teamKey)\n\n if (!team) {\n throw new LinearServiceError('NOT_FOUND', `Linear team ${teamKey} not found`)\n }\n\n // Fetch issues: filter out completed and canceled states, order by updatedAt\n const issues = await team.issues({\n first: limit,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- PaginationOrderBy is a const enum incompatible with isolatedModules\n orderBy: 'updatedAt' as any,\n filter: {\n state: {\n type: {\n nin: ['completed', 'canceled'],\n },\n },\n },\n })\n\n // Build results, resolving state names in parallel\n const results = await Promise.all(\n issues.nodes.map(async (issue) => {\n const stateObj = await issue.state\n const state = stateObj?.name ?? 'unknown'\n\n return {\n id: issue.identifier,\n title: issue.title,\n updatedAt: issue.updatedAt.toISOString(),\n url: issue.url,\n state,\n }\n }),\n )\n\n return results\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssueList')\n }\n}\n\n/**\n * Find the relation ID between two issues for removal\n * @param blockingIdentifier - Identifier of the blocking issue\n * @param blockedIdentifier - Identifier of the blocked issue\n * @returns The relation ID if found, null otherwise\n * @throws LinearServiceError on fetch failure\n */\nexport async function findLinearIssueRelation(\n blockingIdentifier: string,\n blockedIdentifier: string,\n): Promise<string | null> {\n try {\n logger.debug(`Finding Linear issue relation: ${blockingIdentifier} blocks ${blockedIdentifier}`)\n const client = createLinearClient()\n\n // Get the blocking issue\n const blockingIssue = await client.issue(blockingIdentifier)\n if (!blockingIssue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${blockingIdentifier} not found`)\n }\n\n // Get the blocked issue to get its ID\n const blockedIssue = await client.issue(blockedIdentifier)\n if (!blockedIssue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${blockedIdentifier} not found`)\n }\n\n // Find the relation from blockingIssue that blocks blockedIssue\n const relations = await blockingIssue.relations()\n\n // Filter to only 'blocks' relations and fetch related issues in parallel\n const blockingRelations = relations.nodes.filter(\n (r) => r.type === IssueRelationType.Blocks,\n )\n\n const relationsWithIssues = await Promise.all(\n blockingRelations.map(async (relation) => ({\n relation,\n relatedIssue: await relation.relatedIssue,\n })),\n )\n\n const matchingRelation = relationsWithIssues.find(\n ({ relatedIssue }) => relatedIssue?.id === blockedIssue.id,\n )\n\n return matchingRelation?.relation.id ?? null\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'findLinearIssueRelation')\n }\n}\n","/**\n * Linear API response types (from @linear/sdk)\n */\n\n/**\n * Linear issue response from SDK\n */\nexport interface LinearIssue {\n /** Linear internal UUID */\n id: string\n /** Issue identifier in TEAM-NUMBER format (e.g., ENG-123) */\n identifier: string\n /** Issue title */\n title: string\n /** Issue description (markdown) */\n description?: string\n /** Current workflow state name */\n state?: string\n /** Linear web URL */\n url: string\n /** Creation timestamp (ISO string) */\n createdAt: string\n /** Last update timestamp (ISO string) */\n updatedAt: string\n}\n\n/**\n * Linear comment response from SDK\n */\nexport interface LinearComment {\n /** Comment UUID */\n id: string\n /** Comment body (markdown) */\n body: string\n /** Creation timestamp (ISO string) */\n createdAt: string\n /** Last update timestamp (ISO string) */\n updatedAt: string\n /** Comment URL */\n url: string\n}\n\n/**\n * Linear error codes\n */\nexport type LinearErrorCode =\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'INVALID_STATE'\n | 'RATE_LIMITED'\n | 'CLI_NOT_FOUND'\n | 'CLI_ERROR'\n\n/**\n * Linear error details\n */\nexport interface LinearError {\n code: LinearErrorCode\n message: string\n details?: unknown\n}\n\n/**\n * Custom error class for Linear operations\n */\nexport class LinearServiceError extends Error {\n constructor(\n public code: LinearErrorCode,\n message: string,\n public details?: unknown,\n ) {\n super(message)\n this.name = 'LinearServiceError'\n // Maintain proper stack trace for where error was thrown (V8 only)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, LinearServiceError)\n }\n }\n}\n","import { appendFileSync } from 'node:fs'\nimport { join, dirname, basename, extname } from 'node:path'\n\n/**\n * Utility class for converting HTML details/summary format to Linear's collapsible format\n *\n * Converts:\n * <details>\n * <summary>Header</summary>\n * CONTENT\n * </details>\n *\n * Into Linear format:\n * +++ Header\n *\n * CONTENT\n *\n * +++\n */\nexport class LinearMarkupConverter {\n\t/**\n\t * Convert HTML details/summary blocks to Linear's collapsible format\n\t * Handles nested details blocks recursively\n\t *\n\t * @param text - Text containing HTML details/summary blocks\n\t * @returns Text with details/summary converted to Linear format\n\t */\n\tstatic convertDetailsToLinear(text: string): string {\n\t\tif (!text) {\n\t\t\treturn text\n\t\t}\n\n\t\t// Process from innermost to outermost to handle nesting correctly\n\t\t// Keep converting until no more details blocks are found\n\t\tlet previousText = ''\n\t\tlet currentText = text\n\n\t\twhile (previousText !== currentText) {\n\t\t\tpreviousText = currentText\n\t\t\tcurrentText = this.convertSinglePass(currentText)\n\t\t}\n\n\t\treturn currentText\n\t}\n\n\t/**\n\t * Perform a single pass of details block conversion\n\t * Converts the innermost details blocks first\n\t */\n\tprivate static convertSinglePass(text: string): string {\n\t\t// Match <details> blocks with optional attributes on the details tag\n\t\t// Supports multiline content between tags\n\t\tconst detailsRegex = /<details[^>]*>\\s*<summary[^>]*>(.*?)<\\/summary>\\s*(.*?)\\s*<\\/details>/gis\n\n\t\treturn text.replace(detailsRegex, (_match, summary, content) => {\n\t\t\t// Clean up the summary - trim whitespace and decode HTML entities\n\t\t\tconst cleanSummary = this.cleanText(summary)\n\n\t\t\t// Clean up the content - preserve internal structure but normalize outer whitespace\n\t\t\t// Note: Don't recursively convert here - the while loop handles that\n\t\t\tconst cleanContent = this.cleanContent(content)\n\n\t\t\t// Build Linear collapsible format\n\t\t\t// Always include blank lines around content for readability\n\t\t\tif (cleanContent) {\n\t\t\t\treturn `+++ ${cleanSummary}\\n\\n${cleanContent}\\n\\n+++`\n\t\t\t} else {\n\t\t\t\t// Empty content - use minimal format\n\t\t\t\treturn `+++ ${cleanSummary}\\n\\n+++`\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Clean text by trimming whitespace and decoding common HTML entities\n\t */\n\tprivate static cleanText(text: string): string {\n\t\treturn text\n\t\t\t.trim()\n\t\t\t.replace(/&lt;/g, '<')\n\t\t\t.replace(/&gt;/g, '>')\n\t\t\t.replace(/&amp;/g, '&')\n\t\t\t.replace(/&quot;/g, '\"')\n\t\t\t.replace(/&#39;/g, \"'\")\n\t}\n\n\t/**\n\t * Clean content while preserving internal structure\n\t * - Removes leading/trailing whitespace\n\t * - Normalizes internal blank lines (max 2 consecutive newlines)\n\t * - Preserves code blocks and other formatting\n\t */\n\tprivate static cleanContent(content: string): string {\n\t\tif (!content) {\n\t\t\treturn ''\n\t\t}\n\n\t\t// Trim outer whitespace\n\t\tlet cleaned = content.trim()\n\n\t\t// Normalize excessive blank lines (3+ newlines -> 2 newlines)\n\t\tcleaned = cleaned.replace(/\\n{3,}/g, '\\n\\n')\n\n\t\treturn cleaned\n\t}\n\n\t/**\n\t * Check if text contains HTML details/summary blocks\n\t * Useful for conditional conversion\n\t */\n\tstatic hasDetailsBlocks(text: string): boolean {\n\t\tif (!text) {\n\t\t\treturn false\n\t\t}\n\n\t\tconst detailsRegex = /<details[^>]*>.*?<summary[^>]*>.*?<\\/summary>.*?<\\/details>/is\n\t\treturn detailsRegex.test(text)\n\t}\n\n\t/**\n\t * Remove wrapper tags from code sample details blocks\n\t * Identifies details blocks where summary contains \"X lines\" pattern\n\t * and removes the details/summary tags while preserving the content\n\t *\n\t * @param text - Text containing potential code sample details blocks\n\t * @returns Text with code sample wrappers removed\n\t */\n\tstatic removeCodeSampleWrappers(text: string): string {\n\t\tif (!text) {\n\t\t\treturn text\n\t\t}\n\n\t\t// Match details blocks where summary contains \"X lines\" (e.g., \"45 lines\", \"120 lines\")\n\t\t// Pattern: <details><summary>...N lines...</summary>CONTENT</details>\n\t\t// Use [^<]* to match summary content without allowing nested tags to interfere\n\t\t// Then use [\\s\\S]*? for the content to allow any characters including newlines\n\t\tconst codeSampleRegex = /<details[^>]*>\\s*<summary[^>]*>([^<]*\\d+\\s+lines[^<]*)<\\/summary>\\s*([\\s\\S]*?)<\\/details>/gi\n\n\t\treturn text.replace(codeSampleRegex, (_match, _summary, content) => {\n\t\t\t// Return just the content, without any wrapper tags\n\t\t\t// Preserve the content exactly as-is\n\t\t\treturn content.trim()\n\t\t})\n\t}\n\n\t/**\n\t * Convert text for Linear - applies all necessary conversions\n\t * Currently only converts details/summary blocks, but can be extended\n\t * for other HTML to Linear markdown conversions\n\t */\n\tstatic convertToLinear(text: string): string {\n\t\tif (!text) {\n\t\t\treturn text\n\t\t}\n\n\t\t// Log input if logging is enabled\n\t\tthis.logConversion('INPUT', text)\n\n\t\t// Apply all conversions\n\t\tlet converted = text\n\n\t\t// First, remove code sample wrappers (details blocks with \"X lines\" pattern)\n\t\t// This prevents them from being converted to Linear's +++ format\n\t\tconverted = this.removeCodeSampleWrappers(converted)\n\n\t\t// Then convert remaining details/summary blocks to Linear format\n\t\tconverted = this.convertDetailsToLinear(converted)\n\n\t\t// Log output if logging is enabled\n\t\tthis.logConversion('OUTPUT', converted)\n\n\t\treturn converted\n\t}\n\n\t/**\n\t * Log conversion input/output if LINEAR_MARKDOWN_LOG_FILE is set\n\t */\n\tprivate static logConversion(label: string, content: string): void {\n\t\tconst logFilePath = process.env.LINEAR_MARKDOWN_LOG_FILE\n\t\tif (!logFilePath) {\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst timestampedPath = this.getTimestampedLogPath(logFilePath)\n\t\t\tconst timestamp = new Date().toISOString()\n\t\t\tconst separator = '================================'\n\n\t\t\tconst logEntry = `${separator}\\n[${timestamp}] CONVERSION ${label}\\n${separator}\\n${label}:\\n${content}\\n\\n`\n\n\t\t\tappendFileSync(timestampedPath, logEntry, 'utf-8')\n\t\t} catch {\n\t\t\t// Silently fail - don't crash if logging fails\n\t\t\t// This is a debug feature and shouldn't break the conversion\n\t\t}\n\t}\n\n\t/**\n\t * Generate timestamped log file path\n\t * Example: debug.log -> debug-20231202-161234.log\n\t */\n\tprivate static getTimestampedLogPath(logFilePath: string): string {\n\t\tconst dir = dirname(logFilePath)\n\t\tconst ext = extname(logFilePath)\n\t\tconst base = basename(logFilePath, ext)\n\n\t\t// Generate timestamp: YYYYMMDD-HHMMSS\n\t\tconst now = new Date()\n\t\tconst timestamp = [\n\t\t\tnow.getFullYear(),\n\t\t\tString(now.getMonth() + 1).padStart(2, '0'),\n\t\t\tString(now.getDate()).padStart(2, '0'),\n\t\t].join('') + '-' + [\n\t\t\tString(now.getHours()).padStart(2, '0'),\n\t\t\tString(now.getMinutes()).padStart(2, '0'),\n\t\t\tString(now.getSeconds()).padStart(2, '0'),\n\t\t].join('')\n\n\t\treturn join(dir, `${base}-${timestamp}${ext}`)\n\t}\n}\n","/**\n * Linear implementation of Issue Management Provider\n * Uses @linear/sdk for all operations\n */\n\nimport type {\n\tIssueManagementProvider,\n\tGetIssueInput,\n\tGetPRInput,\n\tGetCommentInput,\n\tCreateCommentInput,\n\tUpdateCommentInput,\n\tCreateIssueInput,\n\tCreateChildIssueInput,\n\tCreateDependencyInput,\n\tGetDependenciesInput,\n\tRemoveDependencyInput,\n\tGetChildIssuesInput,\n\tCreateIssueResult,\n\tIssueResult,\n\tPRResult,\n\tCommentDetailResult,\n\tCommentResult,\n\tDependenciesResult,\n\tChildIssueResult,\n} from './types.js'\nimport {\n\tfetchLinearIssue,\n\tcreateLinearComment,\n\tgetLinearComment,\n\tupdateLinearComment,\n\tfetchLinearIssueComments,\n\tcreateLinearIssue,\n\tcreateLinearChildIssue,\n\tcreateLinearIssueRelation,\n\tgetLinearIssueDependencies,\n\tfindLinearIssueRelation,\n\tdeleteLinearIssueRelation,\n\tgetLinearChildIssues,\n} from '../utils/linear.js'\nimport { LinearMarkupConverter } from '../utils/linear-markup-converter.js'\nimport { processMarkdownImages } from '../utils/image-processor.js'\n\n/**\n * Linear-specific implementation of IssueManagementProvider\n */\nexport class LinearIssueManagementProvider implements IssueManagementProvider {\n\treadonly providerName = 'linear'\n\treadonly issuePrefix = ''\n\n\t/**\n\t * Cached team key extracted from issue identifiers (e.g., \"ENG-123\" -> \"ENG\")\n\t * Used as fallback when teamKey is not explicitly provided to createIssue()\n\t */\n\tprivate cachedTeamKey: string | undefined = undefined\n\n\t/**\n\t * Fetch issue details using Linear SDK\n\t */\n\tasync getIssue(input: GetIssueInput): Promise<IssueResult> {\n\t\tconst { number, includeComments = true } = input\n\n\t\t// Extract and cache team key from identifier (e.g., \"ENG-123\" -> \"ENG\")\n\t\t// This enables createIssue() to use the team key as a fallback\n\t\tconst match = number.match(/^([A-Z]{2,})-\\d+$/i)\n\t\tif (match?.[1]) {\n\t\t\tthis.cachedTeamKey = match[1].toUpperCase()\n\t\t}\n\n\t\t// Fetch issue - Linear uses alphanumeric identifiers like \"ENG-123\"\n\t\tconst raw = await fetchLinearIssue(number)\n\n\t\t// Map Linear state name to open/closed\n\t\tconst state = raw.state && (raw.state.toLowerCase().includes('done') || raw.state.toLowerCase().includes('completed') || raw.state.toLowerCase().includes('canceled'))\n\t\t\t? 'closed'\n\t\t\t: 'open'\n\n\t\t// Build result\n\t\tconst result: IssueResult = {\n\t\t\tid: raw.identifier,\n\t\t\ttitle: raw.title,\n\t\t\tbody: raw.description ?? '',\n\t\t\tstate,\n\t\t\turl: raw.url,\n\t\t\tprovider: 'linear',\n\t\t\tauthor: null, // Linear SDK doesn't return author in basic fetch\n\n\t\t\t// Linear-specific fields\n\t\t\tlinearState: raw.state,\n\t\t\tcreatedAt: raw.createdAt,\n\t\t\tupdatedAt: raw.updatedAt,\n\t\t}\n\n\t\t// Fetch comments if requested\n\t\tif (includeComments) {\n\t\t\ttry {\n\t\t\t\tconst comments = await this.fetchIssueComments(number)\n\t\t\t\tif (comments) {\n\t\t\t\t\tresult.comments = comments\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If comments fail, continue without them\n\t\t\t}\n\t\t}\n\n\t\t// Process images in body and comments to make them accessible\n\t\tresult.body = await processMarkdownImages(result.body, 'linear')\n\t\tif (result.comments) {\n\t\t\tfor (const comment of result.comments) {\n\t\t\t\tcomment.body = await processMarkdownImages(comment.body, 'linear')\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch pull request details\n\t * Linear does not support PRs - this throws an error directing to use GitHub\n\t */\n\tasync getPR(_input: GetPRInput): Promise<PRResult> {\n\t\tthrow new Error('Linear does not support pull requests. PRs exist only on GitHub. Use the GitHub provider for PR operations.')\n\t}\n\n\t/**\n\t * Fetch comments for an issue\n\t */\n\tprivate async fetchIssueComments(identifier: string): Promise<IssueResult['comments']> {\n\t\ttry {\n\t\t\tconst comments = await fetchLinearIssueComments(identifier)\n\n\t\t\treturn comments.map(comment => ({\n\t\t\t\tid: comment.id,\n\t\t\t\tbody: comment.body,\n\t\t\t\tcreatedAt: comment.createdAt,\n\t\t\t\tauthor: null, // Linear SDK doesn't return comment author info in basic fetch\n\t\t\t\t...(comment.updatedAt && { updatedAt: comment.updatedAt }),\n\t\t\t}))\n\t\t} catch {\n\t\t\treturn []\n\t\t}\n\t}\n\n\t/**\n\t * Fetch a specific comment by ID\n\t */\n\tasync getComment(input: GetCommentInput): Promise<CommentDetailResult> {\n\t\tconst { commentId } = input\n\n\t\tconst raw = await getLinearComment(commentId)\n\n\t\t// Process images to make them accessible\n\t\tconst processedBody = await processMarkdownImages(raw.body, 'linear')\n\n\t\treturn {\n\t\t\tid: raw.id,\n\t\t\tbody: processedBody,\n\t\t\tauthor: null, // Linear SDK doesn't return comment author info in basic fetch\n\t\t\tcreated_at: raw.createdAt,\n\t\t}\n\t}\n\n\t/**\n\t * Create a new comment on an issue\n\t */\n\tasync createComment(input: CreateCommentInput): Promise<CommentResult> {\n\t\tconst { number, body } = input\n\t\t// Note: Linear doesn't distinguish between issue and PR comments\n\t\t// (Linear doesn't have PRs - that's GitHub-specific)\n\n\t\t// Convert HTML details/summary blocks to Linear's collapsible format\n\t\tconst convertedBody = LinearMarkupConverter.convertToLinear(body)\n\n\t\tconst result = await createLinearComment(number, convertedBody)\n\n\t\treturn {\n\t\t\tid: result.id,\n\t\t\turl: result.url,\n\t\t\tcreated_at: result.createdAt,\n\t\t}\n\t}\n\n\t/**\n\t * Update an existing comment\n\t */\n\tasync updateComment(input: UpdateCommentInput): Promise<CommentResult> {\n\t\tconst { commentId, body } = input\n\n\t\t// Convert HTML details/summary blocks to Linear's collapsible format\n\t\tconst convertedBody = LinearMarkupConverter.convertToLinear(body)\n\n\t\tconst result = await updateLinearComment(commentId, convertedBody)\n\n\t\treturn {\n\t\t\tid: result.id,\n\t\t\turl: result.url,\n\t\t\tupdated_at: result.updatedAt,\n\t\t}\n\t}\n\n\t/**\n\t * Create a new issue\n\t */\n\tasync createIssue(input: CreateIssueInput): Promise<CreateIssueResult> {\n\t\tconst { title, body, labels, teamKey } = input\n\n\t\t// Fallback chain: explicit param > settings (via env) > cached key from getIssue()\n\t\tconst effectiveTeamKey = teamKey ?? process.env.LINEAR_TEAM_KEY ?? this.cachedTeamKey\n\n\t\tif (!effectiveTeamKey) {\n\t\t\tthrow new Error('teamKey is required for Linear issue creation. Configure issueManagement.linear.teamId in settings, or call getIssue first to extract the team from an issue identifier.')\n\t\t}\n\n\t\tconst result = await createLinearIssue(title, body, effectiveTeamKey, labels)\n\n\t\treturn {\n\t\t\tid: result.identifier,\n\t\t\turl: result.url,\n\t\t}\n\t}\n\n\t/**\n\t * Create a child issue linked to a parent issue\n\t * Linear supports atomic creation with parentId field\n\t */\n\tasync createChildIssue(input: CreateChildIssueInput): Promise<CreateIssueResult> {\n\t\tconst { parentId, title, body, labels, teamKey } = input\n\n\t\t// Fetch parent issue to get UUID (parentId in input is identifier like \"ENG-123\")\n\t\tconst parentIssue = await fetchLinearIssue(parentId)\n\n\t\t// Extract team key from parent identifier if not provided\n\t\tconst match = parentId.match(/^([A-Z]{2,})-\\d+$/i)\n\t\tconst effectiveTeamKey = teamKey ?? match?.[1]?.toUpperCase() ?? process.env.LINEAR_TEAM_KEY ?? this.cachedTeamKey\n\n\t\tif (!effectiveTeamKey) {\n\t\t\tthrow new Error('teamKey is required for Linear child issue creation. Provide teamKey parameter or use a parent identifier with team prefix.')\n\t\t}\n\n\t\t// Create child issue with parent's UUID\n\t\tconst result = await createLinearChildIssue(\n\t\t\ttitle,\n\t\t\tbody,\n\t\t\teffectiveTeamKey,\n\t\t\tparentIssue.id, // UUID, not identifier\n\t\t\tlabels\n\t\t)\n\n\t\treturn {\n\t\t\tid: result.identifier,\n\t\t\turl: result.url,\n\t\t}\n\t}\n\n\t/**\n\t * Create a blocking dependency between two issues\n\t */\n\tasync createDependency(input: CreateDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue } = input\n\n\t\t// Fetch both issues to get their UUIDs\n\t\tconst [blockingIssueData, blockedIssueData] = await Promise.all([\n\t\t\tfetchLinearIssue(blockingIssue),\n\t\t\tfetchLinearIssue(blockedIssue),\n\t\t])\n\n\t\t// Create the blocking relation (blockingIssue blocks blockedIssue)\n\t\tawait createLinearIssueRelation(blockingIssueData.id, blockedIssueData.id)\n\t}\n\n\t/**\n\t * Get dependencies for an issue\n\t */\n\tasync getDependencies(input: GetDependenciesInput): Promise<DependenciesResult> {\n\t\tconst { number, direction } = input\n\n\t\treturn await getLinearIssueDependencies(number, direction)\n\t}\n\n\t/**\n\t * Remove a blocking dependency between two issues\n\t */\n\tasync removeDependency(input: RemoveDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue } = input\n\n\t\t// Find the relation ID\n\t\tconst relationId = await findLinearIssueRelation(blockingIssue, blockedIssue)\n\n\t\tif (!relationId) {\n\t\t\tthrow new Error(`No blocking dependency found from ${blockingIssue} to ${blockedIssue}`)\n\t\t}\n\n\t\t// Delete the relation\n\t\tawait deleteLinearIssueRelation(relationId)\n\t}\n\n\t/**\n\t * Get child issues of a parent issue\n\t */\n\tasync getChildIssues(input: GetChildIssuesInput): Promise<ChildIssueResult[]> {\n\t\tconst { number } = input\n\t\t// repo is ignored for Linear\n\t\treturn await getLinearChildIssues(number)\n\t}\n}\n","/**\n * Factory for creating issue management providers\n */\n\nimport type { IssueManagementProvider, IssueProvider } from './types.js'\nimport { GitHubIssueManagementProvider } from './GitHubIssueManagementProvider.js'\nimport { LinearIssueManagementProvider } from './LinearIssueManagementProvider.js'\n\n/**\n * Factory class for creating issue management providers\n */\nexport class IssueManagementProviderFactory {\n\t/**\n\t * Create an issue management provider based on the provider type\n\t */\n\tstatic create(provider: IssueProvider): IssueManagementProvider {\n\t\tswitch (provider) {\n\t\t\tcase 'github':\n\t\t\t\treturn new GitHubIssueManagementProvider()\n\t\t\tcase 'linear':\n\t\t\t\treturn new LinearIssueManagementProvider()\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported issue management provider: ${provider}`)\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["execa","execa","execa","existsSync","execa","existsSync","execa","existsSync","issue","join","extname"]}
1
+ {"version":3,"sources":["../../src/mcp/issue-management-server.ts","../../src/utils/github.ts","../../src/utils/logger.ts","../../src/utils/terminal.ts","../../src/utils/env.ts","../../src/utils/image-processor.ts","../../src/mcp/GitHubIssueManagementProvider.ts","../../src/utils/linear.ts","../../src/types/linear.ts","../../src/utils/linear-markup-converter.ts","../../src/mcp/LinearIssueManagementProvider.ts","../../src/utils/jira.ts","../../src/lib/providers/jira/JiraApiClient.ts","../../src/utils/logger-context.ts","../../src/lib/providers/jira/AdfMarkdownConverter.ts","../../src/utils/prompt.ts","../../src/utils/notification.ts","../../src/lib/providers/jira/JiraIssueTracker.ts","../../src/lib/SettingsManager.ts","../../src/mcp/JiraIssueManagementProvider.ts","../../src/mcp/IssueManagementProviderFactory.ts","../../src/utils/jira-wiki-sanitizer.ts"],"sourcesContent":["/**\n * Issue Management MCP Server\n *\n * A Model Context Protocol server that enables Claude to interact with issue trackers\n * (GitHub, Linear, etc.) during workflows. Provides tools for fetching issues, reading\n * comments, and creating/updating comments.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { IssueManagementProviderFactory } from './IssueManagementProviderFactory.js'\nimport { JiraWikiSanitizer } from '../utils/jira-wiki-sanitizer.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\nimport type {\n\tIssueProvider,\n\tGetIssueInput,\n\tGetPRInput,\n\tGetCommentInput,\n\tCreateCommentInput,\n\tUpdateCommentInput,\n\tCreateIssueInput,\n\tCreateChildIssueInput,\n\tCreateDependencyInput,\n\tGetDependenciesInput,\n\tGetChildIssuesInput,\n\tRemoveDependencyInput,\n\tCloseIssueInput,\n\tReopenIssueInput,\n\tEditIssueInput,\n\tGetReviewCommentsInput,\n} from './types.js'\n\n// Module-level settings loaded at startup\nlet settings: IloomSettings | undefined\n\n// Validate required environment variables\nfunction validateEnvironment(): IssueProvider {\n\tconst provider = process.env.ISSUE_PROVIDER as IssueProvider | undefined\n\tif (!provider) {\n\t\tconsole.error('Missing required environment variable: ISSUE_PROVIDER')\n\t\tprocess.exit(1)\n\t}\n\n\tif (provider !== 'github' && provider !== 'linear' && provider !== 'jira') {\n\t\tconsole.error(`Invalid ISSUE_PROVIDER: ${provider}. Must be 'github', 'linear', or 'jira'`)\n\t\tprocess.exit(1)\n\t}\n\n\t// GitHub-specific validation\n\tif (provider === 'github') {\n\t\tconst required = ['REPO_OWNER', 'REPO_NAME']\n\t\tconst missing = required.filter((key) => !process.env[key])\n\n\t\tif (missing.length > 0) {\n\t\t\tconsole.error(\n\t\t\t\t`Missing required environment variables for GitHub provider: ${missing.join(', ')}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\t// Linear requires API token for SDK authentication\n\tif (provider === 'linear') {\n\t\tif (!process.env.LINEAR_API_TOKEN) {\n\t\t\tconsole.error('Missing required environment variable for Linear provider: LINEAR_API_TOKEN')\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\t// Jira requires host, username, API token, and project key\n\tif (provider === 'jira') {\n\t\tconst required = ['JIRA_HOST', 'JIRA_USERNAME', 'JIRA_API_TOKEN', 'JIRA_PROJECT_KEY']\n\t\tconst missing = required.filter((key) => !process.env[key])\n\n\t\tif (missing.length > 0) {\n\t\t\tconsole.error(\n\t\t\t\t`Missing required environment variables for Jira provider: ${missing.join(', ')}`\n\t\t\t)\n\t\t\tprocess.exit(1)\n\t\t}\n\t}\n\n\treturn provider\n}\n\n// Initialize the MCP server\nconst server = new McpServer({\n\tname: 'issue-management-broker',\n\tversion: '0.1.0',\n})\n\n// Define flexible author schema\nconst flexibleAuthorSchema = z.object({\n\tid: z.string(),\n\tdisplayName: z.string(),\n}).passthrough()\n\n// Register get_issue tool\nserver.registerTool(\n\t'get_issue',\n\t{\n\t\ttitle: 'Get Issue',\n\t\tdescription:\n\t\t\t'Fetch issue details including body, title, comments, labels, assignees, and other metadata. ' +\n\t\t\t'Author fields vary by provider: GitHub uses { login }, Linear uses { name, displayName }, Jira uses { displayName, accountId }. ' +\n\t\t\t'All authors have normalized core fields: { id, displayName } plus provider-specific fields.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The issue identifier'),\n\t\t\tincludeComments: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Whether to include comments (default: true)'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\t// Core validated fields\n\t\t\tid: z.string().describe('Issue identifier'),\n\t\t\ttitle: z.string().describe('Issue title'),\n\t\t\tbody: z.string().describe('Issue body/description'),\n\t\t\tstate: z.string().describe('Issue state (open, closed, etc.)'),\n\t\t\turl: z.string().describe('Issue URL'),\n\t\t\tprovider: z.enum(['github', 'linear', 'jira']).describe('Issue management provider'),\n\n\t\t\t// Flexible author - core fields + passthrough\n\t\t\tauthor: flexibleAuthorSchema.nullable().describe(\n\t\t\t\t'Issue author with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\n\t\t\t// Optional flexible arrays\n\t\t\tassignees: z.array(flexibleAuthorSchema).optional().describe(\n\t\t\t\t'Issue assignees with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\t\t\tlabels: z.array(\n\t\t\t\tz.object({ name: z.string() }).passthrough()\n\t\t\t).optional().describe('Issue labels'),\n\n\t\t\t// Comments with flexible author\n\t\t\tcomments: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\tbody: z.string(),\n\t\t\t\t\tauthor: flexibleAuthorSchema.nullable(),\n\t\t\t\t\tcreatedAt: z.string(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('Issue comments with flexible author structure'),\n\t\t},\n\t},\n\tasync ({ number, includeComments, repo }: GetIssueInput) => {\n\t\tconsole.error(`Fetching issue ${number}${repo ? ` from ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tconst result = await provider.getIssue({ number, includeComments, repo })\n\n\t\t\tconsole.error(`Issue fetched successfully: ${result.number} - ${result.title}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to fetch issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to fetch issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Import GitHubIssueManagementProvider for get_pr tool (PRs always use GitHub)\nimport { GitHubIssueManagementProvider } from './GitHubIssueManagementProvider.js'\n\n// Register get_pr tool\n// Note: PRs only exist on GitHub, so this tool always uses the GitHub provider\n// regardless of the configured issue tracker\nserver.registerTool(\n\t'get_pr',\n\t{\n\t\ttitle: 'Get Pull Request',\n\t\tdescription:\n\t\t\t'Fetch pull request details including title, body, comments, files, commits, and branch information. ' +\n\t\t\t'PRs only exist on GitHub, so this tool always uses GitHub regardless of configured issue tracker. ' +\n\t\t\t'Author fields have normalized core fields: { id, displayName } plus provider-specific fields.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The PR number'),\n\t\t\tincludeComments: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Whether to include comments (default: true)'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\t// Core validated fields\n\t\t\tid: z.string().describe('PR identifier'),\n\t\t\tnumber: z.number().describe('PR number'),\n\t\t\ttitle: z.string().describe('PR title'),\n\t\t\tbody: z.string().describe('PR body/description'),\n\t\t\tstate: z.string().describe('PR state (OPEN, CLOSED, MERGED)'),\n\t\t\turl: z.string().describe('PR URL'),\n\n\t\t\t// Branch info\n\t\t\theadRefName: z.string().describe('Source branch name'),\n\t\t\tbaseRefName: z.string().describe('Target branch name'),\n\n\t\t\t// Flexible author - core fields + passthrough\n\t\t\tauthor: flexibleAuthorSchema.nullable().describe(\n\t\t\t\t'PR author with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\n\t\t\t// Optional flexible arrays\n\t\t\tfiles: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tpath: z.string(),\n\t\t\t\t\tadditions: z.number(),\n\t\t\t\t\tdeletions: z.number(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('Changed files in the PR'),\n\t\t\tcommits: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\toid: z.string(),\n\t\t\t\t\tmessageHeadline: z.string(),\n\t\t\t\t\tauthor: flexibleAuthorSchema.nullable(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('Commits in the PR'),\n\t\t\tcomments: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\tbody: z.string(),\n\t\t\t\t\tauthor: flexibleAuthorSchema.nullable(),\n\t\t\t\t\tcreatedAt: z.string(),\n\t\t\t\t}).passthrough()\n\t\t\t).optional().describe('PR comments'),\n\t\t},\n\t},\n\tasync ({ number, includeComments, repo }: GetPRInput) => {\n\t\tconsole.error(`Fetching PR ${number}${repo ? ` from ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\t// PRs always use GitHub provider regardless of configured issue tracker\n\t\t\tconst provider = new GitHubIssueManagementProvider()\n\t\t\tconst result = await provider.getPR({ number, includeComments, repo })\n\n\t\t\tconsole.error(`PR fetched successfully: #${result.number} - ${result.title}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to fetch PR: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to fetch PR: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register get_review_comments tool\n// Note: Review comments only exist on GitHub PRs, so this tool always uses the GitHub provider\n\nserver.registerTool(\n\t'get_review_comments',\n\t{\n\t\ttitle: 'Get PR Review Comments',\n\t\tdescription:\n\t\t\t'Fetch inline code review comments on a pull request (comments on specific files and lines). ' +\n\t\t\t'Returns comments with file path, line number, diff side, author, and reply threading. ' +\n\t\t\t'Optionally filter by review ID. PRs only exist on GitHub, so this tool always uses GitHub.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The PR number'),\n\t\t\treviewId: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Optional review ID to filter comments by a specific review'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tcomments: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tid: z.string().describe('Review comment ID'),\n\t\t\t\t\tbody: z.string().describe('Comment body content'),\n\t\t\t\t\tpath: z.string().describe('File path the comment is on'),\n\t\t\t\t\tline: z.number().nullable().describe('Line number in the diff'),\n\t\t\t\t\tside: z.string().nullable().describe('Side of the diff (LEFT or RIGHT)'),\n\t\t\t\t\tauthor: flexibleAuthorSchema.nullable().describe('Comment author'),\n\t\t\t\t\tcreatedAt: z.string().describe('Comment creation timestamp'),\n\t\t\t\t\tupdatedAt: z.string().nullable().describe('Comment last updated timestamp'),\n\t\t\t\t\tinReplyToId: z.string().nullable().describe('ID of the comment this replies to'),\n\t\t\t\t\tpullRequestReviewId: z.number().nullable().describe('The review this comment belongs to'),\n\t\t\t\t})\n\t\t\t).describe('Inline review comments on the PR'),\n\t\t},\n\t},\n\tasync ({ number, reviewId, repo }: GetReviewCommentsInput) => {\n\t\tconsole.error(`Fetching review comments for PR ${number}${reviewId ? ` (review ${reviewId})` : ''}${repo ? ` from ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\t// Review comments always use GitHub provider regardless of configured issue tracker\n\t\t\tconst provider = new GitHubIssueManagementProvider()\n\t\t\tconst comments = await provider.getReviewComments({ number, reviewId, repo })\n\n\t\t\tconsole.error(`Review comments fetched successfully: ${comments.length} comments`)\n\n\t\t\tconst result = { comments }\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to fetch review comments: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to fetch review comments: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register get_comment tool\nserver.registerTool(\n\t'get_comment',\n\t{\n\t\ttitle: 'Get Comment',\n\t\tdescription:\n\t\t\t'Fetch a specific comment by ID. Author has normalized core fields { id, displayName } plus provider-specific fields.',\n\t\tinputSchema: {\n\t\t\tcommentId: z.string().describe('The comment identifier to fetch'),\n\t\t\tnumber: z.string().describe('The issue or PR identifier (context for providers that need it)'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string().describe('Comment identifier'),\n\t\t\tbody: z.string().describe('Comment body content'),\n\t\t\tauthor: flexibleAuthorSchema.nullable().describe(\n\t\t\t\t'Comment author with normalized { id, displayName } plus provider-specific fields'\n\t\t\t),\n\t\t\tcreated_at: z.string().describe('Comment creation timestamp'),\n\t\t\tupdated_at: z.string().optional().describe('Comment last updated timestamp'),\n\t\t},\n\t},\n\tasync ({ commentId, number, repo }: GetCommentInput) => {\n\t\tconsole.error(`Fetching comment ${commentId} from issue ${number}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tconst result = await provider.getComment({ commentId, number, repo })\n\n\t\t\tconsole.error(`Comment fetched successfully: ${result.id}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to fetch comment: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to fetch comment: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register create_comment tool\nserver.registerTool(\n\t'create_comment',\n\t{\n\t\ttitle: 'Create Comment',\n\t\tdescription:\n\t\t\t'Create a new comment on an issue or pull request. Use this to start tracking a workflow phase.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The issue or PR identifier'),\n\t\t\tbody: z.string().describe('The comment body (markdown supported)'),\n\t\t\ttype: z\n\t\t\t\t.enum(['issue', 'pr'])\n\t\t\t\t.describe('Type of entity to comment on (issue or pr)'),\n\t\t\tmarkupLanguage: z.literal('GFM').describe('The markup language for the body content. Must be GitHub Flavored Markdown (GFM).'),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string(),\n\t\t\turl: z.string(),\n\t\t\tcreated_at: z.string().optional(),\n\t\t},\n\t},\n\tasync ({ number, body, type }: CreateCommentInput) => {\n\t\tconsole.error(`Creating ${type} comment on ${number}`)\n\n\t\ttry {\n\t\t\tconst sanitizedBody = JiraWikiSanitizer.sanitize(body)\n\t\t\t// PR comments must always go to GitHub since PRs only exist on GitHub\n\t\t\tconst providerType = type === 'pr' ? 'github' : (process.env.ISSUE_PROVIDER as IssueProvider)\n\t\t\tconst provider = IssueManagementProviderFactory.create(providerType, settings)\n\t\t\tconst result = await provider.createComment({ number, body: sanitizedBody, type })\n\n\t\t\tconsole.error(\n\t\t\t\t`Comment created successfully: ${result.id} at ${result.url}`\n\t\t\t)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create comment: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create ${type} comment: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register update_comment tool\nserver.registerTool(\n\t'update_comment',\n\t{\n\t\ttitle: 'Update Comment',\n\t\tdescription:\n\t\t\t'Update an existing comment. Use this to update progress during a workflow phase.',\n\t\tinputSchema: {\n\t\t\tcommentId: z.string().describe('The comment identifier to update'),\n\t\t\tnumber: z.string().describe('The issue or PR identifier (context for providers that need it)'),\n\t\t\tbody: z.string().describe('The updated comment body (markdown supported)'),\n\t\t\ttype: z.enum(['issue', 'pr']).optional().describe('Optional type to route PR comments to GitHub regardless of configured provider'),\n\t\t\tmarkupLanguage: z.literal('GFM').describe('The markup language for the body content. Must be GitHub Flavored Markdown (GFM).'),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string(),\n\t\t\turl: z.string(),\n\t\t\tupdated_at: z.string().optional(),\n\t\t},\n\t},\n\tasync ({ commentId, number, body, type }: UpdateCommentInput) => {\n\t\tconsole.error(`Updating comment ${commentId} on ${type === 'pr' ? 'PR' : 'issue'} ${number}`)\n\n\t\ttry {\n\t\t\tconst sanitizedBody = JiraWikiSanitizer.sanitize(body)\n\t\t\t// PR comments must always go to GitHub since PRs only exist on GitHub\n\t\t\tconst providerType = type === 'pr' ? 'github' : (process.env.ISSUE_PROVIDER as IssueProvider)\n\t\t\tconst provider = IssueManagementProviderFactory.create(providerType, settings)\n\t\t\tconst result = await provider.updateComment({ commentId, number, body: sanitizedBody })\n\n\t\t\tconsole.error(\n\t\t\t\t`Comment updated successfully: ${result.id} at ${result.url}`\n\t\t\t)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to update comment: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to update comment: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register create_issue tool\nserver.registerTool(\n\t'create_issue',\n\t{\n\t\ttitle: 'Create Issue',\n\t\tdescription:\n\t\t\t'Create a new issue in the configured issue tracker. ' +\n\t\t\t'For GitHub: creates issue in the configured repository. ' +\n\t\t\t'For Linear: requires teamKey parameter (e.g., \"ENG\", \"PLAT\"), or configure issueManagement.linear.teamId in settings, or call get_issue first to auto-detect the team.',\n\t\tinputSchema: {\n\t\t\ttitle: z.string().describe('The issue title'),\n\t\t\tbody: z.string().describe('The issue body/description (markdown supported)'),\n\t\t\tlabels: z.array(z.string()).optional().describe('Optional labels to apply to the issue'),\n\t\t\tteamKey: z.string().optional().describe('Team key for Linear (e.g., \"ENG\"). Falls back to settings or team extracted from previous get_issue call. Ignored for GitHub.'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t\tmarkupLanguage: z.literal('GFM').describe('The markup language for the body content. Must be GitHub Flavored Markdown (GFM).'),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string().describe('Issue identifier'),\n\t\t\turl: z.string().describe('Issue URL'),\n\t\t\tnumber: z.number().optional().describe('Issue number (GitHub only)'),\n\t\t},\n\t},\n\tasync ({ title, body, labels, teamKey, repo }: CreateIssueInput) => {\n\t\tconsole.error(`Creating issue: ${title}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst sanitizedBody = JiraWikiSanitizer.sanitize(body)\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tconst result = await provider.createIssue({ title, body: sanitizedBody, labels, teamKey, repo })\n\n\t\t\tconsole.error(`Issue created successfully: ${result.id} at ${result.url}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register create_child_issue tool\nserver.registerTool(\n\t'create_child_issue',\n\t{\n\t\ttitle: 'Create Child Issue',\n\t\tdescription:\n\t\t\t'Create a new child issue linked to a parent issue. ' +\n\t\t\t'For GitHub: creates issue and links via sub-issue API (requires two API calls). ' +\n\t\t\t'For Linear: creates issue atomically with parent relationship. ' +\n\t\t\t'The parentId should be the parent issue identifier (GitHub issue number or Linear identifier like \"ENG-123\").',\n\t\tinputSchema: {\n\t\t\tparentId: z.string().describe('Parent issue identifier (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\ttitle: z.string().describe('The child issue title'),\n\t\t\tbody: z.string().describe('The child issue body/description (markdown supported)'),\n\t\t\tlabels: z.array(z.string()).optional().describe('Optional labels to apply to the child issue'),\n\t\t\tteamKey: z.string().optional().describe('Team key for Linear (e.g., \"ENG\"). Falls back to parent team. Ignored for GitHub.'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t\tmarkupLanguage: z.literal('GFM').describe('The markup language for the body content. Must be GitHub Flavored Markdown (GFM).'),\n\t\t},\n\t\toutputSchema: {\n\t\t\tid: z.string().describe('Issue identifier'),\n\t\t\turl: z.string().describe('Issue URL'),\n\t\t\tnumber: z.number().optional().describe('Issue number (GitHub only)'),\n\t\t},\n\t},\n\tasync ({ parentId, title, body, labels, teamKey, repo }: CreateChildIssueInput) => {\n\t\tconsole.error(`Creating child issue for parent ${parentId}: ${title}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst sanitizedBody = JiraWikiSanitizer.sanitize(body)\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tconst result = await provider.createChildIssue({ parentId, title, body: sanitizedBody, labels, teamKey, repo })\n\n\t\t\tconsole.error(`Child issue created successfully: ${result.id} at ${result.url}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create child issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create child issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Define dependency result schema\nconst dependencyResultSchema = z.object({\n\tid: z.string().describe('Issue identifier'),\n\ttitle: z.string().describe('Issue title'),\n\turl: z.string().describe('Issue URL'),\n\tstate: z.string().describe('Issue state'),\n})\n\n// Register create_dependency tool\nserver.registerTool(\n\t'create_dependency',\n\t{\n\t\ttitle: 'Create Dependency',\n\t\tdescription:\n\t\t\t'Create a blocking dependency between two issues. ' +\n\t\t\t'The blockingIssue will block the blockedIssue. ' +\n\t\t\t'For GitHub: uses the sub-issue API. ' +\n\t\t\t'For Linear: creates a \"blocks\" relation.',\n\t\tinputSchema: {\n\t\t\tblockingIssue: z.string().describe('The issue that blocks (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\tblockedIssue: z.string().describe('The issue being blocked (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tsuccess: z.boolean().describe('Whether the dependency was created successfully'),\n\t\t},\n\t},\n\tasync ({ blockingIssue, blockedIssue, repo }: CreateDependencyInput) => {\n\t\tconsole.error(`Creating dependency: ${blockingIssue} blocks ${blockedIssue}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tawait provider.createDependency({ blockingIssue, blockedIssue, repo })\n\n\t\t\tconsole.error(`Dependency created successfully: ${blockingIssue} -> ${blockedIssue}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ success: true }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { success: true },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to create dependency: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to create dependency: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register get_dependencies tool\nserver.registerTool(\n\t'get_dependencies',\n\t{\n\t\ttitle: 'Get Dependencies',\n\t\tdescription:\n\t\t\t'Get blocking/blocked_by dependencies for an issue. ' +\n\t\t\t'Returns lists of issues that this issue blocks and/or is blocked by.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('Issue identifier (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\tdirection: z\n\t\t\t\t.enum(['blocking', 'blocked_by', 'both'])\n\t\t\t\t.describe('Which dependencies to fetch: \"blocking\" for issues this blocks, \"blocked_by\" for issues blocking this, \"both\" for all'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tblocking: z.array(dependencyResultSchema).describe('Issues that this issue blocks'),\n\t\t\tblockedBy: z.array(dependencyResultSchema).describe('Issues that block this issue'),\n\t\t},\n\t},\n\tasync ({ number, direction, repo }: GetDependenciesInput) => {\n\t\tconsole.error(`Getting dependencies for ${number} (direction: ${direction})${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tconst result = await provider.getDependencies({ number, direction, repo })\n\n\t\t\tconsole.error(`Dependencies fetched: ${result.blocking.length} blocking, ${result.blockedBy.length} blocked_by`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(result),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: result as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to get dependencies: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to get dependencies: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register remove_dependency tool\nserver.registerTool(\n\t'remove_dependency',\n\t{\n\t\ttitle: 'Remove Dependency',\n\t\tdescription:\n\t\t\t'Remove a blocking dependency between two issues. ' +\n\t\t\t'The blockingIssue will no longer block the blockedIssue.',\n\t\tinputSchema: {\n\t\t\tblockingIssue: z.string().describe('The issue that blocks (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\tblockedIssue: z.string().describe('The issue being blocked (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tsuccess: z.boolean().describe('Whether the dependency was removed successfully'),\n\t\t},\n\t},\n\tasync ({ blockingIssue, blockedIssue, repo }: RemoveDependencyInput) => {\n\t\tconsole.error(`Removing dependency: ${blockingIssue} blocks ${blockedIssue}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tawait provider.removeDependency({ blockingIssue, blockedIssue, repo })\n\n\t\t\tconsole.error(`Dependency removed successfully: ${blockingIssue} -> ${blockedIssue}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ success: true }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { success: true },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to remove dependency: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to remove dependency: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Define child issue result schema (reuse dependencyResultSchema pattern)\nconst childIssueResultSchema = z.object({\n\tid: z.string().describe('Issue identifier'),\n\ttitle: z.string().describe('Issue title'),\n\turl: z.string().describe('Issue URL'),\n\tstate: z.string().describe('Issue state'),\n})\n\n// Register get_child_issues tool\nserver.registerTool(\n\t'get_child_issues',\n\t{\n\t\ttitle: 'Get Child Issues',\n\t\tdescription:\n\t\t\t'Get child issues (sub-issues) of a parent issue. ' +\n\t\t\t'Returns a list of issues that are children of the specified parent.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('Parent issue identifier (GitHub issue number or Linear identifier like \"ENG-123\")'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tchildren: z.array(childIssueResultSchema).describe('Child issues of the parent'),\n\t\t},\n\t},\n\tasync ({ number, repo }: GetChildIssuesInput) => {\n\t\tconsole.error(`Getting child issues for ${number}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tconst result = await provider.getChildIssues({ number, repo })\n\n\t\t\tconsole.error(`Child issues fetched: ${result.length} children`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ children: result }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { children: result } as unknown as { [x: string]: unknown },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to get child issues: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to get child issues: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register close_issue tool\nserver.registerTool(\n\t'close_issue',\n\t{\n\t\ttitle: 'Close Issue',\n\t\tdescription:\n\t\t\t'Close an issue in the configured issue tracker. ' +\n\t\t\t'For GitHub: uses `gh issue close`. ' +\n\t\t\t'For Linear: transitions issue to \"Done\" state. ' +\n\t\t\t'For Jira: transitions issue to \"Done\" state.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The issue identifier'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tsuccess: z.boolean().describe('Whether the issue was closed successfully'),\n\t\t},\n\t},\n\tasync ({ number, repo }: CloseIssueInput) => {\n\t\tconsole.error(`Closing issue ${number}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tawait provider.closeIssue({ number, repo })\n\n\t\t\tconsole.error(`Issue closed successfully: ${number}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ success: true }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { success: true },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to close issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to close issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register reopen_issue tool\nserver.registerTool(\n\t'reopen_issue',\n\t{\n\t\ttitle: 'Reopen Issue',\n\t\tdescription:\n\t\t\t'Reopen a closed issue in the configured issue tracker. ' +\n\t\t\t'For GitHub: uses `gh issue reopen`. ' +\n\t\t\t'For Linear: transitions issue to \"Todo\" state. ' +\n\t\t\t'For Jira: transitions issue to \"Reopen\" or \"To Do\" state.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The issue identifier'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t},\n\t\toutputSchema: {\n\t\t\tsuccess: z.boolean().describe('Whether the issue was reopened successfully'),\n\t\t},\n\t},\n\tasync ({ number, repo }: ReopenIssueInput) => {\n\t\tconsole.error(`Reopening issue ${number}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tawait provider.reopenIssue({ number, repo })\n\n\t\t\tconsole.error(`Issue reopened successfully: ${number}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ success: true }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { success: true },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to reopen issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to reopen issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Register edit_issue tool\nserver.registerTool(\n\t'edit_issue',\n\t{\n\t\ttitle: 'Edit Issue',\n\t\tdescription:\n\t\t\t'Edit an issue\\'s properties (title, body, state, labels) in the configured issue tracker. ' +\n\t\t\t'State changes use close/reopen internally. ' +\n\t\t\t'For GitHub: uses `gh issue edit` for field updates and `gh issue close/reopen` for state. ' +\n\t\t\t'For Linear: uses Linear SDK to update fields and state transitions. ' +\n\t\t\t'For Jira: uses REST API to update fields and transitions for state.',\n\t\tinputSchema: {\n\t\t\tnumber: z.string().describe('The issue identifier'),\n\t\t\ttitle: z.string().optional().describe('New issue title'),\n\t\t\tbody: z.string().optional().describe('New issue body/description'),\n\t\t\tstate: z.enum(['open', 'closed']).optional().describe('New issue state'),\n\t\t\tlabels: z.array(z.string()).optional().describe('Labels to add to the issue'),\n\t\t\trepo: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Optional repository in \"owner/repo\" format or full GitHub URL. ' +\n\t\t\t\t\t'When not provided, uses the current repository. GitHub only.'\n\t\t\t\t),\n\t\t\tmarkupLanguage: z.literal('GFM').optional().describe('The markup language for the body content. Must be GitHub Flavored Markdown (GFM).'),\n\t\t},\n\t\toutputSchema: {\n\t\t\tsuccess: z.boolean().describe('Whether the issue was edited successfully'),\n\t\t},\n\t},\n\tasync ({ number, title, body, state, labels, repo }: EditIssueInput) => {\n\t\tconsole.error(`Editing issue ${number}${repo ? ` in ${repo}` : ''}`)\n\n\t\ttry {\n\t\t\tconst sanitizedBody = body ? JiraWikiSanitizer.sanitize(body) : undefined\n\t\t\tconst provider = IssueManagementProviderFactory.create(\n\t\t\t\tprocess.env.ISSUE_PROVIDER as IssueProvider,\n\t\t\t\tsettings\n\t\t\t)\n\t\t\tawait provider.editIssue({ number, title, body: sanitizedBody, state, labels, repo })\n\n\t\t\tconsole.error(`Issue edited successfully: ${number}`)\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify({ success: true }),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tstructuredContent: { success: true },\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tconsole.error(`Failed to edit issue: ${errorMessage}`)\n\t\t\tthrow new Error(`Failed to edit issue: ${errorMessage}`)\n\t\t}\n\t}\n)\n\n// Main server startup\nasync function main(): Promise<void> {\n\tconsole.error('=== Issue Management 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: ${new URL(import.meta.url).pathname}`)\n\n\t// Log all ISSUE_PROVIDER-related env vars (redact sensitive values)\n\tconst relevantEnvKeys = [\n\t\t'ISSUE_PROVIDER', 'REPO_OWNER', 'REPO_NAME', 'GITHUB_API_URL', 'GITHUB_EVENT_NAME',\n\t\t'DRAFT_PR_NUMBER', 'LINEAR_API_TOKEN', 'LINEAR_TEAM_KEY',\n\t\t'JIRA_HOST', 'JIRA_USERNAME', 'JIRA_API_TOKEN', 'JIRA_PROJECT_KEY',\n\t]\n\tconsole.error('Environment variables:')\n\tfor (const key of relevantEnvKeys) {\n\t\tconst val = process.env[key]\n\t\tif (val !== undefined) {\n\t\t\tconsole.error(` ${key}=${val}`)\n\t\t}\n\t}\n\n\t// Load settings for providers that need them\n\tconst settingsManager = new SettingsManager()\n\tsettings = await settingsManager.loadSettings()\n\tconsole.error('Settings loaded')\n\n\t// Validate environment and get provider\n\tconst provider = validateEnvironment()\n\tconsole.error('Environment validated')\n\tconsole.error(`Issue management provider: ${provider}`)\n\n\tif (provider === 'github') {\n\t\tconsole.error(`Repository: ${process.env.REPO_OWNER}/${process.env.REPO_NAME}`)\n\t\tconsole.error(`Event type: ${process.env.GITHUB_EVENT_NAME ?? 'not specified'}`)\n\t}\n\n\t// Connect stdio transport\n\tconst transport = new StdioServerTransport()\n\tawait server.connect(transport)\n\n\tconsole.error('=== Issue Management MCP Server READY (stdio transport) ===')\n}\n\n// Run the server\nmain().catch((error) => {\n\tconsole.error('Fatal error starting MCP server:', error)\n\tprocess.exit(1)\n})\n","import { execa } from 'execa'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tGitHubAuthStatus,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { logger } from './logger.js'\n\n// Core GitHub CLI execution wrapper\nexport async function executeGhCommand<T = unknown>(\n\targs: string[],\n\toptions?: { cwd?: string; timeout?: number }\n): Promise<T> {\n\tconst result = await execa('gh', args, {\n\t\tcwd: options?.cwd ?? process.cwd(),\n\t\ttimeout: options?.timeout ?? 30000,\n\t\tencoding: 'utf8',\n\t})\n\n\t// Parse JSON output if --json flag, --format json, --jq, or GraphQL was used\n\tconst isJson =\n\t\targs.includes('--json') ||\n\t\targs.includes('--jq') ||\n\t\t(args.includes('--format') && args[args.indexOf('--format') + 1] === 'json') ||\n\t\t(args[0] === 'api' && args[1] === 'graphql')\n\tconst data = isJson ? JSON.parse(result.stdout) : result.stdout\n\n\treturn data as T\n}\n\n// Authentication checking\nexport async function checkGhAuth(): Promise<GitHubAuthStatus> {\n\ttry {\n\t\tconst output = await executeGhCommand<string>(['auth', 'status'])\n\n\t\t// Parse auth status output - handle both old and new formats\n\t\t// Old format: \"Logged in to github.com as username\"\n\t\t// New format: \"✓ Logged in to github.com account username (keyring)\"\n\n\t\t// Split output into lines to find the active account\n\t\tconst lines = output.split('\\n')\n\t\tlet username: string | undefined\n\t\tlet scopes: string[] = []\n\n\t\t// Find the active account (look for \"Active account: true\" or first account if none marked)\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst line = lines[i]\n\n\t\t\t// Match new format: \"✓ Logged in to github.com account username\"\n\t\t\tconst newFormatMatch = line?.match(/Logged in to github\\.com account ([^\\s(]+)/)\n\t\t\tif (newFormatMatch) {\n\t\t\t\tconst accountName = newFormatMatch[1]\n\n\t\t\t\t// Check if this is the active account\n\t\t\t\tconst nextFewLines = lines.slice(i + 1, i + 5).join('\\n')\n\t\t\t\tconst isActive = nextFewLines.includes('Active account: true')\n\n\t\t\t\t// If this is the active account, or we haven't found one yet and there's no \"Active account\" marker\n\t\t\t\tif (isActive || (!username && !output.includes('Active account:'))) {\n\t\t\t\t\tusername = accountName\n\n\t\t\t\t\t// Find scopes for this account\n\t\t\t\t\tconst scopeMatch = nextFewLines.match(/Token scopes: (.+)/)\n\t\t\t\t\tif (scopeMatch?.[1]) {\n\t\t\t\t\t\tscopes = scopeMatch[1].split(', ').map(scope => scope.replace(/^'|'$/g, ''))\n\t\t\t\t\t}\n\n\t\t\t\t\t// If this is the active account, we're done\n\t\t\t\t\tif (isActive) break\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fallback: match old format\n\t\t\tif (!username) {\n\t\t\t\tconst oldFormatMatch = line?.match(/Logged in to github\\.com as ([^\\s]+)/)\n\t\t\t\tif (oldFormatMatch) {\n\t\t\t\t\tusername = oldFormatMatch[1]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If scopes not yet extracted, try the old \"Token scopes\" format\n\t\tif (scopes.length === 0) {\n\t\t\tconst scopeMatch = output.match(/Token scopes: (.+)/)\n\t\t\tscopes = scopeMatch?.[1]?.split(', ').map(scope => scope.replace(/^'|'$/g, '')) ?? []\n\t\t}\n\n\t\treturn {\n\t\t\thasAuth: true,\n\t\t\tscopes,\n\t\t\t...(username && { username }),\n\t\t}\n\t} catch (error) {\n\t\t// Only return \"no auth\" for specific authentication errors\n\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('You are not logged into any GitHub hosts')) {\n\t\t\treturn { hasAuth: false, scopes: [] }\n\t\t}\n\t\t// Re-throw unexpected errors\n\t\tthrow error\n\t}\n}\n\nexport async function hasProjectScope(): Promise<boolean> {\n\tconst auth = await checkGhAuth()\n\treturn auth.scopes.includes('project')\n}\n\n// Issue fetching\nexport async function fetchGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<GitHubIssue> {\n\tlogger.debug('Fetching GitHub issue', { issueNumber, repo })\n\n\tconst args = [\n\t\t'issue',\n\t\t'view',\n\t\tString(issueNumber),\n\t\t'--json',\n\t\t'number,title,body,state,labels,assignees,url,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubIssue>(args)\n}\n\n// PR fetching\nexport async function fetchGhPR(\n\tprNumber: number,\n\trepo?: string\n): Promise<GitHubPullRequest> {\n\tlogger.debug('Fetching GitHub PR', { prNumber, repo })\n\n\tconst args = [\n\t\t'pr',\n\t\t'view',\n\t\tString(prNumber),\n\t\t'--json',\n\t\t'number,title,body,state,headRefName,baseRefName,url,isDraft,isCrossRepository,mergeable,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubPullRequest>(args)\n}\n\n// Project operations\nexport async function fetchProjectList(\n\towner: string\n): Promise<GitHubProject[]> {\n\tconst result = await executeGhCommand<{ projects: GitHubProject[] }>([\n\t\t'project',\n\t\t'list',\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'100',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.projects ?? []\n}\n\nexport async function fetchProjectItems(\n\tprojectNumber: number,\n\towner: string\n): Promise<ProjectItem[]> {\n\tconst result = await executeGhCommand<{ items: ProjectItem[] }>([\n\t\t'project',\n\t\t'item-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'10000',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.items ?? []\n}\n\nexport async function fetchProjectFields(\n\tprojectNumber: number,\n\towner: string\n): Promise<{ fields: ProjectField[] }> {\n\tconst result = await executeGhCommand<{ fields: ProjectField[] }>([\n\t\t'project',\n\t\t'field-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result ?? { fields: [] }\n}\n\nexport async function updateProjectItemField(\n\titemId: string,\n\tprojectId: string,\n\tfieldId: string,\n\toptionId: string\n): Promise<void> {\n\tawait executeGhCommand([\n\t\t'project',\n\t\t'item-edit',\n\t\t'--id',\n\t\titemId,\n\t\t'--project-id',\n\t\tprojectId,\n\t\t'--field-id',\n\t\tfieldId,\n\t\t'--single-select-option-id',\n\t\toptionId,\n\t\t'--format',\n\t\t'json',\n\t])\n}\n\n// GitHub Issue Operations\n\ninterface IssueCreateResponse {\n\tnumber: string | number\n\turl: string\n}\n\n/**\n * Create a new GitHub issue\n * @param title - The issue title\n * @param body - The issue body (markdown supported)\n * @param options - Optional configuration\n * @param options.repo - Repository in format \"owner/repo\" (uses current repo if not provided)\n * @param options.labels - Array of label names to add to the issue\n * @returns Issue metadata including number and URL\n */\nexport async function createIssue(\n\ttitle: string,\n\tbody: string,\n\toptions?: { repo?: string | undefined; labels?: string[] | undefined }\n): Promise<IssueCreateResponse> {\n\tconst { repo, labels } = options ?? {}\n\n\tlogger.debug('Creating GitHub issue', { title, repo, labels })\n\n\tconst args = [\n\t\t'issue',\n\t\t'create',\n\t\t'--title',\n\t\ttitle,\n\t\t'--body',\n\t\tbody,\n\t]\n\n\t// Add repo if provided\n\tif (repo) {\n\t\targs.splice(2, 0, '--repo', repo)\n\t}\n\n\t// Add labels if provided\n\tif (labels && labels.length > 0) {\n\t\targs.push('--label', labels.join(','))\n\t}\n\n\tconst execaOptions: { timeout: number; encoding: 'utf8'; cwd?: string } = {\n\t\ttimeout: 30000,\n\t\tencoding: 'utf8',\n\t}\n\n\tif (!repo) {\n\t\texecaOptions.cwd = process.cwd()\n\t}\n\n\tconst result = await execa('gh', args, execaOptions)\n\n\t// Parse the URL from the output (format: \"https://github.com/owner/repo/issues/123\")\n\tconst urlMatch = result.stdout.trim().match(/https:\\/\\/github\\.com\\/[^/]+\\/[^/]+\\/issues\\/(\\d+)/)\n\tif (!urlMatch?.[1]) {\n\t\tthrow new Error(`Failed to parse issue URL from gh output: ${result.stdout}`)\n\t}\n\n\tconst issueNumber = parseInt(urlMatch[1], 10)\n\tconst issueUrl = urlMatch[0]\n\n\treturn {\n\t\tnumber: issueNumber,\n\t\turl: issueUrl,\n\t}\n}\n\n/**\n * @deprecated Use createIssue with options.repo instead\n * Create a new GitHub issue in a specific repository\n * @param title - Issue title\n * @param body - Issue body (markdown)\n * @param repository - Repository in format \"owner/repo\"\n * @param labels - Optional array of label names to add to the issue\n * @returns Issue number and URL\n */\nexport async function createIssueInRepo(\n\ttitle: string,\n\tbody: string,\n\trepository: string,\n\tlabels?: string[]\n): Promise<IssueCreateResponse> {\n\treturn createIssue(title, body, { repo: repository, labels })\n}\n\n// GitHub Comment Operations\n\ninterface CommentResponse {\n\tid: number\n\turl: string\n\tcreated_at?: string\n\tupdated_at?: string\n}\n\ninterface RepoInfo {\n\towner: string\n\tname: string\n}\n\n/**\n * Create a comment on a GitHub issue\n * @param issueNumber - The issue number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createIssueComment(\n\tissueNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating issue comment', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/comments`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Update an existing GitHub comment\n * @param commentId - The comment ID\n * @param body - The updated comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Updated comment metadata\n */\nexport async function updateIssueComment(\n\tcommentId: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Updating issue comment', { commentId, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/comments/${commentId}`\n\t\t: `repos/:owner/:repo/issues/comments/${commentId}`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-X',\n\t\t'PATCH',\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, updated_at: .updated_at}',\n\t])\n}\n\n/**\n * Create a comment on a GitHub pull request\n * Note: PR comments use the same endpoint as issue comments\n * @param prNumber - The PR number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createPRComment(\n\tprNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating PR comment', { prNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${prNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${prNumber}/comments`\n\n\t// PR comments use the issues endpoint\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Get repository owner and name from current directory\n * @returns Repository owner and name\n */\nexport async function getRepoInfo(): Promise<RepoInfo> {\n\tlogger.debug('Fetching repository info')\n\n\tconst result = await executeGhCommand<{ owner: { login: string }; name: string }>([\n\t\t'repo',\n\t\t'view',\n\t\t'--json',\n\t\t'owner,name',\n\t])\n\n\treturn {\n\t\towner: result.owner.login,\n\t\tname: result.name,\n\t}\n}\n\n// GitHub Sub-Issue Operations\n\n/**\n * Get the GraphQL node ID for a GitHub issue\n * Required for sub-issue API which uses node IDs, not issue numbers\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns GraphQL node ID (e.g., \"I_kwDOPvp_cc7...\")\n */\nexport async function getIssueNodeId(\n\tissueNumber: number,\n\trepo?: string\n): Promise<string> {\n\tlogger.debug('Fetching GitHub issue node ID', { issueNumber, repo })\n\n\tconst args = ['issue', 'view', String(issueNumber), '--json', 'id']\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tconst result = await executeGhCommand<{ id: string }>(args)\n\treturn result.id\n}\n\n/**\n * Link a child issue to a parent issue using GitHub's sub-issue API\n * Requires GraphQL-Features: sub_issues header\n * @param parentNodeId - GraphQL node ID of the parent issue\n * @param childNodeId - GraphQL node ID of the child issue\n */\nexport async function addSubIssue(\n\tparentNodeId: string,\n\tchildNodeId: string\n): Promise<void> {\n\tlogger.debug('Linking child issue to parent', { parentNodeId, childNodeId })\n\n\tconst mutation = `\n\t\tmutation addSubIssue($parentId: ID!, $subIssueId: ID!) {\n\t\t\taddSubIssue(input: { issueId: $parentId, subIssueId: $subIssueId }) {\n\t\t\t\tissue { id }\n\t\t\t\tsubIssue { id }\n\t\t\t}\n\t\t}\n\t`\n\n\tawait executeGhCommand([\n\t\t'api', 'graphql',\n\t\t'-H', 'GraphQL-Features: sub_issues',\n\t\t'-f', `query=${mutation}`,\n\t\t'-F', `parentId=${parentNodeId}`,\n\t\t'-F', `subIssueId=${childNodeId}`,\n\t])\n}\n\n/**\n * Get sub-issues (children) of a parent GitHub issue\n * Uses GraphQL to query the sub-issue relationship\n * @param issueNumber - The parent issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Array of child issues with id, title, url, and state\n */\nexport async function getSubIssues(\n\tissueNumber: number,\n\trepo?: string\n): Promise<Array<{ id: string; title: string; url: string; state: string }>> {\n\tlogger.debug('Fetching GitHub sub-issues', { issueNumber, repo })\n\n\t// Get the node ID for the parent issue\n\tconst parentNodeId = await getIssueNodeId(issueNumber, repo)\n\n\t// Query sub-issues using GraphQL\n\tconst query = `\n\t\tquery getSubIssues($parentId: ID!) {\n\t\t\tnode(id: $parentId) {\n\t\t\t\t... on Issue {\n\t\t\t\t\tsubIssues(first: 100) {\n\t\t\t\t\t\tnodes {\n\t\t\t\t\t\t\tnumber\n\t\t\t\t\t\t\ttitle\n\t\t\t\t\t\t\turl\n\t\t\t\t\t\t\tstate\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t`\n\n\tinterface SubIssueNode {\n\t\tnumber: number\n\t\ttitle: string\n\t\turl: string\n\t\tstate: string\n\t}\n\n\tinterface SubIssuesResponse {\n\t\tdata: {\n\t\t\tnode: {\n\t\t\t\tsubIssues: {\n\t\t\t\t\tnodes: SubIssueNode[]\n\t\t\t\t}\n\t\t\t} | null\n\t\t}\n\t}\n\n\ttry {\n\t\tconst result = await executeGhCommand<SubIssuesResponse>([\n\t\t\t'api', 'graphql',\n\t\t\t'-H', 'GraphQL-Features: sub_issues',\n\t\t\t'-f', `query=${query}`,\n\t\t\t'-F', `parentId=${parentNodeId}`,\n\t\t])\n\n\t\tconst subIssues = result.data.node?.subIssues?.nodes ?? []\n\n\t\treturn subIssues.map(issue => ({\n\t\t\tid: String(issue.number),\n\t\t\ttitle: issue.title,\n\t\t\turl: issue.url,\n\t\t\tstate: issue.state.toLowerCase(),\n\t\t}))\n\t} catch (error) {\n\t\t// Return empty array if sub-issues feature is not available or no children\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for feature not available or empty result\n\t\t\tif (combinedError.includes('sub_issues') || combinedError.includes('null')) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tthrow error\n\t}\n}\n\n// GitHub Issue Dependency Operations\n\n/**\n * GitHub dependency result from API\n */\ninterface GitHubDependency {\n\tid: number\n\tnumber: number\n\ttitle: string\n\tstate: string\n\thtml_url: string\n}\n\n/**\n * Get the internal database ID for a GitHub issue\n * Required for dependency API which uses database IDs, not node IDs\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Internal GitHub issue database ID\n */\nexport async function getIssueDatabaseId(\n\tissueNumber: number,\n\trepo?: string\n): Promise<number> {\n\tlogger.debug('Fetching GitHub issue database ID', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}`\n\n\tconst result = await executeGhCommand<{ id: number }>([\n\t\t'api',\n\t\tapiPath,\n\t\t'--jq',\n\t\t'{id: .id}',\n\t])\n\n\treturn result.id\n}\n\n/**\n * Get dependencies for a GitHub issue\n * Uses GitHub's issue dependencies API\n * @param issueNumber - The issue number\n * @param direction - 'blocking' for issues this blocks, 'blocked_by' for issues blocking this\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Array of dependency objects with id, title, url, state\n */\nexport async function getIssueDependencies(\n\tissueNumber: number,\n\tdirection: 'blocking' | 'blocked_by',\n\trepo?: string\n): Promise<Array<{ id: string; databaseId: number; title: string; url: string; state: string }>> {\n\tlogger.debug('Fetching GitHub issue dependencies', { issueNumber, direction, repo })\n\n\t// Use the dependencies API with the appropriate direction endpoint\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/dependencies/${direction}`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/dependencies/${direction}`\n\n\ttry {\n\t\tconst result = await executeGhCommand<GitHubDependency[]>([\n\t\t\t'api',\n\t\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\t\t'--jq', '.',\n\t\t\tapiPath,\n\t\t])\n\n\t\treturn (result ?? []).map(dep => ({\n\t\t\tid: String(dep.number),\n\t\t\tdatabaseId: dep.id,\n\t\t\ttitle: dep.title,\n\t\t\turl: dep.html_url,\n\t\t\tstate: dep.state,\n\t\t}))\n\t} catch (error) {\n\t\t// Return empty array for 404 on the dependencies endpoint\n\t\t// This indicates the issue exists but has no dependencies configured\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for 404 specifically on dependencies endpoint\n\t\t\tif (combinedError.includes('404') && combinedError.includes('dependencies')) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/**\n * Create a dependency between two issues (A blocks B)\n * Uses GitHub's issue dependencies API\n * @param blockedIssueNumber - The issue number that is blocked\n * @param blockingIssueDatabaseId - The database ID of the issue that blocks\n * @param repo - Optional repo in \"owner/repo\" format\n * @throws Error with specific message for: dependency already exists, issue not found, or dependencies feature not enabled\n */\nexport async function createIssueDependency(\n\tblockedIssueNumber: number,\n\tblockingIssueDatabaseId: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Creating GitHub issue dependency', { blockedIssueNumber, blockingIssueDatabaseId, repo })\n\n\t// POST to the blocked issue's blocked_by endpoint with the blocking issue's database ID\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by`\n\t\t: `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by`\n\n\ttry {\n\t\tawait executeGhCommand([\n\t\t\t'api',\n\t\t\t'-X', 'POST',\n\t\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\t\tapiPath,\n\t\t\t'-F', `issue_id=${blockingIssueDatabaseId}`,\n\t\t])\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for dependency already exists (422 Unprocessable Entity)\n\t\t\tif (combinedError.includes('422') || combinedError.includes('already exists') || combinedError.includes('Unprocessable Entity')) {\n\t\t\t\tthrow new Error(`Dependency already exists: issue #${blockedIssueNumber} is already blocked by the specified issue`)\n\t\t\t}\n\n\t\t\t// Check for issue not found (404)\n\t\t\tif (combinedError.includes('404') || combinedError.includes('Not Found')) {\n\t\t\t\tthrow new Error(`Issue not found: unable to create dependency for issue #${blockedIssueNumber}. The issue may not exist or you may not have access to it.`)\n\t\t\t}\n\n\t\t\t// Check for dependencies feature not enabled (403 or specific error message)\n\t\t\tif (combinedError.includes('403') || combinedError.includes('Forbidden') || combinedError.includes('not enabled')) {\n\t\t\t\tthrow new Error(`Dependencies feature not enabled: the repository may not have issue dependencies enabled. This feature requires GitHub Enterprise or specific repository settings.`)\n\t\t\t}\n\t\t}\n\n\t\t// Re-throw the original error if it doesn't match any known patterns\n\t\tthrow error\n\t}\n}\n\n/**\n * Remove a dependency between two issues (A blocks B)\n * Uses GitHub's issue dependencies API\n * @param blockedIssueNumber - The issue number that is blocked\n * @param blockingIssueDatabaseId - The database ID of the issue that blocks\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function removeIssueDependency(\n\tblockedIssueNumber: number,\n\tblockingIssueDatabaseId: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Removing GitHub issue dependency', { blockedIssueNumber, blockingIssueDatabaseId, repo })\n\n\t// DELETE from the blocked issue's blocked_by endpoint with the blocking issue's database ID\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}`\n\t\t: `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}`\n\n\tawait executeGhCommand([\n\t\t'api',\n\t\t'-X', 'DELETE',\n\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\tapiPath,\n\t])\n}\n\n// Issue State Operations\n\n/**\n * Close a GitHub issue\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function closeGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Closing GitHub issue', { issueNumber, repo })\n\n\tconst args = ['issue', 'close', String(issueNumber)]\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tawait executeGhCommand(args)\n}\n\n/**\n * Reopen a GitHub issue\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function reopenGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Reopening GitHub issue', { issueNumber, repo })\n\n\tconst args = ['issue', 'reopen', String(issueNumber)]\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tawait executeGhCommand(args)\n}\n\n/**\n * Edit a GitHub issue's properties\n * @param issueNumber - The issue number\n * @param options - Fields to update\n * @param options.title - New issue title\n * @param options.body - New issue body\n * @param options.labels - Labels to add to the issue\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function editGhIssue(\n\tissueNumber: number,\n\toptions: { title?: string; body?: string; labels?: string[] },\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Editing GitHub issue', { issueNumber, options, repo })\n\n\tconst args = ['issue', 'edit', String(issueNumber)]\n\n\tif (options.title !== undefined) {\n\t\targs.push('--title', options.title)\n\t}\n\tif (options.body !== undefined) {\n\t\targs.push('--body', options.body)\n\t}\n\tif (options.labels) {\n\t\t// Use --add-label for each label. gh issue edit replaces with comma-separated --add-label\n\t\tif (options.labels.length > 0) {\n\t\t\targs.push('--add-label', options.labels.join(','))\n\t\t}\n\t}\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tawait executeGhCommand(args)\n}\n\n// Issue List Operations (for il issues command)\n\nexport interface GitHubIssueListItem {\n\tid: string\n\ttitle: string\n\tupdatedAt: string\n\turl: string\n\tstate: string\n}\n\n/**\n * Fetch a list of open GitHub issues sorted by recently updated\n * @param options - Fetch options\n * @param options.limit - Maximum number of issues to return (default: 100)\n * @param options.cwd - Working directory for gh CLI (default: process.cwd())\n * @returns Array of issues\n */\nexport async function fetchGitHubIssueList(\n\toptions?: { limit?: number; cwd?: string; mine?: boolean }\n): Promise<GitHubIssueListItem[]> {\n\tconst limit = options?.limit ?? 100\n\n\tlogger.debug('Fetching GitHub issue list', { limit, cwd: options?.cwd, mine: options?.mine })\n\n\tconst args = [\n\t\t'issue',\n\t\t'list',\n\t\t'--state', 'open',\n\t\t'--json', 'number,title,updatedAt,url,state',\n\t\t'--limit', String(limit),\n\t\t'--search', 'sort:updated-desc',\n\t]\n\n\tif (options?.mine) {\n\t\targs.push('--assignee', '@me')\n\t}\n\n\tconst result = await executeGhCommand<Array<{\n\t\tnumber: number\n\t\ttitle: string\n\t\tupdatedAt: string\n\t\turl: string\n\t\tstate: string\n\t}>>(args, options?.cwd ? { cwd: options.cwd } : undefined)\n\n\treturn (result ?? []).map(item => ({\n\t\tid: String(item.number),\n\t\ttitle: item.title,\n\t\tupdatedAt: item.updatedAt,\n\t\turl: item.url,\n\t\tstate: item.state.toLowerCase(),\n\t}))\n}\n\n/**\n * Fetch a list of open, non-draft GitHub PRs sorted by recently updated\n * @param options - Fetch options\n * @param options.limit - Maximum number of PRs to return (default: 100)\n * @param options.cwd - Working directory for gh CLI (default: process.cwd())\n * @returns Array of PRs mapped to GitHubIssueListItem (with [PR] title prefix)\n */\nexport async function fetchGitHubPRList(\n\toptions?: { limit?: number; cwd?: string; mine?: boolean }\n): Promise<GitHubIssueListItem[]> {\n\tconst limit = options?.limit ?? 100\n\t// Over-fetch to account for draft PRs that will be filtered out client-side\n\t// gh pr list has no --draft=false flag\n\tconst fetchLimit = Math.max(limit * 2, 50)\n\n\tlogger.debug('Fetching GitHub PR list', { limit, fetchLimit, cwd: options?.cwd, mine: options?.mine })\n\n\tconst args = [\n\t\t'pr', 'list',\n\t\t'--state', 'open',\n\t\t'--json', 'number,title,updatedAt,url,state,isDraft',\n\t\t'--limit', String(fetchLimit),\n\t]\n\n\tif (options?.mine) {\n\t\targs.push('--assignee', '@me')\n\t}\n\n\tconst result = await executeGhCommand<Array<{\n\t\tnumber: number\n\t\ttitle: string\n\t\tupdatedAt: string\n\t\turl: string\n\t\tstate: string\n\t\tisDraft: boolean\n\t}>>(args, options?.cwd ? { cwd: options.cwd } : undefined)\n\n\treturn (result ?? [])\n\t\t.filter(item => !item.isDraft)\n\t\t.slice(0, limit)\n\t\t.map(item => ({\n\t\t\tid: String(item.number),\n\t\t\ttitle: `[PR] ${item.title}`,\n\t\t\tupdatedAt: item.updatedAt,\n\t\t\turl: item.url,\n\t\t\tstate: item.state.toLowerCase(),\n\t\t}))\n}\n","// Lines 1-5: Imports\nimport chalk, { Chalk } from 'chalk'\nimport { detectDarkMode, type ThemeMode } from './terminal.js'\n\n// Lines 7-17: Type definitions\nexport interface LoggerOptions {\n prefix?: string\n timestamp?: boolean\n silent?: boolean\n forceColor?: boolean | undefined | null\n debug?: boolean\n}\n\nexport interface Logger {\n info: (message: string, ...args: unknown[]) => void\n success: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n debug: (message: string, ...args: unknown[]) => void\n setDebug: (enabled: boolean) => void\n isDebugEnabled: () => boolean\n stdout: NodeJS.WriteStream // Stream for progress output (stdout normally, stderr in JSON mode)\n}\n\n// Lines 19-29: Stream-specific chalk instances\nconst stdoutChalk = new Chalk({ level: chalk.level })\nconst stderrChalk = new Chalk({ level: chalk.level })\n\n// Lines 31-60: Theme-aware color selection\nlet currentThemeMode: ThemeMode = 'light'\n\n/**\n * Initialize theme mode detection\n * This is called automatically on module load\n */\nasync function initializeThemeMode(): Promise<void> {\n try {\n currentThemeMode = await detectDarkMode()\n } catch {\n // Default to light mode on error\n currentThemeMode = 'light'\n }\n}\n\n// Initialize theme mode on module load (non-blocking)\nvoid initializeThemeMode()\n\n/**\n * Get color function based on current theme mode\n * Light mode uses standard colors, dark mode uses brighter/more saturated variants\n */\nfunction getInfoColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.cyan : chalkInstance.blue\n}\n\nfunction getSuccessColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.greenBright : chalkInstance.green\n}\n\nfunction getWarnColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.yellowBright : chalkInstance.yellow\n}\n\nfunction getErrorColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.redBright : chalkInstance.red\n}\n\nfunction getDebugColor(chalkInstance: InstanceType<typeof Chalk>): (str: string) => string {\n return currentThemeMode === 'dark' ? chalkInstance.gray : chalkInstance.gray\n}\n\n// Lines 31-45: Helper functions\nfunction formatMessage(message: string, ...args: unknown[]): string {\n // Convert args to strings and append to message\n const formattedArgs = args.map(arg =>\n typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)\n )\n return formattedArgs.length > 0 ? `${message} ${formattedArgs.join(' ')}` : message\n}\n\nfunction formatWithEmoji(message: string, emoji: string, colorFn: (str: string) => string): string {\n if (message.trim()) {\n return colorFn(`${emoji} ${message}`)\n } else {\n return ''\n }\n}\n\nlet globalDebugEnabled = false\n\n// Lines 47-96: Main logger implementation\n/* eslint-disable no-console */\nexport const logger: Logger = {\n info: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '🗂️ ', getInfoColor(stdoutChalk))\n console.log(output)\n },\n\n success: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '✅', getSuccessColor(stdoutChalk))\n console.log(output)\n },\n\n warn: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '⚠️ ', getWarnColor(stderrChalk))\n console.error(output)\n },\n\n error: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '❌', getErrorColor(stderrChalk))\n console.error(output)\n },\n\n debug: (message: string, ...args: unknown[]): void => {\n if (globalDebugEnabled) {\n const formatted = formatMessage(message, ...args)\n const output = formatWithEmoji(formatted, '🔍', getDebugColor(stdoutChalk))\n console.log(output)\n }\n },\n\n setDebug: (enabled: boolean): void => {\n globalDebugEnabled = enabled\n },\n\n isDebugEnabled: (): boolean => {\n return globalDebugEnabled\n },\n\n stdout: process.stdout\n}\n/* eslint-enable no-console */\n\n// Lines 98-145: Factory function for custom logger instances\nexport function createLogger(options: LoggerOptions = {}): Logger {\n const { prefix = '', timestamp = false, silent = false, forceColor, debug = globalDebugEnabled } = options\n\n // Local debug flag for this logger instance\n let localDebugEnabled = debug\n\n // Create chalk instances with forced color if needed\n const customStdoutChalk = forceColor !== undefined\n ? new Chalk({ level: forceColor ? 3 : 0 })\n : stdoutChalk\n const customStderrChalk = forceColor !== undefined\n ? new Chalk({ level: forceColor ? 3 : 0 })\n : stderrChalk\n\n const prefixStr = prefix ? `[${prefix}] ` : ''\n const getTimestamp = (): string => timestamp ? `[${new Date().toISOString()}] ` : ''\n\n if (silent) {\n // Return no-op logger when silent\n return {\n info: (): void => {},\n success: (): void => {},\n warn: (): void => {},\n error: (): void => {},\n debug: (): void => {},\n setDebug: (): void => {},\n isDebugEnabled: (): boolean => {\n return false\n },\n stdout: process.stdout\n }\n }\n\n /* eslint-disable no-console */\n return {\n info: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🗂️ ', getInfoColor(customStdoutChalk))\n console.log(output)\n },\n success: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '✅', getSuccessColor(customStdoutChalk))\n console.log(output)\n },\n warn: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '⚠️ ', getWarnColor(customStderrChalk))\n console.error(output)\n },\n error: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '❌', getErrorColor(customStderrChalk))\n console.error(output)\n },\n debug: (message: string, ...args: unknown[]): void => {\n if (localDebugEnabled) {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🔍', getDebugColor(customStdoutChalk))\n console.log(output)\n }\n },\n setDebug: (enabled: boolean): void => {\n localDebugEnabled = enabled\n },\n isDebugEnabled: (): boolean => {\n return globalDebugEnabled\n },\n stdout: process.stdout\n }\n /* eslint-enable no-console */\n}\n\n// Lines 147-200: Factory function for stderr-only logger (for JSON mode)\n/**\n * Creates a logger that redirects all output to stderr.\n * Use this in JSON mode so progress messages don't pollute stdout.\n * The JSON output can then be cleanly piped.\n */\nexport function createStderrLogger(options: LoggerOptions = {}): Logger {\n const { prefix = '', timestamp = false, forceColor, debug = globalDebugEnabled } = options\n\n // Local debug flag for this logger instance\n let localDebugEnabled = debug\n\n // Create chalk instances with forced color if needed\n const customChalk = forceColor !== undefined\n ? new Chalk({ level: forceColor ? 3 : 0 })\n : stderrChalk\n\n const prefixStr = prefix ? `[${prefix}] ` : ''\n const getTimestamp = (): string => timestamp ? `[${new Date().toISOString()}] ` : ''\n\n return {\n info: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🗂️ ', getInfoColor(customChalk))\n console.error(output) // Redirect to stderr\n },\n success: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '✅', getSuccessColor(customChalk))\n console.error(output) // Redirect to stderr\n },\n warn: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '⚠️ ', getWarnColor(customChalk))\n console.error(output)\n },\n error: (message: string, ...args: unknown[]): void => {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '❌', getErrorColor(customChalk))\n console.error(output)\n },\n debug: (message: string, ...args: unknown[]): void => {\n if (localDebugEnabled) {\n const formatted = formatMessage(message, ...args)\n const fullMessage = `${getTimestamp()}${prefixStr}${formatted}`\n const output = formatWithEmoji(fullMessage, '🔍', getDebugColor(customChalk))\n console.error(output) // Redirect to stderr\n }\n },\n setDebug: (enabled: boolean): void => {\n localDebugEnabled = enabled\n },\n isDebugEnabled: (): boolean => {\n return globalDebugEnabled\n },\n stdout: process.stderr // Use stderr for progress output in JSON mode\n }\n}\n\n/**\n * Set the theme mode for logger colors\n * This is useful for testing or overriding auto-detection\n *\n * @param mode - Theme mode to use ('light' or 'dark')\n */\nexport function setThemeMode(mode: ThemeMode): void {\n currentThemeMode = mode\n}\n\n/**\n * Get the current theme mode\n *\n * @returns Current theme mode\n */\nexport function getThemeMode(): ThemeMode {\n return currentThemeMode\n}\n\n// Default export\nexport default logger\n","import { execa } from 'execa'\nimport { existsSync } from 'node:fs'\nimport type { Platform } from '../types/index.js'\nimport { buildEnvSourceCommands } from './env.js'\n\nexport interface TerminalWindowOptions {\n\tworkspacePath?: string\n\tcommand?: string\n\tbackgroundColor?: { r: number; g: number; b: number }\n\tport?: number\n\tincludeEnvSetup?: boolean // source .env\n\tincludePortExport?: boolean // export PORT=<port>\n\ttitle?: string // Terminal tab title\n}\n\n/**\n * Detect current platform\n */\nexport function detectPlatform(): Platform {\n\tconst platform = process.platform\n\tif (platform === 'darwin') return 'darwin'\n\tif (platform === 'linux') return 'linux'\n\tif (platform === 'win32') return 'win32'\n\treturn 'unsupported'\n}\n\n/**\n * Theme mode for color palette selection\n */\nexport type ThemeMode = 'light' | 'dark'\n\n/**\n * Detect macOS dark mode using defaults command\n * Returns 'light' as default for non-macOS platforms or detection failures\n *\n * Uses `defaults read -g AppleInterfaceStyle` which returns \"Dark\" in dark mode\n * and errors (exit code 1) in light mode. This approach doesn't require\n * System Events permission unlike AppleScript.\n */\nexport async function detectDarkMode(): Promise<ThemeMode> {\n\tconst platform = detectPlatform()\n\tif (platform !== 'darwin') {\n\t\treturn 'light'\n\t}\n\n\ttry {\n\t\tconst result = await execa('defaults', ['read', '-g', 'AppleInterfaceStyle'])\n\t\treturn result.stdout.trim().toLowerCase() === 'dark' ? 'dark' : 'light'\n\t} catch {\n\t\t// defaults command errors when AppleInterfaceStyle is not set (light mode)\n\t\treturn 'light'\n\t}\n}\n\n/**\n * Detect if iTerm2 is installed on macOS\n * Returns false on non-macOS platforms\n */\nexport async function detectITerm2(): Promise<boolean> {\n\tconst platform = detectPlatform()\n\tif (platform !== 'darwin') return false\n\n\t// Check if iTerm.app exists at standard location\n\treturn existsSync('/Applications/iTerm.app')\n}\n\n/**\n * Open new terminal window with specified options\n * Currently supports macOS only\n */\nexport async function openTerminalWindow(\n\toptions: TerminalWindowOptions\n): Promise<void> {\n\tconst platform = detectPlatform()\n\n\tif (platform !== 'darwin') {\n\t\tthrow new Error(\n\t\t\t`Terminal window launching not yet supported on ${platform}. ` +\n\t\t\t\t`Currently only macOS is supported.`\n\t\t)\n\t}\n\n\t// Detect if iTerm2 is available\n\tconst hasITerm2 = await detectITerm2()\n\n\t// Build appropriate AppleScript based on terminal availability\n\tconst applescript = hasITerm2\n\t\t? await buildITerm2SingleTabScript(options)\n\t\t: await buildAppleScript(options)\n\n\ttry {\n\t\tawait execa('osascript', ['-e', applescript])\n\n\t\t// Activate the appropriate terminal application (only needed for Terminal.app)\n\t\t// iTerm2 script includes its own activation\n\t\tif (!hasITerm2) {\n\t\t\tawait execa('osascript', ['-e', 'tell application \"Terminal\" to activate'])\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to open terminal window: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t)\n\t}\n}\n\n/**\n * Build AppleScript for macOS Terminal.app\n */\nasync function buildAppleScript(options: TerminalWindowOptions): Promise<string> {\n\tconst {\n\t\tworkspacePath,\n\t\tcommand,\n\t\tbackgroundColor,\n\t\tport,\n\t\tincludeEnvSetup,\n\t\tincludePortExport,\n\t} = options\n\n\t// Build command sequence\n\tconst commands: string[] = []\n\n\t// Navigate to workspace\n\tif (workspacePath) {\n\t\tcommands.push(`cd '${escapePathForAppleScript(workspacePath)}'`)\n\t}\n\n\t// Source all dotenv-flow files\n\tif (includeEnvSetup && workspacePath) {\n\t\tconst sourceCommands = await buildEnvSourceCommands(\n\t\t\tworkspacePath,\n\t\t\tasync (p) => existsSync(p)\n\t\t)\n\t\tcommands.push(...sourceCommands)\n\t}\n\n\t// Export PORT variable\n\tif (includePortExport && port !== undefined) {\n\t\tcommands.push(`export PORT=${port}`)\n\t}\n\n\t// Add custom command\n\tif (command) {\n\t\tcommands.push(command)\n\t}\n\n\t// Join with &&\n\tconst fullCommand = commands.join(' && ')\n\n\t// Prefix with space to prevent shell history pollution\n\t// Most shells (bash/zsh) ignore commands starting with space when HISTCONTROL=ignorespace\n\tconst historyFreeCommand = ` ${fullCommand}`\n\n\t// Build AppleScript\n\tlet script = `tell application \"Terminal\"\\n`\n\tscript += ` set newTab to do script \"${escapeForAppleScript(historyFreeCommand)}\"\\n`\n\n\t// Apply background color if provided\n\tif (backgroundColor) {\n\t\tconst { r, g, b } = backgroundColor\n\t\t// Convert 8-bit RGB (0-255) to 16-bit RGB (0-65535)\n\t\tscript += ` set background color of newTab to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\tscript += `end tell`\n\n\treturn script\n}\n\n/**\n * Escape path for AppleScript string\n * Single quotes in path need special escaping\n */\nfunction escapePathForAppleScript(path: string): string {\n\t// Replace single quote with '\\''\n\treturn path.replace(/'/g, \"'\\\\''\")\n}\n\n/**\n * Escape command for AppleScript do script\n * Must handle double quotes and backslashes\n */\nfunction escapeForAppleScript(command: string): string {\n\treturn (\n\t\tcommand\n\t\t\t.replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n\t\t\t.replace(/\"/g, '\\\\\"') // Escape double quotes\n\t)\n}\n\n/**\n * Build iTerm2 AppleScript for single tab\n */\nasync function buildITerm2SingleTabScript(options: TerminalWindowOptions): Promise<string> {\n\tconst command = await buildCommandSequence(options)\n\n\tlet script = 'tell application id \"com.googlecode.iterm2\"\\n'\n\tscript += ' create window with default profile\\n'\n\tscript += ' set s1 to current session of current window\\n\\n'\n\n\t// Set background color\n\tif (options.backgroundColor) {\n\t\tconst { r, g, b } = options.backgroundColor\n\t\tscript += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\t// Execute command\n\tscript += ` tell s1 to write text \"${escapeForAppleScript(command)}\"\\n\\n`\n\n\t// Set session name (tab title)\n\tif (options.title) {\n\t\tscript += ` set name of s1 to \"${escapeForAppleScript(options.title)}\"\\n\\n`\n\t}\n\n\t// Activate iTerm2\n\tscript += ' activate\\n'\n\tscript += 'end tell'\n\n\treturn script\n}\n\n/**\n * Build command sequence for terminal\n */\nasync function buildCommandSequence(options: TerminalWindowOptions): Promise<string> {\n\tconst {\n\t\tworkspacePath,\n\t\tcommand,\n\t\tport,\n\t\tincludeEnvSetup,\n\t\tincludePortExport,\n\t} = options\n\n\tconst commands: string[] = []\n\n\t// Navigate to workspace\n\tif (workspacePath) {\n\t\tcommands.push(`cd '${escapePathForAppleScript(workspacePath)}'`)\n\t}\n\n\t// Source all dotenv-flow files\n\tif (includeEnvSetup && workspacePath) {\n\t\tconst sourceCommands = await buildEnvSourceCommands(\n\t\t\tworkspacePath,\n\t\t\tasync (p) => existsSync(p)\n\t\t)\n\t\tcommands.push(...sourceCommands)\n\t}\n\n\t// Export PORT variable\n\tif (includePortExport && port !== undefined) {\n\t\tcommands.push(`export PORT=${port}`)\n\t}\n\n\t// Add custom command\n\tif (command) {\n\t\tcommands.push(command)\n\t}\n\n\t// Join with &&\n\tconst fullCommand = commands.join(' && ')\n\n\t// Prefix with space to prevent shell history pollution\n\treturn ` ${fullCommand}`\n}\n\n/**\n * Build iTerm2 AppleScript for multiple tabs (2+) in single window\n */\nasync function buildITerm2MultiTabScript(\n\toptionsArray: TerminalWindowOptions[]\n): Promise<string> {\n\tif (optionsArray.length < 2) {\n\t\tthrow new Error('buildITerm2MultiTabScript requires at least 2 terminal options')\n\t}\n\n\tlet script = 'tell application id \"com.googlecode.iterm2\"\\n'\n\tscript += ' create window with default profile\\n'\n\tscript += ' set newWindow to current window\\n'\n\n\t// First tab\n\tconst options1 = optionsArray[0]\n\tif (!options1) {\n\t\tthrow new Error('First terminal option is undefined')\n\t}\n\tconst command1 = await buildCommandSequence(options1)\n\n\tscript += ' set s1 to current session of newWindow\\n\\n'\n\n\t// Set background color for first tab\n\tif (options1.backgroundColor) {\n\t\tconst { r, g, b } = options1.backgroundColor\n\t\tscript += ` set background color of s1 to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t}\n\n\t// Execute command in first tab\n\tscript += ` tell s1 to write text \"${escapeForAppleScript(command1)}\"\\n\\n`\n\n\t// Set tab title for first tab\n\tif (options1.title) {\n\t\tscript += ` set name of s1 to \"${escapeForAppleScript(options1.title)}\"\\n\\n`\n\t}\n\n\t// Subsequent tabs (2, 3, ...)\n\tfor (let i = 1; i < optionsArray.length; i++) {\n\t\tconst options = optionsArray[i]\n\t\tif (!options) {\n\t\t\tthrow new Error(`Terminal option at index ${i} is undefined`)\n\t\t}\n\t\tconst command = await buildCommandSequence(options)\n\t\tconst sessionVar = `s${i + 1}`\n\n\t\t// Create tab\n\t\tscript += ' tell newWindow\\n'\n\t\tscript += ` set newTab${i} to (create tab with default profile)\\n`\n\t\tscript += ' end tell\\n'\n\t\tscript += ` set ${sessionVar} to current session of newTab${i}\\n\\n`\n\n\t\t// Set background color\n\t\tif (options.backgroundColor) {\n\t\t\tconst { r, g, b } = options.backgroundColor\n\t\t\tscript += ` set background color of ${sessionVar} to {${Math.round(r * 257)}, ${Math.round(g * 257)}, ${Math.round(b * 257)}}\\n`\n\t\t}\n\n\t\t// Execute command\n\t\tscript += ` tell ${sessionVar} to write text \"${escapeForAppleScript(command)}\"\\n\\n`\n\n\t\t// Set tab title\n\t\tif (options.title) {\n\t\t\tscript += ` set name of ${sessionVar} to \"${escapeForAppleScript(options.title)}\"\\n\\n`\n\t\t}\n\t}\n\n\t// Activate iTerm2\n\tscript += ' activate\\n'\n\tscript += 'end tell'\n\n\treturn script\n}\n\n/**\n * Open multiple terminal windows/tabs (2+) with specified options\n * If iTerm2 is available on macOS, creates single window with multiple tabs\n * Otherwise falls back to multiple separate Terminal.app windows\n */\nexport async function openMultipleTerminalWindows(\n\toptionsArray: TerminalWindowOptions[]\n): Promise<void> {\n\tif (optionsArray.length < 2) {\n\t\tthrow new Error('openMultipleTerminalWindows requires at least 2 terminal options. Use openTerminalWindow for single terminal.')\n\t}\n\n\tconst platform = detectPlatform()\n\n\tif (platform !== 'darwin') {\n\t\tthrow new Error(\n\t\t\t`Terminal window launching not yet supported on ${platform}. ` +\n\t\t\t\t`Currently only macOS is supported.`\n\t\t)\n\t}\n\n\t// Detect if iTerm2 is available\n\tconst hasITerm2 = await detectITerm2()\n\n\tif (hasITerm2) {\n\t\t// Use iTerm2 with multiple tabs in single window\n\t\tconst applescript = await buildITerm2MultiTabScript(optionsArray)\n\n\t\ttry {\n\t\t\tawait execa('osascript', ['-e', applescript])\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to open iTerm2 window: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t} else {\n\t\t// Fall back to multiple Terminal.app windows\n\t\tfor (let i = 0; i < optionsArray.length; i++) {\n\t\t\tconst options = optionsArray[i]\n\t\t\tif (!options) {\n\t\t\t\tthrow new Error(`Terminal option at index ${i} is undefined`)\n\t\t\t}\n\t\t\tawait openTerminalWindow(options)\n\n\t\t\t// Brief pause between terminals (except after last one)\n\t\t\tif (i < optionsArray.length - 1) {\n\t\t\t\t// eslint-disable-next-line no-undef\n\t\t\t\tawait new Promise<void>((resolve) => setTimeout(resolve, 1000))\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Open dual terminal windows/tabs with specified options\n * If iTerm2 is available on macOS, creates single window with two tabs\n * Otherwise falls back to two separate Terminal.app windows\n */\nexport async function openDualTerminalWindow(\n\toptions1: TerminalWindowOptions,\n\toptions2: TerminalWindowOptions\n): Promise<void> {\n\t// Delegate to openMultipleTerminalWindows for consistency\n\tawait openMultipleTerminalWindows([options1, options2])\n}\n","import path from 'path'\nimport dotenvFlow, { type DotenvFlowConfigOptions } from 'dotenv-flow'\nimport { logger } from './logger.js'\n\n/**\n * Parse .env file content into key-value map\n * Handles comments, empty lines, quoted/unquoted values, multiline values\n */\nexport function parseEnvFile(content: string): Map<string, string> {\n const envMap = new Map<string, string>()\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const trimmedLine = line.trim()\n\n // Skip empty lines and comments\n if (!trimmedLine || trimmedLine.startsWith('#')) {\n continue\n }\n\n // Remove 'export ' prefix if present\n const cleanLine = trimmedLine.startsWith('export ')\n ? trimmedLine.substring(7)\n : trimmedLine\n\n // Find the first equals sign\n const equalsIndex = cleanLine.indexOf('=')\n if (equalsIndex === -1) {\n continue\n }\n\n const key = cleanLine.substring(0, equalsIndex).trim()\n let value = cleanLine.substring(equalsIndex + 1)\n\n // Handle quoted values\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.substring(1, value.length - 1)\n // Unescape quotes\n value = value.replace(/\\\\\"/g, '\"').replace(/\\\\'/g, \"'\")\n // Unescape newlines\n value = value.replace(/\\\\n/g, '\\n')\n }\n\n if (key) {\n envMap.set(key, value)\n }\n }\n\n return envMap\n}\n\n/**\n * Format environment variable as line for .env file\n * Always quotes values and escapes internal quotes\n */\nexport function formatEnvLine(key: string, value: string): string {\n // Escape quotes and newlines in the value\n const escapedValue = value\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n\n return `${key}=\"${escapedValue}\"`\n}\n\n/**\n * Validate environment variable name and value\n */\nexport function validateEnvVariable(\n key: string,\n _value?: string\n): { valid: boolean; error?: string } {\n if (!key || key.length === 0) {\n return {\n valid: false,\n error: 'Environment variable key cannot be empty',\n }\n }\n\n if (!isValidEnvKey(key)) {\n return {\n valid: false,\n error: `Invalid environment variable name: ${key}. Must start with a letter or underscore and contain only letters, numbers, and underscores.`,\n }\n }\n\n // Values can be any string, including empty\n return { valid: true }\n}\n\n/**\n * Normalize line endings for cross-platform compatibility\n */\nexport function normalizeLineEndings(content: string): string {\n return content.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n}\n\n/**\n * Extract port from .env file if present\n */\nexport function extractPort(envContent: Map<string, string>): number | null {\n const portValue = envContent.get('PORT')\n if (!portValue) {\n return null\n }\n\n const port = parseInt(portValue, 10)\n if (isNaN(port)) {\n return null\n }\n\n return port\n}\n\n/**\n * Check if environment variable key is valid\n */\nexport function isValidEnvKey(key: string): boolean {\n if (!key || key.length === 0) {\n return false\n }\n\n // Must start with letter or underscore, followed by letters, numbers, or underscores\n const validKeyRegex = /^[A-Za-z_][A-Za-z0-9_]*$/\n return validKeyRegex.test(key)\n}\n\n/**\n * Load environment variables using dotenv-flow\n * Supports environment-specific files (.env.development, .env.production, etc.)\n * and local overrides (.env.local, .env.development.local)\n */\nexport function loadEnvIntoProcess(options?: {\n path?: string\n nodeEnv?: string\n defaultNodeEnv?: string\n}): { parsed?: Record<string, string>; error?: Error } {\n logger.debug('Loading environment variables with dotenv-flow', {\n options: {\n path: options?.path ?? 'current working directory',\n nodeEnv: options?.nodeEnv ?? 'not specified',\n defaultNodeEnv: options?.defaultNodeEnv ?? 'development (default)'\n }\n })\n\n const configOptions: Partial<DotenvFlowConfigOptions> = {\n silent: true, // Don't throw errors if .env files are missing\n }\n\n // Only add defined values to avoid TypeScript strict type issues\n if (options?.path !== undefined) {\n configOptions.path = options.path\n logger.debug(`Using custom path: ${options.path}`)\n }\n if (options?.nodeEnv !== undefined) {\n configOptions.node_env = options.nodeEnv\n logger.debug(`Using NODE_ENV: ${options.nodeEnv}`)\n }\n if (options?.defaultNodeEnv !== undefined) {\n configOptions.default_node_env = options.defaultNodeEnv\n logger.debug(`Using default NODE_ENV: ${options.defaultNodeEnv}`)\n } else {\n configOptions.default_node_env = 'development'\n logger.debug('Using default NODE_ENV: development')\n }\n\n logger.debug('dotenv-flow config options:', configOptions)\n\n const result = dotenvFlow.config(configOptions)\n\n const returnValue: { parsed?: Record<string, string>; error?: Error } = {}\n\n if (result.parsed) {\n returnValue.parsed = result.parsed as Record<string, string>\n const variableCount = Object.keys(result.parsed).length\n logger.debug(`Successfully loaded ${variableCount} environment variables`)\n } else {\n logger.debug('No environment variables were parsed')\n }\n\n if (result.error) {\n returnValue.error = result.error\n logger.debug('dotenv-flow returned an error', {\n error: result.error.message,\n name: result.error.name\n })\n } else {\n logger.debug('dotenv-flow completed without errors')\n }\n\n return returnValue\n}\n\n/**\n * Check if an error from loadEnvIntoProcess indicates no .env files were found\n * This is a harmless condition that shouldn't be logged as a warning\n */\nexport function isNoEnvFilesFoundError(error: Error): boolean {\n return error.message.startsWith('no \".env*\" files matching pattern')\n}\n\n/**\n * Load environment variables for a specific workspace\n * Automatically determines environment based on NODE_ENV or defaults to development\n */\nexport function loadWorkspaceEnv(workspacePath: string): {\n parsed?: Record<string, string>\n error?: Error\n} {\n const nodeEnv = process.env.NODE_ENV ?? 'development'\n\n logger.debug('Loading workspace environment variables', {\n workspacePath,\n detectedNodeEnv: nodeEnv,\n processNodeEnv: process.env.NODE_ENV ?? 'not set'\n })\n\n return loadEnvIntoProcess({\n path: workspacePath,\n nodeEnv: nodeEnv,\n defaultNodeEnv: 'development'\n })\n}\n\n// CONSTANT: Always use 'development' per critical constraint, unless overridden\nconst DOTENV_FLOW_NODE_ENV = process.env.DOTENV_FLOW_NODE_ENV ?? 'development'\n\n/**\n * Get dotenv-flow files in precedence order (lowest to highest)\n * Always uses 'development' as NODE_ENV per constraint\n */\nexport function getDotenvFlowFiles(): string[] {\n return [\n '.env',\n '.env.local',\n `.env.${DOTENV_FLOW_NODE_ENV}`,\n `.env.${DOTENV_FLOW_NODE_ENV}.local`\n ]\n}\n\n/**\n * Map a file to its \"local\" equivalent for git-safe writes\n * .env -> .env.local\n * .env.{NODE_ENV} -> .env.{NODE_ENV}.local\n * Already local files return unchanged\n */\nexport function getLocalEquivalent(filename: string): string {\n // Already a .local file\n if (filename.endsWith('.local')) {\n return filename\n }\n return `${filename}.local`\n}\n\n/**\n * Find the appropriate env file to write a database URL variable to\n * Considers dotenv-flow precedence and git tracking status\n * Returns path relative to workspacePath\n *\n * Algorithm:\n * 1. Search files in reverse precedence order (highest first)\n * 2. Find first file containing the variable\n * 3. If tracked by git, return its .local equivalent\n * 4. If not tracked, return the file itself\n * 5. If not found anywhere, return '.env.local' (safe default)\n */\nexport async function findEnvFileForDatabaseUrl(\n workspacePath: string,\n variableName: string,\n isFileTracked: (filePath: string, cwd: string) => Promise<boolean>,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<string> {\n // Find the highest-precedence file containing the variable\n const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable)\n\n if (file === null) {\n // Variable not found anywhere - use safe default\n return '.env.local'\n }\n\n // Found the variable - check git tracking\n const isTracked = await isFileTracked(file, workspacePath)\n if (isTracked) {\n // Return .local equivalent for git safety\n return getLocalEquivalent(file)\n }\n\n return file\n}\n\n/**\n * Build shell source commands for all existing dotenv-flow files\n * Returns commands in precedence order (later overrides earlier)\n */\nexport async function buildEnvSourceCommands(\n workspacePath: string,\n fileExists: (filePath: string) => Promise<boolean>\n): Promise<string[]> {\n const files = getDotenvFlowFiles()\n const commands: string[] = []\n\n for (const file of files) {\n const fullPath = path.join(workspacePath, file)\n const exists = await fileExists(fullPath)\n if (exists) {\n commands.push(`source ${file}`)\n }\n }\n\n return commands\n}\n\n/**\n * Find the highest-precedence env file containing a variable\n * Searches all dotenv-flow files in reverse precedence order (highest first)\n * Returns the relative filename if found, null otherwise\n */\nexport async function findEnvFileContainingVariable(\n workspacePath: string,\n variableName: string,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<string | null> {\n const files = getDotenvFlowFiles().reverse() // highest precedence first\n\n for (const file of files) {\n const fullPath = path.join(workspacePath, file)\n\n // Skip if file doesn't exist\n if (!(await fileExists(fullPath))) {\n continue\n }\n\n // Check if file contains the variable\n const value = await getEnvVariable(fullPath, variableName)\n if (value !== null) {\n return file\n }\n }\n\n return null\n}\n\n/**\n * Check if a variable exists in any dotenv-flow file\n * Searches all dotenv-flow files (.env, .env.local, .env.{NODE_ENV}, .env.{NODE_ENV}.local)\n * Returns true if variable is found in any file, false otherwise\n */\nexport async function hasVariableInAnyEnvFile(\n workspacePath: string,\n variableName: string,\n fileExists: (filePath: string) => Promise<boolean>,\n getEnvVariable: (filePath: string, varName: string) => Promise<string | null>\n): Promise<boolean> {\n const file = await findEnvFileContainingVariable(workspacePath, variableName, fileExists, getEnvVariable)\n return file !== null\n}\n","/* global fetch, AbortController, setTimeout, clearTimeout */\nimport { tmpdir } from 'node:os'\nimport { join, extname } from 'node:path'\nimport { existsSync, mkdirSync, createWriteStream, unlinkSync } from 'node:fs'\nimport { pipeline } from 'node:stream/promises'\nimport { Readable } from 'node:stream'\nimport { createHash } from 'node:crypto'\nimport { execa } from 'execa'\nimport { logger } from './logger.js'\nimport type { IssueProvider } from '../mcp/types.js'\n\n/**\n * Represents a matched image in markdown content\n */\nexport interface ImageMatch {\n fullMatch: string\n url: string\n isMarkdown: boolean // true for ![](url), false for <img>\n}\n\n/**\n * Supported image extensions\n */\nconst SUPPORTED_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg']\n\n/**\n * Maximum allowed image size (10MB)\n */\nconst MAX_IMAGE_SIZE = 10 * 1024 * 1024\n\n/**\n * Request timeout in milliseconds (30 seconds)\n */\nconst REQUEST_TIMEOUT_MS = 30000\n\n/**\n * Cache directory path for downloaded images\n */\nexport const CACHE_DIR = join(tmpdir(), 'iloom-images')\n\n/**\n * Cached GitHub auth token (module-level to avoid repeated `gh auth token` calls)\n */\nlet cachedGitHubToken: string | undefined\n\n/**\n * Extract all image URLs from markdown content\n * Handles both ![alt](url) and <img src=\"url\"> formats\n *\n * @param content - Markdown content to parse\n * @returns Array of image matches with full match string and URL\n */\nexport function extractMarkdownImageUrls(content: string): ImageMatch[] {\n if (!content) {\n return []\n }\n\n const matches: ImageMatch[] = []\n\n // Regex for markdown images: ![alt](url)\n // Captures the entire match and the URL separately\n // Handles parentheses in URLs by matching balanced parens\n // The URL part matches: non-paren chars OR (balanced paren group)*, followed by non-paren/non-space chars\n const markdownRegex = /!\\[([^\\]]*)\\]\\(((?:[^()\\s]|\\((?:[^()\\s]|\\([^()]*\\))*\\))+)\\)/g\n let match: RegExpExecArray | null\n\n while ((match = markdownRegex.exec(content)) !== null) {\n const url = match[2]\n if (url) {\n matches.push({\n fullMatch: match[0],\n url,\n isMarkdown: true\n })\n }\n }\n\n // Regex for HTML img tags: <img ... src=\"url\" ...>\n // Handles both double and single quotes, and self-closing tags\n const htmlImgRegex = /<img\\s+[^>]*src=[\"']([^\"']+)[\"'][^>]*\\/?>/gi\n\n while ((match = htmlImgRegex.exec(content)) !== null) {\n const url = match[1]\n if (url) {\n matches.push({\n fullMatch: match[0],\n url,\n isMarkdown: false\n })\n }\n }\n\n return matches\n}\n\n/**\n * Check if URL requires authentication to download\n * - Linear: uploads.linear.app\n * - GitHub: private-user-images.githubusercontent.com\n *\n * @param url - Image URL to check\n * @returns true if URL requires authentication\n */\nexport function isAuthenticatedImageUrl(url: string): boolean {\n try {\n const parsedUrl = new URL(url)\n const hostname = parsedUrl.hostname.toLowerCase()\n\n // Linear uploads require authentication\n if (hostname === 'uploads.linear.app') {\n return true\n }\n\n // GitHub private user images require authentication\n if (hostname === 'private-user-images.githubusercontent.com') {\n return true\n }\n\n // GitHub user-attachments (uploaded images in issues/PRs) require authentication\n if (hostname === 'github.com' && parsedUrl.pathname.startsWith('/user-attachments/assets/')) {\n return true\n }\n\n return false\n } catch {\n // Invalid URL - treat as not authenticated\n return false\n }\n}\n\n/**\n * Get extension from URL pathname\n *\n * @param url - URL to extract extension from\n * @returns Extension including dot (e.g., '.png') or null if not found\n */\nfunction getExtensionFromUrl(url: string): string | null {\n try {\n const parsedUrl = new URL(url)\n const pathname = parsedUrl.pathname\n const ext = extname(pathname).toLowerCase()\n\n if (SUPPORTED_EXTENSIONS.includes(ext)) {\n return ext\n }\n return null\n } catch {\n return null\n }\n}\n\n/**\n * Generate cache key from URL\n * For GitHub URLs, strips JWT query params to ensure consistent caching\n * Returns hash + original extension\n *\n * @param url - Image URL to generate cache key for\n * @returns Cache key (hash + extension)\n */\nexport function getCacheKey(url: string): string {\n const parsedUrl = new URL(url)\n\n // For GitHub private images, remove jwt query param to get stable cache key\n // The jwt changes each fetch but the base URL is the same for the same image\n if (parsedUrl.hostname === 'private-user-images.githubusercontent.com') {\n parsedUrl.searchParams.delete('jwt')\n }\n\n // Get URL without volatile params for hashing\n const stableUrl = parsedUrl.toString()\n\n // Generate SHA256 hash of the stable URL (first 16 chars for brevity)\n const hash = createHash('sha256').update(stableUrl).digest('hex').slice(0, 16)\n\n // Extract extension from URL pathname, default to .png\n const ext = getExtensionFromUrl(url) ?? '.png'\n\n return `${hash}${ext}`\n}\n\n/**\n * Check if image is already cached\n * Returns file path if exists, undefined otherwise\n *\n * @param url - Image URL to check cache for\n * @returns Cached file path or undefined\n */\nexport function getCachedImagePath(url: string): string | undefined {\n const cacheKey = getCacheKey(url)\n const cachedPath = join(CACHE_DIR, cacheKey)\n\n if (existsSync(cachedPath)) {\n return cachedPath\n }\n return undefined\n}\n\n/**\n * Get authentication token for the given provider\n *\n * @param provider - Provider type ('github' or 'linear')\n * @returns Authentication token or undefined\n */\nasync function getAuthToken(provider: IssueProvider): Promise<string | undefined> {\n if (provider === 'github') {\n // Return cached token if available\n if (cachedGitHubToken !== undefined) {\n return cachedGitHubToken\n }\n\n try {\n // Execute `gh auth token` to get GitHub token\n const result = await execa('gh', ['auth', 'token'])\n cachedGitHubToken = result.stdout.trim()\n return cachedGitHubToken\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.warn(`Failed to get GitHub auth token via gh CLI: ${message}`)\n return undefined\n }\n }\n\n if (provider === 'linear') {\n // Linear token from environment variable\n return process.env.LINEAR_API_TOKEN\n }\n\n return undefined\n}\n\n/**\n * Clear the cached GitHub auth token (for testing purposes)\n */\nexport function clearCachedGitHubToken(): void {\n cachedGitHubToken = undefined\n}\n\n/**\n * Download image from URL and stream it directly to a file\n *\n * @param url - Image URL to download\n * @param destPath - Destination file path\n * @param authHeader - Optional Authorization header value\n * @throws Error if download fails, times out, or exceeds size limit\n */\nexport async function downloadAndSaveImage(\n url: string,\n destPath: string,\n authHeader?: string\n): Promise<void> {\n const headers: Record<string, string> = {}\n if (authHeader) {\n headers['Authorization'] = authHeader\n }\n\n // Set up abort controller for timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS)\n\n try {\n const response = await fetch(url, { headers, signal: controller.signal })\n\n if (!response.ok) {\n throw new Error(`Failed to download image: ${response.status} ${response.statusText}`)\n }\n\n // Check Content-Length header if available\n const contentLength = response.headers.get('Content-Length')\n if (contentLength && parseInt(contentLength, 10) > MAX_IMAGE_SIZE) {\n throw new Error(`Image too large: ${contentLength} bytes exceeds ${MAX_IMAGE_SIZE} byte limit`)\n }\n\n if (!response.body) {\n throw new Error('Response body is null')\n }\n\n // Convert ReadableStream to Node.js Readable\n const reader = response.body.getReader()\n let bytesWritten = 0\n\n const nodeReadable = new Readable({\n async read(): Promise<void> {\n try {\n const { done, value } = await reader.read()\n if (done) {\n this.push(null)\n return\n }\n\n bytesWritten += value.byteLength\n if (bytesWritten > MAX_IMAGE_SIZE) {\n reader.cancel()\n this.destroy(new Error(`Image too large: ${bytesWritten} bytes exceeds ${MAX_IMAGE_SIZE} byte limit`))\n return\n }\n\n this.push(Buffer.from(value))\n } catch (err) {\n this.destroy(err instanceof Error ? err : new Error(String(err)))\n }\n }\n })\n\n // Ensure cache directory exists\n if (!existsSync(CACHE_DIR)) {\n mkdirSync(CACHE_DIR, { recursive: true })\n }\n\n // Stream to file\n const writeStream = createWriteStream(destPath)\n\n try {\n await pipeline(nodeReadable, writeStream)\n } catch (pipelineError) {\n // Clean up partial file on error\n try {\n if (existsSync(destPath)) {\n unlinkSync(destPath)\n }\n } catch {\n // Ignore cleanup errors\n }\n throw pipelineError\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Image download timed out after ${REQUEST_TIMEOUT_MS}ms`)\n }\n throw error\n } finally {\n clearTimeout(timeoutId)\n }\n}\n\n/**\n * Get the destination path for caching an image\n *\n * @param url - Original image URL (used to generate cache key)\n * @returns Local file path where image should be saved\n */\nexport function getCacheDestPath(url: string): string {\n // Ensure cache directory exists\n if (!existsSync(CACHE_DIR)) {\n mkdirSync(CACHE_DIR, { recursive: true })\n }\n\n // Generate cache key from URL\n const cacheKey = getCacheKey(url)\n return join(CACHE_DIR, cacheKey)\n}\n\n/**\n * Rewrite image URLs in markdown content\n *\n * @param content - Original markdown content\n * @param urlMap - Map of original URLs to local file paths\n * @returns Content with URLs replaced\n */\nexport function rewriteMarkdownUrls(\n content: string,\n urlMap: Map<string, string>\n): string {\n let result = content\n\n for (const [originalUrl, localPath] of urlMap) {\n // Escape special regex characters in the URL\n const escapedUrl = originalUrl.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const urlRegex = new RegExp(escapedUrl, 'g')\n result = result.replace(urlRegex, localPath)\n }\n\n return result\n}\n\n/**\n * Main entry point: process all images in markdown content\n * Downloads authenticated images (with caching), saves locally, rewrites URLs\n *\n * @param content - Markdown content to process\n * @param provider - Image provider for authentication ('github' or 'linear')\n * @returns Content with authenticated image URLs replaced with local file paths\n */\nexport async function processMarkdownImages(\n content: string,\n provider: IssueProvider\n): Promise<string> {\n // Early return if empty\n if (!content) {\n return ''\n }\n\n // Extract all image URLs\n const images = extractMarkdownImageUrls(content)\n if (images.length === 0) {\n return content\n }\n\n // Filter to only authenticated URLs\n const authImages = images.filter(img => isAuthenticatedImageUrl(img.url))\n if (authImages.length === 0) {\n return content\n }\n\n // Get auth token for provider\n const authToken = await getAuthToken(provider)\n\n // Deduplicate URLs (same image might appear multiple times)\n const uniqueUrls = [...new Set(authImages.map(img => img.url))]\n\n // Build URL map - process all unique URLs in parallel\n const urlMap = new Map<string, string>()\n\n // Download/cache images in parallel\n const downloadPromises = uniqueUrls.map(async (url) => {\n try {\n // Check cache first\n const cachedPath = getCachedImagePath(url)\n if (cachedPath) {\n logger.debug(`Using cached image: ${cachedPath}`)\n return { url, localPath: cachedPath }\n }\n\n // Cache miss - download and stream directly to file\n logger.debug(`Downloading image: ${url}`)\n const destPath = getCacheDestPath(url)\n await downloadAndSaveImage(\n url,\n destPath,\n authToken ? `Bearer ${authToken}` : undefined\n )\n return { url, localPath: destPath }\n } catch (error) {\n // Graceful degradation - log warning, return null to keep original URL\n const message = error instanceof Error ? error.message : String(error)\n logger.warn(`Failed to download image ${url}: ${message}`)\n return null\n }\n })\n\n const results = await Promise.all(downloadPromises)\n\n // Build URL map from results\n for (const result of results) {\n if (result !== null) {\n urlMap.set(result.url, result.localPath)\n }\n }\n\n // Rewrite and return\n return rewriteMarkdownUrls(content, urlMap)\n}\n","/**\n * GitHub implementation of Issue Management Provider\n * Uses GitHub CLI for all operations\n * Normalizes GitHub-specific fields (login) to provider-agnostic core fields (id, displayName)\n */\n\nimport type {\n\tIssueManagementProvider,\n\tGetIssueInput,\n\tGetPRInput,\n\tGetReviewCommentsInput,\n\tGetCommentInput,\n\tCreateCommentInput,\n\tUpdateCommentInput,\n\tCreateIssueInput,\n\tCreateChildIssueInput,\n\tCreateDependencyInput,\n\tGetDependenciesInput,\n\tRemoveDependencyInput,\n\tGetChildIssuesInput,\n\tCloseIssueInput,\n\tReopenIssueInput,\n\tEditIssueInput,\n\tCreateIssueResult,\n\tIssueResult,\n\tPRResult,\n\tReviewCommentResult,\n\tCommentDetailResult,\n\tCommentResult,\n\tDependenciesResult,\n\tChildIssueResult,\n\tFlexibleAuthor,\n} from './types.js'\nimport {\n\texecuteGhCommand,\n\tcreateIssueComment,\n\tupdateIssueComment,\n\tcreatePRComment,\n\tcreateIssue,\n\tgetIssueNodeId,\n\taddSubIssue,\n\tgetIssueDatabaseId,\n\tgetIssueDependencies,\n\tcreateIssueDependency,\n\tremoveIssueDependency,\n\tgetSubIssues,\n\tcloseGhIssue,\n\treopenGhIssue,\n\teditGhIssue,\n} from '../utils/github.js'\nimport { processMarkdownImages } from '../utils/image-processor.js'\n\n/**\n * GitHub-specific author structure from API\n */\ninterface GitHubAuthor {\n\tlogin: string\n\tid?: number\n\tavatarUrl?: string\n\turl?: string\n}\n\n/**\n * Normalize GitHub author to FlexibleAuthor format\n */\nfunction normalizeAuthor(author: GitHubAuthor | null | undefined): FlexibleAuthor | null {\n\tif (!author) return null\n\n\treturn {\n\t\tid: author.id ? String(author.id) : author.login,\n\t\tdisplayName: author.login, // GitHub uses login as primary identifier\n\t\tlogin: author.login, // Preserve original GitHub field\n\t\t...(author.avatarUrl && { avatarUrl: author.avatarUrl }),\n\t\t...(author.url && { url: author.url }),\n\t}\n}\n\n/**\n * Extract numeric comment ID from GitHub comment URL\n * URL format: https://github.com/owner/repo/issues/123#issuecomment-3615239386\n */\nexport function extractNumericIdFromUrl(url: string): string {\n\tconst match = url.match(/#issuecomment-(\\d+)$/)\n\tif (!match?.[1]) {\n\t\tthrow new Error(`Cannot extract comment ID from URL: ${url}`)\n\t}\n\treturn match[1]\n}\n\n/**\n * GitHub-specific implementation of IssueManagementProvider\n */\nexport class GitHubIssueManagementProvider implements IssueManagementProvider {\n\treadonly providerName = 'github'\n\treadonly issuePrefix = '#'\n\n\t/**\n\t * Fetch issue details using gh CLI\n\t * Normalizes GitHub-specific fields to provider-agnostic format\n\t */\n\tasync getIssue(input: GetIssueInput): Promise<IssueResult> {\n\t\tconst { number, includeComments = true, repo } = input\n\n\t\t// Convert string ID to number for GitHub CLI\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Build fields list based on whether we need comments\n\t\tconst fields = includeComments\n\t\t\t? 'body,title,comments,labels,assignees,milestone,author,state,number,url'\n\t\t\t: 'body,title,labels,assignees,milestone,author,state,number,url'\n\n\t\t// Use gh issue view to fetch issue details\n\t\tinterface GitHubIssueResponse {\n\t\t\tnumber: number\n\t\t\ttitle: string\n\t\t\tbody: string\n\t\t\tstate: string\n\t\t\turl: string\n\t\t\tauthor?: GitHubAuthor\n\t\t\tlabels?: Array<{ name: string; color?: string; description?: string }>\n\t\t\tassignees?: Array<GitHubAuthor>\n\t\t\tmilestone?: { title: string; number?: number; state?: string }\n\t\t\tcomments?: Array<{\n\t\t\t\tid: number\n\t\t\t\tauthor: GitHubAuthor\n\t\t\t\tbody: string\n\t\t\t\tcreatedAt: string\n\t\t\t\tupdatedAt?: string\n\t\t\t\turl: string\n\t\t\t}>\n\t\t}\n\n\t\tconst args = [\n\t\t\t'issue',\n\t\t\t'view',\n\t\t\tString(issueNumber),\n\t\t\t'--json',\n\t\t\tfields,\n\t\t]\n\n\t\t// Add --repo flag if repo is provided (gh CLI handles both owner/repo and URL formats)\n\t\tif (repo) {\n\t\t\targs.push('--repo', repo)\n\t\t}\n\n\t\tconst raw = await executeGhCommand<GitHubIssueResponse>(args)\n\n\t\t// Normalize to IssueResult with core fields + passthrough\n\t\tconst result: IssueResult = {\n\t\t\t// Core fields\n\t\t\tid: String(raw.number),\n\t\t\ttitle: raw.title,\n\t\t\tbody: raw.body,\n\t\t\tstate: raw.state,\n\t\t\turl: raw.url,\n\t\t\tprovider: 'github',\n\n\t\t\t// Normalized author\n\t\t\tauthor: normalizeAuthor(raw.author),\n\n\t\t\t// Optional flexible fields\n\t\t\t...(raw.assignees && {\n\t\t\t\tassignees: raw.assignees.map(a => normalizeAuthor(a)).filter((a): a is FlexibleAuthor => a !== null),\n\t\t\t}),\n\t\t\t...(raw.labels && {\n\t\t\t\tlabels: raw.labels,\n\t\t\t}),\n\n\t\t\t// GitHub-specific passthrough fields\n\t\t\t...(raw.milestone && {\n\t\t\t\tmilestone: raw.milestone,\n\t\t\t}),\n\t\t}\n\n\t\t// Handle comments with normalized authors\n\t\t// Use extractNumericIdFromUrl to get REST API-compatible numeric IDs from comment URLs\n\t\t// (GitHub CLI returns GraphQL node IDs in the id field, but REST API expects numeric IDs)\n\t\tif (raw.comments !== undefined) {\n\t\t\tresult.comments = raw.comments.map(comment => ({\n\t\t\t\tid: extractNumericIdFromUrl(comment.url),\n\t\t\t\tbody: comment.body,\n\t\t\t\tcreatedAt: comment.createdAt,\n\t\t\t\tauthor: normalizeAuthor(comment.author),\n\t\t\t\t...(comment.updatedAt && { updatedAt: comment.updatedAt }),\n\t\t\t}))\n\t\t}\n\n\t\t// Process authenticated images in body and comments\n\t\tresult.body = await processMarkdownImages(result.body, 'github')\n\t\tif (result.comments) {\n\t\t\tfor (const comment of result.comments) {\n\t\t\t\tcomment.body = await processMarkdownImages(comment.body, 'github')\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch pull request details using gh CLI\n\t * Normalizes GitHub-specific fields to provider-agnostic format\n\t */\n\tasync getPR(input: GetPRInput): Promise<PRResult> {\n\t\tconst { number, includeComments = true, repo } = input\n\n\t\t// Convert string ID to number for GitHub CLI\n\t\tconst prNumber = parseInt(number, 10)\n\t\tif (isNaN(prNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub PR number: ${number}. GitHub PR IDs must be numeric.`)\n\t\t}\n\n\t\t// Build fields list based on whether we need comments\n\t\tconst baseFields = 'number,title,body,state,url,author,headRefName,baseRefName,files,commits'\n\t\tconst fields = includeComments\n\t\t\t? `${baseFields},comments`\n\t\t\t: baseFields\n\n\t\t// GitHub PR response structure\n\t\tinterface GitHubPRResponse {\n\t\t\tnumber: number\n\t\t\ttitle: string\n\t\t\tbody: string\n\t\t\tstate: string\n\t\t\turl: string\n\t\t\tauthor?: GitHubAuthor\n\t\t\theadRefName: string\n\t\t\tbaseRefName: string\n\t\t\tfiles?: Array<{\n\t\t\t\tpath: string\n\t\t\t\tadditions: number\n\t\t\t\tdeletions: number\n\t\t\t}>\n\t\t\tcommits?: Array<{\n\t\t\t\toid: string\n\t\t\t\tmessageHeadline: string\n\t\t\t\tauthors: Array<{ name: string; email: string }>\n\t\t\t}>\n\t\t\tcomments?: Array<{\n\t\t\t\tid: number\n\t\t\t\tauthor: GitHubAuthor\n\t\t\t\tbody: string\n\t\t\t\tcreatedAt: string\n\t\t\t\tupdatedAt?: string\n\t\t\t\turl: string\n\t\t\t}>\n\t\t}\n\n\t\tconst args = [\n\t\t\t'pr',\n\t\t\t'view',\n\t\t\tString(prNumber),\n\t\t\t'--json',\n\t\t\tfields,\n\t\t]\n\n\t\t// Add --repo flag if repo is provided\n\t\tif (repo) {\n\t\t\targs.push('--repo', repo)\n\t\t}\n\n\t\tconst raw = await executeGhCommand<GitHubPRResponse>(args)\n\n\t\t// Normalize to PRResult with core fields + passthrough\n\t\tconst result: PRResult = {\n\t\t\t// Core fields\n\t\t\tid: String(raw.number),\n\t\t\tnumber: raw.number,\n\t\t\ttitle: raw.title,\n\t\t\tbody: raw.body,\n\t\t\tstate: raw.state,\n\t\t\turl: raw.url,\n\n\t\t\t// Normalized author\n\t\t\tauthor: normalizeAuthor(raw.author),\n\n\t\t\t// PR-specific fields\n\t\t\theadRefName: raw.headRefName,\n\t\t\tbaseRefName: raw.baseRefName,\n\n\t\t\t// Optional files\n\t\t\t...(raw.files && {\n\t\t\t\tfiles: raw.files,\n\t\t\t}),\n\n\t\t\t// Optional commits - normalize author\n\t\t\t...(raw.commits && {\n\t\t\t\tcommits: raw.commits.map(commit => ({\n\t\t\t\t\toid: commit.oid,\n\t\t\t\t\tmessageHeadline: commit.messageHeadline,\n\t\t\t\t\tauthor: commit.authors?.[0]\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\tid: commit.authors[0].email,\n\t\t\t\t\t\t\tdisplayName: commit.authors[0].name,\n\t\t\t\t\t\t\tname: commit.authors[0].name,\n\t\t\t\t\t\t\temail: commit.authors[0].email,\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: null,\n\t\t\t\t})),\n\t\t\t}),\n\t\t}\n\n\t\t// Handle comments with normalized authors\n\t\t// Use extractNumericIdFromUrl to get REST API-compatible numeric IDs from comment URLs\n\t\tif (raw.comments !== undefined) {\n\t\t\tresult.comments = raw.comments.map(comment => ({\n\t\t\t\tid: extractNumericIdFromUrl(comment.url),\n\t\t\t\tbody: comment.body,\n\t\t\t\tcreatedAt: comment.createdAt,\n\t\t\t\tauthor: normalizeAuthor(comment.author),\n\t\t\t\t...(comment.updatedAt && { updatedAt: comment.updatedAt }),\n\t\t\t}))\n\t\t}\n\n\t\t// Process authenticated images in body and comments\n\t\tresult.body = await processMarkdownImages(result.body, 'github')\n\t\tif (result.comments) {\n\t\t\tfor (const comment of result.comments) {\n\t\t\t\tcomment.body = await processMarkdownImages(comment.body, 'github')\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch PR review comments (inline code comments on specific files/lines)\n\t * Uses gh api with --paginate to handle PRs with many review comments\n\t * Optionally filters by review ID\n\t */\n\tasync getReviewComments(input: GetReviewCommentsInput): Promise<ReviewCommentResult[]> {\n\t\tconst { number, reviewId, repo } = input\n\n\t\t// Convert string ID to number for GitHub API\n\t\tconst prNumber = parseInt(number, 10)\n\t\tif (isNaN(prNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub PR number: ${number}. GitHub PR IDs must be numeric.`)\n\t\t}\n\n\t\t// Validate reviewId early to avoid unnecessary API call\n\t\tlet numericReviewId: number | undefined\n\t\tif (reviewId) {\n\t\t\tnumericReviewId = parseInt(reviewId, 10)\n\t\t\tif (isNaN(numericReviewId)) {\n\t\t\t\tthrow new Error(`Invalid review ID: ${reviewId}. Review IDs must be numeric.`)\n\t\t\t}\n\t\t}\n\n\t\t// GitHub API response structure for review comments\n\t\tinterface GitHubReviewComment {\n\t\t\tid: number\n\t\t\tbody: string\n\t\t\tpath: string\n\t\t\tline: number | null\n\t\t\tside: string | null\n\t\t\tuser: GitHubAuthor | null\n\t\t\tcreated_at: string\n\t\t\tupdated_at: string | null\n\t\t\tin_reply_to_id: number | null\n\t\t\tpull_request_review_id: number | null\n\t\t}\n\n\t\t// Use explicit repo path if provided, otherwise use :owner/:repo placeholder\n\t\tconst apiPath = repo\n\t\t\t? `repos/${repo}/pulls/${prNumber}/comments`\n\t\t\t: `repos/:owner/:repo/pulls/${prNumber}/comments`\n\n\t\tconst args = [\n\t\t\t'api',\n\t\t\tapiPath,\n\t\t\t'--paginate',\n\t\t\t'--jq',\n\t\t\t'[.[] | {id: .id, body: .body, path: .path, line: .line, side: .side, user: .user, created_at: .created_at, updated_at: .updated_at, in_reply_to_id: .in_reply_to_id, pull_request_review_id: .pull_request_review_id}]',\n\t\t]\n\n\t\tconst raw = await executeGhCommand<GitHubReviewComment[]>(args)\n\n\t\t// Filter by reviewId if provided (already validated above)\n\t\tlet comments = raw\n\t\tif (numericReviewId !== undefined) {\n\t\t\tcomments = comments.filter(c => c.pull_request_review_id === numericReviewId)\n\t\t}\n\n\t\t// Normalize and process each comment\n\t\tconst results: ReviewCommentResult[] = []\n\t\tfor (const comment of comments) {\n\t\t\tconst processedBody = await processMarkdownImages(comment.body, 'github')\n\t\t\tresults.push({\n\t\t\t\tid: String(comment.id),\n\t\t\t\tbody: processedBody,\n\t\t\t\tpath: comment.path,\n\t\t\t\tline: comment.line,\n\t\t\t\tside: comment.side,\n\t\t\t\tauthor: normalizeAuthor(comment.user),\n\t\t\t\tcreatedAt: comment.created_at,\n\t\t\t\tupdatedAt: comment.updated_at ?? null,\n\t\t\t\tinReplyToId: comment.in_reply_to_id ? String(comment.in_reply_to_id) : null,\n\t\t\t\tpullRequestReviewId: comment.pull_request_review_id,\n\t\t\t})\n\t\t}\n\n\t\treturn results\n\t}\n\n\t/**\n\t * Fetch a specific comment by ID using gh API\n\t * Normalizes author to FlexibleAuthor format\n\t */\n\tasync getComment(input: GetCommentInput): Promise<CommentDetailResult> {\n\t\tconst { commentId, repo } = input\n\t\t// Note: GitHub doesn't need the issue number parameter - comment IDs are globally unique\n\t\t// But we accept it for interface compatibility with other providers\n\n\t\t// Convert string ID to number for GitHub API\n\t\tconst numericCommentId = parseInt(commentId, 10)\n\t\tif (isNaN(numericCommentId)) {\n\t\t\tthrow new Error(`Invalid GitHub comment ID: ${commentId}. GitHub comment IDs must be numeric.`)\n\t\t}\n\n\t\t// GitHub API response structure\n\t\tinterface GitHubCommentResponse {\n\t\t\tid: number\n\t\t\tbody: string\n\t\t\tuser: GitHubAuthor\n\t\t\tcreated_at: string\n\t\t\tupdated_at?: string\n\t\t\thtml_url?: string\n\t\t\treactions?: Record<string, unknown>\n\t\t}\n\n\t\t// Use explicit repo path if provided, otherwise use :owner/:repo placeholder\n\t\tconst apiPath = repo\n\t\t\t? `repos/${repo}/issues/comments/${numericCommentId}`\n\t\t\t: `repos/:owner/:repo/issues/comments/${numericCommentId}`\n\n\t\t// Use gh api to fetch specific comment\n\t\tconst raw = await executeGhCommand<GitHubCommentResponse>([\n\t\t\t'api',\n\t\t\tapiPath,\n\t\t\t'--jq',\n\t\t\t'{id: .id, body: .body, user: .user, created_at: .created_at, updated_at: .updated_at, html_url: .html_url, reactions: .reactions}',\n\t\t])\n\n\t\t// Process authenticated images in comment body\n\t\tconst processedBody = await processMarkdownImages(raw.body, 'github')\n\n\t\t// Normalize to CommentDetailResult\n\t\treturn {\n\t\t\tid: String(raw.id),\n\t\t\tbody: processedBody,\n\t\t\tauthor: normalizeAuthor(raw.user),\n\t\t\tcreated_at: raw.created_at,\n\t\t\t...(raw.updated_at && { updated_at: raw.updated_at }),\n\t\t\t// Passthrough GitHub-specific fields\n\t\t\t...(raw.html_url && { html_url: raw.html_url }),\n\t\t\t...(raw.reactions && { reactions: raw.reactions }),\n\t\t}\n\t}\n\n\t/**\n\t * Create a new comment on an issue or PR\n\t */\n\tasync createComment(input: CreateCommentInput): Promise<CommentResult> {\n\t\tconst { number, body, type } = input\n\n\t\t// Convert string ID to number for GitHub utilities\n\t\tconst numericId = parseInt(number, 10)\n\t\tif (isNaN(numericId)) {\n\t\t\tthrow new Error(`Invalid GitHub ${type} number: ${number}. GitHub IDs must be numeric.`)\n\t\t}\n\n\t\t// Delegate to existing GitHub utilities\n\t\tconst result =\n\t\t\ttype === 'issue'\n\t\t\t\t? await createIssueComment(numericId, body)\n\t\t\t\t: await createPRComment(numericId, body)\n\n\t\t// Convert numeric ID to string for the interface\n\t\treturn {\n\t\t\t...result,\n\t\t\tid: String(result.id),\n\t\t}\n\t}\n\n\t/**\n\t * Update an existing comment\n\t */\n\tasync updateComment(input: UpdateCommentInput): Promise<CommentResult> {\n\t\tconst { commentId, body } = input\n\t\t// Note: GitHub doesn't need the issue number parameter - comment IDs are globally unique\n\t\t// But we accept it for interface compatibility with other providers\n\n\t\t// Convert string ID to number for GitHub utility\n\t\tconst numericCommentId = parseInt(commentId, 10)\n\t\tif (isNaN(numericCommentId)) {\n\t\t\tthrow new Error(`Invalid GitHub comment ID: ${commentId}. GitHub comment IDs must be numeric.`)\n\t\t}\n\n\t\t// Delegate to existing GitHub utility\n\t\tconst result = await updateIssueComment(numericCommentId, body)\n\n\t\t// Convert numeric ID to string for the interface\n\t\treturn {\n\t\t\t...result,\n\t\t\tid: String(result.id),\n\t\t}\n\t}\n\n\t/**\n\t * Create a new issue\n\t */\n\tasync createIssue(input: CreateIssueInput): Promise<CreateIssueResult> {\n\t\tconst { title, body, labels, repo } = input\n\t\t// teamKey is ignored for GitHub\n\n\t\tconst result = await createIssue(title, body, { labels, repo })\n\n\t\t// Ensure number is numeric\n\t\tconst issueNumber = typeof result.number === 'number'\n\t\t\t? result.number\n\t\t\t: parseInt(String(result.number), 10)\n\n\t\treturn {\n\t\t\tid: String(issueNumber),\n\t\t\turl: result.url,\n\t\t\tnumber: issueNumber,\n\t\t}\n\t}\n\n\t/**\n\t * Create a child issue linked to a parent issue\n\t * GitHub requires two-step process: create issue, then link via GraphQL\n\t */\n\tasync createChildIssue(input: CreateChildIssueInput): Promise<CreateIssueResult> {\n\t\tconst { parentId, title, body, labels, repo } = input\n\t\t// teamKey is ignored for GitHub\n\n\t\t// Convert parent identifier to number\n\t\tconst parentNumber = parseInt(parentId, 10)\n\t\tif (isNaN(parentNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub parent issue number: ${parentId}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Step 1: Get parent issue's GraphQL node ID\n\t\tconst parentNodeId = await getIssueNodeId(parentNumber, repo)\n\n\t\t// Step 2: Create the child issue\n\t\tconst childResult = await createIssue(title, body, { labels, repo })\n\t\tconst childNumber = typeof childResult.number === 'number'\n\t\t\t? childResult.number\n\t\t\t: parseInt(String(childResult.number), 10)\n\n\t\t// Step 3: Get child issue's GraphQL node ID\n\t\tconst childNodeId = await getIssueNodeId(childNumber, repo)\n\n\t\t// Step 4: Link child to parent via GraphQL mutation\n\t\tawait addSubIssue(parentNodeId, childNodeId)\n\n\t\treturn {\n\t\t\tid: String(childNumber),\n\t\t\turl: childResult.url,\n\t\t\tnumber: childNumber,\n\t\t}\n\t}\n\n\t/**\n\t * Create a blocking dependency between two issues (A blocks B)\n\t * Uses GitHub's sub-issues API: blocking issue becomes parent, blocked issue becomes sub-issue\n\t */\n\tasync createDependency(input: CreateDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue, repo } = input\n\n\t\t// Convert string IDs to numbers\n\t\tconst blockingNumber = parseInt(blockingIssue, 10)\n\t\tif (isNaN(blockingNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockingIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tconst blockedNumber = parseInt(blockedIssue, 10)\n\t\tif (isNaN(blockedNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockedIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Get the database ID of the blocking issue\n\t\t// GitHub API: POST /issues/{blocked_issue_number}/dependencies/blocked_by with body issue_id={blocking_database_id}\n\t\tconst blockingDatabaseId = await getIssueDatabaseId(blockingNumber, repo)\n\n\t\t// Create the dependency: path uses blocked issue number, body uses blocking issue DB ID\n\t\tawait createIssueDependency(blockedNumber, blockingDatabaseId, repo)\n\t}\n\n\t/**\n\t * Get dependencies for an issue\n\t */\n\tasync getDependencies(input: GetDependenciesInput): Promise<DependenciesResult> {\n\t\tconst { number, direction, repo } = input\n\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tconst result: DependenciesResult = {\n\t\t\tblocking: [],\n\t\t\tblockedBy: [],\n\t\t}\n\n\t\t// Fetch dependencies based on direction\n\t\tif (direction === 'blocking' || direction === 'both') {\n\t\t\tresult.blocking = await getIssueDependencies(issueNumber, 'blocking', repo)\n\t\t}\n\n\t\tif (direction === 'blocked_by' || direction === 'both') {\n\t\t\tresult.blockedBy = await getIssueDependencies(issueNumber, 'blocked_by', repo)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Remove a blocking dependency between two issues (A blocks B)\n\t * Uses GitHub's sub-issues API: blocking issue is parent, blocked issue is sub-issue\n\t */\n\tasync removeDependency(input: RemoveDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue, repo } = input\n\n\t\t// Convert string IDs to numbers\n\t\tconst blockingNumber = parseInt(blockingIssue, 10)\n\t\tif (isNaN(blockingNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockingIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tconst blockedNumber = parseInt(blockedIssue, 10)\n\t\tif (isNaN(blockedNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${blockedIssue}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Get the database ID of the blocking issue\n\t\t// GitHub API: DELETE /issues/{blocked_issue_number}/dependencies/blocked_by with body issue_id={blocking_database_id}\n\t\tconst blockingDatabaseId = await getIssueDatabaseId(blockingNumber, repo)\n\n\t\t// Remove the dependency: path uses blocked issue number, body uses blocking issue DB ID\n\t\tawait removeIssueDependency(blockedNumber, blockingDatabaseId, repo)\n\t}\n\n\t/**\n\t * Get child issues (sub-issues) of a parent issue\n\t */\n\tasync getChildIssues(input: GetChildIssuesInput): Promise<ChildIssueResult[]> {\n\t\tconst { number, repo } = input\n\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\treturn await getSubIssues(issueNumber, repo)\n\t}\n\n\t/**\n\t * Close an issue\n\t */\n\tasync closeIssue(input: CloseIssueInput): Promise<void> {\n\t\tconst { number, repo } = input\n\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tawait closeGhIssue(issueNumber, repo)\n\t}\n\n\t/**\n\t * Reopen a closed issue\n\t */\n\tasync reopenIssue(input: ReopenIssueInput): Promise<void> {\n\t\tconst { number, repo } = input\n\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\tawait reopenGhIssue(issueNumber, repo)\n\t}\n\n\t/**\n\t * Edit an issue's properties\n\t * State changes are delegated to closeIssue/reopenIssue\n\t */\n\tasync editIssue(input: EditIssueInput): Promise<void> {\n\t\tconst { number, title, body, state, labels, repo } = input\n\n\t\tconst issueNumber = parseInt(number, 10)\n\t\tif (isNaN(issueNumber)) {\n\t\t\tthrow new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`)\n\t\t}\n\n\t\t// Handle state changes via close/reopen\n\t\tif (state === 'closed') {\n\t\t\tawait this.closeIssue({ number, repo })\n\t\t} else if (state === 'open') {\n\t\t\tawait this.reopenIssue({ number, repo })\n\t\t}\n\n\t\t// Handle other field updates\n\t\tif (title !== undefined || body !== undefined || labels !== undefined) {\n\t\t\tawait editGhIssue(\n\t\t\t\tissueNumber,\n\t\t\t\t{\n\t\t\t\t\t...(title !== undefined && { title }),\n\t\t\t\t\t...(body !== undefined && { body }),\n\t\t\t\t\t...(labels !== undefined && { labels }),\n\t\t\t\t},\n\t\t\t\trepo\n\t\t\t)\n\t\t}\n\t}\n}\n","/**\n * Linear SDK utilities\n * Wrapper functions for the @linear/sdk\n */\n\nimport { LinearClient, IssueRelationType } from '@linear/sdk'\nimport type { LinearIssue, LinearComment } from '../types/linear.js'\nimport { LinearServiceError } from '../types/linear.js'\nimport { logger } from './logger.js'\n\n/**\n * Slugify a title for use in Linear URLs\n * Converts to lowercase, replaces non-alphanumeric with hyphens, truncates to reasonable length\n * @param title - Issue title\n * @param maxLength - Maximum slug length (default: 50)\n * @returns Slugified title\n */\nexport function slugifyTitle(title: string, maxLength: number = 50): string {\n // Convert to lowercase, replace non-alphanumeric chars with hyphens\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') // trim leading/trailing hyphens\n\n // If already short enough, return as-is\n if (slug.length <= maxLength) {\n return slug\n }\n\n // Split by hyphens and rebuild until we hit the limit\n const parts = slug.split('-')\n let result = ''\n for (const part of parts) {\n const candidate = result ? `${result}-${part}` : part\n if (candidate.length > maxLength) {\n break\n }\n result = candidate\n }\n\n return result || slug.slice(0, maxLength) // fallback if first part is too long\n}\n\n/**\n * Build a Linear issue URL with optional title slug\n * @param identifier - Issue identifier (e.g., \"ENG-123\")\n * @param title - Optional issue title for slug\n * @returns Linear URL\n */\nexport function buildLinearIssueUrl(identifier: string, title?: string): string {\n const base = `https://linear.app/issue/${identifier}`\n if (title) {\n const slug = slugifyTitle(title)\n return slug ? `${base}/${slug}` : base\n }\n return base\n}\n\n/**\n * Get Linear API token from environment\n * @returns API token\n * @throws LinearServiceError if token not found\n */\nfunction getLinearApiToken(): string {\n const token = process.env.LINEAR_API_TOKEN\n if (!token) {\n throw new LinearServiceError(\n 'UNAUTHORIZED',\n 'LINEAR_API_TOKEN not set. Configure in settings.local.json or set environment variable.',\n )\n }\n return token\n}\n\n/**\n * Create a Linear SDK client instance\n * @param apiToken - Optional API token (takes precedence over env var)\n * @returns Configured LinearClient\n */\nfunction createLinearClient(apiToken?: string): LinearClient {\n const token = apiToken ?? getLinearApiToken()\n return new LinearClient({ apiKey: token })\n}\n\n/**\n * Handle SDK errors and convert to LinearServiceError\n * @param error - Error from SDK\n * @param context - Context string for debugging\n * @throws LinearServiceError\n */\nfunction handleLinearError(error: unknown, context: string): never {\n logger.debug(`${context}: Handling error`, { error })\n\n // SDK errors typically have a message property\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Map common error patterns\n if (errorMessage.includes('not found') || errorMessage.includes('Not found')) {\n throw new LinearServiceError('NOT_FOUND', 'Linear issue or resource not found', { error })\n }\n\n if (\n errorMessage.includes('unauthorized') ||\n errorMessage.includes('Unauthorized') ||\n errorMessage.includes('Invalid API key')\n ) {\n throw new LinearServiceError(\n 'UNAUTHORIZED',\n 'Linear authentication failed. Check LINEAR_API_TOKEN.',\n { error },\n )\n }\n\n if (errorMessage.includes('rate limit')) {\n throw new LinearServiceError('RATE_LIMITED', 'Linear API rate limit exceeded', { error })\n }\n\n // Generic SDK error\n throw new LinearServiceError('CLI_ERROR', `Linear SDK error: ${errorMessage}`, { error })\n}\n\n/**\n * Fetch a Linear issue by identifier\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @returns Linear issue details\n * @throws LinearServiceError if issue not found or SDK error\n */\nexport async function fetchLinearIssue(identifier: string): Promise<LinearIssue> {\n try {\n logger.debug(`Fetching Linear issue: ${identifier}`)\n const client = createLinearClient()\n const issue = await client.issue(identifier)\n\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Convert SDK issue to our LinearIssue type\n const result: LinearIssue = {\n id: issue.id,\n identifier: issue.identifier,\n title: issue.title,\n url: issue.url,\n createdAt: issue.createdAt.toISOString(),\n updatedAt: issue.updatedAt.toISOString(),\n }\n\n // Add optional fields if present\n if (issue.description) {\n result.description = issue.description\n }\n\n if (issue.state) {\n const state = await issue.state\n if (state?.name) {\n result.state = state.name\n }\n }\n\n return result\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssue')\n }\n}\n\n/**\n * Create a new Linear issue\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param teamKey - Team key (e.g., \"ENG\", \"PLAT\")\n * @param labels - Optional label names to apply\n * @returns Created issue identifier and URL\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearIssue(\n title: string,\n body: string,\n teamKey: string,\n _labels?: string[],\n): Promise<{ identifier: string; url: string }> {\n try {\n logger.debug(`Creating Linear issue in team ${teamKey}: ${title}`)\n const client = createLinearClient()\n\n // Get team by key\n const teams = await client.teams()\n const team = teams.nodes.find((t) => t.key === teamKey)\n\n if (!team) {\n throw new LinearServiceError('NOT_FOUND', `Linear team ${teamKey} not found`)\n }\n\n // Create issue\n const issueInput: { teamId: string; title: string; description?: string } = {\n teamId: team.id,\n title,\n }\n\n if (body) {\n issueInput.description = body\n }\n\n const payload = await client.createIssue(issueInput)\n\n const issue = await payload.issue\n\n if (!issue) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear issue')\n }\n\n // Construct URL\n const url = issue.url ?? buildLinearIssueUrl(issue.identifier, title)\n\n return {\n identifier: issue.identifier,\n url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearIssue')\n }\n}\n\n/**\n * Create a child issue linked to a parent issue\n * Linear supports atomic creation with parentId field\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param teamKey - Team key (e.g., \"ENG\")\n * @param parentId - Parent issue UUID (from issue.id, not identifier)\n * @param labels - Optional label names to apply\n * @returns Created issue identifier and URL\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearChildIssue(\n title: string,\n body: string,\n teamKey: string,\n parentId: string,\n _labels?: string[],\n): Promise<{ identifier: string; url: string }> {\n try {\n logger.debug(`Creating Linear child issue in team ${teamKey}: ${title}`)\n const client = createLinearClient()\n\n // Get team by key\n const teams = await client.teams()\n const team = teams.nodes.find((t) => t.key === teamKey)\n\n if (!team) {\n throw new LinearServiceError('NOT_FOUND', `Linear team ${teamKey} not found`)\n }\n\n // Create issue with parentId for atomic parent-child relationship\n const issueInput: { teamId: string; title: string; description?: string; parentId: string } = {\n teamId: team.id,\n title,\n parentId, // UUID of parent issue\n }\n\n if (body) {\n issueInput.description = body\n }\n\n const payload = await client.createIssue(issueInput)\n\n const issue = await payload.issue\n\n if (!issue) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear child issue')\n }\n\n // Construct URL\n const url = issue.url ?? buildLinearIssueUrl(issue.identifier, title)\n\n return {\n identifier: issue.identifier,\n url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearChildIssue')\n }\n}\n\n/**\n * Create a comment on a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param body - Comment body (markdown)\n * @returns Created comment details\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearComment(\n identifier: string,\n body: string,\n): Promise<LinearComment> {\n try {\n logger.debug(`Creating comment on Linear issue ${identifier}`)\n const client = createLinearClient()\n\n // Get issue by identifier to get its ID\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Create comment using issue ID\n const payload = await client.createComment({\n issueId: issue.id,\n body,\n })\n\n const comment = await payload.comment\n\n if (!comment) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear comment')\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearComment')\n }\n}\n\n/**\n * Update a Linear issue's workflow state\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param stateName - Target state name (e.g., \"In Progress\", \"Done\")\n * @throws LinearServiceError on update failure\n */\nexport async function updateLinearIssueState(\n identifier: string,\n stateName: string,\n): Promise<void> {\n try {\n logger.debug(`Updating Linear issue ${identifier} state to: ${stateName}`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Get team to find state\n const team = await issue.team\n if (!team) {\n throw new LinearServiceError('CLI_ERROR', 'Issue has no team')\n }\n\n // Find state by name\n const states = await team.states()\n const state = states.nodes.find((s) => s.name === stateName)\n\n if (!state) {\n throw new LinearServiceError(\n 'NOT_FOUND',\n `State \"${stateName}\" not found in team ${team.key}`,\n )\n }\n\n // Update issue state\n await client.updateIssue(issue.id, {\n stateId: state.id,\n })\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'updateLinearIssueState')\n }\n}\n\n/**\n * Edit a Linear issue's properties (title, description)\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param updates - Fields to update\n * @throws LinearServiceError on update failure\n */\nexport async function editLinearIssue(\n identifier: string,\n updates: { title?: string; description?: string },\n): Promise<void> {\n try {\n logger.debug(`Editing Linear issue ${identifier}`, { updates })\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Build update payload\n const updatePayload: { title?: string; description?: string } = {}\n if (updates.title !== undefined) {\n updatePayload.title = updates.title\n }\n if (updates.description !== undefined) {\n updatePayload.description = updates.description\n }\n\n if (Object.keys(updatePayload).length > 0) {\n await client.updateIssue(issue.id, updatePayload)\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'editLinearIssue')\n }\n}\n\n/**\n * Get a specific comment by ID\n * @param commentId - Linear comment UUID\n * @returns Comment details\n * @throws LinearServiceError if comment not found\n */\nexport async function getLinearComment(commentId: string): Promise<LinearComment> {\n try {\n logger.debug(`Fetching Linear comment: ${commentId}`)\n const client = createLinearClient()\n const comment = await client.comment({ id: commentId })\n\n if (!comment) {\n throw new LinearServiceError('NOT_FOUND', `Linear comment ${commentId} not found`)\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'getLinearComment')\n }\n}\n\n/**\n * Update an existing comment\n * @param commentId - Linear comment UUID\n * @param body - New comment body (markdown)\n * @returns Updated comment details\n * @throws LinearServiceError on update failure\n */\nexport async function updateLinearComment(\n commentId: string,\n body: string,\n): Promise<LinearComment> {\n try {\n logger.debug(`Updating Linear comment: ${commentId}`)\n const client = createLinearClient()\n\n const payload = await client.updateComment(commentId, { body })\n const comment = await payload.comment\n\n if (!comment) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to update Linear comment')\n }\n\n return {\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'updateLinearComment')\n }\n}\n\n/**\n * Fetch all comments for a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @returns Array of comments\n * @throws LinearServiceError on fetch failure\n */\nexport async function fetchLinearIssueComments(identifier: string): Promise<LinearComment[]> {\n try {\n logger.debug(`Fetching comments for Linear issue: ${identifier}`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Fetch comments\n const comments = await issue.comments({ first: 100 })\n\n return comments.nodes.map((comment) => ({\n id: comment.id,\n body: comment.body,\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n url: comment.url,\n }))\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssueComments')\n }\n}\n\n/**\n * Get child issues of a parent Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param options - Optional settings\n * @param options.apiToken - Optional API token (takes precedence over env var)\n * @returns Array of child issues\n * @throws LinearServiceError on fetch failure\n */\nexport async function getLinearChildIssues(\n identifier: string,\n options?: { apiToken?: string },\n): Promise<Array<{ id: string; title: string; url: string; state: string }>> {\n try {\n logger.debug(`Fetching child issues for Linear issue: ${identifier}`)\n const client = createLinearClient(options?.apiToken)\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Fetch child issues\n const children = await issue.children({ first: 100 })\n\n // Build results, fetching state in parallel\n const results = await Promise.all(\n children.nodes.map(async (child) => {\n const stateObj = await child.state\n const state = stateObj?.name ?? 'unknown'\n\n return {\n id: child.identifier,\n title: child.title,\n url: child.url,\n state,\n }\n }),\n )\n\n return results\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'getLinearChildIssues')\n }\n}\n\n// Linear Issue Dependency Operations\n\n/**\n * Dependency result for Linear issues\n */\nexport interface LinearDependencyResult {\n id: string\n title: string\n url: string\n state: string\n}\n\n/**\n * Create a blocking relationship between two issues\n * @param blockingIssueId - UUID of the issue that blocks (the blocker)\n * @param blockedIssueId - UUID of the issue being blocked\n * @throws LinearServiceError on creation failure\n */\nexport async function createLinearIssueRelation(\n blockingIssueId: string,\n blockedIssueId: string,\n): Promise<void> {\n try {\n logger.debug(`Creating Linear issue relation: ${blockingIssueId} blocks ${blockedIssueId}`)\n const client = createLinearClient()\n\n // Create a \"blocks\" relation from blockingIssue to blockedIssue\n // In Linear, the relation is created on the blocking issue pointing to the blocked issue\n const payload = await client.createIssueRelation({\n issueId: blockingIssueId,\n relatedIssueId: blockedIssueId,\n type: IssueRelationType.Blocks,\n })\n\n if (!payload.success) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to create Linear issue relation')\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'createLinearIssueRelation')\n }\n}\n\n/**\n * Get dependencies for a Linear issue\n * @param identifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param direction - 'blocking' for issues this blocks, 'blocked_by' for blockers, 'both' for all\n * @returns Object with blocking and blockedBy arrays\n * @throws LinearServiceError on fetch failure\n */\nexport async function getLinearIssueDependencies(\n identifier: string,\n direction: 'blocking' | 'blocked_by' | 'both',\n): Promise<{ blocking: LinearDependencyResult[]; blockedBy: LinearDependencyResult[] }> {\n try {\n logger.debug(`Fetching Linear issue dependencies: ${identifier} (direction: ${direction})`)\n const client = createLinearClient()\n\n // Get issue by identifier\n const issue = await client.issue(identifier)\n if (!issue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${identifier} not found`)\n }\n\n // Fetch relations and inverse relations in parallel\n const [relations, inverseRelations] = await Promise.all([\n issue.relations(),\n issue.inverseRelations(),\n ])\n\n const blocking: LinearDependencyResult[] = []\n const blockedBy: LinearDependencyResult[] = []\n\n // Helper to build dependency result from a resolved issue\n const buildDependencyResult = async (\n relatedIssue: { identifier: string; title: string; url: string; state: unknown } | null | undefined,\n ): Promise<LinearDependencyResult | null> => {\n if (!relatedIssue) return null\n\n const stateObj = await (relatedIssue.state as Promise<{ name?: string } | null | undefined>)\n const state = stateObj?.name ?? 'unknown'\n\n return {\n id: relatedIssue.identifier,\n title: relatedIssue.title,\n url: relatedIssue.url,\n state,\n }\n }\n\n // Process blocking relations (this issue blocks the related issue)\n // relations with type 'blocks' mean this issue blocks the related issue\n if (direction === 'blocking' || direction === 'both') {\n const blockingRelations = relations.nodes.filter(\n (r) => r.type === IssueRelationType.Blocks,\n )\n // Resolve all related issues in parallel, then build results\n // Filter out undefined relatedIssue before Promise.all (LinearFetch<Issue> | undefined)\n const relatedIssuePromises = blockingRelations\n .map((r) => r.relatedIssue)\n .filter((p): p is NonNullable<typeof p> => p !== undefined)\n const relatedIssues = await Promise.all(relatedIssuePromises)\n const blockingResults = await Promise.all(\n relatedIssues.map((issue) => buildDependencyResult(issue)),\n )\n blocking.push(...blockingResults.filter((r): r is LinearDependencyResult => r !== null))\n }\n\n // Process blocked by relations (the related issue blocks this issue)\n // inverseRelations with type 'blocks' mean the related issue blocks this issue\n if (direction === 'blocked_by' || direction === 'both') {\n const blockedByRelations = inverseRelations.nodes.filter(\n (r) => r.type === IssueRelationType.Blocks,\n )\n // Resolve all source issues in parallel, then build results\n // Filter out undefined issue before Promise.all (LinearFetch<Issue> | undefined)\n const sourceIssuePromises = blockedByRelations\n .map((r) => r.issue)\n .filter((p): p is NonNullable<typeof p> => p !== undefined)\n const sourceIssues = await Promise.all(sourceIssuePromises)\n const blockedByResults = await Promise.all(\n sourceIssues.map((issue) => buildDependencyResult(issue)),\n )\n blockedBy.push(...blockedByResults.filter((r): r is LinearDependencyResult => r !== null))\n }\n\n return { blocking, blockedBy }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'getLinearIssueDependencies')\n }\n}\n\n/**\n * Delete an issue relation by ID\n * @param relationId - UUID of the relation to delete\n * @throws LinearServiceError on deletion failure\n */\nexport async function deleteLinearIssueRelation(relationId: string): Promise<void> {\n try {\n logger.debug(`Deleting Linear issue relation: ${relationId}`)\n const client = createLinearClient()\n\n const payload = await client.deleteIssueRelation(relationId)\n\n if (!payload.success) {\n throw new LinearServiceError('CLI_ERROR', 'Failed to delete Linear issue relation')\n }\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'deleteLinearIssueRelation')\n }\n}\n\n// Issue List Operations (for il issues command)\n\nexport interface LinearIssueListItem {\n id: string\n title: string\n updatedAt: string\n url: string\n state: string\n}\n\n/**\n * Fetch a list of active Linear issues for a team, sorted by recently updated\n * @param teamKey - Team key (e.g., \"ENG\", \"PLAT\")\n * @param options - Fetch options\n * @param options.limit - Maximum number of issues to return (default: 100)\n * @param options.apiToken - Optional API token (takes precedence over env var)\n * @returns Array of issues\n * @throws LinearServiceError on fetch failure\n */\nexport async function fetchLinearIssueList(\n teamKey: string,\n options?: { limit?: number; apiToken?: string; mine?: boolean },\n): Promise<LinearIssueListItem[]> {\n try {\n const limit = options?.limit ?? 100\n\n logger.debug(`Fetching Linear issue list for team ${teamKey}`, { limit, mine: options?.mine })\n const client = createLinearClient(options?.apiToken)\n\n // Get team by key\n const teams = await client.teams()\n const team = teams.nodes.find((t) => t.key === teamKey)\n\n if (!team) {\n throw new LinearServiceError('NOT_FOUND', `Linear team ${teamKey} not found`)\n }\n\n // Build filter: always exclude completed/canceled states\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Linear SDK filter types are complex and not fully exported\n const filter: any = {\n state: {\n type: {\n nin: ['completed', 'canceled'],\n },\n },\n }\n\n // When --mine is set, filter to issues assigned to the authenticated user\n if (options?.mine) {\n filter.assignee = { isMe: { eq: true } }\n }\n\n // Fetch issues: filter out completed and canceled states, order by updatedAt\n const issues = await team.issues({\n first: limit,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- PaginationOrderBy is a const enum incompatible with isolatedModules\n orderBy: 'updatedAt' as any,\n filter,\n })\n\n // Build results, resolving state names in parallel\n const results = await Promise.all(\n issues.nodes.map(async (issue) => {\n const stateObj = await issue.state\n const state = stateObj?.name ?? 'unknown'\n\n return {\n id: issue.identifier,\n title: issue.title,\n updatedAt: issue.updatedAt.toISOString(),\n url: issue.url,\n state,\n }\n }),\n )\n\n return results\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'fetchLinearIssueList')\n }\n}\n\n/**\n * Find the relation ID between two issues for removal\n * @param blockingIdentifier - Identifier of the blocking issue\n * @param blockedIdentifier - Identifier of the blocked issue\n * @returns The relation ID if found, null otherwise\n * @throws LinearServiceError on fetch failure\n */\nexport async function findLinearIssueRelation(\n blockingIdentifier: string,\n blockedIdentifier: string,\n): Promise<string | null> {\n try {\n logger.debug(`Finding Linear issue relation: ${blockingIdentifier} blocks ${blockedIdentifier}`)\n const client = createLinearClient()\n\n // Get the blocking issue\n const blockingIssue = await client.issue(blockingIdentifier)\n if (!blockingIssue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${blockingIdentifier} not found`)\n }\n\n // Get the blocked issue to get its ID\n const blockedIssue = await client.issue(blockedIdentifier)\n if (!blockedIssue) {\n throw new LinearServiceError('NOT_FOUND', `Linear issue ${blockedIdentifier} not found`)\n }\n\n // Find the relation from blockingIssue that blocks blockedIssue\n const relations = await blockingIssue.relations()\n\n // Filter to only 'blocks' relations and fetch related issues in parallel\n const blockingRelations = relations.nodes.filter(\n (r) => r.type === IssueRelationType.Blocks,\n )\n\n const relationsWithIssues = await Promise.all(\n blockingRelations.map(async (relation) => ({\n relation,\n relatedIssue: await relation.relatedIssue,\n })),\n )\n\n const matchingRelation = relationsWithIssues.find(\n ({ relatedIssue }) => relatedIssue?.id === blockedIssue.id,\n )\n\n return matchingRelation?.relation.id ?? null\n } catch (error) {\n if (error instanceof LinearServiceError) {\n throw error\n }\n handleLinearError(error, 'findLinearIssueRelation')\n }\n}\n","/**\n * Linear API response types (from @linear/sdk)\n */\n\n/**\n * Linear issue response from SDK\n */\nexport interface LinearIssue {\n /** Linear internal UUID */\n id: string\n /** Issue identifier in TEAM-NUMBER format (e.g., ENG-123) */\n identifier: string\n /** Issue title */\n title: string\n /** Issue description (markdown) */\n description?: string\n /** Current workflow state name */\n state?: string\n /** Linear web URL */\n url: string\n /** Creation timestamp (ISO string) */\n createdAt: string\n /** Last update timestamp (ISO string) */\n updatedAt: string\n}\n\n/**\n * Linear comment response from SDK\n */\nexport interface LinearComment {\n /** Comment UUID */\n id: string\n /** Comment body (markdown) */\n body: string\n /** Creation timestamp (ISO string) */\n createdAt: string\n /** Last update timestamp (ISO string) */\n updatedAt: string\n /** Comment URL */\n url: string\n}\n\n/**\n * Linear error codes\n */\nexport type LinearErrorCode =\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'INVALID_STATE'\n | 'RATE_LIMITED'\n | 'CLI_NOT_FOUND'\n | 'CLI_ERROR'\n\n/**\n * Linear error details\n */\nexport interface LinearError {\n code: LinearErrorCode\n message: string\n details?: unknown\n}\n\n/**\n * Custom error class for Linear operations\n */\nexport class LinearServiceError extends Error {\n constructor(\n public code: LinearErrorCode,\n message: string,\n public details?: unknown,\n ) {\n super(message)\n this.name = 'LinearServiceError'\n // Maintain proper stack trace for where error was thrown (V8 only)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, LinearServiceError)\n }\n }\n}\n","import { appendFileSync } from 'node:fs'\nimport { join, dirname, basename, extname } from 'node:path'\n\n/**\n * Utility class for converting HTML details/summary format to Linear's collapsible format\n *\n * Converts:\n * <details>\n * <summary>Header</summary>\n * CONTENT\n * </details>\n *\n * Into Linear format:\n * +++ Header\n *\n * CONTENT\n *\n * +++\n */\nexport class LinearMarkupConverter {\n\t/**\n\t * Convert HTML details/summary blocks to Linear's collapsible format\n\t * Handles nested details blocks recursively\n\t *\n\t * @param text - Text containing HTML details/summary blocks\n\t * @returns Text with details/summary converted to Linear format\n\t */\n\tstatic convertDetailsToLinear(text: string): string {\n\t\tif (!text) {\n\t\t\treturn text\n\t\t}\n\n\t\t// Process from innermost to outermost to handle nesting correctly\n\t\t// Keep converting until no more details blocks are found\n\t\tlet previousText = ''\n\t\tlet currentText = text\n\n\t\twhile (previousText !== currentText) {\n\t\t\tpreviousText = currentText\n\t\t\tcurrentText = this.convertSinglePass(currentText)\n\t\t}\n\n\t\treturn currentText\n\t}\n\n\t/**\n\t * Perform a single pass of details block conversion\n\t * Converts the innermost details blocks first\n\t */\n\tprivate static convertSinglePass(text: string): string {\n\t\t// Match <details> blocks with optional attributes on the details tag\n\t\t// Supports multiline content between tags\n\t\tconst detailsRegex = /<details[^>]*>\\s*<summary[^>]*>(.*?)<\\/summary>\\s*(.*?)\\s*<\\/details>/gis\n\n\t\treturn text.replace(detailsRegex, (_match, summary, content) => {\n\t\t\t// Clean up the summary - trim whitespace and decode HTML entities\n\t\t\tconst cleanSummary = this.cleanText(summary)\n\n\t\t\t// Clean up the content - preserve internal structure but normalize outer whitespace\n\t\t\t// Note: Don't recursively convert here - the while loop handles that\n\t\t\tconst cleanContent = this.cleanContent(content)\n\n\t\t\t// Build Linear collapsible format\n\t\t\t// Always include blank lines around content for readability\n\t\t\tif (cleanContent) {\n\t\t\t\treturn `+++ ${cleanSummary}\\n\\n${cleanContent}\\n\\n+++`\n\t\t\t} else {\n\t\t\t\t// Empty content - use minimal format\n\t\t\t\treturn `+++ ${cleanSummary}\\n\\n+++`\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Clean text by trimming whitespace and decoding common HTML entities\n\t */\n\tprivate static cleanText(text: string): string {\n\t\treturn text\n\t\t\t.trim()\n\t\t\t.replace(/&lt;/g, '<')\n\t\t\t.replace(/&gt;/g, '>')\n\t\t\t.replace(/&amp;/g, '&')\n\t\t\t.replace(/&quot;/g, '\"')\n\t\t\t.replace(/&#39;/g, \"'\")\n\t}\n\n\t/**\n\t * Clean content while preserving internal structure\n\t * - Removes leading/trailing whitespace\n\t * - Normalizes internal blank lines (max 2 consecutive newlines)\n\t * - Preserves code blocks and other formatting\n\t */\n\tprivate static cleanContent(content: string): string {\n\t\tif (!content) {\n\t\t\treturn ''\n\t\t}\n\n\t\t// Trim outer whitespace\n\t\tlet cleaned = content.trim()\n\n\t\t// Normalize excessive blank lines (3+ newlines -> 2 newlines)\n\t\tcleaned = cleaned.replace(/\\n{3,}/g, '\\n\\n')\n\n\t\treturn cleaned\n\t}\n\n\t/**\n\t * Check if text contains HTML details/summary blocks\n\t * Useful for conditional conversion\n\t */\n\tstatic hasDetailsBlocks(text: string): boolean {\n\t\tif (!text) {\n\t\t\treturn false\n\t\t}\n\n\t\tconst detailsRegex = /<details[^>]*>.*?<summary[^>]*>.*?<\\/summary>.*?<\\/details>/is\n\t\treturn detailsRegex.test(text)\n\t}\n\n\t/**\n\t * Remove wrapper tags from code sample details blocks\n\t * Identifies details blocks where summary contains \"X lines\" pattern\n\t * and removes the details/summary tags while preserving the content\n\t *\n\t * @param text - Text containing potential code sample details blocks\n\t * @returns Text with code sample wrappers removed\n\t */\n\tstatic removeCodeSampleWrappers(text: string): string {\n\t\tif (!text) {\n\t\t\treturn text\n\t\t}\n\n\t\t// Match details blocks where summary contains \"X lines\" (e.g., \"45 lines\", \"120 lines\")\n\t\t// Pattern: <details><summary>...N lines...</summary>CONTENT</details>\n\t\t// Use [^<]* to match summary content without allowing nested tags to interfere\n\t\t// Then use [\\s\\S]*? for the content to allow any characters including newlines\n\t\tconst codeSampleRegex = /<details[^>]*>\\s*<summary[^>]*>([^<]*\\d+\\s+lines[^<]*)<\\/summary>\\s*([\\s\\S]*?)<\\/details>/gi\n\n\t\treturn text.replace(codeSampleRegex, (_match, _summary, content) => {\n\t\t\t// Return just the content, without any wrapper tags\n\t\t\t// Preserve the content exactly as-is\n\t\t\treturn content.trim()\n\t\t})\n\t}\n\n\t/**\n\t * Convert text for Linear - applies all necessary conversions\n\t * Currently only converts details/summary blocks, but can be extended\n\t * for other HTML to Linear markdown conversions\n\t */\n\tstatic convertToLinear(text: string): string {\n\t\tif (!text) {\n\t\t\treturn text\n\t\t}\n\n\t\t// Log input if logging is enabled\n\t\tthis.logConversion('INPUT', text)\n\n\t\t// Apply all conversions\n\t\tlet converted = text\n\n\t\t// First, remove code sample wrappers (details blocks with \"X lines\" pattern)\n\t\t// This prevents them from being converted to Linear's +++ format\n\t\tconverted = this.removeCodeSampleWrappers(converted)\n\n\t\t// Then convert remaining details/summary blocks to Linear format\n\t\tconverted = this.convertDetailsToLinear(converted)\n\n\t\t// Log output if logging is enabled\n\t\tthis.logConversion('OUTPUT', converted)\n\n\t\treturn converted\n\t}\n\n\t/**\n\t * Log conversion input/output if LINEAR_MARKDOWN_LOG_FILE is set\n\t */\n\tprivate static logConversion(label: string, content: string): void {\n\t\tconst logFilePath = process.env.LINEAR_MARKDOWN_LOG_FILE\n\t\tif (!logFilePath) {\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tconst timestampedPath = this.getTimestampedLogPath(logFilePath)\n\t\t\tconst timestamp = new Date().toISOString()\n\t\t\tconst separator = '================================'\n\n\t\t\tconst logEntry = `${separator}\\n[${timestamp}] CONVERSION ${label}\\n${separator}\\n${label}:\\n${content}\\n\\n`\n\n\t\t\tappendFileSync(timestampedPath, logEntry, 'utf-8')\n\t\t} catch {\n\t\t\t// Silently fail - don't crash if logging fails\n\t\t\t// This is a debug feature and shouldn't break the conversion\n\t\t}\n\t}\n\n\t/**\n\t * Generate timestamped log file path\n\t * Example: debug.log -> debug-20231202-161234.log\n\t */\n\tprivate static getTimestampedLogPath(logFilePath: string): string {\n\t\tconst dir = dirname(logFilePath)\n\t\tconst ext = extname(logFilePath)\n\t\tconst base = basename(logFilePath, ext)\n\n\t\t// Generate timestamp: YYYYMMDD-HHMMSS\n\t\tconst now = new Date()\n\t\tconst timestamp = [\n\t\t\tnow.getFullYear(),\n\t\t\tString(now.getMonth() + 1).padStart(2, '0'),\n\t\t\tString(now.getDate()).padStart(2, '0'),\n\t\t].join('') + '-' + [\n\t\t\tString(now.getHours()).padStart(2, '0'),\n\t\t\tString(now.getMinutes()).padStart(2, '0'),\n\t\t\tString(now.getSeconds()).padStart(2, '0'),\n\t\t].join('')\n\n\t\treturn join(dir, `${base}-${timestamp}${ext}`)\n\t}\n}\n","/**\n * Linear implementation of Issue Management Provider\n * Uses @linear/sdk for all operations\n */\n\nimport type {\n\tIssueManagementProvider,\n\tGetIssueInput,\n\tGetPRInput,\n\tGetCommentInput,\n\tCreateCommentInput,\n\tUpdateCommentInput,\n\tCreateIssueInput,\n\tCreateChildIssueInput,\n\tCreateDependencyInput,\n\tGetDependenciesInput,\n\tRemoveDependencyInput,\n\tGetChildIssuesInput,\n\tCloseIssueInput,\n\tReopenIssueInput,\n\tEditIssueInput,\n\tCreateIssueResult,\n\tIssueResult,\n\tPRResult,\n\tCommentDetailResult,\n\tCommentResult,\n\tDependenciesResult,\n\tChildIssueResult,\n} from './types.js'\nimport {\n\tfetchLinearIssue,\n\tcreateLinearComment,\n\tgetLinearComment,\n\tupdateLinearComment,\n\tfetchLinearIssueComments,\n\tcreateLinearIssue,\n\tcreateLinearChildIssue,\n\tcreateLinearIssueRelation,\n\tgetLinearIssueDependencies,\n\tfindLinearIssueRelation,\n\tdeleteLinearIssueRelation,\n\tgetLinearChildIssues,\n\tupdateLinearIssueState,\n\teditLinearIssue,\n} from '../utils/linear.js'\nimport { LinearMarkupConverter } from '../utils/linear-markup-converter.js'\nimport { processMarkdownImages } from '../utils/image-processor.js'\n\n/**\n * Linear-specific implementation of IssueManagementProvider\n */\nexport class LinearIssueManagementProvider implements IssueManagementProvider {\n\treadonly providerName = 'linear'\n\treadonly issuePrefix = ''\n\n\t/**\n\t * Cached team key extracted from issue identifiers (e.g., \"ENG-123\" -> \"ENG\")\n\t * Used as fallback when teamKey is not explicitly provided to createIssue()\n\t */\n\tprivate cachedTeamKey: string | undefined = undefined\n\n\t/**\n\t * Fetch issue details using Linear SDK\n\t */\n\tasync getIssue(input: GetIssueInput): Promise<IssueResult> {\n\t\tconst { number, includeComments = true } = input\n\n\t\t// Extract and cache team key from identifier (e.g., \"ENG-123\" -> \"ENG\")\n\t\t// This enables createIssue() to use the team key as a fallback\n\t\tconst match = number.match(/^([A-Z]{2,})-\\d+$/i)\n\t\tif (match?.[1]) {\n\t\t\tthis.cachedTeamKey = match[1].toUpperCase()\n\t\t}\n\n\t\t// Fetch issue - Linear uses alphanumeric identifiers like \"ENG-123\"\n\t\tconst raw = await fetchLinearIssue(number)\n\n\t\t// Map Linear state name to open/closed\n\t\tconst state = raw.state && (raw.state.toLowerCase().includes('done') || raw.state.toLowerCase().includes('completed') || raw.state.toLowerCase().includes('canceled'))\n\t\t\t? 'closed'\n\t\t\t: 'open'\n\n\t\t// Build result\n\t\tconst result: IssueResult = {\n\t\t\tid: raw.identifier,\n\t\t\ttitle: raw.title,\n\t\t\tbody: raw.description ?? '',\n\t\t\tstate,\n\t\t\turl: raw.url,\n\t\t\tprovider: 'linear',\n\t\t\tauthor: null, // Linear SDK doesn't return author in basic fetch\n\n\t\t\t// Linear-specific fields\n\t\t\tlinearState: raw.state,\n\t\t\tcreatedAt: raw.createdAt,\n\t\t\tupdatedAt: raw.updatedAt,\n\t\t}\n\n\t\t// Fetch comments if requested\n\t\tif (includeComments) {\n\t\t\ttry {\n\t\t\t\tconst comments = await this.fetchIssueComments(number)\n\t\t\t\tif (comments) {\n\t\t\t\t\tresult.comments = comments\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If comments fail, continue without them\n\t\t\t}\n\t\t}\n\n\t\t// Process images in body and comments to make them accessible\n\t\tresult.body = await processMarkdownImages(result.body, 'linear')\n\t\tif (result.comments) {\n\t\t\tfor (const comment of result.comments) {\n\t\t\t\tcomment.body = await processMarkdownImages(comment.body, 'linear')\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch pull request details\n\t * Linear does not support PRs - this throws an error directing to use GitHub\n\t */\n\tasync getPR(_input: GetPRInput): Promise<PRResult> {\n\t\tthrow new Error('Linear does not support pull requests. PRs exist only on GitHub. Use the GitHub provider for PR operations.')\n\t}\n\n\t/**\n\t * Fetch comments for an issue\n\t */\n\tprivate async fetchIssueComments(identifier: string): Promise<IssueResult['comments']> {\n\t\ttry {\n\t\t\tconst comments = await fetchLinearIssueComments(identifier)\n\n\t\t\treturn comments.map(comment => ({\n\t\t\t\tid: comment.id,\n\t\t\t\tbody: comment.body,\n\t\t\t\tcreatedAt: comment.createdAt,\n\t\t\t\tauthor: null, // Linear SDK doesn't return comment author info in basic fetch\n\t\t\t\t...(comment.updatedAt && { updatedAt: comment.updatedAt }),\n\t\t\t}))\n\t\t} catch {\n\t\t\treturn []\n\t\t}\n\t}\n\n\t/**\n\t * Fetch a specific comment by ID\n\t */\n\tasync getComment(input: GetCommentInput): Promise<CommentDetailResult> {\n\t\tconst { commentId } = input\n\n\t\tconst raw = await getLinearComment(commentId)\n\n\t\t// Process images to make them accessible\n\t\tconst processedBody = await processMarkdownImages(raw.body, 'linear')\n\n\t\treturn {\n\t\t\tid: raw.id,\n\t\t\tbody: processedBody,\n\t\t\tauthor: null, // Linear SDK doesn't return comment author info in basic fetch\n\t\t\tcreated_at: raw.createdAt,\n\t\t}\n\t}\n\n\t/**\n\t * Create a new comment on an issue\n\t */\n\tasync createComment(input: CreateCommentInput): Promise<CommentResult> {\n\t\tconst { number, body } = input\n\t\t// Note: Linear doesn't distinguish between issue and PR comments\n\t\t// (Linear doesn't have PRs - that's GitHub-specific)\n\n\t\t// Convert HTML details/summary blocks to Linear's collapsible format\n\t\tconst convertedBody = LinearMarkupConverter.convertToLinear(body)\n\n\t\tconst result = await createLinearComment(number, convertedBody)\n\n\t\treturn {\n\t\t\tid: result.id,\n\t\t\turl: result.url,\n\t\t\tcreated_at: result.createdAt,\n\t\t}\n\t}\n\n\t/**\n\t * Update an existing comment\n\t */\n\tasync updateComment(input: UpdateCommentInput): Promise<CommentResult> {\n\t\tconst { commentId, body } = input\n\n\t\t// Convert HTML details/summary blocks to Linear's collapsible format\n\t\tconst convertedBody = LinearMarkupConverter.convertToLinear(body)\n\n\t\tconst result = await updateLinearComment(commentId, convertedBody)\n\n\t\treturn {\n\t\t\tid: result.id,\n\t\t\turl: result.url,\n\t\t\tupdated_at: result.updatedAt,\n\t\t}\n\t}\n\n\t/**\n\t * Create a new issue\n\t */\n\tasync createIssue(input: CreateIssueInput): Promise<CreateIssueResult> {\n\t\tconst { title, body, labels, teamKey } = input\n\n\t\t// Fallback chain: explicit param > settings (via env) > cached key from getIssue()\n\t\tconst effectiveTeamKey = teamKey ?? process.env.LINEAR_TEAM_KEY ?? this.cachedTeamKey\n\n\t\tif (!effectiveTeamKey) {\n\t\t\tthrow new Error('teamKey is required for Linear issue creation. Configure issueManagement.linear.teamId in settings, or call getIssue first to extract the team from an issue identifier.')\n\t\t}\n\n\t\tconst result = await createLinearIssue(title, body, effectiveTeamKey, labels)\n\n\t\treturn {\n\t\t\tid: result.identifier,\n\t\t\turl: result.url,\n\t\t}\n\t}\n\n\t/**\n\t * Create a child issue linked to a parent issue\n\t * Linear supports atomic creation with parentId field\n\t */\n\tasync createChildIssue(input: CreateChildIssueInput): Promise<CreateIssueResult> {\n\t\tconst { parentId, title, body, labels, teamKey } = input\n\n\t\t// Fetch parent issue to get UUID (parentId in input is identifier like \"ENG-123\")\n\t\tconst parentIssue = await fetchLinearIssue(parentId)\n\n\t\t// Extract team key from parent identifier if not provided\n\t\tconst match = parentId.match(/^([A-Z]{2,})-\\d+$/i)\n\t\tconst effectiveTeamKey = teamKey ?? match?.[1]?.toUpperCase() ?? process.env.LINEAR_TEAM_KEY ?? this.cachedTeamKey\n\n\t\tif (!effectiveTeamKey) {\n\t\t\tthrow new Error('teamKey is required for Linear child issue creation. Provide teamKey parameter or use a parent identifier with team prefix.')\n\t\t}\n\n\t\t// Create child issue with parent's UUID\n\t\tconst result = await createLinearChildIssue(\n\t\t\ttitle,\n\t\t\tbody,\n\t\t\teffectiveTeamKey,\n\t\t\tparentIssue.id, // UUID, not identifier\n\t\t\tlabels\n\t\t)\n\n\t\treturn {\n\t\t\tid: result.identifier,\n\t\t\turl: result.url,\n\t\t}\n\t}\n\n\t/**\n\t * Create a blocking dependency between two issues\n\t */\n\tasync createDependency(input: CreateDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue } = input\n\n\t\t// Fetch both issues to get their UUIDs\n\t\tconst [blockingIssueData, blockedIssueData] = await Promise.all([\n\t\t\tfetchLinearIssue(blockingIssue),\n\t\t\tfetchLinearIssue(blockedIssue),\n\t\t])\n\n\t\t// Create the blocking relation (blockingIssue blocks blockedIssue)\n\t\tawait createLinearIssueRelation(blockingIssueData.id, blockedIssueData.id)\n\t}\n\n\t/**\n\t * Get dependencies for an issue\n\t */\n\tasync getDependencies(input: GetDependenciesInput): Promise<DependenciesResult> {\n\t\tconst { number, direction } = input\n\n\t\treturn await getLinearIssueDependencies(number, direction)\n\t}\n\n\t/**\n\t * Remove a blocking dependency between two issues\n\t */\n\tasync removeDependency(input: RemoveDependencyInput): Promise<void> {\n\t\tconst { blockingIssue, blockedIssue } = input\n\n\t\t// Find the relation ID\n\t\tconst relationId = await findLinearIssueRelation(blockingIssue, blockedIssue)\n\n\t\tif (!relationId) {\n\t\t\tthrow new Error(`No blocking dependency found from ${blockingIssue} to ${blockedIssue}`)\n\t\t}\n\n\t\t// Delete the relation\n\t\tawait deleteLinearIssueRelation(relationId)\n\t}\n\n\t/**\n\t * Get child issues of a parent issue\n\t */\n\tasync getChildIssues(input: GetChildIssuesInput): Promise<ChildIssueResult[]> {\n\t\tconst { number } = input\n\t\t// repo is ignored for Linear\n\t\treturn await getLinearChildIssues(number)\n\t}\n\n\t/**\n\t * Close an issue by transitioning to \"Done\" state\n\t */\n\tasync closeIssue(input: CloseIssueInput): Promise<void> {\n\t\tconst { number } = input\n\t\t// repo is ignored for Linear\n\t\tawait updateLinearIssueState(number, 'Done')\n\t}\n\n\t/**\n\t * Reopen a closed issue by transitioning to \"Todo\" state\n\t */\n\tasync reopenIssue(input: ReopenIssueInput): Promise<void> {\n\t\tconst { number } = input\n\t\t// repo is ignored for Linear\n\t\tawait updateLinearIssueState(number, 'Todo')\n\t}\n\n\t/**\n\t * Edit an issue's properties\n\t * State changes are delegated to closeIssue/reopenIssue\n\t */\n\tasync editIssue(input: EditIssueInput): Promise<void> {\n\t\tconst { number, title, body, state } = input\n\t\t// repo and labels are ignored for Linear\n\n\t\t// Handle state changes via close/reopen\n\t\tif (state === 'closed') {\n\t\t\tawait this.closeIssue({ number })\n\t\t} else if (state === 'open') {\n\t\t\tawait this.reopenIssue({ number })\n\t\t}\n\n\t\t// Handle title/body updates\n\t\tif (title !== undefined || body !== undefined) {\n\t\t\tawait editLinearIssue(number, {\n\t\t\t\t...(title !== undefined && { title }),\n\t\t\t\t...(body !== undefined && { description: body }),\n\t\t\t})\n\t\t}\n\t}\n}\n","/**\n * Jira utilities for the issues command\n * Follows the pattern of fetchGitHubIssueList and fetchLinearIssueList\n */\n\nimport type { JiraApiClient, JiraIssue } from '../lib/providers/jira/index.js'\n\n/**\n * Escape a string value for safe interpolation into JQL queries.\n * Prevents JQL injection by escaping backslashes and double quotes.\n */\nexport function escapeJql(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n}\n\nexport interface JiraIssueListItem {\n id: string // issue key e.g. \"PROJ-123\"\n title: string // fields.summary\n updatedAt: string // fields.updated (ISO string)\n url: string // {host}/browse/{key}\n state: string // fields.status.name\n}\n\n/**\n * Fetch a list of Jira issues for a project, excluding done statuses\n * @param client - Configured JiraApiClient instance\n * @param options - Fetch options\n * @returns Array of issues sorted by updated date\n */\nexport async function fetchJiraIssueList(\n client: JiraApiClient,\n options: {\n host: string\n projectKey: string\n doneStatuses?: string[]\n limit?: number\n sprint?: string | undefined\n mine?: boolean | undefined\n },\n): Promise<JiraIssueListItem[]> {\n const { host, projectKey, doneStatuses = ['Done'], limit = 100, sprint, mine } = options\n\n // Build JQL with status exclusion\n const statusExclusions = doneStatuses.map((s) => `\"${escapeJql(s)}\"`).join(', ')\n let jql = `project = \"${escapeJql(projectKey)}\" AND status NOT IN (${statusExclusions})`\n\n // Add sprint filter\n if (sprint === 'current') {\n jql += ' AND sprint in openSprints()'\n } else if (sprint) {\n jql += ` AND sprint = \"${escapeJql(sprint)}\"`\n }\n\n // Add assignee filter\n if (mine) {\n jql += ' AND assignee = currentUser()'\n }\n\n jql += ' ORDER BY updated DESC'\n\n const issues: JiraIssue[] = await client.searchIssues(jql)\n\n const baseUrl = host.replace(/\\/$/, '')\n\n return issues.slice(0, limit).map((issue) => ({\n id: issue.key,\n title: issue.fields.summary,\n updatedAt: issue.fields.updated,\n url: `${baseUrl}/browse/${issue.key}`,\n state: issue.fields.status.name,\n }))\n}\n","// JiraApiClient - REST API wrapper for Jira operations\n// Handles authentication and common API request patterns\n\nimport https from 'node:https'\nimport { getLogger } from '../../../utils/logger-context.js'\nimport { markdownToAdf } from './AdfMarkdownConverter.js'\n\n/**\n * Jira API configuration\n */\nexport interface JiraConfig {\n\thost: string // e.g., \"https://yourcompany.atlassian.net\"\n\tusername: string // email address or username\n\tapiToken: string // API token from Atlassian account\n}\n\n/**\n * Jira issue response from API\n */\n/**\n * Jira issue link (relationship between issues)\n */\nexport interface JiraIssueLink {\n\tid: string\n\ttype: {\n\t\tid: string\n\t\tname: string\n\t\tinward: string\n\t\toutward: string\n\t}\n\tinwardIssue?: {\n\t\tid: string\n\t\tkey: string\n\t\tfields: {\n\t\t\tsummary: string\n\t\t\tstatus: { name: string }\n\t\t}\n\t}\n\toutwardIssue?: {\n\t\tid: string\n\t\tkey: string\n\t\tfields: {\n\t\t\tsummary: string\n\t\t\tstatus: { name: string }\n\t\t}\n\t}\n}\n\nexport interface JiraIssue {\n\tid: string\n\tkey: string\n\tfields: {\n\t\tsummary: string\n\t\tdescription: string | null | unknown // Can be string, ADF object, or null\n\t\tstatus: {\n\t\t\tname: string\n\t\t}\n\t\tissuetype: {\n\t\t\tname: string\n\t\t}\n\t\tproject: {\n\t\t\tkey: string\n\t\t\tname: string\n\t\t}\n\t\tassignee: {\n\t\t\tdisplayName: string\n\t\t\temailAddress: string\n\t\t\taccountId: string\n\t\t} | null\n\t\treporter: {\n\t\t\tdisplayName: string\n\t\t\temailAddress: string\n\t\t\taccountId: string\n\t\t}\n\t\tlabels: string[]\n\t\tcreated: string\n\t\tupdated: string\n\t\tissuelinks?: JiraIssueLink[]\n\t\tparent?: {\n\t\t\tid: string\n\t\t\tkey: string\n\t\t\tfields: {\n\t\t\t\tsummary: string\n\t\t\t\tstatus: { name: string }\n\t\t\t}\n\t\t}\n\t\t[key: string]: unknown // Allow additional fields\n\t}\n\t[key: string]: unknown // Allow additional top-level fields\n}\n\n/**\n * Jira comment response from API\n */\nexport interface JiraComment {\n\tid: string\n\tauthor: {\n\t\tdisplayName: string\n\t\temailAddress: string\n\t\taccountId: string\n\t}\n\tbody: string | unknown // Can be string or ADF object\n\tcreated: string\n\tupdated: string\n\t[key: string]: unknown\n}\n\n/**\n * Jira transition response from API\n */\nexport interface JiraTransition {\n\tid: string\n\tname: string\n\tto: {\n\t\tid: string\n\t\tname: string\n\t}\n}\n\n/**\n * JiraApiClient provides low-level REST API access to Jira\n * \n * Authentication: Basic Auth with username and API token\n * API Reference: https://developer.atlassian.com/cloud/jira/platform/rest/v3/\n */\nexport class JiraApiClient {\n\tprivate readonly baseUrl: string\n\tprivate readonly authHeader: string\n\n\tconstructor(config: JiraConfig) {\n\t\tthis.baseUrl = `${config.host.replace(/\\/$/, '')}/rest/api/3`\n\t\t\n\t\t// Create Basic Auth header\n\t\tconst credentials = Buffer.from(`${config.username}:${config.apiToken}`).toString('base64')\n\t\tthis.authHeader = `Basic ${credentials}`\n\t}\n\n\t/**\n\t * Make an HTTP request to Jira API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: unknown\n\t): Promise<T> {\n\t\tconst url = new URL(`${this.baseUrl}${endpoint}`)\n\t\tgetLogger().debug(`Jira API ${method} request`, { url: url.toString() })\n\t\tif (body) {\n\t\t\tgetLogger().debug('Jira API request body', JSON.stringify(body, null, 2))\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst options: https.RequestOptions = {\n\t\t\t\thostname: url.hostname,\n\t\t\t\tport: url.port || 443,\n\t\t\t\tpath: url.pathname + url.search,\n\t\t\t\tmethod,\n\t\t\t\theaders: {\n\t\t\t\t\t'Authorization': this.authHeader,\n\t\t\t\t\t'Accept': 'application/json',\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst req = https.request({ ...options, timeout: 30000 }, (res) => {\n\t\t\t\tconst chunks: Buffer[] = []\n\n\t\t\t\tres.on('data', (chunk: Buffer) => {\n\t\t\t\t\tchunks.push(chunk)\n\t\t\t\t})\n\n\t\t\t\tres.on('end', () => {\n\t\t\t\t\tconst data = Buffer.concat(chunks).toString('utf8')\n\n\t\t\t\t\tif (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {\n\t\t\t\t\t\tlet errorDetail = data\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst parsed = JSON.parse(data)\n\t\t\t\t\t\t\tconst parts: string[] = []\n\t\t\t\t\t\t\tif (parsed.errorMessages?.length) {\n\t\t\t\t\t\t\t\tparts.push(`messages: ${parsed.errorMessages.join(', ')}`)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (parsed.errors && Object.keys(parsed.errors).length) {\n\t\t\t\t\t\t\t\tparts.push(`field errors: ${JSON.stringify(parsed.errors)}`)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (parts.length) {\n\t\t\t\t\t\t\t\terrorDetail = parts.join('; ')\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Use raw data if not JSON\n\t\t\t\t\t\t}\n\t\t\t\t\t\treject(new Error(`Jira API error (${res.statusCode}): ${errorDetail}`))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Handle empty response (e.g., 204 No Content)\n\t\t\t\t\tif (res.statusCode === 204 || !data) {\n\t\t\t\t\t\tresolve({} as T)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresolve(JSON.parse(data) as T)\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treject(new Error(`Failed to parse Jira API response: ${error}`))\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\n\t\t\treq.on('timeout', () => {\n\t\t\t\treq.destroy()\n\t\t\t\treject(new Error('Jira API request timed out after 30 seconds'))\n\t\t\t})\n\n\t\t\treq.on('error', (error) => {\n\t\t\t\treject(new Error(`Jira API request failed: ${error.message}`))\n\t\t\t})\n\n\t\t\tif (body) {\n\t\t\t\treq.write(JSON.stringify(body))\n\t\t\t}\n\n\t\t\treq.end()\n\t\t})\n\t}\n\n\t/**\n\t * Make a GET request to Jira API\n\t */\n\tprivate async get<T>(endpoint: string): Promise<T> {\n\t\treturn this.request<T>('GET', endpoint)\n\t}\n\n\t/**\n\t * Make a POST request to Jira API\n\t */\n\tprivate async post<T>(endpoint: string, body: unknown): Promise<T> {\n\t\treturn this.request<T>('POST', endpoint, body)\n\t}\n\n\t/**\n\t * Make a PUT request to Jira API\n\t */\n\tprivate async put<T>(endpoint: string, body: unknown): Promise<T> {\n\t\treturn this.request<T>('PUT', endpoint, body)\n\t}\n\n\t/**\n\t * Make a DELETE request to Jira API\n\t */\n\tprivate async delete(endpoint: string): Promise<void> {\n\t\tawait this.request('DELETE', endpoint)\n\t}\n\n\t/**\n\t * Fetch an issue by key (e.g., \"PROJ-123\")\n\t */\n\tasync getIssue(issueKey: string): Promise<JiraIssue> {\n\t\treturn this.get<JiraIssue>(`/issue/${issueKey}`)\n\t}\n\n\t/**\n\t * Add a comment to an issue\n\t * Accepts Markdown content which is converted to ADF for Jira\n\t */\n\tasync addComment(issueKey: string, body: string): Promise<JiraComment> {\n\t\tconst adfBody = markdownToAdf(body);\n\t\tgetLogger().debug('Adding comment to Jira issue', { issueKey, bodyLength: body.length })\n\t\treturn this.post<JiraComment>(`/issue/${issueKey}/comment`, {\n\t\t\tbody: adfBody\n\t\t})\n\t}\n\n\t/**\n\t * Get all comments for an issue\n\t */\n\tasync getComments(issueKey: string): Promise<JiraComment[]> {\n\t\tconst response = await this.get<{ comments: JiraComment[]; total: number; maxResults: number }>(`/issue/${issueKey}/comment?maxResults=5000`)\n\t\tif (response.total > response.comments.length) {\n\t\t\tgetLogger().warn(`Comments truncated for issue ${issueKey}: returned ${response.comments.length} of ${response.total} total comments`)\n\t\t}\n\t\treturn response.comments\n\t}\n\n\t/**\n\t * Update a comment on an issue\n\t * Accepts Markdown content which is converted to ADF for Jira\n\t */\n\tasync updateComment(issueKey: string, commentId: string, body: string): Promise<JiraComment> {\n\t\treturn this.put<JiraComment>(`/issue/${issueKey}/comment/${commentId}`, {\n\t\t\tbody: markdownToAdf(body),\n\t\t})\n\t}\n\n\t/**\n\t * Get available transitions for an issue\n\t */\n\tasync getTransitions(issueKey: string): Promise<JiraTransition[]> {\n\t\tconst response = await this.get<{ transitions: JiraTransition[] }>(`/issue/${issueKey}/transitions`)\n\t\treturn response.transitions\n\t}\n\n\t/**\n\t * Transition an issue to a new state\n\t */\n\tasync transitionIssue(issueKey: string, transitionId: string): Promise<void> {\n\t\tawait this.post(`/issue/${issueKey}/transitions`, {\n\t\t\ttransition: {\n\t\t\t\tid: transitionId,\n\t\t\t},\n\t\t})\n\t}\n\n\t/**\n\t * Create a new issue\n\t * Accepts Markdown description which is converted to ADF for Jira\n\t */\n\tasync createIssue(projectKey: string, summary: string, description: string, issueType = 'Task'): Promise<JiraIssue> {\n\t\treturn this.post<JiraIssue>('/issue', {\n\t\t\tfields: {\n\t\t\t\tproject: {\n\t\t\t\t\tkey: projectKey,\n\t\t\t\t},\n\t\t\t\tsummary,\n\t\t\t\tdescription: markdownToAdf(description),\n\t\t\t\tissuetype: {\n\t\t\t\t\tname: issueType,\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t}\n\n\t/**\n\t * Update an issue's fields (summary, description)\n\t * @param issueKey - Jira issue key (e.g., \"PROJ-123\")\n\t * @param fields - Fields to update\n\t */\n\tasync updateIssue(issueKey: string, fields: { summary?: string; description?: string }): Promise<void> {\n\t\tconst updateFields: Record<string, unknown> = {}\n\t\tif (fields.summary !== undefined) {\n\t\t\tupdateFields.summary = fields.summary\n\t\t}\n\t\tif (fields.description !== undefined) {\n\t\t\tupdateFields.description = markdownToAdf(fields.description)\n\t\t}\n\n\t\tawait this.put(`/issue/${issueKey}`, { fields: updateFields })\n\t}\n\n\t/**\n\t * Create an issue with a parent (subtask or child issue)\n\t * Accepts Markdown description which is converted to ADF for Jira\n\t */\n\tasync createIssueWithParent(\n\t\tprojectKey: string,\n\t\tsummary: string,\n\t\tdescription: string,\n\t\tparentKey: string,\n\t\tissueType = 'Subtask'\n\t): Promise<JiraIssue> {\n\t\treturn this.post<JiraIssue>('/issue', {\n\t\t\tfields: {\n\t\t\t\tproject: {\n\t\t\t\t\tkey: projectKey,\n\t\t\t\t},\n\t\t\t\tsummary,\n\t\t\t\tdescription: markdownToAdf(description),\n\t\t\t\tissuetype: {\n\t\t\t\t\tname: issueType,\n\t\t\t\t},\n\t\t\t\tparent: {\n\t\t\t\t\tkey: parentKey,\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t}\n\n\t/**\n\t * Create an issue link (dependency/relationship between issues)\n\t * @param inwardKey - The issue key for the inward side (e.g., the blocked issue)\n\t * @param outwardKey - The issue key for the outward side (e.g., the blocking issue)\n\t * @param linkType - The link type name (e.g., \"Blocks\")\n\t */\n\tasync createIssueLink(inwardKey: string, outwardKey: string, linkType: string): Promise<void> {\n\t\tawait this.post('/issueLink', {\n\t\t\ttype: {\n\t\t\t\tname: linkType,\n\t\t\t},\n\t\t\tinwardIssue: {\n\t\t\t\tkey: inwardKey,\n\t\t\t},\n\t\t\toutwardIssue: {\n\t\t\t\tkey: outwardKey,\n\t\t\t},\n\t\t})\n\t}\n\n\t/**\n\t * Delete an issue link by ID\n\t */\n\tasync deleteIssueLink(linkId: string): Promise<void> {\n\t\tawait this.delete(`/issueLink/${linkId}`)\n\t}\n\n\t/**\n\t * Search issues using JQL\n\t * Automatically paginates through all results up to MAX_SEARCH_RESULTS.\n\t */\n\tasync searchIssues(jql: string): Promise<JiraIssue[]> {\n\t\tconst MAX_SEARCH_RESULTS = 5000\n\t\tconst allIssues: JiraIssue[] = []\n\t\tlet nextPageToken: string | undefined\n\t\tconst maxResults = 100\n\n\t\twhile (allIssues.length < MAX_SEARCH_RESULTS) {\n\t\t\tconst body: Record<string, unknown> = {\n\t\t\t\tjql,\n\t\t\t\tmaxResults,\n\t\t\t\tfields: [\n\t\t\t\t\t'summary', 'description', 'status', 'issuetype', 'project',\n\t\t\t\t\t'assignee', 'reporter', 'labels', 'created', 'updated',\n\t\t\t\t\t'issuelinks', 'parent',\n\t\t\t\t],\n\t\t\t}\n\t\t\tif (nextPageToken) {\n\t\t\t\tbody.nextPageToken = nextPageToken\n\t\t\t}\n\t\t\tconst response = await this.post<{ issues: JiraIssue[]; nextPageToken?: string }>(\n\t\t\t\t'/search/jql',\n\t\t\t\tbody\n\t\t\t)\n\t\t\tallIssues.push(...response.issues)\n\n\t\t\tif (!response.nextPageToken || response.issues.length === 0) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tnextPageToken = response.nextPageToken\n\t\t}\n\n\t\tif (allIssues.length >= MAX_SEARCH_RESULTS) {\n\t\t\tgetLogger().warn(`Search results truncated at ${MAX_SEARCH_RESULTS} issues. The query matched more results than the safety cap allows.`, { jql, returnedCount: allIssues.length })\n\t\t}\n\n\t\treturn allIssues\n\t}\n\n\t/**\n\t * Test connection to Jira API\n\t */\n\tasync testConnection(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.get('/myself')\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\tif (message.includes('Jira API error (401)') || message.includes('Jira API error (403)')) {\n\t\t\t\tgetLogger().error('Jira connection test failed: authentication error', { error })\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n}\n","import { AsyncLocalStorage } from 'node:async_hooks'\nimport { logger as defaultLogger, type Logger } from './logger.js'\n\nconst loggerStorage = new AsyncLocalStorage<Logger>()\n\n/**\n * Get current logger from context, or fall back to default\n * Use this instead of importing logger directly to support JSON mode\n */\nexport function getLogger(): Logger {\n return loggerStorage.getStore() ?? defaultLogger\n}\n\n/**\n * Run code with a specific logger in context\n * All code within the callback (including async operations) will use the provided logger\n */\nexport function withLogger<T>(logger: Logger, fn: () => T | Promise<T>): T | Promise<T> {\n return loggerStorage.run(logger, fn)\n}\n","// AdfMarkdownConverter - Converts between Atlassian Document Format (ADF) and Markdown\n// Uses extended-markdown-adf-parser for bidirectional conversion\n\nimport { ADFDocument, Parser } from 'extended-markdown-adf-parser'\n\nconst parser = new Parser()\n\n/**\n * Represents a node in the ADF tree structure\n */\ninterface AdfNode {\n\ttype: string\n\tcontent?: AdfNode[]\n\tmarks?: Array<{ type: string; attrs?: Record<string, unknown> }>\n\ttext?: string\n\tattrs?: Record<string, unknown>\n}\n\n/**\n * Recursively traverse ADF tree and ensure code-marked text only has the code mark.\n * ADF specification requires that code marks are standalone - no other marks allowed.\n */\nfunction sanitizeCodeMarks(node: AdfNode): AdfNode {\n\t// If node has marks and one of them is 'code', keep only the code mark\n\tif (node.marks?.some((mark) => mark.type === 'code')) {\n\t\tnode.marks = [{ type: 'code' }]\n\t}\n\n\t// Recursively process child nodes\n\tif (node.content && Array.isArray(node.content)) {\n\t\tnode.content = node.content.map((child) => sanitizeCodeMarks(child))\n\t}\n\n\treturn node\n}\n\n/**\n * Block-level ADF node types that can appear directly inside table cells.\n * Any node type NOT in this set is considered inline and must be wrapped in a paragraph.\n */\nconst BLOCK_LEVEL_TYPES = new Set([\n\t'paragraph',\n\t'bulletList',\n\t'orderedList',\n\t'codeBlock',\n\t'heading',\n\t'blockquote',\n\t'rule',\n\t'mediaGroup',\n\t'nestedExpand',\n\t'panel',\n\t'table',\n\t'taskList',\n\t'decisionList',\n\t'mediaSingle',\n])\n\n/**\n * Recursively traverse ADF tree and ensure tableCell/tableHeader content\n * is wrapped in block-level nodes. Jira's ADF spec requires that table cells\n * only contain block-level nodes (like paragraph), not inline nodes (like text).\n */\nfunction wrapTableCellContent(node: AdfNode): AdfNode {\n\t// Recursively process child nodes first\n\tif (node.content && Array.isArray(node.content)) {\n\t\tnode.content = node.content.map((child) => wrapTableCellContent(child))\n\t}\n\n\t// Only process tableCell and tableHeader nodes\n\tif (node.type !== 'tableCell' && node.type !== 'tableHeader') {\n\t\treturn node\n\t}\n\n\tif (!node.content || node.content.length === 0) {\n\t\treturn node\n\t}\n\n\tconst allInline = node.content.every((child) => !BLOCK_LEVEL_TYPES.has(child.type))\n\n\tif (allInline) {\n\t\t// All children are inline - wrap them all in a single paragraph\n\t\tnode.content = [{ type: 'paragraph', content: node.content }]\n\t} else {\n\t\t// Mixed block and inline nodes - wrap consecutive inline runs in paragraphs\n\t\tconst newContent: AdfNode[] = []\n\t\tlet inlineRun: AdfNode[] = []\n\n\t\tfor (const child of node.content) {\n\t\t\tif (BLOCK_LEVEL_TYPES.has(child.type)) {\n\t\t\t\t// Flush any accumulated inline nodes as a paragraph\n\t\t\t\tif (inlineRun.length > 0) {\n\t\t\t\t\tnewContent.push({ type: 'paragraph', content: inlineRun })\n\t\t\t\t\tinlineRun = []\n\t\t\t\t}\n\t\t\t\tnewContent.push(child)\n\t\t\t} else {\n\t\t\t\tinlineRun.push(child)\n\t\t\t}\n\t\t}\n\n\t\t// Flush remaining inline nodes\n\t\tif (inlineRun.length > 0) {\n\t\t\tnewContent.push({ type: 'paragraph', content: inlineRun })\n\t\t}\n\n\t\tnode.content = newContent\n\t}\n\n\treturn node\n}\n\n/**\n * Counter for generating unique task item local IDs\n */\nlet taskIdCounter = 0\n\n/**\n * Represents a contiguous block of checkbox bullet items extracted from markdown.\n */\ninterface CheckboxBlock {\n\tstates: Array<'DONE' | 'TODO'>\n\ttexts: string[]\n}\n\n/**\n * Get the canonical plain text for a markdown string by parsing it through\n * the same ADF parser used for the full document. This ensures consistent\n * text matching regardless of markdown formatting quirks (e.g., literal\n * asterisks being misinterpreted as italic markers).\n */\nfunction getCanonicalPlainText(text: string): string {\n\tconst miniAdf = parser.markdownToAdf(text)\n\treturn getPlainText(miniAdf as AdfNode)\n}\n\n/**\n * Parse the original markdown to find contiguous blocks of bullet items where\n * ALL items use `- [x] ` or `- [ ] ` syntax (also supports `*` and `+` markers).\n * Returns the blocks in document order. The text field stores the canonical plain\n * text (obtained by mini-parsing through the ADF parser) for matching against ADF content.\n *\n * Only groups items at the same indentation level into a block. When indent changes,\n * the current block is broken to avoid mismatching nested checkbox lists.\n */\nfunction extractCheckboxBlocks(markdown: string): CheckboxBlock[] {\n\tconst lines = markdown.split('\\n')\n\tconst blocks: CheckboxBlock[] = []\n\n\tlet i = 0\n\twhile (i < lines.length) {\n\t\t// Collect contiguous bullet lines (checkbox or regular) at the same indent level\n\t\t// Store raw text first, then compute canonical text after collecting continuations\n\t\tconst bulletLines: Array<{ isCheckbox: boolean; state: 'DONE' | 'TODO' | null; rawText: string }> = []\n\t\tlet blockIndent: number | null = null\n\n\t\twhile (i < lines.length) {\n\t\t\tconst line = lines[i] ?? ''\n\t\t\tconst checkboxMatch = line.match(/^(\\s*)[-*+] \\[([ xX])\\] (.*)$/)\n\t\t\tif (checkboxMatch) {\n\t\t\t\tconst indent = checkboxMatch[1]?.length ?? 0\n\t\t\t\tif (blockIndent === null) {\n\t\t\t\t\tblockIndent = indent\n\t\t\t\t} else if (indent !== blockIndent) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tconst state = checkboxMatch[2] === ' ' ? 'TODO' : 'DONE'\n\t\t\t\tbulletLines.push({ isCheckbox: true, state, rawText: checkboxMatch[3] ?? '' })\n\t\t\t\ti++\n\t\t\t} else if (line.match(/^\\s*[-*+] /)) {\n\t\t\t\t// Regular bullet item (no checkbox) - check indent\n\t\t\t\tconst indentMatch = line.match(/^(\\s*)/)\n\t\t\t\tconst indent = indentMatch?.[1]?.length ?? 0\n\t\t\t\tif (blockIndent === null) {\n\t\t\t\t\tblockIndent = indent\n\t\t\t\t} else if (indent !== blockIndent) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tbulletLines.push({ isCheckbox: false, state: null, rawText: '' })\n\t\t\t\ti++\n\t\t\t} else if (bulletLines.length > 0 && line.match(/^\\s/) && line.trim() !== '') {\n\t\t\t\t// Continuation line of the previous list item (indented, non-empty)\n\t\t\t\t// Append to the last bullet item's raw text\n\t\t\t\tconst lastItem = bulletLines[bulletLines.length - 1]\n\t\t\t\tif (lastItem) {\n\t\t\t\t\tlastItem.rawText += '\\n' + line.trim()\n\t\t\t\t}\n\t\t\t\ti++\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (bulletLines.length > 0) {\n\t\t\tconst allCheckboxes = bulletLines.every((l) => l.isCheckbox)\n\t\t\tif (allCheckboxes) {\n\t\t\t\tblocks.push({\n\t\t\t\t\tstates: bulletLines.map((l) => l.state as 'DONE' | 'TODO'),\n\t\t\t\t\ttexts: bulletLines.map((l) => getCanonicalPlainText(l.rawText)),\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\ti++\n\t\t}\n\t}\n\n\treturn blocks\n}\n\n/**\n * Recursively extract all plain text from an ADF node,\n * handling formatted content (bold, italic, code, links).\n */\nfunction getPlainText(node: AdfNode): string {\n\tif (node.type === 'text' && node.text !== undefined) return node.text\n\tif (!node.content) return ''\n\treturn node.content.map(getPlainText).join('')\n}\n\n/**\n * Recursively traverse ADF tree and convert bulletList nodes that match\n * extracted checkbox blocks into taskList/taskItem nodes.\n *\n * Uses a cursor into the blocks array to match bulletLists in document order.\n * The cursor is shared across the entire tree traversal.\n */\nfunction convertCheckboxesToTaskList(node: AdfNode, blocks: CheckboxBlock[]): AdfNode {\n\tconst cursor = { index: 0 }\n\treturn convertCheckboxesRecursive(node, blocks, cursor)\n}\n\nfunction convertCheckboxesRecursive(\n\tnode: AdfNode,\n\tblocks: CheckboxBlock[],\n\tcursor: { index: number }\n): AdfNode {\n\tif (node.type === 'bulletList' && node.content && node.content.length > 0 && cursor.index < blocks.length) {\n\t\tconst block = blocks[cursor.index]\n\t\tif (!block) return node\n\n\t\t// Check if this bulletList matches the next checkbox block\n\t\tif (node.content.length === block.states.length) {\n\t\t\tconst plaintexts = node.content.map((listItem) => getPlainText(listItem))\n\t\t\tconst matches = plaintexts.every((text, i) => text === block.texts[i])\n\n\t\t\tif (matches) {\n\t\t\t\t// Guard: all listItems must have simple structure (single paragraph child)\n\t\t\t\t// Multi-paragraph or complex items cannot be safely converted to taskItem\n\t\t\t\tconst allSimple = node.content.every((item) => {\n\t\t\t\t\treturn item.content?.length === 1 && item.content[0]?.type === 'paragraph'\n\t\t\t\t})\n\t\t\t\tif (!allSimple) {\n\t\t\t\t\tcursor.index++ // Consume the block even though we can't convert it\n\t\t\t\t\treturn node // Items too complex for taskItem\n\t\t\t\t}\n\n\t\t\t\t// Convert bulletList -> taskList\n\t\t\t\tcursor.index++\n\t\t\t\tnode.type = 'taskList'\n\t\t\t\tnode.attrs = { localId: `tasklist-${++taskIdCounter}` }\n\n\t\t\t\tfor (const [i, listItem] of node.content.entries()) {\n\t\t\t\t\tlistItem.type = 'taskItem'\n\t\t\t\t\tlistItem.attrs = {\n\t\t\t\t\t\tlocalId: `task-${++taskIdCounter}`,\n\t\t\t\t\t\tstate: block.states[i],\n\t\t\t\t\t}\n\n\t\t\t\t\t// Unwrap paragraph: listItem has paragraph > [inline nodes],\n\t\t\t\t\t// but taskItem should have inline nodes directly\n\t\t\t\t\tconst firstChild = listItem.content?.[0]\n\t\t\t\t\tif (firstChild?.type === 'paragraph' && firstChild.content) {\n\t\t\t\t\t\tlistItem.content = firstChild.content\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn node\n\t\t\t}\n\t\t}\n\t}\n\n\t// Recursively process child nodes\n\tif (node.content && Array.isArray(node.content)) {\n\t\tnode.content = node.content.map((child) => convertCheckboxesRecursive(child, blocks, cursor))\n\t}\n\n\treturn node\n}\n\n/**\n * Convert HTML details/summary blocks to ADF expand fence syntax\n * The extended-markdown-adf-parser library supports ~~~expand title=\"...\"~~~ syntax\n * but not HTML <details><summary> tags\n *\n * @param markdown - Markdown string potentially containing HTML details/summary blocks\n * @returns Markdown with details/summary converted to ADF expand fence syntax\n */\nexport function convertDetailsToExpandSyntax(markdown: string): string {\n\tif (!markdown) return markdown\n\n\t// Process from innermost to outermost to handle nesting correctly\n\tlet previousText = ''\n\tlet currentText = markdown\n\n\twhile (previousText !== currentText) {\n\t\tpreviousText = currentText\n\t\t// Match <details> blocks with optional attributes on the tags\n\t\tcurrentText = currentText.replace(\n\t\t\t/<details[^>]*>\\s*<summary[^>]*>([\\s\\S]*?)<\\/summary>([\\s\\S]*?)<\\/details>/gi,\n\t\t\t(_match, summary, content) => {\n\t\t\t\t// Clean up the summary - trim whitespace and decode HTML entities\n\t\t\t\tconst cleanSummary = summary\n\t\t\t\t\t.trim()\n\t\t\t\t\t.replace(/&lt;/g, '<')\n\t\t\t\t\t.replace(/&gt;/g, '>')\n\t\t\t\t\t.replace(/&amp;/g, '&')\n\t\t\t\t\t.replace(/&quot;/g, '\"')\n\t\t\t\t\t.replace(/&#39;/g, \"'\")\n\n\t\t\t\t// Clean up the content - trim and normalize excessive blank lines\n\t\t\t\tlet cleanContent = content.trim()\n\t\t\t\tcleanContent = cleanContent.replace(/\\n{3,}/g, '\\n\\n')\n\n\t\t\t\t// Build ADF expand fence syntax\n\t\t\t\tif (cleanContent) {\n\t\t\t\t\treturn `~~~expand title=\"${cleanSummary}\"\\n${cleanContent}\\n~~~`\n\t\t\t\t} else {\n\t\t\t\t\treturn `~~~expand title=\"${cleanSummary}\"\\n~~~`\n\t\t\t\t}\n\t\t\t}\n\t\t)\n\t}\n\n\treturn currentText\n}\n\n/**\n * Convert ADF (Atlassian Document Format) to Markdown\n * Used when reading issue descriptions and comments from Jira\n *\n * @param adf - ADF object, string, null, or undefined\n * @returns Markdown string\n */\nexport function adfToMarkdown(adf: unknown): string {\n\t// Handle null/undefined\n\tif (!adf) return ''\n\n\t// Handle plain string (already text, not ADF)\n\tif (typeof adf === 'string') return adf\n\n\t// Convert ADF object to markdown\n\treturn parser.adfToMarkdown(adf as ADFDocument)\n}\n\n/**\n * Convert Markdown to ADF (Atlassian Document Format)\n * Used when writing issue descriptions and comments to Jira\n *\n * @param markdown - Markdown string\n * @returns ADF object suitable for Jira API v3\n */\nexport function markdownToAdf(markdown: string): object {\n\tif (!markdown) {\n\t\treturn { type: 'doc', version: 1, content: [] }\n\t}\n\t// Reset task ID counter for deterministic output\n\ttaskIdCounter = 0\n\t// Extract checkbox info BEFORE conversion (library will strip [x]/[ ])\n\tconst checkboxBlocks = extractCheckboxBlocks(markdown)\n\t// Pre-process: convert details/summary to expand syntax\n\tconst preprocessed = convertDetailsToExpandSyntax(markdown)\n\tconst adf = parser.markdownToAdf(preprocessed)\n\t// Post-process the ADF tree\n\tlet result = sanitizeCodeMarks(adf as AdfNode)\n\tresult = wrapTableCellContent(result)\n\tresult = convertCheckboxesToTaskList(result, checkboxBlocks)\n\treturn result\n}\n","import * as readline from 'node:readline'\nimport { logger } from './logger.js'\nimport {\n\tbroadcastApprovalNotification,\n\tclearApprovalNotification,\n} from './notification.js'\n\n/**\n * Prompt user for confirmation (yes/no)\n * @param message The question to ask the user\n * @param defaultValue Default value if user just presses enter (default: false)\n * @returns Promise<boolean> - true if user confirms, false otherwise\n */\nexport async function promptConfirmation(\n\tmessage: string,\n\tdefaultValue = false\n): Promise<boolean> {\n\tconst suffix = defaultValue ? '[Y/n]' : '[y/N]'\n\tconst fullMessage = `${message} ${suffix}: `\n\n\t// Loop until valid input is received\n\twhile (true) {\n\t\tconst rl = readline.createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t})\n\n\t\tconst answer = await new Promise<string>((resolve) => {\n\t\t\trl.question(fullMessage, (ans) => {\n\t\t\t\trl.close()\n\t\t\t\tresolve(ans)\n\t\t\t})\n\t\t})\n\n\t\tconst normalized = answer.trim().toLowerCase()\n\n\t\tif (normalized === '') {\n\t\t\treturn defaultValue\n\t\t}\n\n\t\tif (normalized === 'y' || normalized === 'yes') {\n\t\t\treturn true\n\t\t}\n\n\t\tif (normalized === 'n' || normalized === 'no') {\n\t\t\treturn false\n\t\t}\n\n\t\t// Invalid input - show warning and re-prompt\n\t\tlogger.warn('Invalid input. Please enter y/yes or n/no.')\n\t}\n}\n\n/**\n * Prompt user for text input\n * @param message The prompt message\n * @param defaultValue Optional default value\n * @returns Promise<string> - the user's input\n */\nexport async function promptInput(\n\tmessage: string,\n\tdefaultValue?: string\n): Promise<string> {\n\tconst rl = readline.createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stdout,\n\t})\n\n\tconst suffix = defaultValue ? ` [${defaultValue}]` : ''\n\tconst fullMessage = `${message}${suffix}: `\n\n\treturn new Promise((resolve) => {\n\t\trl.question(fullMessage, (answer) => {\n\t\t\trl.close()\n\n\t\t\tconst trimmed = answer.trim()\n\n\t\t\tif (trimmed === '' && defaultValue !== undefined) {\n\t\t\t\tresolve(defaultValue)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tresolve(trimmed)\n\t\t})\n\t})\n}\n\n/**\n * Wait for the user to press any key\n * @param message Optional message to display (default: \"Press any key to continue...\")\n * @returns Promise<string> - resolves with the key that was pressed, or empty string in non-interactive environments\n */\nexport async function waitForKeypress(\n\tmessage = 'Press any key to continue...'\n): Promise<string> {\n\t// Check if we can use raw mode (only available in TTY)\n\tif (!process.stdin.isTTY || typeof process.stdin.setRawMode !== 'function') {\n\t\t// Non-interactive environment - skip keypress wait\n\t\treturn ''\n\t}\n\n\t// Display message first\n\tprocess.stdout.write(message)\n\n\treturn new Promise((resolve) => {\n\t\t// Enable raw mode to capture single keypresses\n\t\tprocess.stdin.setRawMode(true)\n\t\tprocess.stdin.resume()\n\n\t\t// Listen for single data event\n\t\tprocess.stdin.once('data', (chunk: Buffer) => {\n\t\t\tconst key = chunk.toString('utf8')\n\n\t\t\t// Restore normal mode first (cleanup before any exit)\n\t\t\tprocess.stdin.setRawMode(false)\n\t\t\tprocess.stdin.pause()\n\n\t\t\t// Handle Ctrl+C (ETX character \\x03)\n\t\t\tif (key === '\\x03') {\n\t\t\t\tprocess.stdout.write('\\n')\n\t\t\t\tprocess.exit(130) // Standard exit code for SIGINT (128 + 2)\n\t\t\t}\n\n\t\t\t// Add newline after keypress for clean output\n\t\t\tprocess.stdout.write('\\n')\n\t\t\tresolve(key)\n\t\t})\n\t})\n}\n\n/**\n * Check if running in an interactive environment\n * Returns false if CI environment or no TTY\n */\nexport function isInteractiveEnvironment(): boolean {\n\treturn process.stdin.isTTY === true && process.env.CI !== 'true'\n}\n\n// Commit action type for type safety\nexport type CommitAction = 'accept' | 'edit' | 'abort'\n\n/**\n * Display commit message and prompt for action\n * @param message The commit message to display\n * @returns Promise<CommitAction> - 'accept', 'edit', or 'abort'\n */\nexport async function promptCommitAction(message: string): Promise<CommitAction> {\n\t// Check for non-interactive environment first\n\tif (!isInteractiveEnvironment()) {\n\t\treturn 'accept'\n\t}\n\n\tconst cwd = process.cwd()\n\n\t// Display the commit message with clear demarcation\n\tprocess.stdout.write('\\n' + '='.repeat(60) + '\\n')\n\tprocess.stdout.write('COMMIT MESSAGE:\\n')\n\tprocess.stdout.write('='.repeat(60) + '\\n')\n\tprocess.stdout.write(message + '\\n')\n\tprocess.stdout.write('='.repeat(60) + '\\n\\n')\n\n\t// Broadcast approval notification before prompting\n\tawait broadcastApprovalNotification(cwd)\n\n\ttry {\n\t\t// Loop until valid input is received\n\t\twhile (true) {\n\t\t\tconst rl = readline.createInterface({\n\t\t\t\tinput: process.stdin,\n\t\t\t\toutput: process.stdout,\n\t\t\t})\n\n\t\t\tconst answer = await new Promise<string>((resolve) => {\n\t\t\t\trl.question('[A]ccept as-is, [E]dit in editor, A[b]ort? [A/e/b]: ', (ans) => {\n\t\t\t\t\trl.close()\n\t\t\t\t\tresolve(ans)\n\t\t\t\t})\n\t\t\t})\n\n\t\t\tconst normalized = answer.trim().toLowerCase()\n\n\t\t\tif (normalized === '' || normalized === 'a' || normalized === 'accept') {\n\t\t\t\treturn 'accept'\n\t\t\t}\n\n\t\t\tif (normalized === 'e' || normalized === 'edit') {\n\t\t\t\treturn 'edit'\n\t\t\t}\n\n\t\t\tif (normalized === 'b' || normalized === 'abort') {\n\t\t\t\treturn 'abort'\n\t\t\t}\n\n\t\t\t// Invalid input - show warning and re-prompt\n\t\t\tlogger.warn('Invalid input. Please enter A (accept), E (edit), or B (abort).')\n\t\t}\n\t} finally {\n\t\t// Clear approval notification after user makes selection\n\t\tawait clearApprovalNotification(cwd)\n\t}\n}\n","import net from 'node:net'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { execSync } from 'node:child_process'\n\n// Debug logging - Set ILOOM_NOTIF_DEBUG=1 to enable\nconst DEBUG = process.env.ILOOM_NOTIF_DEBUG === '1'\nconst LOG_FILE = '/tmp/iloom-notif.log'\n\nfunction debug(message: string, data: Record<string, unknown> = {}): void {\n\tif (!DEBUG) return\n\n\tconst timestamp = new Date().toISOString()\n\tconst dataStr = Object.keys(data).length > 0 ? ` ${JSON.stringify(data)}` : ''\n\tconst logLine = `[${timestamp}] ${message}${dataStr}\\n`\n\n\ttry {\n\t\tfs.appendFileSync(LOG_FILE, logLine)\n\t} catch {\n\t\t// Ignore logging errors\n\t}\n}\n\n/**\n * Slugify a worktree path to create a metadata filename.\n * Must match MetadataManager.slugifyPath() algorithm.\n *\n * @param worktreePath - Absolute path to worktree\n * @returns Slugified filename with .json extension\n */\nfunction slugifyPath(worktreePath: string): string {\n\t// 1. Trim trailing slashes\n\tlet slug = worktreePath.replace(/[/\\\\]+$/, '')\n\t// 2. Replace path separators with triple underscores\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\t// 3. Replace non-alphanumeric chars (except _ and -) with hyphens\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\t// 4. Append .json\n\treturn `${slug}.json`\n}\n\n/**\n * Get the full path to the metadata file for a worktree.\n *\n * @param cwd - Working directory (worktree path)\n * @returns Full path to metadata JSON file\n */\nfunction getMetadataFilePath(cwd: string): string {\n\tconst loomsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'looms')\n\treturn path.join(loomsDir, slugifyPath(cwd))\n}\n\n/**\n * Read session ID from the metadata file for a worktree.\n * Returns null if the file doesn't exist or can't be read.\n *\n * @param cwd - Working directory (worktree path)\n * @returns Session ID or null\n */\nfunction readSessionId(cwd: string): string | null {\n\ttry {\n\t\tconst filePath = getMetadataFilePath(cwd)\n\t\tdebug('Reading metadata file', { cwd, filePath })\n\n\t\tif (!fs.existsSync(filePath)) {\n\t\t\tdebug('Metadata file not found', { filePath })\n\t\t\treturn null\n\t\t}\n\n\t\tconst content = fs.readFileSync(filePath, 'utf8')\n\t\tconst metadata = JSON.parse(content) as { sessionId?: string }\n\t\tdebug('Read session ID from metadata', { sessionId: metadata.sessionId })\n\t\treturn metadata.sessionId ?? null\n\t} catch (error) {\n\t\tdebug('Failed to read session ID', { cwd, error: String(error) })\n\t\treturn null\n\t}\n}\n\n/**\n * Find all iloom named pipes on Windows.\n * Named pipes are listed under \\\\.\\pipe\\ and we look for iloom-* pattern.\n *\n * @returns Array of named pipe paths\n */\nfunction findWindowsNamedPipes(): string[] {\n\ttry {\n\t\t// Use PowerShell to list named pipes matching our pattern\n\t\t// Get-ChildItem \\\\.\\pipe\\ lists all named pipes\n\t\tconst output = execSync(\n\t\t\t'powershell -Command \"Get-ChildItem \\\\\\\\.\\\\pipe\\\\ | Where-Object { $_.Name -like \\'iloom-*\\' } | Select-Object -ExpandProperty FullName\"',\n\t\t\t{ encoding: 'utf-8', timeout: 5000 }\n\t\t)\n\n\t\treturn output\n\t\t\t.split('\\n')\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter((line) => line.length > 0)\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Find all iloom sockets/pipes in the system.\n * Mirrors findAllIloomSockets() from iloom-hook.js.\n *\n * On Windows, looks for named pipes matching \\\\.\\pipe\\iloom-* pattern.\n * On Unix, looks for Unix sockets matching iloom-*.sock in temp directory.\n *\n * @returns Array of socket/pipe paths\n */\nexport function findAllIloomSockets(): string[] {\n\t// On Windows, use named pipes instead of Unix sockets\n\tif (process.platform === 'win32') {\n\t\treturn findWindowsNamedPipes()\n\t}\n\n\ttry {\n\t\t// Use /tmp explicitly, NOT tmpdir() which returns /var/folders/... on macOS\n\t\t// The VS Code extension creates sockets in /tmp, matching iloom-hook.js behavior\n\t\tconst tempDir = '/tmp'\n\t\tconst files = fs.readdirSync(tempDir)\n\t\tconst sockets = files\n\t\t\t.filter((file) => file.startsWith('iloom-') && file.endsWith('.sock'))\n\t\t\t.map((file) => path.join(tempDir, file))\n\t\t\t.filter((socketPath) => {\n\t\t\t\t// Verify it's actually a socket\n\t\t\t\ttry {\n\t\t\t\t\tconst stat = fs.statSync(socketPath)\n\t\t\t\t\treturn stat.isSocket()\n\t\t\t\t} catch {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t})\n\n\t\treturn sockets\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Send status to a single socket (fire and forget).\n * Mirrors sendStatus() from iloom-hook.js.\n *\n * @param socketPath Path to Unix socket or Windows named pipe\n * @param status Session status\n * @param cwd Current working directory\n * @param hookEventName Synthetic hook event name for the VS Code extension\n * @param sessionId Session ID from metadata (required by VS Code extension)\n */\nasync function sendStatus(\n\tsocketPath: string,\n\tstatus: string,\n\tcwd: string,\n\thookEventName: string,\n\tsessionId: string | null,\n\ttoolName: string\n): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tconst client = net.createConnection(socketPath, () => {\n\t\t\t// Match the message format from iloom-hook.js so VS Code extension handles it correctly\n\t\t\tconst message = JSON.stringify({\n\t\t\t\ttype: 'session_status',\n\t\t\t\tstatus,\n\t\t\t\tsession_id: sessionId,\n\t\t\t\thook_event_name: hookEventName,\n\t\t\t\ttool_name: toolName,\n\t\t\t\tcwd,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t})\n\n\t\t\tdebug('Sending status to socket', { socketPath, status, sessionId, hookEventName, cwd })\n\n\t\t\tclient.write(message + '\\n')\n\t\t\t// Fire and forget - close connection immediately after sending\n\t\t\tclient.end()\n\t\t\tresolve()\n\t\t})\n\n\t\t// Handle connection errors silently\n\t\tclient.on('error', (err) => {\n\t\t\tdebug('Socket connection error', { socketPath, error: String(err) })\n\t\t\tresolve()\n\t\t})\n\t})\n}\n\n/**\n * Broadcast status to all iloom sockets.\n *\n * @param status Session status to broadcast\n * @param cwd Current working directory\n * @param hookEventName Synthetic hook event name for the VS Code extension\n */\nasync function broadcastStatus(\n\tstatus: string,\n\tcwd: string,\n\thookEventName: string,\n\ttoolName: string\n): Promise<void> {\n\tdebug('broadcastStatus called', { status, cwd, hookEventName, toolName })\n\n\tconst sockets = findAllIloomSockets()\n\tdebug('Found sockets', { count: sockets.length, sockets })\n\n\tif (sockets.length === 0) {\n\t\tdebug('No sockets found, skipping broadcast')\n\t\treturn\n\t}\n\n\t// Read session ID from metadata file\n\tconst sessionId = readSessionId(cwd)\n\tdebug('Session ID for broadcast', { sessionId })\n\n\tconst promises = sockets.map((socketPath) =>\n\t\tsendStatus(socketPath, status, cwd, hookEventName, sessionId, toolName).catch(() => {\n\t\t\t// Silent failure - don't interrupt the CLI\n\t\t})\n\t)\n\n\tawait Promise.allSettled(promises)\n\tdebug('Broadcast completed')\n}\n\n/**\n * Broadcast approval notification (waiting_for_approval status).\n * Call this when the CLI is waiting for user input.\n * Uses 'CommitApproval' as the synthetic hook event name.\n * Uses 'commit' as the tool_name to distinguish from Claude's PermissionRequest.\n *\n * @param cwd Current working directory\n */\nexport async function broadcastApprovalNotification(cwd: string): Promise<void> {\n\t// Use 'CommitApproval' hook event and 'iloom_commit' tool_name to distinguish from Claude's hooks\n\t// This allows the VS Code extension to prevent PostToolUse from clearing commit approvals\n\tawait broadcastStatus('waiting_for_approval', cwd, 'CommitApproval', 'iloom_commit')\n}\n\n/**\n * Clear approval notification (working status).\n * Call this when the user has made their selection.\n *\n * @param cwd Current working directory\n */\nexport async function clearApprovalNotification(cwd: string): Promise<void> {\n\t// Use 'CommitApprovalResponse' to indicate user responded to commit prompt\n\tawait broadcastStatus('working', cwd, 'CommitApprovalResponse', 'iloom_commit')\n}\n","// JiraIssueTracker - Implements IssueTracker interface for Jira\n// Provides issue management operations via Jira REST API\n\nimport type { IssueTracker } from '../../IssueTracker.js'\nimport type { Issue, IssueTrackerInputDetection } from '../../../types/index.js'\nimport { JiraApiClient, type JiraConfig, type JiraIssue, type JiraTransition } from './JiraApiClient.js'\nimport { getLogger } from '../../../utils/logger-context.js'\nimport { promptConfirmation } from '../../../utils/prompt.js'\nimport { adfToMarkdown } from './AdfMarkdownConverter.js'\n\n/**\n * Jira-specific configuration\n */\nexport interface JiraTrackerConfig extends JiraConfig {\n\tprojectKey: string\n\ttransitionMappings?: Record<string, string> // Map iloom states to Jira transition names\n\tdefaultIssueType?: string // Default issue type for creating issues (e.g., \"Task\", \"Story\")\n\tdefaultSubtaskType?: string // Default issue type for creating subtasks (e.g., \"Subtask\", \"Sub-task\")\n}\n\n/**\n * JiraIssueTracker implements IssueTracker for Jira\n * \n * Key differences from GitHub/Linear:\n * - Issue identifiers are strings (e.g., \"PROJ-123\")\n * - No issue prefix (unlike GitHub's \"#\")\n * - State changes require workflow transitions (not direct status updates)\n * - Content uses Atlassian Document Format (ADF), converted to/from Markdown\n */\nexport class JiraIssueTracker implements IssueTracker {\n\treadonly providerName = 'jira'\n\treadonly supportsPullRequests = false\n\n\tprivate readonly client: JiraApiClient\n\tprivate readonly config: JiraTrackerConfig\n\tprivate prompter: (message: string) => Promise<boolean>\n\n\tconstructor(config: JiraTrackerConfig, options?: {\n\t\tprompter?: (message: string) => Promise<boolean>\n\t}) {\n\t\tthis.config = config\n\t\tthis.client = new JiraApiClient({\n\t\t\thost: config.host,\n\t\t\tusername: config.username,\n\t\t\tapiToken: config.apiToken,\n\t\t})\n\t\tthis.prompter = options?.prompter ?? promptConfirmation\n\t}\n\n\t/**\n\t * Normalize identifier to canonical uppercase form\n\t * Jira issue keys are case-sensitive in the API (must be uppercase)\n\t */\n\tnormalizeIdentifier(identifier: string | number): string {\n\t\treturn String(identifier).toUpperCase()\n\t}\n\n\t/**\n\t * Detect input type from user input\n\t * Jira issues follow pattern: PROJECTKEY-123 (case-insensitive)\n\t */\n\tasync detectInputType(input: string): Promise<IssueTrackerInputDetection> {\n\t\t// Pattern: PROJECTKEY-123 (case-insensitive to accept lowercase from branch names or user input)\n\t\tconst jiraPattern = /^([A-Z][A-Z0-9]+)-(\\d+)$/i\n\t\tconst match = input.match(jiraPattern)\n\n\t\tif (!match) {\n\t\t\treturn { type: 'unknown', identifier: null, rawInput: input }\n\t\t}\n\n\t\tconst issueKey = this.normalizeIdentifier(input)\n\t\tgetLogger().debug('Checking if input is a Jira issue', { issueKey })\n\n\t\t// Verify the issue exists\n\t\ttry {\n\t\t\tawait this.client.getIssue(issueKey)\n\t\t\treturn { type: 'issue', identifier: issueKey, rawInput: input }\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && (/404/.test(error.message) || /not found/i.test(error.message))) {\n\t\t\t\tgetLogger().debug('Issue not found', { issueKey, error })\n\t\t\t\treturn { type: 'unknown', identifier: null, rawInput: input }\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Fetch issue details\n\t */\n\tasync fetchIssue(identifier: string | number): Promise<Issue> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Fetching Jira issue', { issueKey })\n\n\t\tconst jiraIssue = await this.client.getIssue(issueKey)\n\t\treturn this.mapJiraIssueToIssue(jiraIssue)\n\t}\n\n\t/**\n\t * Check if issue exists (silent validation)\n\t */\n\tasync isValidIssue(identifier: string | number): Promise<Issue | false> {\n\t\ttry {\n\t\t\treturn await this.fetchIssue(identifier)\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && (/404/.test(error.message) || /not found/i.test(error.message))) {\n\t\t\t\tgetLogger().debug('Issue validation failed: not found', { identifier, error })\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Validate issue state\n\t * Note: Jira doesn't have a simple \"closed\" state - depends on workflow\n\t */\n\tasync validateIssueState(issue: Issue): Promise<void> {\n\t\tgetLogger().debug('Jira issue state', { issueKey: issue.number, state: issue.state })\n\t\tif (issue.state === 'closed') {\n\t\t\tconst shouldContinue = await this.prompter(\n\t\t\t\t`Issue ${issue.number} is in a completed state. Continue anyway?`\n\t\t\t)\n\t\t\tif (!shouldContinue) {\n\t\t\t\tthrow new Error('User cancelled due to completed issue')\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a new issue\n\t */\n\tasync createIssue(\n\t\ttitle: string,\n\t\tbody: string,\n\t\t_repository?: string,\n\t\t_labels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\tgetLogger().debug('Creating Jira issue', { title, projectKey: this.config.projectKey })\n\n\t\t// Convert markdown body to plain text for Jira description\n\t\t// Note: Jira API expects Atlassian Document Format (ADF)\n\t\t// We use a simplified plain text approach here\n\t\tconst jiraIssue = await this.client.createIssue(\n\t\t\tthis.config.projectKey,\n\t\t\ttitle,\n\t\t\tbody,\n\t\t\tthis.config.defaultIssueType\n\t\t)\n\n\t\treturn {\n\t\t\tnumber: jiraIssue.key,\n\t\t\turl: `${this.config.host}/browse/${jiraIssue.key}`,\n\t\t}\n\t}\n\n\t/**\n\t * Get issue URL\n\t */\n\tasync getIssueUrl(identifier: string | number): Promise<string> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\treturn `${this.config.host}/browse/${issueKey}`\n\t}\n\n\t/**\n\t * Move issue to \"In Progress\" state\n\t * Uses configured transition mapping or default transition name\n\t */\n\tasync moveIssueToInProgress(identifier: string | number): Promise<void> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Moving Jira issue to In Progress', { issueKey })\n\n\t\t// Get available transitions\n\t\tconst transitions = await this.client.getTransitions(issueKey)\n\t\t\n\t\t// Look for the transition in config mapping or use default names\n\t\tconst transitionName = this.config.transitionMappings?.['In Progress'] \n\t\t\t?? this.findTransitionByName(transitions, ['In Progress', 'Start Progress', 'Start'])\n\n\t\tif (!transitionName) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find \"In Progress\" transition for ${issueKey}. ` +\n\t\t\t\t`Available transitions: ${transitions.map(t => t.name).join(', ')}. ` +\n\t\t\t\t`Configure custom mapping in settings.json: issueManagement.jira.transitionMappings`\n\t\t\t)\n\t\t}\n\n\t\t// Find transition ID\n\t\tconst transition = transitions.find(t => t.name === transitionName)\n\t\tif (!transition) {\n\t\t\tthrow new Error(`Transition \"${transitionName}\" not found`)\n\t\t}\n\n\t\tawait this.client.transitionIssue(issueKey, transition.id)\n\t\tgetLogger().info('Issue transitioned successfully', { issueKey, transition: transitionName })\n\t}\n\n\t/**\n\t * Move issue to \"Ready for Review\" state\n\t * Uses configured transition mapping or default transition name\n\t */\n\tasync moveIssueToReadyForReview(identifier: string | number): Promise<void> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Moving Jira issue to Ready for Review', { issueKey })\n\n\t\t// Get available transitions\n\t\tconst transitions = await this.client.getTransitions(issueKey)\n\n\t\t// Look for the transition in config mapping or use default names\n\t\tconst transitionName = this.config.transitionMappings?.['Ready for Review']\n\t\t\t?? this.findTransitionByName(transitions, ['Ready for Review', 'In Review', 'Code Review', 'Review'])\n\n\t\tif (!transitionName) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find \"Ready for Review\" transition for ${issueKey}. ` +\n\t\t\t\t`Available transitions: ${transitions.map(t => t.name).join(', ')}. ` +\n\t\t\t\t`Configure custom mapping in settings.json: issueManagement.jira.transitionMappings`\n\t\t\t)\n\t\t}\n\n\t\t// Find transition ID\n\t\tconst transition = transitions.find(t => t.name === transitionName)\n\t\tif (!transition) {\n\t\t\tthrow new Error(`Transition \"${transitionName}\" not found`)\n\t\t}\n\n\t\tawait this.client.transitionIssue(issueKey, transition.id)\n\t\tgetLogger().info('Issue transitioned to Ready for Review', { issueKey, transition: transitionName })\n\t}\n\n\t/**\n\t * Close an issue by transitioning to \"Done\" state\n\t * Uses configured transition mapping or default transition names\n\t */\n\tasync closeIssue(identifier: string | number): Promise<void> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Closing Jira issue', { issueKey })\n\n\t\t// Get available transitions\n\t\tconst transitions = await this.client.getTransitions(issueKey)\n\n\t\t// Look for the transition in config mapping or use default names\n\t\tconst transitionName = this.config.transitionMappings?.['Done']\n\t\t\t?? this.findTransitionByName(transitions, ['Done', 'Close', 'Closed', 'Resolve', 'Resolved'])\n\n\t\tif (!transitionName) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find \"Done\" transition for ${issueKey}. ` +\n\t\t\t\t`Available transitions: ${transitions.map(t => t.name).join(', ')}. ` +\n\t\t\t\t`Configure custom mapping in settings.json: issueManagement.jira.transitionMappings`\n\t\t\t)\n\t\t}\n\n\t\t// Find transition ID\n\t\tconst transition = transitions.find(t => t.name === transitionName)\n\t\tif (!transition) {\n\t\t\tthrow new Error(`Transition \"${transitionName}\" not found`)\n\t\t}\n\n\t\tawait this.client.transitionIssue(issueKey, transition.id)\n\t\tgetLogger().info('Issue closed successfully', { issueKey, transition: transitionName })\n\t}\n\n\t/**\n\t * Reopen an issue by transitioning back to an open state\n\t * Uses configured transition mapping or default transition names\n\t */\n\tasync reopenIssue(identifier: string | number): Promise<void> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Reopening Jira issue', { issueKey })\n\n\t\t// Get available transitions\n\t\tconst transitions = await this.client.getTransitions(issueKey)\n\n\t\t// Look for the transition in config mapping or use default names\n\t\tconst transitionName = this.config.transitionMappings?.['Reopen']\n\t\t\t?? this.findTransitionByName(transitions, ['Reopen', 'To Do', 'Open', 'Backlog'])\n\n\t\tif (!transitionName) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find \"Reopen\" transition for ${issueKey}. ` +\n\t\t\t\t`Available transitions: ${transitions.map(t => t.name).join(', ')}. ` +\n\t\t\t\t`Configure custom mapping in settings.json: issueManagement.jira.transitionMappings`\n\t\t\t)\n\t\t}\n\n\t\t// Find transition ID\n\t\tconst transition = transitions.find(t => t.name === transitionName)\n\t\tif (!transition) {\n\t\t\tthrow new Error(`Transition \"${transitionName}\" not found`)\n\t\t}\n\n\t\tawait this.client.transitionIssue(issueKey, transition.id)\n\t\tgetLogger().info('Issue reopened successfully', { issueKey, transition: transitionName })\n\t}\n\n\t/**\n\t * Extract context from issue for AI prompts\n\t */\n\textractContext(entity: Issue): string {\n\t\treturn `Issue: ${entity.number}\nTitle: ${entity.title}\nStatus: ${entity.state}\nURL: ${entity.url}\n\nDescription:\n${entity.body}\n\n${entity.labels.length > 0 ? `Labels: ${entity.labels.join(', ')}` : ''}\n${entity.assignees.length > 0 ? `Assignees: ${entity.assignees.join(', ')}` : ''}`\n\t}\n\n\t/**\n\t * Fetch child issues of a Jira parent issue using JQL\n\t * @param parentIdentifier - Jira issue key (e.g., \"PROJ-123\")\n\t * @param _repo - Repository (unused for Jira)\n\t * @returns Array of child issues\n\t */\n\tasync getChildIssues(parentIdentifier: string, _repo?: string): Promise<Array<{ id: string; title: string; url: string; state: string }>> {\n\t\tconst parentKey = this.normalizeIdentifier(parentIdentifier)\n\t\tconst jiraKeyPattern = /^[A-Z][A-Z0-9]+-\\d+$/\n\t\tif (!jiraKeyPattern.test(parentKey)) {\n\t\t\tgetLogger().warn(`Invalid Jira issue key format: ${parentKey}`)\n\t\t\treturn []\n\t\t}\n\t\tconst issues = await this.client.searchIssues(`parent = ${parentKey}`)\n\t\treturn issues.map(issue => ({\n\t\t\tid: issue.key,\n\t\t\ttitle: issue.fields.summary,\n\t\t\turl: `${this.config.host}/browse/${issue.key}`,\n\t\t\tstate: issue.fields.status.name.toLowerCase(),\n\t\t}))\n\t}\n\n\t/**\n\t * Get issue details (alias for fetchIssue for MCP compatibility)\n\t */\n\tasync getIssue(identifier: string | number): Promise<Issue> {\n\t\treturn this.fetchIssue(identifier)\n\t}\n\n\t/**\n\t * Get all comments for an issue\n\t */\n\tasync getComments(identifier: string | number): Promise<Array<{\n\t\tid: string\n\t\tbody: string\n\t\tauthor: { displayName: string; emailAddress: string; accountId: string }\n\t\tcreatedAt: string\n\t\tupdatedAt: string\n\t}>> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Fetching Jira comments', { issueKey })\n\n\t\tconst comments = await this.client.getComments(issueKey)\n\t\t\n\t\t// Map to expected format\n\t\treturn comments.map(comment => ({\n\t\t\tid: comment.id,\n\t\t\tbody: adfToMarkdown(comment.body),\n\t\t\tauthor: comment.author,\n\t\t\tcreatedAt: comment.created,\n\t\t\tupdatedAt: comment.updated,\n\t\t}))\n\t}\n\n\t/**\n\t * Add a comment to an issue\n\t */\n\tasync addComment(identifier: string | number, body: string): Promise<{ id: string }> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Adding Jira comment', { issueKey })\n\n\t\tconst comment = await this.client.addComment(issueKey, body)\n\t\treturn { id: comment.id }\n\t}\n\n\t/**\n\t * Update an existing comment\n\t */\n\tasync updateComment(identifier: string | number, commentId: string, body: string): Promise<void> {\n\t\tconst issueKey = this.normalizeIdentifier(identifier)\n\t\tgetLogger().debug('Updating Jira comment', { issueKey, commentId })\n\n\t\tawait this.client.updateComment(issueKey, commentId, body)\n\t}\n\n\t/**\n\t * Get the underlying API client (for direct API access by MCP provider)\n\t */\n\tgetApiClient(): JiraApiClient {\n\t\treturn this.client\n\t}\n\n\t/**\n\t * Get configuration (for MCP provider)\n\t */\n\tgetConfig(): JiraTrackerConfig {\n\t\treturn this.config\n\t}\n\n\t/**\n\t * Map Jira API issue to generic Issue type\n\t */\n\tprivate mapJiraIssueToIssue(jiraIssue: JiraIssue): Issue & {\n\t\tid?: string\n\t\tkey?: string\n\t\tauthor?: {\n\t\t\tdisplayName: string\n\t\t\temailAddress: string\n\t\t\taccountId: string\n\t\t}\n\t\tassignee?: {\n\t\t\tdisplayName: string\n\t\t\temailAddress: string\n\t\t\taccountId: string\n\t\t} | null\n\t\tissueType?: string\n\t\tstatus?: string\n\t} {\n\t\t// Extract description - handle ADF format or plain string\n\t\tconst description = adfToMarkdown(jiraIssue.fields.description)\n\n\t\treturn {\n\t\t\tid: jiraIssue.id,\n\t\t\tkey: jiraIssue.key,\n\t\t\tnumber: jiraIssue.key,\n\t\t\ttitle: jiraIssue.fields.summary,\n\t\t\tbody: description,\n\t\t\tstate: this.mapJiraStatusToState(jiraIssue.fields.status.name),\n\t\t\tlabels: jiraIssue.fields.labels,\n\t\t\tassignees: jiraIssue.fields.assignee \n\t\t\t\t? [jiraIssue.fields.assignee.displayName]\n\t\t\t\t: [],\n\t\t\tassignee: jiraIssue.fields.assignee,\n\t\t\tauthor: jiraIssue.fields.reporter,\n\t\t\turl: `${this.config.host}/browse/${jiraIssue.key}`,\n\t\t\tissueType: jiraIssue.fields.issuetype.name,\n\t\t\tstatus: jiraIssue.fields.status.name,\n\t\t}\n\t}\n\n\tprivate mapJiraStatusToState(statusName: string): 'open' | 'closed' {\n\t\tconst normalized = statusName.toLowerCase()\n\t\tconst closedStatuses = ['done', 'closed', 'resolved', 'cancelled', 'canceled']\n\t\treturn closedStatuses.includes(normalized) ? 'closed' : 'open'\n\t}\n\n\t/**\n\t * Find a transition by name, trying multiple possible names\n\t */\n\tprivate findTransitionByName(transitions: JiraTransition[], names: string[]): string | null {\n\t\tfor (const name of names) {\n\t\t\tconst transition = transitions.find(t => \n\t\t\t\tt.name.toLowerCase() === name.toLowerCase()\n\t\t\t)\n\t\t\tif (transition) {\n\t\t\t\treturn transition.name\n\t\t\t}\n\t\t}\n\t\treturn null\n\t}\n}\n","import { readFile } from 'fs/promises'\nimport path from 'path'\nimport os from 'os'\nimport { z } from 'zod'\nimport deepmerge from 'deepmerge'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Zod schema for base agent settings (without nested agents)\n */\nexport const BaseAgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Claude model shorthand: sonnet, opus, or haiku'),\n\tswarmModel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Model to use for this agent in swarm mode. Overrides the base model when running inside swarm workers.'),\n\tenabled: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether this agent is enabled. Defaults to true.'),\n\tproviders: z\n\t\t.record(\n\t\t\tz.enum(['claude', 'gemini', 'codex']),\n\t\t\tz.string()\n\t\t)\n\t\t.optional()\n\t\t.describe('Map of review providers to model names. Keys: claude, gemini, codex. Values: model name strings (e.g., \"sonnet\", \"gemini-3-pro-preview\", \"gpt-5.2-codex\")'),\n\treview: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether artifacts from this agent should be reviewed before posting (defaults to false)'),\n})\n\n/**\n * Zod schema for agent settings, extends base with sub-agent timeout and nested agents record.\n */\nexport const AgentSettingsSchema = BaseAgentSettingsSchema.extend({\n\tagents: z.record(z.string(), BaseAgentSettingsSchema)\n\t\t.optional()\n\t\t.describe('Nested per-agent settings. Only meaningful under the iloom-swarm-worker agent entry for sub-agent timeout configuration.'),\n\tsubAgentTimeout: z\n\t\t.number()\n\t\t.min(1, 'Sub-agent timeout must be at least 1 minute')\n\t\t.max(120, 'Sub-agent timeout cannot exceed 120 minutes')\n\t\t.default(10)\n\t\t.describe('Timeout in minutes for sub-agent claude -p invocations in swarm mode. Applies to each phase agent (evaluator, analyzer, planner, implementer) when invoked via the Bash tool. Default: 10 minutes. Only meaningful under the iloom-swarm-worker agent entry.'),\n})\n\n/**\n * Zod schema for spin agent settings with default model\n * Used for the spin orchestrator configuration\n */\nexport const SpinAgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('opus')\n\t\t.describe('Claude model shorthand for spin orchestrator'),\n\tswarmModel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Model for the spin orchestrator when running in swarm mode. Overrides spin.model for swarm workflows.'),\n})\n\n/**\n * Zod schema for plan command settings with default model\n * Used for the plan command configuration\n */\nexport const PlanCommandSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('opus')\n\t\t.describe('Claude model shorthand for plan command'),\n\tplanner: z\n\t\t.enum(['claude', 'gemini', 'codex'])\n\t\t.default('claude')\n\t\t.describe('AI provider for creating the plan'),\n\treviewer: z\n\t\t.enum(['claude', 'gemini', 'codex', 'none'])\n\t\t.default('none')\n\t\t.describe('AI provider for reviewing the plan (none to skip review)'),\n})\n\n/**\n * Zod schema for summary settings with default model\n * Used for session summary generation configuration\n */\nexport const SummarySettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('sonnet')\n\t\t.describe('Claude model shorthand for session summary generation'),\n})\n\n/**\n * Zod schema for workflow permission configuration\n */\nexport const WorkflowPermissionSchema = z.object({\n\tpermissionMode: z\n\t\t.enum(['plan', 'acceptEdits', 'bypassPermissions', 'default'])\n\t\t.optional()\n\t\t.describe('Permission mode for Claude CLI in this workflow type'),\n\tnoVerify: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows'),\n\tstartIde: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch IDE (code) when starting this workflow type'),\n\tstartDevServer: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch development server when starting this workflow type'),\n\tstartAiAgent: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch Claude Code agent when starting this workflow type'),\n\tstartTerminal: z\n\t\t.boolean()\n\t\t.default(false)\n\t\t.describe('Launch terminal window without dev server when starting this workflow type'),\n\tgenerateSummary: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Generate and post Claude session summary when finishing this workflow type'),\n})\n\n/**\n * Non-defaulting variant for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const WorkflowPermissionSchemaNoDefaults = z.object({\n\tpermissionMode: z\n\t\t.enum(['plan', 'acceptEdits', 'bypassPermissions', 'default'])\n\t\t.optional()\n\t\t.describe('Permission mode for Claude CLI in this workflow type'),\n\tnoVerify: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows'),\n\tstartIde: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch IDE (code) when starting this workflow type'),\n\tstartDevServer: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch development server when starting this workflow type'),\n\tstartAiAgent: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch Claude Code agent when starting this workflow type'),\n\tstartTerminal: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch terminal window without dev server when starting this workflow type'),\n\tgenerateSummary: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Generate and post Claude session summary when finishing this workflow type'),\n})\n\n/**\n * Zod schema for workflows settings\n */\nexport const WorkflowsSettingsSchema = z\n\t.object({\n\t\tissue: WorkflowPermissionSchema.optional(),\n\t\tpr: WorkflowPermissionSchema.optional(),\n\t\tregular: WorkflowPermissionSchema.optional(),\n\t})\n\t.optional()\n\n/**\n * Non-defaulting variant for pre-merge validation\n */\nexport const WorkflowsSettingsSchemaNoDefaults = z\n\t.object({\n\t\tissue: WorkflowPermissionSchemaNoDefaults.optional(),\n\t\tpr: WorkflowPermissionSchemaNoDefaults.optional(),\n\t\tregular: WorkflowPermissionSchemaNoDefaults.optional(),\n\t})\n\t.optional()\n\n/**\n * Zod schema for capabilities settings\n */\nexport const CapabilitiesSettingsSchema = z\n\t.object({\n\t\tweb: z\n\t\t\t.object({\n\t\t\t\tbasePort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Base port must be >= 1')\n\t\t\t\t\t.max(65535, 'Base port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Base port for web workspace port calculations (default: 3000)'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe('Web dev server settings. To declare a project as a web project, add \"web\" to the capabilities array in .iloom/package.iloom.json or .iloom/package.iloom.local.json.'),\n\t\tdatabase: z\n\t\t\t.object({\n\t\t\t\tdatabaseUrlEnvVarName: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.min(1, 'Database URL variable name cannot be empty')\n\t\t\t\t\t.regex(/^[A-Z_][A-Z0-9_]*$/, 'Must be valid env var name (uppercase, underscores)')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.default('DATABASE_URL')\n\t\t\t\t\t.describe('Name of environment variable for database connection URL'),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.optional()\n\n/**\n * Non-defaulting variant for pre-merge validation\n */\nexport const CapabilitiesSettingsSchemaNoDefaults = z\n\t.object({\n\t\tweb: z\n\t\t\t.object({\n\t\t\t\tbasePort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Base port must be >= 1')\n\t\t\t\t\t.max(65535, 'Base port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Base port for web workspace port calculations (default: 3000)'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe('Web dev server settings. To declare a project as a web project, add \"web\" to the capabilities array in .iloom/package.iloom.json or .iloom/package.iloom.local.json.'),\n\t\tdatabase: z\n\t\t\t.object({\n\t\t\t\tdatabaseUrlEnvVarName: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.min(1, 'Database URL variable name cannot be empty')\n\t\t\t\t\t.regex(/^[A-Z_][A-Z0-9_]*$/, 'Must be valid env var name (uppercase, underscores)')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Name of environment variable for database connection URL'),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.optional()\n\n/**\n * Zod schema for Neon database provider settings\n */\nexport const NeonSettingsSchema = z.object({\n\tprojectId: z\n\t\t.string()\n\t\t.min(1)\n\t\t.regex(/^[a-zA-Z0-9-]+$/, 'Neon project ID must contain only letters, numbers, and hyphens')\n\t\t.describe('Neon project ID found in your project URL (e.g., \"fantastic-fox-3566354\")'),\n\tparentBranch: z\n\t\t.string()\n\t\t.min(1)\n\t\t.describe('Branch from which new database branches are created'),\n})\n\n/**\n * Zod schema for database provider settings\n */\nexport const DatabaseProvidersSettingsSchema = z\n\t.object({\n\t\tneon: NeonSettingsSchema.optional().describe(\n\t\t\t'Neon database configuration. Requires Neon CLI installed and authenticated for database branching.',\n\t\t),\n\t})\n\t.optional()\n\n/**\n * Zod schema for iloom settings\n */\nexport const IloomSettingsSchema = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.default(false)\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env file compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchema.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting), ' +\n\t\t\t\t'iloom-swarm-worker (swarm worker agent, dynamically generated). ' +\n\t\t\t\t'Use swarmModel on any agent to override its model in swarm mode.',\n\t\t),\n\tspin: SpinAgentSettingsSchema.optional().describe(\n\t\t'Spin orchestrator configuration. Model defaults to opus when not configured.',\n\t),\n\tplan: PlanCommandSettingsSchema.optional().describe(\n\t\t'Plan command configuration. Model defaults to opus, planner to claude, reviewer to none when not configured.',\n\t),\n\tsummary: SummarySettingsSchema.optional().describe(\n\t\t'Session summary generation configuration. Model defaults to sonnet when not configured.',\n\t),\n\tcapabilities: CapabilitiesSettingsSchema.describe('Project capability configurations'),\n\tdatabaseProviders: DatabaseProvidersSettingsSchema.describe('Database provider configurations'),\n\tissueManagement: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\tprovider: z.enum(['github', 'linear', 'jira']).optional().default('github').describe('Issue tracker provider (github, linear, jira)'),\n\t\t\tgithub: z\n\t\t\t\t.object({\n\t\t\t\t\tremote: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Remote name cannot be empty')\n\t\t\t\t\t\t.describe('Git remote name to use for GitHub operations'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tlinear: z\n\t\t\t\t.object({\n\t\t\t\t\tteamId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Team ID cannot be empty')\n\t\t\t\t\t\t.describe('Linear team identifier (e.g., \"ENG\", \"PLAT\")'),\n\t\t\t\t\tbranchFormat: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Branch naming template for Linear issues'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Linear API token (lin_api_...). SECURITY: Store in settings.local.json only, never commit to source control.'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tjira: z\n\t\t\t\t.object({\n\t\t\t\t\thost: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira host cannot be empty')\n\t\t\t\t\t\t.describe('Jira instance URL (e.g., \"https://yourcompany.atlassian.net\")'),\n\t\t\t\t\tusername: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira username/email cannot be empty')\n\t\t\t\t\t\t.describe('Jira username or email address'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://id.atlassian.com/manage-profile/security/api-tokens'),\n\t\t\t\t\tprojectKey: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Project key cannot be empty')\n\t\t\t\t\t\t.describe('Jira project key (e.g., \"PROJ\", \"ENG\")'),\n\t\t\t\t\tboardId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira board ID for sprint/workflow operations (optional)'),\n\t\t\t\t\ttransitionMappings: z\n\t\t\t\t\t\t.record(z.string(), z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Map iloom states to Jira transition names (e.g., {\"In Review\": \"Start Review\"})'),\n\t\t\t\t\tdefaultIssueType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default('Task')\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating issues (e.g., \"Task\", \"Story\", \"Bug\")'),\n\t\t\t\t\tdefaultSubtaskType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default('Subtask')\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating subtasks/child issues (e.g., \"Subtask\", \"Sub-task\")'),\n\t\t\t\t\tdoneStatuses: z\n\t\t\t\t\t\t.array(z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default(['Done'])\n\t\t\t\t\t\t.describe('Status names to exclude from issue lists (e.g., [\"Done\", \"Closed\", \"Verify\"])'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Issue management configuration'),\n\tmergeBehavior: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\tmode: z.enum(['local', 'github-pr', 'github-draft-pr']).default('local'),\n\t\t\tremote: z.string().optional(),\n\t\t\tautoCommitPush: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr.'\n\t\t\t\t),\n\t\t\topenBrowserOnFinish: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(true)\n\t\t\t\t.describe(\n\t\t\t\t\t'Open the PR in the default browser after finishing in github-pr or github-draft-pr mode. Use --no-browser flag to override.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)'),\n\tide: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\ttype: z\n\t\t\t\t.enum(['vscode', 'cursor', 'webstorm', 'sublime', 'intellij', 'windsurf', 'antigravity'])\n\t\t\t\t.default('vscode')\n\t\t\t\t.describe(\n\t\t\t\t\t'IDE to launch when starting a loom. Options: vscode (Visual Studio Code), cursor (Cursor AI editor), ' +\n\t\t\t\t\t\t'webstorm (JetBrains WebStorm), sublime (Sublime Text), intellij (JetBrains IntelliJ IDEA), ' +\n\t\t\t\t\t\t'windsurf (Windsurf editor), antigravity (Antigravity IDE).'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe(\n\t\t\t'IDE configuration for workspace launches. Controls which editor opens when you start a loom. ' +\n\t\t\t\t'Supports VSCode, Cursor, WebStorm, Sublime Text, IntelliJ, Windsurf, and Antigravity. ' +\n\t\t\t\t'Note: Color synchronization (title bar colors) only works with VSCode-compatible editors (vscode, cursor, windsurf, antigravity).'\n\t\t),\n\tcolors: z\n\t\t.object({\n\t\t\tterminal: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(true)\n\t\t\t\t.describe('Apply terminal background colors based on branch name (macOS only)'),\n\t\t\tvscode: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(false)\n\t\t\t\t.describe(\n\t\t\t\t\t'Apply VSCode/Cursor title bar colors based on branch name. ' +\n\t\t\t\t\t\t'Note: This modifies .vscode/settings.json which may be in source control. ' +\n\t\t\t\t\t\t'Default is false for safety; enable via init or explicitly if .vscode is gitignored.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Color synchronization settings for workspace identification'),\n\tattribution: z\n\t\t.enum(['off', 'upstreamOnly', 'on'])\n\t\t.default('upstreamOnly')\n\t\t.describe(\n\t\t\t'Controls when iloom attribution appears in session summaries. ' +\n\t\t\t\t'\"off\" - never show attribution. ' +\n\t\t\t\t'\"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). ' +\n\t\t\t\t'\"on\" - always show attribution.'\n\t\t),\n\tgit: z\n\t\t.object({\n\t\t\tcommitTimeout: z\n\t\t\t\t.number()\n\t\t\t\t.min(1000, 'Commit timeout must be at least 1000ms')\n\t\t\t\t.max(600000, 'Commit timeout cannot exceed 600000ms (10 minutes)')\n\t\t\t\t.default(60000)\n\t\t\t\t.describe('Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.'),\n\t\t})\n\t\t.default({ }) // ensures the object always exists and uses default for the inner properties\n\t\t.describe('Git operation settings'),\n})\n\n/**\n * Non-defaulting variant for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const IloomSettingsSchemaNoDefaults = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchemaNoDefaults.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting), ' +\n\t\t\t\t'iloom-swarm-worker (swarm worker agent, dynamically generated). ' +\n\t\t\t\t'Use swarmModel on any agent to override its model in swarm mode.',\n\t\t),\n\tspin: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t\tswarmModel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Spin orchestrator configuration'),\n\tplan: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t\tplanner: z.enum(['claude', 'gemini', 'codex']).optional(),\n\t\t\treviewer: z.enum(['claude', 'gemini', 'codex', 'none']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Plan command configuration'),\n\tsummary: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Session summary generation configuration'),\n\tcapabilities: CapabilitiesSettingsSchemaNoDefaults.describe('Project capability configurations'),\n\tdatabaseProviders: DatabaseProvidersSettingsSchema.describe('Database provider configurations'),\n\tissueManagement: z\n\t\t.object({\n\t\t\tprovider: z.enum(['github', 'linear', 'jira']).optional().describe('Issue tracker provider (github, linear, jira)'),\n\t\t\tgithub: z\n\t\t\t\t.object({\n\t\t\t\t\tremote: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Remote name cannot be empty')\n\t\t\t\t\t\t.describe('Git remote name to use for GitHub operations'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tlinear: z\n\t\t\t\t.object({\n\t\t\t\t\tteamId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Team ID cannot be empty')\n\t\t\t\t\t\t.describe('Linear team identifier (e.g., \"ENG\", \"PLAT\")'),\n\t\t\t\t\tbranchFormat: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Branch naming template for Linear issues'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Linear API token (lin_api_...). SECURITY: Store in settings.local.json only, never commit to source control.'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tjira: z\n\t\t\t\t.object({\n\t\t\t\t\thost: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira host cannot be empty')\n\t\t\t\t\t\t.describe('Jira instance URL (e.g., \"https://yourcompany.atlassian.net\")'),\n\t\t\t\t\tusername: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira username/email cannot be empty')\n\t\t\t\t\t\t.describe('Jira username or email address'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://id.atlassian.com/manage-profile/security/api-tokens'),\n\t\t\t\t\tprojectKey: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Project key cannot be empty')\n\t\t\t\t\t\t.describe('Jira project key (e.g., \"PROJ\", \"ENG\")'),\n\t\t\t\t\tboardId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira board ID for sprint/workflow operations (optional)'),\n\t\t\t\t\ttransitionMappings: z\n\t\t\t\t\t\t.record(z.string(), z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Map iloom states to Jira transition names (e.g., {\"In Review\": \"Start Review\"})'),\n\t\t\t\t\tdefaultIssueType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating issues (e.g., \"Task\", \"Story\", \"Bug\")'),\n\t\t\t\t\tdefaultSubtaskType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating subtasks/child issues (e.g., \"Subtask\", \"Sub-task\")'),\n\t\t\t\t\tdoneStatuses: z\n\t\t\t\t\t\t.array(z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default(['Done'])\n\t\t\t\t\t\t.describe('Status names to exclude from issue lists (e.g., [\"Done\", \"Closed\", \"Verify\"])'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Issue management configuration'),\n\tmergeBehavior: z\n\t\t.object({\n\t\t\tmode: z.enum(['local', 'github-pr', 'github-draft-pr']).optional(),\n\t\t\tremote: z.string().optional(),\n\t\t\tautoCommitPush: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr.'\n\t\t\t\t),\n\t\t\topenBrowserOnFinish: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Open the PR in the default browser after finishing in github-pr or github-draft-pr mode. Use --no-browser flag to override.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)'),\n\tide: z\n\t\t.object({\n\t\t\ttype: z\n\t\t\t\t.enum(['vscode', 'cursor', 'webstorm', 'sublime', 'intellij', 'windsurf', 'antigravity'])\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'IDE to launch when starting a loom. Options: vscode (Visual Studio Code), cursor (Cursor AI editor), ' +\n\t\t\t\t\t\t'webstorm (JetBrains WebStorm), sublime (Sublime Text), intellij (JetBrains IntelliJ IDEA), ' +\n\t\t\t\t\t\t'windsurf (Windsurf editor), antigravity (Antigravity IDE).'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe(\n\t\t\t'IDE configuration for workspace launches. Controls which editor opens when you start a loom. ' +\n\t\t\t\t'Supports VSCode, Cursor, WebStorm, Sublime Text, IntelliJ, Windsurf, and Antigravity. ' +\n\t\t\t\t'Note: Color synchronization (title bar colors) only works with VSCode-compatible editors (vscode, cursor, windsurf, antigravity).'\n\t\t),\n\tcolors: z\n\t\t.object({\n\t\t\tterminal: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Apply terminal background colors based on branch name (macOS only)'),\n\t\t\tvscode: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Apply VSCode/Cursor title bar colors based on branch name. ' +\n\t\t\t\t\t\t'Note: This modifies .vscode/settings.json which may be in source control.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Color synchronization settings for workspace identification'),\n\tattribution: z\n\t\t.enum(['off', 'upstreamOnly', 'on'])\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Controls when iloom attribution appears in session summaries. ' +\n\t\t\t\t'\"off\" - never show attribution. ' +\n\t\t\t\t'\"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). ' +\n\t\t\t\t'\"on\" - always show attribution.'\n\t\t),\n\tgit: z\n\t\t.object({\n\t\t\tcommitTimeout: z\n\t\t\t\t.number()\n\t\t\t\t.min(1000, 'Commit timeout must be at least 1000ms')\n\t\t\t\t.max(600000, 'Commit timeout cannot exceed 600000ms (10 minutes)')\n\t\t\t\t.optional()\n\t\t\t\t.describe('Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.'),\n\t\t})\n\t\t.optional()\n\t\t.describe('Git operation settings'),\n})\n\n/**\n * TypeScript type for Neon settings derived from Zod schema\n */\nexport type NeonSettings = z.infer<typeof NeonSettingsSchema>\n\n/**\n * TypeScript type for database providers settings derived from Zod schema\n */\nexport type DatabaseProvidersSettings = z.infer<typeof DatabaseProvidersSettingsSchema>\n\n/**\n * TypeScript type for agent settings derived from Zod schema\n */\nexport type AgentSettings = z.infer<typeof AgentSettingsSchema>\n\n/**\n * TypeScript type for spin agent settings derived from Zod schema\n */\nexport type SpinAgentSettings = z.infer<typeof SpinAgentSettingsSchema>\n\n/**\n * TypeScript type for plan command settings derived from Zod schema\n */\nexport type PlanCommandSettings = z.infer<typeof PlanCommandSettingsSchema>\n\n/**\n * TypeScript type for summary settings derived from Zod schema\n */\nexport type SummarySettings = z.infer<typeof SummarySettingsSchema>\n\n/**\n * TypeScript type for workflow permission configuration derived from Zod schema\n */\nexport type WorkflowPermission = z.infer<typeof WorkflowPermissionSchema>\n\n/**\n * TypeScript type for workflows settings derived from Zod schema\n */\nexport type WorkflowsSettings = z.infer<typeof WorkflowsSettingsSchema>\n\n/**\n * TypeScript type for capabilities settings derived from Zod schema\n */\nexport type CapabilitiesSettings = z.infer<typeof CapabilitiesSettingsSchema>\n\n/**\n * TypeScript type for IDE settings derived from Zod schema\n */\nexport type IdeSettings = z.infer<typeof IloomSettingsSchema>['ide']\n\n/**\n * TypeScript type for iloom settings derived from Zod schema\n */\nexport type IloomSettings = z.infer<typeof IloomSettingsSchema>\n\n/**\n * TypeScript input type for iloom settings (before Zod defaults are applied)\n * Used for validation where partial/input objects need to be accepted\n */\nexport type IloomSettingsInput = z.input<typeof IloomSettingsSchema>\n\nfunction redactSensitiveFields(obj: unknown): unknown {\n\tif (obj === null || obj === undefined) return obj\n\tif (typeof obj !== 'object') return obj\n\tif (Array.isArray(obj)) return obj.map(redactSensitiveFields)\n\tconst sensitiveKeys = ['apitoken', 'token', 'secret', 'password']\n\tconst result: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n\t\tconst lowerKey = key.toLowerCase()\n\t\tif (sensitiveKeys.some(s => lowerKey.includes(s)) && typeof value === 'string') {\n\t\t\tresult[key] = '[REDACTED]'\n\t\t} else if (typeof value === 'object' && value !== null) {\n\t\t\tresult[key] = redactSensitiveFields(value)\n\t\t} else {\n\t\t\tresult[key] = value\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Manages project-level settings from .iloom/settings.json\n */\nexport class SettingsManager {\n\t/**\n\t * Load settings from global, project, and local sources with proper precedence\n\t * Merge hierarchy (lowest to highest priority):\n\t * 1. Global settings (~/.config/iloom-ai/settings.json)\n\t * 2. Project settings (<PROJECT_ROOT>/.iloom/settings.json)\n\t * 3. Local settings (<PROJECT_ROOT>/.iloom/settings.local.json)\n\t * 4. CLI overrides (--set flags)\n\t * Returns empty object if all files don't exist (not an error)\n\t */\n\tasync loadSettings(\n\t\tprojectRoot?: string,\n\t\tcliOverrides?: Partial<IloomSettings>,\n\t): Promise<IloomSettings> {\n\t\tconst root = this.getProjectRoot(projectRoot)\n\n\t\t// Load global settings (lowest priority)\n\t\tconst globalSettings = await this.loadGlobalSettingsFile()\n\t\tconst globalSettingsPath = this.getGlobalSettingsPath()\n\t\tlogger.debug(`🌍 Global settings from ${globalSettingsPath}:`, JSON.stringify(redactSensitiveFields(globalSettings), null, 2))\n\n\t\t// Load base settings from settings.json\n\t\tconst baseSettings = await this.loadSettingsFile(root, 'settings.json')\n\t\tconst baseSettingsPath = path.join(root, '.iloom', 'settings.json')\n\t\tlogger.debug(`📄 Base settings from ${baseSettingsPath}:`, JSON.stringify(redactSensitiveFields(baseSettings), null, 2))\n\n\t\t// Load local overrides from settings.local.json\n\t\tconst localSettings = await this.loadSettingsFile(root, 'settings.local.json')\n\t\tconst localSettingsPath = path.join(root, '.iloom', 'settings.local.json')\n\t\tlogger.debug(`📄 Local settings from ${localSettingsPath}:`, JSON.stringify(redactSensitiveFields(localSettings), null, 2))\n\n\t\t// Deep merge with priority: cliOverrides > localSettings > baseSettings > globalSettings\n\t\tlet merged = this.mergeSettings(this.mergeSettings(globalSettings, baseSettings), localSettings)\n\t\tlogger.debug('🔄 After merging global + base + local settings:', JSON.stringify(redactSensitiveFields(merged), null, 2))\n\n\t\tif (cliOverrides && Object.keys(cliOverrides).length > 0) {\n\t\t\tlogger.debug('⚙️ CLI overrides to apply:', JSON.stringify(redactSensitiveFields(cliOverrides), null, 2))\n\t\t\tmerged = this.mergeSettings(merged, cliOverrides)\n\t\t\tlogger.debug('🔄 After applying CLI overrides:', JSON.stringify(redactSensitiveFields(merged), null, 2))\n\t\t}\n\n\t\t// Validate merged result\n\t\ttry {\n\t\t\tconst finalSettings = IloomSettingsSchema.parse(merged)\n\n\t\t\t// Debug: Log final merged configuration\n\t\t\tthis.logFinalConfiguration(finalSettings)\n\n\t\t\treturn finalSettings\n\t\t} catch (error) {\n\t\t\t// Show all Zod validation errors\n\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\tconst errorMsg = this.formatAllZodErrors(error, '<merged settings>')\n\t\t\t\t// Enhance error message if CLI overrides were applied\n\t\t\t\tif (cliOverrides && Object.keys(cliOverrides).length > 0) {\n\t\t\t\t\tthrow new Error(`${errorMsg.message}\\n\\nNote: CLI overrides were applied. Check your --set arguments.`)\n\t\t\t\t}\n\t\t\t\tthrow errorMsg\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Log the final merged configuration for debugging\n\t */\n\tprivate logFinalConfiguration(settings: IloomSettings): void {\n\t\tlogger.debug('📋 Final merged configuration:', JSON.stringify(redactSensitiveFields(settings), null, 2))\n\t}\n\n\t/**\n\t * Load and parse a single settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Uses non-defaulting schema to prevent polluting partial settings with defaults before merge\n\t */\n\tprivate async loadSettingsFile(\n\t\tprojectRoot: string,\n\t\tfilename: string,\n\t): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = path.join(projectRoot, '.iloom', filename)\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to parse settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Basic type checking - ensure it's an object, but don't validate schema completeness\n\t\t\t// Individual files may be incomplete (e.g., Linear config split between files)\n\t\t\t// Final validation will happen on the merged result in loadSettings()\n\t\t\tif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Settings validation failed at ${filename}:\\n - root: Expected object, received ${typeof parsed}`\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn parsed as z.infer<typeof IloomSettingsSchemaNoDefaults>\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No settings file found at ${settingsPath}, using defaults`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Re-throw parsing errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Deep merge two settings objects with priority to override\n\t * Uses deepmerge library with array replacement strategy\n\t */\n\tprivate mergeSettings(\n\t\tbase: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t\toverride: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t): IloomSettings {\n\t\t// Use deepmerge with array replacement (not concatenation)\n\t\t// Type assertion is safe because the merged result will be validated with IloomSettingsSchema\n\t\t// which applies all the defaults after merging\n\t\treturn deepmerge(base as Record<string, unknown>, override as Record<string, unknown>, {\n\t\t\t// Replace arrays instead of concatenating them\n\t\t\tarrayMerge: (_destinationArray, sourceArray) => sourceArray,\n\t\t}) as IloomSettings\n\t}\n\n\t/**\n\t * Format all Zod validation errors into a single error message\n\t */\n\tprivate formatAllZodErrors(error: z.ZodError, settingsPath: string): Error {\n\t\tconst errorMessages = error.issues.map(issue => {\n\t\t\tconst path = issue.path.length > 0 ? issue.path.join('.') : 'root'\n\t\t\treturn ` - ${path}: ${issue.message}`\n\t\t})\n\n\t\treturn new Error(\n\t\t\t`Settings validation failed at ${settingsPath}:\\n${errorMessages.join('\\n')}`,\n\t\t)\n\t}\n\n\t/**\n\t * Validate settings structure and model names using Zod schema\n\t * This method is kept for testing purposes but uses Zod internally\n\t * @internal - Only used in tests via bracket notation\n\t */\n\t// @ts-expect-error - Used in tests via bracket notation, TypeScript can't detect this usage\n\tprivate validateSettings(settings: IloomSettingsInput): void {\n\t\ttry {\n\t\t\tIloomSettingsSchema.parse(settings)\n\t\t} catch (error) {\n\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\tthrow this.formatAllZodErrors(error, '<validation>')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Get project root (defaults to process.cwd())\n\t */\n\tprivate getProjectRoot(projectRoot?: string): string {\n\t\treturn projectRoot ?? process.cwd()\n\t}\n\n\t/**\n\t * Get global config directory path (~/.config/iloom-ai)\n\t */\n\tprivate getGlobalConfigDir(): string {\n\t\treturn path.join(os.homedir(), '.config', 'iloom-ai')\n\t}\n\n\t/**\n\t * Get global settings file path (~/.config/iloom-ai/settings.json)\n\t */\n\tprivate getGlobalSettingsPath(): string {\n\t\treturn path.join(this.getGlobalConfigDir(), 'settings.json')\n\t}\n\n\t/**\n\t * Load and parse global settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Warns but returns empty object on validation/parse errors (graceful degradation)\n\t */\n\tprivate async loadGlobalSettingsFile(): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = this.getGlobalSettingsPath()\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to parse global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}. Ignoring global settings.`,\n\t\t\t\t)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Validate with non-defaulting schema\n\t\t\ttry {\n\t\t\t\tconst validated = IloomSettingsSchemaNoDefaults.strict().parse(parsed)\n\t\t\t\treturn validated\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\t\tconst errorMsg = this.formatAllZodErrors(error, 'global settings')\n\t\t\t\t\tlogger.warn(`${errorMsg.message}. Ignoring global settings.`)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Validation error in global settings: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\t\t}\n\t\t\t\treturn {}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No global settings file found at ${settingsPath}`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Other file system errors - warn and continue\n\t\t\tlogger.warn(`Error reading global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\treturn {}\n\t\t}\n\t}\n\n\t/**\n\t * Get effective protected branches list with mainBranch always included\n\t *\n\t * This method provides a single source of truth for protected branches logic:\n\t * 1. Use configured protectedBranches if provided\n\t * 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t * 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t *\n\t * @param projectRoot - Optional project root directory (defaults to process.cwd())\n\t * @returns Array of protected branch names with mainBranch guaranteed to be included\n\t */\n\tasync getProtectedBranches(projectRoot?: string): Promise<string[]> {\n\t\tconst settings = await this.loadSettings(projectRoot)\n\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\tconst mainBranch = settings.mainBranch ?? 'main'\n\n\t\t// Build protected branches list:\n\t\t// 1. Use configured protectedBranches if provided\n\t\t// 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t\t// 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t\tlet protectedBranches: string[]\n\t\tif (settings.protectedBranches) {\n\t\t\t// Use configured list but ensure mainBranch is always included\n\t\t\tprotectedBranches = settings.protectedBranches.includes(mainBranch)\n\t\t\t\t? settings.protectedBranches\n\t\t\t\t: [mainBranch, ...settings.protectedBranches]\n\t\t} else {\n\t\t\t// Use defaults with current mainBranch\n\t\t\tprotectedBranches = [mainBranch, 'main', 'master', 'develop']\n\t\t}\n\n\t\treturn protectedBranches\n\t}\n\n\t/**\n\t * Get the spin orchestrator model with default applied\n\t * Default is defined in SpinAgentSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetSpinModel(settings?: IloomSettings, mode?: 'swarm'): 'sonnet' | 'opus' | 'haiku' {\n\t\tif (mode === 'swarm') {\n\t\t\tif (settings?.spin?.swarmModel) {\n\t\t\t\treturn settings.spin.swarmModel\n\t\t\t}\n\t\t\t// Default to opus for swarm orchestrator (\"Balanced\" mode)\n\t\t\treturn 'opus'\n\t\t}\n\t\treturn settings?.spin?.model ?? SpinAgentSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command model with default applied\n\t * Default is defined in PlanCommandSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetPlanModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.plan?.model ?? PlanCommandSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command planner with default applied\n\t * Default is 'claude'\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Planner provider ('claude', 'gemini', or 'codex')\n\t */\n\tgetPlanPlanner(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' {\n\t\treturn settings?.plan?.planner ?? 'claude'\n\t}\n\n\t/**\n\t * Get the plan command reviewer with default applied\n\t * Default is 'none' (no review step)\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Reviewer provider ('claude', 'gemini', 'codex', or 'none')\n\t */\n\tgetPlanReviewer(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' | 'none' {\n\t\treturn settings?.plan?.reviewer ?? 'none'\n\t}\n\n\t/**\n\t * Get the session summary model with default applied\n\t * Default is defined in SummarySettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetSummaryModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.summary?.model ?? SummarySettingsSchema.parse({}).model\n\t}\n}\n","/**\n * Jira implementation of Issue Management Provider\n * Uses JiraIssueTracker for all operations\n * Normalizes Jira-specific fields to provider-agnostic core fields\n */\n\nimport type {\n\tIssueManagementProvider,\n\tGetIssueInput,\n\tGetPRInput,\n\tPRResult,\n\tGetCommentInput,\n\tCreateCommentInput,\n\tUpdateCommentInput,\n\tCreateIssueInput,\n\tCreateChildIssueInput,\n\tCreateDependencyInput,\n\tGetDependenciesInput,\n\tDependenciesResult,\n\tRemoveDependencyInput,\n\tGetChildIssuesInput,\n\tCloseIssueInput,\n\tReopenIssueInput,\n\tEditIssueInput,\n\tChildIssueResult,\n\tCreateIssueResult,\n\tIssueResult,\n\tCommentDetailResult,\n\tCommentResult,\n\tFlexibleAuthor,\n} from './types.js'\nimport { escapeJql } from '../utils/jira.js'\nimport { JiraIssueTracker } from '../lib/providers/jira/JiraIssueTracker.js'\nimport type { JiraTrackerConfig } from '../lib/providers/jira/JiraIssueTracker.js'\nimport type { Issue } from '../types/index.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\n\n/**\n * Normalize Jira author to FlexibleAuthor format\n */\nfunction normalizeAuthor(author: { displayName?: string; emailAddress?: string; accountId?: string } | null | undefined): FlexibleAuthor | null {\n\tif (!author) return null\n\n\treturn {\n\t\tid: author.accountId ?? author.emailAddress ?? 'unknown',\n\t\tdisplayName: author.displayName ?? author.emailAddress ?? 'Unknown',\n\t\t...(author.emailAddress && { email: author.emailAddress }),\n\t\t...(author.accountId && { accountId: author.accountId }),\n\t}\n}\n/**\n * Extract Jira configuration from settings (for cli usage) or environment variables (in mcp server)\n */\nconst getJiraTrackerConfig = (settings: IloomSettings): JiraTrackerConfig => {\n\tconst jiraSettings = settings.issueManagement?.jira\n\n\tif (jiraSettings?.host && jiraSettings?.username && jiraSettings?.apiToken && jiraSettings?.projectKey) {\n\t\t\tconst config: JiraTrackerConfig = {\n\t\t\thost: jiraSettings.host,\n\t\t\tusername: jiraSettings.username,\n\t\t\tapiToken: jiraSettings.apiToken,\n\t\t\tprojectKey: jiraSettings.projectKey,\n\t\t}\n\n\t\tif (jiraSettings.transitionMappings) {\n\t\t\tconfig.transitionMappings = jiraSettings.transitionMappings\n\t\t}\n\t\tif (jiraSettings.defaultIssueType) {\n\t\t\tconfig.defaultIssueType = jiraSettings.defaultIssueType\n\t\t}\n\t\tif (jiraSettings.defaultSubtaskType) {\n\t\t\tconfig.defaultSubtaskType = jiraSettings.defaultSubtaskType\n\t\t}\n\n\t\treturn config;\n\t}\n\n\tif (process.env.JIRA_HOST && process.env.JIRA_USERNAME && process.env.JIRA_API_TOKEN && process.env.JIRA_PROJECT_KEY) {\n\t\tconst config: JiraTrackerConfig = {\n\t\t\thost: process.env.JIRA_HOST,\n\t\t\tusername: process.env.JIRA_USERNAME,\n\t\t\tapiToken: process.env.JIRA_API_TOKEN,\n\t\t\tprojectKey: process.env.JIRA_PROJECT_KEY,\n\t\t}\n\n\t\tif (process.env.JIRA_TRANSITION_MAPPINGS) {\n\t\t\ttry {\n\t\t\t\tconfig.transitionMappings = JSON.parse(process.env.JIRA_TRANSITION_MAPPINGS)\n\t\t\t} catch {\n\t\t\t\tthrow new Error('Invalid JSON in JIRA_TRANSITION_MAPPINGS environment variable')\n\t\t\t}\n\t\t}\n\t\tif (process.env.JIRA_DEFAULT_ISSUE_TYPE) {\n\t\t\tconfig.defaultIssueType = process.env.JIRA_DEFAULT_ISSUE_TYPE\n\t\t}\n\t\tif (process.env.JIRA_DEFAULT_SUBTASK_TYPE) {\n\t\t\tconfig.defaultSubtaskType = process.env.JIRA_DEFAULT_SUBTASK_TYPE\n\t\t}\n\n\t\treturn config\n\t}\n\n\tthrow new Error(\n\t\t'Missing required Jira settings: issueManagement.jira.{host, username, apiToken, projectKey} or corresponding environment variables'\n\t)\t\n}\n\n/**\n * Jira-specific implementation of IssueManagementProvider\n */\nexport class JiraIssueManagementProvider implements IssueManagementProvider {\n\treadonly providerName = 'jira'\n\treadonly issuePrefix = ''\n\tprivate tracker: JiraIssueTracker\n\tprivate projectKey: string\n\n\tconstructor(settings: IloomSettings) {\n\t\tconst config = getJiraTrackerConfig(settings);\n\n\t\tthis.tracker = new JiraIssueTracker(config)\n\t\tthis.projectKey = config.projectKey\n\t}\n\n\t/**\n\t * Static factory for convenience when settings aren't pre-loaded\n\t */\n\tstatic async create(): Promise<JiraIssueManagementProvider> {\n\t\tconst settingsManager = new SettingsManager()\n\t\tconst settings = await settingsManager.loadSettings()\n\t\treturn new JiraIssueManagementProvider(settings)\n\t}\n\n\t/**\n\t * Fetch issue details using JiraIssueTracker\n\t */\n\tasync getIssue(input: GetIssueInput): Promise<IssueResult> {\n\t\tconst { number, includeComments = true } = input\n\n\t\t// Fetch issue from Jira\n\t\tconst issue = await this.tracker.getIssue(number)\n\t\tconst issueExt = issue as Issue & {\n\t\t\tid?: string\n\t\t\tkey?: string\n\t\t\tauthor?: {\n\t\t\t\tdisplayName?: string\n\t\t\t\temailAddress?: string\n\t\t\t\taccountId?: string\n\t\t\t}\n\t\t\tissueType?: string\n\t\t\tpriority?: string\n\t\t\tstatus?: string\n\t\t}\n\n\t\t// Normalize to IssueResult format\n\t\tconst result: IssueResult = {\n\t\t\tid: issueExt.id ?? String(issue.number),\n\t\t\ttitle: issue.title,\n\t\t\tbody: issue.body,\n\t\t\tstate: issue.state,\n\t\t\turl: issue.url,\n\t\t\tprovider: 'jira',\n\t\t\tauthor: normalizeAuthor(issueExt.author),\n\t\t\tnumber: issue.number,\n\t\t\tkey: issueExt.key,\n\t\t\t// Preserve Jira-specific fields\n\t\t\t...(issueExt.issueType && { issueType: issueExt.issueType }),\n\t\t\t...(issueExt.priority && { priority: issueExt.priority }),\n\t\t\t...(issueExt.status && { status: issueExt.status }),\n\t\t}\n\n\t\t// Add labels if present\n\t\tif (issue.labels && issue.labels.length > 0) {\n\t\t\tresult.labels = issue.labels.map(label => ({ name: label }))\n\t\t}\n\n\t\t// Add assignees if present - Issue type uses assignees array of strings\n\t\tif (issue.assignees && issue.assignees.length > 0) {\n\t\t\tresult.assignees = issue.assignees.map(name => ({\n\t\t\t\tid: name,\n\t\t\t\tdisplayName: name,\n\t\t\t}))\n\t\t}\n\n\t\t// Fetch and add comments if requested\n\t\tif (includeComments) {\n\t\t\tconst comments = await this.tracker.getComments(number)\n\t\t\tresult.comments = comments.map((comment: {\n\t\t\t\tid: string\n\t\t\t\tbody: string\n\t\t\t\tauthor: { displayName: string; emailAddress: string; accountId: string }\n\t\t\t\tcreatedAt: string\n\t\t\t\tupdatedAt: string\n\t\t\t}) => ({\n\t\t\t\tid: comment.id,\n\t\t\t\tbody: comment.body,\n\t\t\t\tauthor: normalizeAuthor(comment.author),\n\t\t\t\tcreatedAt: comment.createdAt,\n\t\t\t\tupdatedAt: comment.updatedAt,\n\t\t\t}))\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch a specific comment by ID\n\t */\n\tasync getComment(input: GetCommentInput): Promise<CommentDetailResult> {\n\t\tconst { commentId, number } = input\n\n\t\t// Fetch all comments and find the specific one\n\t\tconst comments = await this.tracker.getComments(number)\n\t\tconst comment = comments.find(c => c.id === commentId)\n\n\t\tif (!comment) {\n\t\t\tthrow new Error(`Comment ${commentId} not found on issue ${number}`)\n\t\t}\n\n\t\treturn {\n\t\t\tid: comment.id,\n\t\t\tbody: comment.body,\n\t\t\tauthor: normalizeAuthor(comment.author),\n\t\t\tcreated_at: comment.createdAt,\n\t\t\tupdated_at: comment.updatedAt,\n\t\t}\n\t}\n\n\t/**\n\t * Create a new comment on an issue\n\t */\n\tasync createComment(input: CreateCommentInput): Promise<CommentResult> {\n\t\tconst { number, body } = input\n\t\tconst normalizedKey = this.tracker.normalizeIdentifier(number)\n\n\t\t// Jira doesn't distinguish between issue and PR comments\n\t\tconst comment = await this.tracker.addComment(normalizedKey, body)\n\n\t\treturn {\n\t\t\tid: comment.id,\n\t\t\turl: `${this.tracker.getConfig().host}/browse/${normalizedKey}?focusedCommentId=${comment.id}`,\n\t\t\tcreated_at: new Date().toISOString(),\n\t\t}\n\t}\n\n\t/**\n\t * Update an existing comment\n\t */\n\tasync updateComment(input: UpdateCommentInput): Promise<CommentResult> {\n\t\tconst { commentId, number, body } = input\n\t\tconst normalizedKey = this.tracker.normalizeIdentifier(number)\n\n\t\t// Update comment via tracker\n\t\tawait this.tracker.updateComment(normalizedKey, commentId, body)\n\n\t\treturn {\n\t\t\tid: commentId,\n\t\t\turl: `${this.tracker.getConfig().host}/browse/${normalizedKey}?focusedCommentId=${commentId}`,\n\t\t\tupdated_at: new Date().toISOString(),\n\t\t}\n\t}\n\n\t/**\n\t * Create a new issue\n\t */\n\tasync createIssue(input: CreateIssueInput): Promise<CreateIssueResult> {\n\t\tconst { title, body } = input\n\n\t\t// Create issue via tracker (labels not supported in current implementation)\n\t\tconst issue = await this.tracker.createIssue(title, body)\n\n\t\tconst result: CreateIssueResult = {\n\t\t\tid: String(issue.number),\n\t\t\turl: issue.url,\n\t\t}\n\n\t\t// Only add number if it's actually a number\n\t\tif (typeof issue.number === 'number') {\n\t\t\tresult.number = issue.number\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Fetch pull request details\n\t * Jira does not have pull requests - throw like Linear does\n\t */\n\tasync getPR(_input: GetPRInput): Promise<PRResult> {\n\t\tthrow new Error(\n\t\t\t'Jira does not support pull requests. PRs exist only on GitHub. Use the GitHub provider for PR operations.'\n\t\t)\n\t}\n\n\t/**\n\t * Create a child issue linked to a parent issue\n\t * Uses Jira's parent field to create a subtask\n\t */\n\tasync createChildIssue(input: CreateChildIssueInput): Promise<CreateIssueResult> {\n\t\tconst { parentId, title, body } = input\n\t\tconst parentKey = this.tracker.normalizeIdentifier(parentId)\n\n\t\tconst jiraIssue = await this.tracker.getApiClient().createIssueWithParent(\n\t\t\tthis.projectKey,\n\t\t\ttitle,\n\t\t\tbody,\n\t\t\tparentKey,\n\t\t\tthis.tracker.getConfig().defaultSubtaskType\n\t\t)\n\n\t\treturn {\n\t\t\tid: jiraIssue.key,\n\t\t\turl: `${this.tracker.getConfig().host}/browse/${jiraIssue.key}`,\n\t\t}\n\t}\n\n\t/**\n\t * Create a blocking dependency between two issues\n\t * Uses Jira issue links with \"Blocks\" link type\n\t */\n\tasync createDependency(input: CreateDependencyInput): Promise<void> {\n\t\tconst blockingKey = this.tracker.normalizeIdentifier(input.blockingIssue)\n\t\tconst blockedKey = this.tracker.normalizeIdentifier(input.blockedIssue)\n\n\t\t// In Jira \"Blocks\" link type: inwardIssue = blocker, outwardIssue = blocked\n\t\tawait this.tracker.getApiClient().createIssueLink(blockingKey, blockedKey, 'Blocks')\n\t}\n\n\t/**\n\t * Get dependencies for an issue\n\t * Parses issue links of type \"Blocks\"\n\t */\n\tasync getDependencies(input: GetDependenciesInput): Promise<DependenciesResult> {\n\t\tconst issueKey = this.tracker.normalizeIdentifier(input.number)\n\t\tconst host = this.tracker.getConfig().host\n\n\t\tconst issue = await this.tracker.getApiClient().getIssue(issueKey)\n\t\tconst links = issue.fields.issuelinks ?? []\n\n\t\tconst blocking: DependenciesResult['blocking'] = []\n\t\tconst blockedBy: DependenciesResult['blockedBy'] = []\n\n\t\tfor (const link of links) {\n\t\t\tif (link.type.name !== 'Blocks') continue\n\n\t\t\t// inwardIssue present = the other issue is the blocker\n\t\t\t// → that issue blocks this issue → blockedBy\n\t\t\tif (link.inwardIssue) {\n\t\t\t\tblockedBy.push({\n\t\t\t\t\tid: link.inwardIssue.key,\n\t\t\t\t\ttitle: link.inwardIssue.fields.summary,\n\t\t\t\t\turl: `${host}/browse/${link.inwardIssue.key}`,\n\t\t\t\t\tstate: link.inwardIssue.fields.status.name.toLowerCase(),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// outwardIssue present = the other issue is blocked by this issue\n\t\t\t// → this issue blocks that issue → blocking\n\t\t\tif (link.outwardIssue) {\n\t\t\t\tblocking.push({\n\t\t\t\t\tid: link.outwardIssue.key,\n\t\t\t\t\ttitle: link.outwardIssue.fields.summary,\n\t\t\t\t\turl: `${host}/browse/${link.outwardIssue.key}`,\n\t\t\t\t\tstate: link.outwardIssue.fields.status.name.toLowerCase(),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tif (input.direction === 'blocking') {\n\t\t\treturn { blocking, blockedBy: [] }\n\t\t}\n\t\tif (input.direction === 'blocked_by') {\n\t\t\treturn { blocking: [], blockedBy }\n\t\t}\n\t\treturn { blocking, blockedBy }\n\t}\n\n\t/**\n\t * Remove a blocking dependency between two issues\n\t * Finds the matching \"Blocks\" link and deletes it\n\t */\n\tasync removeDependency(input: RemoveDependencyInput): Promise<void> {\n\t\tconst blockingKey = this.tracker.normalizeIdentifier(input.blockingIssue)\n\t\tconst blockedKey = this.tracker.normalizeIdentifier(input.blockedIssue)\n\n\t\t// Fetch the blocked issue to find the link\n\t\tconst issue = await this.tracker.getApiClient().getIssue(blockedKey)\n\t\tconst links = issue.fields.issuelinks ?? []\n\n\t\t// When fetching the blocked issue (B), the blocking issue (A) appears as\n\t\t// inwardIssue in the link data\n\t\tconst matchingLink = links.find(link =>\n\t\t\tlink.type.name === 'Blocks' && link.inwardIssue?.key === blockingKey\n\t\t)\n\n\t\tif (!matchingLink) {\n\t\t\tthrow new Error(\n\t\t\t\t`No \"Blocks\" dependency found from ${blockingKey} to ${blockedKey}`\n\t\t\t)\n\t\t}\n\n\t\tawait this.tracker.getApiClient().deleteIssueLink(matchingLink.id)\n\t}\n\n\t/**\n\t * Get child issues of a parent issue\n\t * Uses JQL search: parent = KEY\n\t */\n\tasync getChildIssues(input: GetChildIssuesInput): Promise<ChildIssueResult[]> {\n\t\tconst parentKey = this.tracker.normalizeIdentifier(input.number)\n\t\tconst host = this.tracker.getConfig().host\n\n\t\tconst issues = await this.tracker.getApiClient().searchIssues(`parent = \"${escapeJql(parentKey)}\"`)\n\n\t\treturn issues.map(issue => ({\n\t\t\tid: issue.key,\n\t\t\ttitle: issue.fields.summary,\n\t\t\turl: `${host}/browse/${issue.key}`,\n\t\t\tstate: issue.fields.status.name.toLowerCase(),\n\t\t}))\n\t}\n\n\t/**\n\t * Close an issue by transitioning to \"Done\" state\n\t */\n\tasync closeIssue(input: CloseIssueInput): Promise<void> {\n\t\tconst issueKey = this.tracker.normalizeIdentifier(input.number)\n\t\tawait this.tracker.closeIssue(issueKey)\n\t}\n\n\t/**\n\t * Reopen a closed issue\n\t */\n\tasync reopenIssue(input: ReopenIssueInput): Promise<void> {\n\t\tconst issueKey = this.tracker.normalizeIdentifier(input.number)\n\t\tawait this.tracker.reopenIssue(issueKey)\n\t}\n\n\t/**\n\t * Edit an issue's properties\n\t * State changes are delegated to closeIssue/reopenIssue\n\t */\n\tasync editIssue(input: EditIssueInput): Promise<void> {\n\t\tconst { number, title, body, state } = input\n\n\t\t// Handle state changes via close/reopen\n\t\tif (state === 'closed') {\n\t\t\tawait this.closeIssue({ number })\n\t\t} else if (state === 'open') {\n\t\t\tawait this.reopenIssue({ number })\n\t\t}\n\n\t\t// Handle title/body updates via Jira API\n\t\tif (title !== undefined || body !== undefined) {\n\t\t\tconst issueKey = this.tracker.normalizeIdentifier(number)\n\t\t\tawait this.tracker.getApiClient().updateIssue(issueKey, {\n\t\t\t\t...(title !== undefined && { summary: title }),\n\t\t\t\t...(body !== undefined && { description: body }),\n\t\t\t})\n\t\t}\n\t}\n}\n","/**\n * Factory for creating issue management providers\n */\n\nimport type { IssueManagementProvider, IssueProvider } from './types.js'\nimport { GitHubIssueManagementProvider } from './GitHubIssueManagementProvider.js'\nimport { LinearIssueManagementProvider } from './LinearIssueManagementProvider.js'\nimport { JiraIssueManagementProvider } from './JiraIssueManagementProvider.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\n\n/**\n * Factory class for creating issue management providers\n */\nexport class IssueManagementProviderFactory {\n\t/**\n\t * Create an issue management provider based on the provider type\n\t * @param provider - The provider type (github, linear, jira)\n\t * @param settings - Required for Jira provider, optional for others\n\t */\n\tstatic create(provider: IssueProvider, settings?: IloomSettings): IssueManagementProvider {\n\t\tswitch (provider) {\n\t\t\tcase 'github':\n\t\t\t\treturn new GitHubIssueManagementProvider()\n\t\t\tcase 'linear':\n\t\t\t\treturn new LinearIssueManagementProvider()\n\t\t\tcase 'jira':\n\t\t\t\tif (!settings) {\n\t\t\t\t\tthrow new Error('Settings required for Jira provider')\n\t\t\t\t}\n\t\t\t\treturn new JiraIssueManagementProvider(settings)\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported issue management provider: ${provider}`)\n\t\t}\n\t}\n}\n","/**\n * Utility class for detecting and converting Jira Wiki markup to GitHub-Flavored Markdown.\n * Applied at the MCP layer to prevent malformed content from reaching issue tracker providers.\n *\n * IMPORTANT: Only converts UNAMBIGUOUS Jira Wiki patterns.\n * Patterns that overlap with valid Markdown (e.g., *bold*, _italic_) are\n * intentionally NOT converted to avoid corrupting valid Markdown content.\n *\n * Safe conversions (unambiguous):\n * - h1. through h6. headings at line start -> # through ######\n * - {code}...{code} and {code:lang}...{code} -> fenced code blocks\n * - {quote}...{quote} -> > blockquotes\n * - [text|url] -> [text](url) (only when url looks like http/https)\n *\n * Intentionally NOT converted (ambiguous):\n * - *text* (could be Markdown italic)\n * - _text_ (could be Markdown italic or snake_case)\n */\nexport class JiraWikiSanitizer {\n\t/**\n\t * Sanitize body text by converting unambiguous Jira Wiki patterns to Markdown.\n\t * Preserves content inside backtick-fenced code blocks.\n\t * Returns text unchanged if no Wiki patterns detected.\n\t */\n\tstatic sanitize(text: string): string {\n\t\tif (!text) {\n\t\t\treturn ''\n\t\t}\n\n\t\t// Split text into fenced code block segments vs non-code-block segments.\n\t\t// We only apply conversions to non-code-block segments to avoid corrupting code examples.\n\t\tconst segments = this.splitByCodeBlocks(text)\n\t\tconst converted = segments.map((segment) => {\n\t\t\tif (segment.isCode) {\n\t\t\t\treturn segment.text\n\t\t\t}\n\t\t\treturn this.convertSegment(segment.text)\n\t\t})\n\n\t\treturn converted.join('')\n\t}\n\n\t/**\n\t * Check if text contains unambiguous Jira Wiki patterns.\n\t * Only checks for patterns that are safe to convert.\n\t */\n\tstatic hasJiraWikiPatterns(text: string): boolean {\n\t\tif (!text) {\n\t\t\treturn false\n\t\t}\n\n\t\t// Check for h1. through h6. at line start\n\t\tif (/^h[1-6]\\.\\s+/m.test(text)) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Check for {code} or {code:lang} blocks\n\t\tif (/\\{code(?::[^}]*)?\\}/i.test(text)) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Check for {quote} blocks\n\t\tif (/\\{quote\\}/i.test(text)) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Check for [text|url] links where url starts with http\n\t\tif (/\\[[^\\]|]+\\|https?:\\/\\/[^\\]]+\\]/.test(text)) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t/**\n\t * Split text into segments, separating existing Markdown fenced code blocks\n\t * from the rest of the content. This ensures we don't modify content inside\n\t * code blocks (e.g., Jira Wiki examples shown in a Markdown code block).\n\t */\n\tprivate static splitByCodeBlocks(text: string): Array<{ text: string; isCode: boolean }> {\n\t\tconst segments: Array<{ text: string; isCode: boolean }> = []\n\t\t// Match fenced code blocks: ``` optionally followed by language, then content, then ```\n\t\tconst codeBlockRegex = /^(`{3,})[^\\n]*\\n[\\s\\S]*?^\\1\\s*$/gm\n\t\tlet lastIndex = 0\n\n\t\tfor (const match of text.matchAll(codeBlockRegex)) {\n\t\t\tconst matchStart = match.index ?? 0\n\t\t\t// Add the text before this code block\n\t\t\tif (matchStart > lastIndex) {\n\t\t\t\tsegments.push({ text: text.slice(lastIndex, matchStart), isCode: false })\n\t\t\t}\n\t\t\t// Add the code block itself\n\t\t\tsegments.push({ text: match[0], isCode: true })\n\t\t\tlastIndex = matchStart + match[0].length\n\t\t}\n\n\t\t// Add any remaining text after the last code block\n\t\tif (lastIndex < text.length) {\n\t\t\tsegments.push({ text: text.slice(lastIndex), isCode: false })\n\t\t}\n\n\t\treturn segments\n\t}\n\n\t/**\n\t * Apply all safe Jira Wiki -> Markdown conversions to a text segment.\n\t */\n\tprivate static convertSegment(text: string): string {\n\t\tlet result = text\n\n\t\t// 1. Convert headings: h1. through h6. at line start\n\t\tresult = result.replace(/^h([1-6])\\.\\s+(.*?)$/gm, (_match, level: string, content: string) => {\n\t\t\tconst hashes = '#'.repeat(parseInt(level, 10))\n\t\t\treturn `${hashes} ${content}`\n\t\t})\n\n\t\t// 2. Convert {code:lang}...{code} blocks (must come before plain {code} conversion)\n\t\tresult = result.replace(\n\t\t\t/\\{code:([^}]+)\\}\\s*\\n([\\s\\S]*?)\\n?\\s*\\{code\\}/gi,\n\t\t\t(_match, lang: string, content: string) => {\n\t\t\t\treturn '```' + lang.trim() + '\\n' + content + '\\n```'\n\t\t\t}\n\t\t)\n\n\t\t// 3. Convert {code}...{code} blocks (plain, no language)\n\t\tresult = result.replace(\n\t\t\t/\\{code\\}\\s*\\n([\\s\\S]*?)\\n?\\s*\\{code\\}/gi,\n\t\t\t(_match, content: string) => {\n\t\t\t\treturn '```\\n' + content + '\\n```'\n\t\t\t}\n\t\t)\n\n\t\t// 4. Convert {quote}...{quote} blocks to > blockquotes\n\t\tresult = result.replace(\n\t\t\t/\\{quote\\}\\s*\\n([\\s\\S]*?)\\n?\\s*\\{quote\\}/gi,\n\t\t\t(_match, content: string) => {\n\t\t\t\t// Prefix each line with > for blockquote\n\t\t\t\tconst lines = content.split('\\n')\n\t\t\t\treturn lines.map((line) => `> ${line}`).join('\\n')\n\t\t\t}\n\t\t)\n\n\t\t// 5. Convert [text|url] links where url starts with http(s)\n\t\tresult = result.replace(\n\t\t\t/\\[([^\\]|]+)\\|(https?:\\/\\/[^\\]]+)\\]/g,\n\t\t\t(_match, linkText: string, url: string) => {\n\t\t\t\treturn `[${linkText}](${url})`\n\t\t\t}\n\t\t)\n\n\t\treturn result\n\t}\n}\n"],"mappings":"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","names":["z","execa","execa","execa","existsSync","execa","existsSync","execa","existsSync","issue","join","extname","_a","path","os","path","settings","os","normalizeAuthor","settings","settings","z"]}