@iloom/cli 0.9.1 → 0.9.2

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 (126) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +21 -2
  3. package/dist/{ClaudeContextManager-X2Y72GRL.js → ClaudeContextManager-HR5JQKAI.js} +4 -4
  4. package/dist/{ClaudeService-7P32TTES.js → ClaudeService-TK7FMC2X.js} +3 -3
  5. package/dist/{GitHubService-O7T6CFAJ.js → GitHubService-TGWJN4V4.js} +3 -3
  6. package/dist/{LoomLauncher-3I47SUPV.js → LoomLauncher-73NXL2CL.js} +4 -4
  7. package/dist/README.md +21 -2
  8. package/dist/{SettingsManager-QR7V2IW2.js → SettingsManager-AW3JTJHD.js} +2 -2
  9. package/dist/{build-IC4CJRMP.js → build-THZI572G.js} +6 -6
  10. package/dist/{chunk-POU2UMWN.js → chunk-3I4ONZRT.js} +3 -3
  11. package/dist/{chunk-TRUMP4DA.js → chunk-4CO6KG5S.js} +26 -3
  12. package/dist/chunk-4CO6KG5S.js.map +1 -0
  13. package/dist/{chunk-MZPRBNYC.js → chunk-6IIL5M2L.js} +2 -2
  14. package/dist/{chunk-2JPXGGP4.js → chunk-A7NJF73J.js} +2 -2
  15. package/dist/{chunk-H6ST2TGP.js → chunk-AR5QKYNE.js} +3 -3
  16. package/dist/{chunk-Y5O2ALDZ.js → chunk-CWRI4JC3.js} +2 -2
  17. package/dist/{chunk-I3HMNWQQ.js → chunk-DGG2VY7B.js} +3 -3
  18. package/dist/{chunk-ENGCJIYQ.js → chunk-FJDRTVJX.js} +3 -3
  19. package/dist/chunk-FJDRTVJX.js.map +1 -0
  20. package/dist/{chunk-NTTSUAVM.js → chunk-IZIYLYPK.js} +2 -2
  21. package/dist/{chunk-TGRK3CHF.js → chunk-KJTVU3HZ.js} +4 -4
  22. package/dist/{chunk-3P6J4IZZ.js → chunk-KXGQYLFZ.js} +3 -3
  23. package/dist/{chunk-USSL2X4A.js → chunk-NUACL52E.js} +2 -2
  24. package/dist/{chunk-JT5LZRMI.js → chunk-NWMORW3U.js} +2 -2
  25. package/dist/{chunk-Y5HSSIK2.js → chunk-OFDN5NKS.js} +4 -3
  26. package/dist/{chunk-Y5HSSIK2.js.map → chunk-OFDN5NKS.js.map} +1 -1
  27. package/dist/{chunk-HZXBHMVM.js → chunk-R4YWBGY6.js} +3 -3
  28. package/dist/{chunk-XFEK2X2D.js → chunk-RI2YL6TK.js} +9 -3
  29. package/dist/chunk-RI2YL6TK.js.map +1 -0
  30. package/dist/{chunk-4ZIHFUPN.js → chunk-SOSQILHO.js} +64 -36
  31. package/dist/chunk-SOSQILHO.js.map +1 -0
  32. package/dist/{chunk-4GAJJUYS.js → chunk-TC7APDKU.js} +2 -2
  33. package/dist/{chunk-OK7LUTRW.js → chunk-ULSWCPQG.js} +3 -3
  34. package/dist/{chunk-5LVVQGB3.js → chunk-VPTAX5TR.js} +7 -7
  35. package/dist/chunk-VPTAX5TR.js.map +1 -0
  36. package/dist/{chunk-RD7OPXZK.js → chunk-W6DP5RVR.js} +3 -3
  37. package/dist/{chunk-J7FJ6PUT.js → chunk-WHI5KEOX.js} +2 -2
  38. package/dist/{chunk-2HZX6AMR.js → chunk-YKFCCV6S.js} +2 -2
  39. package/dist/{chunk-OAVJR4PM.js → chunk-Z2TWEXR7.js} +2 -2
  40. package/dist/{cleanup-D3CSRBBZ.js → cleanup-PJRIFFU4.js} +16 -16
  41. package/dist/cli.js +62 -61
  42. package/dist/cli.js.map +1 -1
  43. package/dist/{commit-IWGT42XN.js → commit-IVP3M4HG.js} +10 -9
  44. package/dist/commit-IVP3M4HG.js.map +1 -0
  45. package/dist/{compile-EOWJORKO.js → compile-R2J65HBQ.js} +6 -6
  46. package/dist/{contribute-WSJTV2RX.js → contribute-VDZXHK5Y.js} +4 -4
  47. package/dist/{dev-server-Q6M62ATG.js → dev-server-7F622OEO.js} +8 -8
  48. package/dist/{feedback-QPNDZQRV.js → feedback-E7VET7CL.js} +6 -6
  49. package/dist/{git-W3XUIFTR.js → git-2QDQ2X2S.js} +3 -3
  50. package/dist/{ignite-OPO6EDYT.js → ignite-IW35CDBD.js} +10 -10
  51. package/dist/index.d.ts +15 -0
  52. package/dist/index.js +11 -4
  53. package/dist/index.js.map +1 -1
  54. package/dist/{init-ALYWKNWG.js → init-676DHF6R.js} +5 -5
  55. package/dist/{issues-L7TBUPXT.js → issues-PJSOLOBJ.js} +31 -9
  56. package/dist/issues-PJSOLOBJ.js.map +1 -0
  57. package/dist/{lint-IHUH45OC.js → lint-CJM7BAIM.js} +6 -6
  58. package/dist/mcp/issue-management-server.js +2 -2
  59. package/dist/mcp/issue-management-server.js.map +1 -1
  60. package/dist/{open-KWOV2OFO.js → open-544H7JF5.js} +8 -8
  61. package/dist/{plan-BRJBFJHF.js → plan-Q7ELXDLC.js} +12 -12
  62. package/dist/prompts/init-prompt.txt +39 -21
  63. package/dist/{rebase-AJOJOZUG.js → rebase-YND35CIE.js} +5 -5
  64. package/dist/{recap-GKJXMDXW.js → recap-3W7COH7D.js} +5 -5
  65. package/dist/{run-QEUVZF7J.js → run-QUXJKDQQ.js} +8 -8
  66. package/dist/schema/settings.schema.json +15 -0
  67. package/dist/{shell-DAAVG4YN.js → shell-QGECBLST.js} +5 -5
  68. package/dist/{summary-ZKOA35PT.js → summary-G2T4452H.js} +9 -9
  69. package/dist/{test-5GPWWO3P.js → test-EA5NQFDC.js} +6 -6
  70. package/dist/{test-git-EJUKDB7F.js → test-git-M7LSLEFL.js} +3 -3
  71. package/dist/{test-prefix-23TOBUXY.js → test-prefix-64NAAUON.js} +3 -3
  72. package/dist/{test-webserver-CKROHFBQ.js → test-webserver-OK6Z5FJM.js} +5 -5
  73. package/dist/{vscode-6TOLFCI2.js → vscode-AR5NNXXI.js} +5 -5
  74. package/package.json +1 -1
  75. package/dist/chunk-4ZIHFUPN.js.map +0 -1
  76. package/dist/chunk-5LVVQGB3.js.map +0 -1
  77. package/dist/chunk-ENGCJIYQ.js.map +0 -1
  78. package/dist/chunk-TRUMP4DA.js.map +0 -1
  79. package/dist/chunk-XFEK2X2D.js.map +0 -1
  80. package/dist/commit-IWGT42XN.js.map +0 -1
  81. package/dist/issues-L7TBUPXT.js.map +0 -1
  82. /package/dist/{ClaudeContextManager-X2Y72GRL.js.map → ClaudeContextManager-HR5JQKAI.js.map} +0 -0
  83. /package/dist/{ClaudeService-7P32TTES.js.map → ClaudeService-TK7FMC2X.js.map} +0 -0
  84. /package/dist/{GitHubService-O7T6CFAJ.js.map → GitHubService-TGWJN4V4.js.map} +0 -0
  85. /package/dist/{LoomLauncher-3I47SUPV.js.map → LoomLauncher-73NXL2CL.js.map} +0 -0
  86. /package/dist/{SettingsManager-QR7V2IW2.js.map → SettingsManager-AW3JTJHD.js.map} +0 -0
  87. /package/dist/{build-IC4CJRMP.js.map → build-THZI572G.js.map} +0 -0
  88. /package/dist/{chunk-POU2UMWN.js.map → chunk-3I4ONZRT.js.map} +0 -0
  89. /package/dist/{chunk-MZPRBNYC.js.map → chunk-6IIL5M2L.js.map} +0 -0
  90. /package/dist/{chunk-2JPXGGP4.js.map → chunk-A7NJF73J.js.map} +0 -0
  91. /package/dist/{chunk-H6ST2TGP.js.map → chunk-AR5QKYNE.js.map} +0 -0
  92. /package/dist/{chunk-Y5O2ALDZ.js.map → chunk-CWRI4JC3.js.map} +0 -0
  93. /package/dist/{chunk-I3HMNWQQ.js.map → chunk-DGG2VY7B.js.map} +0 -0
  94. /package/dist/{chunk-NTTSUAVM.js.map → chunk-IZIYLYPK.js.map} +0 -0
  95. /package/dist/{chunk-TGRK3CHF.js.map → chunk-KJTVU3HZ.js.map} +0 -0
  96. /package/dist/{chunk-3P6J4IZZ.js.map → chunk-KXGQYLFZ.js.map} +0 -0
  97. /package/dist/{chunk-USSL2X4A.js.map → chunk-NUACL52E.js.map} +0 -0
  98. /package/dist/{chunk-JT5LZRMI.js.map → chunk-NWMORW3U.js.map} +0 -0
  99. /package/dist/{chunk-HZXBHMVM.js.map → chunk-R4YWBGY6.js.map} +0 -0
  100. /package/dist/{chunk-4GAJJUYS.js.map → chunk-TC7APDKU.js.map} +0 -0
  101. /package/dist/{chunk-OK7LUTRW.js.map → chunk-ULSWCPQG.js.map} +0 -0
  102. /package/dist/{chunk-RD7OPXZK.js.map → chunk-W6DP5RVR.js.map} +0 -0
  103. /package/dist/{chunk-J7FJ6PUT.js.map → chunk-WHI5KEOX.js.map} +0 -0
  104. /package/dist/{chunk-2HZX6AMR.js.map → chunk-YKFCCV6S.js.map} +0 -0
  105. /package/dist/{chunk-OAVJR4PM.js.map → chunk-Z2TWEXR7.js.map} +0 -0
  106. /package/dist/{cleanup-D3CSRBBZ.js.map → cleanup-PJRIFFU4.js.map} +0 -0
  107. /package/dist/{compile-EOWJORKO.js.map → compile-R2J65HBQ.js.map} +0 -0
  108. /package/dist/{contribute-WSJTV2RX.js.map → contribute-VDZXHK5Y.js.map} +0 -0
  109. /package/dist/{dev-server-Q6M62ATG.js.map → dev-server-7F622OEO.js.map} +0 -0
  110. /package/dist/{feedback-QPNDZQRV.js.map → feedback-E7VET7CL.js.map} +0 -0
  111. /package/dist/{git-W3XUIFTR.js.map → git-2QDQ2X2S.js.map} +0 -0
  112. /package/dist/{ignite-OPO6EDYT.js.map → ignite-IW35CDBD.js.map} +0 -0
  113. /package/dist/{init-ALYWKNWG.js.map → init-676DHF6R.js.map} +0 -0
  114. /package/dist/{lint-IHUH45OC.js.map → lint-CJM7BAIM.js.map} +0 -0
  115. /package/dist/{open-KWOV2OFO.js.map → open-544H7JF5.js.map} +0 -0
  116. /package/dist/{plan-BRJBFJHF.js.map → plan-Q7ELXDLC.js.map} +0 -0
  117. /package/dist/{rebase-AJOJOZUG.js.map → rebase-YND35CIE.js.map} +0 -0
  118. /package/dist/{recap-GKJXMDXW.js.map → recap-3W7COH7D.js.map} +0 -0
  119. /package/dist/{run-QEUVZF7J.js.map → run-QUXJKDQQ.js.map} +0 -0
  120. /package/dist/{shell-DAAVG4YN.js.map → shell-QGECBLST.js.map} +0 -0
  121. /package/dist/{summary-ZKOA35PT.js.map → summary-G2T4452H.js.map} +0 -0
  122. /package/dist/{test-5GPWWO3P.js.map → test-EA5NQFDC.js.map} +0 -0
  123. /package/dist/{test-git-EJUKDB7F.js.map → test-git-M7LSLEFL.js.map} +0 -0
  124. /package/dist/{test-prefix-23TOBUXY.js.map → test-prefix-64NAAUON.js.map} +0 -0
  125. /package/dist/{test-webserver-CKROHFBQ.js.map → test-webserver-OK6Z5FJM.js.map} +0 -0
  126. /package/dist/{vscode-6TOLFCI2.js.map → vscode-AR5NNXXI.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  extractIssueNumber
4
- } from "./chunk-H6ST2TGP.js";
4
+ } from "./chunk-AR5QKYNE.js";
5
5
  import {
6
6
  extractPort,
7
7
  findEnvFileContainingVariable,
@@ -108,4 +108,4 @@ export {
108
108
  calculatePortFromIdentifier,
109
109
  getWorkspacePort
110
110
  };
111
- //# sourceMappingURL=chunk-USSL2X4A.js.map
111
+ //# sourceMappingURL=chunk-NUACL52E.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  GitWorktreeManager
4
- } from "./chunk-4GAJJUYS.js";
4
+ } from "./chunk-TC7APDKU.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-VT4PDUYT.js";
@@ -299,4 +299,4 @@ Please consult your shell's documentation for setting up custom completions.
299
299
  export {
300
300
  ShellCompletion
301
301
  };
