@iloom/cli 0.3.1 → 0.3.3

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 (128) hide show
  1. package/README.md +8 -6
  2. package/dist/{BranchNamingService-OMWKUYMM.js → BranchNamingService-A77VI6AI.js} +2 -2
  3. package/dist/ClaudeContextManager-BN7RE5ZQ.js +15 -0
  4. package/dist/ClaudeService-DLYLJUPA.js +14 -0
  5. package/dist/{GitHubService-EBOETDIW.js → GitHubService-FZHHBOFG.js} +3 -3
  6. package/dist/{LoomLauncher-JF7JZMTZ.js → LoomLauncher-ZV3ZZIBA.js} +40 -26
  7. package/dist/LoomLauncher-ZV3ZZIBA.js.map +1 -0
  8. package/dist/{PromptTemplateManager-A52RUAMS.js → PromptTemplateManager-6HH3PVXV.js} +2 -2
  9. package/dist/README.md +8 -6
  10. package/dist/{SettingsManager-ZCWJ56WP.js → SettingsManager-I2LRCW2A.js} +2 -2
  11. package/dist/{SettingsMigrationManager-AGIIIPDQ.js → SettingsMigrationManager-TJ7UWZG5.js} +3 -3
  12. package/dist/agents/iloom-issue-complexity-evaluator.md +18 -3
  13. package/dist/agents/iloom-issue-enhancer.md +1 -1
  14. package/dist/{chunk-TSKY3JI7.js → chunk-2CXREBLZ.js} +2 -2
  15. package/dist/{chunk-HBYZH6GD.js → chunk-2IJEMXOB.js} +431 -128
  16. package/dist/chunk-2IJEMXOB.js.map +1 -0
  17. package/dist/{chunk-IXKLYTWO.js → chunk-2MAIX45J.js} +8 -8
  18. package/dist/{chunk-4BGK7T6X.js → chunk-5Q3NDNNV.js} +48 -8
  19. package/dist/chunk-5Q3NDNNV.js.map +1 -0
  20. package/dist/{chunk-JQFO7QQN.js → chunk-5VK4NRSF.js} +3 -3
  21. package/dist/{chunk-JQFO7QQN.js.map → chunk-5VK4NRSF.js.map} +1 -1
  22. package/dist/{chunk-XPKDPZ5D.js → chunk-AKUJXDNW.js} +2 -2
  23. package/dist/{chunk-O5OH5MRX.js → chunk-CDZERT7Z.js} +23 -11
  24. package/dist/chunk-CDZERT7Z.js.map +1 -0
  25. package/dist/{chunk-JKXJ7BGL.js → chunk-CE26YH2U.js} +42 -3
  26. package/dist/chunk-CE26YH2U.js.map +1 -0
  27. package/dist/{chunk-ZZZWQGTS.js → chunk-CFFQ2Z7A.js} +74 -75
  28. package/dist/chunk-CFFQ2Z7A.js.map +1 -0
  29. package/dist/{chunk-RO26VS3W.js → chunk-DLHA5VQ3.js} +174 -5
  30. package/dist/chunk-DLHA5VQ3.js.map +1 -0
  31. package/dist/{chunk-ZBQVSHVT.js → chunk-IFB4Z76W.js} +35 -10
  32. package/dist/chunk-IFB4Z76W.js.map +1 -0
  33. package/dist/{chunk-G2IEYOLQ.js → chunk-M7JJCX53.js} +17 -2
  34. package/dist/chunk-M7JJCX53.js.map +1 -0
  35. package/dist/{chunk-KLBYVHPK.js → chunk-OSCLCMDG.js} +2 -2
  36. package/dist/chunk-OXAM2WVC.js +68 -0
  37. package/dist/chunk-OXAM2WVC.js.map +1 -0
  38. package/dist/{chunk-ZWFBBPJI.js → chunk-OYF4VIFI.js} +5 -3
  39. package/dist/chunk-OYF4VIFI.js.map +1 -0
  40. package/dist/{chunk-U5QDY7ZD.js → chunk-PGPI5LR4.js} +8 -8
  41. package/dist/{chunk-WEN5C5DM.js → chunk-RIEO2WML.js} +4 -1
  42. package/dist/chunk-RIEO2WML.js.map +1 -0
  43. package/dist/{chunk-ZE74H5BR.js → chunk-RW54ZMBM.js} +26 -20
  44. package/dist/chunk-RW54ZMBM.js.map +1 -0
  45. package/dist/{chunk-INW24J2W.js → chunk-SUOXY5WJ.js} +2 -2
  46. package/dist/{init-L55Q73H4.js → chunk-UAN4A3YU.js} +345 -45
  47. package/dist/chunk-UAN4A3YU.js.map +1 -0
  48. package/dist/{chunk-IP7SMKIF.js → chunk-UJL4HI2R.js} +59 -60
  49. package/dist/chunk-UJL4HI2R.js.map +1 -0
  50. package/dist/{claude-LUZ35IMK.js → claude-W52VKI6L.js} +4 -2
  51. package/dist/{cleanup-3MONU4PU.js → cleanup-H4VXU3C3.js} +19 -17
  52. package/dist/{cleanup-3MONU4PU.js.map → cleanup-H4VXU3C3.js.map} +1 -1
  53. package/dist/cli.js +347 -114
  54. package/dist/cli.js.map +1 -1
  55. package/dist/{color-ZVALX37U.js → color-F7RU6B6Z.js} +10 -4
  56. package/dist/{contribute-UWJAGIG7.js → contribute-Y7IQV5QY.js} +4 -3
  57. package/dist/{contribute-UWJAGIG7.js.map → contribute-Y7IQV5QY.js.map} +1 -1
  58. package/dist/{feedback-W3BXTGIM.js → feedback-XTUCKJNT.js} +16 -12
  59. package/dist/{feedback-W3BXTGIM.js.map → feedback-XTUCKJNT.js.map} +1 -1
  60. package/dist/{git-34Z6QVDS.js → git-IYA53VIC.js} +9 -2
  61. package/dist/{ignite-KVJEFXNO.js → ignite-T74RYXCA.js} +25 -75
  62. package/dist/ignite-T74RYXCA.js.map +1 -0
  63. package/dist/index.d.ts +71 -14
  64. package/dist/index.js +407 -377
  65. package/dist/index.js.map +1 -1
  66. package/dist/init-4FHTAM3F.js +19 -0
  67. package/dist/mcp/issue-management-server.js +8 -1
  68. package/dist/mcp/issue-management-server.js.map +1 -1
  69. package/dist/{neon-helpers-WPUACUVC.js → neon-helpers-77PBPGJ5.js} +3 -3
  70. package/dist/{open-LNRZL3UU.js → open-UMXANW5S.js} +27 -14
  71. package/dist/open-UMXANW5S.js.map +1 -0
  72. package/dist/{prompt-7INJ7YRU.js → prompt-QALMYTVC.js} +4 -2
  73. package/dist/prompts/init-prompt.txt +89 -9
  74. package/dist/prompts/issue-prompt.txt +18 -11
  75. package/dist/{rebase-C4WNCVGM.js → rebase-VJ2VKR6R.js} +15 -13
  76. package/dist/rebase-VJ2VKR6R.js.map +1 -0
  77. package/dist/{run-IOGNIOYN.js → run-MJYY4PUT.js} +27 -14
  78. package/dist/run-MJYY4PUT.js.map +1 -0
  79. package/dist/schema/settings.schema.json +22 -4
  80. package/dist/{test-git-J7I5MFYH.js → test-git-IT5EWQ5C.js} +5 -5
  81. package/dist/{test-prefix-ZCONBCBX.js → test-prefix-NPWDPUUH.js} +5 -5
  82. package/dist/{test-tabs-RXDBZ6J7.js → test-tabs-PRMRSHKI.js} +3 -2
  83. package/dist/{test-tabs-RXDBZ6J7.js.map → test-tabs-PRMRSHKI.js.map} +1 -1
  84. package/package.json +2 -1
  85. package/dist/ClaudeContextManager-3VXA6UPR.js +0 -13
  86. package/dist/ClaudeService-6CPK43N4.js +0 -12
  87. package/dist/LoomLauncher-JF7JZMTZ.js.map +0 -1
  88. package/dist/chunk-4BGK7T6X.js.map +0 -1
  89. package/dist/chunk-4E4LD3QR.js +0 -302
  90. package/dist/chunk-4E4LD3QR.js.map +0 -1
  91. package/dist/chunk-G2IEYOLQ.js.map +0 -1
  92. package/dist/chunk-HBYZH6GD.js.map +0 -1
  93. package/dist/chunk-IP7SMKIF.js.map +0 -1
  94. package/dist/chunk-JKXJ7BGL.js.map +0 -1
  95. package/dist/chunk-O5OH5MRX.js.map +0 -1
  96. package/dist/chunk-RO26VS3W.js.map +0 -1
  97. package/dist/chunk-WEN5C5DM.js.map +0 -1
  98. package/dist/chunk-ZBQVSHVT.js.map +0 -1
  99. package/dist/chunk-ZE74H5BR.js.map +0 -1
  100. package/dist/chunk-ZWFBBPJI.js.map +0 -1
  101. package/dist/chunk-ZZZWQGTS.js.map +0 -1
  102. package/dist/ignite-KVJEFXNO.js.map +0 -1
  103. package/dist/init-L55Q73H4.js.map +0 -1
  104. package/dist/open-LNRZL3UU.js.map +0 -1
  105. package/dist/rebase-C4WNCVGM.js.map +0 -1
  106. package/dist/run-IOGNIOYN.js.map +0 -1
  107. package/dist/terminal-BIRBZ4AZ.js +0 -16
  108. /package/dist/{BranchNamingService-OMWKUYMM.js.map → BranchNamingService-A77VI6AI.js.map} +0 -0
  109. /package/dist/{ClaudeContextManager-3VXA6UPR.js.map → ClaudeContextManager-BN7RE5ZQ.js.map} +0 -0
  110. /package/dist/{ClaudeService-6CPK43N4.js.map → ClaudeService-DLYLJUPA.js.map} +0 -0
  111. /package/dist/{GitHubService-EBOETDIW.js.map → GitHubService-FZHHBOFG.js.map} +0 -0
  112. /package/dist/{PromptTemplateManager-A52RUAMS.js.map → PromptTemplateManager-6HH3PVXV.js.map} +0 -0
  113. /package/dist/{SettingsManager-ZCWJ56WP.js.map → SettingsManager-I2LRCW2A.js.map} +0 -0
  114. /package/dist/{SettingsMigrationManager-AGIIIPDQ.js.map → SettingsMigrationManager-TJ7UWZG5.js.map} +0 -0
  115. /package/dist/{chunk-TSKY3JI7.js.map → chunk-2CXREBLZ.js.map} +0 -0
  116. /package/dist/{chunk-IXKLYTWO.js.map → chunk-2MAIX45J.js.map} +0 -0
  117. /package/dist/{chunk-XPKDPZ5D.js.map → chunk-AKUJXDNW.js.map} +0 -0
  118. /package/dist/{chunk-KLBYVHPK.js.map → chunk-OSCLCMDG.js.map} +0 -0
  119. /package/dist/{chunk-U5QDY7ZD.js.map → chunk-PGPI5LR4.js.map} +0 -0
  120. /package/dist/{chunk-INW24J2W.js.map → chunk-SUOXY5WJ.js.map} +0 -0
  121. /package/dist/{claude-LUZ35IMK.js.map → claude-W52VKI6L.js.map} +0 -0
  122. /package/dist/{color-ZVALX37U.js.map → color-F7RU6B6Z.js.map} +0 -0
  123. /package/dist/{git-34Z6QVDS.js.map → git-IYA53VIC.js.map} +0 -0
  124. /package/dist/{neon-helpers-WPUACUVC.js.map → init-4FHTAM3F.js.map} +0 -0
  125. /package/dist/{prompt-7INJ7YRU.js.map → neon-helpers-77PBPGJ5.js.map} +0 -0
  126. /package/dist/{terminal-BIRBZ4AZ.js.map → prompt-QALMYTVC.js.map} +0 -0
  127. /package/dist/{test-git-J7I5MFYH.js.map → test-git-IT5EWQ5C.js.map} +0 -0
  128. /package/dist/{test-prefix-ZCONBCBX.js.map → test-prefix-NPWDPUUH.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/PromptTemplateManager.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { logger } from '../utils/logger.js'\n\nexport interface TemplateVariables {\n\tISSUE_NUMBER?: string | number\n\tPR_NUMBER?: number\n\tISSUE_TITLE?: string\n\tPR_TITLE?: string\n\tWORKSPACE_PATH?: string\n\tPORT?: number\n\tONE_SHOT_MODE?: boolean\n\tSETTINGS_SCHEMA?: string\n\tSETTINGS_GLOBAL_JSON?: string\n\tSETTINGS_JSON?: string\n\tSETTINGS_LOCAL_JSON?: string\n\tSHELL_TYPE?: string\n\tSHELL_CONFIG_PATH?: string\n\tSHELL_CONFIG_CONTENT?: string\n\tREMOTES_INFO?: string\n\tMULTIPLE_REMOTES?: string\n\tSINGLE_REMOTE?: string\n\tSINGLE_REMOTE_NAME?: string\n\tSINGLE_REMOTE_URL?: string\n\tNO_REMOTES?: string\n\tREADME_CONTENT?: string\n\tSETTINGS_SCHEMA_CONTENT?: string\n\tFIRST_TIME_USER?: boolean\n}\n\nexport class PromptTemplateManager {\n\tprivate templateDir: string\n\n\tconstructor(templateDir?: string) {\n\t\tif (templateDir) {\n\t\t\tthis.templateDir = templateDir\n\t\t} else {\n\t\t\t// Find templates relative to the package installation\n\t\t\t// When running from dist/, templates are copied to dist/prompts/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath) // dist directory (may be chunked file location)\n\n\t\t\t// Walk up to find the dist directory (in case of chunked files)\n\t\t\tlet templateDir = path.join(distDir, 'prompts')\n\t\t\tlet currentDir = distDir\n\n\t\t\t// Try to find the prompts directory by walking up\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'prompts')\n\t\t\t\ttry {\n\t\t\t\t\t// Check if this directory exists (sync check for constructor)\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\ttemplateDir = candidatePath\n\t\t\t\t\tbreak\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.templateDir = templateDir\n\t\t\tlogger.debug('PromptTemplateManager initialized', {\n\t\t\t\tcurrentFilePath,\n\t\t\t\tdistDir,\n\t\t\t\ttemplateDir: this.templateDir\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Load a template file by name\n\t */\n\tasync loadTemplate(templateName: 'issue' | 'pr' | 'regular' | 'init'): Promise<string> {\n\t\tconst templatePath = path.join(this.templateDir, `${templateName}-prompt.txt`)\n\n\t\tlogger.debug('Loading template', {\n\t\t\ttemplateName,\n\t\t\ttemplateDir: this.templateDir,\n\t\t\ttemplatePath\n\t\t})\n\n\t\ttry {\n\t\t\treturn await readFile(templatePath, 'utf-8')\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to load template', { templateName, templatePath, error })\n\t\t\tthrow new Error(`Template not found: ${templatePath}`)\n\t\t}\n\t}\n\n\t/**\n\t * Substitute variables in a template string\n\t */\n\tsubstituteVariables(template: string, variables: TemplateVariables): string {\n\t\tlet result = template\n\n\t\t// Process conditional sections first\n\t\tresult = this.processConditionalSections(result, variables)\n\n\t\t// Replace each variable if it exists\n\t\tif (variables.ISSUE_NUMBER !== undefined) {\n\t\t\tresult = result.replace(/ISSUE_NUMBER/g, String(variables.ISSUE_NUMBER))\n\t\t}\n\n\t\tif (variables.PR_NUMBER !== undefined) {\n\t\t\tresult = result.replace(/PR_NUMBER/g, String(variables.PR_NUMBER))\n\t\t}\n\n\t\tif (variables.ISSUE_TITLE !== undefined) {\n\t\t\tresult = result.replace(/ISSUE_TITLE/g, variables.ISSUE_TITLE)\n\t\t}\n\n\t\tif (variables.PR_TITLE !== undefined) {\n\t\t\tresult = result.replace(/PR_TITLE/g, variables.PR_TITLE)\n\t\t}\n\n\t\tif (variables.WORKSPACE_PATH !== undefined) {\n\t\t\tresult = result.replace(/WORKSPACE_PATH/g, variables.WORKSPACE_PATH)\n\t\t}\n\n\t\tif (variables.PORT !== undefined) {\n\t\t\tresult = result.replace(/PORT/g, String(variables.PORT))\n\t\t}\n\n\t\tif (variables.SETTINGS_SCHEMA !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_SCHEMA/g, variables.SETTINGS_SCHEMA)\n\t\t}\n\n\t\tif (variables.SETTINGS_GLOBAL_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_GLOBAL_JSON/g, variables.SETTINGS_GLOBAL_JSON)\n\t\t}\n\n\t\tif (variables.SETTINGS_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_JSON/g, variables.SETTINGS_JSON)\n\t\t}\n\n\t\tif (variables.SETTINGS_LOCAL_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_LOCAL_JSON/g, variables.SETTINGS_LOCAL_JSON)\n\t\t}\n\n\t\tif (variables.SHELL_TYPE !== undefined) {\n\t\t\tresult = result.replace(/SHELL_TYPE/g, variables.SHELL_TYPE)\n\t\t}\n\n\t\tif (variables.SHELL_CONFIG_PATH !== undefined) {\n\t\t\tresult = result.replace(/SHELL_CONFIG_PATH/g, variables.SHELL_CONFIG_PATH)\n\t\t}\n\n\t\tif (variables.SHELL_CONFIG_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/SHELL_CONFIG_CONTENT/g, variables.SHELL_CONFIG_CONTENT)\n\t\t}\n\n\t\tif (variables.REMOTES_INFO !== undefined) {\n\t\t\tresult = result.replace(/REMOTES_INFO/g, variables.REMOTES_INFO)\n\t\t}\n\n\t\tif (variables.MULTIPLE_REMOTES !== undefined) {\n\t\t\tresult = result.replace(/MULTIPLE_REMOTES/g, variables.MULTIPLE_REMOTES)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE/g, variables.SINGLE_REMOTE)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE_NAME !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE_NAME/g, variables.SINGLE_REMOTE_NAME)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE_URL !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE_URL/g, variables.SINGLE_REMOTE_URL)\n\t\t}\n\n\t\tif (variables.NO_REMOTES !== undefined) {\n\t\t\tresult = result.replace(/NO_REMOTES/g, variables.NO_REMOTES)\n\t\t}\n\n\t\tif (variables.README_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/README_CONTENT/g, variables.README_CONTENT)\n\t\t}\n\n\t\tif (variables.SETTINGS_SCHEMA_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_SCHEMA_CONTENT/g, variables.SETTINGS_SCHEMA_CONTENT)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Process conditional sections in template\n\t * Format: {{#IF ONE_SHOT_MODE}}content{{/IF ONE_SHOT_MODE}}\n\t *\n\t * Note: /s flag allows . to match newlines\n\t */\n\tprivate processConditionalSections(template: string, variables: TemplateVariables): string {\n\t\tlet result = template\n\n\t\t// Process ONE_SHOT_MODE conditionals\n\t\tconst oneShotRegex = /\\{\\{#IF ONE_SHOT_MODE\\}\\}(.*?)\\{\\{\\/IF ONE_SHOT_MODE\\}\\}/gs\n\n\t\tif (variables.ONE_SHOT_MODE === true) {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(oneShotRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(oneShotRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_JSON conditionals\n\t\tconst settingsJsonRegex = /\\{\\{#IF SETTINGS_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_JSON !== undefined && variables.SETTINGS_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsJsonRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_GLOBAL_JSON conditionals\n\t\tconst settingsGlobalJsonRegex = /\\{\\{#IF SETTINGS_GLOBAL_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_GLOBAL_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_GLOBAL_JSON !== undefined && variables.SETTINGS_GLOBAL_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsGlobalJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsGlobalJsonRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_LOCAL_JSON conditionals\n\t\tconst settingsLocalJsonRegex = /\\{\\{#IF SETTINGS_LOCAL_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_LOCAL_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_LOCAL_JSON !== undefined && variables.SETTINGS_LOCAL_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsLocalJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsLocalJsonRegex, '')\n\t\t}\n\n\t\t// Process MULTIPLE_REMOTES conditionals\n\t\tconst multipleRemotesRegex = /\\{\\{#IF MULTIPLE_REMOTES\\}\\}(.*?)\\{\\{\\/IF MULTIPLE_REMOTES\\}\\}/gs\n\n\t\tif (variables.MULTIPLE_REMOTES !== undefined && variables.MULTIPLE_REMOTES !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(multipleRemotesRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(multipleRemotesRegex, '')\n\t\t}\n\n\t\t// Process SINGLE_REMOTE conditionals\n\t\tconst singleRemoteRegex = /\\{\\{#IF SINGLE_REMOTE\\}\\}(.*?)\\{\\{\\/IF SINGLE_REMOTE\\}\\}/gs\n\n\t\tif (variables.SINGLE_REMOTE !== undefined && variables.SINGLE_REMOTE !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(singleRemoteRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(singleRemoteRegex, '')\n\t\t}\n\n\t\t// Process NO_REMOTES conditionals\n\t\tconst noRemotesRegex = /\\{\\{#IF NO_REMOTES\\}\\}(.*?)\\{\\{\\/IF NO_REMOTES\\}\\}/gs\n\n\t\tif (variables.NO_REMOTES !== undefined && variables.NO_REMOTES !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(noRemotesRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(noRemotesRegex, '')\n\t\t}\n\n\t\t// Process FIRST_TIME_USER conditionals\n\t\tconst firstTimeUserRegex = /\\{\\{#IF FIRST_TIME_USER\\}\\}(.*?)\\{\\{\\/IF FIRST_TIME_USER\\}\\}/gs\n\n\t\tif (variables.FIRST_TIME_USER === true) {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(firstTimeUserRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(firstTimeUserRegex, '')\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Get a fully processed prompt for a workflow type\n\t */\n\tasync getPrompt(\n\t\ttype: 'issue' | 'pr' | 'regular' | 'init',\n\t\tvariables: TemplateVariables\n\t): Promise<string> {\n\t\tconst template = await this.loadTemplate(type)\n\t\treturn this.substituteVariables(template, variables)\n\t}\n}\n"],"mappings":"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","names":["templateDir"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/IssueEnhancementService.ts"],"sourcesContent":["import type { IssueTracker } from './IssueTracker.js'\nimport type { AgentManager } from './AgentManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport { launchClaude } from '../utils/claude.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { waitForKeypress } from '../utils/prompt.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Service for enhancing and creating GitHub issues with AI assistance.\n * Extracts reusable issue enhancement logic from StartCommand.\n */\nexport class IssueEnhancementService {\n\tconstructor(\n\t\tprivate gitHubService: IssueTracker,\n\t\tprivate agentManager: AgentManager,\n\t\tprivate settingsManager: SettingsManager\n\t) {}\n\n\t/**\n\t * Validates that a description meets minimum requirements.\n\t * Requirements: >30 characters AND >2 spaces\n\t */\n\tpublic validateDescription(description: string): boolean {\n\t\tconst trimmedDescription = description.trim()\n\t\tconst spaceCount = (trimmedDescription.match(/ /g) ?? []).length\n\n\t\treturn trimmedDescription.length > 30 && spaceCount > 2\n\t}\n\n\t/**\n\t * Enhances a description using Claude Code in headless mode.\n\t * Falls back to original description if enhancement fails.\n\t */\n\tpublic async enhanceDescription(description: string): Promise<string> {\n\t\ttry {\n\t\t\tlogger.info('Enhancing description with Claude Code. This may take a moment...')\n\n\t\t\t// Load agent configurations\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst loadedAgents = await this.agentManager.loadAgents(settings)\n\t\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t\t// Call Claude in headless mode with issue enhancer agent\n\t\t\tconst prompt = `@agent-iloom-issue-enhancer\n\nTASK: Enhance the following issue description for GitHub.\n\nINPUT:\n${description}\n\nOUTPUT REQUIREMENTS:\n- Return ONLY the enhanced description markdown text\n- NO meta-commentary (no \"Here is...\", \"The enhanced...\", \"I have...\", etc)\n- NO code block markers (\\`\\`\\`)\n- NO conversational framing or acknowledgments\n- NO explanations of your work\n- Start your response immediately with the enhanced content\n\nYour response should be the raw markdown that will become the GitHub issue body.`\n\n\t\t\tconst enhanced = await launchClaude(prompt, {\n\t\t\t\theadless: true,\n\t\t\t\tmodel: 'sonnet',\n\t\t\t\tagents,\n\t\t\t})\n\n\t\t\tif (enhanced && typeof enhanced === 'string') {\n\t\t\t\tlogger.success('Description enhanced successfully')\n\t\t\t\treturn enhanced\n\t\t\t}\n\n\t\t\t// Fallback to original description\n\t\t\tlogger.warn('Claude enhancement returned empty result, using original description')\n\t\t\treturn description\n\t\t} catch (error) {\n\t\t\tlogger.warn(`Failed to enhance description: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn description\n\t\t}\n\t}\n\n\t/**\n\t * Creates a GitHub issue with title and enhanced body.\n\t * @param originalDescription - Used as the issue title\n\t * @param enhancedDescription - Used as the issue body\n\t * @param repository - Optional repository override (format: \"owner/repo\")\n\t * @param labels - Optional array of label names to add to the issue\n\t * @returns Issue number and URL\n\t */\n\tpublic async createEnhancedIssue(\n\t\toriginalDescription: string,\n\t\tenhancedDescription: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\tlogger.info('Creating GitHub issue from description...')\n\n\t\tconst result = await this.gitHubService.createIssue(\n\t\t\toriginalDescription, // Use original description as title\n\t\t\tenhancedDescription, // Use enhanced description as body\n\t\t\trepository,\n\t\t\tlabels\n\t\t)\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Waits for user keypress and opens issue in browser for review.\n\t * @param issueNumber - Issue number to open for review\n\t * @param confirm - If true, wait for additional keypress after opening browser before returning\n\t * @param repository - Optional repository to fetch issue from (format: \"owner/repo\")\n\t */\n\tpublic async waitForReviewAndOpen(issueNumber: string | number, confirm = false, repository?: string): Promise<void> {\n\t\t// Check if running in CI environment\n\t\tconst isCI = process.env.CI === 'true'\n\n\t\tif (isCI) {\n\t\t\t// In CI: Skip all interactive operations\n\t\t\tlogger.info(`Running in CI environment - skipping interactive prompts for issue #${issueNumber}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Get issue URL\n\t\tconst issueUrl = await this.gitHubService.getIssueUrl(issueNumber, repository)\n\n\t\t// Display message and wait for first keypress\n\t\tconst message = `Created issue #${issueNumber}.\nReview and edit the issue in your browser if needed.\nPress any key to open issue for editing...`\n\t\tawait waitForKeypress(message)\n\n\t\t// Open issue in browser\n\t\tawait openBrowser(issueUrl)\n\n\t\t// If confirmation required, wait for second keypress\n\t\tif (confirm) {\n\t\t\tawait waitForKeypress('Press any key to continue with loom creation...')\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAYO,IAAM,0BAAN,MAA8B;AAAA,EACpC,YACS,eACA,cACA,iBACP;AAHO;AACA;AACA;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMI,oBAAoB,aAA8B;AACxD,UAAM,qBAAqB,YAAY,KAAK;AAC5C,UAAM,cAAc,mBAAmB,MAAM,IAAI,KAAK,CAAC,GAAG;AAE1D,WAAO,mBAAmB,SAAS,MAAM,aAAa;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,mBAAmB,aAAsC;AACrE,QAAI;AACH,aAAO,KAAK,mEAAmE;AAG/E,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa;AACzD,YAAM,eAAe,MAAM,KAAK,aAAa,WAAW,QAAQ;AAChE,YAAM,SAAS,KAAK,aAAa,aAAa,YAAY;AAG1D,YAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYV,YAAM,WAAW,MAAM,aAAa,QAAQ;AAAA,QAC3C,UAAU;AAAA,QACV,OAAO;AAAA,QACP;AAAA,MACD,CAAC;AAED,UAAI,YAAY,OAAO,aAAa,UAAU;AAC7C,eAAO,QAAQ,mCAAmC;AAClD,eAAO;AAAA,MACR;AAGA,aAAO,KAAK,sEAAsE;AAClF,aAAO;AAAA,IACR,SAAS,OAAO;AACf,aAAO,KAAK,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AACxG,aAAO;AAAA,IACR;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,oBACZ,qBACA,qBACA,YACA,QACoD;AACpD,WAAO,KAAK,2CAA2C;AAEvD,UAAM,SAAS,MAAM,KAAK,cAAc;AAAA,MACvC;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,qBAAqB,aAA8B,UAAU,OAAO,YAAoC;AAEpH,UAAM,OAAO,QAAQ,IAAI,OAAO;AAEhC,QAAI,MAAM;AAET,aAAO,KAAK,uEAAuE,WAAW,EAAE;AAChG;AAAA,IACD;AAGA,UAAM,WAAW,MAAM,KAAK,cAAc,YAAY,aAAa,UAAU;AAG7E,UAAM,UAAU,kBAAkB,WAAW;AAAA;AAAA;AAG7C,UAAM,gBAAgB,OAAO;AAG7B,UAAM,YAAY,QAAQ;AAG1B,QAAI,SAAS;AACZ,YAAM,gBAAgB,iDAAiD;AAAA,IACxE;AAAA,EACD;AACD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/terminal.ts"],"sourcesContent":["import { execa } from 'execa'\nimport { existsSync } from 'node:fs'\nimport type { Platform } from '../types/index.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 * 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? buildITerm2SingleTabScript(options)\n\t\t: 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 */\nfunction buildAppleScript(options: TerminalWindowOptions): 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 .env file\n\tif (includeEnvSetup) {\n\t\tcommands.push('source .env')\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 */\nfunction buildITerm2SingleTabScript(options: TerminalWindowOptions): string {\n\tconst command = 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 */\nfunction buildCommandSequence(options: TerminalWindowOptions): 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 .env file\n\tif (includeEnvSetup) {\n\t\tcommands.push('source .env')\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 */\nfunction buildITerm2MultiTabScript(\n\toptionsArray: TerminalWindowOptions[]\n): 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 = 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 = 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 = 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"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/claude.ts"],"sourcesContent":["import { execa } from 'execa'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { logger } from './logger.js'\n\nexport interface ClaudeCliOptions {\n\tmodel?: string\n\tpermissionMode?: 'plan' | 'acceptEdits' | 'bypassPermissions' | 'default'\n\taddDir?: string\n\theadless?: boolean\n\tbranchName?: string // Optional branch name for terminal coloring\n\tport?: number // Optional port for terminal window export\n\ttimeout?: number // Timeout in milliseconds\n\tappendSystemPrompt?: string // System instructions to append to system prompt\n\tmcpConfig?: Record<string, unknown>[] // Array of MCP server configurations\n\tallowedTools?: string[] // Tools to allow via --allowed-tools flag\n\tdisallowedTools?: string[] // Tools to disallow via --disallowed-tools flag\n\tagents?: Record<string, unknown> // Agent configurations for --agents flag\n\toneShot?: import('../types/index.js').OneShotMode // One-shot automation mode\n\tsetArguments?: string[] // Raw --set arguments to forward (e.g., ['workflows.issue.startIde=false'])\n\texecutablePath?: string // Executable path to use for spin command (e.g., 'il', 'il-125', or '/path/to/dist/cli.js')\n}\n\n/**\n * Detect if Claude CLI is available on the system\n */\nexport async function detectClaudeCli(): Promise<boolean> {\n\ttry {\n\t\t// Use 'command -v' for cross-platform compatibility (works on macOS/Linux)\n\t\tawait execa('command', ['-v', 'claude'], {\n\t\t\tshell: true,\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn true\n\t} catch (error) {\n\t\t// Claude CLI not found\n\t\tlogger.debug('Claude CLI not available', { error })\n\t\treturn false\n\t}\n}\n\n/**\n * Get Claude CLI version\n */\nexport async function getClaudeVersion(): Promise<string | null> {\n\ttry {\n\t\tconst result = await execa('claude', ['--version'], {\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn result.stdout.trim()\n\t} catch (error) {\n\t\tlogger.warn('Failed to get Claude version', { error })\n\t\treturn null\n\t}\n}\n\n/**\n * Parse JSON stream output and extract result from last JSON object with type:\"result\"\n */\nfunction parseJsonStreamOutput(output: string): string {\n\ttry {\n\t\t// Split by newlines and filter out empty lines\n\t\tconst lines = output.split('\\n').filter(line => line.trim())\n\n\t\t// Find the last valid JSON object with type:\"result\"\n\t\tlet lastResult = ''\n\t\tfor (const line of lines) {\n\t\t\ttry {\n\t\t\t\tconst jsonObj = JSON.parse(line)\n\t\t\t\tif (jsonObj && typeof jsonObj === 'object' && jsonObj.type === 'result' && 'result' in jsonObj) {\n\t\t\t\t\tlastResult = jsonObj.result\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Skip invalid JSON lines\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\treturn lastResult || output // Fallback to original output if no valid result found\n\t} catch {\n\t\t// If parsing fails completely, return original output\n\t\treturn output\n\t}\n}\n\n/**\n * Launch Claude CLI with specified options\n * In headless mode, returns stdout. In interactive mode, returns void.\n */\nexport async function launchClaude(\n\tprompt: string,\n\toptions: ClaudeCliOptions = {}\n): Promise<string | void> {\n\tconst { model, permissionMode, addDir, headless = false, appendSystemPrompt, mcpConfig, allowedTools, disallowedTools, agents } = options\n\n\t// Build command arguments\n\tconst args: string[] = []\n\n\tif (headless) {\n\t\targs.push('-p')\n\n\t\t// Add JSON streaming output for progress tracking\n\t\targs.push('--output-format', 'stream-json')\n\t\targs.push('--verbose')\n\t}\n\n\tif (model) {\n\t\targs.push('--model', model)\n\t}\n\n\tif (permissionMode && permissionMode !== 'default') {\n\t\targs.push('--permission-mode', permissionMode)\n\t}\n\n\tif (addDir) {\n\t\targs.push('--add-dir', addDir)\n\t}\n\n\targs.push('--add-dir', '/tmp') //TODO: Won't work on Windows\n\n\t// Add --append-system-prompt flag if provided\n\tif (appendSystemPrompt) {\n\t\targs.push('--append-system-prompt', appendSystemPrompt)\n\t}\n\n\t// Add --mcp-config flags for each MCP server configuration\n\tif (mcpConfig && mcpConfig.length > 0) {\n\t\tfor (const config of mcpConfig) {\n\t\t\targs.push('--mcp-config', JSON.stringify(config))\n\t\t}\n\t}\n\n\t// Add --allowed-tools flags if provided\n\tif (allowedTools && allowedTools.length > 0) {\n\t\targs.push('--allowed-tools', ...allowedTools)\n\t}\n\n\t// Add --disallowed-tools flags if provided\n\tif (disallowedTools && disallowedTools.length > 0) {\n\t\targs.push('--disallowed-tools', ...disallowedTools)\n\t}\n\n\t// Add --agents flag if provided\n\tif (agents) {\n\t\targs.push('--agents', JSON.stringify(agents))\n\t}\n\n\ttry {\n\t\tif (headless) {\n\t\t\t// Headless mode: capture and return output\n\t\t\tconst isDebugMode = logger.isDebugEnabled()\n\n\t\t\t// Set up execa options based on debug mode\n\t\t\tconst execaOptions = {\n\t\t\t\tinput: prompt,\n\t\t\t\ttimeout: 0, // Disable timeout for long responses\n\t\t\t\t...(addDir && { cwd: addDir }), // Run Claude in the worktree directory\n\t\t\t\tverbose: isDebugMode,\n\t\t\t\t...(isDebugMode && { stdio: ['pipe', 'pipe', 'pipe'] as const }), // Enable streaming in debug mode\n\t\t\t}\n\n\t\t\tconst subprocess = execa('claude', args, execaOptions)\n\n\t\t\t// Check if JSON streaming format is enabled (always true in headless mode)\n\t\t\tconst isJsonStreamFormat = args.includes('--output-format') && args.includes('stream-json')\n\n\t\t\t// Handle real-time streaming (enabled for progress tracking)\n\t\t\tlet outputBuffer = ''\n\t\t\tlet isStreaming = false\n\t\t\tlet isFirstProgress = true\n\t\t\tif (subprocess.stdout && typeof subprocess.stdout.on === 'function') {\n\t\t\t\tisStreaming = true\n\t\t\t\tsubprocess.stdout.on('data', (chunk: Buffer) => {\n\t\t\t\t\tconst text = chunk.toString()\n\t\t\t\t\toutputBuffer += text\n\n\t\t\t\t\tif (isDebugMode) {\n\t\t\t\t\t\tprocess.stdout.write(text) // Full JSON streaming in debug mode\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Progress dots in non-debug mode with robot emoji prefix\n\t\t\t\t\t\tif (isFirstProgress) {\n\t\t\t\t\t\t\tprocess.stdout.write('🤖 .')\n\t\t\t\t\t\t\tisFirstProgress = false\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tprocess.stdout.write('.')\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst result = await subprocess\n\n\t\t\t// Return streamed output if we were streaming, otherwise use result.stdout\n\t\t\tif (isStreaming) {\n\t\t\t\tconst rawOutput = outputBuffer.trim()\n\n\t\t\t\t// Clean up progress dots with newline in non-debug mode\n\t\t\t\tif (!isDebugMode) {\n\t\t\t\t\tprocess.stdout.write('\\n')\n\t\t\t\t}\n\n\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t} else {\n\t\t\t\t// Fallback for mocked tests or when streaming not available\n\t\t\t\tif (isDebugMode) {\n\t\t\t\t\t// In debug mode, write to stdout even if not streaming (old behavior for tests)\n\t\t\t\t\tprocess.stdout.write(result.stdout)\n\t\t\t\t\tif (result.stdout && !result.stdout.endsWith('\\n')) {\n\t\t\t\t\t\tprocess.stdout.write('\\n')\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// In non-debug mode, show a single progress dot even without streaming (for tests)\n\t\t\t\t\tprocess.stdout.write('🤖 .')\n\t\t\t\t\tprocess.stdout.write('\\n')\n\t\t\t\t}\n\t\t\t\tconst rawOutput = result.stdout.trim()\n\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t}\n\t\t} else {\n\t\t\t// Simple interactive mode: run Claude in current terminal with stdio inherit\n\t\t\t// Used for conflict resolution, error fixing, etc.\n\t\t\t// This is the simple approach: claude -- \"prompt\"\n\n\t\t\t// Execute in current terminal (blocking, inherits stdio)\n\t\t\tawait execa('claude', [...args, '--', prompt], {\n\t\t\t\t...(addDir && { cwd: addDir }),\n\t\t\t\tstdio: 'inherit', // Let user interact directly in current terminal\n\t\t\t\ttimeout: 0, // Disable timeout\n\t\t\t\tverbose: logger.isDebugEnabled(),\n\t\t\t})\n\n\t\t\treturn\n\t\t}\n\t} catch (error) {\n\t\t// Check for specific Claude CLI errors\n\t\tconst execaError = error as {\n\t\t\tstderr?: string\n\t\t\tmessage?: string\n\t\t\texitCode?: number\n\t\t}\n\n\t\t// Re-throw with more context\n\t\tconst errorMessage = execaError.stderr ?? execaError.message ?? 'Unknown Claude CLI error'\n\t\tthrow new Error(`Claude CLI error: ${errorMessage}`)\n\t}\n}\n\n/**\n * Launch Claude in a new terminal window with rich context\n * This is specifically for \"end of il start\" workflow\n * Ports the terminal window opening, coloring, and .env sourcing behavior\n */\nexport async function launchClaudeInNewTerminalWindow(\n\t_prompt: string,\n\toptions: ClaudeCliOptions & {\n\t\tworkspacePath: string // Required for terminal window launch\n\t}\n): Promise<void> {\n\tconst { workspacePath, branchName, oneShot = 'default', port, setArguments, executablePath } = options\n\n\t// Verify required parameter\n\tif (!workspacePath) {\n\t\tthrow new Error('workspacePath is required for terminal window launch')\n\t}\n\n\t// Import terminal launcher for new terminal window creation\n\tconst { openTerminalWindow } = await import('./terminal.js')\n\n\t// Build launch command with optional --one-shot flag\n\t// Use provided executable path or fallback to 'il'\n\tconst executable = executablePath ?? 'iloom'\n\tlet launchCommand = `${executable} spin`\n\tif (oneShot !== 'default') {\n\t\tlaunchCommand += ` --one-shot=${oneShot}`\n\t}\n\n\t// Append --set arguments if provided\n\tif (setArguments && setArguments.length > 0) {\n\t\tfor (const setArg of setArguments) {\n\t\t\tlaunchCommand += ` --set ${setArg}`\n\t\t}\n\t}\n\n\t// Apply terminal background color if branch name available\n\tlet backgroundColor: { r: number; g: number; b: number } | undefined\n\tif (branchName) {\n\t\ttry {\n\t\t\tconst { generateColorFromBranchName } = await import('./color.js')\n\t\t\tconst colorData = generateColorFromBranchName(branchName)\n\t\t\tbackgroundColor = colorData.rgb\n\t\t} catch (error) {\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to generate terminal color: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t// Check if .env file exists in workspace\n\tconst hasEnvFile = existsSync(join(workspacePath, '.env'))\n\n\t// Open new terminal window with Claude\n\tawait openTerminalWindow({\n\t\tworkspacePath,\n\t\tcommand: launchCommand,\n\t\t...(backgroundColor && { backgroundColor }),\n\t\tincludeEnvSetup: hasEnvFile, // source .env only if it exists\n\t\t...(port !== undefined && { port, includePortExport: true }),\n\t})\n}\n\n/**\n * Generate a branch name using Claude with fallback\n * This matches the implementation that was working in ClaudeBranchNameStrategy\n */\nexport async function generateBranchName(\n\tissueTitle: string,\n\tissueNumber: string | number,\n\tmodel: string = 'haiku'\n): Promise<string> {\n\ttry {\n\t\t// Check if Claude CLI is available\n\t\tconst isAvailable = await detectClaudeCli()\n\t\tif (!isAvailable) {\n\t\t\tlogger.warn('Claude CLI not available, using fallback branch name')\n\t\t\treturn `feat/issue-${issueNumber}`\n\t\t}\n\n\t\tlogger.debug('Generating branch name with Claude', { issueNumber, issueTitle })\n\n\t\t// Use the proven prompt format from ClaudeBranchNameStrategy\n\t\tconst prompt = `<Task>\nGenerate a git branch name for the following issue:\n<Issue>\n<IssueNumber>${issueNumber}</IssueNumber>\n<IssueTitle>${issueTitle}</IssueTitle>\n</Issue>\n\n<Requirements>\n<IssueNumber>Must use this exact issue number: ${issueNumber}</IssueNumber>\n<Format>Format must be: {prefix}/issue-${issueNumber}__{description}</Format>\n<Prefix>Prefix must be one of: feat, fix, docs, refactor, test, chore</Prefix>\n<MaxLength>Maximum 50 characters total</MaxLength>\n<Characters>Only lowercase letters, numbers, and hyphens allowed</Characters>\n<Output>Reply with ONLY the branch name, nothing else</Output>\n</Requirements>\n</Task>`\n\n\t\tlogger.debug('Sending prompt to Claude', { prompt })\n\n\t\tconst result = (await launchClaude(prompt, {\n\t\t\tmodel,\n\t\t\theadless: true,\n\t\t})) as string\n\n\t\t// Normalize to lowercase for consistency (Linear IDs are uppercase but branches should be lowercase)\n\t\tconst branchName = result.trim().toLowerCase()\n\t\tlogger.debug('Claude returned branch name', { branchName, issueNumber })\n\n\t\t// Validate generated name using same validation as ClaudeBranchNameStrategy\n\t\tif (!branchName || !isValidBranchName(branchName, issueNumber)) {\n\t\t\tlogger.warn('Invalid branch name from Claude, using fallback', { branchName })\n\t\t\treturn `feat/issue-${issueNumber}`.toLowerCase()\n\t\t}\n\n\t\treturn branchName\n\t} catch (error) {\n\t\tlogger.warn('Failed to generate branch name with Claude', { error })\n\t\treturn `feat/issue-${issueNumber}`.toLowerCase()\n\t}\n}\n\n/**\n * Validate branch name format\n * Check format: {prefix}/issue-{number}__{description}\n * Uses case-insensitive matching for issue number (Linear uses uppercase like MARK-1)\n */\nfunction isValidBranchName(name: string, issueNumber: string | number): boolean {\n\tconst pattern = new RegExp(`^(feat|fix|docs|refactor|test|chore)/issue-${issueNumber}__[a-z0-9-]+$`, 'i')\n\treturn pattern.test(name) && name.length <= 50\n}\n"],"mappings":";;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAwBrB,eAAsB,kBAAoC;AACzD,MAAI;AAEH,UAAM,MAAM,WAAW,CAAC,MAAM,QAAQ,GAAG;AAAA,MACxC,OAAO;AAAA,MACP,SAAS;AAAA,IACV,CAAC;AACD,WAAO;AAAA,EACR,SAAS,OAAO;AAEf,WAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC;AAClD,WAAO;AAAA,EACR;AACD;AAKA,eAAsB,mBAA2C;AAChE,MAAI;AACH,UAAM,SAAS,MAAM,MAAM,UAAU,CAAC,WAAW,GAAG;AAAA,MACnD,SAAS;AAAA,IACV,CAAC;AACD,WAAO,OAAO,OAAO,KAAK;AAAA,EAC3B,SAAS,OAAO;AACf,WAAO,KAAK,gCAAgC,EAAE,MAAM,CAAC;AACrD,WAAO;AAAA,EACR;AACD;AAKA,SAAS,sBAAsB,QAAwB;AACtD,MAAI;AAEH,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,UAAQ,KAAK,KAAK,CAAC;AAG3D,QAAI,aAAa;AACjB,eAAW,QAAQ,OAAO;AACzB,UAAI;AACH,cAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,YAAI,WAAW,OAAO,YAAY,YAAY,QAAQ,SAAS,YAAY,YAAY,SAAS;AAC/F,uBAAa,QAAQ;AAAA,QACtB;AAAA,MACD,QAAQ;AAEP;AAAA,MACD;AAAA,IACD;AAEA,WAAO,cAAc;AAAA,EACtB,QAAQ;AAEP,WAAO;AAAA,EACR;AACD;AAMA,eAAsB,aACrB,QACA,UAA4B,CAAC,GACJ;AACzB,QAAM,EAAE,OAAO,gBAAgB,QAAQ,WAAW,OAAO,oBAAoB,WAAW,cAAc,iBAAiB,OAAO,IAAI;AAGlI,QAAM,OAAiB,CAAC;AAExB,MAAI,UAAU;AACb,SAAK,KAAK,IAAI;AAGd,SAAK,KAAK,mBAAmB,aAAa;AAC1C,SAAK,KAAK,WAAW;AAAA,EACtB;AAEA,MAAI,OAAO;AACV,SAAK,KAAK,WAAW,KAAK;AAAA,EAC3B;AAEA,MAAI,kBAAkB,mBAAmB,WAAW;AACnD,SAAK,KAAK,qBAAqB,cAAc;AAAA,EAC9C;AAEA,MAAI,QAAQ;AACX,SAAK,KAAK,aAAa,MAAM;AAAA,EAC9B;AAEA,OAAK,KAAK,aAAa,MAAM;AAG7B,MAAI,oBAAoB;AACvB,SAAK,KAAK,0BAA0B,kBAAkB;AAAA,EACvD;AAGA,MAAI,aAAa,UAAU,SAAS,GAAG;AACtC,eAAW,UAAU,WAAW;AAC/B,WAAK,KAAK,gBAAgB,KAAK,UAAU,MAAM,CAAC;AAAA,IACjD;AAAA,EACD;AAGA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC5C,SAAK,KAAK,mBAAmB,GAAG,YAAY;AAAA,EAC7C;AAGA,MAAI,mBAAmB,gBAAgB,SAAS,GAAG;AAClD,SAAK,KAAK,sBAAsB,GAAG,eAAe;AAAA,EACnD;AAGA,MAAI,QAAQ;AACX,SAAK,KAAK,YAAY,KAAK,UAAU,MAAM,CAAC;AAAA,EAC7C;AAEA,MAAI;AACH,QAAI,UAAU;AAEb,YAAM,cAAc,OAAO,eAAe;AAG1C,YAAM,eAAe;AAAA,QACpB,OAAO;AAAA,QACP,SAAS;AAAA;AAAA,QACT,GAAI,UAAU,EAAE,KAAK,OAAO;AAAA;AAAA,QAC5B,SAAS;AAAA,QACT,GAAI,eAAe,EAAE,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAW;AAAA;AAAA,MAC/D;AAEA,YAAM,aAAa,MAAM,UAAU,MAAM,YAAY;AAGrD,YAAM,qBAAqB,KAAK,SAAS,iBAAiB,KAAK,KAAK,SAAS,aAAa;AAG1F,UAAI,eAAe;AACnB,UAAI,cAAc;AAClB,UAAI,kBAAkB;AACtB,UAAI,WAAW,UAAU,OAAO,WAAW,OAAO,OAAO,YAAY;AACpE,sBAAc;AACd,mBAAW,OAAO,GAAG,QAAQ,CAAC,UAAkB;AAC/C,gBAAM,OAAO,MAAM,SAAS;AAC5B,0BAAgB;AAEhB,cAAI,aAAa;AAChB,oBAAQ,OAAO,MAAM,IAAI;AAAA,UAC1B,OAAO;AAEN,gBAAI,iBAAiB;AACpB,sBAAQ,OAAO,MAAM,aAAM;AAC3B,gCAAkB;AAAA,YACnB,OAAO;AACN,sBAAQ,OAAO,MAAM,GAAG;AAAA,YACzB;AAAA,UACD;AAAA,QACD,CAAC;AAAA,MACF;AAEA,YAAM,SAAS,MAAM;AAGrB,UAAI,aAAa;AAChB,cAAM,YAAY,aAAa,KAAK;AAGpC,YAAI,CAAC,aAAa;AACjB,kBAAQ,OAAO,MAAM,IAAI;AAAA,QAC1B;AAEA,eAAO,qBAAqB,sBAAsB,SAAS,IAAI;AAAA,MAChE,OAAO;AAEN,YAAI,aAAa;AAEhB,kBAAQ,OAAO,MAAM,OAAO,MAAM;AAClC,cAAI,OAAO,UAAU,CAAC,OAAO,OAAO,SAAS,IAAI,GAAG;AACnD,oBAAQ,OAAO,MAAM,IAAI;AAAA,UAC1B;AAAA,QACD,OAAO;AAEN,kBAAQ,OAAO,MAAM,aAAM;AAC3B,kBAAQ,OAAO,MAAM,IAAI;AAAA,QAC1B;AACA,cAAM,YAAY,OAAO,OAAO,KAAK;AACrC,eAAO,qBAAqB,sBAAsB,SAAS,IAAI;AAAA,MAChE;AAAA,IACD,OAAO;AAMN,YAAM,MAAM,UAAU,CAAC,GAAG,MAAM,MAAM,MAAM,GAAG;AAAA,QAC9C,GAAI,UAAU,EAAE,KAAK,OAAO;AAAA,QAC5B,OAAO;AAAA;AAAA,QACP,SAAS;AAAA;AAAA,QACT,SAAS,OAAO,eAAe;AAAA,MAChC,CAAC;AAED;AAAA,IACD;AAAA,EACD,SAAS,OAAO;AAEf,UAAM,aAAa;AAOnB,UAAM,eAAe,WAAW,UAAU,WAAW,WAAW;AAChE,UAAM,IAAI,MAAM,qBAAqB,YAAY,EAAE;AAAA,EACpD;AACD;AAOA,eAAsB,gCACrB,SACA,SAGgB;AAChB,QAAM,EAAE,eAAe,YAAY,UAAU,WAAW,MAAM,cAAc,eAAe,IAAI;AAG/F,MAAI,CAAC,eAAe;AACnB,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACvE;AAGA,QAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,wBAAe;AAI3D,QAAM,aAAa,kBAAkB;AACrC,MAAI,gBAAgB,GAAG,UAAU;AACjC,MAAI,YAAY,WAAW;AAC1B,qBAAiB,eAAe,OAAO;AAAA,EACxC;AAGA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC5C,eAAW,UAAU,cAAc;AAClC,uBAAiB,UAAU,MAAM;AAAA,IAClC;AAAA,EACD;AAGA,MAAI;AACJ,MAAI,YAAY;AACf,QAAI;AACH,YAAM,EAAE,4BAA4B,IAAI,MAAM,OAAO,qBAAY;AACjE,YAAM,YAAY,4BAA4B,UAAU;AACxD,wBAAkB,UAAU;AAAA,IAC7B,SAAS,OAAO;AACf,aAAO;AAAA,QACN,sCAAsC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MAC/F;AAAA,IACD;AAAA,EACD;AAGA,QAAM,aAAa,WAAW,KAAK,eAAe,MAAM,CAAC;AAGzD,QAAM,mBAAmB;AAAA,IACxB;AAAA,IACA,SAAS;AAAA,IACT,GAAI,mBAAmB,EAAE,gBAAgB;AAAA,IACzC,iBAAiB;AAAA;AAAA,IACjB,GAAI,SAAS,UAAa,EAAE,MAAM,mBAAmB,KAAK;AAAA,EAC3D,CAAC;AACF;AAMA,eAAsB,mBACrB,YACA,aACA,QAAgB,SACE;AAClB,MAAI;AAEH,UAAM,cAAc,MAAM,gBAAgB;AAC1C,QAAI,CAAC,aAAa;AACjB,aAAO,KAAK,sDAAsD;AAClE,aAAO,cAAc,WAAW;AAAA,IACjC;AAEA,WAAO,MAAM,sCAAsC,EAAE,aAAa,WAAW,CAAC;AAG9E,UAAM,SAAS;AAAA;AAAA;AAAA,eAGF,WAAW;AAAA,cACZ,UAAU;AAAA;AAAA;AAAA;AAAA,iDAIyB,WAAW;AAAA,yCACnB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQlD,WAAO,MAAM,4BAA4B,EAAE,OAAO,CAAC;AAEnD,UAAM,SAAU,MAAM,aAAa,QAAQ;AAAA,MAC1C;AAAA,MACA,UAAU;AAAA,IACX,CAAC;AAGD,UAAM,aAAa,OAAO,KAAK,EAAE,YAAY;AAC7C,WAAO,MAAM,+BAA+B,EAAE,YAAY,YAAY,CAAC;AAGvE,QAAI,CAAC,cAAc,CAAC,kBAAkB,YAAY,WAAW,GAAG;AAC/D,aAAO,KAAK,mDAAmD,EAAE,WAAW,CAAC;AAC7E,aAAO,cAAc,WAAW,GAAG,YAAY;AAAA,IAChD;AAEA,WAAO;AAAA,EACR,SAAS,OAAO;AACf,WAAO,KAAK,8CAA8C,EAAE,MAAM,CAAC;AACnE,WAAO,cAAc,WAAW,GAAG,YAAY;AAAA,EAChD;AACD;AAOA,SAAS,kBAAkB,MAAc,aAAuC;AAC/E,QAAM,UAAU,IAAI,OAAO,8CAA8C,WAAW,iBAAiB,GAAG;AACxG,SAAO,QAAQ,KAAK,IAAI,KAAK,KAAK,UAAU;AAC7C;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/color.ts"],"sourcesContent":["import { createHash } from 'crypto'\nimport logger from './logger'\n\n/**\n * RGB color representation\n */\nexport interface RgbColor {\n\tr: number\n\tg: number\n\tb: number\n}\n\n/**\n * Complete color data with RGB, hex, and palette index\n */\nexport interface ColorData {\n\trgb: RgbColor\n\thex: string\n\tindex: number\n}\n\n/**\n * Get the predefined color palette (40 subtle, professional colors)\n * Matches the terminal color palette from bash/new-branch-workflow.sh\n *\n * @returns Array of 40 RGB colors\n */\nexport function getColorPalette(): RgbColor[] {\n\treturn [\n\t\t// First 10 colors preserved for backward compatibility\n\t\t{ r: 220, g: 235, b: 248 }, // 0: Soft blue\n\t\t{ r: 248, g: 220, b: 235 }, // 1: Soft pink\n\t\t{ r: 220, g: 248, b: 235 }, // 2: Soft green\n\t\t{ r: 248, g: 240, b: 220 }, // 3: Soft cream\n\t\t{ r: 240, g: 220, b: 248 }, // 4: Soft lavender\n\t\t{ r: 220, g: 240, b: 248 }, // 5: Soft cyan\n\t\t{ r: 235, g: 235, b: 235 }, // 6: Soft grey\n\t\t{ r: 228, g: 238, b: 248 }, // 7: Soft ice blue\n\t\t{ r: 248, g: 228, b: 238 }, // 8: Soft rose\n\t\t{ r: 228, g: 248, b: 238 }, // 9: Soft mint\n\t\t// 30 new colors (indices 10-39)\n\t\t{ r: 235, g: 245, b: 250 }, // 10: Pale sky blue\n\t\t{ r: 250, g: 235, b: 245 }, // 11: Pale orchid\n\t\t{ r: 235, g: 250, b: 245 }, // 12: Pale seafoam\n\t\t{ r: 250, g: 245, b: 235 }, // 13: Pale peach\n\t\t{ r: 245, g: 235, b: 250 }, // 14: Pale periwinkle\n\t\t{ r: 235, g: 245, b: 235 }, // 15: Pale sage\n\t\t{ r: 245, g: 250, b: 235 }, // 16: Pale lemon\n\t\t{ r: 245, g: 235, b: 235 }, // 17: Pale blush\n\t\t{ r: 235, g: 235, b: 250 }, // 18: Pale lavender blue\n\t\t{ r: 250, g: 235, b: 235 }, // 19: Pale coral\n\t\t{ r: 235, g: 250, b: 250 }, // 20: Pale aqua\n\t\t{ r: 240, g: 248, b: 255 }, // 21: Alice blue\n\t\t{ r: 255, g: 240, b: 248 }, // 22: Lavender blush\n\t\t{ r: 240, g: 255, b: 248 }, // 23: Honeydew tint\n\t\t{ r: 255, g: 248, b: 240 }, // 24: Antique white\n\t\t{ r: 248, g: 240, b: 255 }, // 25: Magnolia\n\t\t{ r: 240, g: 248, b: 240 }, // 26: Mint cream tint\n\t\t{ r: 248, g: 255, b: 240 }, // 27: Ivory tint\n\t\t{ r: 248, g: 240, b: 240 }, // 28: Misty rose tint\n\t\t{ r: 240, g: 240, b: 255 }, // 29: Ghost white tint\n\t\t{ r: 255, g: 245, b: 238 }, // 30: Seashell\n\t\t{ r: 245, g: 255, b: 250 }, // 31: Azure mist\n\t\t{ r: 250, g: 245, b: 255 }, // 32: Lilac mist\n\t\t{ r: 255, g: 250, b: 245 }, // 33: Snow peach\n\t\t{ r: 238, g: 245, b: 255 }, // 34: Powder blue\n\t\t{ r: 255, g: 238, b: 245 }, // 35: Pink lace\n\t\t{ r: 245, g: 255, b: 238 }, // 36: Pale lime\n\t\t{ r: 238, g: 255, b: 245 }, // 37: Pale turquoise\n\t\t{ r: 245, g: 238, b: 255 }, // 38: Pale violet\n\t\t{ r: 255, g: 245, b: 255 }, // 39: Pale magenta\n\t]\n}\n\n/**\n * Convert RGB values to hex color format\n *\n * @param r - Red value (0-255)\n * @param g - Green value (0-255)\n * @param b - Blue value (0-255)\n * @returns Hex color string (e.g., \"#dcebf8\")\n * @throws Error if RGB values are out of range\n */\nexport function rgbToHex(r: number, g: number, b: number): string {\n\t// Validate RGB values\n\tif (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {\n\t\tthrow new Error('RGB values must be between 0 and 255')\n\t}\n\n\t// Convert to hex and pad with zeros\n\tconst rHex = r.toString(16).padStart(2, '0')\n\tconst gHex = g.toString(16).padStart(2, '0')\n\tconst bHex = b.toString(16).padStart(2, '0')\n\n\treturn `#${rHex}${gHex}${bHex}`\n}\n\n/**\n * Convert hex color format to RGB values\n *\n * @param hex - Hex color string (with or without # prefix)\n * @returns RGB color object\n * @throws Error if hex format is invalid\n */\nexport function hexToRgb(hex: string): RgbColor {\n\t// Remove # prefix if present\n\tconst cleanHex = hex.startsWith('#') ? hex.slice(1) : hex\n\n\t// Validate format (must be exactly 6 hex characters)\n\tif (cleanHex.length !== 6 || !/^[0-9a-fA-F]{6}$/.test(cleanHex)) {\n\t\tthrow new Error('Invalid hex color format. Expected format: #RRGGBB or RRGGBB')\n\t}\n\n\t// Parse hex values\n\tconst r = parseInt(cleanHex.slice(0, 2), 16)\n\tconst g = parseInt(cleanHex.slice(2, 4), 16)\n\tconst b = parseInt(cleanHex.slice(4, 6), 16)\n\n\treturn { r, g, b }\n}\n\n/**\n * Generate deterministic color from branch name using SHA256 hash\n * Matches the bash implementation in bash/new-branch-workflow.sh\n *\n * @param branchName - Branch name to generate color from\n * @returns ColorData with RGB, hex, and palette index\n */\nexport function generateColorFromBranchName(branchName: string): ColorData {\n\t// Generate SHA256 hash of branch name\n\tconst hash = createHash('sha256').update(branchName).digest('hex')\n\n\t// Take first 8 hex characters and convert to index (0-39)\n\t// Matches bash: local index=$(( 0x$hash % ${#colors[@]} ))\n\tconst hashPrefix = hash.slice(0, 8)\n\tconst palette = getColorPalette()\n\tconst hashAsInt = parseInt(hashPrefix, 16)\n\tconst index = hashAsInt % palette.length\n\tlogger.debug(`[generateColorFromBranchName] Branch name: ${branchName}, Hash: ${hash}, Hash prefix: ${hashPrefix}, Hash as int: ${hashAsInt}, Index: ${index}`)\n\n\t// Get color from palette\n\tconst rgb = palette[index]\n\n\t// This should never happen as index is always in range [0, palette.length)\n\tif (!rgb) {\n\t\tthrow new Error(`Invalid color index: ${index}`)\n\t}\n\n\t// Convert to hex format\n\tconst hex = rgbToHex(rgb.r, rgb.g, rgb.b)\n\n\treturn {\n\t\trgb,\n\t\thex,\n\t\tindex,\n\t}\n}\n\n/**\n * Lighten a color by a given amount\n * Useful for creating slightly lighter variants for hover states\n *\n * @param rgb - RGB color to lighten\n * @param amount - Amount to lighten (0-1, where 0.1 = 10% lighter)\n * @returns Lightened RGB color\n */\nexport function lightenColor(rgb: RgbColor, amount: number): RgbColor {\n\tconst clamp = (value: number): number => Math.min(255, Math.max(0, Math.round(value)))\n\n\treturn {\n\t\tr: clamp(rgb.r + (255 - rgb.r) * amount),\n\t\tg: clamp(rgb.g + (255 - rgb.g) * amount),\n\t\tb: clamp(rgb.b + (255 - rgb.b) * amount),\n\t}\n}\n\n/**\n * Saturate a color by pushing it away from grey towards its dominant hue\n * Makes subtle colors more vivid while maintaining their hue\n *\n * @param rgb - RGB color to saturate\n * @param amount - Amount to saturate (0-1, where 0.4 = 40% more saturated)\n * @returns Saturated RGB color\n */\nexport function saturateColor(rgb: RgbColor, amount: number): RgbColor {\n\tconst clamp = (value: number): number => Math.min(255, Math.max(0, Math.round(value)))\n\n\t// Calculate average (grey point)\n\tconst avg = (rgb.r + rgb.g + rgb.b) / 3\n\n\t// Push each channel away from grey\n\treturn {\n\t\tr: clamp(rgb.r + (rgb.r - avg) * amount),\n\t\tg: clamp(rgb.g + (rgb.g - avg) * amount),\n\t\tb: clamp(rgb.b + (rgb.b - avg) * amount),\n\t}\n}\n\n/**\n * Calculate appropriate foreground color (black or white) for a given background\n * Uses relative luminance formula from WCAG 2.0\n *\n * @param rgb - Background RGB color\n * @returns '#000000' for light backgrounds, '#ffffff' for dark backgrounds\n */\nexport function calculateForegroundColor(rgb: RgbColor): string {\n\t// Convert RGB to relative luminance (WCAG 2.0 formula)\n\tconst toLinear = (channel: number): number => {\n\t\tconst c = channel / 255\n\t\treturn c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n\t}\n\n\tconst r = toLinear(rgb.r)\n\tconst g = toLinear(rgb.g)\n\tconst b = toLinear(rgb.b)\n\n\tconst luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b\n\n\t// Use black text for light backgrounds (luminance > 0.5)\n\t// Use white text for dark backgrounds\n\treturn luminance > 0.5 ? '#000000' : '#ffffff'\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/ignite.ts","../src/utils/FirstRunManager.ts"],"sourcesContent":["import path from 'path'\nimport { logger } from '../utils/logger.js'\nimport { ClaudeWorkflowOptions } from '../lib/ClaudeService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { launchClaude, ClaudeCliOptions } from '../utils/claude.js'\nimport { PromptTemplateManager, TemplateVariables } from '../lib/PromptTemplateManager.js'\nimport { generateIssueManagementMcpConfig } from '../utils/mcp.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { readFile } from 'fs/promises'\n\n/**\n * IgniteCommand: Auto-detect workspace context and launch Claude\n *\n * This command:\n * 1. Auto-detects context from current directory and git branch\n * 2. Loads appropriate prompt template with variable substitution\n * 3. Launches Claude with existing agent system (NO changes to agent loading)\n * 4. Executes in current terminal (not opening a new window)\n *\n * CRITICAL: This command works with agents exactly as they currently function.\n * NO modifications to agent loading mechanisms.\n */\nexport class IgniteCommand {\n\tprivate templateManager: PromptTemplateManager\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate agentManager: AgentManager\n\tprivate settingsManager: SettingsManager\n\tprivate firstRunManager: FirstRunManager\n\tprivate settings?: import('../lib/SettingsManager.js').IloomSettings\n\n\tconstructor(\n\t\ttemplateManager?: PromptTemplateManager,\n\t\tgitWorktreeManager?: GitWorktreeManager,\n\t\tagentManager?: AgentManager,\n\t\tsettingsManager?: SettingsManager,\n\t\tfirstRunManager?: FirstRunManager\n\t) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.agentManager = agentManager ?? new AgentManager()\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\tthis.firstRunManager = firstRunManager ?? new FirstRunManager('spin')\n\t}\n\n\t/**\n\t * Main entry point for spin command\n\t */\n\tasync execute(oneShot: import('../types/index.js').OneShotMode = 'default'): Promise<void> {\n\t\ttry {\n\t\t\tlogger.info('🚀 Your loom is spinning up, please wait...')\n\n\t\t\t// Step 0.5: Check if this is first-time user\n\t\t\tconst isFirstRun = await this.firstRunManager.isFirstRun()\n\t\t\tif (isFirstRun) {\n\t\t\t\tlogger.success('Welcome to iloom! Preparing first-time experience...')\n\t\t\t}\n\n\t\t\t// Step 1: Auto-detect workspace context\n\t\t\tconst context = await this.detectWorkspaceContext()\n\n\t\t\tlogger.debug('Auto-detected workspace context', { context })\n\n\t\t\t// Inform user what context was detected\n\t\t\tthis.logDetectedContext(context)\n\n\t\t\tlogger.info('📝 Loading prompt template and preparing Claude...')\n\n\t\t\t// Step 2: Get prompt template with variable substitution\n\t\t\tconst variables = this.buildTemplateVariables(context, oneShot)\n\n\t\t\t// Step 2.5: Add first-time user context if needed\n\t\t\tif (isFirstRun) {\n\t\t\t\tvariables.FIRST_TIME_USER = true\n\t\t\t\tvariables.README_CONTENT = await this.loadReadmeContent()\n\t\t\t\tvariables.SETTINGS_SCHEMA_CONTENT = await this.loadSettingsSchemaContent()\n\t\t\t}\n\n\t\t\tconst systemInstructions = await this.templateManager.getPrompt(context.type, variables)\n\n\t\t\t// User prompt to trigger the workflow (includes one-shot bypass instructions if needed)\n\t\t\tconst userPrompt = this.buildUserPrompt(oneShot)\n\n\t\t\t// Step 2.5: Load settings if not cached with CLI overrides\n\t\t\t// Settings are pre-validated at CLI startup, so no error handling needed here\n\t\t\tif (!this.settings) {\n\t\t\t\tconst cliOverrides = extractSettingsOverrides()\n\t\t\t\tthis.settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\t\t}\n\n\t\t\t// Step 3: Determine model and permission mode based on workflow type\n\t\t\tconst model = this.getModelForWorkflow(context.type)\n\t\t\tlet permissionMode = this.getPermissionModeForWorkflow(context.type)\n\n\t\t\t// Override permission mode if bypassPermissions oneShot mode\n\t\t\tif (oneShot === 'bypassPermissions') {\n\t\t\t\tpermissionMode = 'bypassPermissions'\n\t\t\t}\n\n\t\t\t// Display warning if bypassPermissions is used\n\t\t\tif (permissionMode === 'bypassPermissions') {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t'⚠️ WARNING: Using bypassPermissions mode - Claude will execute all tool calls without confirmation. ' +\n\t\t\t\t\t\t'This can be dangerous. Use with caution.'\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Step 4: Build Claude CLI options\n\t\t\tconst claudeOptions: ClaudeCliOptions = {\n\t\t\t\theadless: false, // Enable stdio: 'inherit' for current terminal\n\t\t\t\taddDir: context.workspacePath,\n\t\t\t}\n\n\t\t\t// Add optional model if present\n\t\t\tif (model !== undefined) {\n\t\t\t\tclaudeOptions.model = model\n\t\t\t}\n\n\t\t\t// Add permission mode if not default\n\t\t\tif (permissionMode !== undefined && permissionMode !== 'default') {\n\t\t\t\tclaudeOptions.permissionMode = permissionMode\n\t\t\t}\n\n\t\t\t// Add optional branch name for context\n\t\t\tif (context.branchName !== undefined) {\n\t\t\t\tclaudeOptions.branchName = context.branchName\n\t\t\t}\n\n\t\t\t// Step 4.5: Generate MCP config and tool filtering for issue/PR workflows\n\t\t\tlet mcpConfig: Record<string, unknown>[] | undefined\n\t\t\tlet allowedTools: string[] | undefined\n\t\t\tlet disallowedTools: string[] | undefined\n\n\t\t\tif (context.type === 'issue' || context.type === 'pr') {\n\t\t\t\ttry {\n\t\t\t\t\tconst provider = this.settings ? IssueTrackerFactory.getProviderName(this.settings) : 'github'\n\t\t\t\t\tmcpConfig = await generateIssueManagementMcpConfig(context.type, undefined, provider, this.settings)\n\t\t\t\t\tlogger.debug('Generated MCP configuration for issue management', { provider })\n\n\t\t\t\t\t// Configure tool filtering for issue/PR workflows\n\t\t\t\t\tallowedTools = [\n\t\t\t\t\t\t'mcp__issue_management__get_issue',\n\t\t\t\t\t\t'mcp__issue_management__get_comment',\n\t\t\t\t\t\t'mcp__issue_management__create_comment',\n\t\t\t\t\t\t'mcp__issue_management__update_comment',\n\t\t\t\t\t]\n\t\t\t\t\tdisallowedTools = ['Bash(gh api:*), Bash(gh issue view:*), Bash(gh pr view:*), Bash(gh issue comment:*)']\n\n\t\t\t\t\tlogger.debug('Configured tool filtering for issue/PR workflow', { allowedTools, disallowedTools })\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Log warning but continue without MCP\n\t\t\t\t\tlogger.warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Step 4.6: Load agent configurations using cached settings\n\t\t\tlet agents: Record<string, unknown> | undefined\n\t\t\ttry {\n\t\t\t\t// Use cached settings from Step 2.5\n\t\t\t\tif (this.settings?.agents && Object.keys(this.settings.agents).length > 0) {\n\t\t\t\t\tlogger.debug('Loaded project settings', {\n\t\t\t\t\t\tagentOverrides: Object.keys(this.settings.agents),\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t// Load agents with settings overrides\n\t\t\t\tconst loadedAgents = await this.agentManager.loadAgents(this.settings)\n\t\t\t\tagents = this.agentManager.formatForCli(loadedAgents)\n\t\t\t\tlogger.debug('Loaded agent configurations', {\n\t\t\t\t\tagentCount: Object.keys(agents).length,\n\t\t\t\t\tagentNames: Object.keys(agents),\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\t// Log warning but continue without agents\n\t\t\t\tlogger.warn(`Failed to load agents: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\t}\n\n\t\t\tlogger.debug('Launching Claude in current terminal', {\n\t\t\t\ttype: context.type,\n\t\t\t\tmodel,\n\t\t\t\tpermissionMode,\n\t\t\t\tworkspacePath: context.workspacePath,\n\t\t\t\thasMcpConfig: !!mcpConfig,\n\t\t\t})\n\n\t\t\tlogger.info('✨ Launching Claude in current terminal...')\n\n\t\t\t// Step 5: Launch Claude with system instructions appended and user prompt\n\t\t\tawait launchClaude(userPrompt, {\n\t\t\t\t...claudeOptions,\n\t\t\t\tappendSystemPrompt: systemInstructions,\n\t\t\t\t...(mcpConfig && { mcpConfig }),\n\t\t\t\t...(allowedTools && { allowedTools }),\n\t\t\t\t...(disallowedTools && { disallowedTools }),\n\t\t\t\t...(agents && { agents }),\n\t\t\t})\n\n\t\t\t// Step 6: Mark as run after successful launch\n\t\t\tif (isFirstRun) {\n\t\t\t\tawait this.firstRunManager.markAsRun()\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\t\t\tlogger.error(`Failed to launch Claude: ${errorMessage}`)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Log user-friendly information about detected context\n\t */\n\tprivate logDetectedContext(context: ClaudeWorkflowOptions): void {\n\t\tif (context.type === 'issue') {\n\t\t\tlogger.info(`🎯 Detected issue workflow: Issue #${context.issueNumber}`)\n\t\t} else if (context.type === 'pr') {\n\t\t\tlogger.info(`🔄 Detected PR workflow: PR #${context.prNumber}`)\n\t\t} else {\n\t\t\tlogger.info('🌟 Detected regular workflow')\n\t\t}\n\n\t\tif (context.branchName) {\n\t\t\tlogger.info(`🌿 Working on branch: ${context.branchName}`)\n\t\t}\n\n\t\tif (context.port) {\n\t\t\tlogger.info(`🌐 Development server port: ${context.port}`)\n\t\t}\n\t}\n\n\t/**\n\t * Build template variables from context\n\t */\n\tprivate buildTemplateVariables(context: ClaudeWorkflowOptions, oneShot: import('../types/index.js').OneShotMode): TemplateVariables {\n\t\tconst variables: TemplateVariables = {\n\t\t\tWORKSPACE_PATH: context.workspacePath,\n\t\t}\n\n\t\tif (context.issueNumber !== undefined) {\n\t\t\tvariables.ISSUE_NUMBER = context.issueNumber\n\t\t}\n\n\t\tif (context.prNumber !== undefined) {\n\t\t\tvariables.PR_NUMBER = context.prNumber\n\t\t}\n\n\t\tif (context.title !== undefined) {\n\t\t\tif (context.type === 'issue') {\n\t\t\t\tvariables.ISSUE_TITLE = context.title\n\t\t\t} else if (context.type === 'pr') {\n\t\t\t\tvariables.PR_TITLE = context.title\n\t\t\t}\n\t\t}\n\n\t\tif (context.port !== undefined) {\n\t\t\tvariables.PORT = context.port\n\t\t}\n\n\t\t// Set ONE_SHOT_MODE flag for template conditional sections\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\tvariables.ONE_SHOT_MODE = true\n\t\t}\n\n\t\treturn variables\n\t}\n\n\t/**\n\t * Get the appropriate model for a workflow type\n\t * Same logic as ClaudeService.getModelForWorkflow()\n\t */\n\tprivate getModelForWorkflow(type: 'issue' | 'pr' | 'regular'): string | undefined {\n\t\t// Issue workflows use claude-sonnet-4-20250514\n\t\tif (type === 'issue') {\n\t\t\treturn 'claude-sonnet-4-20250514'\n\t\t}\n\t\t// For PR and regular workflows, use Claude's default model\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Get the appropriate permission mode for a workflow type\n\t * Same logic as ClaudeService.getPermissionModeForWorkflow()\n\t */\n\tprivate getPermissionModeForWorkflow(\n\t\ttype: 'issue' | 'pr' | 'regular'\n\t): ClaudeCliOptions['permissionMode'] {\n\t\t// Check settings for configured permission mode\n\t\tif (this.settings?.workflows) {\n\t\t\tconst workflowConfig =\n\t\t\t\ttype === 'issue'\n\t\t\t\t\t? this.settings.workflows.issue\n\t\t\t\t\t: type === 'pr'\n\t\t\t\t\t\t? this.settings.workflows.pr\n\t\t\t\t\t\t: this.settings.workflows.regular\n\n\t\t\tif (workflowConfig?.permissionMode) {\n\t\t\t\treturn workflowConfig.permissionMode\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to current defaults\n\t\tif (type === 'issue') {\n\t\t\treturn 'acceptEdits'\n\t\t}\n\t\t// For PR and regular workflows, use default permissions\n\t\treturn 'default'\n\t}\n\n\t/**\n\t * Auto-detect workspace context from current directory and git branch\n\t *\n\t * Detection priority:\n\t * 1. Directory name patterns (_pr_N, issue-N)\n\t * 2. Git branch name patterns\n\t * 3. Fallback to 'regular' workflow\n\t *\n\t * This leverages the same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async detectWorkspaceContext(): Promise<ClaudeWorkflowOptions> {\n\t\tconst workspacePath = process.cwd()\n\t\tconst currentDir = path.basename(workspacePath)\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\t// Pattern: /.*_pr_(\\d+)$/\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\n\t\t\treturn this.buildContextForPR(prNumber, workspacePath)\n\t\t}\n\n\t\t// Check for issue pattern in directory name\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\n\t\t\treturn this.buildContextForIssue(issueNumber, workspacePath)\n\t\t}\n\n\t\t// Fallback: Try to extract from git branch name\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\t\tif (currentBranch) {\n\t\t\t\t// Try to extract issue from branch name\n\t\t\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\t\t\tif (branchIssueNumber !== null) {\n\t\t\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\n\t\t\t\t\treturn this.buildContextForIssue(branchIssueNumber, workspacePath, currentBranch)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Git command failed - not a git repo or other git error\n\t\t\tlogger.debug('Could not detect from git branch', { error })\n\t\t}\n\n\t\t// Last resort: use regular workflow\n\t\tlogger.debug('No specific context detected, using regular workflow')\n\t\treturn this.buildContextForRegular(workspacePath)\n\t}\n\n\t/**\n\t * Build context for issue workflow\n\t */\n\tprivate async buildContextForIssue(\n\t\tissueNumber: string | number,\n\t\tworkspacePath: string,\n\t\tbranchName?: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name if not provided\n\t\tif (!branchName) {\n\t\t\ttry {\n\t\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t\t} catch {\n\t\t\t\t// Ignore git errors\n\t\t\t}\n\t\t}\n\n\t\tconst port = this.getPortFromEnv()\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'issue',\n\t\t\tissueNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (port !== undefined) {\n\t\t\tcontext.port = port\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for PR workflow\n\t */\n\tprivate async buildContextForPR(\n\t\tprNumber: number,\n\t\tworkspacePath: string\n\t): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst port = this.getPortFromEnv()\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'pr',\n\t\t\tprNumber,\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (port !== undefined) {\n\t\t\tcontext.port = port\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Build context for regular workflow\n\t */\n\tprivate async buildContextForRegular(workspacePath: string): Promise<ClaudeWorkflowOptions> {\n\t\t// Get branch name\n\t\tlet branchName: string | undefined\n\t\ttry {\n\t\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\t\tbranchName = repoInfo.currentBranch ?? undefined\n\t\t} catch {\n\t\t\t// Ignore git errors\n\t\t}\n\n\t\tconst port = this.getPortFromEnv()\n\t\tconst context: ClaudeWorkflowOptions = {\n\t\t\ttype: 'regular',\n\t\t\tworkspacePath,\n\t\t\theadless: false, // Interactive mode\n\t\t}\n\n\t\tif (port !== undefined) {\n\t\t\tcontext.port = port\n\t\t}\n\n\t\tif (branchName !== undefined) {\n\t\t\tcontext.branchName = branchName\n\t\t}\n\n\t\treturn context\n\t}\n\n\t/**\n\t * Get PORT from environment variables\n\t * Returns undefined if PORT is not set or invalid\n\t */\n\tprivate getPortFromEnv(): number | undefined {\n\t\tconst portStr = process.env.PORT\n\t\tif (!portStr) {\n\t\t\treturn undefined\n\t\t}\n\n\t\tconst port = parseInt(portStr, 10)\n\t\tif (isNaN(port)) {\n\t\t\tlogger.warn(`Invalid PORT environment variable: ${portStr}`)\n\t\t\treturn undefined\n\t\t}\n\n\t\treturn port\n\t}\n\n\n\t/**\n\t * Build user prompt based on one-shot mode\n\t */\n\tprivate buildUserPrompt(oneShot: import('../types/index.js').OneShotMode = 'default'): string {\n\t\t// For one-shot modes, add bypass instructions to override template approval requirements\n\t\tif (oneShot === 'noReview' || oneShot === 'bypassPermissions') {\n\t\t\treturn 'Guide the user through the iloom workflow! The user has requested you move through the workflow without awaiting confirmation. This supersedes any other guidance.'\n\t\t}\n\n\t\t// Default mode: simple \"Go!\" prompt\n\t\treturn 'Guide the user through the iloom workflow!'\n\t}\n\n\t/**\n\t * Load README.md content for first-time users\n\t * Walks up from dist directory to find README.md in project root\n\t */\n\tprivate async loadReadmeContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find README.md\n\t\t\t// Use same pattern as PromptTemplateManager for finding files\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst readmePath = path.join(currentDir, 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(readmePath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded README.md for first-time user', { readmePath })\n\t\t\t\t\treturn content\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.debug('README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n\n\t/**\n\t * Load settings schema content for first-time users\n\t * Walks up from dist directory to find .iloom/README.md\n\t */\n\tprivate async loadSettingsSchemaContent(): Promise<string> {\n\t\ttry {\n\t\t\t// Walk up from current file location to find .iloom/README.md\n\t\t\tlet currentDir = path.dirname(new URL(import.meta.url).pathname)\n\n\t\t\t// Walk up to find .iloom/README.md\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst schemaPath = path.join(currentDir, '.iloom', 'README.md')\n\t\t\t\ttry {\n\t\t\t\t\tconst content = await readFile(schemaPath, 'utf-8')\n\t\t\t\t\tlogger.debug('Loaded .iloom/README.md for first-time user', { schemaPath })\n\t\t\t\t\treturn content\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.debug('.iloom/README.md not found, returning empty string')\n\t\t\treturn ''\n\t\t} catch (error) {\n\t\t\t// Graceful degradation - return empty string on error\n\t\t\tlogger.debug(`Failed to load .iloom/README.md: ${error}`)\n\t\t\treturn ''\n\t\t}\n\t}\n}\n","import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { logger } from './logger.js'\n\n/**\n * FirstRunManager: Detect and track first-time spin usage via marker file\n *\n * Follows the same pattern as UpdateNotifier for file-based state tracking\n * in ~/.config/iloom-ai/ directory.\n */\nexport class FirstRunManager {\n\tprivate markerFilePath: string\n\n\tconstructor(feature: string = 'spin') {\n\t\tconst configDir = path.join(os.homedir(), '.config', 'iloom-ai')\n\t\tthis.markerFilePath = path.join(configDir, `${feature}-first-run`)\n\t\tlogger.debug('FirstRunManager initialized', {\n\t\t\tfeature,\n\t\t\tmarkerFilePath: this.markerFilePath\n\t\t})\n\t}\n\n\t/**\n\t * Check if this is the first run of the feature\n\t * Returns true if marker file doesn't exist\n\t * Handles errors gracefully by returning true (treat as first-run on error)\n\t */\n\tasync isFirstRun(): Promise<boolean> {\n\t\tlogger.debug('isFirstRun: Checking for marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(this.markerFilePath)\n\t\t\tlogger.debug(`isFirstRun: Marker file exists=${exists}`)\n\t\t\treturn !exists\n\t\t} catch (error) {\n\t\t\t// On error, gracefully degrade by treating as first-run\n\t\t\tlogger.debug(`isFirstRun: Error checking marker file, treating as first-run: ${error}`)\n\t\t\treturn true\n\t\t}\n\t}\n\n\t/**\n\t * Mark the feature as having been run\n\t * Creates the marker file in config directory\n\t * Handles errors gracefully without throwing\n\t */\n\tasync markAsRun(): Promise<void> {\n\t\tlogger.debug('markAsRun: Attempting to create marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\t// Ensure directory exists\n\t\t\tconst configDir = path.dirname(this.markerFilePath)\n\t\t\tlogger.debug(`markAsRun: Ensuring config directory exists: ${configDir}`)\n\t\t\tawait fs.ensureDir(configDir)\n\n\t\t\t// Write marker file with timestamp for debugging\n\t\t\tconst markerContent = {\n\t\t\t\tfirstRun: new Date().toISOString(),\n\t\t\t}\n\t\t\tawait fs.writeFile(this.markerFilePath, JSON.stringify(markerContent, null, 2), 'utf8')\n\t\t\tlogger.debug('markAsRun: Marker file created successfully')\n\t\t} catch (error) {\n\t\t\t// Don't throw on errors - just log debug message\n\t\t\t// Failing to write marker shouldn't break the workflow\n\t\t\tlogger.debug(`markAsRun: Failed to create marker file: ${error}`)\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["path","path"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/init.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { ShellCompletion } from '../lib/ShellCompletion.js'\nimport chalk from 'chalk'\nimport { mkdir, writeFile, readFile } from 'fs/promises'\nimport { existsSync } from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport { detectClaudeCli, launchClaude } from '../utils/claude.js'\nimport { PromptTemplateManager } from '../lib/PromptTemplateManager.js'\nimport { fileURLToPath } from 'url'\nimport { GitRemote, parseGitRemotes } from '../utils/remote.js'\nimport { SettingsMigrationManager } from '../lib/SettingsMigrationManager.js'\n\n/**\n * Initialize iloom configuration and setup shell autocomplete\n * Implements the `il init` command requested in issue #94\n */\nexport class InitCommand {\n private readonly shellCompletion: ShellCompletion\n private readonly templateManager: PromptTemplateManager\n\n constructor(shellCompletion?: ShellCompletion, templateManager?: PromptTemplateManager) {\n this.shellCompletion = shellCompletion ?? new ShellCompletion()\n this.templateManager = templateManager ?? new PromptTemplateManager()\n }\n\n /**\n * Main entry point for the init command\n * Prompts user for autocomplete setup and displays instructions\n * @param customInitialMessage Optional custom initial message to send to Claude (defaults to \"Help me configure iloom settings.\")\n */\n public async execute(customInitialMessage?: string): Promise<void> {\n try {\n logger.debug('InitCommand.execute() starting', {\n cwd: process.cwd(),\n nodeVersion: process.version,\n hasCustomInitialMessage: !!customInitialMessage\n })\n\n logger.info(chalk.bold('Welcome to iloom setup'))\n\n // Setup project configuration\n logger.info(chalk.bold('Verifying current setup...'))\n\n await this.setupProjectConfiguration()\n\n // Launch guided Claude configuration if available\n await this.launchGuidedInit(customInitialMessage)\n\n logger.info(chalk.green('Setup complete! Enjoy using iloom CLI.'))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.error(`Initialization failed: ${message}`)\n throw error\n }\n }\n\n /**\n * Setup project configuration files\n * Creates settings.local.json and updates .gitignore\n */\n private async setupProjectConfiguration(): Promise<void> {\n logger.debug('setupProjectConfiguration() starting')\n\n // Migrate legacy .hatchbox settings to .iloom (BEFORE creating new files)\n try {\n logger.debug('Loading SettingsMigrationManager for legacy migration')\n const migrationManager = new SettingsMigrationManager()\n logger.debug('Running settings migration check')\n await migrationManager.migrateSettingsIfNeeded()\n logger.debug('Settings migration check completed')\n } catch (error) {\n // Log warning but don't fail\n logger.warn(`Settings migration failed: ${error instanceof Error ? error.message : 'Unknown'}`)\n logger.debug('Settings migration error details', { error })\n }\n\n // Update .gitignore\n logger.debug('Starting .gitignore update')\n await this.updateGitignore()\n logger.debug('setupProjectConfiguration() completed')\n\n // Ensure .iloom directory exists\n const iloomDir = path.join(process.cwd(), '.iloom')\n logger.debug('Creating .iloom directory', { iloomDir })\n await mkdir(iloomDir, { recursive: true })\n logger.debug('.iloom directory created/verified')\n\n // // Create settings.local.json if it doesn't exist\n // const settingsLocalPath = path.join(iloomDir, 'settings.local.json')\n // logger.debug('Checking for existing settings.local.json', { settingsLocalPath })\n\n // if (!existsSync(settingsLocalPath)) {\n // logger.debug('Creating settings.local.json file')\n // await writeFile(settingsLocalPath, '{}\\n', 'utf-8')\n // logger.info('Created .iloom/settings.local.json')\n // logger.debug('settings.local.json file created successfully')\n // } else {\n // logger.debug('settings.local.json file already exists, skipping')\n // }\n }\n\n /**\n * Add settings.local.json to .gitignore if not already present\n */\n private async updateGitignore(): Promise<void> {\n const gitignorePath = path.join(process.cwd(), '.gitignore')\n const entryToAdd = '.iloom/settings.local.json'\n\n logger.debug('updateGitignore() starting', {\n gitignorePath,\n entryToAdd\n })\n\n // Read existing .gitignore or create empty\n let content = ''\n if (existsSync(gitignorePath)) {\n logger.debug('.gitignore file exists, reading content')\n content = await readFile(gitignorePath, 'utf-8')\n logger.debug('Read .gitignore content', {\n contentLength: content.length,\n lineCount: content.split('\\n').length\n })\n } else {\n logger.debug('.gitignore file does not exist, will create new one')\n }\n\n // Check if entry already exists\n const lines = content.split('\\n')\n const entryExists = lines.some(line => line.trim() === entryToAdd)\n logger.debug('Checking if entry already exists', {\n entryExists,\n totalLines: lines.length\n })\n\n if (entryExists) {\n logger.debug('Entry already exists, skipping .gitignore update')\n return\n }\n\n // Add entry with comment\n const commentLine = '\\n# Added by iloom CLI'\n const separator = content.endsWith('\\n') || content === '' ? '' : '\\n'\n const newContent = content + separator + commentLine + '\\n' + entryToAdd + '\\n'\n\n logger.debug('Writing updated .gitignore', {\n originalLength: content.length,\n newLength: newContent.length,\n addedLines: 3 // comment + entry + newline\n })\n\n await writeFile(gitignorePath, newContent, 'utf-8')\n logger.info('Added .iloom/settings.local.json to .gitignore')\n logger.debug('.gitignore update completed successfully')\n }\n\n /**\n * Launch interactive Claude-guided configuration\n * @param customInitialMessage Optional custom initial message to send to Claude\n */\n private async launchGuidedInit(customInitialMessage?: string): Promise<void> {\n logger.debug('launchGuidedInit() starting', { hasCustomInitialMessage: !!customInitialMessage })\n logger.info(chalk.bold('Starting interactive Claude-guided configuration...'))\n\n // Check if Claude CLI is available\n logger.debug('Checking Claude CLI availability')\n const claudeAvailable = await detectClaudeCli()\n logger.debug('Claude CLI availability check result', { claudeAvailable })\n\n if (!claudeAvailable) {\n logger.warn('Claude Code not detected. Skipping guided configuration.')\n logger.info('iloom won\\'t be able to help you much without Claude Code, so please install it: npm install -g @anthropic-ai/claude-code')\n logger.debug('Exiting launchGuidedInit() due to missing Claude CLI')\n return\n }\n\n try {\n // Load schema from dist/schema/settings.schema.json\n // Use similar approach to PromptTemplateManager for path resolution\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = path.dirname(__filename)\n\n // Walk up to find the schema directory (in case of chunked files)\n let schemaPath = path.join(__dirname, 'schema', 'settings.schema.json')\n\n logger.debug('Loading settings schema', {\n __filename,\n __dirname,\n schemaPath,\n schemaExists: existsSync(schemaPath)\n })\n\n let schemaContent = ''\n if (existsSync(schemaPath)) {\n logger.debug('Reading schema file')\n schemaContent = await readFile(schemaPath, 'utf-8')\n logger.debug('Schema file loaded', {\n contentLength: schemaContent.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(schemaContent)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.warn('Schema file not found - Claude will work without schema validation')\n logger.debug('Schema file not found at expected path', { schemaPath })\n }\n\n // Check for existing settings - read ALL three files if they exist (global, project, local)\n const settingsGlobalPath = path.join(os.homedir(), '.config', 'iloom-ai', 'settings.json')\n const settingsLocalPath = path.join(process.cwd(), '.iloom', 'settings.local.json')\n const settingsCommittedPath = path.join(process.cwd(), '.iloom', 'settings.json')\n\n let settingsGlobalJson = ''\n let settingsJson = ''\n let settingsLocalJson = ''\n\n logger.debug('Checking for settings files', {\n settingsGlobalPath,\n settingsLocalPath,\n settingsCommittedPath,\n globalExists: existsSync(settingsGlobalPath),\n localExists: existsSync(settingsLocalPath),\n committedExists: existsSync(settingsCommittedPath)\n })\n\n // Read global settings.json if it exists\n if (existsSync(settingsGlobalPath)) {\n logger.debug('Reading global settings.json')\n const content = await readFile(settingsGlobalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsGlobalJson = content\n logger.debug('global settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('global settings.json is empty, skipping')\n }\n } else {\n logger.debug('global settings.json does not exist')\n }\n\n // Read settings.json if it exists\n if (existsSync(settingsCommittedPath)) {\n logger.debug('Reading settings.json')\n const content = await readFile(settingsCommittedPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsJson = content\n logger.debug('settings.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.json is empty, skipping')\n }\n } else {\n logger.debug('settings.json does not exist')\n }\n\n // Read settings.local.json if it exists\n if (existsSync(settingsLocalPath)) {\n logger.debug('Reading settings.local.json')\n const content = await readFile(settingsLocalPath, 'utf-8')\n const trimmed = content.trim()\n if (trimmed !== '{}' && trimmed !== '') {\n settingsLocalJson = content\n logger.debug('settings.local.json loaded', {\n contentLength: content.length,\n isValidJson: ((): boolean => {\n try {\n JSON.parse(content)\n return true\n } catch {\n return false\n }\n })()\n })\n } else {\n logger.debug('settings.local.json is empty, skipping')\n }\n } else {\n logger.debug('settings.local.json does not exist')\n }\n\n // Log summary\n logger.debug('Settings files summary', {\n hasSettingsGlobalJson: !!settingsGlobalJson,\n hasSettingsJson: !!settingsJson,\n hasSettingsLocalJson: !!settingsLocalJson,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Detect shell and read config\n logger.debug('Detecting user shell for autocomplete setup')\n const shell = this.shellCompletion.detectShell()\n logger.debug('Shell detection result', { shell })\n\n let shellConfigPath = ''\n let shellConfigContent = ''\n\n if (shell !== 'unknown') {\n logger.debug('Grepping shell config for completion setup')\n const shellConfig = await this.shellCompletion.grepCompletionConfig(shell)\n if (shellConfig) {\n shellConfigPath = shellConfig.path\n shellConfigContent = shellConfig.content\n logger.debug('Shell config completion grep completed', {\n path: shellConfigPath,\n contentLength: shellConfigContent.length,\n configExists: existsSync(shellConfigPath),\n hasMatches: shellConfigContent.trim().length > 0\n })\n } else {\n logger.debug('Could not read shell config')\n }\n } else {\n logger.debug('Unknown shell detected, skipping config read')\n }\n\n let remotes: GitRemote[] = []\n try {\n // Detect git remotes for GitHub configuration\n logger.debug('Detecting git remotes for GitHub configuration')\n remotes = await parseGitRemotes()\n logger.debug('Git remotes detected', { count: remotes.length, remotes })\n } catch (error) {\n const message = error instanceof Error ? error.stack : 'Unknown error'\n logger.debug(\"Error occured while getting remote info: \", message)\n }\n\n\n let remotesInfo = ''\n let multipleRemotes = false\n let singleRemote = false\n let singleRemoteName = ''\n let singleRemoteUrl = ''\n let noRemotes = false\n\n if (remotes.length === 0) {\n noRemotes = true\n remotesInfo = 'No git remotes detected in this repository.'\n } else if (remotes.length === 1 && remotes[0]) {\n singleRemote = true\n singleRemoteName = remotes[0].name\n singleRemoteUrl = remotes[0].url\n remotesInfo = `Detected Remote:\\n- **${remotes[0].name}**: ${remotes[0].url} (${remotes[0].owner}/${remotes[0].repo})`\n } else {\n multipleRemotes = true\n remotesInfo = `Detected Remotes (${remotes.length}):\\n` +\n remotes.map(r => `- **${r.name}**: ${r.url} (${r.owner}/${r.repo})`).join('\\n')\n }\n\n // Load README content for comprehensive documentation\n logger.info('Loading iloom documentation...')\n const readmeContent = await this.loadReadmeContent()\n logger.debug('README content loaded', {\n readmeContentLength: readmeContent.length,\n })\n\n // Build template variables\n const variables = {\n SETTINGS_SCHEMA: schemaContent,\n SETTINGS_GLOBAL_JSON: settingsGlobalJson,\n SETTINGS_JSON: settingsJson,\n SETTINGS_LOCAL_JSON: settingsLocalJson,\n SHELL_TYPE: shell,\n SHELL_CONFIG_PATH: shellConfigPath,\n SHELL_CONFIG_CONTENT: shellConfigContent,\n REMOTES_INFO: remotesInfo,\n MULTIPLE_REMOTES: multipleRemotes.toString(),\n SINGLE_REMOTE: singleRemote.toString(),\n SINGLE_REMOTE_NAME: singleRemoteName,\n SINGLE_REMOTE_URL: singleRemoteUrl,\n NO_REMOTES: noRemotes.toString(),\n README_CONTENT: readmeContent,\n }\n\n logger.debug('Building template variables', {\n variableKeys: Object.keys(variables),\n schemaContentLength: schemaContent.length,\n settingsGlobalJsonLength: settingsGlobalJson.length,\n settingsJsonLength: settingsJson.length,\n settingsLocalJsonLength: settingsLocalJson.length\n })\n\n // Get init prompt\n logger.debug('Loading init prompt template')\n const prompt = await this.templateManager.getPrompt('init', variables)\n\n logger.debug('Init prompt loaded', {\n promptLength: prompt.length,\n containsSchema: prompt.includes('SETTINGS_SCHEMA'),\n containsExistingSettings: prompt.includes('EXISTING_SETTINGS')\n })\n\n const claudeOptions = {\n headless: false,\n appendSystemPrompt: prompt,\n addDir: process.cwd(),\n }\n\n logger.debug('Launching Claude with options', {\n optionKeys: Object.keys(claudeOptions),\n headless: claudeOptions.headless,\n hasSystemPrompt: !!claudeOptions.appendSystemPrompt,\n addDir: claudeOptions.addDir,\n promptLength: prompt.length,\n hasCustomInitialMessage: !!customInitialMessage\n })\n\n // Launch Claude in interactive mode with custom initial message if provided\n const initialMessage = customInitialMessage ?? 'Help me configure iloom settings.'\n await launchClaude(initialMessage, claudeOptions)\n logger.debug('Claude session completed')\n\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n logger.warn(`Guided configuration failed: ${message}`)\n logger.debug('launchGuidedInit() error details', error instanceof Error ? error.stack : {error})\n logger.info('You can manually edit .iloom/settings.json to configure iloom.')\n }\n\n logger.debug('launchGuidedInit() completed')\n }\n\n /**\n * Load README.md content for init prompt\n * Walks up from dist directory to find README.md in project root\n */\n private async loadReadmeContent(): Promise<string> {\n try {\n // Walk up from current file location to find README.md\n // Use same pattern as PromptTemplateManager for finding files\n let currentDir = path.dirname(fileURLToPath(import.meta.url))\n\n // Walk up to find README.md\n while (currentDir !== path.dirname(currentDir)) {\n const readmePath = path.join(currentDir, 'README.md')\n try {\n const content = await readFile(readmePath, 'utf-8')\n logger.debug('Loaded README.md for init prompt', { readmePath })\n return content\n } catch {\n currentDir = path.dirname(currentDir)\n }\n }\n\n logger.debug('README.md not found, returning empty string')\n return ''\n } catch (error) {\n // Graceful degradation - return empty string on error\n logger.debug(`Failed to load README.md: ${error}`)\n return ''\n }\n }\n\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAEA,OAAO,WAAW;AAClB,SAAS,OAAO,WAAW,gBAAgB;AAC3C,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AACjB,OAAO,QAAQ;AAGf,SAAS,qBAAqB;AAQvB,IAAM,cAAN,MAAkB;AAAA,EAIvB,YAAY,iBAAmC,iBAAyC;AACtF,SAAK,kBAAkB,mBAAmB,IAAI,gBAAgB;AAC9D,SAAK,kBAAkB,mBAAmB,IAAI,sBAAsB;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,QAAQ,sBAA8C;AACjE,QAAI;AACF,aAAO,MAAM,kCAAkC;AAAA,QAC7C,KAAK,QAAQ,IAAI;AAAA,QACjB,aAAa,QAAQ;AAAA,QACrB,yBAAyB,CAAC,CAAC;AAAA,MAC7B,CAAC;AAED,aAAO,KAAK,MAAM,KAAK,wBAAwB,CAAC;AAGhD,aAAO,KAAK,MAAM,KAAK,4BAA4B,CAAC;AAEpD,YAAM,KAAK,0BAA0B;AAGrC,YAAM,KAAK,iBAAiB,oBAAoB;AAEhD,aAAO,KAAK,MAAM,MAAM,wCAAwC,CAAC;AAAA,IACnE,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,aAAO,MAAM,0BAA0B,OAAO,EAAE;AAChD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,4BAA2C;AACvD,WAAO,MAAM,sCAAsC;AAGnD,QAAI;AACF,aAAO,MAAM,uDAAuD;AACpE,YAAM,mBAAmB,IAAI,yBAAyB;AACtD,aAAO,MAAM,kCAAkC;AAC/C,YAAM,iBAAiB,wBAAwB;AAC/C,aAAO,MAAM,oCAAoC;AAAA,IACnD,SAAS,OAAO;AAEd,aAAO,KAAK,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,SAAS,EAAE;AAC9F,aAAO,MAAM,oCAAoC,EAAE,MAAM,CAAC;AAAA,IAC5D;AAGA,WAAO,MAAM,4BAA4B;AACzC,UAAM,KAAK,gBAAgB;AAC3B,WAAO,MAAM,uCAAuC;AAGpD,UAAM,WAAW,KAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ;AAClD,WAAO,MAAM,6BAA6B,EAAE,SAAS,CAAC;AACtD,UAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AACzC,WAAO,MAAM,mCAAmC;AAAA,EAclD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAiC;AAC7C,UAAM,gBAAgB,KAAK,KAAK,QAAQ,IAAI,GAAG,YAAY;AAC3D,UAAM,aAAa;AAEnB,WAAO,MAAM,8BAA8B;AAAA,MACzC;AAAA,MACA;AAAA,IACF,CAAC;AAGD,QAAI,UAAU;AACd,QAAI,WAAW,aAAa,GAAG;AAC7B,aAAO,MAAM,yCAAyC;AACtD,gBAAU,MAAM,SAAS,eAAe,OAAO;AAC/C,aAAO,MAAM,2BAA2B;AAAA,QACtC,eAAe,QAAQ;AAAA,QACvB,WAAW,QAAQ,MAAM,IAAI,EAAE;AAAA,MACjC,CAAC;AAAA,IACH,OAAO;AACL,aAAO,MAAM,qDAAqD;AAAA,IACpE;AAGA,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,UAAM,cAAc,MAAM,KAAK,UAAQ,KAAK,KAAK,MAAM,UAAU;AACjE,WAAO,MAAM,oCAAoC;AAAA,MAC/C;AAAA,MACA,YAAY,MAAM;AAAA,IACpB,CAAC;AAED,QAAI,aAAa;AACf,aAAO,MAAM,kDAAkD;AAC/D;AAAA,IACF;AAGA,UAAM,cAAc;AACpB,UAAM,YAAY,QAAQ,SAAS,IAAI,KAAK,YAAY,KAAK,KAAK;AAClE,UAAM,aAAa,UAAU,YAAY,cAAc,OAAO,aAAa;AAE3E,WAAO,MAAM,8BAA8B;AAAA,MACzC,gBAAgB,QAAQ;AAAA,MACxB,WAAW,WAAW;AAAA,MACtB,YAAY;AAAA;AAAA,IACd,CAAC;AAED,UAAM,UAAU,eAAe,YAAY,OAAO;AAClD,WAAO,KAAK,gDAAgD;AAC5D,WAAO,MAAM,0CAA0C;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBAAiB,sBAA8C;AAC3E,WAAO,MAAM,+BAA+B,EAAE,yBAAyB,CAAC,CAAC,qBAAqB,CAAC;AAC/F,WAAO,KAAK,MAAM,KAAK,qDAAqD,CAAC;AAG7E,WAAO,MAAM,kCAAkC;AAC/C,UAAM,kBAAkB,MAAM,gBAAgB;AAC9C,WAAO,MAAM,wCAAwC,EAAE,gBAAgB,CAAC;AAExE,QAAI,CAAC,iBAAiB;AACpB,aAAO,KAAK,0DAA0D;AACtE,aAAO,KAAK,0HAA2H;AACvI,aAAO,MAAM,sDAAsD;AACnE;AAAA,IACF;AAEA,QAAI;AAGF,YAAM,aAAa,cAAc,YAAY,GAAG;AAChD,YAAM,YAAY,KAAK,QAAQ,UAAU;AAGzC,UAAI,aAAa,KAAK,KAAK,WAAW,UAAU,sBAAsB;AAEtE,aAAO,MAAM,2BAA2B;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,WAAW,UAAU;AAAA,MACrC,CAAC;AAED,UAAI,gBAAgB;AACpB,UAAI,WAAW,UAAU,GAAG;AAC1B,eAAO,MAAM,qBAAqB;AAClC,wBAAgB,MAAM,SAAS,YAAY,OAAO;AAClD,eAAO,MAAM,sBAAsB;AAAA,UACjC,eAAe,cAAc;AAAA,UAC7B,cAAc,MAAe;AAC3B,gBAAI;AACF,mBAAK,MAAM,aAAa;AACxB,qBAAO;AAAA,YACT,QAAQ;AACN,qBAAO;AAAA,YACT;AAAA,UACF,GAAG;AAAA,QACL,CAAC;AAAA,MACH,OAAO;AACL,eAAO,KAAK,oEAAoE;AAChF,eAAO,MAAM,0CAA0C,EAAE,WAAW,CAAC;AAAA,MACvE;AAGA,YAAM,qBAAqB,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY,eAAe;AACzF,YAAM,oBAAoB,KAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,qBAAqB;AAClF,YAAM,wBAAwB,KAAK,KAAK,QAAQ,IAAI,GAAG,UAAU,eAAe;AAEhF,UAAI,qBAAqB;AACzB,UAAI,eAAe;AACnB,UAAI,oBAAoB;AAExB,aAAO,MAAM,+BAA+B;AAAA,QAC1C;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,WAAW,kBAAkB;AAAA,QAC3C,aAAa,WAAW,iBAAiB;AAAA,QACzC,iBAAiB,WAAW,qBAAqB;AAAA,MACnD,CAAC;AAGD,UAAI,WAAW,kBAAkB,GAAG;AAClC,eAAO,MAAM,8BAA8B;AAC3C,cAAM,UAAU,MAAM,SAAS,oBAAoB,OAAO;AAC1D,cAAM,UAAU,QAAQ,KAAK;AAC7B,YAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,+BAAqB;AACrB,iBAAO,MAAM,+BAA+B;AAAA,YAC1C,eAAe,QAAQ;AAAA,YACvB,cAAc,MAAe;AAC3B,kBAAI;AACF,qBAAK,MAAM,OAAO;AAClB,uBAAO;AAAA,cACT,QAAQ;AACN,uBAAO;AAAA,cACT;AAAA,YACF,GAAG;AAAA,UACL,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,yCAAyC;AAAA,QACxD;AAAA,MACF,OAAO;AACL,eAAO,MAAM,qCAAqC;AAAA,MACpD;AAGA,UAAI,WAAW,qBAAqB,GAAG;AACrC,eAAO,MAAM,uBAAuB;AACpC,cAAM,UAAU,MAAM,SAAS,uBAAuB,OAAO;AAC7D,cAAM,UAAU,QAAQ,KAAK;AAC7B,YAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,yBAAe;AACf,iBAAO,MAAM,wBAAwB;AAAA,YACnC,eAAe,QAAQ;AAAA,YACvB,cAAc,MAAe;AAC3B,kBAAI;AACF,qBAAK,MAAM,OAAO;AAClB,uBAAO;AAAA,cACT,QAAQ;AACN,uBAAO;AAAA,cACT;AAAA,YACF,GAAG;AAAA,UACL,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,kCAAkC;AAAA,QACjD;AAAA,MACF,OAAO;AACL,eAAO,MAAM,8BAA8B;AAAA,MAC7C;AAGA,UAAI,WAAW,iBAAiB,GAAG;AACjC,eAAO,MAAM,6BAA6B;AAC1C,cAAM,UAAU,MAAM,SAAS,mBAAmB,OAAO;AACzD,cAAM,UAAU,QAAQ,KAAK;AAC7B,YAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,8BAAoB;AACpB,iBAAO,MAAM,8BAA8B;AAAA,YACzC,eAAe,QAAQ;AAAA,YACvB,cAAc,MAAe;AAC3B,kBAAI;AACF,qBAAK,MAAM,OAAO;AAClB,uBAAO;AAAA,cACT,QAAQ;AACN,uBAAO;AAAA,cACT;AAAA,YACF,GAAG;AAAA,UACL,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,wCAAwC;AAAA,QACvD;AAAA,MACF,OAAO;AACL,eAAO,MAAM,oCAAoC;AAAA,MACnD;AAGA,aAAO,MAAM,0BAA0B;AAAA,QACrC,uBAAuB,CAAC,CAAC;AAAA,QACzB,iBAAiB,CAAC,CAAC;AAAA,QACnB,sBAAsB,CAAC,CAAC;AAAA,QACxB,0BAA0B,mBAAmB;AAAA,QAC7C,oBAAoB,aAAa;AAAA,QACjC,yBAAyB,kBAAkB;AAAA,MAC7C,CAAC;AAGD,aAAO,MAAM,6CAA6C;AAC1D,YAAM,QAAQ,KAAK,gBAAgB,YAAY;AAC/C,aAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC;AAEhD,UAAI,kBAAkB;AACtB,UAAI,qBAAqB;AAEzB,UAAI,UAAU,WAAW;AACvB,eAAO,MAAM,4CAA4C;AACzD,cAAM,cAAc,MAAM,KAAK,gBAAgB,qBAAqB,KAAK;AACzE,YAAI,aAAa;AACf,4BAAkB,YAAY;AAC9B,+BAAqB,YAAY;AACjC,iBAAO,MAAM,0CAA0C;AAAA,YACrD,MAAM;AAAA,YACN,eAAe,mBAAmB;AAAA,YAClC,cAAc,WAAW,eAAe;AAAA,YACxC,YAAY,mBAAmB,KAAK,EAAE,SAAS;AAAA,UACjD,CAAC;AAAA,QACH,OAAO;AACL,iBAAO,MAAM,6BAA6B;AAAA,QAC5C;AAAA,MACF,OAAO;AACL,eAAO,MAAM,8CAA8C;AAAA,MAC7D;AAEA,UAAI,UAAuB,CAAC;AAC5B,UAAI;AAEF,eAAO,MAAM,gDAAgD;AAC7D,kBAAU,MAAM,gBAAgB;AAChC,eAAO,MAAM,wBAAwB,EAAE,OAAO,QAAQ,QAAQ,QAAQ,CAAC;AAAA,MACzE,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,QAAQ;AACvD,eAAO,MAAM,6CAA6C,OAAO;AAAA,MACnE;AAGA,UAAI,cAAc;AAClB,UAAI,kBAAkB;AACtB,UAAI,eAAe;AACnB,UAAI,mBAAmB;AACvB,UAAI,kBAAkB;AACtB,UAAI,YAAY;AAEhB,UAAI,QAAQ,WAAW,GAAG;AACxB,oBAAY;AACZ,sBAAc;AAAA,MAChB,WAAW,QAAQ,WAAW,KAAK,QAAQ,CAAC,GAAG;AAC7C,uBAAe;AACf,2BAAmB,QAAQ,CAAC,EAAE;AAC9B,0BAAkB,QAAQ,CAAC,EAAE;AAC7B,sBAAc;AAAA,MAAyB,QAAQ,CAAC,EAAE,IAAI,OAAO,QAAQ,CAAC,EAAE,GAAG,KAAK,QAAQ,CAAC,EAAE,KAAK,IAAI,QAAQ,CAAC,EAAE,IAAI;AAAA,MACrH,OAAO;AACL,0BAAkB;AAClB,sBAAc,qBAAqB,QAAQ,MAAM;AAAA,IAC/C,QAAQ,IAAI,OAAK,OAAO,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,IAAI,GAAG,EAAE,KAAK,IAAI;AAAA,MAClF;AAGA,aAAO,KAAK,gCAAgC;AAC5C,YAAM,gBAAgB,MAAM,KAAK,kBAAkB;AACnD,aAAO,MAAM,yBAAyB;AAAA,QACpC,qBAAqB,cAAc;AAAA,MACrC,CAAC;AAGD,YAAM,YAAY;AAAA,QAChB,iBAAiB;AAAA,QACjB,sBAAsB;AAAA,QACtB,eAAe;AAAA,QACf,qBAAqB;AAAA,QACrB,YAAY;AAAA,QACZ,mBAAmB;AAAA,QACnB,sBAAsB;AAAA,QACtB,cAAc;AAAA,QACd,kBAAkB,gBAAgB,SAAS;AAAA,QAC3C,eAAe,aAAa,SAAS;AAAA,QACrC,oBAAoB;AAAA,QACpB,mBAAmB;AAAA,QACnB,YAAY,UAAU,SAAS;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAEA,aAAO,MAAM,+BAA+B;AAAA,QAC1C,cAAc,OAAO,KAAK,SAAS;AAAA,QACnC,qBAAqB,cAAc;AAAA,QACnC,0BAA0B,mBAAmB;AAAA,QAC7C,oBAAoB,aAAa;AAAA,QACjC,yBAAyB,kBAAkB;AAAA,MAC7C,CAAC;AAGD,aAAO,MAAM,8BAA8B;AAC3C,YAAM,SAAS,MAAM,KAAK,gBAAgB,UAAU,QAAQ,SAAS;AAErE,aAAO,MAAM,sBAAsB;AAAA,QACjC,cAAc,OAAO;AAAA,QACrB,gBAAgB,OAAO,SAAS,iBAAiB;AAAA,QACjD,0BAA0B,OAAO,SAAS,mBAAmB;AAAA,MAC/D,CAAC;AAED,YAAM,gBAAgB;AAAA,QACpB,UAAU;AAAA,QACV,oBAAoB;AAAA,QACpB,QAAQ,QAAQ,IAAI;AAAA,MACtB;AAEA,aAAO,MAAM,iCAAiC;AAAA,QAC5C,YAAY,OAAO,KAAK,aAAa;AAAA,QACrC,UAAU,cAAc;AAAA,QACxB,iBAAiB,CAAC,CAAC,cAAc;AAAA,QACjC,QAAQ,cAAc;AAAA,QACtB,cAAc,OAAO;AAAA,QACrB,yBAAyB,CAAC,CAAC;AAAA,MAC7B,CAAC;AAGD,YAAM,iBAAiB,wBAAwB;AAC/C,YAAM,aAAa,gBAAgB,aAAa;AAChD,aAAO,MAAM,0BAA0B;AAAA,IAEzC,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,aAAO,KAAK,gCAAgC,OAAO,EAAE;AACrD,aAAO,MAAM,oCAAoC,iBAAiB,QAAQ,MAAM,QAAQ,EAAC,MAAK,CAAC;AAC/F,aAAO,KAAK,gEAAgE;AAAA,IAC9E;AAEA,WAAO,MAAM,8BAA8B;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,oBAAqC;AACjD,QAAI;AAGF,UAAI,aAAa,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG5D,aAAO,eAAe,KAAK,QAAQ,UAAU,GAAG;AAC9C,cAAM,aAAa,KAAK,KAAK,YAAY,WAAW;AACpD,YAAI;AACF,gBAAM,UAAU,MAAM,SAAS,YAAY,OAAO;AAClD,iBAAO,MAAM,oCAAoC,EAAE,WAAW,CAAC;AAC/D,iBAAO;AAAA,QACT,QAAQ;AACN,uBAAa,KAAK,QAAQ,UAAU;AAAA,QACtC;AAAA,MACF;AAEA,aAAO,MAAM,6CAA6C;AAC1D,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,aAAO,MAAM,6BAA6B,KAAK,EAAE;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AAEF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/open.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { parseEnvFile, extractPort } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface OpenCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedOpenInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * OpenCommand - Opens workspace in browser or runs CLI tool\n * Priority: Web first, CLI fallback\n */\nexport class OpenCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: OpenCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (web first, CLI fallback)\n\t\tif (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree.path)\n\t\t} else if (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No web or CLI capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedOpenInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach open command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in open command')\n\t\t}\n\n\t\tconst result: ParsedOpenInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedOpenInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedOpenInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedOpenInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktreePath: string): Promise<void> {\n\t\tconst port = await this.getWorkspacePort(worktreePath)\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktreePath,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Get port for workspace - reads from .env or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from .env file (as override)\n\t\tconst envPath = path.join(worktreePath, '.env')\n\t\tif (await fs.pathExists(envPath)) {\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from .env: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in .env, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in .env, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n}\n"],"mappings":"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","names":["port"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/rebase.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { MergeManager } from '../lib/MergeManager.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { isValidGitRepo, getRepoRoot } from '../utils/git.js'\nimport type { MergeOptions } from '../types/index.js'\n\nexport interface RebaseOptions {\n\tforce?: boolean\n\tdryRun?: boolean\n}\n\n/**\n * Error thrown when the rebase command is run from an invalid location\n */\nexport class WorktreeValidationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly suggestion: string\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'WorktreeValidationError'\n\t}\n}\n\n/**\n * RebaseCommand: Rebase current branch on main with Claude-assisted conflict resolution\n *\n * This command:\n * 1. Validates the current directory is an iloom-managed worktree\n * 2. Detects the worktree root (supports running from subdirectories)\n * 3. Delegates to MergeManager.rebaseOnMain() which handles:\n * - Checking main branch exists\n * - Detecting uncommitted changes (throws if found)\n * - Checking if already up-to-date\n * - Executing rebase\n * - Claude-assisted conflict resolution\n * 4. Reports success\n */\nexport class RebaseCommand {\n\tprivate mergeManager: MergeManager\n\tprivate gitWorktreeManager: GitWorktreeManager\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(mergeManager?: MergeManager, gitWorktreeManager?: GitWorktreeManager, settingsManager?: SettingsManager) {\n\t\tthis.mergeManager = mergeManager ?? new MergeManager()\n\t\tthis.gitWorktreeManager = gitWorktreeManager ?? new GitWorktreeManager()\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t}\n\n\t/**\n\t * Validate that the current directory is within an iloom-managed worktree\n\t * Returns the worktree root path if valid\n\t * @throws WorktreeValidationError if validation fails\n\t */\n\tprivate async validateWorktreeContext(): Promise<string> {\n\t\tconst currentDir = process.cwd()\n\n\t\t// Step 1: Check if we're in a git repository at all\n\t\tconst isGitRepo = await isValidGitRepo(currentDir)\n\t\tif (!isGitRepo) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'Not a git repository.',\n\t\t\t\t\"Run 'il rebase' from within an iloom worktree created by 'il start'.\"\n\t\t\t)\n\t\t}\n\n\t\t// Step 2: Get the repository root (handles subdirectories)\n\t\tconst repoRoot = await getRepoRoot(currentDir)\n\t\tif (!repoRoot) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'Could not determine repository root.',\n\t\t\t\t\"Run 'il rebase' from within an iloom worktree created by 'il start'.\"\n\t\t\t)\n\t\t}\n\n\t\t// Step 3: Check if this path is a registered git worktree\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst currentWorktree = worktrees.find(wt => wt.path === repoRoot)\n\n\t\tif (!currentWorktree) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'This directory is not an iloom worktree.',\n\t\t\t\t\"Run 'il rebase' from within a worktree created by 'il start <issue>'. Use 'il list' to see available worktrees.\"\n\t\t\t)\n\t\t}\n\n\t\t// Step 4: Check if this is the main worktree (we shouldn't rebase from main)\n\t\tconst isMain = await this.gitWorktreeManager.isMainWorktree(currentWorktree, this.settingsManager)\n\t\tif (isMain) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'Cannot rebase from the main worktree.',\n\t\t\t\t\"Navigate to a feature worktree created by 'il start <issue>' and run 'il rebase' from there.\"\n\t\t\t)\n\t\t}\n\n\t\treturn repoRoot\n\t}\n\n\tasync execute(options: RebaseOptions = {}): Promise<void> {\n\t\t// Step 1: Validate we're in a valid iloom worktree\n\t\tlet worktreePath: string\n\t\ttry {\n\t\t\tworktreePath = await this.validateWorktreeContext()\n\t\t} catch (error) {\n\t\t\tif (error instanceof WorktreeValidationError) {\n\t\t\t\tlogger.error(error.message)\n\t\t\t\tlogger.info(error.suggestion)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\tconst mergeOptions: MergeOptions = {\n\t\t\tdryRun: options.dryRun ?? false,\n\t\t\tforce: options.force ?? false,\n\t\t}\n\n\t\t// MergeManager.rebaseOnMain() handles:\n\t\t// - Checking main branch exists\n\t\t// - Detecting uncommitted changes (throws if found)\n\t\t// - Checking if already up-to-date\n\t\t// - Executing rebase\n\t\t// - Claude-assisted conflict resolution\n\t\tawait this.mergeManager.rebaseOnMain(worktreePath, mergeOptions)\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAeO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EAClD,YACC,SACgB,YACf;AACD,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EACb;AACD;AAgBO,IAAM,gBAAN,MAAoB;AAAA,EAK1B,YAAY,cAA6B,oBAAyC,iBAAmC;AACpH,SAAK,eAAe,gBAAgB,IAAI,aAAa;AACrD,SAAK,qBAAqB,sBAAsB,IAAI,mBAAmB;AACvE,SAAK,kBAAkB,mBAAmB,IAAI,gBAAgB;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,0BAA2C;AACxD,UAAM,aAAa,QAAQ,IAAI;AAG/B,UAAM,YAAY,MAAM,eAAe,UAAU;AACjD,QAAI,CAAC,WAAW;AACf,YAAM,IAAI;AAAA,QACT;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAGA,UAAM,WAAW,MAAM,YAAY,UAAU;AAC7C,QAAI,CAAC,UAAU;AACd,YAAM,IAAI;AAAA,QACT;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAGA,UAAM,YAAY,MAAM,KAAK,mBAAmB,cAAc;AAC9D,UAAM,kBAAkB,UAAU,KAAK,QAAM,GAAG,SAAS,QAAQ;AAEjE,QAAI,CAAC,iBAAiB;AACrB,YAAM,IAAI;AAAA,QACT;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAGA,UAAM,SAAS,MAAM,KAAK,mBAAmB,eAAe,iBAAiB,KAAK,eAAe;AACjG,QAAI,QAAQ;AACX,YAAM,IAAI;AAAA,QACT;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,QAAQ,UAAyB,CAAC,GAAkB;AAEzD,QAAI;AACJ,QAAI;AACH,qBAAe,MAAM,KAAK,wBAAwB;AAAA,IACnD,SAAS,OAAO;AACf,UAAI,iBAAiB,yBAAyB;AAC7C,eAAO,MAAM,MAAM,OAAO;AAC1B,eAAO,KAAK,MAAM,UAAU;AAC5B,cAAM;AAAA,MACP;AACA,YAAM;AAAA,IACP;AAEA,UAAM,eAA6B;AAAA,MAClC,QAAQ,QAAQ,UAAU;AAAA,MAC1B,OAAO,QAAQ,SAAS;AAAA,IACzB;AAQA,UAAM,KAAK,aAAa,aAAa,cAAc,YAAY;AAAA,EAChE;AACD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/run.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { parseEnvFile, extractPort } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface RunCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedRunInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * RunCommand - Runs CLI tool or opens workspace in browser\n * Priority: CLI first, Web fallback\n */\nexport class RunCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: RunCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (CLI first, web fallback)\n\t\tif (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else if (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree.path)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No CLI or web capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedRunInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach run command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in run command')\n\t\t}\n\n\t\tconst result: ParsedRunInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedRunInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedRunInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedRunInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktreePath: string): Promise<void> {\n\t\tconst port = await this.getWorkspacePort(worktreePath)\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktreePath,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Get port for workspace - reads from .env or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from .env file (as override)\n\t\tconst envPath = path.join(worktreePath, '.env')\n\t\tif (await fs.pathExists(envPath)) {\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from .env: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in .env, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in .env, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n}\n"],"mappings":"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","names":["port"]}
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- detectITerm2,
4
- detectPlatform,
5
- openDualTerminalWindow,
6
- openMultipleTerminalWindows,
7
- openTerminalWindow
8
- } from "./chunk-ZE74H5BR.js";
9
- export {
10
- detectITerm2,
11
- detectPlatform,
12
- openDualTerminalWindow,
13
- openMultipleTerminalWindows,
14
- openTerminalWindow
15
- };
16
- //# sourceMappingURL=terminal-BIRBZ4AZ.js.map