@iloom/cli 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +159 -40
  3. package/dist/{BranchNamingService-K6XNWQ6C.js → BranchNamingService-ECJHBB67.js} +2 -2
  4. package/dist/ClaudeContextManager-QXX6ZFST.js +14 -0
  5. package/dist/ClaudeService-NJNK2SUH.js +13 -0
  6. package/dist/{GitHubService-TGWJN4V4.js → GitHubService-MEHKHUQP.js} +4 -4
  7. package/dist/IssueTrackerFactory-NG53YX5S.js +14 -0
  8. package/dist/{LoomLauncher-73NXL2CL.js → LoomLauncher-L64HHS3T.js} +9 -9
  9. package/dist/{MetadataManager-W3C54UYT.js → MetadataManager-5QZSTKNN.js} +2 -2
  10. package/dist/{ProjectCapabilityDetector-N5L7T4IY.js → ProjectCapabilityDetector-5KSYUTBJ.js} +3 -3
  11. package/dist/{PromptTemplateManager-36YLQRHP.js → PromptTemplateManager-DULSVRRE.js} +2 -2
  12. package/dist/README.md +159 -40
  13. package/dist/{SettingsManager-AW3JTJHD.js → SettingsManager-BQDQA3FK.js} +4 -2
  14. package/dist/agents/iloom-artifact-reviewer.md +11 -0
  15. package/dist/agents/iloom-code-reviewer.md +14 -0
  16. package/dist/agents/iloom-issue-analyze-and-plan.md +55 -12
  17. package/dist/agents/iloom-issue-analyzer.md +49 -6
  18. package/dist/agents/iloom-issue-complexity-evaluator.md +47 -6
  19. package/dist/agents/iloom-issue-enhancer.md +86 -7
  20. package/dist/agents/iloom-issue-implementer.md +48 -7
  21. package/dist/agents/iloom-issue-planner.md +115 -62
  22. package/dist/{build-THZI572G.js → build-5GO3XW26.js} +9 -9
  23. package/dist/{chunk-NUACL52E.js → chunk-3D7WQM7I.js} +2 -2
  24. package/dist/chunk-4232AHNQ.js +35 -0
  25. package/dist/chunk-4232AHNQ.js.map +1 -0
  26. package/dist/{chunk-QN47QVBX.js → chunk-4WJNIR5O.js} +1 -1
  27. package/dist/chunk-4WJNIR5O.js.map +1 -0
  28. package/dist/{chunk-A7NJF73J.js → chunk-5MWV33NN.js} +4 -4
  29. package/dist/{chunk-3I4ONZRT.js → chunk-6EU6TCF6.js} +10 -10
  30. package/dist/chunk-6EU6TCF6.js.map +1 -0
  31. package/dist/{chunk-CWRI4JC3.js → chunk-FB47TIJG.js} +29 -11
  32. package/dist/chunk-FB47TIJG.js.map +1 -0
  33. package/dist/chunk-HEXKPKCK.js +1396 -0
  34. package/dist/chunk-HEXKPKCK.js.map +1 -0
  35. package/dist/{chunk-KAYXR544.js → chunk-J5S7DFYC.js} +2 -2
  36. package/dist/{chunk-ULSWCPQG.js → chunk-JO2LZ6EQ.js} +476 -5
  37. package/dist/chunk-JO2LZ6EQ.js.map +1 -0
  38. package/dist/{chunk-KBEIQP4G.js → chunk-KB64WNBZ.js} +43 -3
  39. package/dist/chunk-KB64WNBZ.js.map +1 -0
  40. package/dist/{chunk-OFDN5NKS.js → chunk-KXDRI47U.js} +69 -12
  41. package/dist/chunk-KXDRI47U.js.map +1 -0
  42. package/dist/{chunk-R4YWBGY6.js → chunk-LXLMMXXY.js} +54 -14
  43. package/dist/chunk-LXLMMXXY.js.map +1 -0
  44. package/dist/{chunk-AR5QKYNE.js → chunk-MNHZB4Z2.js} +4 -4
  45. package/dist/{chunk-TL72BGP6.js → chunk-MORRVYPT.js} +2 -2
  46. package/dist/{chunk-KJTVU3HZ.js → chunk-NRSWLOAZ.js} +8 -8
  47. package/dist/chunk-NRSWLOAZ.js.map +1 -0
  48. package/dist/{chunk-FO5GGFOV.js → chunk-ONQYPICO.js} +13 -5
  49. package/dist/chunk-ONQYPICO.js.map +1 -0
  50. package/dist/{chunk-7ZEHSSUP.js → chunk-P4O6EH46.js} +4 -4
  51. package/dist/chunk-QZWEJVWV.js +207 -0
  52. package/dist/chunk-QZWEJVWV.js.map +1 -0
  53. package/dist/chunk-RSYT7MVI.js +202 -0
  54. package/dist/chunk-RSYT7MVI.js.map +1 -0
  55. package/dist/{chunk-Z2TWEXR7.js → chunk-RYWFS37M.js} +6 -6
  56. package/dist/chunk-RYWFS37M.js.map +1 -0
  57. package/dist/{chunk-B7U6OKUR.js → chunk-SF2P22EE.js} +11 -3
  58. package/dist/chunk-SF2P22EE.js.map +1 -0
  59. package/dist/{chunk-6IIL5M2L.js → chunk-SN3SQCFK.js} +10 -8
  60. package/dist/{chunk-6IIL5M2L.js.map → chunk-SN3SQCFK.js.map} +1 -1
  61. package/dist/{chunk-SOSQILHO.js → chunk-UD3WJDIV.js} +92 -82
  62. package/dist/chunk-UD3WJDIV.js.map +1 -0
  63. package/dist/{chunk-KXGQYLFZ.js → chunk-UKBAJ2QQ.js} +61 -7
  64. package/dist/chunk-UKBAJ2QQ.js.map +1 -0
  65. package/dist/{chunk-W6DP5RVR.js → chunk-UVD4CZKS.js} +3 -3
  66. package/dist/chunk-UWGVCXRF.js +207 -0
  67. package/dist/chunk-UWGVCXRF.js.map +1 -0
  68. package/dist/{chunk-NWMORW3U.js → chunk-VECNX6VX.js} +2 -2
  69. package/dist/{chunk-4CO6KG5S.js → chunk-VG45TUYK.js} +53 -7
  70. package/dist/{chunk-4CO6KG5S.js.map → chunk-VG45TUYK.js.map} +1 -1
  71. package/dist/{chunk-TC7APDKU.js → chunk-VGGST52X.js} +2 -2
  72. package/dist/{chunk-4LKGCFGG.js → chunk-WWKOVDWC.js} +2 -2
  73. package/dist/{chunk-YKFCCV6S.js → chunk-WY4QBK43.js} +7 -7
  74. package/dist/chunk-WY4QBK43.js.map +1 -0
  75. package/dist/chunk-Y4YZTHZE.js +73 -0
  76. package/dist/chunk-Y4YZTHZE.js.map +1 -0
  77. package/dist/{chunk-VOGGLPG5.js → chunk-YQ57ORTV.js} +14 -1
  78. package/dist/chunk-YQ57ORTV.js.map +1 -0
  79. package/dist/{chunk-RI2YL6TK.js → chunk-YYAKPQBT.js} +65 -18
  80. package/dist/chunk-YYAKPQBT.js.map +1 -0
  81. package/dist/{chunk-IZIYLYPK.js → chunk-ZEWU5PZK.js} +2 -2
  82. package/dist/{chunk-VPTAX5TR.js → chunk-ZHPNZC75.js} +12 -12
  83. package/dist/chunk-ZHPNZC75.js.map +1 -0
  84. package/dist/{chunk-DGG2VY7B.js → chunk-ZW2LKWWE.js} +9 -9
  85. package/dist/chunk-ZW2LKWWE.js.map +1 -0
  86. package/dist/{claude-TP2QO3BU.js → claude-P3NQR6IJ.js} +2 -2
  87. package/dist/{cleanup-PJRIFFU4.js → cleanup-6UCPVMFG.js} +81 -32
  88. package/dist/cleanup-6UCPVMFG.js.map +1 -0
  89. package/dist/cli.js +638 -349
  90. package/dist/cli.js.map +1 -1
  91. package/dist/{commit-IVP3M4HG.js → commit-L3EPY5QG.js} +21 -20
  92. package/dist/commit-L3EPY5QG.js.map +1 -0
  93. package/dist/{compile-R2J65HBQ.js → compile-ZS4HYRX5.js} +9 -9
  94. package/dist/{contribute-VDZXHK5Y.js → contribute-ORDDQGSL.js} +14 -6
  95. package/dist/contribute-ORDDQGSL.js.map +1 -0
  96. package/dist/{dev-server-7F622OEO.js → dev-server-FYZ2AQIH.js} +29 -15
  97. package/dist/dev-server-FYZ2AQIH.js.map +1 -0
  98. package/dist/{feedback-E7VET7CL.js → feedback-TMBXSCM5.js} +15 -15
  99. package/dist/{git-2QDQ2X2S.js → git-ET64COO3.js} +4 -4
  100. package/dist/hooks/iloom-hook.js +15 -0
  101. package/dist/ignite-CGOV3TD4.js +1393 -0
  102. package/dist/ignite-CGOV3TD4.js.map +1 -0
  103. package/dist/index.d.ts +382 -53
  104. package/dist/index.js +1167 -36
  105. package/dist/index.js.map +1 -1
  106. package/dist/{init-676DHF6R.js → init-GFQ5W7GK.js} +57 -21
  107. package/dist/init-GFQ5W7GK.js.map +1 -0
  108. package/dist/{issues-PJSOLOBJ.js → issues-T4ZZSPEG.js} +61 -20
  109. package/dist/issues-T4ZZSPEG.js.map +1 -0
  110. package/dist/{lint-CJM7BAIM.js → lint-6TQXDZ3T.js} +9 -9
  111. package/dist/mcp/issue-management-server.js +2471 -256
  112. package/dist/mcp/issue-management-server.js.map +1 -1
  113. package/dist/mcp/recap-server.js +144 -21
  114. package/dist/mcp/recap-server.js.map +1 -1
  115. package/dist/{neon-helpers-VVFFTLXE.js → neon-helpers-CQN2PB4S.js} +3 -3
  116. package/dist/neon-helpers-CQN2PB4S.js.map +1 -0
  117. package/dist/{open-544H7JF5.js → open-5QZGXQRF.js} +15 -15
  118. package/dist/open-5QZGXQRF.js.map +1 -0
  119. package/dist/{plan-Q7ELXDLC.js → plan-U7ZQWLFY.js} +41 -25
  120. package/dist/plan-U7ZQWLFY.js.map +1 -0
  121. package/dist/{projects-LH362JZQ.js → projects-2UOXFLNZ.js} +4 -4
  122. package/dist/prompts/CLAUDE.md +62 -0
  123. package/dist/prompts/init-prompt.txt +347 -26
  124. package/dist/prompts/issue-prompt.txt +427 -54
  125. package/dist/prompts/plan-prompt.txt +97 -16
  126. package/dist/prompts/pr-prompt.txt +44 -1
  127. package/dist/prompts/regular-prompt.txt +42 -1
  128. package/dist/prompts/session-summary-prompt.txt +14 -0
  129. package/dist/prompts/swarm-orchestrator-prompt.txt +437 -0
  130. package/dist/{rebase-YND35CIE.js → rebase-DWIB77KV.js} +10 -10
  131. package/dist/{recap-3W7COH7D.js → recap-MX63HAKV.js} +47 -19
  132. package/dist/recap-MX63HAKV.js.map +1 -0
  133. package/dist/{run-QUXJKDQQ.js → run-O3TFNQFC.js} +15 -15
  134. package/dist/run-O3TFNQFC.js.map +1 -0
  135. package/dist/schema/package-iloom.schema.json +58 -0
  136. package/dist/schema/settings.schema.json +115 -15
  137. package/dist/{shell-QGECBLST.js → shell-G6VC2CYR.js} +14 -7
  138. package/dist/shell-G6VC2CYR.js.map +1 -0
  139. package/dist/{summary-G2T4452H.js → summary-FWHAX55O.js} +27 -25
  140. package/dist/summary-FWHAX55O.js.map +1 -0
  141. package/dist/{test-EA5NQFDC.js → test-F7JNJZYP.js} +9 -9
  142. package/dist/{test-git-M7LSLEFL.js → test-git-BTAOIUE2.js} +4 -4
  143. package/dist/test-jira-CHYNV33F.js +96 -0
  144. package/dist/test-jira-CHYNV33F.js.map +1 -0
  145. package/dist/{test-prefix-64NAAUON.js → test-prefix-Q6TFSU6F.js} +4 -4
  146. package/dist/{test-webserver-OK6Z5FJM.js → test-webserver-EONCG7E7.js} +6 -6
  147. package/dist/{vscode-AR5NNXXI.js → vscode-VA5X4P25.js} +7 -7
  148. package/package.json +5 -1
  149. package/dist/ClaudeContextManager-HR5JQKAI.js +0 -14
  150. package/dist/ClaudeService-TK7FMC2X.js +0 -13
  151. package/dist/chunk-3I4ONZRT.js.map +0 -1
  152. package/dist/chunk-B7U6OKUR.js.map +0 -1
  153. package/dist/chunk-CWRI4JC3.js.map +0 -1
  154. package/dist/chunk-DGG2VY7B.js.map +0 -1
  155. package/dist/chunk-FJDRTVJX.js +0 -520
  156. package/dist/chunk-FJDRTVJX.js.map +0 -1
  157. package/dist/chunk-FO5GGFOV.js.map +0 -1
  158. package/dist/chunk-KBEIQP4G.js.map +0 -1
  159. package/dist/chunk-KJTVU3HZ.js.map +0 -1
  160. package/dist/chunk-KXGQYLFZ.js.map +0 -1
  161. package/dist/chunk-OFDN5NKS.js.map +0 -1
  162. package/dist/chunk-QN47QVBX.js.map +0 -1
  163. package/dist/chunk-R4YWBGY6.js.map +0 -1
  164. package/dist/chunk-RI2YL6TK.js.map +0 -1
  165. package/dist/chunk-SOSQILHO.js.map +0 -1
  166. package/dist/chunk-ULSWCPQG.js.map +0 -1
  167. package/dist/chunk-VOGGLPG5.js.map +0 -1
  168. package/dist/chunk-VPTAX5TR.js.map +0 -1
  169. package/dist/chunk-WHI5KEOX.js +0 -121
  170. package/dist/chunk-WHI5KEOX.js.map +0 -1
  171. package/dist/chunk-YKFCCV6S.js.map +0 -1
  172. package/dist/chunk-Z2TWEXR7.js.map +0 -1
  173. package/dist/cleanup-PJRIFFU4.js.map +0 -1
  174. package/dist/commit-IVP3M4HG.js.map +0 -1
  175. package/dist/contribute-VDZXHK5Y.js.map +0 -1
  176. package/dist/dev-server-7F622OEO.js.map +0 -1
  177. package/dist/ignite-IW35CDBD.js +0 -784
  178. package/dist/ignite-IW35CDBD.js.map +0 -1
  179. package/dist/init-676DHF6R.js.map +0 -1
  180. package/dist/issues-PJSOLOBJ.js.map +0 -1
  181. package/dist/open-544H7JF5.js.map +0 -1
  182. package/dist/plan-Q7ELXDLC.js.map +0 -1
  183. package/dist/recap-3W7COH7D.js.map +0 -1
  184. package/dist/run-QUXJKDQQ.js.map +0 -1
  185. package/dist/shell-QGECBLST.js.map +0 -1
  186. package/dist/summary-G2T4452H.js.map +0 -1
  187. /package/dist/{BranchNamingService-K6XNWQ6C.js.map → BranchNamingService-ECJHBB67.js.map} +0 -0
  188. /package/dist/{ClaudeContextManager-HR5JQKAI.js.map → ClaudeContextManager-QXX6ZFST.js.map} +0 -0
  189. /package/dist/{ClaudeService-TK7FMC2X.js.map → ClaudeService-NJNK2SUH.js.map} +0 -0
  190. /package/dist/{GitHubService-TGWJN4V4.js.map → GitHubService-MEHKHUQP.js.map} +0 -0
  191. /package/dist/{MetadataManager-W3C54UYT.js.map → IssueTrackerFactory-NG53YX5S.js.map} +0 -0
  192. /package/dist/{LoomLauncher-73NXL2CL.js.map → LoomLauncher-L64HHS3T.js.map} +0 -0
  193. /package/dist/{ProjectCapabilityDetector-N5L7T4IY.js.map → MetadataManager-5QZSTKNN.js.map} +0 -0
  194. /package/dist/{PromptTemplateManager-36YLQRHP.js.map → ProjectCapabilityDetector-5KSYUTBJ.js.map} +0 -0
  195. /package/dist/{SettingsManager-AW3JTJHD.js.map → PromptTemplateManager-DULSVRRE.js.map} +0 -0
  196. /package/dist/{claude-TP2QO3BU.js.map → SettingsManager-BQDQA3FK.js.map} +0 -0
  197. /package/dist/{build-THZI572G.js.map → build-5GO3XW26.js.map} +0 -0
  198. /package/dist/{chunk-NUACL52E.js.map → chunk-3D7WQM7I.js.map} +0 -0
  199. /package/dist/{chunk-A7NJF73J.js.map → chunk-5MWV33NN.js.map} +0 -0
  200. /package/dist/{chunk-KAYXR544.js.map → chunk-J5S7DFYC.js.map} +0 -0
  201. /package/dist/{chunk-AR5QKYNE.js.map → chunk-MNHZB4Z2.js.map} +0 -0
  202. /package/dist/{chunk-TL72BGP6.js.map → chunk-MORRVYPT.js.map} +0 -0
  203. /package/dist/{chunk-7ZEHSSUP.js.map → chunk-P4O6EH46.js.map} +0 -0
  204. /package/dist/{chunk-W6DP5RVR.js.map → chunk-UVD4CZKS.js.map} +0 -0
  205. /package/dist/{chunk-NWMORW3U.js.map → chunk-VECNX6VX.js.map} +0 -0
  206. /package/dist/{chunk-TC7APDKU.js.map → chunk-VGGST52X.js.map} +0 -0
  207. /package/dist/{chunk-4LKGCFGG.js.map → chunk-WWKOVDWC.js.map} +0 -0
  208. /package/dist/{chunk-IZIYLYPK.js.map → chunk-ZEWU5PZK.js.map} +0 -0
  209. /package/dist/{git-2QDQ2X2S.js.map → claude-P3NQR6IJ.js.map} +0 -0
  210. /package/dist/{compile-R2J65HBQ.js.map → compile-ZS4HYRX5.js.map} +0 -0
  211. /package/dist/{feedback-E7VET7CL.js.map → feedback-TMBXSCM5.js.map} +0 -0
  212. /package/dist/{neon-helpers-VVFFTLXE.js.map → git-ET64COO3.js.map} +0 -0
  213. /package/dist/{lint-CJM7BAIM.js.map → lint-6TQXDZ3T.js.map} +0 -0
  214. /package/dist/{projects-LH362JZQ.js.map → projects-2UOXFLNZ.js.map} +0 -0
  215. /package/dist/{rebase-YND35CIE.js.map → rebase-DWIB77KV.js.map} +0 -0
  216. /package/dist/{test-EA5NQFDC.js.map → test-F7JNJZYP.js.map} +0 -0
  217. /package/dist/{test-git-M7LSLEFL.js.map → test-git-BTAOIUE2.js.map} +0 -0
  218. /package/dist/{test-prefix-64NAAUON.js.map → test-prefix-Q6TFSU6F.js.map} +0 -0
  219. /package/dist/{test-webserver-OK6Z5FJM.js.map → test-webserver-EONCG7E7.js.map} +0 -0
  220. /package/dist/{vscode-AR5NNXXI.js.map → vscode-VA5X4P25.js.map} +0 -0