302
- //# sourceMappingURL=chunk-JT5LZRMI.js.map
302
+ //# sourceMappingURL=chunk-NWMORW3U.js.map
@@ -9,7 +9,7 @@ import {
9
9
  fetchProjectList,
10
10
  hasProjectScope,
11
11
  updateProjectItemField
12
- } from "./chunk-TRUMP4DA.js";
12
+ } from "./chunk-4CO6KG5S.js";
13
13
  import {
14
14
  promptConfirmation
15
15
  } from "./chunk-7JDMYTFZ.js";
@@ -289,7 +289,8 @@ State: ${entity.state}`;
289
289
  branch: ghPR.headRefName,
290
290
  baseBranch: ghPR.baseRefName,
291
291
  url: ghPR.url,
292
- isDraft: ghPR.isDraft
292
+ isDraft: ghPR.isDraft,
293
+ isFork: ghPR.isCrossRepository
293
294
  };
294
295
  }
295
296
  async promptUserConfirmation(message) {
@@ -300,4 +301,4 @@ State: ${entity.state}`;
300
301
  export {
301
302
  GitHubService
302
303
  };
303
- //# sourceMappingURL=chunk-Y5HSSIK2.js.map
304
+ //# sourceMappingURL=chunk-OFDN5NKS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/GitHubService.ts","../src/types/github.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport type { Issue, PullRequest, IssueTrackerInputDetection } from '../types/index.js'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { GitHubError, GitHubErrorCode } from '../types/github.js'\nimport {\n\texecuteGhCommand,\n\thasProjectScope,\n\tfetchGhIssue,\n\tfetchGhPR,\n\tfetchProjectList,\n\tfetchProjectItems,\n\tfetchProjectFields,\n\tupdateProjectItemField,\n\tcreateIssue,\n} from '../utils/github.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { promptConfirmation } from '../utils/prompt.js'\nimport type { IssueTracker } from './IssueTracker.js'\n\nexport class GitHubService implements IssueTracker {\n\t// IssueTracker interface implementation\n\treadonly providerName = 'github'\n\treadonly supportsPullRequests = true\n\tprivate prompter: (message: string) => Promise<boolean>\n\n\tconstructor(options?: {\n\t\tprompter?: (message: string) => Promise<boolean>\n\t}) {\n\t\t// Set up prompter (use provided or default to promptConfirmation)\n\t\tthis.prompter = options?.prompter ?? promptConfirmation\n\t}\n\n\t/**\n\t * Check if GitHub CLI (gh) is available on the system\n\t * @returns true if gh CLI is installed and accessible, false otherwise\n\t */\n\tpublic static isCliAvailable(): boolean {\n\t\ttry {\n\t\t\texecSync('gh --version', { stdio: 'ignore' })\n\t\t\treturn true\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// Input detection - IssueTracker interface implementation\n\tpublic async detectInputType(input: string, repo?: string): Promise<IssueTrackerInputDetection> {\n\t\t// Pattern: #123 or just 123\n\t\tconst numberMatch = input.match(/^#?(\\d+)$/)\n\n\t\tif (!numberMatch?.[1]) {\n\t\t\treturn { type: 'unknown', identifier: null, rawInput: input }\n\t\t}\n\n\t\tconst number = parseInt(numberMatch[1], 10)\n\n\t\t// Try PR first (based on bash script logic at lines 500-533)\n\t\tgetLogger().debug('Checking if input is a PR', { number })\n\t\tconst pr = await this.isValidPR(number, repo)\n\t\tif (pr) {\n\t\t\treturn { type: 'pr', identifier: number.toString(), rawInput: input }\n\t\t}\n\n\t\t// Try issue next (lines 536-575 in bash)\n\t\tgetLogger().debug('Checking if input is an issue', { number })\n\t\tconst issue = await this.isValidIssue(number, repo)\n\t\tif (issue) {\n\t\t\treturn { type: 'issue', identifier: number.toString(), rawInput: input }\n\t\t}\n\n\t\t// Neither PR nor issue found\n\t\treturn { type: 'unknown', identifier: null, rawInput: input }\n\t}\n\n\t// Issue fetching with validation\n\tpublic async fetchIssue(issueNumber: number, repo?: string): Promise<Issue> {\n\t\ttry {\n\t\t\treturn await this.fetchIssueInternal(issueNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Only throw NOT_FOUND for actual \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.NOT_FOUND,\n\t\t\t\t\t`Issue #${issueNumber} not found`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t\t// Re-throw all other errors unchanged\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Silent issue validation (for detection phase)\n\tpublic async isValidIssue(issueNumber: number, repo?: string): Promise<Issue | false> {\n\t\ttry {\n\t\t\treturn await this.fetchIssueInternal(issueNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Silently return false for \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Internal issue fetching logic (shared by fetchIssue and isValidIssue)\n\tprivate async fetchIssueInternal(issueNumber: number, repo?: string): Promise<Issue> {\n\t\tconst ghIssue = await fetchGhIssue(issueNumber, repo)\n\t\treturn this.mapGitHubIssueToIssue(ghIssue)\n\t}\n\n\tpublic async validateIssueState(issue: Issue): Promise<void> {\n\t\tif (issue.state === 'closed') {\n\t\t\tconst response = await this.promptUserConfirmation(\n\t\t\t\t`Issue #${issue.number} is closed. Continue anyway?`\n\t\t\t)\n\t\t\tif (!response) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.INVALID_STATE,\n\t\t\t\t\t'User cancelled due to closed issue'\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// PR fetching with validation\n\tpublic async fetchPR(prNumber: number, repo?: string): Promise<PullRequest> {\n\t\ttry {\n\t\t\treturn await this.fetchPRInternal(prNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Only throw NOT_FOUND for actual \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.NOT_FOUND,\n\t\t\t\t\t`PR #${prNumber} not found`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t\t// Re-throw all other errors unchanged\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Silent PR validation (for detection phase)\n\tpublic async isValidPR(prNumber: number, repo?: string): Promise<PullRequest | false> {\n\t\ttry {\n\t\t\treturn await this.fetchPRInternal(prNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Silently return false for \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Internal PR fetching logic (shared by fetchPR and isValidPR)\n\tprivate async fetchPRInternal(prNumber: number, repo?: string): Promise<PullRequest> {\n\t\tconst ghPR = await fetchGhPR(prNumber, repo)\n\t\treturn this.mapGitHubPRToPullRequest(ghPR)\n\t}\n\n\tpublic async validatePRState(pr: PullRequest): Promise<void> {\n\t\tif (pr.state === 'closed' || pr.state === 'merged') {\n\t\t\tconst response = await this.promptUserConfirmation(\n\t\t\t\t`PR #${pr.number} is ${pr.state}. Continue anyway?`\n\t\t\t)\n\t\t\tif (!response) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.INVALID_STATE,\n\t\t\t\t\t`User cancelled due to ${pr.state} PR`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Issue creation\n\tpublic async createIssue(\n\t\ttitle: string,\n\t\tbody: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\t// getLogger().info('Creating GitHub issue', { title })\n\t\treturn createIssue(title, body, { repo: repository, labels })\n\t}\n\n\tpublic async getIssueUrl(issueNumber: number, repo?: string): Promise<string> {\n\t\tgetLogger().debug('Fetching issue URL', { issueNumber, repo })\n\t\tconst issue = await fetchGhIssue(issueNumber, repo)\n\t\treturn issue.url\n\t}\n\n\t// GitHub Projects integration\n\tpublic async moveIssueToInProgress(issueNumber: number): Promise<void> {\n\t\t// Based on bash script lines 374-463\n\t\tgetLogger().info('Moving issue to In Progress in GitHub Projects', {\n\t\t\tissueNumber,\n\t\t})\n\n\t\t// Check for project scope\n\t\tif (!(await hasProjectScope())) {\n\t\t\tgetLogger().warn('Missing project scope in GitHub CLI auth')\n\t\t\tthrow new GitHubError(\n\t\t\t\tGitHubErrorCode.MISSING_SCOPE,\n\t\t\t\t'GitHub CLI lacks project scope. Run: gh auth refresh -s project'\n\t\t\t)\n\t\t}\n\n\t\t// Get repository info\n\t\tlet owner: string\n\t\ttry {\n\t\t\tconst repoInfo = await executeGhCommand<{\n\t\t\t\towner: { login: string }\n\t\t\t\tname: string\n\t\t\t}>(['repo', 'view', '--json', 'owner,name'])\n\t\t\towner = repoInfo.owner.login\n\t\t} catch (error) {\n\t\t\tgetLogger().warn('Could not determine repository info', { error })\n\t\t\treturn\n\t\t}\n\n\t\t// List all projects\n\t\tlet projects: GitHubProject[]\n\t\ttry {\n\t\t\tprojects = await fetchProjectList(owner)\n\t\t} catch (error) {\n\t\t\tgetLogger().warn('Could not fetch projects', { owner, error })\n\t\t\treturn\n\t\t}\n\n\t\tif (!projects.length) {\n\t\t\tgetLogger().warn('No projects found', { owner })\n\t\t\treturn\n\t\t}\n\n\t\t// Process each project (lines 404-460 in bash)\n\t\tfor (const project of projects) {\n\t\t\tawait this.updateIssueStatusInProject(project, issueNumber, owner)\n\t\t}\n\t}\n\n\tprivate async updateIssueStatusInProject(\n\t\tproject: GitHubProject,\n\t\tissueNumber: number,\n\t\towner: string\n\t): Promise<void> {\n\t\t// Check if issue is in project\n\t\tlet items: ProjectItem[]\n\t\ttry {\n\t\t\titems = await fetchProjectItems(project.number, owner)\n\t\t} catch (error) {\n\t\t\tgetLogger().debug('Could not fetch project items', { project: project.number, error })\n\t\t\treturn\n\t\t}\n\n\t\t// Find issue item\n\t\tconst item = items.find(\n\t\t\t(i: ProjectItem) =>\n\t\t\t\ti.content.type === 'Issue' && i.content.number === issueNumber\n\t\t)\n\n\t\tif (!item) {\n\t\t\tgetLogger().debug('Issue not found in project', {\n\t\t\t\tissueNumber,\n\t\t\t\tprojectNumber: project.number,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Fetch project fields separately (like bash script does)\n\t\tlet fieldsData: { fields: ProjectField[] }\n\t\ttry {\n\t\t\tfieldsData = await fetchProjectFields(project.number, owner)\n\t\t} catch (error) {\n\t\t\tgetLogger().debug('Could not fetch project fields', { project: project.number, error })\n\t\t\treturn\n\t\t}\n\n\t\t// Find Status field and In Progress option\n\t\tconst statusField = fieldsData.fields.find((f) => f.name === 'Status')\n\t\tif (!statusField) {\n\t\t\tgetLogger().debug('No Status field found in project', { projectNumber: project.number })\n\t\t\treturn\n\t\t}\n\n\t\tconst inProgressOption = statusField.options?.find(\n\t\t\t(o: { id: string; name: string }) => o.name === 'In Progress' || o.name === 'In progress'\n\t\t)\n\n\t\tif (!inProgressOption) {\n\t\t\tgetLogger().debug('No In Progress option found in Status field', { projectNumber: project.number })\n\t\t\treturn\n\t\t}\n\n\t\t// Update status\n\t\ttry {\n\t\t\tawait updateProjectItemField(\n\t\t\t\titem.id,\n\t\t\t\tproject.id,\n\t\t\t\tstatusField.id,\n\t\t\t\tinProgressOption.id\n\t\t\t)\n\n\t\t\tgetLogger().info('Updated issue status in project', {\n\t\t\t\tissueNumber,\n\t\t\t\tprojectNumber: project.number,\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tgetLogger().debug('Could not update project item', { item: item.id, error })\n\t\t}\n\t}\n\n\t// Utility methods\n\tpublic extractContext(entity: Issue | PullRequest): string {\n\t\tif ('branch' in entity) {\n\t\t\t// It's a PullRequest\n\t\t\treturn `Pull Request #${entity.number}: ${entity.title}\\nBranch: ${entity.branch}\\nState: ${entity.state}`\n\t\t} else {\n\t\t\t// It's an Issue\n\t\t\treturn `GitHub Issue #${entity.number}: ${entity.title}\\nState: ${entity.state}`\n\t\t}\n\t}\n\n\tprivate mapGitHubIssueToIssue(ghIssue: GitHubIssue): Issue {\n\t\treturn {\n\t\t\tnumber: ghIssue.number,\n\t\t\ttitle: ghIssue.title,\n\t\t\tbody: ghIssue.body,\n\t\t\tstate: ghIssue.state.toLowerCase() as 'open' | 'closed',\n\t\t\tlabels: ghIssue.labels.map((l) => l.name),\n\t\t\tassignees: ghIssue.assignees.map((a) => a.login),\n\t\t\turl: ghIssue.url,\n\t\t}\n\t}\n\n\tprivate mapGitHubPRToPullRequest(ghPR: GitHubPullRequest): PullRequest {\n\t\treturn {\n\t\t\tnumber: ghPR.number,\n\t\t\ttitle: ghPR.title,\n\t\t\tbody: ghPR.body,\n\t\t\tstate: ghPR.state.toLowerCase() as 'open' | 'closed' | 'merged',\n\t\t\tbranch: ghPR.headRefName,\n\t\t\tbaseBranch: ghPR.baseRefName,\n\t\t\turl: ghPR.url,\n\t\t\tisDraft: ghPR.isDraft,\n\t\t}\n\t}\n\n\tprivate async promptUserConfirmation(message: string): Promise<boolean> {\n\t\treturn this.prompter(message)\n\t}\n}\n","// Core GitHub response types\nexport interface GitHubIssue {\n\tnumber: number\n\ttitle: string\n\tbody: string\n\tstate: 'OPEN' | 'CLOSED' // GitHub GraphQL format\n\tlabels: { name: string }[]\n\tassignees: { login: string }[]\n\turl: string\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\n// Pull Request types\nexport interface GitHubPullRequest {\n\tnumber: number\n\ttitle: string\n\tbody: string\n\tstate: 'OPEN' | 'CLOSED' | 'MERGED'\n\theadRefName: string // source branch\n\tbaseRefName: string // target branch\n\turl: string\n\tisDraft: boolean\n\tmergeable: 'CONFLICTING' | 'MERGEABLE' | 'UNKNOWN'\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\n// GitHub Projects types\nexport interface GitHubProject {\n\tnumber: number\n\tid: string\n\tname: string\n\tfields: ProjectField[]\n}\n\nexport interface ProjectField {\n\tid: string\n\tname: string\n\tdataType: 'SINGLE_SELECT' | 'TEXT' | 'NUMBER' | 'DATE'\n\toptions?: ProjectFieldOption[]\n}\n\nexport interface ProjectFieldOption {\n\tid: string\n\tname: string\n}\n\nexport interface ProjectItem {\n\tid: string\n\tcontent: {\n\t\ttype: 'Issue' | 'PullRequest' | 'DraftIssue'\n\t\tnumber: number\n\t}\n\tfieldValues: Record<string, unknown>\n}\n\n// Command result types\nexport interface GitHubCommandResult<T = unknown> {\n\tsuccess: boolean\n\tdata?: T\n\terror?: string\n\trateLimitRemaining?: number\n\trateLimitReset?: Date\n}\n\nexport interface GitHubAuthStatus {\n\thasAuth: boolean\n\tscopes: string[]\n\tusername?: string\n}\n\n// Input detection types\nexport interface GitHubInputDetection {\n\ttype: 'issue' | 'pr' | 'unknown'\n\tnumber: number | null\n\trawInput: string\n}\n\n// Context and error types\nexport interface GitHubContext {\n\tissue?: GitHubIssue\n\tpullRequest?: GitHubPullRequest\n\tformattedContext: string\n}\n\nexport enum GitHubErrorCode {\n\tNOT_FOUND = 'NOT_FOUND',\n\tUNAUTHORIZED = 'UNAUTHORIZED',\n\tRATE_LIMITED = 'RATE_LIMITED',\n\tNETWORK_ERROR = 'NETWORK_ERROR',\n\tINVALID_STATE = 'INVALID_STATE',\n\tMISSING_SCOPE = 'MISSING_SCOPE',\n}\n\nexport class GitHubError extends Error {\n\tconstructor(\n\t\tpublic code: GitHubErrorCode,\n\t\tmessage: string,\n\t\tpublic details?: unknown\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'GitHubError'\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/lib/GitHubService.ts","../src/types/github.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport type { Issue, PullRequest, IssueTrackerInputDetection } from '../types/index.js'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { GitHubError, GitHubErrorCode } from '../types/github.js'\nimport {\n\texecuteGhCommand,\n\thasProjectScope,\n\tfetchGhIssue,\n\tfetchGhPR,\n\tfetchProjectList,\n\tfetchProjectItems,\n\tfetchProjectFields,\n\tupdateProjectItemField,\n\tcreateIssue,\n} from '../utils/github.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { promptConfirmation } from '../utils/prompt.js'\nimport type { IssueTracker } from './IssueTracker.js'\n\nexport class GitHubService implements IssueTracker {\n\t// IssueTracker interface implementation\n\treadonly providerName = 'github'\n\treadonly supportsPullRequests = true\n\tprivate prompter: (message: string) => Promise<boolean>\n\n\tconstructor(options?: {\n\t\tprompter?: (message: string) => Promise<boolean>\n\t}) {\n\t\t// Set up prompter (use provided or default to promptConfirmation)\n\t\tthis.prompter = options?.prompter ?? promptConfirmation\n\t}\n\n\t/**\n\t * Check if GitHub CLI (gh) is available on the system\n\t * @returns true if gh CLI is installed and accessible, false otherwise\n\t */\n\tpublic static isCliAvailable(): boolean {\n\t\ttry {\n\t\t\texecSync('gh --version', { stdio: 'ignore' })\n\t\t\treturn true\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// Input detection - IssueTracker interface implementation\n\tpublic async detectInputType(input: string, repo?: string): Promise<IssueTrackerInputDetection> {\n\t\t// Pattern: #123 or just 123\n\t\tconst numberMatch = input.match(/^#?(\\d+)$/)\n\n\t\tif (!numberMatch?.[1]) {\n\t\t\treturn { type: 'unknown', identifier: null, rawInput: input }\n\t\t}\n\n\t\tconst number = parseInt(numberMatch[1], 10)\n\n\t\t// Try PR first (based on bash script logic at lines 500-533)\n\t\tgetLogger().debug('Checking if input is a PR', { number })\n\t\tconst pr = await this.isValidPR(number, repo)\n\t\tif (pr) {\n\t\t\treturn { type: 'pr', identifier: number.toString(), rawInput: input }\n\t\t}\n\n\t\t// Try issue next (lines 536-575 in bash)\n\t\tgetLogger().debug('Checking if input is an issue', { number })\n\t\tconst issue = await this.isValidIssue(number, repo)\n\t\tif (issue) {\n\t\t\treturn { type: 'issue', identifier: number.toString(), rawInput: input }\n\t\t}\n\n\t\t// Neither PR nor issue found\n\t\treturn { type: 'unknown', identifier: null, rawInput: input }\n\t}\n\n\t// Issue fetching with validation\n\tpublic async fetchIssue(issueNumber: number, repo?: string): Promise<Issue> {\n\t\ttry {\n\t\t\treturn await this.fetchIssueInternal(issueNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Only throw NOT_FOUND for actual \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.NOT_FOUND,\n\t\t\t\t\t`Issue #${issueNumber} not found`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t\t// Re-throw all other errors unchanged\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Silent issue validation (for detection phase)\n\tpublic async isValidIssue(issueNumber: number, repo?: string): Promise<Issue | false> {\n\t\ttry {\n\t\t\treturn await this.fetchIssueInternal(issueNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Silently return false for \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Internal issue fetching logic (shared by fetchIssue and isValidIssue)\n\tprivate async fetchIssueInternal(issueNumber: number, repo?: string): Promise<Issue> {\n\t\tconst ghIssue = await fetchGhIssue(issueNumber, repo)\n\t\treturn this.mapGitHubIssueToIssue(ghIssue)\n\t}\n\n\tpublic async validateIssueState(issue: Issue): Promise<void> {\n\t\tif (issue.state === 'closed') {\n\t\t\tconst response = await this.promptUserConfirmation(\n\t\t\t\t`Issue #${issue.number} is closed. Continue anyway?`\n\t\t\t)\n\t\t\tif (!response) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.INVALID_STATE,\n\t\t\t\t\t'User cancelled due to closed issue'\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// PR fetching with validation\n\tpublic async fetchPR(prNumber: number, repo?: string): Promise<PullRequest> {\n\t\ttry {\n\t\t\treturn await this.fetchPRInternal(prNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Only throw NOT_FOUND for actual \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.NOT_FOUND,\n\t\t\t\t\t`PR #${prNumber} not found`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t\t// Re-throw all other errors unchanged\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Silent PR validation (for detection phase)\n\tpublic async isValidPR(prNumber: number, repo?: string): Promise<PullRequest | false> {\n\t\ttry {\n\t\t\treturn await this.fetchPRInternal(prNumber, repo)\n\t\t} catch (error) {\n\t\t\t// Silently return false for \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Internal PR fetching logic (shared by fetchPR and isValidPR)\n\tprivate async fetchPRInternal(prNumber: number, repo?: string): Promise<PullRequest> {\n\t\tconst ghPR = await fetchGhPR(prNumber, repo)\n\t\treturn this.mapGitHubPRToPullRequest(ghPR)\n\t}\n\n\tpublic async validatePRState(pr: PullRequest): Promise<void> {\n\t\tif (pr.state === 'closed' || pr.state === 'merged') {\n\t\t\tconst response = await this.promptUserConfirmation(\n\t\t\t\t`PR #${pr.number} is ${pr.state}. Continue anyway?`\n\t\t\t)\n\t\t\tif (!response) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.INVALID_STATE,\n\t\t\t\t\t`User cancelled due to ${pr.state} PR`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Issue creation\n\tpublic async createIssue(\n\t\ttitle: string,\n\t\tbody: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\t// getLogger().info('Creating GitHub issue', { title })\n\t\treturn createIssue(title, body, { repo: repository, labels })\n\t}\n\n\tpublic async getIssueUrl(issueNumber: number, repo?: string): Promise<string> {\n\t\tgetLogger().debug('Fetching issue URL', { issueNumber, repo })\n\t\tconst issue = await fetchGhIssue(issueNumber, repo)\n\t\treturn issue.url\n\t}\n\n\t// GitHub Projects integration\n\tpublic async moveIssueToInProgress(issueNumber: number): Promise<void> {\n\t\t// Based on bash script lines 374-463\n\t\tgetLogger().info('Moving issue to In Progress in GitHub Projects', {\n\t\t\tissueNumber,\n\t\t})\n\n\t\t// Check for project scope\n\t\tif (!(await hasProjectScope())) {\n\t\t\tgetLogger().warn('Missing project scope in GitHub CLI auth')\n\t\t\tthrow new GitHubError(\n\t\t\t\tGitHubErrorCode.MISSING_SCOPE,\n\t\t\t\t'GitHub CLI lacks project scope. Run: gh auth refresh -s project'\n\t\t\t)\n\t\t}\n\n\t\t// Get repository info\n\t\tlet owner: string\n\t\ttry {\n\t\t\tconst repoInfo = await executeGhCommand<{\n\t\t\t\towner: { login: string }\n\t\t\t\tname: string\n\t\t\t}>(['repo', 'view', '--json', 'owner,name'])\n\t\t\towner = repoInfo.owner.login\n\t\t} catch (error) {\n\t\t\tgetLogger().warn('Could not determine repository info', { error })\n\t\t\treturn\n\t\t}\n\n\t\t// List all projects\n\t\tlet projects: GitHubProject[]\n\t\ttry {\n\t\t\tprojects = await fetchProjectList(owner)\n\t\t} catch (error) {\n\t\t\tgetLogger().warn('Could not fetch projects', { owner, error })\n\t\t\treturn\n\t\t}\n\n\t\tif (!projects.length) {\n\t\t\tgetLogger().warn('No projects found', { owner })\n\t\t\treturn\n\t\t}\n\n\t\t// Process each project (lines 404-460 in bash)\n\t\tfor (const project of projects) {\n\t\t\tawait this.updateIssueStatusInProject(project, issueNumber, owner)\n\t\t}\n\t}\n\n\tprivate async updateIssueStatusInProject(\n\t\tproject: GitHubProject,\n\t\tissueNumber: number,\n\t\towner: string\n\t): Promise<void> {\n\t\t// Check if issue is in project\n\t\tlet items: ProjectItem[]\n\t\ttry {\n\t\t\titems = await fetchProjectItems(project.number, owner)\n\t\t} catch (error) {\n\t\t\tgetLogger().debug('Could not fetch project items', { project: project.number, error })\n\t\t\treturn\n\t\t}\n\n\t\t// Find issue item\n\t\tconst item = items.find(\n\t\t\t(i: ProjectItem) =>\n\t\t\t\ti.content.type === 'Issue' && i.content.number === issueNumber\n\t\t)\n\n\t\tif (!item) {\n\t\t\tgetLogger().debug('Issue not found in project', {\n\t\t\t\tissueNumber,\n\t\t\t\tprojectNumber: project.number,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Fetch project fields separately (like bash script does)\n\t\tlet fieldsData: { fields: ProjectField[] }\n\t\ttry {\n\t\t\tfieldsData = await fetchProjectFields(project.number, owner)\n\t\t} catch (error) {\n\t\t\tgetLogger().debug('Could not fetch project fields', { project: project.number, error })\n\t\t\treturn\n\t\t}\n\n\t\t// Find Status field and In Progress option\n\t\tconst statusField = fieldsData.fields.find((f) => f.name === 'Status')\n\t\tif (!statusField) {\n\t\t\tgetLogger().debug('No Status field found in project', { projectNumber: project.number })\n\t\t\treturn\n\t\t}\n\n\t\tconst inProgressOption = statusField.options?.find(\n\t\t\t(o: { id: string; name: string }) => o.name === 'In Progress' || o.name === 'In progress'\n\t\t)\n\n\t\tif (!inProgressOption) {\n\t\t\tgetLogger().debug('No In Progress option found in Status field', { projectNumber: project.number })\n\t\t\treturn\n\t\t}\n\n\t\t// Update status\n\t\ttry {\n\t\t\tawait updateProjectItemField(\n\t\t\t\titem.id,\n\t\t\t\tproject.id,\n\t\t\t\tstatusField.id,\n\t\t\t\tinProgressOption.id\n\t\t\t)\n\n\t\t\tgetLogger().info('Updated issue status in project', {\n\t\t\t\tissueNumber,\n\t\t\t\tprojectNumber: project.number,\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tgetLogger().debug('Could not update project item', { item: item.id, error })\n\t\t}\n\t}\n\n\t// Utility methods\n\tpublic extractContext(entity: Issue | PullRequest): string {\n\t\tif ('branch' in entity) {\n\t\t\t// It's a PullRequest\n\t\t\treturn `Pull Request #${entity.number}: ${entity.title}\\nBranch: ${entity.branch}\\nState: ${entity.state}`\n\t\t} else {\n\t\t\t// It's an Issue\n\t\t\treturn `GitHub Issue #${entity.number}: ${entity.title}\\nState: ${entity.state}`\n\t\t}\n\t}\n\n\tprivate mapGitHubIssueToIssue(ghIssue: GitHubIssue): Issue {\n\t\treturn {\n\t\t\tnumber: ghIssue.number,\n\t\t\ttitle: ghIssue.title,\n\t\t\tbody: ghIssue.body,\n\t\t\tstate: ghIssue.state.toLowerCase() as 'open' | 'closed',\n\t\t\tlabels: ghIssue.labels.map((l) => l.name),\n\t\t\tassignees: ghIssue.assignees.map((a) => a.login),\n\t\t\turl: ghIssue.url,\n\t\t}\n\t}\n\n\tprivate mapGitHubPRToPullRequest(ghPR: GitHubPullRequest): PullRequest {\n\t\treturn {\n\t\t\tnumber: ghPR.number,\n\t\t\ttitle: ghPR.title,\n\t\t\tbody: ghPR.body,\n\t\t\tstate: ghPR.state.toLowerCase() as 'open' | 'closed' | 'merged',\n\t\t\tbranch: ghPR.headRefName,\n\t\t\tbaseBranch: ghPR.baseRefName,\n\t\t\turl: ghPR.url,\n\t\t\tisDraft: ghPR.isDraft,\n\t\t\tisFork: ghPR.isCrossRepository,\n\t\t}\n\t}\n\n\tprivate async promptUserConfirmation(message: string): Promise<boolean> {\n\t\treturn this.prompter(message)\n\t}\n}\n","// Core GitHub response types\nexport interface GitHubIssue {\n\tnumber: number\n\ttitle: string\n\tbody: string\n\tstate: 'OPEN' | 'CLOSED' // GitHub GraphQL format\n\tlabels: { name: string }[]\n\tassignees: { login: string }[]\n\turl: string\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\n// Pull Request types\nexport interface GitHubPullRequest {\n\tnumber: number\n\ttitle: string\n\tbody: string\n\tstate: 'OPEN' | 'CLOSED' | 'MERGED'\n\theadRefName: string // source branch\n\tbaseRefName: string // target branch\n\turl: string\n\tisDraft: boolean\n\tisCrossRepository: boolean\n\tmergeable: 'CONFLICTING' | 'MERGEABLE' | 'UNKNOWN'\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\n// GitHub Projects types\nexport interface GitHubProject {\n\tnumber: number\n\tid: string\n\tname: string\n\tfields: ProjectField[]\n}\n\nexport interface ProjectField {\n\tid: string\n\tname: string\n\tdataType: 'SINGLE_SELECT' | 'TEXT' | 'NUMBER' | 'DATE'\n\toptions?: ProjectFieldOption[]\n}\n\nexport interface ProjectFieldOption {\n\tid: string\n\tname: string\n}\n\nexport interface ProjectItem {\n\tid: string\n\tcontent: {\n\t\ttype: 'Issue' | 'PullRequest' | 'DraftIssue'\n\t\tnumber: number\n\t}\n\tfieldValues: Record<string, unknown>\n}\n\n// Command result types\nexport interface GitHubCommandResult<T = unknown> {\n\tsuccess: boolean\n\tdata?: T\n\terror?: string\n\trateLimitRemaining?: number\n\trateLimitReset?: Date\n}\n\nexport interface GitHubAuthStatus {\n\thasAuth: boolean\n\tscopes: string[]\n\tusername?: string\n}\n\n// Input detection types\nexport interface GitHubInputDetection {\n\ttype: 'issue' | 'pr' | 'unknown'\n\tnumber: number | null\n\trawInput: string\n}\n\n// Context and error types\nexport interface GitHubContext {\n\tissue?: GitHubIssue\n\tpullRequest?: GitHubPullRequest\n\tformattedContext: string\n}\n\nexport enum GitHubErrorCode {\n\tNOT_FOUND = 'NOT_FOUND',\n\tUNAUTHORIZED = 'UNAUTHORIZED',\n\tRATE_LIMITED = 'RATE_LIMITED',\n\tNETWORK_ERROR = 'NETWORK_ERROR',\n\tINVALID_STATE = 'INVALID_STATE',\n\tMISSING_SCOPE = 'MISSING_SCOPE',\n}\n\nexport class GitHubError extends Error {\n\tconstructor(\n\t\tpublic code: GitHubErrorCode,\n\t\tmessage: string,\n\t\tpublic details?: unknown\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'GitHubError'\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -16,10 +16,10 @@ import {
16
16
  findMainWorktreePathWithSettings,
