@iloom/cli 0.12.0 → 0.13.0-beta.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 (133) hide show
  1. package/README.md +17 -2
  2. package/dist/{ClaudeContextManager-IENAE2CP.js → ClaudeContextManager-RRGREEZQ.js} +4 -4
  3. package/dist/{ClaudeService-YIJCZUUB.js → ClaudeService-LEPW6QAC.js} +3 -3
  4. package/dist/{IssueTrackerFactory-2OI7YIN6.js → IssueTrackerFactory-KE2BDCLC.js} +3 -3
  5. package/dist/{LoomLauncher-3TSFW7QP.js → LoomLauncher-GKQMR5E6.js} +4 -4
  6. package/dist/README.md +17 -2
  7. package/dist/{SettingsManager-BMQCAXPP.js → SettingsManager-KQU7OX7G.js} +4 -2
  8. package/dist/{build-DMWSIME6.js → build-V3KADFMO.js} +6 -6
  9. package/dist/{chunk-QFDM23CO.js → chunk-3XEXT35Z.js} +2 -2
  10. package/dist/{chunk-FV4KXBGO.js → chunk-4VQXMEEP.js} +2 -2
  11. package/dist/{chunk-V5IYLWRA.js → chunk-H3T3EPF3.js} +2 -2
  12. package/dist/{chunk-QF2DROQR.js → chunk-JDN4SPV3.js} +2 -2
  13. package/dist/{chunk-M3FBM4T3.js → chunk-KQSV7FOG.js} +2 -2
  14. package/dist/{chunk-R7DGN73N.js → chunk-NTDY5AMO.js} +2 -2
  15. package/dist/{chunk-35CBWAJL.js → chunk-NUUFP53X.js} +2 -2
  16. package/dist/{chunk-IS46GQRA.js → chunk-PD75ZCFT.js} +14 -14
  17. package/dist/chunk-PD75ZCFT.js.map +1 -0
  18. package/dist/{chunk-EQIII6GI.js → chunk-Q7VXHJP6.js} +3 -3
  19. package/dist/{chunk-IHSA7VGI.js → chunk-QED2WB2D.js} +4 -4
  20. package/dist/{chunk-OPQC4OWM.js → chunk-QNPJXO53.js} +3 -3
  21. package/dist/{chunk-YWNF5755.js → chunk-QQULYI2S.js} +2 -2
  22. package/dist/{chunk-HIGWKLQR.js → chunk-QXGM32TO.js} +2 -2
  23. package/dist/{chunk-PMB6TYV4.js → chunk-RFCAPHL5.js} +2 -2
  24. package/dist/{chunk-OKB2NEDQ.js → chunk-SA446KA2.js} +5 -5
  25. package/dist/{chunk-ZM2AYHMO.js → chunk-SN4S5CWL.js} +2 -2
  26. package/dist/{chunk-XVCGPTEQ.js → chunk-TAEVA4QR.js} +2 -2
  27. package/dist/chunk-TAEVA4QR.js.map +1 -0
  28. package/dist/{chunk-PDG74IJT.js → chunk-TN2D2RX7.js} +22 -20
  29. package/dist/chunk-TN2D2RX7.js.map +1 -0
  30. package/dist/{chunk-GWJWECZB.js → chunk-VVQQIG64.js} +16 -17
  31. package/dist/{chunk-GWJWECZB.js.map → chunk-VVQQIG64.js.map} +1 -1
  32. package/dist/{chunk-653XBU3L.js → chunk-WGUGB54H.js} +10 -1
  33. package/dist/chunk-WGUGB54H.js.map +1 -0
  34. package/dist/{chunk-KCZSUJUR.js → chunk-X5DRLONY.js} +4 -4
  35. package/dist/{chunk-VA6CWUAE.js → chunk-XCP2WDYA.js} +2 -2
  36. package/dist/{chunk-HKEXRZMU.js → chunk-YUOVWWJX.js} +310 -5
  37. package/dist/chunk-YUOVWWJX.js.map +1 -0
  38. package/dist/{chunk-BFF27W3S.js → chunk-ZUIFO7B4.js} +2 -2
  39. package/dist/{cleanup-RLBLNQZN.js → cleanup-RJKLI47I.js} +19 -19
  40. package/dist/cleanup-RJKLI47I.js.map +1 -0
  41. package/dist/cli.js +89 -85
  42. package/dist/cli.js.map +1 -1
  43. package/dist/{commit-RILBXFWO.js → commit-SUHRUMDE.js} +6 -6
  44. package/dist/{compile-QEL5724K.js → compile-2MD346PO.js} +6 -6
  45. package/dist/{contribute-EHWLYOMZ.js → contribute-P4BMRY7C.js} +3 -3
  46. package/dist/database-helpers-PRDFNDRO.js +11 -0
  47. package/dist/{dev-server-2WSWZXJG.js → dev-server-ZNTLWOL5.js} +8 -8
  48. package/dist/{feedback-I6ZEHEUB.js → feedback-Q6WG2WX4.js} +2 -2
  49. package/dist/{git-I3PO6FY7.js → git-TX2IEMB3.js} +3 -3
  50. package/dist/{ignite-XZFYRVRJ.js → ignite-P644W2PK.js} +9 -9
  51. package/dist/index.d.ts +36 -2
  52. package/dist/index.js +17 -9
  53. package/dist/index.js.map +1 -1
  54. package/dist/{init-A6WRP77L.js → init-5HFY7JG6.js} +5 -5
  55. package/dist/{install-deps-HXP2TM7G.js → install-deps-J4ALTM27.js} +6 -6
  56. package/dist/{issues-SUFQJY6O.js → issues-LZMIF22U.js} +4 -4
  57. package/dist/{lint-FDZC77GL.js → lint-XIXKU22H.js} +6 -6
  58. package/dist/mcp/issue-management-server.js +8 -0
  59. package/dist/mcp/issue-management-server.js.map +1 -1
  60. package/dist/{open-US4XACLW.js → open-KUO35JIJ.js} +8 -8
  61. package/dist/{plan-PL3ZB32J.js → plan-7CF56OIR.js} +17 -17
  62. package/dist/prompts/init-prompt.txt +24 -0
  63. package/dist/prompts/issue-prompt.txt +1 -1
  64. package/dist/prompts/regular-prompt.txt +1 -1
  65. package/dist/{rebase-JA3RW2XO.js → rebase-MAMWPA2L.js} +5 -5
  66. package/dist/{recap-5TO42HN2.js → recap-IDBO3KM5.js} +6 -6
  67. package/dist/{run-KKCRBRLW.js → run-RGZHCQ6M.js} +8 -8
  68. package/dist/schema/settings.schema.json +24 -0
  69. package/dist/{shell-GAB2FCXH.js → shell-7ADCDFIV.js} +5 -5
  70. package/dist/{summary-P7QE3TNW.js → summary-7J2HORFD.js} +7 -7
  71. package/dist/{test-6LFB5WOO.js → test-SRB7EWU6.js} +6 -6
  72. package/dist/{test-git-PYJOYSED.js → test-git-G7ATVIXG.js} +3 -3
  73. package/dist/{test-jira-SM7IU5HW.js → test-jira-Q2HPA522.js} +3 -3
  74. package/dist/{test-prefix-HIRZBXTM.js → test-prefix-JMDGXR5A.js} +3 -3
  75. package/dist/{test-webserver-43PVP2JL.js → test-webserver-GZFVXBGD.js} +5 -5
  76. package/dist/{vscode-HXIXRZ3A.js → vscode-3I7ISHUU.js} +5 -5
  77. package/package.json +1 -1
  78. package/dist/chunk-653XBU3L.js.map +0 -1
  79. package/dist/chunk-HKEXRZMU.js.map +0 -1
  80. package/dist/chunk-IS46GQRA.js.map +0 -1
  81. package/dist/chunk-PDG74IJT.js.map +0 -1
  82. package/dist/chunk-XVCGPTEQ.js.map +0 -1
  83. package/dist/cleanup-RLBLNQZN.js.map +0 -1
  84. package/dist/neon-helpers-LCZAN4U4.js +0 -11
  85. /package/dist/{ClaudeContextManager-IENAE2CP.js.map → ClaudeContextManager-RRGREEZQ.js.map} +0 -0
  86. /package/dist/{ClaudeService-YIJCZUUB.js.map → ClaudeService-LEPW6QAC.js.map} +0 -0
  87. /package/dist/{IssueTrackerFactory-2OI7YIN6.js.map → IssueTrackerFactory-KE2BDCLC.js.map} +0 -0
  88. /package/dist/{LoomLauncher-3TSFW7QP.js.map → LoomLauncher-GKQMR5E6.js.map} +0 -0
  89. /package/dist/{SettingsManager-BMQCAXPP.js.map → SettingsManager-KQU7OX7G.js.map} +0 -0
  90. /package/dist/{build-DMWSIME6.js.map → build-V3KADFMO.js.map} +0 -0
  91. /package/dist/{chunk-QFDM23CO.js.map → chunk-3XEXT35Z.js.map} +0 -0
  92. /package/dist/{chunk-FV4KXBGO.js.map → chunk-4VQXMEEP.js.map} +0 -0
  93. /package/dist/{chunk-V5IYLWRA.js.map → chunk-H3T3EPF3.js.map} +0 -0
  94. /package/dist/{chunk-QF2DROQR.js.map → chunk-JDN4SPV3.js.map} +0 -0
  95. /package/dist/{chunk-M3FBM4T3.js.map → chunk-KQSV7FOG.js.map} +0 -0
  96. /package/dist/{chunk-R7DGN73N.js.map → chunk-NTDY5AMO.js.map} +0 -0
  97. /package/dist/{chunk-35CBWAJL.js.map → chunk-NUUFP53X.js.map} +0 -0
  98. /package/dist/{chunk-EQIII6GI.js.map → chunk-Q7VXHJP6.js.map} +0 -0
  99. /package/dist/{chunk-IHSA7VGI.js.map → chunk-QED2WB2D.js.map} +0 -0
  100. /package/dist/{chunk-OPQC4OWM.js.map → chunk-QNPJXO53.js.map} +0 -0
  101. /package/dist/{chunk-YWNF5755.js.map → chunk-QQULYI2S.js.map} +0 -0
  102. /package/dist/{chunk-HIGWKLQR.js.map → chunk-QXGM32TO.js.map} +0 -0
  103. /package/dist/{chunk-PMB6TYV4.js.map → chunk-RFCAPHL5.js.map} +0 -0
  104. /package/dist/{chunk-OKB2NEDQ.js.map → chunk-SA446KA2.js.map} +0 -0
  105. /package/dist/{chunk-ZM2AYHMO.js.map → chunk-SN4S5CWL.js.map} +0 -0
  106. /package/dist/{chunk-KCZSUJUR.js.map → chunk-X5DRLONY.js.map} +0 -0
  107. /package/dist/{chunk-VA6CWUAE.js.map → chunk-XCP2WDYA.js.map} +0 -0
  108. /package/dist/{chunk-BFF27W3S.js.map → chunk-ZUIFO7B4.js.map} +0 -0
  109. /package/dist/{commit-RILBXFWO.js.map → commit-SUHRUMDE.js.map} +0 -0
  110. /package/dist/{compile-QEL5724K.js.map → compile-2MD346PO.js.map} +0 -0
  111. /package/dist/{contribute-EHWLYOMZ.js.map → contribute-P4BMRY7C.js.map} +0 -0
  112. /package/dist/{git-I3PO6FY7.js.map → database-helpers-PRDFNDRO.js.map} +0 -0
  113. /package/dist/{dev-server-2WSWZXJG.js.map → dev-server-ZNTLWOL5.js.map} +0 -0
  114. /package/dist/{feedback-I6ZEHEUB.js.map → feedback-Q6WG2WX4.js.map} +0 -0
  115. /package/dist/{ignite-XZFYRVRJ.js.map → git-TX2IEMB3.js.map} +0 -0
  116. /package/dist/{neon-helpers-LCZAN4U4.js.map → ignite-P644W2PK.js.map} +0 -0
  117. /package/dist/{init-A6WRP77L.js.map → init-5HFY7JG6.js.map} +0 -0
  118. /package/dist/{install-deps-HXP2TM7G.js.map → install-deps-J4ALTM27.js.map} +0 -0
  119. /package/dist/{issues-SUFQJY6O.js.map → issues-LZMIF22U.js.map} +0 -0
  120. /package/dist/{lint-FDZC77GL.js.map → lint-XIXKU22H.js.map} +0 -0
  121. /package/dist/{open-US4XACLW.js.map → open-KUO35JIJ.js.map} +0 -0
  122. /package/dist/{plan-PL3ZB32J.js.map → plan-7CF56OIR.js.map} +0 -0
  123. /package/dist/{rebase-JA3RW2XO.js.map → rebase-MAMWPA2L.js.map} +0 -0
  124. /package/dist/{recap-5TO42HN2.js.map → recap-IDBO3KM5.js.map} +0 -0
  125. /package/dist/{run-KKCRBRLW.js.map → run-RGZHCQ6M.js.map} +0 -0
  126. /package/dist/{shell-GAB2FCXH.js.map → shell-7ADCDFIV.js.map} +0 -0
  127. /package/dist/{summary-P7QE3TNW.js.map → summary-7J2HORFD.js.map} +0 -0
  128. /package/dist/{test-6LFB5WOO.js.map → test-SRB7EWU6.js.map} +0 -0
  129. /package/dist/{test-git-PYJOYSED.js.map → test-git-G7ATVIXG.js.map} +0 -0
  130. /package/dist/{test-jira-SM7IU5HW.js.map → test-jira-Q2HPA522.js.map} +0 -0
  131. /package/dist/{test-prefix-HIRZBXTM.js.map → test-prefix-JMDGXR5A.js.map} +0 -0
  132. /package/dist/{test-webserver-43PVP2JL.js.map → test-webserver-GZFVXBGD.js.map} +0 -0
  133. /package/dist/{vscode-HXIXRZ3A.js.map → vscode-3I7ISHUU.js.map} +0 -0