@@ -2,18 +2,18 @@
2
2
  import {
3
3
  extractIssueNumber,
4
4
  extractPRNumber
5
- } from "./chunk-AR5QKYNE.js";
5
+ } from "./chunk-MNHZB4Z2.js";
6
6
 
7
7
  // src/utils/IdentifierParser.ts
8
8
  function matchIssueIdentifier(input) {
9
9
  const trimmed = input.trim();
10
- const linearPattern = /^([A-Z]{2,}-\d+)$/i;
11
- const linearMatch = trimmed.match(linearPattern);
12
- if (linearMatch == null ? void 0 : linearMatch[1]) {
10
+ const projectKeyPattern = /^([A-Z]{2,}-\d+)$/i;
11
+ const projectKeyMatch = trimmed.match(projectKeyPattern);
12
+ if (projectKeyMatch == null ? void 0 : projectKeyMatch[1]) {
13
13
  return {
14
14
  isIssueIdentifier: true,
15
- type: "linear",
16
- identifier: linearMatch[1].toUpperCase()
15
+ type: "project-key",
16
+ identifier: projectKeyMatch[1].toUpperCase()
17
17
  };
18
18
  }
19
19
  const numericPattern = /^#?(\d+)$/;
@@ -108,4 +108,4 @@ export {
108
108
  matchIssueIdentifier,
109
109
  IdentifierParser
110
110
  };
111
- //# sourceMappingURL=chunk-YKFCCV6S.js.map
111
+ //# sourceMappingURL=chunk-WY4QBK43.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/IdentifierParser.ts"],"sourcesContent":["import type { ParsedInput } from '../commands/start.js'\nimport type { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { extractIssueNumber, extractPRNumber } from './git.js'\n\n/**\n * Result of parsing an issue identifier input\n */\nexport interface IssueIdentifierMatch {\n /** Whether the input matches an issue identifier pattern */\n isIssueIdentifier: boolean\n /** The type of identifier: 'numeric' (GitHub) or 'project-key' (project key format, e.g., Linear, Jira) */\n type?: 'numeric' | 'project-key'\n /** The extracted identifier (without # prefix for numeric) */\n identifier?: string\n}\n\n/**\n * Check if a string looks like an issue identifier\n *\n * Matches:\n * - Numeric patterns: \"123\", \"#123\" (GitHub format)\n * - Project key patterns: \"ENG-123\", \"PLAT-456\" (requires at least 2 letters before dash)\n *\n * This is a pure pattern match - it does NOT validate that the issue exists.\n * Use IssueTracker.detectInputType() to validate existence.\n *\n * @param input - The input string to check\n * @returns Object with isIssueIdentifier flag and optional type/identifier\n */\nexport function matchIssueIdentifier(input: string): IssueIdentifierMatch {\n const trimmed = input.trim()\n\n // Check for project key identifier format (TEAM-NUMBER, e.g., ENG-123, PLAT-456, PROJ-789)\n // Requires at least 2 letters before dash to avoid conflict with PR-123 format\n const projectKeyPattern = /^([A-Z]{2,}-\\d+)$/i\n const projectKeyMatch = trimmed.match(projectKeyPattern)\n if (projectKeyMatch?.[1]) {\n return {\n isIssueIdentifier: true,\n type: 'project-key',\n identifier: projectKeyMatch[1].toUpperCase(),\n }\n }\n\n // Check for numeric pattern (GitHub format: 123 or #123)\n const numericPattern = /^#?(\\d+)$/\n const numericMatch = trimmed.match(numericPattern)\n if (numericMatch?.[1]) {\n return {\n isIssueIdentifier: true,\n type: 'numeric',\n identifier: numericMatch[1],\n }\n }\n\n return { isIssueIdentifier: false }\n}\n\n/**\n * IdentifierParser provides consistent identifier parsing across commands\n * using pattern-based detection without GitHub API calls.\n *\n * Detection Strategy:\n * 1. For numeric input (e.g., \"42\", \"#66\"):\n * - Check for PR worktree first (_pr_N pattern in path)\n * - Then check for issue worktree (issue-N pattern in branch)\n * 2. For alphanumeric input (e.g., \"ENG-123\"):\n * - Check for issue worktree with alphanumeric identifier\n * 3. For branch-style input (e.g., \"feat/issue-42__description\", \"pr/123\"):\n * - Find matching worktree by branch name\n * - Extract PR number from branch name if present (priority)\n * - Extract issue number from branch name if present\n * - Return as PR/issue type if number found, otherwise branch type\n *\n * This ensures:\n * - No unnecessary GitHub API calls\n * - Consistent behavior across finish/cleanup commands\n * - PR detection takes priority over issue detection\n * - Issue numbers are extracted from branch names for \"Fixes #N\" commit trailers\n */\nexport class IdentifierParser {\n\tconstructor(private gitWorktreeManager: GitWorktreeManager) {}\n\n\t/**\n\t * Parse identifier using pattern-based detection on existing worktrees.\n\t * Does NOT make GitHub API calls - only checks local worktree patterns.\n\t *\n\t * @param identifier - The identifier to parse (e.g., \"42\", \"#66\", \"ENG-123\", \"my-branch\")\n\t * @returns ParsedInput with type, number/branchName, and originalInput\n\t * @throws Error if no matching worktree is found\n\t */\n\tasync parseForPatternDetection(identifier: string): Promise<ParsedInput> {\n\t\t// Remove # prefix if present and trim whitespace\n\t\tconst cleanId = identifier.replace(/^#/, '').trim()\n\t\tconst originalInput = identifier\n\n\t\t// Check if input is numeric (GitHub-style issue/PR numbers)\n\t\tconst numericMatch = cleanId.match(/^(\\d+)$/)\n\n\t\tif (numericMatch?.[1]) {\n\t\t\tconst number = parseInt(numericMatch[1], 10)\n\n\t\t\t// Priority 1: Check for PR worktree (_pr_N pattern)\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tconst prWorktree = await this.gitWorktreeManager.findWorktreeForPR(number, '')\n\t\t\tif (prWorktree) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'pr',\n\t\t\t\t\tnumber,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Priority 2: Check for issue worktree (issue-N pattern)\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(number)\n\t\t\tif (issueWorktree) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No matching worktree found for numeric input\n\t\t\tthrow new Error(`No worktree found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check if input is alphanumeric issue identifier (Linear/Jira-style: ABC-123, ENG-42)\n\t\tconst alphanumericMatch = cleanId.match(/^([A-Za-z]+-\\d+)$/)\n\n\t\tif (alphanumericMatch?.[1]) {\n\t\t\tconst alphanumericId = alphanumericMatch[1]\n\n\t\t\t// Check for issue worktree with alphanumeric identifier\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(alphanumericId)\n\t\t\tif (issueWorktree) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber: alphanumericId,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No matching worktree found for alphanumeric identifier\n\t\t\tthrow new Error(`No worktree found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Non-numeric/non-alphanumeric input: treat as branch name\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(cleanId)\n\t\tif (branchWorktree) {\n\t\t\t// Priority 1: Check for PR pattern in the input\n\t\t\tconst prFromBranch = extractPRNumber(cleanId)\n\t\t\tif (prFromBranch !== null) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'pr',\n\t\t\t\t\tnumber: prFromBranch,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Priority 2: Try to extract issue number from branch name\n\t\t\t// This handles cases like \"feat/issue-42__description\" passed as explicit input\n\t\t\tconst issueFromBranch = extractIssueNumber(cleanId)\n\t\t\tif (issueFromBranch !== null) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber: issueFromBranch,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: 'branch',\n\t\t\t\tbranchName: cleanId,\n\t\t\t\toriginalInput,\n\t\t\t}\n\t\t}\n\n\t\t// No matching worktree found for branch name\n\t\tthrow new Error(`No worktree found for identifier: ${identifier}`)\n\t}\n}\n"],"mappings":";;;;;;;AA6BO,SAAS,qBAAqB,OAAqC;AACxE,QAAM,UAAU,MAAM,KAAK;AAI3B,QAAM,oBAAoB;AAC1B,QAAM,kBAAkB,QAAQ,MAAM,iBAAiB;AACvD,MAAI,mDAAkB,IAAI;AACxB,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,MAAM;AAAA,MACN,YAAY,gBAAgB,CAAC,EAAE,YAAY;AAAA,IAC7C;AAAA,EACF;AAGA,QAAM,iBAAiB;AACvB,QAAM,eAAe,QAAQ,MAAM,cAAc;AACjD,MAAI,6CAAe,IAAI;AACrB,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,MAAM;AAAA,MACN,YAAY,aAAa,CAAC;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,EAAE,mBAAmB,MAAM;AACpC;AAwBO,IAAM,mBAAN,MAAuB;AAAA,EAC7B,YAAoB,oBAAwC;AAAxC;AAAA,EAAyC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7D,MAAM,yBAAyB,YAA0C;AAExE,UAAM,UAAU,WAAW,QAAQ,MAAM,EAAE,EAAE,KAAK;AAClD,UAAM,gBAAgB;AAGtB,UAAM,eAAe,QAAQ,MAAM,SAAS;AAE5C,QAAI,6CAAe,IAAI;AACtB,YAAM,SAAS,SAAS,aAAa,CAAC,GAAG,EAAE;AAI3C,YAAM,aAAa,MAAM,KAAK,mBAAmB,kBAAkB,QAAQ,EAAE;AAC7E,UAAI,YAAY;AACf,eAAO;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA;AAAA,QACD;AAAA,MACD;AAGA,YAAM,gBAAgB,MAAM,KAAK,mBAAmB,qBAAqB,MAAM;AAC/E,UAAI,eAAe;AAClB,eAAO;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA;AAAA,QACD;AAAA,MACD;AAGA,YAAM,IAAI,MAAM,qCAAqC,UAAU,EAAE;AAAA,IAClE;AAGA,UAAM,oBAAoB,QAAQ,MAAM,mBAAmB;AAE3D,QAAI,uDAAoB,IAAI;AAC3B,YAAM,iBAAiB,kBAAkB,CAAC;AAG1C,YAAM,gBAAgB,MAAM,KAAK,mBAAmB,qBAAqB,cAAc;AACvF,UAAI,eAAe;AAClB,eAAO;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AAGA,YAAM,IAAI,MAAM,qCAAqC,UAAU,EAAE;AAAA,IAClE;AAGA,UAAM,iBAAiB,MAAM,KAAK,mBAAmB,sBAAsB,OAAO;AAClF,QAAI,gBAAgB;AAEnB,YAAM,eAAe,gBAAgB,OAAO;AAC5C,UAAI,iBAAiB,MAAM;AAC1B,eAAO;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AAIA,YAAM,kBAAkB,mBAAmB,OAAO;AAClD,UAAI,oBAAoB,MAAM;AAC7B,eAAO;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AAEA,aAAO;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,QACZ;AAAA,MACD;AAAA,IACD;AAGA,UAAM,IAAI,MAAM,qCAAqC,UAAU,EAAE;AAAA,EAClE;AACD;","names":[]}
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ findMainWorktreePathWithSettings
4
+ } from "./chunk-MNHZB4Z2.js";
5
+ import {
6
+ MetadataManager
7
+ } from "./chunk-KB64WNBZ.js";
8
+ import {
9
+ getLogger
10
+ } from "./chunk-6MLEBAYZ.js";
11
+
12
+ // src/utils/recap-archiver.ts
13
+ import path from "path";
14
+ import os from "os";
15
+ import fs from "fs-extra";
16
+ var RECAPS_DIR = path.join(os.homedir(), ".config", "iloom-ai", "recaps");
17
+ var ARCHIVED_DIR = path.join(RECAPS_DIR, "archived");
18
+ function slugifyPath(loomPath) {
19
+ let slug = loomPath.replace(/[/\\]+$/, "");
20
+ slug = slug.replace(/[/\\]/g, "___");
21
+ slug = slug.replace(/[^a-zA-Z0-9_-]/g, "-");
22
+ return `${slug}.json`;
23
+ }
24
+ async function archiveRecap(worktreePath) {
25
+ const filename = slugifyPath(worktreePath);
26
+ const sourcePath = path.join(RECAPS_DIR, filename);
27
+ if (!await fs.pathExists(sourcePath)) {
28
+ getLogger().debug(`No recap file to archive for worktree: ${worktreePath}`);
29
+ return;
30
+ }
31
+ const content = await fs.readFile(sourcePath, "utf8");
32
+ const data = JSON.parse(content);
33
+ const archivedData = {
34
+ ...data,
35
+ archivedAt: (/* @__PURE__ */ new Date()).toISOString()
36
+ };
37
+ await fs.ensureDir(ARCHIVED_DIR, { mode: 493 });
38
+ const destPath = path.join(ARCHIVED_DIR, filename);
39
+ await fs.writeFile(destPath, JSON.stringify(archivedData, null, 2), { mode: 420 });
40
+ await fs.unlink(sourcePath);
41
+ getLogger().debug(`Recap archived for worktree: ${worktreePath}`);
42
+ }
43
+ async function findArchivedRecap(type, number) {
44
+ try {
45
+ const currentProjectPath = await findMainWorktreePathWithSettings();
46
+ const metadataManager = new MetadataManager();
47
+ const finishedLooms = await metadataManager.listFinishedMetadata();
48
+ const numberStr = String(number);
49
+ const match = finishedLooms.find((loom) => {
50
+ var _a, _b;
51
+ if (!loom.projectPath || loom.projectPath !== currentProjectPath) return false;
52
+ if (!loom.worktreePath) return false;
53
+ if (type === "issue") return ((_a = loom.issue_numbers) == null ? void 0 : _a.includes(numberStr)) ?? false;
54
+ if (type === "pr") return ((_b = loom.pr_numbers) == null ? void 0 : _b.includes(numberStr)) ?? false;
55
+ return false;
56
+ });
57
+ if (!(match == null ? void 0 : match.worktreePath)) return null;
58
+ const archivedPath = path.join(ARCHIVED_DIR, slugifyPath(match.worktreePath));
59
+ if (await fs.pathExists(archivedPath)) {
60
+ return archivedPath;
61
+ }
62
+ return null;
63
+ } catch (error) {
64
+ getLogger().debug(`Failed to find archived recap: ${error instanceof Error ? error.message : String(error)}`);
65
+ return null;
66
+ }
67
+ }
68
+
69
+ export {
70
+ archiveRecap,
71
+ findArchivedRecap
72
+ };
73
+ //# sourceMappingURL=chunk-Y4YZTHZE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/recap-archiver.ts"],"sourcesContent":["/**\n * RecapArchiver - Archive recap files during cleanup/finish operations\n *\n * Follows the MetadataManager.archiveMetadata() pattern:\n * - Move recap file to archived/ subdirectory\n * - Add archivedAt timestamp\n * - Idempotent: silently succeeds if file doesn't exist\n * - Non-fatal: logs warning on errors but doesn't throw\n */\nimport path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport { getLogger } from './logger-context.js'\nimport type { RecapFile } from '../mcp/recap-types.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { findMainWorktreePathWithSettings } from './git.js'\n\nconst RECAPS_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\nconst ARCHIVED_DIR = path.join(RECAPS_DIR, 'archived')\n\n/**\n * Convert worktree path to filename slug\n * Uses the same algorithm as MetadataManager.slugifyPath() and RecapCommand\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with ___ (triple underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n */\nfunction slugifyPath(loomPath: string): string {\n\tlet slug = loomPath.replace(/[/\\\\]+$/, '')\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\treturn `${slug}.json`\n}\n\n/**\n * Archive recap file for a finished/cleaned up worktree\n *\n * Moves the recap file to the archived/ subdirectory and adds\n * an archivedAt timestamp field.\n *\n * Idempotent: silently succeeds if source file doesn't exist\n * Throws on errors: caller (ResourceCleanup.ts) handles errors as non-fatal\n *\n * @param worktreePath - Absolute path to the worktree\n */\nexport async function archiveRecap(worktreePath: string): Promise<void> {\n\tconst filename = slugifyPath(worktreePath)\n\tconst sourcePath = path.join(RECAPS_DIR, filename)\n\n\t// Check if source file exists - silently return if not (idempotent)\n\tif (!(await fs.pathExists(sourcePath))) {\n\t\tgetLogger().debug(`No recap file to archive for worktree: ${worktreePath}`)\n\t\treturn\n\t}\n\n\t// Read existing recap content\n\tconst content = await fs.readFile(sourcePath, 'utf8')\n\tconst data: RecapFile = JSON.parse(content)\n\n\t// Add archived timestamp\n\tconst archivedData = {\n\t\t...data,\n\t\tarchivedAt: new Date().toISOString(),\n\t}\n\n\t// Ensure archived directory exists\n\tawait fs.ensureDir(ARCHIVED_DIR, { mode: 0o755 })\n\n\t// Write to archived subdirectory\n\tconst destPath = path.join(ARCHIVED_DIR, filename)\n\tawait fs.writeFile(destPath, JSON.stringify(archivedData, null, 2), { mode: 0o644 })\n\n\t// Delete original file\n\tawait fs.unlink(sourcePath)\n\n\tgetLogger().debug(`Recap archived for worktree: ${worktreePath}`)\n}\n\n/**\n * Find an archived recap file by issue or PR number.\n * Uses finished metadata to filter by project and derive the recap filename.\n *\n * Approach:\n * 1. Get current project's main worktree path via findMainWorktreePathWithSettings()\n * 2. List all finished metadata via MetadataManager.listFinishedMetadata()\n * 3. Filter by projectPath matching current project\n * 4. Filter by issue_numbers or pr_numbers containing the target number\n * 5. From matching metadata, get worktreePath and derive archived recap filename\n * 6. Check if that file exists in ARCHIVED_DIR\n * 7. Return the path if found, null otherwise\n */\nexport async function findArchivedRecap(\n\ttype: 'issue' | 'pr',\n\tnumber: number\n): Promise<string | null> {\n\ttry {\n\t\tconst currentProjectPath = await findMainWorktreePathWithSettings()\n\n\t\tconst metadataManager = new MetadataManager()\n\t\tconst finishedLooms = await metadataManager.listFinishedMetadata()\n\n\t\tconst numberStr = String(number)\n\t\tconst match = finishedLooms.find(loom => {\n\t\t\t// Skip legacy metadata without projectPath\n\t\t\tif (!loom.projectPath || loom.projectPath !== currentProjectPath) return false\n\t\t\t// Must have worktreePath to derive filename\n\t\t\tif (!loom.worktreePath) return false\n\t\t\t// Match by issue or PR number\n\t\t\tif (type === 'issue') return loom.issue_numbers?.includes(numberStr) ?? false\n\t\t\tif (type === 'pr') return loom.pr_numbers?.includes(numberStr) ?? false\n\t\t\treturn false\n\t\t})\n\n\t\tif (!match?.worktreePath) return null\n\n\t\tconst archivedPath = path.join(ARCHIVED_DIR, slugifyPath(match.worktreePath))\n\t\tif (await fs.pathExists(archivedPath)) {\n\t\t\treturn archivedPath\n\t\t}\n\n\t\treturn null\n\t} catch (error) {\n\t\tgetLogger().debug(`Failed to find archived recap: ${error instanceof Error ? error.message : String(error)}`)\n\t\treturn null\n\t}\n}\n\n// Export for testing\nexport { RECAPS_DIR, ARCHIVED_DIR, slugifyPath }\n"],"mappings":";;;;;;;;;;;;AASA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,QAAQ;AAMf,IAAM,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY,QAAQ;AAC1E,IAAM,eAAe,KAAK,KAAK,YAAY,UAAU;AAYrD,SAAS,YAAY,UAA0B;AAC9C,MAAI,OAAO,SAAS,QAAQ,WAAW,EAAE;AACzC,SAAO,KAAK,QAAQ,UAAU,KAAK;AACnC,SAAO,KAAK,QAAQ,mBAAmB,GAAG;AAC1C,SAAO,GAAG,IAAI;AACf;AAaA,eAAsB,aAAa,cAAqC;AACvE,QAAM,WAAW,YAAY,YAAY;AACzC,QAAM,aAAa,KAAK,KAAK,YAAY,QAAQ;AAGjD,MAAI,CAAE,MAAM,GAAG,WAAW,UAAU,GAAI;AACvC,cAAU,EAAE,MAAM,0CAA0C,YAAY,EAAE;AAC1E;AAAA,EACD;AAGA,QAAM,UAAU,MAAM,GAAG,SAAS,YAAY,MAAM;AACpD,QAAM,OAAkB,KAAK,MAAM,OAAO;AAG1C,QAAM,eAAe;AAAA,IACpB,GAAG;AAAA,IACH,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AAGA,QAAM,GAAG,UAAU,cAAc,EAAE,MAAM,IAAM,CAAC;AAGhD,QAAM,WAAW,KAAK,KAAK,cAAc,QAAQ;AACjD,QAAM,GAAG,UAAU,UAAU,KAAK,UAAU,cAAc,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAGnF,QAAM,GAAG,OAAO,UAAU;AAE1B,YAAU,EAAE,MAAM,gCAAgC,YAAY,EAAE;AACjE;AAeA,eAAsB,kBACrB,MACA,QACyB;AACzB,MAAI;AACH,UAAM,qBAAqB,MAAM,iCAAiC;AAElE,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,gBAAgB,MAAM,gBAAgB,qBAAqB;AAEjE,UAAM,YAAY,OAAO,MAAM;AAC/B,UAAM,QAAQ,cAAc,KAAK,UAAQ;AAzG3C;AA2GG,UAAI,CAAC,KAAK,eAAe,KAAK,gBAAgB,mBAAoB,QAAO;AAEzE,UAAI,CAAC,KAAK,aAAc,QAAO;AAE/B,UAAI,SAAS,QAAS,UAAO,UAAK,kBAAL,mBAAoB,SAAS,eAAc;AACxE,UAAI,SAAS,KAAM,UAAO,UAAK,eAAL,mBAAiB,SAAS,eAAc;AAClE,aAAO;AAAA,IACR,CAAC;AAED,QAAI,EAAC,+BAAO,cAAc,QAAO;AAEjC,UAAM,eAAe,KAAK,KAAK,cAAc,YAAY,MAAM,YAAY,CAAC;AAC5E,QAAI,MAAM,GAAG,WAAW,YAAY,GAAG;AACtC,aAAO;AAAA,IACR;AAEA,WAAO;AAAA,EACR,SAAS,OAAO;AACf,cAAU,EAAE,MAAM,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAC5G,WAAO;AAAA,EACR;AACD;","names":[]}
@@ -6,8 +6,21 @@ import {
6
6
  // src/utils/package-json.ts
7
7
  import fs from "fs-extra";
8
8
  import path from "path";
9
+ import { z } from "zod";
9
10
  var ILOOM_PACKAGE_PATH = ".iloom/package.iloom.json";
10
11
  var ILOOM_PACKAGE_LOCAL_PATH = ".iloom/package.iloom.local.json";
12
+ var PackageIloomSchema = z.object({
13
+ capabilities: z.array(z.enum(["cli", "web"])).optional().describe('Project capabilities - "cli" for command-line tools (enables CLI isolation), "web" for web applications (enables port assignment and dev server)'),
14
+ scripts: z.object({
15
+ install: z.string().optional().describe('Install command (e.g., "bundle install", "poetry install")'),
16
+ build: z.string().optional().describe("Build/compile command"),
17
+ test: z.string().optional().describe("Test suite command"),
18
+ dev: z.string().optional().describe("Dev server command"),
19
+ lint: z.string().optional().describe("Linting command"),
20
+ typecheck: z.string().optional().describe("Type checking command"),
21
+ compile: z.string().optional().describe("Compilation command (preferred over typecheck if both exist)")
22
+ }).optional().describe("Custom shell commands for project operations. These are raw shell commands, not npm script names.")
23
+ });
11
24
  async function readPackageJson(dir) {
12
25
  const pkgPath = path.join(dir, "package.json");
13
26
  try {
@@ -147,4 +160,4 @@ export {
147
160
  getPackageScripts,
148
161
  getExplicitCapabilities
149
162
  };
150
- //# sourceMappingURL=chunk-VOGGLPG5.js.map
163
+ //# sourceMappingURL=chunk-YQ57ORTV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/package-json.ts"],"sourcesContent":["import fs from 'fs-extra'\nimport path from 'path'\nimport { z } from 'zod'\nimport { getLogger } from './logger-context.js'\nimport type { ProjectCapability } from '../types/loom.js'\n\n/**\n * Path to the iloom package configuration file (relative to project root)\n * This file allows non-Node.js projects to define scripts for iloom workflows\n */\nexport const ILOOM_PACKAGE_PATH = '.iloom/package.iloom.json'\nexport const ILOOM_PACKAGE_LOCAL_PATH = '.iloom/package.iloom.local.json'\n\n/**\n * Zod schema for package.iloom.json / package.iloom.local.json\n * Defines project capabilities and custom shell commands for non-Node.js projects\n */\nexport const PackageIloomSchema = z.object({\n capabilities: z.array(z.enum(['cli', 'web'])).optional()\n .describe('Project capabilities - \"cli\" for command-line tools (enables CLI isolation), \"web\" for web applications (enables port assignment and dev server)'),\n scripts: z.object({\n install: z.string().optional().describe('Install command (e.g., \"bundle install\", \"poetry install\")'),\n build: z.string().optional().describe('Build/compile command'),\n test: z.string().optional().describe('Test suite command'),\n dev: z.string().optional().describe('Dev server command'),\n lint: z.string().optional().describe('Linting command'),\n typecheck: z.string().optional().describe('Type checking command'),\n compile: z.string().optional().describe('Compilation command (preferred over typecheck if both exist)'),\n }).optional().describe('Custom shell commands for project operations. These are raw shell commands, not npm script names.'),\n})\n\nexport interface PackageJson {\n name: string\n version?: string\n bin?: string | Record<string, string>\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n capabilities?: ProjectCapability[]\n [key: string]: unknown\n}\n\n/**\n * Source of a script - determines how it should be executed\n * - 'package-manager': Execute via package manager (pnpm/npm/yarn)\n * - 'iloom-config': Execute directly as shell command\n */\nexport type ScriptSource = 'package-manager' | 'iloom-config'\n\n/**\n * Configuration for a single script including its source\n * The source determines whether to use package manager or direct shell execution\n */\nexport interface PackageScriptConfig {\n /** The script command to execute */\n command: string\n /** Source of the script - determines execution method */\n source: ScriptSource\n}\n\n/**\n * Read and parse package.json from a directory\n * @param dir Directory containing package.json\n * @returns Parsed package.json object\n * @throws Error if package.json doesn't exist or contains invalid JSON\n */\nexport async function readPackageJson(dir: string): Promise<PackageJson> {\n const pkgPath = path.join(dir, 'package.json')\n\n try {\n const pkgJson = await fs.readJson(pkgPath)\n return pkgJson as PackageJson\n } catch (error) {\n if ((error as { code?: string }).code === 'ENOENT') {\n throw new Error(`package.json not found in ${dir}`)\n }\n const message = error instanceof Error ? error.message : 'Unknown error'\n throw new Error(`Invalid package.json in ${dir}: ${message}`)\n }\n}\n\n/**\n * Read scripts from .iloom/package.iloom.json if it exists, merged with\n * .iloom/package.iloom.local.json (local takes precedence).\n * These files take precedence over package.json and contain raw shell commands.\n * @param dir Directory containing .iloom/package.iloom.json\n * @returns PackageJson-like object with scripts, or null if neither file exists\n */\nexport async function readIloomPackageScripts(dir: string): Promise<PackageJson | null> {\n const iloomPkgPath = path.join(dir, ILOOM_PACKAGE_PATH)\n const localPkgPath = path.join(dir, ILOOM_PACKAGE_LOCAL_PATH)\n\n // Read base package.iloom.json\n let baseConfig: PackageJson | null = null\n try {\n const exists = await fs.pathExists(iloomPkgPath)\n if (exists) {\n baseConfig = await fs.readJson(iloomPkgPath) as PackageJson\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n getLogger().warn(`Failed to read ${ILOOM_PACKAGE_PATH}: ${message}`)\n }\n\n // Read local override if exists\n let localConfig: PackageJson | null = null\n try {\n const localExists = await fs.pathExists(localPkgPath)\n if (localExists) {\n localConfig = await fs.readJson(localPkgPath) as PackageJson\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error'\n getLogger().warn(`Failed to read ${ILOOM_PACKAGE_LOCAL_PATH}: ${message}`)\n }\n\n // Merge: local scripts override base scripts\n if (baseConfig && localConfig) {\n return {\n ...baseConfig,\n scripts: { ...baseConfig.scripts, ...localConfig.scripts },\n ...(localConfig.capabilities && { capabilities: localConfig.capabilities }),\n }\n }\n\n // Return whichever exists (or null if neither)\n return localConfig ?? baseConfig\n}\n\n/**\n * Read package configuration for a project, merging .iloom/package.iloom.json scripts over package.json\n * This allows non-Node.js projects to define scripts for iloom workflows while preserving\n * all other package.json fields (name, version, bin, dependencies, etc.)\n *\n * @param dir Directory to read package configuration from\n * @returns PackageJson object with merged scripts (iloom scripts take precedence)\n * @throws Error if neither file exists or contains valid JSON\n */\nexport async function getPackageConfig(dir: string): Promise<PackageJson> {\n // Check for .iloom/package.iloom.json first\n const iloomPackage = await readIloomPackageScripts(dir)\n\n if (iloomPackage) {\n // Try to read package.json as base\n try {\n const basePackage = await readPackageJson(dir)\n getLogger().debug('Merging scripts from .iloom/package.iloom.json over package.json')\n // Merge: base package.json with iloom scripts taking precedence\n return {\n ...basePackage,\n scripts: {\n ...basePackage.scripts,\n ...iloomPackage.scripts,\n },\n }\n } catch {\n // No package.json - use iloom package as-is (non-Node project)\n getLogger().debug('Using scripts from .iloom/package.iloom.json (no package.json)')\n return iloomPackage\n }\n }\n\n // Fall back to package.json only\n return readPackageJson(dir)\n}\n\n/**\n * Parse bin field into normalized Record format\n * @param bin The bin field from package.json (string or object)\n * @param packageName Package name to use for string bin variant\n * @returns Normalized bin entries as Record<string, string>\n */\nexport function parseBinField(\n bin: string | Record<string, string> | undefined,\n packageName: string\n): Record<string, string> {\n if (!bin) {\n return {}\n }\n\n if (typeof bin === 'string') {\n return { [packageName]: bin }\n }\n\n return bin\n}\n\n/**\n * Check if package.json indicates a web application\n * @param pkgJson Parsed package.json object\n * @returns true if package has web framework dependencies\n */\nexport function hasWebDependencies(pkgJson: PackageJson): boolean {\n const webIndicators = [\n 'next',\n 'vite',\n 'express',\n 'react-scripts',\n 'nuxt',\n 'svelte-kit',\n 'astro',\n 'remix',\n 'fastify',\n 'koa',\n 'hapi',\n '@angular/core',\n 'gatsby',\n '@11ty/eleventy',\n 'ember-cli'\n ]\n\n const allDeps = {\n ...pkgJson.dependencies,\n ...pkgJson.devDependencies\n }\n\n return webIndicators.some(indicator => indicator in allDeps)\n}\n\n/**\n * Check if package.json has a specific script\n * @param pkgJson Parsed package.json object\n * @param scriptName Script name to check for\n * @returns true if script exists\n */\nexport function hasScript(pkgJson: PackageJson, scriptName: string): boolean {\n return !!pkgJson.scripts?.[scriptName]\n}\n\n/**\n * Get all scripts with their source metadata\n * Scripts from .iloom/package.iloom.json are marked as 'iloom-config' and should be executed directly\n * Scripts from package.json are marked as 'package-manager' and should use pnpm/npm/yarn\n *\n * @param dir Directory to read package configuration from\n * @returns Map of script names to their configurations including source\n */\nexport async function getPackageScripts(dir: string): Promise<Record<string, PackageScriptConfig>> {\n const scripts: Record<string, PackageScriptConfig> = {}\n\n // First, check if package.json exists and read scripts (these are package-manager sourced)\n const packageJsonPath = path.join(dir, 'package.json')\n if (await fs.pathExists(packageJsonPath)) {\n const pkgJson = await readPackageJson(dir)\n if (pkgJson.scripts) {\n for (const [name, command] of Object.entries(pkgJson.scripts)) {\n scripts[name] = { command, source: 'package-manager' }\n }\n }\n }\n\n // Then, read iloom package scripts (these override and are iloom-config sourced)\n const iloomPackage = await readIloomPackageScripts(dir)\n if (iloomPackage?.scripts) {\n for (const [name, command] of Object.entries(iloomPackage.scripts)) {\n scripts[name] = { command, source: 'iloom-config' }\n }\n }\n\n return scripts\n}\n\n/**\n * Valid capability values that can be explicitly declared\n */\nconst VALID_CAPABILITIES: readonly ProjectCapability[] = ['cli', 'web'] as const\n\n/**\n * Extract explicit capabilities from package configuration\n * Used for non-Node.js projects that declare capabilities in package.iloom.json\n * @param pkgJson Parsed package configuration object\n * @returns Array of valid ProjectCapability values, or empty array if none declared\n */\nexport function getExplicitCapabilities(pkgJson: PackageJson): ProjectCapability[] {\n // Return empty if no capabilities field or not an array\n if (!pkgJson.capabilities || !Array.isArray(pkgJson.capabilities)) {\n return []\n }\n\n // Filter to only valid ProjectCapability values\n return pkgJson.capabilities.filter(\n (cap): cap is ProjectCapability => VALID_CAPABILITIES.includes(cap as ProjectCapability)\n )\n}\n"],"mappings":"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","names":[]}
@@ -9,8 +9,7 @@ import path from "path";
9
9
  import os from "os";
10
10
  import { z } from "zod";
11
11
  import deepmerge from "deepmerge";
12
- var PROJECT_CAPABILITIES = ["cli", "web"];
13
- var AgentSettingsSchema = z.object({
12
+ var BaseAgentSettingsSchema = z.object({
14
13
  model: z.enum(["sonnet", "opus", "haiku"]).optional().describe("Claude model shorthand: sonnet, opus, or haiku"),
15
14
  enabled: z.boolean().optional().describe("Whether this agent is enabled. Defaults to true."),
16
15
  providers: z.record(
@@ -19,6 +18,9 @@ var AgentSettingsSchema = z.object({
19
18
  ).optional().describe('Map of review providers to model names. Keys: claude, gemini, codex. Values: model name strings (e.g., "sonnet", "gemini-3-pro-preview", "gpt-5.2-codex")'),
20
19
  review: z.boolean().optional().describe("Whether artifacts from this agent should be reviewed before posting (defaults to false)")
21
20
  });
21
+ var AgentSettingsSchema = BaseAgentSettingsSchema.extend({
22
+ agents: z.record(z.string(), BaseAgentSettingsSchema).optional().describe("Nested per-agent model overrides for swarm mode. Configure under agents.iloom-swarm-worker.agents.<agent-name>.model to set a different model for phase agents when running inside swarm workers. Fallback chain: swarm-specific agent model > explicit swarm worker model > base agent model. Only meaningful under the iloom-swarm-worker agent entry.")
23
+ });
22
24
  var SpinAgentSettingsSchema = z.object({
23
25
  model: z.enum(["sonnet", "opus", "haiku"]).default("opus").describe("Claude model shorthand for spin orchestrator")
24
26
  });
@@ -59,19 +61,17 @@ var WorkflowsSettingsSchemaNoDefaults = z.object({
59
61
  regular: WorkflowPermissionSchemaNoDefaults.optional()
60
62
  }).optional();
61
63
  var CapabilitiesSettingsSchema = z.object({
62
- capabilities: z.array(z.enum(PROJECT_CAPABILITIES)).optional().describe("Explicitly declared project capabilities (auto-detected if not specified)"),
63
64
  web: z.object({
64
65
  basePort: z.number().min(1, "Base port must be >= 1").max(65535, "Base port must be <= 65535").optional().describe("Base port for web workspace port calculations (default: 3000)")
65
- }).optional(),
66
+ }).optional().describe('Web dev server settings. To declare a project as a web project, add "web" to the capabilities array in .iloom/package.iloom.json or .iloom/package.iloom.local.json.'),
66
67
  database: z.object({
67
68
  databaseUrlEnvVarName: z.string().min(1, "Database URL variable name cannot be empty").regex(/^[A-Z_][A-Z0-9_]*$/, "Must be valid env var name (uppercase, underscores)").optional().default("DATABASE_URL").describe("Name of environment variable for database connection URL")
68
69
  }).optional()
69
70
  }).optional();
70
71
  var CapabilitiesSettingsSchemaNoDefaults = z.object({
71
- capabilities: z.array(z.enum(PROJECT_CAPABILITIES)).optional().describe("Explicitly declared project capabilities (auto-detected if not specified)"),
72
72
  web: z.object({
73
73
  basePort: z.number().min(1, "Base port must be >= 1").max(65535, "Base port must be <= 65535").optional().describe("Base port for web workspace port calculations (default: 3000)")
74
- }).optional(),
74
+ }).optional().describe('Web dev server settings. To declare a project as a web project, add "web" to the capabilities array in .iloom/package.iloom.json or .iloom/package.iloom.local.json.'),
75
75
  database: z.object({
76
76
  databaseUrlEnvVarName: z.string().min(1, "Database URL variable name cannot be empty").regex(/^[A-Z_][A-Z0-9_]*$/, "Must be valid env var name (uppercase, underscores)").optional().describe("Name of environment variable for database connection URL")
77
77
  }).optional()
@@ -115,7 +115,7 @@ var IloomSettingsSchema = z.object({
115
115
  copyGitIgnoredPatterns: z.array(z.string().min(1, "Pattern cannot be empty")).optional().describe(`Glob patterns for gitignored files to copy to looms (e.g., ["*.db", "data/*.sqlite"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom's and claude's local settings are automatically copied and do not need to be specified here.`),
116
116
  workflows: WorkflowsSettingsSchema.describe("Per-workflow-type permission configurations"),
117
117
  agents: z.record(z.string(), AgentSettingsSchema).optional().nullable().describe(
118
- "Per-agent configuration overrides. Available agents: iloom-issue-analyzer (analyzes issues), iloom-issue-planner (creates implementation plans), iloom-issue-analyze-and-plan (combined analysis and planning), iloom-issue-complexity-evaluator (evaluates complexity), iloom-issue-enhancer (enhances issue descriptions), iloom-issue-implementer (implements code changes), iloom-code-reviewer (reviews code changes against requirements), iloom-artifact-reviewer (reviews artifacts before posting)"
118
+ 'Per-agent configuration overrides. Available agents: iloom-issue-analyzer (analyzes issues), iloom-issue-planner (creates implementation plans), iloom-issue-analyze-and-plan (combined analysis and planning), iloom-issue-complexity-evaluator (evaluates complexity), iloom-issue-enhancer (enhances issue descriptions), iloom-issue-implementer (implements code changes), iloom-code-reviewer (reviews code changes against requirements), iloom-artifact-reviewer (reviews artifacts before posting), iloom-swarm-worker (swarm worker agent, dynamically generated). The iloom-swarm-worker agent supports a nested "agents" sub-record for configuring phase agent models specifically in swarm mode.'
119
119
  ),
120
120
  spin: SpinAgentSettingsSchema.optional().describe(
121
121
  "Spin orchestrator configuration. Model defaults to opus when not configured."
@@ -130,7 +130,7 @@ var IloomSettingsSchema = z.object({
130
130
  databaseProviders: DatabaseProvidersSettingsSchema.describe("Database provider configurations"),
131
131
  issueManagement: z.object({
132
132
  // SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts
133
- provider: z.enum(["github", "linear"]).optional().default("github").describe("Issue tracker provider (github, linear)"),
133
+ provider: z.enum(["github", "linear", "jira"]).optional().default("github").describe("Issue tracker provider (github, linear, jira)"),
134
134
  github: z.object({
135
135
  remote: z.string().min(1, "Remote name cannot be empty").describe("Git remote name to use for GitHub operations")
136
136
  }).optional(),
@@ -138,6 +138,17 @@ var IloomSettingsSchema = z.object({
138
138
  teamId: z.string().min(1, "Team ID cannot be empty").describe('Linear team identifier (e.g., "ENG", "PLAT")'),
139
139
  branchFormat: z.string().optional().describe("Branch naming template for Linear issues"),
140
140
  apiToken: z.string().optional().describe("Linear API token (lin_api_...). SECURITY: Store in settings.local.json only, never commit to source control.")
141
+ }).optional(),
142
+ jira: z.object({
143
+ host: z.string().min(1, "Jira host cannot be empty").describe('Jira instance URL (e.g., "https://yourcompany.atlassian.net")'),
144
+ username: z.string().min(1, "Jira username/email cannot be empty").describe("Jira username or email address"),
145
+ apiToken: z.string().optional().describe("Jira API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://id.atlassian.com/manage-profile/security/api-tokens"),
146
+ projectKey: z.string().min(1, "Project key cannot be empty").describe('Jira project key (e.g., "PROJ", "ENG")'),
147
+ boardId: z.string().optional().describe("Jira board ID for sprint/workflow operations (optional)"),
148
+ transitionMappings: z.record(z.string(), z.string()).optional().describe('Map iloom states to Jira transition names (e.g., {"In Review": "Start Review"})'),
149
+ defaultIssueType: z.string().min(1).optional().default("Task").describe('Default Jira issue type name for creating issues (e.g., "Task", "Story", "Bug")'),
150
+ defaultSubtaskType: z.string().min(1).optional().default("Subtask").describe('Default Jira issue type name for creating subtasks/child issues (e.g., "Subtask", "Sub-task")'),
151
+ doneStatuses: z.array(z.string()).optional().default(["Done"]).describe('Status names to exclude from issue lists (e.g., ["Done", "Closed", "Verify"])')
141
152
  }).optional()
142
153
  }).optional().describe("Issue management configuration"),
143
154
  mergeBehavior: z.object({
@@ -146,6 +157,9 @@ var IloomSettingsSchema = z.object({
146
157
  remote: z.string().optional(),
147
158
  autoCommitPush: z.boolean().optional().describe(
148
159
  "Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr."
160
+ ),
161
+ openBrowserOnFinish: z.boolean().default(true).describe(
162
+ "Open the PR in the default browser after finishing in github-pr or github-draft-pr mode. Use --no-browser flag to override."
149
163
  )
150
164
  }).optional().describe("Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)"),
151
165
  ide: z.object({
@@ -199,7 +213,7 @@ var IloomSettingsSchemaNoDefaults = z.object({
199
213
  copyGitIgnoredPatterns: z.array(z.string().min(1, "Pattern cannot be empty")).optional().describe(`Glob patterns for gitignored files to copy to looms (e.g., ["*.db", "data/*.sqlite"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom's and claude's local settings are automatically copied and do not need to be specified here.`),
200
214
  workflows: WorkflowsSettingsSchemaNoDefaults.describe("Per-workflow-type permission configurations"),
201
215
  agents: z.record(z.string(), AgentSettingsSchema).optional().nullable().describe(
202
- "Per-agent configuration overrides. Available agents: iloom-issue-analyzer (analyzes issues), iloom-issue-planner (creates implementation plans), iloom-issue-analyze-and-plan (combined analysis and planning), iloom-issue-complexity-evaluator (evaluates complexity), iloom-issue-enhancer (enhances issue descriptions), iloom-issue-implementer (implements code changes), iloom-code-reviewer (reviews code changes against requirements), iloom-artifact-reviewer (reviews artifacts before posting)"
216
+ 'Per-agent configuration overrides. Available agents: iloom-issue-analyzer (analyzes issues), iloom-issue-planner (creates implementation plans), iloom-issue-analyze-and-plan (combined analysis and planning), iloom-issue-complexity-evaluator (evaluates complexity), iloom-issue-enhancer (enhances issue descriptions), iloom-issue-implementer (implements code changes), iloom-code-reviewer (reviews code changes against requirements), iloom-artifact-reviewer (reviews artifacts before posting), iloom-swarm-worker (swarm worker agent, dynamically generated). The iloom-swarm-worker agent supports a nested "agents" sub-record for configuring phase agent models specifically in swarm mode.'
203
217
  ),
204
218
  spin: z.object({
205
219
  model: z.enum(["sonnet", "opus", "haiku"]).optional()
@@ -215,7 +229,7 @@ var IloomSettingsSchemaNoDefaults = z.object({
215
229
  capabilities: CapabilitiesSettingsSchemaNoDefaults.describe("Project capability configurations"),
216
230
  databaseProviders: DatabaseProvidersSettingsSchema.describe("Database provider configurations"),
217
231
  issueManagement: z.object({
218
- provider: z.enum(["github", "linear"]).optional().describe("Issue tracker provider (github, linear)"),
232
+ provider: z.enum(["github", "linear", "jira"]).optional().describe("Issue tracker provider (github, linear, jira)"),
219
233
  github: z.object({
220
234
  remote: z.string().min(1, "Remote name cannot be empty").describe("Git remote name to use for GitHub operations")
221
235
  }).optional(),
@@ -223,6 +237,17 @@ var IloomSettingsSchemaNoDefaults = z.object({
223
237
  teamId: z.string().min(1, "Team ID cannot be empty").describe('Linear team identifier (e.g., "ENG", "PLAT")'),
224
238
  branchFormat: z.string().optional().describe("Branch naming template for Linear issues"),
225
239
  apiToken: z.string().optional().describe("Linear API token (lin_api_...). SECURITY: Store in settings.local.json only, never commit to source control.")
240
+ }).optional(),
241
+ jira: z.object({
242
+ host: z.string().min(1, "Jira host cannot be empty").describe('Jira instance URL (e.g., "https://yourcompany.atlassian.net")'),
243
+ username: z.string().min(1, "Jira username/email cannot be empty").describe("Jira username or email address"),
244
+ apiToken: z.string().optional().describe("Jira API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://id.atlassian.com/manage-profile/security/api-tokens"),
245
+ projectKey: z.string().min(1, "Project key cannot be empty").describe('Jira project key (e.g., "PROJ", "ENG")'),
246
+ boardId: z.string().optional().describe("Jira board ID for sprint/workflow operations (optional)"),
247
+ transitionMappings: z.record(z.string(), z.string()).optional().describe('Map iloom states to Jira transition names (e.g., {"In Review": "Start Review"})'),
248
+ defaultIssueType: z.string().min(1).optional().describe('Default Jira issue type name for creating issues (e.g., "Task", "Story", "Bug")'),
249
+ defaultSubtaskType: z.string().min(1).optional().describe('Default Jira issue type name for creating subtasks/child issues (e.g., "Subtask", "Sub-task")'),
250
+ doneStatuses: z.array(z.string()).optional().default(["Done"]).describe('Status names to exclude from issue lists (e.g., ["Done", "Closed", "Verify"])')
226
251
  }).optional()
227
252
  }).optional().describe("Issue management configuration"),
228
253
  mergeBehavior: z.object({
@@ -230,6 +255,9 @@ var IloomSettingsSchemaNoDefaults = z.object({
230
255
  remote: z.string().optional(),
231
256
  autoCommitPush: z.boolean().optional().describe(
232
257
  "Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr."
258
+ ),
259
+ openBrowserOnFinish: z.boolean().optional().describe(
260
+ "Open the PR in the default browser after finishing in github-pr or github-draft-pr mode. Use --no-browser flag to override."
233
261
  )
234
262
  }).optional().describe("Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)"),
235
263
  ide: z.object({
@@ -252,6 +280,24 @@ var IloomSettingsSchemaNoDefaults = z.object({
252
280
  commitTimeout: z.number().min(1e3, "Commit timeout must be at least 1000ms").max(6e5, "Commit timeout cannot exceed 600000ms (10 minutes)").optional().describe("Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.")
253
281
  }).optional().describe("Git operation settings")
254
282
  });
283
+ function redactSensitiveFields(obj) {
284
+ if (obj === null || obj === void 0) return obj;
285
+ if (typeof obj !== "object") return obj;
286
+ if (Array.isArray(obj)) return obj.map(redactSensitiveFields);
287
+ const sensitiveKeys = ["apitoken", "token", "secret", "password"];
288
+ const result = {};
289
+ for (const [key, value] of Object.entries(obj)) {
290
+ const lowerKey = key.toLowerCase();
291
+ if (sensitiveKeys.some((s) => lowerKey.includes(s)) && typeof value === "string") {
292
+ result[key] = "[REDACTED]";
293
+ } else if (typeof value === "object" && value !== null) {
294
+ result[key] = redactSensitiveFields(value);
295
+ } else {
296
+ result[key] = value;
297
+ }
298
+ }
299
+ return result;
300
+ }
255
301
  var SettingsManager = class {
256
302
  /**
257
303
  * Load settings from global, project, and local sources with proper precedence
@@ -266,19 +312,19 @@ var SettingsManager = class {
266
312
  const root = this.getProjectRoot(projectRoot);
267
313
  const globalSettings = await this.loadGlobalSettingsFile();
268
314
  const globalSettingsPath = this.getGlobalSettingsPath();
269
- logger.debug(`\u{1F30D} Global settings from ${globalSettingsPath}:`, JSON.stringify(globalSettings, null, 2));
315
+ logger.debug(`\u{1F30D} Global settings from ${globalSettingsPath}:`, JSON.stringify(redactSensitiveFields(globalSettings), null, 2));
270
316
  const baseSettings = await this.loadSettingsFile(root, "settings.json");
271
317
  const baseSettingsPath = path.join(root, ".iloom", "settings.json");
272
- logger.debug(`\u{1F4C4} Base settings from ${baseSettingsPath}:`, JSON.stringify(baseSettings, null, 2));
318
+ logger.debug(`\u{1F4C4} Base settings from ${baseSettingsPath}:`, JSON.stringify(redactSensitiveFields(baseSettings), null, 2));
273
319
  const localSettings = await this.loadSettingsFile(root, "settings.local.json");
274
320
  const localSettingsPath = path.join(root, ".iloom", "settings.local.json");
275
- logger.debug(`\u{1F4C4} Local settings from ${localSettingsPath}:`, JSON.stringify(localSettings, null, 2));
321
+ logger.debug(`\u{1F4C4} Local settings from ${localSettingsPath}:`, JSON.stringify(redactSensitiveFields(localSettings), null, 2));
276
322
  let merged = this.mergeSettings(this.mergeSettings(globalSettings, baseSettings), localSettings);
277
- logger.debug("\u{1F504} After merging global + base + local settings:", JSON.stringify(merged, null, 2));
323
+ logger.debug("\u{1F504} After merging global + base + local settings:", JSON.stringify(redactSensitiveFields(merged), null, 2));
278
324
  if (cliOverrides && Object.keys(cliOverrides).length > 0) {
279
- logger.debug("\u2699\uFE0F CLI overrides to apply:", JSON.stringify(cliOverrides, null, 2));
325
+ logger.debug("\u2699\uFE0F CLI overrides to apply:", JSON.stringify(redactSensitiveFields(cliOverrides), null, 2));
280
326
  merged = this.mergeSettings(merged, cliOverrides);
281
- logger.debug("\u{1F504} After applying CLI overrides:", JSON.stringify(merged, null, 2));
327
+ logger.debug("\u{1F504} After applying CLI overrides:", JSON.stringify(redactSensitiveFields(merged), null, 2));
282
328
  }
283
329
  try {
284
330
  const finalSettings = IloomSettingsSchema.parse(merged);
@@ -301,7 +347,7 @@ Note: CLI overrides were applied. Check your --set arguments.`);
301
347
  * Log the final merged configuration for debugging
302
348
  */
303
349
  logFinalConfiguration(settings) {
304
- logger.debug("\u{1F4CB} Final merged configuration:", JSON.stringify(settings, null, 2));
350
+ logger.debug("\u{1F4CB} Final merged configuration:", JSON.stringify(redactSensitiveFields(settings), null, 2));
305
351
  }
306
352
  /**
307
353
  * Load and parse a single settings file
@@ -511,6 +557,7 @@ ${errorMessages.join("\n")}`
511
557
  };
512
558
 
513
559
  export {
560
+ BaseAgentSettingsSchema,
514
561
  AgentSettingsSchema,
515
562
  SpinAgentSettingsSchema,
516
563
  PlanCommandSettingsSchema,
@@ -527,4 +574,4 @@ export {
527
574
  IloomSettingsSchemaNoDefaults,
528
575
  SettingsManager
529
576
  };
530
- //# sourceMappingURL=chunk-RI2YL6TK.js.map
577
+ //# sourceMappingURL=chunk-YYAKPQBT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/SettingsManager.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport path from 'path'\nimport os from 'os'\nimport { z } from 'zod'\nimport deepmerge from 'deepmerge'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Zod schema for base agent settings (without nested agents)\n */\nexport const BaseAgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Claude model shorthand: sonnet, opus, or haiku'),\n\tenabled: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether this agent is enabled. Defaults to true.'),\n\tproviders: z\n\t\t.record(\n\t\t\tz.enum(['claude', 'gemini', 'codex']),\n\t\t\tz.string()\n\t\t)\n\t\t.optional()\n\t\t.describe('Map of review providers to model names. Keys: claude, gemini, codex. Values: model name strings (e.g., \"sonnet\", \"gemini-3-pro-preview\", \"gpt-5.2-codex\")'),\n\treview: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether artifacts from this agent should be reviewed before posting (defaults to false)'),\n})\n\n/**\n * Zod schema for agent settings, extends base with optional nested agents sub-record.\n * The nested agents field is used for swarm-specific per-agent overrides under iloom-swarm-worker.\n */\nexport const AgentSettingsSchema = BaseAgentSettingsSchema.extend({\n\tagents: z.record(z.string(), BaseAgentSettingsSchema)\n\t\t.optional()\n\t\t.describe('Nested per-agent model overrides for swarm mode. Configure under agents.iloom-swarm-worker.agents.<agent-name>.model to set a different model for phase agents when running inside swarm workers. Fallback chain: swarm-specific agent model > explicit swarm worker model > base agent model. Only meaningful under the iloom-swarm-worker agent entry.'),\n})\n\n/**\n * Zod schema for spin agent settings with default model\n * Used for the spin orchestrator configuration\n */\nexport const SpinAgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('opus')\n\t\t.describe('Claude model shorthand for spin orchestrator'),\n})\n\n/**\n * Zod schema for plan command settings with default model\n * Used for the plan command configuration\n */\nexport const PlanCommandSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('opus')\n\t\t.describe('Claude model shorthand for plan command'),\n\tplanner: z\n\t\t.enum(['claude', 'gemini', 'codex'])\n\t\t.default('claude')\n\t\t.describe('AI provider for creating the plan'),\n\treviewer: z\n\t\t.enum(['claude', 'gemini', 'codex', 'none'])\n\t\t.default('none')\n\t\t.describe('AI provider for reviewing the plan (none to skip review)'),\n})\n\n/**\n * Zod schema for summary settings with default model\n * Used for session summary generation configuration\n */\nexport const SummarySettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('sonnet')\n\t\t.describe('Claude model shorthand for session summary generation'),\n})\n\n/**\n * Zod schema for workflow permission configuration\n */\nexport const WorkflowPermissionSchema = z.object({\n\tpermissionMode: z\n\t\t.enum(['plan', 'acceptEdits', 'bypassPermissions', 'default'])\n\t\t.optional()\n\t\t.describe('Permission mode for Claude CLI in this workflow type'),\n\tnoVerify: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows'),\n\tstartIde: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch IDE (code) when starting this workflow type'),\n\tstartDevServer: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch development server when starting this workflow type'),\n\tstartAiAgent: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch Claude Code agent when starting this workflow type'),\n\tstartTerminal: z\n\t\t.boolean()\n\t\t.default(false)\n\t\t.describe('Launch terminal window without dev server when starting this workflow type'),\n\tgenerateSummary: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Generate and post Claude session summary when finishing this workflow type'),\n})\n\n/**\n * Non-defaulting variant for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const WorkflowPermissionSchemaNoDefaults = z.object({\n\tpermissionMode: z\n\t\t.enum(['plan', 'acceptEdits', 'bypassPermissions', 'default'])\n\t\t.optional()\n\t\t.describe('Permission mode for Claude CLI in this workflow type'),\n\tnoVerify: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows'),\n\tstartIde: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch IDE (code) when starting this workflow type'),\n\tstartDevServer: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch development server when starting this workflow type'),\n\tstartAiAgent: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch Claude Code agent when starting this workflow type'),\n\tstartTerminal: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch terminal window without dev server when starting this workflow type'),\n\tgenerateSummary: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Generate and post Claude session summary when finishing this workflow type'),\n})\n\n/**\n * Zod schema for workflows settings\n */\nexport const WorkflowsSettingsSchema = z\n\t.object({\n\t\tissue: WorkflowPermissionSchema.optional(),\n\t\tpr: WorkflowPermissionSchema.optional(),\n\t\tregular: WorkflowPermissionSchema.optional(),\n\t})\n\t.optional()\n\n/**\n * Non-defaulting variant for pre-merge validation\n */\nexport const WorkflowsSettingsSchemaNoDefaults = z\n\t.object({\n\t\tissue: WorkflowPermissionSchemaNoDefaults.optional(),\n\t\tpr: WorkflowPermissionSchemaNoDefaults.optional(),\n\t\tregular: WorkflowPermissionSchemaNoDefaults.optional(),\n\t})\n\t.optional()\n\n/**\n * Zod schema for capabilities settings\n */\nexport const CapabilitiesSettingsSchema = z\n\t.object({\n\t\tweb: z\n\t\t\t.object({\n\t\t\t\tbasePort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Base port must be >= 1')\n\t\t\t\t\t.max(65535, 'Base port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Base port for web workspace port calculations (default: 3000)'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe('Web dev server settings. To declare a project as a web project, add \"web\" to the capabilities array in .iloom/package.iloom.json or .iloom/package.iloom.local.json.'),\n\t\tdatabase: z\n\t\t\t.object({\n\t\t\t\tdatabaseUrlEnvVarName: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.min(1, 'Database URL variable name cannot be empty')\n\t\t\t\t\t.regex(/^[A-Z_][A-Z0-9_]*$/, 'Must be valid env var name (uppercase, underscores)')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.default('DATABASE_URL')\n\t\t\t\t\t.describe('Name of environment variable for database connection URL'),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.optional()\n\n/**\n * Non-defaulting variant for pre-merge validation\n */\nexport const CapabilitiesSettingsSchemaNoDefaults = z\n\t.object({\n\t\tweb: z\n\t\t\t.object({\n\t\t\t\tbasePort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Base port must be >= 1')\n\t\t\t\t\t.max(65535, 'Base port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Base port for web workspace port calculations (default: 3000)'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe('Web dev server settings. To declare a project as a web project, add \"web\" to the capabilities array in .iloom/package.iloom.json or .iloom/package.iloom.local.json.'),\n\t\tdatabase: z\n\t\t\t.object({\n\t\t\t\tdatabaseUrlEnvVarName: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.min(1, 'Database URL variable name cannot be empty')\n\t\t\t\t\t.regex(/^[A-Z_][A-Z0-9_]*$/, 'Must be valid env var name (uppercase, underscores)')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Name of environment variable for database connection URL'),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.optional()\n\n/**\n * Zod schema for Neon database provider settings\n */\nexport const NeonSettingsSchema = z.object({\n\tprojectId: z\n\t\t.string()\n\t\t.min(1)\n\t\t.regex(/^[a-zA-Z0-9-]+$/, 'Neon project ID must contain only letters, numbers, and hyphens')\n\t\t.describe('Neon project ID found in your project URL (e.g., \"fantastic-fox-3566354\")'),\n\tparentBranch: z\n\t\t.string()\n\t\t.min(1)\n\t\t.describe('Branch from which new database branches are created'),\n})\n\n/**\n * Zod schema for database provider settings\n */\nexport const DatabaseProvidersSettingsSchema = z\n\t.object({\n\t\tneon: NeonSettingsSchema.optional().describe(\n\t\t\t'Neon database configuration. Requires Neon CLI installed and authenticated for database branching.',\n\t\t),\n\t})\n\t.optional()\n\n/**\n * Zod schema for iloom settings\n */\nexport const IloomSettingsSchema = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.default(false)\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env file compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchema.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting), ' +\n\t\t\t\t'iloom-swarm-worker (swarm worker agent, dynamically generated). ' +\n\t\t\t\t'The iloom-swarm-worker agent supports a nested \"agents\" sub-record for configuring phase agent models specifically in swarm mode.',\n\t\t),\n\tspin: SpinAgentSettingsSchema.optional().describe(\n\t\t'Spin orchestrator configuration. Model defaults to opus when not configured.',\n\t),\n\tplan: PlanCommandSettingsSchema.optional().describe(\n\t\t'Plan command configuration. Model defaults to opus, planner to claude, reviewer to none when not configured.',\n\t),\n\tsummary: SummarySettingsSchema.optional().describe(\n\t\t'Session summary generation configuration. Model defaults to sonnet when not configured.',\n\t),\n\tcapabilities: CapabilitiesSettingsSchema.describe('Project capability configurations'),\n\tdatabaseProviders: DatabaseProvidersSettingsSchema.describe('Database provider configurations'),\n\tissueManagement: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\tprovider: z.enum(['github', 'linear', 'jira']).optional().default('github').describe('Issue tracker provider (github, linear, jira)'),\n\t\t\tgithub: z\n\t\t\t\t.object({\n\t\t\t\t\tremote: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Remote name cannot be empty')\n\t\t\t\t\t\t.describe('Git remote name to use for GitHub operations'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tlinear: z\n\t\t\t\t.object({\n\t\t\t\t\tteamId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Team ID cannot be empty')\n\t\t\t\t\t\t.describe('Linear team identifier (e.g., \"ENG\", \"PLAT\")'),\n\t\t\t\t\tbranchFormat: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Branch naming template for Linear issues'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Linear API token (lin_api_...). SECURITY: Store in settings.local.json only, never commit to source control.'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tjira: z\n\t\t\t\t.object({\n\t\t\t\t\thost: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira host cannot be empty')\n\t\t\t\t\t\t.describe('Jira instance URL (e.g., \"https://yourcompany.atlassian.net\")'),\n\t\t\t\t\tusername: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira username/email cannot be empty')\n\t\t\t\t\t\t.describe('Jira username or email address'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://id.atlassian.com/manage-profile/security/api-tokens'),\n\t\t\t\t\tprojectKey: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Project key cannot be empty')\n\t\t\t\t\t\t.describe('Jira project key (e.g., \"PROJ\", \"ENG\")'),\n\t\t\t\t\tboardId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira board ID for sprint/workflow operations (optional)'),\n\t\t\t\t\ttransitionMappings: z\n\t\t\t\t\t\t.record(z.string(), z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Map iloom states to Jira transition names (e.g., {\"In Review\": \"Start Review\"})'),\n\t\t\t\t\tdefaultIssueType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default('Task')\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating issues (e.g., \"Task\", \"Story\", \"Bug\")'),\n\t\t\t\t\tdefaultSubtaskType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default('Subtask')\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating subtasks/child issues (e.g., \"Subtask\", \"Sub-task\")'),\n\t\t\t\t\tdoneStatuses: z\n\t\t\t\t\t\t.array(z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default(['Done'])\n\t\t\t\t\t\t.describe('Status names to exclude from issue lists (e.g., [\"Done\", \"Closed\", \"Verify\"])'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Issue management configuration'),\n\tmergeBehavior: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\tmode: z.enum(['local', 'github-pr', 'github-draft-pr']).default('local'),\n\t\t\tremote: z.string().optional(),\n\t\t\tautoCommitPush: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr.'\n\t\t\t\t),\n\t\t\topenBrowserOnFinish: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(true)\n\t\t\t\t.describe(\n\t\t\t\t\t'Open the PR in the default browser after finishing in github-pr or github-draft-pr mode. Use --no-browser flag to override.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)'),\n\tide: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\ttype: z\n\t\t\t\t.enum(['vscode', 'cursor', 'webstorm', 'sublime', 'intellij', 'windsurf', 'antigravity'])\n\t\t\t\t.default('vscode')\n\t\t\t\t.describe(\n\t\t\t\t\t'IDE to launch when starting a loom. Options: vscode (Visual Studio Code), cursor (Cursor AI editor), ' +\n\t\t\t\t\t\t'webstorm (JetBrains WebStorm), sublime (Sublime Text), intellij (JetBrains IntelliJ IDEA), ' +\n\t\t\t\t\t\t'windsurf (Windsurf editor), antigravity (Antigravity IDE).'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe(\n\t\t\t'IDE configuration for workspace launches. Controls which editor opens when you start a loom. ' +\n\t\t\t\t'Supports VSCode, Cursor, WebStorm, Sublime Text, IntelliJ, Windsurf, and Antigravity. ' +\n\t\t\t\t'Note: Color synchronization (title bar colors) only works with VSCode-compatible editors (vscode, cursor, windsurf, antigravity).'\n\t\t),\n\tcolors: z\n\t\t.object({\n\t\t\tterminal: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(true)\n\t\t\t\t.describe('Apply terminal background colors based on branch name (macOS only)'),\n\t\t\tvscode: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(false)\n\t\t\t\t.describe(\n\t\t\t\t\t'Apply VSCode/Cursor title bar colors based on branch name. ' +\n\t\t\t\t\t\t'Note: This modifies .vscode/settings.json which may be in source control. ' +\n\t\t\t\t\t\t'Default is false for safety; enable via init or explicitly if .vscode is gitignored.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Color synchronization settings for workspace identification'),\n\tattribution: z\n\t\t.enum(['off', 'upstreamOnly', 'on'])\n\t\t.default('upstreamOnly')\n\t\t.describe(\n\t\t\t'Controls when iloom attribution appears in session summaries. ' +\n\t\t\t\t'\"off\" - never show attribution. ' +\n\t\t\t\t'\"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). ' +\n\t\t\t\t'\"on\" - always show attribution.'\n\t\t),\n\tgit: z\n\t\t.object({\n\t\t\tcommitTimeout: z\n\t\t\t\t.number()\n\t\t\t\t.min(1000, 'Commit timeout must be at least 1000ms')\n\t\t\t\t.max(600000, 'Commit timeout cannot exceed 600000ms (10 minutes)')\n\t\t\t\t.default(60000)\n\t\t\t\t.describe('Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.'),\n\t\t})\n\t\t.default({ }) // ensures the object always exists and uses default for the inner properties\n\t\t.describe('Git operation settings'),\n})\n\n/**\n * Non-defaulting variant for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const IloomSettingsSchemaNoDefaults = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchemaNoDefaults.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting), ' +\n\t\t\t\t'iloom-swarm-worker (swarm worker agent, dynamically generated). ' +\n\t\t\t\t'The iloom-swarm-worker agent supports a nested \"agents\" sub-record for configuring phase agent models specifically in swarm mode.',\n\t\t),\n\tspin: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Spin orchestrator configuration'),\n\tplan: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t\tplanner: z.enum(['claude', 'gemini', 'codex']).optional(),\n\t\t\treviewer: z.enum(['claude', 'gemini', 'codex', 'none']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Plan command configuration'),\n\tsummary: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Session summary generation configuration'),\n\tcapabilities: CapabilitiesSettingsSchemaNoDefaults.describe('Project capability configurations'),\n\tdatabaseProviders: DatabaseProvidersSettingsSchema.describe('Database provider configurations'),\n\tissueManagement: z\n\t\t.object({\n\t\t\tprovider: z.enum(['github', 'linear', 'jira']).optional().describe('Issue tracker provider (github, linear, jira)'),\n\t\t\tgithub: z\n\t\t\t\t.object({\n\t\t\t\t\tremote: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Remote name cannot be empty')\n\t\t\t\t\t\t.describe('Git remote name to use for GitHub operations'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tlinear: z\n\t\t\t\t.object({\n\t\t\t\t\tteamId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Team ID cannot be empty')\n\t\t\t\t\t\t.describe('Linear team identifier (e.g., \"ENG\", \"PLAT\")'),\n\t\t\t\t\tbranchFormat: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Branch naming template for Linear issues'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Linear API token (lin_api_...). SECURITY: Store in settings.local.json only, never commit to source control.'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tjira: z\n\t\t\t\t.object({\n\t\t\t\t\thost: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira host cannot be empty')\n\t\t\t\t\t\t.describe('Jira instance URL (e.g., \"https://yourcompany.atlassian.net\")'),\n\t\t\t\t\tusername: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Jira username/email cannot be empty')\n\t\t\t\t\t\t.describe('Jira username or email address'),\n\t\t\t\t\tapiToken: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://id.atlassian.com/manage-profile/security/api-tokens'),\n\t\t\t\t\tprojectKey: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'Project key cannot be empty')\n\t\t\t\t\t\t.describe('Jira project key (e.g., \"PROJ\", \"ENG\")'),\n\t\t\t\t\tboardId: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Jira board ID for sprint/workflow operations (optional)'),\n\t\t\t\t\ttransitionMappings: z\n\t\t\t\t\t\t.record(z.string(), z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Map iloom states to Jira transition names (e.g., {\"In Review\": \"Start Review\"})'),\n\t\t\t\t\tdefaultIssueType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating issues (e.g., \"Task\", \"Story\", \"Bug\")'),\n\t\t\t\t\tdefaultSubtaskType: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1)\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.describe('Default Jira issue type name for creating subtasks/child issues (e.g., \"Subtask\", \"Sub-task\")'),\n\t\t\t\t\tdoneStatuses: z\n\t\t\t\t\t\t.array(z.string())\n\t\t\t\t\t\t.optional()\n\t\t\t\t\t\t.default(['Done'])\n\t\t\t\t\t\t.describe('Status names to exclude from issue lists (e.g., [\"Done\", \"Closed\", \"Verify\"])'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Issue management configuration'),\n\tmergeBehavior: z\n\t\t.object({\n\t\t\tmode: z.enum(['local', 'github-pr', 'github-draft-pr']).optional(),\n\t\t\tremote: z.string().optional(),\n\t\t\tautoCommitPush: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr.'\n\t\t\t\t),\n\t\t\topenBrowserOnFinish: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Open the PR in the default browser after finishing in github-pr or github-draft-pr mode. Use --no-browser flag to override.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)'),\n\tide: z\n\t\t.object({\n\t\t\ttype: z\n\t\t\t\t.enum(['vscode', 'cursor', 'webstorm', 'sublime', 'intellij', 'windsurf', 'antigravity'])\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'IDE to launch when starting a loom. Options: vscode (Visual Studio Code), cursor (Cursor AI editor), ' +\n\t\t\t\t\t\t'webstorm (JetBrains WebStorm), sublime (Sublime Text), intellij (JetBrains IntelliJ IDEA), ' +\n\t\t\t\t\t\t'windsurf (Windsurf editor), antigravity (Antigravity IDE).'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe(\n\t\t\t'IDE configuration for workspace launches. Controls which editor opens when you start a loom. ' +\n\t\t\t\t'Supports VSCode, Cursor, WebStorm, Sublime Text, IntelliJ, Windsurf, and Antigravity. ' +\n\t\t\t\t'Note: Color synchronization (title bar colors) only works with VSCode-compatible editors (vscode, cursor, windsurf, antigravity).'\n\t\t),\n\tcolors: z\n\t\t.object({\n\t\t\tterminal: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Apply terminal background colors based on branch name (macOS only)'),\n\t\t\tvscode: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Apply VSCode/Cursor title bar colors based on branch name. ' +\n\t\t\t\t\t\t'Note: This modifies .vscode/settings.json which may be in source control.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Color synchronization settings for workspace identification'),\n\tattribution: z\n\t\t.enum(['off', 'upstreamOnly', 'on'])\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Controls when iloom attribution appears in session summaries. ' +\n\t\t\t\t'\"off\" - never show attribution. ' +\n\t\t\t\t'\"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). ' +\n\t\t\t\t'\"on\" - always show attribution.'\n\t\t),\n\tgit: z\n\t\t.object({\n\t\t\tcommitTimeout: z\n\t\t\t\t.number()\n\t\t\t\t.min(1000, 'Commit timeout must be at least 1000ms')\n\t\t\t\t.max(600000, 'Commit timeout cannot exceed 600000ms (10 minutes)')\n\t\t\t\t.optional()\n\t\t\t\t.describe('Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.'),\n\t\t})\n\t\t.optional()\n\t\t.describe('Git operation settings'),\n})\n\n/**\n * TypeScript type for Neon settings derived from Zod schema\n */\nexport type NeonSettings = z.infer<typeof NeonSettingsSchema>\n\n/**\n * TypeScript type for database providers settings derived from Zod schema\n */\nexport type DatabaseProvidersSettings = z.infer<typeof DatabaseProvidersSettingsSchema>\n\n/**\n * TypeScript type for agent settings derived from Zod schema\n */\nexport type AgentSettings = z.infer<typeof AgentSettingsSchema>\n\n/**\n * TypeScript type for spin agent settings derived from Zod schema\n */\nexport type SpinAgentSettings = z.infer<typeof SpinAgentSettingsSchema>\n\n/**\n * TypeScript type for plan command settings derived from Zod schema\n */\nexport type PlanCommandSettings = z.infer<typeof PlanCommandSettingsSchema>\n\n/**\n * TypeScript type for summary settings derived from Zod schema\n */\nexport type SummarySettings = z.infer<typeof SummarySettingsSchema>\n\n/**\n * TypeScript type for workflow permission configuration derived from Zod schema\n */\nexport type WorkflowPermission = z.infer<typeof WorkflowPermissionSchema>\n\n/**\n * TypeScript type for workflows settings derived from Zod schema\n */\nexport type WorkflowsSettings = z.infer<typeof WorkflowsSettingsSchema>\n\n/**\n * TypeScript type for capabilities settings derived from Zod schema\n */\nexport type CapabilitiesSettings = z.infer<typeof CapabilitiesSettingsSchema>\n\n/**\n * TypeScript type for IDE settings derived from Zod schema\n */\nexport type IdeSettings = z.infer<typeof IloomSettingsSchema>['ide']\n\n/**\n * TypeScript type for iloom settings derived from Zod schema\n */\nexport type IloomSettings = z.infer<typeof IloomSettingsSchema>\n\n/**\n * TypeScript input type for iloom settings (before Zod defaults are applied)\n * Used for validation where partial/input objects need to be accepted\n */\nexport type IloomSettingsInput = z.input<typeof IloomSettingsSchema>\n\nfunction redactSensitiveFields(obj: unknown): unknown {\n\tif (obj === null || obj === undefined) return obj\n\tif (typeof obj !== 'object') return obj\n\tif (Array.isArray(obj)) return obj.map(redactSensitiveFields)\n\tconst sensitiveKeys = ['apitoken', 'token', 'secret', 'password']\n\tconst result: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n\t\tconst lowerKey = key.toLowerCase()\n\t\tif (sensitiveKeys.some(s => lowerKey.includes(s)) && typeof value === 'string') {\n\t\t\tresult[key] = '[REDACTED]'\n\t\t} else if (typeof value === 'object' && value !== null) {\n\t\t\tresult[key] = redactSensitiveFields(value)\n\t\t} else {\n\t\t\tresult[key] = value\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Manages project-level settings from .iloom/settings.json\n */\nexport class SettingsManager {\n\t/**\n\t * Load settings from global, project, and local sources with proper precedence\n\t * Merge hierarchy (lowest to highest priority):\n\t * 1. Global settings (~/.config/iloom-ai/settings.json)\n\t * 2. Project settings (<PROJECT_ROOT>/.iloom/settings.json)\n\t * 3. Local settings (<PROJECT_ROOT>/.iloom/settings.local.json)\n\t * 4. CLI overrides (--set flags)\n\t * Returns empty object if all files don't exist (not an error)\n\t */\n\tasync loadSettings(\n\t\tprojectRoot?: string,\n\t\tcliOverrides?: Partial<IloomSettings>,\n\t): Promise<IloomSettings> {\n\t\tconst root = this.getProjectRoot(projectRoot)\n\n\t\t// Load global settings (lowest priority)\n\t\tconst globalSettings = await this.loadGlobalSettingsFile()\n\t\tconst globalSettingsPath = this.getGlobalSettingsPath()\n\t\tlogger.debug(`🌍 Global settings from ${globalSettingsPath}:`, JSON.stringify(redactSensitiveFields(globalSettings), null, 2))\n\n\t\t// Load base settings from settings.json\n\t\tconst baseSettings = await this.loadSettingsFile(root, 'settings.json')\n\t\tconst baseSettingsPath = path.join(root, '.iloom', 'settings.json')\n\t\tlogger.debug(`📄 Base settings from ${baseSettingsPath}:`, JSON.stringify(redactSensitiveFields(baseSettings), null, 2))\n\n\t\t// Load local overrides from settings.local.json\n\t\tconst localSettings = await this.loadSettingsFile(root, 'settings.local.json')\n\t\tconst localSettingsPath = path.join(root, '.iloom', 'settings.local.json')\n\t\tlogger.debug(`📄 Local settings from ${localSettingsPath}:`, JSON.stringify(redactSensitiveFields(localSettings), null, 2))\n\n\t\t// Deep merge with priority: cliOverrides > localSettings > baseSettings > globalSettings\n\t\tlet merged = this.mergeSettings(this.mergeSettings(globalSettings, baseSettings), localSettings)\n\t\tlogger.debug('🔄 After merging global + base + local settings:', JSON.stringify(redactSensitiveFields(merged), null, 2))\n\n\t\tif (cliOverrides && Object.keys(cliOverrides).length > 0) {\n\t\t\tlogger.debug('⚙️ CLI overrides to apply:', JSON.stringify(redactSensitiveFields(cliOverrides), null, 2))\n\t\t\tmerged = this.mergeSettings(merged, cliOverrides)\n\t\t\tlogger.debug('🔄 After applying CLI overrides:', JSON.stringify(redactSensitiveFields(merged), null, 2))\n\t\t}\n\n\t\t// Validate merged result\n\t\ttry {\n\t\t\tconst finalSettings = IloomSettingsSchema.parse(merged)\n\n\t\t\t// Debug: Log final merged configuration\n\t\t\tthis.logFinalConfiguration(finalSettings)\n\n\t\t\treturn finalSettings\n\t\t} catch (error) {\n\t\t\t// Show all Zod validation errors\n\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\tconst errorMsg = this.formatAllZodErrors(error, '<merged settings>')\n\t\t\t\t// Enhance error message if CLI overrides were applied\n\t\t\t\tif (cliOverrides && Object.keys(cliOverrides).length > 0) {\n\t\t\t\t\tthrow new Error(`${errorMsg.message}\\n\\nNote: CLI overrides were applied. Check your --set arguments.`)\n\t\t\t\t}\n\t\t\t\tthrow errorMsg\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Log the final merged configuration for debugging\n\t */\n\tprivate logFinalConfiguration(settings: IloomSettings): void {\n\t\tlogger.debug('📋 Final merged configuration:', JSON.stringify(redactSensitiveFields(settings), null, 2))\n\t}\n\n\t/**\n\t * Load and parse a single settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Uses non-defaulting schema to prevent polluting partial settings with defaults before merge\n\t */\n\tprivate async loadSettingsFile(\n\t\tprojectRoot: string,\n\t\tfilename: string,\n\t): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = path.join(projectRoot, '.iloom', filename)\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to parse settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Basic type checking - ensure it's an object, but don't validate schema completeness\n\t\t\t// Individual files may be incomplete (e.g., Linear config split between files)\n\t\t\t// Final validation will happen on the merged result in loadSettings()\n\t\t\tif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Settings validation failed at ${filename}:\\n - root: Expected object, received ${typeof parsed}`\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn parsed as z.infer<typeof IloomSettingsSchemaNoDefaults>\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No settings file found at ${settingsPath}, using defaults`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Re-throw parsing errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Deep merge two settings objects with priority to override\n\t * Uses deepmerge library with array replacement strategy\n\t */\n\tprivate mergeSettings(\n\t\tbase: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t\toverride: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t): IloomSettings {\n\t\t// Use deepmerge with array replacement (not concatenation)\n\t\t// Type assertion is safe because the merged result will be validated with IloomSettingsSchema\n\t\t// which applies all the defaults after merging\n\t\treturn deepmerge(base as Record<string, unknown>, override as Record<string, unknown>, {\n\t\t\t// Replace arrays instead of concatenating them\n\t\t\tarrayMerge: (_destinationArray, sourceArray) => sourceArray,\n\t\t}) as IloomSettings\n\t}\n\n\t/**\n\t * Format all Zod validation errors into a single error message\n\t */\n\tprivate formatAllZodErrors(error: z.ZodError, settingsPath: string): Error {\n\t\tconst errorMessages = error.issues.map(issue => {\n\t\t\tconst path = issue.path.length > 0 ? issue.path.join('.') : 'root'\n\t\t\treturn ` - ${path}: ${issue.message}`\n\t\t})\n\n\t\treturn new Error(\n\t\t\t`Settings validation failed at ${settingsPath}:\\n${errorMessages.join('\\n')}`,\n\t\t)\n\t}\n\n\t/**\n\t * Validate settings structure and model names using Zod schema\n\t * This method is kept for testing purposes but uses Zod internally\n\t * @internal - Only used in tests via bracket notation\n\t */\n\t// @ts-expect-error - Used in tests via bracket notation, TypeScript can't detect this usage\n\tprivate validateSettings(settings: IloomSettingsInput): void {\n\t\ttry {\n\t\t\tIloomSettingsSchema.parse(settings)\n\t\t} catch (error) {\n\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\tthrow this.formatAllZodErrors(error, '<validation>')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Get project root (defaults to process.cwd())\n\t */\n\tprivate getProjectRoot(projectRoot?: string): string {\n\t\treturn projectRoot ?? process.cwd()\n\t}\n\n\t/**\n\t * Get global config directory path (~/.config/iloom-ai)\n\t */\n\tprivate getGlobalConfigDir(): string {\n\t\treturn path.join(os.homedir(), '.config', 'iloom-ai')\n\t}\n\n\t/**\n\t * Get global settings file path (~/.config/iloom-ai/settings.json)\n\t */\n\tprivate getGlobalSettingsPath(): string {\n\t\treturn path.join(this.getGlobalConfigDir(), 'settings.json')\n\t}\n\n\t/**\n\t * Load and parse global settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Warns but returns empty object on validation/parse errors (graceful degradation)\n\t */\n\tprivate async loadGlobalSettingsFile(): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = this.getGlobalSettingsPath()\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to parse global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}. Ignoring global settings.`,\n\t\t\t\t)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Validate with non-defaulting schema\n\t\t\ttry {\n\t\t\t\tconst validated = IloomSettingsSchemaNoDefaults.strict().parse(parsed)\n\t\t\t\treturn validated\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\t\tconst errorMsg = this.formatAllZodErrors(error, 'global settings')\n\t\t\t\t\tlogger.warn(`${errorMsg.message}. Ignoring global settings.`)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Validation error in global settings: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\t\t}\n\t\t\t\treturn {}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No global settings file found at ${settingsPath}`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Other file system errors - warn and continue\n\t\t\tlogger.warn(`Error reading global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\treturn {}\n\t\t}\n\t}\n\n\t/**\n\t * Get effective protected branches list with mainBranch always included\n\t *\n\t * This method provides a single source of truth for protected branches logic:\n\t * 1. Use configured protectedBranches if provided\n\t * 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t * 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t *\n\t * @param projectRoot - Optional project root directory (defaults to process.cwd())\n\t * @returns Array of protected branch names with mainBranch guaranteed to be included\n\t */\n\tasync getProtectedBranches(projectRoot?: string): Promise<string[]> {\n\t\tconst settings = await this.loadSettings(projectRoot)\n\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\tconst mainBranch = settings.mainBranch ?? 'main'\n\n\t\t// Build protected branches list:\n\t\t// 1. Use configured protectedBranches if provided\n\t\t// 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t\t// 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t\tlet protectedBranches: string[]\n\t\tif (settings.protectedBranches) {\n\t\t\t// Use configured list but ensure mainBranch is always included\n\t\t\tprotectedBranches = settings.protectedBranches.includes(mainBranch)\n\t\t\t\t? settings.protectedBranches\n\t\t\t\t: [mainBranch, ...settings.protectedBranches]\n\t\t} else {\n\t\t\t// Use defaults with current mainBranch\n\t\t\tprotectedBranches = [mainBranch, 'main', 'master', 'develop']\n\t\t}\n\n\t\treturn protectedBranches\n\t}\n\n\t/**\n\t * Get the spin orchestrator model with default applied\n\t * Default is defined in SpinAgentSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetSpinModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.spin?.model ?? SpinAgentSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command model with default applied\n\t * Default is defined in PlanCommandSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetPlanModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.plan?.model ?? PlanCommandSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command planner with default applied\n\t * Default is 'claude'\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Planner provider ('claude', 'gemini', or 'codex')\n\t */\n\tgetPlanPlanner(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' {\n\t\treturn settings?.plan?.planner ?? 'claude'\n\t}\n\n\t/**\n\t * Get the plan command reviewer with default applied\n\t * Default is 'none' (no review step)\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Reviewer provider ('claude', 'gemini', 'codex', or 'none')\n\t */\n\tgetPlanReviewer(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' | 'none' {\n\t\treturn settings?.plan?.reviewer ?? 'none'\n\t}\n\n\t/**\n\t * Get the session summary model with default applied\n\t * Default is defined in SummarySettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetSummaryModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.summary?.model ?? SummarySettingsSchema.parse({}).model\n\t}\n}\n"],"mappings":"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","names":["path"]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  calculatePortFromIdentifier
4
- } from "./chunk-NUACL52E.js";
4
+ } from "./chunk-3D7WQM7I.js";
5
5
 
6
6
  // src/lib/process/ProcessManager.ts
7
7
  import { execa } from "execa";
@@ -179,4 +179,4 @@ var ProcessManager = class {
179
179
  export {
180
180
  ProcessManager
181
181
  };
182
- //# sourceMappingURL=chunk-IZIYLYPK.js.map
182
+ //# sourceMappingURL=chunk-ZEWU5PZK.js.map