17
17
  findWorktreeForBranch,
18
18
  getMergeTargetBranch
19
- } from "./chunk-H6ST2TGP.js";
19
+ } from "./chunk-AR5QKYNE.js";
20
20
  import {
21
21
  SettingsManager
22
- } from "./chunk-XFEK2X2D.js";
22
+ } from "./chunk-RI2YL6TK.js";
23
23
  import {
24
24
  MetadataManager
25
25
  } from "./chunk-KBEIQP4G.js";
@@ -494,4 +494,4 @@ export {
494
494
  MergeManager,
495
495
  BuildRunner
496
496
  };
497
- //# sourceMappingURL=chunk-HZXBHMVM.js.map
497
+ //# sourceMappingURL=chunk-R4YWBGY6.js.map
@@ -164,7 +164,10 @@ var IloomSettingsSchema = z.object({
164
164
  }).optional().describe("Color synchronization settings for workspace identification"),
165
165
  attribution: z.enum(["off", "upstreamOnly", "on"]).default("upstreamOnly").describe(
166
166
  'Controls when iloom attribution appears in session summaries. "off" - never show attribution. "upstreamOnly" - only show for contributions to external repositories (e.g., open source). "on" - always show attribution.'
167
- )
167
+ ),
168
+ git: z.object({
169
+ commitTimeout: z.number().min(1e3, "Commit timeout must be at least 1000ms").max(6e5, "Commit timeout cannot exceed 600000ms (10 minutes)").default(6e4).describe("Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.")
170
+ }).default({}).describe("Git operation settings")
168
171
  });