@@ -1 +0,0 @@
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// Merge mode: canonical values + legacy aliases accepted at parse time\nexport const mergeModeValues = ['local', 'pr', 'draft-pr', 'github-pr', 'github-draft-pr', 'bitbucket-pr'] as const\nexport type MergeMode = 'local' | 'pr' | 'draft-pr'\nconst mergeModeTransform = (val: string): MergeMode => {\n\tconst map: Record<string, MergeMode> = { 'github-pr': 'pr', 'github-draft-pr': 'draft-pr', 'bitbucket-pr': 'pr' }\n\treturn (map[val] ?? val) as MergeMode\n}\n\n/**\n * Zod schema for base agent settings (without nested agents)\n */\nexport const BaseAgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Claude model shorthand: sonnet, opus, or haiku'),\n\tswarmModel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Model to use for this agent in swarm mode. Overrides the base model when running inside swarm workers.'),\n\tenabled: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether this agent is enabled. Defaults to true.'),\n\tproviders: z\n\t\t.record(\n\t\t\tz.enum(['claude', 'gemini', 'codex']),\n\t\t\tz.string()\n\t\t)\n\t\t.optional()\n\t\t.describe('Map of review providers to model names. Keys: claude, gemini, codex. Values: model name strings (e.g., \"sonnet\", \"gemini-3-pro-preview\", \"gpt-5.2-codex\")'),\n\treview: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether artifacts from this agent should be reviewed before posting (defaults to false)'),\n\tswarmReview: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether artifacts from this agent should be reviewed in swarm mode. Defaults to false if not set (review is off in swarm mode for speed and cost unless explicitly enabled).'),\n})\n\n/**\n * Zod schema for agent settings.\n */\nexport const AgentSettingsSchema = BaseAgentSettingsSchema\n\n/**\n * Zod schema for spin agent settings with default model\n * Used for the spin orchestrator configuration\n */\nexport const SpinAgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('opus')\n\t\t.describe('Claude model shorthand for spin orchestrator'),\n\tswarmModel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Model for the spin orchestrator when running in swarm mode. Overrides spin.model for swarm workflows.'),\n\tpostSwarmReview: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Run a full code review after swarm completion, auto-fixing issues with confidence 80+. Defaults to true.'),\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\twaveVerification: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('When enabled, the planner generates verification child issues between dependency waves to catch integration issues early.'),\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\tdevServer: z\n\t\t\t\t\t.enum(['process', 'docker'])\n\t\t\t\t\t.default('process')\n\t\t\t\t\t.describe('Dev server mode: \"process\" runs natively, \"docker\" runs inside a Docker container with port mapping'),\n\t\t\t\tdockerFile: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default('./Dockerfile')\n\t\t\t\t\t.describe('Path to Dockerfile relative to worktree root (only used when devServer is \"docker\")'),\n\t\t\t\tcontainerPort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Container port must be >= 1')\n\t\t\t\t\t.max(65535, 'Container port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Port the app runs on inside the Docker container (auto-detected from EXPOSE directive if not set)'),\n\t\t\t\tdockerBuildArgs: z\n\t\t\t\t\t.record(z.string())\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Build arguments to pass to docker build (e.g., {\"NODE_ENV\": \"development\"})'),\n\t\t\t\tdockerRunArgs: z\n\t\t\t\t\t.array(z.string())\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Additional arguments for docker run (e.g., [\"-v\", \"./src:/app/src\"] for volume mounts)'),\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\tdevServer: z\n\t\t\t\t\t.enum(['process', 'docker'])\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Dev server mode: \"process\" runs natively, \"docker\" runs inside a Docker container with port mapping'),\n\t\t\t\tdockerFile: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Path to Dockerfile relative to worktree root (only used when devServer is \"docker\")'),\n\t\t\t\tcontainerPort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Container port must be >= 1')\n\t\t\t\t\t.max(65535, 'Container port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Port the app runs on inside the Docker container (auto-detected from EXPOSE directive if not set)'),\n\t\t\t\tdockerBuildArgs: z\n\t\t\t\t\t.record(z.string())\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Build arguments to pass to docker build (e.g., {\"NODE_ENV\": \"development\"})'),\n\t\t\t\tdockerRunArgs: z\n\t\t\t\t\t.array(z.string())\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Additional arguments for docker run (e.g., [\"-v\", \"./src:/app/src\"] for volume mounts)'),\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 Docker dev server settings\n */\nexport const DevServerSettingsSchema = z.object({\n\tmode: z\n\t\t.enum(['docker'])\n\t\t.default('docker')\n\t\t.describe('Dev server mode. Currently only \"docker\" is supported.'),\n\tdocker: z\n\t\t.object({\n\t\t\tdockerFile: z\n\t\t\t\t.string()\n\t\t\t\t.default('./Dockerfile')\n\t\t\t\t.refine(\n\t\t\t\t\t(val) => {\n\t\t\t\t\t\t// Must be a relative path (no leading slash)\n\t\t\t\t\t\tif (path.isAbsolute(val)) return false\n\t\t\t\t\t\t// Must not traverse outside the project root\n\t\t\t\t\t\tconst normalized = path.normalize(val)\n\t\t\t\t\t\tif (normalized.startsWith('..')) return false\n\t\t\t\t\t\treturn true\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t'dockerFile must be a relative path that does not traverse outside the project root (no leading \"/\" and no \"../\" escaping)',\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t.describe('Path to Dockerfile relative to worktree root'),\n\t\t\tcontainerPort: z\n\t\t\t\t.number()\n\t\t\t\t.min(1, 'Container port must be >= 1')\n\t\t\t\t.max(65535, 'Container port must be <= 65535')\n\t\t\t\t.optional()\n\t\t\t\t.describe('Port the app runs on inside the Docker container (auto-detected from EXPOSE directive if not set)'),\n\t\t\tbuildArgs: z\n\t\t\t\t.record(z.string(), z.string())\n\t\t\t\t.optional()\n\t\t\t\t.describe('Build arguments to pass to docker build'),\n\t\t\trunArgs: z\n\t\t\t\t.array(z.string())\n\t\t\t\t.optional()\n\t\t\t\t.describe('Additional arguments for docker run'),\n\t\t})\n\t\t.optional(),\n})\n\n/**\n * Non-defaulting variant of DevServerSettingsSchema for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const DevServerSettingsSchemaNoDefaults = z.object({\n\tmode: z\n\t\t.enum(['docker'])\n\t\t.optional()\n\t\t.describe('Dev server mode. Currently only \"docker\" is supported.'),\n\tdocker: z\n\t\t.object({\n\t\t\tdockerFile: z\n\t\t\t\t.string()\n\t\t\t\t.optional()\n\t\t\t\t.refine(\n\t\t\t\t\t(val) => {\n\t\t\t\t\t\tif (val === undefined) return true\n\t\t\t\t\t\tif (path.isAbsolute(val)) return false\n\t\t\t\t\t\tconst normalized = path.normalize(val)\n\t\t\t\t\t\tif (normalized.startsWith('..')) return false\n\t\t\t\t\t\treturn true\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t'dockerFile must be a relative path that does not traverse outside the project root (no leading \"/\" and no \"../\" escaping)',\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t.describe('Path to Dockerfile relative to worktree root'),\n\t\t\tcontainerPort: z\n\t\t\t\t.number()\n\t\t\t\t.min(1, 'Container port must be >= 1')\n\t\t\t\t.max(65535, 'Container port must be <= 65535')\n\t\t\t\t.optional()\n\t\t\t\t.describe('Port the app runs on inside the Docker container (auto-detected from EXPOSE directive if not set)'),\n\t\t\tbuildArgs: z\n\t\t\t\t.record(z.string(), z.string())\n\t\t\t\t.optional()\n\t\t\t\t.describe('Build arguments to pass to docker build'),\n\t\t\trunArgs: z\n\t\t\t\t.array(z.string())\n\t\t\t\t.optional()\n\t\t\t\t.describe('Additional arguments for docker run'),\n\t\t})\n\t\t.optional(),\n})\n\n/**\n * Zod schema for Neon database provider settings\n */\nexport const NeonSettingsSchema = z.object({\n\tprojectId: z\n\t\t.string()\n\t\t.min(1)\n\t\t.regex(/^[a-zA-Z0-9-]+$/, 'Neon project ID must contain only letters, numbers, and hyphens')\n\t\t.describe('Neon project ID found in your project URL (e.g., \"fantastic-fox-3566354\")'),\n\tparentBranch: z\n\t\t.string()\n\t\t.min(1)\n\t\t.describe('Branch from which new database branches are created'),\n})\n\n/**\n * Zod schema for database provider settings\n */\nexport const DatabaseProvidersSettingsSchema = z\n\t.object({\n\t\tneon: NeonSettingsSchema.optional().describe(\n\t\t\t'Neon database configuration. Requires Neon CLI installed and authenticated for database branching.',\n\t\t),\n\t})\n\t.optional()\n\n/**\n * Zod schema for iloom settings\n */\nexport const IloomSettingsSchema = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.default(false)\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env file compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchema.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting), ' +\n\t\t\t\t'iloom-swarm-worker (swarm worker agent, dynamically generated). ' +\n\t\t\t\t'Use swarmModel on any agent to override its model in swarm mode.',\n\t\t),\n\tspin: SpinAgentSettingsSchema.optional().describe(\n\t\t'Spin orchestrator configuration. Model defaults to opus when not configured.',\n\t),\n\tplan: PlanCommandSettingsSchema.optional().describe(\n\t\t'Plan command configuration. Model defaults to opus, planner to claude, reviewer to none when not configured.',\n\t),\n\tsummary: SummarySettingsSchema.optional().describe(\n\t\t'Session summary generation configuration. Model defaults to sonnet when not configured.',\n\t),\n\tcapabilities: CapabilitiesSettingsSchema.describe('Project capability configurations'),\n\tdevServer: DevServerSettingsSchema.optional().describe('Docker-based dev server configuration'),\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\tversionControl: z\n\t\t.object({\n\t\t\tprovider: z.enum(['github', 'bitbucket']).optional().default('github').describe('Version control provider (github, bitbucket)'),\n\t\t\tbitbucket: z\n\t\t\t\t.object({\n\t\t\t\t\tusername: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'BitBucket username cannot be empty')\n\t\t\t\t\t\t.describe('BitBucket username'),\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('BitBucket API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://bitbucket.org/account/settings/app-passwords/ (Note: App passwords deprecated Sep 2025, use API tokens)'),\n\t\t\t\t\tworkspace: 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('BitBucket workspace (optional, auto-detected from git remote if not provided)'),\n\t\t\t\t\trepoSlug: 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('BitBucket repository slug (optional, auto-detected from git remote if not provided)'),\n\t\t\t\treviewers: z\n\t\t\t\t\t.array(z.string().describe('Reviewer username'))\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('List of usernames to add as PR reviewers. Usernames are resolved to Bitbucket account IDs at PR creation time.'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Version control provider 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(mergeModeValues).default('local').transform(mergeModeTransform),\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 draft-pr.'\n\t\t\t\t),\n\t\t\tprTitlePrefix: z.boolean().default(false).optional().describe('Prefix PR titles with the issue number (e.g., \"QLH-123: Title\"). Default: false'),\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 pr or 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), pr (create PR), or draft-pr (create draft PR at start, mark ready on finish)'),\n\tide: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\ttype: z\n\t\t\t\t.enum(['vscode', 'cursor', 'webstorm', 'sublime', 'intellij', 'windsurf', 'antigravity'])\n\t\t\t\t.default('vscode')\n\t\t\t\t.describe(\n\t\t\t\t\t'IDE to launch when starting a loom. Options: vscode (Visual Studio Code), cursor (Cursor AI editor), ' +\n\t\t\t\t\t\t'webstorm (JetBrains WebStorm), sublime (Sublime Text), intellij (JetBrains IntelliJ IDEA), ' +\n\t\t\t\t\t\t'windsurf (Windsurf editor), antigravity (Antigravity IDE).'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe(\n\t\t\t'IDE configuration for workspace launches. Controls which editor opens when you start a loom. ' +\n\t\t\t\t'Supports VSCode, Cursor, WebStorm, Sublime Text, IntelliJ, Windsurf, and Antigravity. ' +\n\t\t\t\t'Note: Color synchronization (title bar colors) only works with VSCode-compatible editors (vscode, cursor, windsurf, antigravity).'\n\t\t),\n\tcolors: z\n\t\t.object({\n\t\t\tterminal: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(true)\n\t\t\t\t.describe('Apply terminal background colors based on branch name (macOS only)'),\n\t\t\tvscode: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(false)\n\t\t\t\t.describe(\n\t\t\t\t\t'Apply VSCode/Cursor title bar colors based on branch name. ' +\n\t\t\t\t\t\t'Note: This modifies .vscode/settings.json which may be in source control. ' +\n\t\t\t\t\t\t'Default is false for safety; enable via init or explicitly if .vscode is gitignored.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Color synchronization settings for workspace identification'),\n\tattribution: z\n\t\t.enum(['off', 'upstreamOnly', 'on'])\n\t\t.default('upstreamOnly')\n\t\t.describe(\n\t\t\t'Controls when iloom attribution appears in session summaries. ' +\n\t\t\t\t'\"off\" - never show attribution. ' +\n\t\t\t\t'\"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). ' +\n\t\t\t\t'\"on\" - always show attribution.'\n\t\t),\n\tgit: z\n\t\t.object({\n\t\t\tcommitTimeout: z\n\t\t\t\t.number()\n\t\t\t\t.min(1000, 'Commit timeout must be at least 1000ms')\n\t\t\t\t.max(600000, 'Commit timeout cannot exceed 600000ms (10 minutes)')\n\t\t\t\t.default(60000)\n\t\t\t\t.describe('Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.'),\n\t\t})\n\t\t.default({ }) // ensures the object always exists and uses default for the inner properties\n\t\t.describe('Git operation settings'),\n})\n\n/**\n * Non-defaulting variant for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const IloomSettingsSchemaNoDefaults = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchemaNoDefaults.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting), ' +\n\t\t\t\t'iloom-swarm-worker (swarm worker agent, dynamically generated). ' +\n\t\t\t\t'Use swarmModel on any agent to override its model in swarm mode.',\n\t\t),\n\tspin: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t\tswarmModel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t\tpostSwarmReview: z.boolean().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\twaveVerification: z.boolean().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\tdevServer: DevServerSettingsSchemaNoDefaults.optional().describe('Docker-based dev server configuration'),\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\tversionControl: z\n\t\t.object({\n\t\t\tprovider: z.enum(['github', 'bitbucket']).optional().describe('Version control provider (github, bitbucket)'),\n\t\t\tbitbucket: z\n\t\t\t\t.object({\n\t\t\t\t\tusername: z\n\t\t\t\t\t\t.string()\n\t\t\t\t\t\t.min(1, 'BitBucket username cannot be empty')\n\t\t\t\t\t\t.describe('BitBucket username'),\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('BitBucket API token. SECURITY: Store in settings.local.json only, never commit to source control. Generate at: https://bitbucket.org/account/settings/app-passwords/ (Note: App passwords deprecated Sep 2025, use API tokens)'),\n\t\t\t\t\tworkspace: 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('BitBucket workspace (optional, auto-detected from git remote if not provided)'),\n\t\t\t\t\trepoSlug: 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('BitBucket repository slug (optional, auto-detected from git remote if not provided)'),\n\t\t\t\treviewers: z\n\t\t\t\t\t.array(z.string().describe('Reviewer username'))\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('List of usernames to add as PR reviewers. Usernames are resolved to Bitbucket account IDs at PR creation time.'),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Version control provider configuration'),\n\tmergeBehavior: z\n\t\t.object({\n\t\t\tmode: z.enum(mergeModeValues).transform(mergeModeTransform).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 draft-pr.'\n\t\t\t\t),\n\t\t\tprTitlePrefix: z.boolean().optional(),\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 pr or 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), pr (create PR), or 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 dev server settings derived from Zod schema\n */\nexport type DevServerSettings = z.infer<typeof DevServerSettingsSchema>\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\n/**\n * Recursively redact sensitive fields (tokens, secrets, passwords) from an object.\n * Returns a deep copy with sensitive string values replaced by '[REDACTED]'.\n */\nexport function 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\n\tconst sensitiveKeys = ['apitoken', 'token', 'secret', 'password', 'credential']\n\tconst result: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n\t\tconst lowerKey = key.toLowerCase()\n\t\tif (sensitiveKeys.some(s => lowerKey.includes(s)) && typeof value === 'string') {\n\t\t\tresult[key] = '[REDACTED]'\n\t\t} else if (typeof value === 'object' && value !== null) {\n\t\t\tresult[key] = redactSensitiveFields(value)\n\t\t} else {\n\t\t\tresult[key] = value\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Manages project-level settings from .iloom/settings.json\n */\nexport class SettingsManager {\n\t/**\n\t * Load settings from global, project, and local sources with proper precedence\n\t * Merge hierarchy (lowest to highest priority):\n\t * 1. Global settings (~/.config/iloom-ai/settings.json)\n\t * 2. Project settings (<PROJECT_ROOT>/.iloom/settings.json)\n\t * 3. Local settings (<PROJECT_ROOT>/.iloom/settings.local.json)\n\t * 4. CLI overrides (--set flags)\n\t * Returns empty object if all files don't exist (not an error)\n\t */\n\tasync loadSettings(\n\t\tprojectRoot?: string,\n\t\tcliOverrides?: Partial<IloomSettings>,\n\t): Promise<IloomSettings> {\n\t\tconst root = this.getProjectRoot(projectRoot)\n\n\t\t// Load global settings (lowest priority)\n\t\tconst globalSettings = await this.loadGlobalSettingsFile()\n\t\tconst globalSettingsPath = this.getGlobalSettingsPath()\n\t\tlogger.debug(`🌍 Global settings from ${globalSettingsPath}:`, JSON.stringify(redactSensitiveFields(globalSettings), null, 2))\n\n\t\t// Load base settings from settings.json\n\t\tconst baseSettings = await this.loadSettingsFile(root, 'settings.json')\n\t\tconst baseSettingsPath = path.join(root, '.iloom', 'settings.json')\n\t\tlogger.debug(`📄 Base settings from ${baseSettingsPath}:`, JSON.stringify(redactSensitiveFields(baseSettings), null, 2))\n\n\t\t// Load local overrides from settings.local.json\n\t\tconst localSettings = await this.loadSettingsFile(root, 'settings.local.json')\n\t\tconst localSettingsPath = path.join(root, '.iloom', 'settings.local.json')\n\t\tlogger.debug(`📄 Local settings from ${localSettingsPath}:`, JSON.stringify(redactSensitiveFields(localSettings), null, 2))\n\n\t\t// Deep merge with priority: cliOverrides > localSettings > baseSettings > globalSettings\n\t\tlet merged = this.mergeSettings(this.mergeSettings(globalSettings, baseSettings), localSettings)\n\t\tlogger.debug('🔄 After merging global + base + local settings:', JSON.stringify(redactSensitiveFields(merged), null, 2))\n\n\t\tif (cliOverrides && Object.keys(cliOverrides).length > 0) {\n\t\t\tlogger.debug('⚙️ CLI overrides to apply:', JSON.stringify(redactSensitiveFields(cliOverrides), null, 2))\n\t\t\tmerged = this.mergeSettings(merged, cliOverrides)\n\t\t\tlogger.debug('🔄 After applying CLI overrides:', JSON.stringify(redactSensitiveFields(merged), null, 2))\n\t\t}\n\n\t\t// Validate merged result\n\t\ttry {\n\t\t\tconst finalSettings = IloomSettingsSchema.parse(merged)\n\n\t\t\t// Debug: Log final merged configuration\n\t\t\tthis.logFinalConfiguration(finalSettings)\n\n\t\t\treturn finalSettings\n\t\t} catch (error) {\n\t\t\t// Show all Zod validation errors\n\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\tconst errorMsg = this.formatAllZodErrors(error, '<merged settings>')\n\t\t\t\t// Enhance error message if CLI overrides were applied\n\t\t\t\tif (cliOverrides && Object.keys(cliOverrides).length > 0) {\n\t\t\t\t\tthrow new Error(`${errorMsg.message}\\n\\nNote: CLI overrides were applied. Check your --set arguments.`)\n\t\t\t\t}\n\t\t\t\tthrow errorMsg\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Log the final merged configuration for debugging\n\t */\n\tprivate logFinalConfiguration(settings: IloomSettings): void {\n\t\tlogger.debug('📋 Final merged configuration:', JSON.stringify(redactSensitiveFields(settings), null, 2))\n\t}\n\n\t/**\n\t * Load and parse a single settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Uses non-defaulting schema to prevent polluting partial settings with defaults before merge\n\t */\n\tprivate async loadSettingsFile(\n\t\tprojectRoot: string,\n\t\tfilename: string,\n\t): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = path.join(projectRoot, '.iloom', filename)\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to parse settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Basic type checking - ensure it's an object, but don't validate schema completeness\n\t\t\t// Individual files may be incomplete (e.g., Linear config split between files)\n\t\t\t// Final validation will happen on the merged result in loadSettings()\n\t\t\tif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Settings validation failed at ${filename}:\\n - root: Expected object, received ${typeof parsed}`\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn parsed as z.infer<typeof IloomSettingsSchemaNoDefaults>\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No settings file found at ${settingsPath}, using defaults`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Re-throw parsing errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Deep merge two settings objects with priority to override\n\t * Uses deepmerge library with array replacement strategy\n\t */\n\tprivate mergeSettings(\n\t\tbase: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t\toverride: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t): IloomSettings {\n\t\t// Use deepmerge with array replacement (not concatenation)\n\t\t// Type assertion is safe because the merged result will be validated with IloomSettingsSchema\n\t\t// which applies all the defaults after merging\n\t\treturn deepmerge(base as Record<string, unknown>, override as Record<string, unknown>, {\n\t\t\t// Replace arrays instead of concatenating them\n\t\t\tarrayMerge: (_destinationArray, sourceArray) => sourceArray,\n\t\t}) as IloomSettings\n\t}\n\n\t/**\n\t * Format all Zod validation errors into a single error message\n\t */\n\tprivate formatAllZodErrors(error: z.ZodError, settingsPath: string): Error {\n\t\tconst errorMessages = error.issues.map(issue => {\n\t\t\tconst path = issue.path.length > 0 ? issue.path.join('.') : 'root'\n\t\t\treturn ` - ${path}: ${issue.message}`\n\t\t})\n\n\t\treturn new Error(\n\t\t\t`Settings validation failed at ${settingsPath}:\\n${errorMessages.join('\\n')}`,\n\t\t)\n\t}\n\n\t/**\n\t * Validate settings structure and model names using Zod schema\n\t * This method is kept for testing purposes but uses Zod internally\n\t * @internal - Only used in tests via bracket notation\n\t */\n\t// @ts-expect-error - Used in tests via bracket notation, TypeScript can't detect this usage\n\tprivate validateSettings(settings: IloomSettingsInput): void {\n\t\ttry {\n\t\t\tIloomSettingsSchema.parse(settings)\n\t\t} catch (error) {\n\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\tthrow this.formatAllZodErrors(error, '<validation>')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Get project root (defaults to process.cwd())\n\t */\n\tprivate getProjectRoot(projectRoot?: string): string {\n\t\treturn projectRoot ?? process.cwd()\n\t}\n\n\t/**\n\t * Get global config directory path (~/.config/iloom-ai)\n\t */\n\tprivate getGlobalConfigDir(): string {\n\t\treturn path.join(os.homedir(), '.config', 'iloom-ai')\n\t}\n\n\t/**\n\t * Get global settings file path (~/.config/iloom-ai/settings.json)\n\t */\n\tprivate getGlobalSettingsPath(): string {\n\t\treturn path.join(this.getGlobalConfigDir(), 'settings.json')\n\t}\n\n\t/**\n\t * Load and parse global settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Warns but returns empty object on validation/parse errors (graceful degradation)\n\t */\n\tprivate async loadGlobalSettingsFile(): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = this.getGlobalSettingsPath()\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to parse global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}. Ignoring global settings.`,\n\t\t\t\t)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Validate with non-defaulting schema\n\t\t\ttry {\n\t\t\t\tconst validated = IloomSettingsSchemaNoDefaults.strict().parse(parsed)\n\t\t\t\treturn validated\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\t\tconst errorMsg = this.formatAllZodErrors(error, 'global settings')\n\t\t\t\t\tlogger.warn(`${errorMsg.message}. Ignoring global settings.`)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Validation error in global settings: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\t\t}\n\t\t\t\treturn {}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No global settings file found at ${settingsPath}`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Other file system errors - warn and continue\n\t\t\tlogger.warn(`Error reading global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\treturn {}\n\t\t}\n\t}\n\n\t/**\n\t * Get effective protected branches list with mainBranch always included\n\t *\n\t * This method provides a single source of truth for protected branches logic:\n\t * 1. Use configured protectedBranches if provided\n\t * 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t * 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t *\n\t * @param projectRoot - Optional project root directory (defaults to process.cwd())\n\t * @returns Array of protected branch names with mainBranch guaranteed to be included\n\t */\n\tasync getProtectedBranches(projectRoot?: string): Promise<string[]> {\n\t\tconst settings = await this.loadSettings(projectRoot)\n\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\tconst mainBranch = settings.mainBranch ?? 'main'\n\n\t\t// Build protected branches list:\n\t\t// 1. Use configured protectedBranches if provided\n\t\t// 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t\t// 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t\tlet protectedBranches: string[]\n\t\tif (settings.protectedBranches) {\n\t\t\t// Use configured list but ensure mainBranch is always included\n\t\t\tprotectedBranches = settings.protectedBranches.includes(mainBranch)\n\t\t\t\t? settings.protectedBranches\n\t\t\t\t: [mainBranch, ...settings.protectedBranches]\n\t\t} else {\n\t\t\t// Use defaults with current mainBranch\n\t\t\tprotectedBranches = [mainBranch, 'main', 'master', 'develop']\n\t\t}\n\n\t\treturn protectedBranches\n\t}\n\n\t/**\n\t * Get the spin orchestrator model with default applied\n\t * Default is defined in SpinAgentSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetSpinModel(settings?: IloomSettings, mode?: 'swarm'): 'sonnet' | 'opus' | 'haiku' {\n\t\tif (mode === 'swarm') {\n\t\t\tif (settings?.spin?.swarmModel) {\n\t\t\t\treturn settings.spin.swarmModel\n\t\t\t}\n\t\t\t// Default to opus for swarm orchestrator (\"Balanced\" mode)\n\t\t\treturn 'opus'\n\t\t}\n\t\treturn settings?.spin?.model ?? SpinAgentSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command model with default applied\n\t * Default is defined in PlanCommandSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetPlanModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.plan?.model ?? PlanCommandSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command planner with default applied\n\t * Default is 'claude'\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Planner provider ('claude', 'gemini', or 'codex')\n\t */\n\tgetPlanPlanner(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' {\n\t\treturn settings?.plan?.planner ?? 'claude'\n\t}\n\n\t/**\n\t * Get the plan command reviewer with default applied\n\t * Default is 'none' (no review step)\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Reviewer provider ('claude', 'gemini', 'codex', or 'none')\n\t */\n\tgetPlanReviewer(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' | 'none' {\n\t\treturn settings?.plan?.reviewer ?? 'none'\n\t}\n\n\t/**\n\t * Get the plan command waveVerification setting with default applied\n\t * Default is true (verification tasks are generated)\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Whether wave verification is enabled\n\t */\n\tgetPlanWaveVerification(settings?: IloomSettings): boolean {\n\t\treturn settings?.plan?.waveVerification !== false\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 +0,0 @@
1
- {"version":3,"sources":["../src/lib/providers/NeonProvider.ts","../src/utils/neon-helpers.ts"],"sourcesContent":["import { execa, type ExecaError } from 'execa'\nimport type { DatabaseProvider } from '../../types/index.js'\nimport { getLogger } from '../../utils/logger-context.js'\nimport { promptConfirmation } from '../../utils/prompt.js'\n\ninterface NeonBranch {\n name: string\n id: string\n [key: string]: unknown\n}\n\nexport interface NeonConfig {\n projectId: string\n parentBranch: string\n}\n\n/**\n * Validate Neon configuration\n * Checks that required configuration values are present\n */\nexport function validateNeonConfig(config: {\n projectId?: string\n parentBranch?: string\n}): { valid: boolean; error?: string } {\n if (!config.projectId) {\n return {\n valid: false,\n error: 'Neon projectId is required. Configure in .iloom/settings.json under databaseProviders.neon',\n }\n }\n\n if (!config.parentBranch) {\n return {\n valid: false,\n error: 'Neon parentBranch is required. Configure in .iloom/settings.json under databaseProviders.neon',\n }\n }\n\n // Basic validation for project ID format (should start with appropriate prefix)\n if (!/^[a-zA-Z0-9-]+$/.test(config.projectId)) {\n return {\n valid: false,\n error: 'Neon projectId contains invalid characters',\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Neon database provider implementation\n * Ports functionality from bash/utils/neon-utils.sh\n */\nexport class NeonProvider implements DatabaseProvider {\n private _isConfigured: boolean = false\n\n constructor(private config: NeonConfig) {\n getLogger().debug('NeonProvider initialized with config:', {\n projectId: config.projectId,\n parentBranch: config.parentBranch,\n hasProjectId: !!config.projectId,\n hasParentBranch: !!config.parentBranch,\n })\n\n // Validate config but don't throw - just mark as not configured\n // This allows the provider to be instantiated even when Neon is not being used\n const validation = validateNeonConfig(config)\n if (!validation.valid) {\n getLogger().debug(`NeonProvider not configured: ${validation.error}`)\n getLogger().debug('Neon database branching will not be used')\n this._isConfigured = false\n } else {\n this._isConfigured = true\n }\n }\n\n /**\n * Check if provider is properly configured\n * Returns true if projectId and parentBranch are valid in settings\n */\n isConfigured(): boolean {\n return this._isConfigured\n }\n\n /**\n * Execute a Neon CLI command and return stdout\n * Throws an error if the command fails\n *\n * @param args - Command arguments to pass to neon CLI\n * @param cwd - Optional working directory to run the command from (defaults to current directory)\n */\n private async executeNeonCommand(args: string[], cwd?: string): Promise<string> {\n // Check if provider is properly configured\n if (!this._isConfigured) {\n throw new Error('NeonProvider is not configured. Check databaseProviders.neon configuration in .iloom/settings.json')\n }\n\n // Log the exact command being executed for debugging\n const command = `neon ${args.join(' ')}`\n getLogger().debug(`Executing Neon CLI command: ${command}`)\n getLogger().debug(`Project ID being used: ${this.config.projectId}`)\n if (cwd) {\n getLogger().debug(`Working directory: ${cwd}`)\n }\n\n const result = await execa('neon', args, {\n timeout: 30000,\n encoding: 'utf8',\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return result.stdout\n }\n\n /**\n * Check if neon CLI is available\n * Ports: check_neon_cli() from bash/utils/neon-utils.sh:18-23\n */\n async isCliAvailable(): Promise<boolean> {\n try {\n await execa('command', ['-v', 'neon'], {\n timeout: 5000,\n shell: true,\n })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Check if user is authenticated with Neon CLI\n * Ports: check_neon_auth() from bash/utils/neon-utils.sh:25-36\n *\n * @param cwd - Optional working directory to run the command from (prevents issues with deleted directories)\n * @throws Error if authentication check fails for reasons other than not being authenticated\n */\n async isAuthenticated(cwd?: string): Promise<boolean> {\n const cliAvailable = await this.isCliAvailable()\n if (!cliAvailable) {\n return false\n }\n\n try {\n await execa('neon', ['me'], {\n timeout: 10000,\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return true\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr?.trim() ?? ''\n\n // Check for authentication failure patterns (should return false, not throw)\n const isAuthError =\n stderr.toLowerCase().includes('not authenticated') ||\n stderr.toLowerCase().includes('not logged in') ||\n stderr.toLowerCase().includes('authentication required') ||\n stderr.toLowerCase().includes('login required')\n\n if (isAuthError) {\n return false\n }\n\n // For any other error, let it bubble up\n throw error\n }\n }\n\n /**\n * Sanitize branch name for Neon (replace slashes with underscores)\n * Ports: sanitize_neon_branch_name() from bash/utils/neon-utils.sh:11-15\n */\n sanitizeBranchName(branchName: string): string {\n return branchName.replace(/\\//g, '_')\n }\n\n /**\n * Extract endpoint ID from Neon connection string\n * Pattern matches: ep-abc-123 or ep-abc-123-pooler\n * Returns: ep-abc-123 (without -pooler suffix)\n * Used by: get_neon_branch_name() from bash/utils/neon-utils.sh:294\n */\n private extractEndpointId(connectionString: string): string | null {\n // First, extract the full host part between @ and first dot\n // Examples:\n // @ep-abc123.us-east-1.neon.tech -> ep-abc123\n // @ep-abc123-pooler.us-east-1.neon.tech -> ep-abc123-pooler\n const hostMatch = connectionString.match(/@(ep-[a-z0-9-]+)\\./)\n if (!hostMatch?.[1]) {\n return null\n }\n\n const fullEndpoint = hostMatch[1]\n // Remove -pooler suffix if present\n return fullEndpoint.replace(/-pooler$/, '')\n }\n\n /**\n * List all branches in the Neon project\n * Ports: list_neon_branches() from bash/utils/neon-utils.sh:63-74\n *\n * @param cwd - Optional working directory to run commands from\n */\n async listBranches(cwd?: string): Promise<string[]> {\n const output = await this.executeNeonCommand([\n 'branches',\n 'list',\n '--project-id',\n this.config.projectId,\n '--output',\n 'json',\n ], cwd)\n\n const branches: NeonBranch[] = JSON.parse(output)\n return branches.map(branch => branch.name)\n }\n\n /**\n * Check if a branch exists\n * Ports: check_neon_branch_exists() from bash/utils/neon-utils.sh:38-61\n *\n * @param name - Branch name to check\n * @param cwd - Optional working directory to run commands from\n */\n async branchExists(name: string, cwd?: string): Promise<boolean> {\n const branches = await this.listBranches(cwd)\n return branches.includes(name)\n }\n\n /**\n * Get connection string for a specific branch\n * Ports: get_neon_connection_string() from bash/utils/neon-utils.sh:76-90\n *\n * @param branch - Branch name to get connection string for\n * @param cwd - Optional working directory to run commands from\n */\n async getConnectionString(branch: string, cwd?: string): Promise<string> {\n const connectionString = await this.executeNeonCommand([\n 'connection-string',\n '--branch',\n branch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n return connectionString.trim()\n }\n\n /**\n * Find Vercel preview database branch\n * Checks for both patterns: preview/<branch> and preview_<sanitized-branch>\n * Ports: find_preview_database_branch() from bash/utils/neon-utils.sh:92-124\n *\n * @param branchName - Branch name to find preview for\n * @param cwd - Optional working directory to run commands from\n */\n async findPreviewBranch(branchName: string, cwd?: string): Promise<string | null> {\n // Check for exact preview branch match with slash pattern\n const slashPattern = `preview/${branchName}`\n if (await this.branchExists(slashPattern, cwd)) {\n getLogger().info(`Found Vercel preview database: ${slashPattern}`)\n return slashPattern\n }\n\n // Check for underscore pattern variation\n const sanitized = this.sanitizeBranchName(branchName)\n const underscorePattern = `preview_${sanitized}`\n if (await this.branchExists(underscorePattern, cwd)) {\n getLogger().info(`Found Vercel preview database: ${underscorePattern}`)\n return underscorePattern\n }\n\n return null\n }\n\n /**\n * Remove expiration date from a Neon branch\n * Used when parent branches need to have child branches created\n * Neon limitation: \"Branches with an expiration date cannot have child branches\"\n *\n * @param branchName - Name of the branch to remove expiration from\n * @param cwd - Optional working directory to run commands from\n */\n private async removeExpiration(branchName: string, cwd?: string): Promise<void> {\n getLogger().info(`Removing expiration date from branch: ${branchName}`)\n await this.executeNeonCommand([\n 'branches',\n 'set-expiration',\n branchName,\n '--project-id',\n this.config.projectId,\n ], cwd)\n getLogger().success(`Expiration date removed from ${branchName}`)\n }\n\n /**\n * Create a new database branch\n * ALWAYS checks for Vercel preview database first\n * Handles Neon limitation where parent branches with expiration dates cannot have children\n * Returns connection string for the branch\n * Ports: create_neon_database_branch() from bash/utils/neon-utils.sh:126-187\n *\n * @param name - Name for the new branch\n * @param fromBranch - Parent branch to create from (defaults to config.parentBranch)\n * @param cwd - Optional working directory to run commands from\n */\n async createBranch(name: string, fromBranch?: string, cwd?: string): Promise<string> {\n // Always check for existing Vercel preview database first (lines 149-158)\n const previewBranch = await this.findPreviewBranch(name, cwd)\n if (previewBranch) {\n const connectionString = await this.getConnectionString(previewBranch, cwd)\n getLogger().success(`Using existing Vercel preview database: ${previewBranch}`)\n return connectionString\n }\n\n // Sanitize branch name for Neon (replace slashes with underscores)\n const sanitizedName = this.sanitizeBranchName(name)\n const parentBranch = fromBranch ?? this.config.parentBranch\n\n getLogger().info('Creating Neon database branch...')\n getLogger().info(` Parent branch: ${parentBranch}`)\n getLogger().info(` New branch: ${sanitizedName}`)\n\n try {\n // Create the database branch\n await this.executeNeonCommand([\n 'branches',\n 'create',\n '--name',\n sanitizedName,\n '--parent',\n parentBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n } catch (error) {\n // Check if error is about parent branch having an expiration date\n const errorMessage = error instanceof Error ? error.message : String(error)\n if (errorMessage.toLowerCase().includes('expiration') &&\n errorMessage.toLowerCase().includes('child')) {\n getLogger().warn('Parent branch has an expiration date - removing it to allow child branch creation')\n\n // Remove expiration from parent branch\n await this.removeExpiration(parentBranch, cwd)\n\n // Retry branch creation\n getLogger().info('Retrying database branch creation...')\n await this.executeNeonCommand([\n 'branches',\n 'create',\n '--name',\n sanitizedName,\n '--parent',\n parentBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n } else {\n // Re-throw if it's a different error\n throw error\n }\n }\n\n getLogger().success('Database branch created successfully')\n\n // Get the connection string for the new branch\n getLogger().info('Getting connection string for new database branch...')\n const connectionString = await this.getConnectionString(sanitizedName, cwd)\n\n return connectionString\n }\n\n /**\n * Delete a database branch\n * Includes preview database protection with user confirmation\n * Ports: delete_neon_database_branch() from bash/utils/neon-utils.sh:204-259\n *\n * @param name - Name of the branch to delete\n * @param isPreview - Whether this is a preview database branch\n * @param cwd - Optional working directory to run commands from (prevents issues with deleted directories)\n */\n async deleteBranch(name: string, isPreview: boolean = false, cwd?: string): Promise<import('../../types/index.js').DatabaseDeletionResult> {\n // Sanitize branch name for Neon\n const sanitizedName = this.sanitizeBranchName(name)\n\n // For preview contexts, check for preview databases first\n if (isPreview) {\n const previewBranch = await this.findPreviewBranch(name, cwd)\n if (previewBranch) {\n getLogger().warn(`Found Vercel preview database: ${previewBranch}`)\n getLogger().warn('Preview databases are managed by Vercel and will be cleaned up automatically')\n getLogger().warn('Manual deletion may interfere with Vercel\\'s preview deployments')\n\n const confirmed = await promptConfirmation(\n 'Delete preview database anyway?',\n false\n )\n\n if (confirmed) {\n // User confirmed - delete preview branch\n try {\n getLogger().info(`Deleting Vercel preview database: ${previewBranch}`)\n await this.executeNeonCommand([\n 'branches',\n 'delete',\n previewBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n getLogger().success('Preview database deleted successfully')\n return {\n success: true,\n deleted: true,\n notFound: false,\n branchName: previewBranch\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n getLogger().error(`Failed to delete preview database: ${errorMessage}`)\n return {\n success: false,\n deleted: false,\n notFound: false,\n error: errorMessage,\n branchName: previewBranch\n }\n }\n } else {\n // User declined deletion\n getLogger().info('Skipping preview database deletion')\n return {\n success: true,\n deleted: false,\n notFound: false,\n userDeclined: true,\n branchName: previewBranch\n }\n }\n }\n // If no preview database found, fall through to check regular branch\n }\n\n // Check for regular branch\n getLogger().info(`Checking for Neon database branch: ${sanitizedName}`)\n\n try {\n const exists = await this.branchExists(sanitizedName, cwd)\n\n if (!exists) {\n getLogger().info(`No database branch found for '${name}'`)\n return {\n success: true,\n deleted: false,\n notFound: true,\n branchName: sanitizedName\n }\n }\n\n // Branch exists - delete it\n getLogger().info(`Deleting Neon database branch: ${sanitizedName}`)\n await this.executeNeonCommand([\n 'branches',\n 'delete',\n sanitizedName,\n '--project-id',\n this.config.projectId,\n ], cwd)\n getLogger().success('Database branch deleted successfully')\n\n return {\n success: true,\n deleted: true,\n notFound: false,\n branchName: sanitizedName\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n getLogger().error(`Failed to delete database branch: ${errorMessage}`)\n return {\n success: false,\n deleted: false,\n notFound: false,\n error: errorMessage,\n branchName: sanitizedName\n }\n }\n }\n\n /**\n * Get branch name from endpoint ID (reverse lookup)\n * Searches all branches to find one with matching endpoint\n * Ports: get_neon_branch_name() from bash/utils/neon-utils.sh:262-308\n *\n * @param endpointId - Endpoint ID to search for\n * @param cwd - Optional working directory to run commands from\n */\n async getBranchNameFromEndpoint(endpointId: string, cwd?: string): Promise<string | null> {\n const branches = await this.listBranches(cwd)\n\n for (const branch of branches) {\n try {\n const connectionString = await this.getConnectionString(branch, cwd)\n const branchEndpointId = this.extractEndpointId(connectionString)\n\n if (branchEndpointId === endpointId) {\n return branch\n }\n } catch {\n // Skip branches that fail to get connection string\n continue\n }\n }\n\n return null\n }\n\n /**\n * Get branch name from a connection string (reverse lookup)\n * Extracts endpoint ID from connection string and finds matching branch\n *\n * @param connectionString - Neon connection string (e.g., postgres://...@ep-abc-123.region.neon.tech/...)\n * @param cwd - Optional working directory to run commands from\n */\n async getBranchNameFromConnectionString(connectionString: string, cwd?: string): Promise<string | null> {\n const endpointId = this.extractEndpointId(connectionString)\n if (!endpointId) {\n getLogger().debug('Could not extract endpoint ID from connection string')\n return null\n }\n return this.getBranchNameFromEndpoint(endpointId, cwd)\n }\n}\n","import { NeonProvider } from '../lib/providers/NeonProvider.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\n\n/**\n * Create NeonProvider from settings configuration\n * Returns provider with isConfigured() = false if neon settings missing\n */\nexport function createNeonProviderFromSettings(settings: IloomSettings): NeonProvider {\n\tconst neonConfig = settings.databaseProviders?.neon\n\n\treturn new NeonProvider({\n\t\tprojectId: neonConfig?.projectId ?? '',\n\t\tparentBranch: neonConfig?.parentBranch ?? '',\n\t})\n}\n"],"mappings":"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","names":["connectionString"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/first-run-setup.ts","../src/commands/start.ts"],"sourcesContent":["import { existsSync } from 'fs'\nimport { readFile } from 'fs/promises'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { logger } from './logger.js'\nimport { FirstRunManager } from './FirstRunManager.js'\nimport { getRepoRoot } from './git.js'\nimport { promptConfirmation, waitForKeypress } from './prompt.js'\n\n/**\n * Get the project root path for first-run tracking\n * Uses git repo root if available, otherwise falls back to cwd\n * This ensures consistent path resolution regardless of where the CLI is run from\n */\nasync function getProjectRoot(): Promise<string> {\n\tconst repoRoot = await getRepoRoot()\n\tif (repoRoot) {\n\t\tlogger.debug(`getProjectRoot: Using git repo root: ${repoRoot}`)\n\t\treturn repoRoot\n\t}\n\tconst cwd = process.cwd()\n\tlogger.debug(`getProjectRoot: Not in git repo, using cwd: ${cwd}`)\n\treturn cwd\n}\n\n/**\n * Check if project needs first-run setup\n * Returns true if:\n * 1. Project is not tracked as configured globally AND\n * 2. .iloom directory is missing or settings files are empty\n *\n * Uses git repo root for path resolution to ensure consistent behavior\n * regardless of whether the CLI is run from a subdirectory or worktree\n */\nexport async function needsFirstRunSetup(): Promise<boolean> {\n\tconst projectRoot = await getProjectRoot()\n\tconst firstRunManager = new FirstRunManager()\n\n\t// Check if project is tracked as configured globally\n\tconst isConfigured = await firstRunManager.isProjectConfigured(projectRoot)\n\tif (isConfigured) {\n\t\tlogger.debug('needsFirstRunSetup: Project is tracked as configured globally')\n\t\treturn false\n\t}\n\n\tconst iloomDir = path.join(projectRoot, '.iloom')\n\n\t// Check if .iloom directory exists\n\tif (!existsSync(iloomDir)) {\n\t\treturn true\n\t}\n\n\t// Check if either settings file has meaningful content\n\tconst settingsPath = path.join(iloomDir, 'settings.json')\n\tconst settingsLocalPath = path.join(iloomDir, 'settings.local.json')\n\n\tconst hasSettings = await hasNonEmptySettings(settingsPath)\n\tconst hasLocalSettings = await hasNonEmptySettings(settingsLocalPath)\n\n\treturn !hasSettings && !hasLocalSettings\n}\n\nasync function hasNonEmptySettings(filePath: string): Promise<boolean> {\n\tif (!existsSync(filePath)) return false\n\ttry {\n\t\tconst content = await readFile(filePath, 'utf-8')\n\t\tconst parsed = JSON.parse(content)\n\t\treturn Object.keys(parsed).length > 0\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Display default configuration values in a formatted box\n */\nfunction displayDefaultsBox(): void {\n\tlogger.info(chalk.bold('Default Configuration:'))\n\tlogger.info('')\n\tlogger.info(` ${chalk.cyan('Main Branch:')} main`)\n\tlogger.info(` ${chalk.cyan('IDE:')} vscode`)\n\tlogger.info(` ${chalk.cyan('Issue Tracker:')} GitHub Issues`)\n\tlogger.info(` ${chalk.cyan('Merge Mode:')} local ${chalk.dim('(merge locally)')}`)\n\tlogger.info(` ${chalk.cyan('Base Port:')} 3000`)\n}\n\n/**\n * Launch interactive first-run setup via InitCommand\n * Shows defaults first, allows quick acceptance or full wizard\n */\nexport async function launchFirstRunSetup(): Promise<void> {\n\tlogger.info('First-time project setup detected.')\n\tlogger.info('')\n\n\t// Display the defaults\n\tdisplayDefaultsBox()\n\n\tlogger.info('')\n\n\t// Import prompt utility\n\n\t// Ask if defaults are OK (default to Yes)\n\tconst acceptDefaults = await promptConfirmation(\n\t\t'Are these defaults OK?',\n\t\ttrue // default to true, so Enter accepts\n\t)\n\n\tif (acceptDefaults) {\n\t\t// User accepted defaults - just mark as configured\n\t\tconst projectRoot = await getProjectRoot()\n\t\tconst firstRunManager = new FirstRunManager()\n\t\tawait firstRunManager.markProjectAsConfigured(projectRoot)\n\t\tlogger.info(chalk.green('Configuration complete! Using defaults.'))\n\t\tlogger.info('You can run `il init` anytime to customize settings.')\n\t\treturn\n\t}\n\n\t// User declined - launch full wizard\n\tlogger.info('')\n\tlogger.info('iloom will now launch an interactive configuration session with Claude.')\n\n\tawait waitForKeypress('Press any key to start configuration...')\n\n\tconst { InitCommand } = await import('../commands/init.js')\n\tconst initCommand = new InitCommand()\n\tawait initCommand.execute(\n\t\t'Help me configure iloom settings for this project. This is my first time using iloom here. Note: Your iloom command will execute once we are done with configuration changes.'\n\t)\n\t// Note: InitCommand.execute() now handles markProjectAsConfigured() internally\n\t// when the guided init completes successfully\n\n\tlogger.info('Configuration complete! Continuing with your original command...')\n}\n","import path from 'path'\nimport { getLogger } from '../utils/logger-context.js'\nimport type { IssueTracker } from '../lib/IssueTracker.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { LoomManager } from '../lib/LoomManager.js'\nimport { DefaultBranchNamingService } from '../lib/BranchNamingService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { EnvironmentManager } from '../lib/EnvironmentManager.js'\nimport { ClaudeContextManager } from '../lib/ClaudeContextManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { CLIIsolationManager } from '../lib/CLIIsolationManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { DatabaseManager } from '../lib/DatabaseManager.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { matchIssueIdentifier } from '../utils/IdentifierParser.js'\nimport { loadEnvIntoProcess } from '../utils/env.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport { createNeonProviderFromSettings } from '../utils/neon-helpers.js'\nimport { getConfiguredRepoFromSettings, hasMultipleRemotes } from '../utils/remote.js'\nimport { capitalizeFirstLetter } from '../utils/text.js'\nimport type { StartOptions, StartResult } from '../types/index.js'\nimport { fetchChildIssues, fetchChildIssueDetails } from '../utils/list-children.js'\nimport { buildDependencyMap } from '../utils/dependency-map.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { launchFirstRunSetup, needsFirstRunSetup } from '../utils/first-run-setup.js'\nimport { isInteractiveEnvironment, promptConfirmation } from '../utils/prompt.js'\nimport { TelemetryService } from '../lib/TelemetryService.js'\nimport type { LoomCreatedProperties } from '../types/telemetry.js'\nimport { resolveRecapFilePath, readRecapFile, writeRecapFile } from '../utils/mcp.js'\n\nexport interface StartCommandInput {\n\tidentifier: string\n\toptions: StartOptions\n}\n\nexport interface ParsedInput {\n\ttype: 'issue' | 'pr' | 'branch' | 'description' | 'epic'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n}\n\nexport class StartCommand {\n\tprivate issueTracker: IssueTracker\n\tprivate loomManager: LoomManager | null = null\n\tprivate settingsManager: SettingsManager\n\tprivate providedLoomManager: LoomManager | undefined\n\tprivate githubService: GitHubService | null = null\n\n\tconstructor(\n\t\tissueTracker: IssueTracker,\n\t\tloomManager?: LoomManager,\n\t\t_agentManager?: AgentManager, // Kept for API compatibility\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\tthis.issueTracker = issueTracker\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\t// Store provided LoomManager for testing, but don't initialize yet\n\t\tthis.providedLoomManager = loomManager\n\n\t\t// Load environment variables first\n\t\tconst envResult = loadEnvIntoProcess()\n\t\tif (envResult.error) {\n\t\t\tgetLogger().debug(`Environment loading warning: ${envResult.error.message}`)\n\t\t}\n\t\tif (envResult.parsed) {\n\t\t\tgetLogger().debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`)\n\t\t}\n\t}\n\n\t/**\n\t * Get or create a GitHubService instance for PR operations\n\t * Used when the configured issue tracker doesn't support PRs (e.g., Linear)\n\t */\n\tprivate getGitHubService(): GitHubService {\n\t\tthis.githubService ??= new GitHubService()\n\t\treturn this.githubService\n\t}\n\n\t/**\n\t * Initialize LoomManager with the main worktree path\n\t * Uses lazy initialization to ensure we have the correct path\n\t */\n\tprivate async initializeLoomManager(): Promise<LoomManager> {\n\t\tif (this.loomManager) {\n\t\t\treturn this.loomManager\n\t\t}\n\n\t\tif (this.providedLoomManager) {\n\t\t\tthis.loomManager = this.providedLoomManager\n\t\t\treturn this.loomManager\n\t\t}\n\n\t\t// Find main worktree path\n\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings()\n\n\t\t// Load settings to get database configuration\n\t\tconst settings = await this.settingsManager.loadSettings()\n\n\t\t// Create DatabaseManager with NeonProvider and EnvironmentManager\n\t\tconst environmentManager = new EnvironmentManager()\n\t\tconst neonProvider = createNeonProviderFromSettings(settings)\n\t\tconst databaseUrlEnvVarName = settings.capabilities?.database?.databaseUrlEnvVarName ?? 'DATABASE_URL'\n\n\t\tconst databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName)\n\n\t\t// Create BranchNamingService (defaults to Claude-based strategy)\n\t\tconst branchNaming = new DefaultBranchNamingService({ useClaude: true })\n\n\t\tthis.loomManager = new LoomManager(\n\t\t\tnew GitWorktreeManager(mainWorktreePath),\n\t\t\tthis.issueTracker,\n\t\t\tbranchNaming, // Add branch naming service\n\t\t\tenvironmentManager, // Reuse same instance\n\t\t\tnew ClaudeContextManager(),\n\t\t\tnew ProjectCapabilityDetector(),\n\t\t\tnew CLIIsolationManager(),\n\t\t\tthis.settingsManager, // Use same instance with CLI overrides\n\t\t\tdatabaseManager // Add database manager\n\t\t)\n\n\t\treturn this.loomManager\n\t}\n\n\t/**\n\t * Main entry point for the start command\n\t */\n\tpublic async execute(input: StartCommandInput): Promise<StartResult | void> {\n\t\tconst isJsonMode = input.options.json === true\n\n\t\t// Handle --create-only flag: disable all launch components\n\t\tif (input.options.createOnly) {\n\t\t\tinput.options.claude = false\n\t\t\tinput.options.code = false\n\t\t\tinput.options.devServer = false\n\t\t\tinput.options.terminal = false\n\t\t}\n\n\t\ttry {\n\t\t\t// Step 0: Load settings and get configured repo for GitHub operations\n\t\t\tconst initialSettings = await this.settingsManager.loadSettings()\n\n\t\t\t// Skip first-run setup in JSON mode\n\t\t\tif (!isJsonMode && (process.env.FORCE_FIRST_TIME_SETUP === \"true\" || await needsFirstRunSetup())) {\n\t\t\t\tawait launchFirstRunSetup()\n\t\t\t\t// Reload settings and recreate issueTracker if provider changed during setup\n\t\t\t\tconst newSettings = await this.settingsManager.loadSettings()\n\t\t\t\tconst newProvider = newSettings.issueManagement?.provider ?? 'github'\n\t\t\t\tif (newProvider !== this.issueTracker.providerName) {\n\t\t\t\t\tgetLogger().debug(`Reinitializing issue tracker: provider changed to \"${newProvider}\"`)\n\t\t\t\t\tthis.issueTracker = IssueTrackerFactory.create(newSettings)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet repo: string | undefined\n\n\t\t\t// Only get repo if we have multiple remotes (prehook already validated config)\n\t\t\tif (this.issueTracker.providerName === 'github' && (await hasMultipleRemotes())) {\n\t\t\t\t// Only relevant for GitHub - Linear doesn't use repo info\n\t\t\t\trepo = await getConfiguredRepoFromSettings(initialSettings)\n\t\t\t\tgetLogger().info(`Using GitHub repository: ${repo}`)\n\t\t\t}\n\n\t\t\t// Step 0.5: Initialize LoomManager with main worktree path\n\t\t\tconst loomManager = await this.initializeLoomManager()\n\n\t\t\t// Step 0.6: Detect if running from inside an existing loom (for nested loom support)\n\t\t\tlet parentLoom = await this.detectParentLoom(loomManager)\n\n\t\t\t// Step 1: Parse and validate input (pass repo to methods)\n\t\t\tconst parsed = await this.parseInput(input.identifier, repo)\n\n\t\t\t// Step 2: Validate based on type\n\t\t\tawait this.validateInput(parsed, repo)\n\n\t\t\t// Step 2.4: Handle child loom decision\n\t\t\tif (parentLoom) {\n\n\t\t\t\t// Format display message based on parent type\n\t\t\t\tconst parentDisplay = parentLoom.type === 'issue'\n\t\t\t\t\t? `issue #${parentLoom.identifier}`\n\t\t\t\t\t: parentLoom.type === 'pr'\n\t\t\t\t\t? `PR #${parentLoom.identifier}`\n\t\t\t\t\t: `branch ${parentLoom.identifier}`\n\n\t\t\t\t// Check for explicit flag first\n\t\t\t\tif (input.options.childLoom === true) {\n\t\t\t\t\t// --child-loom flag: force child loom (no prompt)\n\t\t\t\t\tgetLogger().info(`Creating as child loom of ${parentDisplay} (--child-loom flag)`)\n\t\t\t\t} else if (input.options.childLoom === false) {\n\t\t\t\t\t// --no-child-loom flag: force independent (no prompt)\n\t\t\t\t\tparentLoom = null\n\t\t\t\t\tgetLogger().info('Creating as independent loom (--no-child-loom flag)')\n\t\t\t\t} else {\n\t\t\t\t\t// No flag: use existing behavior (prompt or error if non-interactive)\n\t\t\t\t\t// JSON mode requires explicit flag\n\t\t\t\t\tif (isJsonMode) {\n\t\t\t\t\t\tthrow new Error('JSON mode requires explicit --child-loom or --no-child-loom flag when running from inside a loom')\n\t\t\t\t\t}\n\t\t\t\t\tlet createAsChild = true // Default for non-interactive\n\t\t\t\t\tif (isInteractiveEnvironment()) {\n\t\t\t\t\t\tcreateAsChild = await promptConfirmation(\n\t\t\t\t\t\t\t`You are not in your main worktree. Create as a child loom of ${parentDisplay}?`,\n\t\t\t\t\t\t\ttrue // Default yes\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new Error('Non-interactive environment detected, use either --child-loom or --no-child-loom to specify behavior')\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!createAsChild) {\n\t\t\t\t\t\tparentLoom = null // User declined, proceed as normal loom\n\t\t\t\t\t\tgetLogger().info('Creating as independent loom')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (input.options.childLoom === true) {\n\t\t\t\t// --child-loom flag but not in a parent loom - ignore silently (per requirements)\n\t\t\t\tgetLogger().debug('--child-loom flag provided but not running from inside an existing loom (ignored)')\n\t\t\t}\n\t\t\t// Note: --no-child-loom when no parent is a no-op (already independent)\n\n\t\t\t// Step 2.5: Handle description input - create issue\n\t\t\tif (parsed.type === 'description') {\n\t\t\t\tgetLogger().info('Creating issue from description...')\n\t\t\t\t// Apply first-letter capitalization to title and body\n\t\t\t\tconst title = capitalizeFirstLetter(parsed.originalInput)\n\t\t\t\tconst body = input.options.body ? capitalizeFirstLetter(input.options.body) : \"\"\n\t\t\t\tconst result = await this.issueTracker.createIssue(\n\t\t\t\t\ttitle, // Use capitalized description as title\n\t\t\t\t\tbody // Use capitalized body or empty\n\t\t\t\t)\n\t\t\t\tgetLogger().success(`Created issue #${result.number}: ${result.url}`)\n\t\t\t\t// Update parsed to be an issue type with the new number\n\t\t\t\tparsed.type = 'issue'\n\t\t\t\tparsed.number = result.number\n\t\t\t}\n\n\t\t\t// Step 2.6: Detect epic (issue with child issues) and handle --epic/--no-epic flags\n\t\t\tlet childIssueNumbers: string[] = []\n\t\t\tlet childIssues: Array<{ number: string; title: string; body: string; url: string }> = []\n\t\t\tlet dependencyMap: Record<string, string[]> = {}\n\n\t\t\tif (parsed.type === 'issue' && parsed.number) {\n\t\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\t\tconst epicIssueTracker = IssueTrackerFactory.create(settings)\n\t\t\t\tlet children: Awaited<ReturnType<typeof fetchChildIssues>> = []\n\t\t\t\ttry {\n\t\t\t\t\tchildren = await fetchChildIssues(String(parsed.number), epicIssueTracker, repo)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().warn(`Failed to check for child issues: ${error instanceof Error ? error.message : 'Unknown error'}. Proceeding as normal loom.`)\n\t\t\t\t}\n\n\t\t\t\tif (children.length > 0) {\n\t\t\t\t\tchildIssueNumbers = children.map(c => c.id)\n\t\t\t\t\tlet createAsEpic = false\n\n\t\t\t\t\tif (input.options.epic === true) {\n\t\t\t\t\t\t// --epic flag: force epic mode (no prompt)\n\t\t\t\t\t\tcreateAsEpic = true\n\t\t\t\t\t\tgetLogger().info(`Creating as epic loom with ${children.length} child issue(s) (--epic flag)`)\n\t\t\t\t\t} else if (input.options.epic === false) {\n\t\t\t\t\t\t// --no-epic flag: proceed as normal loom (no prompt)\n\t\t\t\t\t\tcreateAsEpic = false\n\t\t\t\t\t\tgetLogger().info('Creating as normal loom (--no-epic flag)')\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// No flag: prompt or error\n\t\t\t\t\t\tif (isJsonMode) {\n\t\t\t\t\t\t\tthrow new Error('JSON mode requires explicit --epic or --no-epic flag when issue has child issues')\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (isInteractiveEnvironment()) {\n\t\t\t\t\t\t\tcreateAsEpic = await promptConfirmation(\n\t\t\t\t\t\t\t\t`This issue has ${children.length} child issue(s). Create as epic loom?`,\n\t\t\t\t\t\t\t\ttrue // Default yes\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new Error('Non-interactive environment detected, use either --epic or --no-epic to specify behavior')\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (createAsEpic) {\n\t\t\t\t\t\tparsed.type = 'epic'\n\n\t\t\t\t\t\t// Fetch rich child issue details and dependency map for epic metadata\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [details, depMap] = await Promise.all([\n\t\t\t\t\t\t\t\tfetchChildIssueDetails(String(parsed.number), epicIssueTracker, repo),\n\t\t\t\t\t\t\t\tbuildDependencyMap(childIssueNumbers, settings, repo),\n\t\t\t\t\t\t\t])\n\t\t\t\t\t\t\tchildIssues = details ?? []\n\t\t\t\t\t\t\tdependencyMap = depMap ?? {}\n\t\t\t\t\t\t\tgetLogger().info(`Fetched ${childIssues.length} child issue details and dependency map`)\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Revert to issue type since child data fetch failed\n\t\t\t\t\t\t\t// il spin needs child data to enter swarm mode, so an epic without it would be broken\n\t\t\t\t\t\t\tparsed.type = 'issue'\n\t\t\t\t\t\t\tchildIssueNumbers = []\n\t\t\t\t\t\t\tgetLogger().warn(`Failed to fetch epic child data, reverting to normal loom: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Not creating as epic, clear child issue numbers\n\t\t\t\t\t\tchildIssueNumbers = []\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// --epic or --no-epic flags are silently ignored when there are no child issues\n\t\t\t}\n\n\t\t\t// Step 2.7: Confirm bypassPermissions mode if applicable\n\t\t\t// Only prompt in interactive mode when Claude is enabled.\n\t\t\t// Skip when: --no-claude (Claude won't launch now), JSON mode (non-interactive).\n\t\t\t// The explicit --one-shot=bypassPermissions flag is sufficient intent.\n\t\t\t// The warning is shown again when Claude launches via 'il spin'.\n\t\t\tif (input.options.oneShot === 'bypassPermissions' && input.options.claude !== false && !isJsonMode) {\n\t\t\t\tconst confirmed = await promptConfirmation(\n\t\t\t\t\t'WARNING: bypassPermissions mode will allow Claude to execute all tool calls without confirmation. ' +\n\t\t\t\t\t'This can be dangerous. Do you want to proceed?'\n\t\t\t\t)\n\t\t\t\tif (!confirmed) {\n\t\t\t\t\tgetLogger().info('Operation cancelled by user')\n\t\t\t\t\tprocess.exit(0)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Step 2.8: Load workflow-specific settings with CLI overrides\n\t\t\tconst cliOverrides = extractSettingsOverrides()\n\t\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\t\tconst workflowType = parsed.type === 'branch' ? 'regular' : parsed.type === 'epic' ? 'issue' : parsed.type\n\t\t\tconst workflowConfig = settings.workflows?.[workflowType]\n\n\t\t\t// Step 2.9: Extract raw --set arguments and executable path for forwarding to spin\n\t\t\tconst { extractRawSetArguments, getExecutablePath } = await import('../utils/cli-overrides.js')\n\t\t\tconst setArguments = extractRawSetArguments()\n\t\t\tconst executablePath = getExecutablePath()\n\n\t\t\t// Step 3: Log success and create loom\n\t\t\tgetLogger().info(`Validated input: ${this.formatParsedInput(parsed)}`)\n\n\t\t\t// Step 4: Create loom using LoomManager\n\t\t\tconst identifier =\n\t\t\t\tparsed.type === 'branch'\n\t\t\t\t\t? parsed.branchName ?? ''\n\t\t\t\t\t: parsed.number ?? 0\n\n\t\t\t// Apply configuration precedence: CLI flags > workflow config > defaults (true)\n\t\t\tconst enableClaude = input.options.claude ?? workflowConfig?.startAiAgent ?? true\n\t\t\tconst enableCode = input.options.code ?? workflowConfig?.startIde ?? true\n\t\t\tconst enableDevServer = input.options.devServer ?? workflowConfig?.startDevServer ?? true\n\t\t\tconst enableTerminal = input.options.terminal ?? workflowConfig?.startTerminal ?? false\n\n\t\t\tgetLogger().debug('Final workflow config values:', {\n\t\t\t\tenableClaude,\n\t\t\t\tenableCode,\n\t\t\t\tenableDevServer,\n\t\t\t\tenableTerminal,\n\t\t\t})\n\n\t\t\tconst loom = await loomManager.createIloom({\n\t\t\t\ttype: parsed.type,\n\t\t\t\tidentifier,\n\t\t\t\toriginalInput: parsed.originalInput,\n\t\t\t\t...(parentLoom && { parentLoom }),\n\t\t\t\toptions: {\n\t\t\t\t\tenableClaude,\n\t\t\t\t\tenableCode,\n\t\t\t\t\tenableDevServer,\n\t\t\t\t\tenableTerminal,\n\t\t\t\t\t...(input.options.oneShot && { oneShot: input.options.oneShot }),\n\t\t\t\t\t...(input.options.complexity && { complexity: input.options.complexity }),\n\t\t\t\t\t...(setArguments.length > 0 && { setArguments }),\n\t\t\t\t\t...(executablePath && { executablePath }),\n\t\t\t\t\t...(childIssueNumbers.length > 0 && { childIssueNumbers }),\n\t\t\t\t\t...(childIssues.length > 0 && { childIssues }),\n\t\t\t\t\t...(Object.keys(dependencyMap).length > 0 && { dependencyMap }),\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tgetLogger().success(`Created loom: ${loom.id} at ${loom.path}`)\n\n\t\t\t// Set recap complexity if overridden via CLI flag\n\t\t\tif (input.options.complexity) {\n\t\t\t\ttry {\n\t\t\t\t\tconst recapFilePath = resolveRecapFilePath(loom.path)\n\t\t\t\t\tconst recap = await readRecapFile(recapFilePath)\n\t\t\t\t\trecap.complexity = { level: input.options.complexity, reason: 'Overridden via CLI flag', timestamp: new Date().toISOString() }\n\t\t\t\t\tawait writeRecapFile(recapFilePath, recap)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().debug(`Failed to set recap complexity: ${error instanceof Error ? error.message : error}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Track loom.created telemetry event\n\t\t\ttry {\n\t\t\t\tconst oneShotMap: Record<string, LoomCreatedProperties['one_shot_mode']> = {\n\t\t\t\t\tnoReview: 'skip-reviews',\n\t\t\t\t\tbypassPermissions: 'yolo',\n\t\t\t\t}\n\t\t\t\tTelemetryService.getInstance().track('loom.created', {\n\t\t\t\t\tsource_type: parsed.type === 'epic' ? 'issue' : parsed.type as LoomCreatedProperties['source_type'],\n\t\t\t\t\ttracker: this.issueTracker.providerName,\n\t\t\t\t\tis_child_loom: !!parentLoom,\n\t\t\t\t\tone_shot_mode: oneShotMap[input.options.oneShot ?? ''] ?? 'default',\n\t\t\t\t\tcomplexity_override: !!input.options.complexity,\n\t\t\t\t\tcreate_only: !!input.options.createOnly,\n\t\t\t\t})\n\t\t\t} catch (error: unknown) {\n\t\t\t\tgetLogger().debug(`Failed to track loom.created telemetry: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t}\n\n\t\t\tgetLogger().info(` Branch: ${loom.branch}`)\n\t\t\t// Only show port for web projects\n\t\t\tif (loom.capabilities?.includes('web')) {\n\t\t\t\tgetLogger().info(` Port: ${loom.port}`)\n\t\t\t}\n\t\t\tif (loom.issueData?.title) {\n\t\t\t\tgetLogger().info(` Title: ${loom.issueData.title}`)\n\t\t\t}\n\t\t\tif (parsed.type === 'epic') {\n\t\t\t\tgetLogger().info(` Epic: yes (${childIssueNumbers.length} child issue(s))`)\n\t\t\t}\n\n\t\t\t// Return StartResult in JSON mode\n\t\t\tif (isJsonMode) {\n\t\t\t\treturn {\n\t\t\t\t\tid: loom.id,\n\t\t\t\t\tpath: loom.path,\n\t\t\t\t\tbranch: loom.branch,\n\t\t\t\t\ttype: parsed.type,\n\t\t\t\t\tidentifier: loom.identifier,\n\t\t\t\t\t...(loom.port !== undefined && { port: loom.port }),\n\t\t\t\t\t...(loom.issueData?.title && { title: loom.issueData.title }),\n\t\t\t\t\t...(loom.capabilities && { capabilities: loom.capabilities }),\n\t\t\t\t\t...(childIssueNumbers.length > 0 && { childIssueNumbers }),\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tgetLogger().error(`${error.message}`)\n\t\t\t} else {\n\t\t\t\tgetLogger().error('An unknown error occurred')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Parse input to determine type and extract relevant data\n\t */\n\tprivate async parseInput(identifier: string, repo?: string): Promise<ParsedInput> {\n\t\t// Check if user wants to skip capitalization by prefixing with space\n\t\t// We preserve this for description types so capitalizeFirstLetter() can handle it\n\t\tconst hasLeadingSpace = identifier.startsWith(' ')\n\n\t\t// Handle empty input\n\t\tconst trimmedIdentifier = identifier.trim()\n\t\tif (!trimmedIdentifier) {\n\t\t\tthrow new Error('Missing required argument: identifier')\n\t\t}\n\n\t\t// Check for description: >15 chars AND has spaces (likely a natural language description)\n\t\t// Short inputs with spaces are rejected later as invalid branch names\n\t\tconst spaceCount = (trimmedIdentifier.match(/ /g) ?? []).length\n\t\tif (trimmedIdentifier.length > 15 && spaceCount >= 1) {\n\t\t\t// Preserve leading space if present so capitalizeFirstLetter() can detect the override\n\t\t\treturn {\n\t\t\t\ttype: 'description',\n\t\t\t\toriginalInput: hasLeadingSpace ? ' ' + trimmedIdentifier : trimmedIdentifier,\n\t\t\t}\n\t\t}\n\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123, Pr-123 (case-insensitive)\n\t\tconst prPattern = /^pr[/-](\\d+)$/i\n\t\tconst prMatch = trimmedIdentifier.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: parseInt(prMatch[1], 10),\n\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue identifier patterns using shared utility\n\t\t// - Project key pattern: ENG-123 (requires at least 2 letters before dash)\n\t\t// - Numeric pattern: #123 or 123 (GitHub format)\n\t\tconst identifierMatch = matchIssueIdentifier(trimmedIdentifier)\n\n\t\tif (identifierMatch.type === 'project-key' && identifierMatch.identifier) {\n\t\t\t// Use IssueTracker to validate it exists\n\t\t\tconst detection = await this.issueTracker.detectInputType(\n\t\t\t\ttrimmedIdentifier,\n\t\t\t\trepo\n\t\t\t)\n\n\t\t\tif (detection.type === 'issue' && detection.identifier) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber: detection.identifier, // Keep as string for project key identifiers\n\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Project key identifier format matched but not found\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find issue matching identifier ${identifierMatch.identifier}`\n\t\t\t)\n\t\t}\n\n\t\t// Check for numeric pattern (could be issue or PR)\n\t\tif (identifierMatch.type === 'numeric' && identifierMatch.identifier) {\n\t\t\tconst number = parseInt(identifierMatch.identifier, 10)\n\n\t\t\t// If issue tracker supports PRs, use it for detection\n\t\t\tif (this.issueTracker.supportsPullRequests) {\n\t\t\t\tconst detection = await this.issueTracker.detectInputType(\n\t\t\t\t\ttrimmedIdentifier,\n\t\t\t\t\trepo\n\t\t\t\t)\n\n\t\t\t\tif (detection.type === 'pr') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'pr',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else if (detection.type === 'issue') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Could not find issue or PR #${number}`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Issue tracker doesn't support PRs (e.g., Linear, Jira)\n\t\t\t\t// Check GitHub first for PR, then fall back to issue tracker for issues\n\t\t\t\tconst githubService = this.getGitHubService()\n\t\t\t\tconst detection = await githubService.detectInputType(trimmedIdentifier, repo)\n\n\t\t\t\tif (detection.type === 'pr') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'pr',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Not a GitHub PR - try the configured issue tracker\n\t\t\t\t\t// This allows future trackers with numeric IDs to work naturally\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnumber,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Treat as branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: trimmedIdentifier,\n\t\t\toriginalInput: trimmedIdentifier,\n\t\t}\n\t}\n\n\t/**\n\t * Validate the parsed input based on its type\n\t */\n\tprivate async validateInput(parsed: ParsedInput, repo?: string): Promise<void> {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\n\t\t\t\t// Determine which service to use for PR operations\n\t\t\t\tif (this.issueTracker.supportsPullRequests && this.issueTracker.fetchPR && this.issueTracker.validatePRState) {\n\t\t\t\t\t// Use issue tracker for PR operations (e.g., GitHub)\n\t\t\t\t\tconst pr = await this.issueTracker.fetchPR(parsed.number, repo)\n\t\t\t\t\tawait this.issueTracker.validatePRState(pr)\n\t\t\t\t} else {\n\t\t\t\t\t// Use GitHubService for PR operations when issue tracker doesn't support PRs (e.g., Linear)\n\t\t\t\t\tconst githubService = this.getGitHubService()\n\t\t\t\t\tconst pr = await githubService.fetchPR(parsed.number as number, repo)\n\t\t\t\t\tawait githubService.validatePRState(pr)\n\t\t\t\t}\n\t\t\t\tgetLogger().debug(`Validated PR #${parsed.number}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'issue': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid issue number')\n\t\t\t\t}\n\t\t\t\t// Fetch and validate issue state\n\t\t\t\tconst issue = await this.issueTracker.fetchIssue(parsed.number, repo)\n\t\t\t\tawait this.issueTracker.validateIssueState(issue)\n\t\t\t\tgetLogger().debug(`Validated issue #${parsed.number}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'branch': {\n\t\t\t\tif (!parsed.branchName) {\n\t\t\t\t\tthrow new Error('Invalid branch name')\n\t\t\t\t}\n\t\t\t\t// Validate branch name characters (from bash script line 586)\n\t\t\t\tif (!this.isValidBranchName(parsed.branchName)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes'\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tgetLogger().debug(`Validated branch name: ${parsed.branchName}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'description': {\n\t\t\t\t// Description inputs are valid - they will be converted to issues\n\t\t\t\tgetLogger().debug('Detected description input', {\n\t\t\t\t\tlength: parsed.originalInput.length\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownType = parsed as { type: string }\n\t\t\t\tthrow new Error(`Unknown input type: ${unknownType.type}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate branch name format\n\t */\n\tprivate isValidBranchName(branch: string): boolean {\n\t\t// Pattern from bash script line 586\n\t\treturn /^[a-zA-Z0-9/_-]+$/.test(branch)\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedInput): string {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr':\n\t\t\t\treturn `PR #${parsed.number}`\n\t\t\tcase 'issue':\n\t\t\t\treturn `Issue #${parsed.number}`\n\t\t\tcase 'epic':\n\t\t\t\treturn `Epic #${parsed.number}`\n\t\t\tcase 'branch':\n\t\t\t\treturn `Branch '${parsed.branchName}'`\n\t\t\tcase 'description':\n\t\t\t\treturn `Description: ${parsed.originalInput.slice(0, 50)}...`\n\t\t\tdefault:\n\t\t\t\treturn 'Unknown input'\n\t\t}\n\t}\n\n\t/**\n\t * Detect if running from inside an existing loom worktree\n\t * Returns parent loom info if detected, null otherwise\n\t */\n\tprivate async detectParentLoom(loomManager: LoomManager): Promise<{\n\t\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\t\tidentifier: string | number\n\t\tbranchName: string\n\t\tworktreePath: string\n\t\tdatabaseBranch?: string\n\t} | null> {\n\t\ttry {\n\t\t\tconst cwd = process.cwd()\n\t\t\tconst looms = await loomManager.listLooms()\n\n\t\t\tif (!looms) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Get main worktree path to exclude it from valid parents\n\t\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings()\n\n\t\t\t// Find loom containing current directory\n\t\t\t// Fix #2: Add path.sep check to prevent false positives (e.g., issue-123 vs issue-1234)\n\t\t\t// Exclude main worktree from being a valid parent\n\t\t\tconst parentLoom = looms.find(loom => {\n\t\t\t\t// Skip main worktree - it shouldn't be a parent for child looms\n\t\t\t\tif (loom.path === mainWorktreePath) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\t// Either exact match OR cwd starts with loom.path followed by path separator\n\t\t\t\treturn cwd === loom.path || cwd.startsWith(loom.path + path.sep)\n\t\t\t})\n\t\t\tif (!parentLoom) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tgetLogger().debug(`Detected parent loom: ${parentLoom.type} ${parentLoom.identifier} at ${parentLoom.path}`)\n\n\t\t\tconst result: {\n\t\t\t\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\t\t\t\tidentifier: string | number\n\t\t\t\tbranchName: string\n\t\t\t\tworktreePath: string\n\t\t\t\tdatabaseBranch?: string\n\t\t\t} = {\n\t\t\t\ttype: parentLoom.type,\n\t\t\t\tidentifier: parentLoom.identifier,\n\t\t\t\tbranchName: parentLoom.branch,\n\t\t\t\tworktreePath: parentLoom.path,\n\t\t\t}\n\n\t\t\t// Only include databaseBranch if it exists (exactOptionalPropertyTypes compatibility)\n\t\t\tif (parentLoom.databaseBranch) {\n\t\t\t\tresult.databaseBranch = parentLoom.databaseBranch\n\t\t\t}\n\n\t\t\t// Try to get database branch from parent's .env file via reverse lookup\n\t\t\tif (!result.databaseBranch) {\n\t\t\t\tconst databaseBranch = await loomManager.getDatabaseBranchForLoom(parentLoom.path)\n\t\t\t\tif (databaseBranch) {\n\t\t\t\t\tresult.databaseBranch = databaseBranch\n\t\t\t\t\tgetLogger().debug(`Detected parent database branch: ${databaseBranch}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\t// If detection fails for any reason, just return null (don't break the start workflow)\n\t\t\tgetLogger().debug(`Failed to detect parent loom: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn null\n\t\t}\n\t}\n\n}\n"],"mappings":"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","names":["path","settings","path"]}