169
172
  var IloomSettingsSchemaNoDefaults = z.object({
170
173
  mainBranch: z.string().min(1, "Settings 'mainBranch' cannot be empty").optional().describe("Name of the main/primary branch for the repository"),
@@ -244,7 +247,10 @@ var IloomSettingsSchemaNoDefaults = z.object({
244
247
  }).optional().describe("Color synchronization settings for workspace identification"),
245
248
  attribution: z.enum(["off", "upstreamOnly", "on"]).optional().describe(
246
249
  'Controls when iloom attribution appears in session summaries. "off" - never show attribution. "upstreamOnly" - only show for contributions to external repositories (e.g., open source). "on" - always show attribution.'
247
- )
250
+ ),
251
+ git: z.object({
252
+ commitTimeout: z.number().min(1e3, "Commit timeout must be at least 1000ms").max(6e5, "Commit timeout cannot exceed 600000ms (10 minutes)").optional().describe("Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.")
253
+ }).optional().describe("Git operation settings")
248
254
  });
249
255
  var SettingsManager = class {
250
256
  /**
@@ -521,4 +527,4 @@ export {
521
527
  IloomSettingsSchemaNoDefaults,
522
528
  SettingsManager
523
529
  };
524
- //# sourceMappingURL=chunk-XFEK2X2D.js.map
530
+ //# sourceMappingURL=chunk-RI2YL6TK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/SettingsManager.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport path from 'path'\nimport os from 'os'\nimport { z } from 'zod'\nimport deepmerge from 'deepmerge'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Valid project capability values for Zod enum validation.\n * When updating this constant, also update ProjectCapability type in src/types/loom.ts\n */\nconst PROJECT_CAPABILITIES = ['cli', 'web'] as const\n\n/**\n * Zod schema for agent settings\n */\nexport const AgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.optional()\n\t\t.describe('Claude model shorthand: sonnet, opus, or haiku'),\n\tenabled: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether this agent is enabled. Defaults to true.'),\n\tproviders: z\n\t\t.record(\n\t\t\tz.enum(['claude', 'gemini', 'codex']),\n\t\t\tz.string()\n\t\t)\n\t\t.optional()\n\t\t.describe('Map of review providers to model names. Keys: claude, gemini, codex. Values: model name strings (e.g., \"sonnet\", \"gemini-3-pro-preview\", \"gpt-5.2-codex\")'),\n\treview: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Whether artifacts from this agent should be reviewed before posting (defaults to false)'),\n})\n\n/**\n * Zod schema for spin agent settings with default model\n * Used for the spin orchestrator configuration\n */\nexport const SpinAgentSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('opus')\n\t\t.describe('Claude model shorthand for spin orchestrator'),\n})\n\n/**\n * Zod schema for plan command settings with default model\n * Used for the plan command configuration\n */\nexport const PlanCommandSettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('opus')\n\t\t.describe('Claude model shorthand for plan command'),\n\tplanner: z\n\t\t.enum(['claude', 'gemini', 'codex'])\n\t\t.default('claude')\n\t\t.describe('AI provider for creating the plan'),\n\treviewer: z\n\t\t.enum(['claude', 'gemini', 'codex', 'none'])\n\t\t.default('none')\n\t\t.describe('AI provider for reviewing the plan (none to skip review)'),\n})\n\n/**\n * Zod schema for summary settings with default model\n * Used for session summary generation configuration\n */\nexport const SummarySettingsSchema = z.object({\n\tmodel: z\n\t\t.enum(['sonnet', 'opus', 'haiku'])\n\t\t.default('sonnet')\n\t\t.describe('Claude model shorthand for session summary generation'),\n})\n\n/**\n * Zod schema for workflow permission configuration\n */\nexport const WorkflowPermissionSchema = z.object({\n\tpermissionMode: z\n\t\t.enum(['plan', 'acceptEdits', 'bypassPermissions', 'default'])\n\t\t.optional()\n\t\t.describe('Permission mode for Claude CLI in this workflow type'),\n\tnoVerify: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows'),\n\tstartIde: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch IDE (code) when starting this workflow type'),\n\tstartDevServer: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch development server when starting this workflow type'),\n\tstartAiAgent: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Launch Claude Code agent when starting this workflow type'),\n\tstartTerminal: z\n\t\t.boolean()\n\t\t.default(false)\n\t\t.describe('Launch terminal window without dev server when starting this workflow type'),\n\tgenerateSummary: z\n\t\t.boolean()\n\t\t.default(true)\n\t\t.describe('Generate and post Claude session summary when finishing this workflow type'),\n})\n\n/**\n * Non-defaulting variant for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const WorkflowPermissionSchemaNoDefaults = z.object({\n\tpermissionMode: z\n\t\t.enum(['plan', 'acceptEdits', 'bypassPermissions', 'default'])\n\t\t.optional()\n\t\t.describe('Permission mode for Claude CLI in this workflow type'),\n\tnoVerify: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows'),\n\tstartIde: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch IDE (code) when starting this workflow type'),\n\tstartDevServer: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch development server when starting this workflow type'),\n\tstartAiAgent: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch Claude Code agent when starting this workflow type'),\n\tstartTerminal: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Launch terminal window without dev server when starting this workflow type'),\n\tgenerateSummary: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe('Generate and post Claude session summary when finishing this workflow type'),\n})\n\n/**\n * Zod schema for workflows settings\n */\nexport const WorkflowsSettingsSchema = z\n\t.object({\n\t\tissue: WorkflowPermissionSchema.optional(),\n\t\tpr: WorkflowPermissionSchema.optional(),\n\t\tregular: WorkflowPermissionSchema.optional(),\n\t})\n\t.optional()\n\n/**\n * Non-defaulting variant for pre-merge validation\n */\nexport const WorkflowsSettingsSchemaNoDefaults = z\n\t.object({\n\t\tissue: WorkflowPermissionSchemaNoDefaults.optional(),\n\t\tpr: WorkflowPermissionSchemaNoDefaults.optional(),\n\t\tregular: WorkflowPermissionSchemaNoDefaults.optional(),\n\t})\n\t.optional()\n\n/**\n * Zod schema for capabilities settings\n */\nexport const CapabilitiesSettingsSchema = z\n\t.object({\n\t\tcapabilities: z\n\t\t\t.array(z.enum(PROJECT_CAPABILITIES))\n\t\t\t.optional()\n\t\t\t.describe('Explicitly declared project capabilities (auto-detected if not specified)'),\n\t\tweb: z\n\t\t\t.object({\n\t\t\t\tbasePort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Base port must be >= 1')\n\t\t\t\t\t.max(65535, 'Base port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Base port for web workspace port calculations (default: 3000)'),\n\t\t\t})\n\t\t\t.optional(),\n\t\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\tcapabilities: z\n\t\t\t.array(z.enum(PROJECT_CAPABILITIES))\n\t\t\t.optional()\n\t\t\t.describe('Explicitly declared project capabilities (auto-detected if not specified)'),\n\t\tweb: z\n\t\t\t.object({\n\t\t\t\tbasePort: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.min(1, 'Base port must be >= 1')\n\t\t\t\t\t.max(65535, 'Base port must be <= 65535')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Base port for web workspace port calculations (default: 3000)'),\n\t\t\t})\n\t\t\t.optional(),\n\t\tdatabase: z\n\t\t\t.object({\n\t\t\t\tdatabaseUrlEnvVarName: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.min(1, 'Database URL variable name cannot be empty')\n\t\t\t\t\t.regex(/^[A-Z_][A-Z0-9_]*$/, 'Must be valid env var name (uppercase, underscores)')\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Name of environment variable for database connection URL'),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.optional()\n\n/**\n * Zod schema for Neon database provider settings\n */\nexport const NeonSettingsSchema = z.object({\n\tprojectId: z\n\t\t.string()\n\t\t.min(1)\n\t\t.regex(/^[a-zA-Z0-9-]+$/, 'Neon project ID must contain only letters, numbers, and hyphens')\n\t\t.describe('Neon project ID found in your project URL (e.g., \"fantastic-fox-3566354\")'),\n\tparentBranch: z\n\t\t.string()\n\t\t.min(1)\n\t\t.describe('Branch from which new database branches are created'),\n})\n\n/**\n * Zod schema for database provider settings\n */\nexport const DatabaseProvidersSettingsSchema = z\n\t.object({\n\t\tneon: NeonSettingsSchema.optional().describe(\n\t\t\t'Neon database configuration. Requires Neon CLI installed and authenticated for database branching.',\n\t\t),\n\t})\n\t.optional()\n\n/**\n * Zod schema for iloom settings\n */\nexport const IloomSettingsSchema = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.default(false)\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env file compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchema.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting)',\n\t\t),\n\tspin: SpinAgentSettingsSchema.optional().describe(\n\t\t'Spin orchestrator configuration. Model defaults to opus when not configured.',\n\t),\n\tplan: PlanCommandSettingsSchema.optional().describe(\n\t\t'Plan command configuration. Model defaults to opus, planner to claude, reviewer to none when not configured.',\n\t),\n\tsummary: SummarySettingsSchema.optional().describe(\n\t\t'Session summary generation configuration. Model defaults to sonnet when not configured.',\n\t),\n\tcapabilities: CapabilitiesSettingsSchema.describe('Project capability configurations'),\n\tdatabaseProviders: DatabaseProvidersSettingsSchema.describe('Database provider configurations'),\n\tissueManagement: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\tprovider: z.enum(['github', 'linear']).optional().default('github').describe('Issue tracker provider (github, linear)'),\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})\n\t\t.optional()\n\t\t.describe('Issue management configuration'),\n\tmergeBehavior: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\tmode: z.enum(['local', 'github-pr', 'github-draft-pr']).default('local'),\n\t\t\tremote: z.string().optional(),\n\t\t\tautoCommitPush: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)'),\n\tide: z\n\t\t.object({\n\t\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\t\ttype: z\n\t\t\t\t.enum(['vscode', 'cursor', 'webstorm', 'sublime', 'intellij', 'windsurf', 'antigravity'])\n\t\t\t\t.default('vscode')\n\t\t\t\t.describe(\n\t\t\t\t\t'IDE to launch when starting a loom. Options: vscode (Visual Studio Code), cursor (Cursor AI editor), ' +\n\t\t\t\t\t\t'webstorm (JetBrains WebStorm), sublime (Sublime Text), intellij (JetBrains IntelliJ IDEA), ' +\n\t\t\t\t\t\t'windsurf (Windsurf editor), antigravity (Antigravity IDE).'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe(\n\t\t\t'IDE configuration for workspace launches. Controls which editor opens when you start a loom. ' +\n\t\t\t\t'Supports VSCode, Cursor, WebStorm, Sublime Text, IntelliJ, Windsurf, and Antigravity. ' +\n\t\t\t\t'Note: Color synchronization (title bar colors) only works with VSCode-compatible editors (vscode, cursor, windsurf, antigravity).'\n\t\t),\n\tcolors: z\n\t\t.object({\n\t\t\tterminal: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(true)\n\t\t\t\t.describe('Apply terminal background colors based on branch name (macOS only)'),\n\t\t\tvscode: z\n\t\t\t\t.boolean()\n\t\t\t\t.default(false)\n\t\t\t\t.describe(\n\t\t\t\t\t'Apply VSCode/Cursor title bar colors based on branch name. ' +\n\t\t\t\t\t\t'Note: This modifies .vscode/settings.json which may be in source control. ' +\n\t\t\t\t\t\t'Default is false for safety; enable via init or explicitly if .vscode is gitignored.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Color synchronization settings for workspace identification'),\n\tattribution: z\n\t\t.enum(['off', 'upstreamOnly', 'on'])\n\t\t.default('upstreamOnly')\n\t\t.describe(\n\t\t\t'Controls when iloom attribution appears in session summaries. ' +\n\t\t\t\t'\"off\" - never show attribution. ' +\n\t\t\t\t'\"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). ' +\n\t\t\t\t'\"on\" - always show attribution.'\n\t\t),\n\tgit: z\n\t\t.object({\n\t\t\tcommitTimeout: z\n\t\t\t\t.number()\n\t\t\t\t.min(1000, 'Commit timeout must be at least 1000ms')\n\t\t\t\t.max(600000, 'Commit timeout cannot exceed 600000ms (10 minutes)')\n\t\t\t\t.default(60000)\n\t\t\t\t.describe('Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.'),\n\t\t})\n\t\t.default({ }) // ensures the object always exists and uses default for the inner properties\n\t\t.describe('Git operation settings'),\n})\n\n/**\n * Non-defaulting variant for pre-merge validation\n * This prevents Zod from polluting partial settings with default values before merge\n */\nexport const IloomSettingsSchemaNoDefaults = z.object({\n\tmainBranch: z\n\t\t.string()\n\t\t.min(1, \"Settings 'mainBranch' cannot be empty\")\n\t\t.optional()\n\t\t.describe('Name of the main/primary branch for the repository'),\n\tsourceEnvOnStart: z\n\t\t.boolean()\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Source all dotenv-flow files (.env, .env.local, .env.development, .env.development.local) when launching terminal processes (Claude, dev server, terminal). ' +\n\t\t\t\t'Files are sourced in precedence order so later files override earlier ones. ' +\n\t\t\t\t'NOTE TO AI AGENTS: Before making changes to settings files, YOU MUST OFFER to check .env compatibility. ' +\n\t\t\t\t'WARNING: Unquoted values in .env files may break sourcing in bash/zsh shells. ' +\n\t\t\t\t'Before enabling, verify ALL your .env.* files do not contain unquoted special characters ' +\n\t\t\t\t'(e.g., database URLs with ?, &, or other shell metacharacters). ' +\n\t\t\t\t'Shell compatibility issues may cause processes to fail or behave unexpectedly.',\n\t\t),\n\tworktreePrefix: z\n\t\t.string()\n\t\t.optional()\n\t\t.refine(\n\t\t\t(val) => {\n\t\t\t\tif (val === undefined) return true // undefined = use default calculation\n\t\t\t\tif (val === '') return true // empty string = no prefix mode\n\n\t\t\t\t// Allowlist: only alphanumeric, hyphens, underscores, and forward slashes\n\t\t\t\tconst allowedChars = /^[a-zA-Z0-9\\-_/]+$/\n\t\t\t\tif (!allowedChars.test(val)) return false\n\n\t\t\t\t// Reject if only special characters (no alphanumeric content)\n\t\t\t\tif (/^[-_/]+$/.test(val)) return false\n\n\t\t\t\t// Check each segment (split by /) contains at least one alphanumeric character\n\t\t\t\tconst segments = val.split('/')\n\t\t\t\tfor (const segment of segments) {\n\t\t\t\t\tif (segment && /^[-_]+$/.test(segment)) {\n\t\t\t\t\t\t// Segment exists but contains only hyphens/underscores\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true\n\t\t\t},\n\t\t\t{\n\t\t\t\tmessage:\n\t\t\t\t\t\"worktreePrefix contains invalid characters. Only alphanumeric characters, hyphens (-), underscores (_), and forward slashes (/) are allowed. Use forward slashes for nested directories.\",\n\t\t\t},\n\t\t)\n\t\t.describe(\n\t\t\t'Prefix for worktree directories. Empty string disables prefix. Defaults to <repo-name>-looms if not set.',\n\t\t),\n\tprotectedBranches: z\n\t\t.array(z.string().min(1, 'Protected branch name cannot be empty'))\n\t\t.optional()\n\t\t.describe('List of branches that cannot be deleted (defaults to [mainBranch, \"main\", \"master\", \"develop\"])'),\n\tcopyGitIgnoredPatterns: z\n\t\t.array(z.string().min(1, 'Pattern cannot be empty'))\n\t\t.optional()\n\t\t.describe('Glob patterns for gitignored files to copy to looms (e.g., [\"*.db\", \"data/*.sqlite\"]). Great for local dbs and large test data files that are too big to commit to git. Note: .env (dotenv-flow) files, iloom\\'s and claude\\'s local settings are automatically copied and do not need to be specified here.'),\n\tworkflows: WorkflowsSettingsSchemaNoDefaults.describe('Per-workflow-type permission configurations'),\n\tagents: z\n\t\t.record(z.string(), AgentSettingsSchema)\n\t\t.optional()\n\t\t.nullable()\n\t\t.describe(\n\t\t\t'Per-agent configuration overrides. Available agents: ' +\n\t\t\t\t'iloom-issue-analyzer (analyzes issues), ' +\n\t\t\t\t'iloom-issue-planner (creates implementation plans), ' +\n\t\t\t\t'iloom-issue-analyze-and-plan (combined analysis and planning), ' +\n\t\t\t\t'iloom-issue-complexity-evaluator (evaluates complexity), ' +\n\t\t\t\t'iloom-issue-enhancer (enhances issue descriptions), ' +\n\t\t\t\t'iloom-issue-implementer (implements code changes), ' +\n\t\t\t\t'iloom-code-reviewer (reviews code changes against requirements), ' +\n\t\t\t\t'iloom-artifact-reviewer (reviews artifacts before posting)',\n\t\t),\n\tspin: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Spin orchestrator configuration'),\n\tplan: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t\tplanner: z.enum(['claude', 'gemini', 'codex']).optional(),\n\t\t\treviewer: z.enum(['claude', 'gemini', 'codex', 'none']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Plan command configuration'),\n\tsummary: z\n\t\t.object({\n\t\t\tmodel: z.enum(['sonnet', 'opus', 'haiku']).optional(),\n\t\t})\n\t\t.optional()\n\t\t.describe('Session summary generation configuration'),\n\tcapabilities: CapabilitiesSettingsSchemaNoDefaults.describe('Project capability configurations'),\n\tdatabaseProviders: DatabaseProvidersSettingsSchema.describe('Database provider configurations'),\n\tissueManagement: z\n\t\t.object({\n\t\t\tprovider: z.enum(['github', 'linear']).optional().describe('Issue tracker provider (github, linear)'),\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})\n\t\t.optional()\n\t\t.describe('Issue management configuration'),\n\tmergeBehavior: z\n\t\t.object({\n\t\t\tmode: z.enum(['local', 'github-pr', 'github-draft-pr']).optional(),\n\t\t\tremote: z.string().optional(),\n\t\t\tautoCommitPush: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Auto-commit and push after code review in draft PR mode. Defaults to true when mode is github-draft-pr.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)'),\n\tide: z\n\t\t.object({\n\t\t\ttype: z\n\t\t\t\t.enum(['vscode', 'cursor', 'webstorm', 'sublime', 'intellij', 'windsurf', 'antigravity'])\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'IDE to launch when starting a loom. Options: vscode (Visual Studio Code), cursor (Cursor AI editor), ' +\n\t\t\t\t\t\t'webstorm (JetBrains WebStorm), sublime (Sublime Text), intellij (JetBrains IntelliJ IDEA), ' +\n\t\t\t\t\t\t'windsurf (Windsurf editor), antigravity (Antigravity IDE).'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe(\n\t\t\t'IDE configuration for workspace launches. Controls which editor opens when you start a loom. ' +\n\t\t\t\t'Supports VSCode, Cursor, WebStorm, Sublime Text, IntelliJ, Windsurf, and Antigravity. ' +\n\t\t\t\t'Note: Color synchronization (title bar colors) only works with VSCode-compatible editors (vscode, cursor, windsurf, antigravity).'\n\t\t),\n\tcolors: z\n\t\t.object({\n\t\t\tterminal: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe('Apply terminal background colors based on branch name (macOS only)'),\n\t\t\tvscode: z\n\t\t\t\t.boolean()\n\t\t\t\t.optional()\n\t\t\t\t.describe(\n\t\t\t\t\t'Apply VSCode/Cursor title bar colors based on branch name. ' +\n\t\t\t\t\t\t'Note: This modifies .vscode/settings.json which may be in source control.'\n\t\t\t\t),\n\t\t})\n\t\t.optional()\n\t\t.describe('Color synchronization settings for workspace identification'),\n\tattribution: z\n\t\t.enum(['off', 'upstreamOnly', 'on'])\n\t\t.optional()\n\t\t.describe(\n\t\t\t'Controls when iloom attribution appears in session summaries. ' +\n\t\t\t\t'\"off\" - never show attribution. ' +\n\t\t\t\t'\"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). ' +\n\t\t\t\t'\"on\" - always show attribution.'\n\t\t),\n\tgit: z\n\t\t.object({\n\t\t\tcommitTimeout: z\n\t\t\t\t.number()\n\t\t\t\t.min(1000, 'Commit timeout must be at least 1000ms')\n\t\t\t\t.max(600000, 'Commit timeout cannot exceed 600000ms (10 minutes)')\n\t\t\t\t.optional()\n\t\t\t\t.describe('Timeout in milliseconds for git commit operations. Increase for long-running pre-commit hooks.'),\n\t\t})\n\t\t.optional()\n\t\t.describe('Git operation settings'),\n})\n\n/**\n * TypeScript type for Neon settings derived from Zod schema\n */\nexport type NeonSettings = z.infer<typeof NeonSettingsSchema>\n\n/**\n * TypeScript type for database providers settings derived from Zod schema\n */\nexport type DatabaseProvidersSettings = z.infer<typeof DatabaseProvidersSettingsSchema>\n\n/**\n * TypeScript type for agent settings derived from Zod schema\n */\nexport type AgentSettings = z.infer<typeof AgentSettingsSchema>\n\n/**\n * TypeScript type for spin agent settings derived from Zod schema\n */\nexport type SpinAgentSettings = z.infer<typeof SpinAgentSettingsSchema>\n\n/**\n * TypeScript type for plan command settings derived from Zod schema\n */\nexport type PlanCommandSettings = z.infer<typeof PlanCommandSettingsSchema>\n\n/**\n * TypeScript type for summary settings derived from Zod schema\n */\nexport type SummarySettings = z.infer<typeof SummarySettingsSchema>\n\n/**\n * TypeScript type for workflow permission configuration derived from Zod schema\n */\nexport type WorkflowPermission = z.infer<typeof WorkflowPermissionSchema>\n\n/**\n * TypeScript type for workflows settings derived from Zod schema\n */\nexport type WorkflowsSettings = z.infer<typeof WorkflowsSettingsSchema>\n\n/**\n * TypeScript type for capabilities settings derived from Zod schema\n */\nexport type CapabilitiesSettings = z.infer<typeof CapabilitiesSettingsSchema>\n\n/**\n * TypeScript type for IDE settings derived from Zod schema\n */\nexport type IdeSettings = z.infer<typeof IloomSettingsSchema>['ide']\n\n/**\n * TypeScript type for iloom settings derived from Zod schema\n */\nexport type IloomSettings = z.infer<typeof IloomSettingsSchema>\n\n/**\n * TypeScript input type for iloom settings (before Zod defaults are applied)\n * Used for validation where partial/input objects need to be accepted\n */\nexport type IloomSettingsInput = z.input<typeof IloomSettingsSchema>\n\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(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(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(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(merged, null, 2))\n\n\t\tif (cliOverrides && Object.keys(cliOverrides).length > 0) {\n\t\t\tlogger.debug('⚙️ CLI overrides to apply:', JSON.stringify(cliOverrides, null, 2))\n\t\t\tmerged = this.mergeSettings(merged, cliOverrides)\n\t\t\tlogger.debug('🔄 After applying CLI overrides:', JSON.stringify(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(settings, null, 2))\n\t}\n\n\t/**\n\t * Load and parse a single settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Uses non-defaulting schema to prevent polluting partial settings with defaults before merge\n\t */\n\tprivate async loadSettingsFile(\n\t\tprojectRoot: string,\n\t\tfilename: string,\n\t): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = path.join(projectRoot, '.iloom', filename)\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to parse settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Basic type checking - ensure it's an object, but don't validate schema completeness\n\t\t\t// Individual files may be incomplete (e.g., Linear config split between files)\n\t\t\t// Final validation will happen on the merged result in loadSettings()\n\t\t\tif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Settings validation failed at ${filename}:\\n - root: Expected object, received ${typeof parsed}`\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn parsed as z.infer<typeof IloomSettingsSchemaNoDefaults>\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No settings file found at ${settingsPath}, using defaults`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Re-throw parsing errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Deep merge two settings objects with priority to override\n\t * Uses deepmerge library with array replacement strategy\n\t */\n\tprivate mergeSettings(\n\t\tbase: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t\toverride: Partial<IloomSettings> | z.infer<typeof IloomSettingsSchemaNoDefaults>,\n\t): IloomSettings {\n\t\t// Use deepmerge with array replacement (not concatenation)\n\t\t// Type assertion is safe because the merged result will be validated with IloomSettingsSchema\n\t\t// which applies all the defaults after merging\n\t\treturn deepmerge(base as Record<string, unknown>, override as Record<string, unknown>, {\n\t\t\t// Replace arrays instead of concatenating them\n\t\t\tarrayMerge: (_destinationArray, sourceArray) => sourceArray,\n\t\t}) as IloomSettings\n\t}\n\n\t/**\n\t * Format all Zod validation errors into a single error message\n\t */\n\tprivate formatAllZodErrors(error: z.ZodError, settingsPath: string): Error {\n\t\tconst errorMessages = error.issues.map(issue => {\n\t\t\tconst path = issue.path.length > 0 ? issue.path.join('.') : 'root'\n\t\t\treturn ` - ${path}: ${issue.message}`\n\t\t})\n\n\t\treturn new Error(\n\t\t\t`Settings validation failed at ${settingsPath}:\\n${errorMessages.join('\\n')}`,\n\t\t)\n\t}\n\n\t/**\n\t * Validate settings structure and model names using Zod schema\n\t * This method is kept for testing purposes but uses Zod internally\n\t * @internal - Only used in tests via bracket notation\n\t */\n\t// @ts-expect-error - Used in tests via bracket notation, TypeScript can't detect this usage\n\tprivate validateSettings(settings: IloomSettingsInput): void {\n\t\ttry {\n\t\t\tIloomSettingsSchema.parse(settings)\n\t\t} catch (error) {\n\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\tthrow this.formatAllZodErrors(error, '<validation>')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Get project root (defaults to process.cwd())\n\t */\n\tprivate getProjectRoot(projectRoot?: string): string {\n\t\treturn projectRoot ?? process.cwd()\n\t}\n\n\t/**\n\t * Get global config directory path (~/.config/iloom-ai)\n\t */\n\tprivate getGlobalConfigDir(): string {\n\t\treturn path.join(os.homedir(), '.config', 'iloom-ai')\n\t}\n\n\t/**\n\t * Get global settings file path (~/.config/iloom-ai/settings.json)\n\t */\n\tprivate getGlobalSettingsPath(): string {\n\t\treturn path.join(this.getGlobalConfigDir(), 'settings.json')\n\t}\n\n\t/**\n\t * Load and parse global settings file\n\t * Returns empty object if file doesn't exist (not an error)\n\t * Warns but returns empty object on validation/parse errors (graceful degradation)\n\t */\n\tprivate async loadGlobalSettingsFile(): Promise<z.infer<typeof IloomSettingsSchemaNoDefaults>> {\n\t\tconst settingsPath = this.getGlobalSettingsPath()\n\n\t\ttry {\n\t\t\tconst content = await readFile(settingsPath, 'utf-8')\n\t\t\tlet parsed: unknown\n\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(content)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to parse global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Invalid JSON'}. Ignoring global settings.`,\n\t\t\t\t)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Validate with non-defaulting schema\n\t\t\ttry {\n\t\t\t\tconst validated = IloomSettingsSchemaNoDefaults.strict().parse(parsed)\n\t\t\t\treturn validated\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof z.ZodError) {\n\t\t\t\t\tconst errorMsg = this.formatAllZodErrors(error, 'global settings')\n\t\t\t\t\tlogger.warn(`${errorMsg.message}. Ignoring global settings.`)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.warn(`Validation error in global settings: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\t\t}\n\t\t\t\treturn {}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// File not found is not an error - return empty settings\n\t\t\tif ((error as { code?: string }).code === 'ENOENT') {\n\t\t\t\tlogger.debug(`No global settings file found at ${settingsPath}`)\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// Other file system errors - warn and continue\n\t\t\tlogger.warn(`Error reading global settings file at ${settingsPath}: ${error instanceof Error ? error.message : 'Unknown error'}. Ignoring global settings.`)\n\t\t\treturn {}\n\t\t}\n\t}\n\n\t/**\n\t * Get effective protected branches list with mainBranch always included\n\t *\n\t * This method provides a single source of truth for protected branches logic:\n\t * 1. Use configured protectedBranches if provided\n\t * 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t * 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t *\n\t * @param projectRoot - Optional project root directory (defaults to process.cwd())\n\t * @returns Array of protected branch names with mainBranch guaranteed to be included\n\t */\n\tasync getProtectedBranches(projectRoot?: string): Promise<string[]> {\n\t\tconst settings = await this.loadSettings(projectRoot)\n\t\t// SYNC: If this default changes, update displayDefaultsBox() in src/utils/first-run-setup.ts\n\t\tconst mainBranch = settings.mainBranch ?? 'main'\n\n\t\t// Build protected branches list:\n\t\t// 1. Use configured protectedBranches if provided\n\t\t// 2. Otherwise use defaults: [mainBranch, 'main', 'master', 'develop']\n\t\t// 3. ALWAYS ensure mainBranch is included even if user configured custom list\n\t\tlet protectedBranches: string[]\n\t\tif (settings.protectedBranches) {\n\t\t\t// Use configured list but ensure mainBranch is always included\n\t\t\tprotectedBranches = settings.protectedBranches.includes(mainBranch)\n\t\t\t\t? settings.protectedBranches\n\t\t\t\t: [mainBranch, ...settings.protectedBranches]\n\t\t} else {\n\t\t\t// Use defaults with current mainBranch\n\t\t\tprotectedBranches = [mainBranch, 'main', 'master', 'develop']\n\t\t}\n\n\t\treturn protectedBranches\n\t}\n\n\t/**\n\t * Get the spin orchestrator model with default applied\n\t * Default is defined in SpinAgentSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetSpinModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.spin?.model ?? SpinAgentSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command model with default applied\n\t * Default is defined in PlanCommandSettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetPlanModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.plan?.model ?? PlanCommandSettingsSchema.parse({}).model\n\t}\n\n\t/**\n\t * Get the plan command planner with default applied\n\t * Default is 'claude'\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Planner provider ('claude', 'gemini', or 'codex')\n\t */\n\tgetPlanPlanner(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' {\n\t\treturn settings?.plan?.planner ?? 'claude'\n\t}\n\n\t/**\n\t * Get the plan command reviewer with default applied\n\t * Default is 'none' (no review step)\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Reviewer provider ('claude', 'gemini', 'codex', or 'none')\n\t */\n\tgetPlanReviewer(settings?: IloomSettings): 'claude' | 'gemini' | 'codex' | 'none' {\n\t\treturn settings?.plan?.reviewer ?? 'none'\n\t}\n\n\t/**\n\t * Get the session summary model with default applied\n\t * Default is defined in SummarySettingsSchema\n\t *\n\t * @param settings - Pre-loaded settings object\n\t * @returns Model shorthand ('opus', 'sonnet', or 'haiku')\n\t */\n\tgetSummaryModel(settings?: IloomSettings): 'sonnet' | 'opus' | 'haiku' {\n\t\treturn settings?.summary?.model ?? SummarySettingsSchema.parse({}).model\n\t}\n}\n"],"mappings":"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","names":["path"]}
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PRManager
4
- } from "./chunk-I3HMNWQQ.js";
4
+ } from "./chunk-DGG2VY7B.js";
5
5
  import {
6
6
  calculatePortForBranch,
7
7
  calculatePortFromIdentifier
8
- } from "./chunk-USSL2X4A.js";
8
+ } from "./chunk-NUACL52E.js";
9
9
  import {
10
10
  installDependencies,
11
11
  runScript
@@ -31,16 +31,16 @@ import {
31
31
  isBranchMergedIntoMain,
32
32
  isFileTrackedByGit,
33
33
  pushBranchToRemote
34
- } from "./chunk-H6ST2TGP.js";
34
+ } from "./chunk-AR5QKYNE.js";
35
35
  import {
36
36
  SettingsManager
37
- } from "./chunk-XFEK2X2D.js";
37
+ } from "./chunk-RI2YL6TK.js";
38
38
  import {
39
39
  MetadataManager
40
40
  } from "./chunk-KBEIQP4G.js";
41
41
  import {
42
42
  GitHubService
43
- } from "./chunk-Y5HSSIK2.js";
43
+ } from "./chunk-OFDN5NKS.js";
44
44
  import {
45
45
  calculateForegroundColor,
46
46
  generateColorFromBranchName,
@@ -259,7 +259,7 @@ var LoomManager = class {
259
259
  getLogger().info("Preparing branch name...");
260
260
  const branchName = await this.prepareBranchName(input, issueData);
261
261
  getLogger().info("Creating git worktree...");
262
- const worktreePath = await this.createWorktreeOnly(input, branchName);
262
+ const worktreePath = await this.createWorktreeOnly(input, branchName, issueData);
263
263
  this.loadMainEnvFile();
264
264
  const { capabilities, binEntries } = await this.capabilityDetector.detectCapabilities(worktreePath);
265
265
  await this.copyEnvironmentFiles(worktreePath);
@@ -440,8 +440,8 @@ ${issueData.body}` : "";
440
440
  const setArguments = (_k = input.options) == null ? void 0 : _k.setArguments;
441
441
  const executablePath = (_l = input.options) == null ? void 0 : _l.executablePath;
442
442
  if (enableClaude || enableCode || enableDevServer || enableTerminal) {
443
- const { LoomLauncher } = await import("./LoomLauncher-3I47SUPV.js");
444
- const { ClaudeContextManager } = await import("./ClaudeContextManager-X2Y72GRL.js");
443
+ const { LoomLauncher } = await import("./LoomLauncher-73NXL2CL.js");
444
+ const { ClaudeContextManager } = await import("./ClaudeContextManager-HR5JQKAI.js");
445
445
  const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
446
446
  const launcher = new LoomLauncher(claudeContext, this.settings);
447
447
  await launcher.launchLoom({
@@ -592,7 +592,7 @@ ${issueData.body}` : "";
592
592
  async checkAndWarnChildLooms(branchName) {
593
593
  let targetBranch = branchName;
594
594
  if (!targetBranch) {
595
- const { getCurrentBranch } = await import("./git-W3XUIFTR.js");
595
+ const { getCurrentBranch } = await import("./git-2QDQ2X2S.js");
596
596
  targetBranch = await getCurrentBranch();
597
597
  }
598
598
  if (!targetBranch) {
@@ -660,7 +660,7 @@ ${issueData.body}` : "";
660
660
  /**
661
661
  * Create worktree for the loom (without dependency installation)
662
662
  */
663
- async createWorktreeOnly(input, branchName) {
663
+ async createWorktreeOnly(input, branchName, issueData) {
664
664
  var _a;
665
665
  getLogger().info("Ensuring repository has initial commit...");
666
666
  await ensureRepositoryHasCommits(this.gitWorktree.workingDirectory);
@@ -680,15 +680,31 @@ ${issueData.body}` : "";
680
680
  void 0,
681
681
  pathOptions
682
682
  );
683
+ const isForkPR = input.type === "pr" && issueData && "isFork" in issueData && issueData.isFork === true;
683
684
  if (input.type === "pr") {
684
- getLogger().info("Fetching all remote branches...");
685
- try {
686
- await executeGitCommand(["fetch", "origin"], { cwd: this.gitWorktree.workingDirectory });
687
- getLogger().success("Successfully fetched from remote");
688
- } catch (error) {
689
- throw new Error(
690
- `Failed to fetch from remote: ${error instanceof Error ? error.message : "Unknown error"}. Make sure you have access to the repository.`
691
- );
685
+ if (isForkPR) {
686
+ getLogger().info(`Fetching PR #${input.identifier} ref from origin...`);
687
+ try {
688
+ await executeGitCommand(
689
+ ["fetch", "origin", `refs/pull/${input.identifier}/head`],
690
+ { cwd: this.gitWorktree.workingDirectory }
691
+ );
692
+ getLogger().success("Successfully fetched PR ref from remote");
693
+ } catch (error) {
694
+ throw new Error(
695
+ `Failed to fetch PR ref: ${error instanceof Error ? error.message : "Unknown error"}. Make sure you have access to the repository.`
696
+ );
697
+ }
698
+ } else {
699
+ getLogger().info("Fetching all remote branches...");
700
+ try {
701
+ await executeGitCommand(["fetch", "origin"], { cwd: this.gitWorktree.workingDirectory });
702
+ getLogger().success("Successfully fetched from remote");
703
+ } catch (error) {
704
+ throw new Error(
705
+ `Failed to fetch from remote: ${error instanceof Error ? error.message : "Unknown error"}. Make sure you have access to the repository.`
706
+ );
707
+ }
692
708
  }
693
709
  }
694
710
  const branchExistedLocally = await branchExists(branchName, process.cwd(), false);
@@ -712,21 +728,33 @@ ${issueData.body}` : "";
712
728
  } else {
713
729
  baseBranch = input.baseBranch;
714
730
  }
715
- await this.gitWorktree.createWorktree({
716
- path: worktreePath,
717
- branch: branchName,
718
- createBranch: input.type !== "pr",
719
- // PRs use existing branches
720
- ...baseBranch && { baseBranch }
721
- });
722
- if (input.type === "pr" && !branchExistedLocally) {
723
- getLogger().info("Resetting new PR branch to match remote exactly...");
724
- try {
725
- await executeGitCommand(["reset", "--hard", `origin/${branchName}`], { cwd: worktreePath });
726
- await executeGitCommand(["branch", "--set-upstream-to", `origin/${branchName}`], { cwd: worktreePath });
727
- getLogger().success("Successfully reset to match remote");
728
- } catch (error) {
729
- getLogger().warn(`Failed to reset to match remote: ${error instanceof Error ? error.message : "Unknown error"}`);
731
+ if (isForkPR) {
732
+ if (branchExistedLocally) {
733
+ await executeGitCommand(["branch", "-D", branchName], { cwd: this.gitWorktree.workingDirectory });
734
+ }
735
+ await this.gitWorktree.createWorktree({
736
+ path: worktreePath,
737
+ branch: branchName,
738
+ createBranch: true,
739
+ baseBranch: "FETCH_HEAD"
740
+ });
741
+ } else {
742
+ await this.gitWorktree.createWorktree({
743
+ path: worktreePath,
744
+ branch: branchName,
745
+ createBranch: input.type !== "pr",
746
+ // PRs use existing branches
747
+ ...baseBranch && { baseBranch }
748
+ });
749
+ if (input.type === "pr" && !branchExistedLocally) {
750
+ getLogger().info("Resetting new PR branch to match remote exactly...");
751
+ try {
752
+ await executeGitCommand(["reset", "--hard", `origin/${branchName}`], { cwd: worktreePath });
753
+ await executeGitCommand(["branch", "--set-upstream-to", `origin/${branchName}`], { cwd: worktreePath });
754
+ getLogger().success("Successfully reset to match remote");
755
+ } catch (error) {
756
+ getLogger().warn(`Failed to reset to match remote: ${error instanceof Error ? error.message : "Unknown error"}`);
757
+ }
730
758
  }
731
759
  }
732
760
  return worktreePath;
@@ -1112,8 +1140,8 @@ ${issueData.body}` : "";
1112
1140
  const executablePath = (_i = input.options) == null ? void 0 : _i.executablePath;
1113
1141
  if (enableClaude || enableCode || enableDevServer || enableTerminal) {
1114
1142
  getLogger().info("Launching workspace components...");
1115
- const { LoomLauncher } = await import("./LoomLauncher-3I47SUPV.js");
1116
- const { ClaudeContextManager } = await import("./ClaudeContextManager-X2Y72GRL.js");
1143
+ const { LoomLauncher } = await import("./LoomLauncher-73NXL2CL.js");
1144
+ const { ClaudeContextManager } = await import("./ClaudeContextManager-HR5JQKAI.js");
1117
1145
  const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
1118
1146
  const launcher = new LoomLauncher(claudeContext, this.settings);
1119
1147
  await launcher.launchLoom({
@@ -2605,4 +2633,4 @@ export {
2605
2633
  DatabaseManager,
2606
2634
  ResourceCleanup
2607
2635
  };
2608
- //# sourceMappingURL=chunk-4ZIHFUPN.js.map
2636
+ //# sourceMappingURL=chunk-SOSQILHO.js.map