@iloom/cli 0.1.16 → 0.1.18

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 (120) hide show
  1. package/README.md +20 -7
  2. package/dist/ClaudeContextManager-LD3VB6EM.js +13 -0
  3. package/dist/ClaudeService-CFFI7DD5.js +12 -0
  4. package/dist/{GitHubService-F7Z3XJOS.js → GitHubService-SH4H6VS5.js} +3 -3
  5. package/dist/{LoomLauncher-MODG2SEM.js → LoomLauncher-FB2MV2ZI.js} +7 -7
  6. package/dist/{PromptTemplateManager-7FINLRDE.js → PromptTemplateManager-WM5GIPEF.js} +2 -2
  7. package/dist/{SettingsManager-VAZF26S2.js → SettingsManager-SKLUVE3K.js} +6 -2
  8. package/dist/{add-issue-22JBNOML.js → add-issue-L5HX6LEV.js} +23 -8
  9. package/dist/add-issue-L5HX6LEV.js.map +1 -0
  10. package/dist/{chunk-U3WU5OWO.js → chunk-4XIDC3NF.js} +2 -2
  11. package/dist/chunk-4XIDC3NF.js.map +1 -0
  12. package/dist/{chunk-SSR5AVRJ.js → chunk-6OTVPRXH.js} +21 -8
  13. package/dist/chunk-6OTVPRXH.js.map +1 -0
  14. package/dist/{chunk-KQDEK2ZW.js → chunk-DGEKUT7Q.js} +9 -5
  15. package/dist/chunk-DGEKUT7Q.js.map +1 -0
  16. package/dist/chunk-FXV24OYZ.js +83 -0
  17. package/dist/chunk-FXV24OYZ.js.map +1 -0
  18. package/dist/{chunk-HPJJSYNS.js → chunk-H5LDRGVK.js} +6 -8
  19. package/dist/{chunk-HPJJSYNS.js.map → chunk-H5LDRGVK.js.map} +1 -1
  20. package/dist/{chunk-WKEWRSDB.js → chunk-HURVAQRK.js} +3 -3
  21. package/dist/{chunk-T7QPXANZ.js → chunk-IIPTBZQW.js} +17 -17
  22. package/dist/chunk-IIPTBZQW.js.map +1 -0
  23. package/dist/{chunk-QEPVTTHD.js → chunk-IO4WFTL2.js} +17 -11
  24. package/dist/chunk-IO4WFTL2.js.map +1 -0
  25. package/dist/{chunk-JQ7VOSTC.js → chunk-KOCQAD2E.js} +3 -3
  26. package/dist/{chunk-F3XBU2R7.js → chunk-L4QGC27H.js} +68 -2
  27. package/dist/chunk-L4QGC27H.js.map +1 -0
  28. package/dist/{chunk-YYSKGAZT.js → chunk-LAPY6NAE.js} +17 -8
  29. package/dist/chunk-LAPY6NAE.js.map +1 -0
  30. package/dist/{chunk-O2QWO64Z.js → chunk-PV3GAXQO.js} +56 -3
  31. package/dist/chunk-PV3GAXQO.js.map +1 -0
  32. package/dist/{chunk-CP2NU2JC.js → chunk-Q2KYPAH2.js} +7 -7
  33. package/dist/{chunk-CP2NU2JC.js.map → chunk-Q2KYPAH2.js.map} +1 -1
  34. package/dist/{chunk-Y7SAGNUT.js → chunk-SLIMABFA.js} +2 -2
  35. package/dist/{chunk-W3DQTW63.js → chunk-USVVV3FP.js} +4 -4
  36. package/dist/chunk-VVH3ANF2.js +307 -0
  37. package/dist/chunk-VVH3ANF2.js.map +1 -0
  38. package/dist/{chunk-JBH2ZYYZ.js → chunk-VYQLLHZ7.js} +22 -3
  39. package/dist/chunk-VYQLLHZ7.js.map +1 -0
  40. package/dist/{chunk-SJUQ2NDR.js → chunk-ZMNQBJUI.js} +24 -19
  41. package/dist/chunk-ZMNQBJUI.js.map +1 -0
  42. package/dist/{cleanup-3LUWPSM7.js → cleanup-ZHROIBSQ.js} +12 -16
  43. package/dist/cleanup-ZHROIBSQ.js.map +1 -0
  44. package/dist/cli.js +97 -49
  45. package/dist/cli.js.map +1 -1
  46. package/dist/{enhance-XJIQHVPD.js → enhance-VVMAKMVZ.js} +18 -8
  47. package/dist/enhance-VVMAKMVZ.js.map +1 -0
  48. package/dist/{feedback-23CLXKFT.js → feedback-AKHD7QIM.js} +8 -8
  49. package/dist/{finish-CY4CIH6O.js → finish-WGPISUEH.js} +60 -313
  50. package/dist/finish-WGPISUEH.js.map +1 -0
  51. package/dist/{git-LVRZ57GJ.js → git-OUYMVYJX.js} +2 -2
  52. package/dist/{ignite-WXEF2ID5.js → ignite-JEN3K3OT.js} +7 -7
  53. package/dist/index.d.ts +791 -712
  54. package/dist/index.js +126 -32
  55. package/dist/index.js.map +1 -1
  56. package/dist/init-EVUT4ZQJ.js +339 -0
  57. package/dist/init-EVUT4ZQJ.js.map +1 -0
  58. package/dist/mcp/github-comment-server.js +12 -9
  59. package/dist/mcp/github-comment-server.js.map +1 -1
  60. package/dist/neon-helpers-ZVIRPKCI.js +10 -0
  61. package/dist/{open-X6BTENPV.js → open-ETZUFSE4.js} +15 -17
  62. package/dist/{open-X6BTENPV.js.map → open-ETZUFSE4.js.map} +1 -1
  63. package/dist/prompts/init-prompt.txt +746 -0
  64. package/dist/prompts/issue-prompt.txt +48 -5
  65. package/dist/prompts/pr-prompt.txt +46 -1
  66. package/dist/prompts/regular-prompt.txt +22 -0
  67. package/dist/rebase-KBWFDZCN.js +95 -0
  68. package/dist/rebase-KBWFDZCN.js.map +1 -0
  69. package/dist/remote-GJEZWRCC.js +14 -0
  70. package/dist/{run-2JCPQAX3.js → run-4SVQ3WEU.js} +15 -17
  71. package/dist/{run-2JCPQAX3.js.map → run-4SVQ3WEU.js.map} +1 -1
  72. package/dist/schema/settings.schema.json +51 -1
  73. package/dist/{start-LWVRBJ6S.js → start-2NEZU7SE.js} +54 -53
  74. package/dist/{start-LWVRBJ6S.js.map → start-2NEZU7SE.js.map} +1 -1
  75. package/dist/{test-git-XPF4SZXJ.js → test-git-MKZATGZN.js} +3 -3
  76. package/dist/{test-prefix-XGFXFAYN.js → test-prefix-ZNLWDI3K.js} +3 -3
  77. package/dist/{update-3ZT2XX2G.js → update-4TDDUR5K.js} +11 -5
  78. package/dist/{update-3ZT2XX2G.js.map → update-4TDDUR5K.js.map} +1 -1
  79. package/dist/{update-notifier-QSSEB5KC.js → update-notifier-QEX3CJHA.js} +2 -2
  80. package/package.json +1 -1
  81. package/dist/ClaudeContextManager-XOSXQ67R.js +0 -13
  82. package/dist/ClaudeService-YSZ6EXWP.js +0 -12
  83. package/dist/NeonProvider-PAGPUH7F.js +0 -12
  84. package/dist/add-issue-22JBNOML.js.map +0 -1
  85. package/dist/chunk-37DYYFVK.js +0 -29
  86. package/dist/chunk-37DYYFVK.js.map +0 -1
  87. package/dist/chunk-F3XBU2R7.js.map +0 -1
  88. package/dist/chunk-JBH2ZYYZ.js.map +0 -1
  89. package/dist/chunk-KQDEK2ZW.js.map +0 -1
  90. package/dist/chunk-O2QWO64Z.js.map +0 -1
  91. package/dist/chunk-QEPVTTHD.js.map +0 -1
  92. package/dist/chunk-SJUQ2NDR.js.map +0 -1
  93. package/dist/chunk-SSR5AVRJ.js.map +0 -1
  94. package/dist/chunk-T7QPXANZ.js.map +0 -1
  95. package/dist/chunk-U3WU5OWO.js.map +0 -1
  96. package/dist/chunk-YYSKGAZT.js.map +0 -1
  97. package/dist/cleanup-3LUWPSM7.js.map +0 -1
  98. package/dist/enhance-XJIQHVPD.js.map +0 -1
  99. package/dist/env-MDFL4ZXL.js +0 -23
  100. package/dist/finish-CY4CIH6O.js.map +0 -1
  101. package/dist/init-RHACUR4E.js +0 -123
  102. package/dist/init-RHACUR4E.js.map +0 -1
  103. /package/dist/{ClaudeContextManager-XOSXQ67R.js.map → ClaudeContextManager-LD3VB6EM.js.map} +0 -0
  104. /package/dist/{ClaudeService-YSZ6EXWP.js.map → ClaudeService-CFFI7DD5.js.map} +0 -0
  105. /package/dist/{GitHubService-F7Z3XJOS.js.map → GitHubService-SH4H6VS5.js.map} +0 -0
  106. /package/dist/{LoomLauncher-MODG2SEM.js.map → LoomLauncher-FB2MV2ZI.js.map} +0 -0
  107. /package/dist/{NeonProvider-PAGPUH7F.js.map → PromptTemplateManager-WM5GIPEF.js.map} +0 -0
  108. /package/dist/{PromptTemplateManager-7FINLRDE.js.map → SettingsManager-SKLUVE3K.js.map} +0 -0
  109. /package/dist/{chunk-WKEWRSDB.js.map → chunk-HURVAQRK.js.map} +0 -0
  110. /package/dist/{chunk-JQ7VOSTC.js.map → chunk-KOCQAD2E.js.map} +0 -0
  111. /package/dist/{chunk-Y7SAGNUT.js.map → chunk-SLIMABFA.js.map} +0 -0
  112. /package/dist/{chunk-W3DQTW63.js.map → chunk-USVVV3FP.js.map} +0 -0
  113. /package/dist/{feedback-23CLXKFT.js.map → feedback-AKHD7QIM.js.map} +0 -0
  114. /package/dist/{SettingsManager-VAZF26S2.js.map → git-OUYMVYJX.js.map} +0 -0
  115. /package/dist/{ignite-WXEF2ID5.js.map → ignite-JEN3K3OT.js.map} +0 -0
  116. /package/dist/{env-MDFL4ZXL.js.map → neon-helpers-ZVIRPKCI.js.map} +0 -0
  117. /package/dist/{git-LVRZ57GJ.js.map → remote-GJEZWRCC.js.map} +0 -0
  118. /package/dist/{test-git-XPF4SZXJ.js.map → test-git-MKZATGZN.js.map} +0 -0
  119. /package/dist/{test-prefix-XGFXFAYN.js.map → test-prefix-ZNLWDI3K.js.map} +0 -0
  120. /package/dist/{update-notifier-QSSEB5KC.js.map → update-notifier-QEX3CJHA.js.map} +0 -0
package/README.md CHANGED
@@ -318,6 +318,12 @@ iloom finish
318
318
  # AI assisted validation, commit, merge steps, as well as loom cleanup (run this from the loom directory)
319
319
  # Alias: dn
320
320
 
321
+ iloom rebase
322
+ # Rebase current branch on main with Claude-assisted conflict resolution (run this from a loom directory)
323
+ # Options:
324
+ # -f, --force - Skip confirmation prompts
325
+ # -n, --dry-run - Preview actions without executing
326
+
321
327
  iloom cleanup [identifier...]
322
328
  # Remove a loom without merging (safely, by default)
323
329
 
@@ -414,6 +420,12 @@ This allows teams to share project defaults via `settings.json` while individual
414
420
  "web": { "basePort": 3000 },
415
421
  "database": { "databaseUrlEnvVarName": "DATABASE_URL" }
416
422
  },
423
+ "databaseProviders": {
424
+ "neon": {
425
+ "projectId": "fantastic-fox-3566354",
426
+ "parentBranch": "main"
427
+ }
428
+ },
417
429
  "workflows": {
418
430
  "issue": {
419
431
  "permissionMode": "default",
@@ -424,23 +436,24 @@ This allows teams to share project defaults via `settings.json` while individual
424
436
  }
425
437
  },
426
438
  "agents": {
427
- "iloom-issue-enhancer": "sonnet",
428
- "iloom-issue-analyzer": "sonnet",
429
- "iloom-issue-analyze-and-plan": "sonnet",
430
- "iloom-issue-implementer": "sonnet"
439
+ "iloom-issue-enhancer": {"model":"opus"},
440
+ "iloom-issue-analyzer": {"model":"opus"},
441
+ "iloom-issue-analyze-and-plan": {"model":"opus"},
442
+ "iloom-issue-implementer": {"model":"haiku"}
431
443
  }
432
444
  }
433
445
  ```
434
446
 
435
- **Note on agent configuration:** All agents use the latest Sonnet model by default. The example above shows a performance-optimized configuration:
447
+ **Note on agent configuration:** All agents use the latest Sonnet model by default, except complexity evaluator which uses Haiku. You could also try a some different configurations:
436
448
  - **Opus for analysis/enhancement** - Maximum reasoning capability for understanding requirements and planning
437
449
  - **Haiku for implementation** - Cost-effective execution of detailed plans (recommended for token-conscious users)
438
- - Other agents (complexity evaluator, planner, reviewer) remain on Sonnet by default
439
450
 
440
- **Configuration options:**
451
+ ** Common configuration options:**
441
452
  - `mainBranch` - Primary branch for merging (default: "main")
442
453
  - `capabilities.web.basePort` - Base port for dev servers (default: 3000)
443
454
  - `capabilities.database.databaseUrlEnvVarName` - Name of environment variable for database connection URL (default: "DATABASE_URL")
455
+ - `databaseProviders.neon.projectId` - Neon project ID (found in project URL, e.g., "fantastic-fox-3566354")
456
+ - `databaseProviders.neon.parentBranch` - Branch from which new database branches are created (default: "main")
444
457
  - `workflows` - Per-workflow Claude CLI permission modes and tool launching
445
458
  - `agents` - Claude model selection (sonnet/opus/haiku) per agent type
446
459
 
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ClaudeContextManager
4
+ } from "./chunk-SLIMABFA.js";
5
+ import "./chunk-HURVAQRK.js";
6
+ import "./chunk-PXZBAC2M.js";
7
+ import "./chunk-L4QGC27H.js";
8
+ import "./chunk-VYQLLHZ7.js";
9
+ import "./chunk-GEHQXLEI.js";
10
+ export {
11
+ ClaudeContextManager
12
+ };
13
+ //# sourceMappingURL=ClaudeContextManager-LD3VB6EM.js.map
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ClaudeService
4
+ } from "./chunk-HURVAQRK.js";
5
+ import "./chunk-PXZBAC2M.js";
6
+ import "./chunk-L4QGC27H.js";
7
+ import "./chunk-VYQLLHZ7.js";
8
+ import "./chunk-GEHQXLEI.js";
9
+ export {
10
+ ClaudeService
11
+ };
12
+ //# sourceMappingURL=ClaudeService-CFFI7DD5.js.map
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  GitHubService
4
- } from "./chunk-T7QPXANZ.js";
5
- import "./chunk-KQDEK2ZW.js";
4
+ } from "./chunk-IIPTBZQW.js";
5
+ import "./chunk-DGEKUT7Q.js";
6
6
  import "./chunk-JNKJ7NJV.js";
7
7
  import "./chunk-GEHQXLEI.js";
8
8
  export {
9
9
  GitHubService
10
10
  };
11
- //# sourceMappingURL=GitHubService-F7Z3XJOS.js.map
11
+ //# sourceMappingURL=GitHubService-SH4H6VS5.js.map
@@ -3,6 +3,9 @@ import {
3
3
  getDevServerLaunchCommand
4
4
  } from "./chunk-GZP4UGGM.js";
5
5
  import "./chunk-BLCTGFZN.js";
6
+ import {
7
+ ClaudeContextManager
8
+ } from "./chunk-SLIMABFA.js";
6
9
  import {
7
10
  openMultipleTerminalWindows,
8
11
  openTerminalWindow
@@ -10,13 +13,10 @@ import {
10
13
  import {
11
14
  generateColorFromBranchName
12
15
  } from "./chunk-ZZZWQGTS.js";
13
- import {
14
- ClaudeContextManager
15
- } from "./chunk-Y7SAGNUT.js";
16
- import "./chunk-WKEWRSDB.js";
16
+ import "./chunk-HURVAQRK.js";
17
17
  import "./chunk-PXZBAC2M.js";
18
- import "./chunk-F3XBU2R7.js";
19
- import "./chunk-JBH2ZYYZ.js";
18
+ import "./chunk-L4QGC27H.js";
19
+ import "./chunk-VYQLLHZ7.js";
20
20
  import {
21
21
  logger
22
22
  } from "./chunk-GEHQXLEI.js";
@@ -260,4 +260,4 @@ var LoomLauncher = class {
260
260
  export {
261
261
  LoomLauncher
262
262
  };
263
- //# sourceMappingURL=LoomLauncher-MODG2SEM.js.map
263
+ //# sourceMappingURL=LoomLauncher-FB2MV2ZI.js.map
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PromptTemplateManager
4
- } from "./chunk-F3XBU2R7.js";
4
+ } from "./chunk-L4QGC27H.js";
5
5
  import "./chunk-GEHQXLEI.js";
6
6
  export {
7
7
  PromptTemplateManager
8
8
  };
9
- //# sourceMappingURL=PromptTemplateManager-7FINLRDE.js.map
9
+ //# sourceMappingURL=PromptTemplateManager-WM5GIPEF.js.map
@@ -2,18 +2,22 @@
2
2
  import {
3
3
  AgentSettingsSchema,
4
4
  CapabilitiesSettingsSchema,
5
+ DatabaseProvidersSettingsSchema,
5
6
  IloomSettingsSchema,
7
+ NeonSettingsSchema,
6
8
  SettingsManager,
7
9
  WorkflowPermissionSchema,
8
10
  WorkflowsSettingsSchema
9
- } from "./chunk-JBH2ZYYZ.js";
11
+ } from "./chunk-VYQLLHZ7.js";
10
12
  import "./chunk-GEHQXLEI.js";
11
13
  export {
12
14
  AgentSettingsSchema,
13
15
  CapabilitiesSettingsSchema,
16
+ DatabaseProvidersSettingsSchema,
14
17
  IloomSettingsSchema,
18
+ NeonSettingsSchema,
15
19
  SettingsManager,
16
20
  WorkflowPermissionSchema,
17
21
  WorkflowsSettingsSchema
18
22
  };
19
- //# sourceMappingURL=SettingsManager-VAZF26S2.js.map
23
+ //# sourceMappingURL=SettingsManager-SKLUVE3K.js.map
@@ -6,24 +6,31 @@ import {
6
6
  AgentManager
7
7
  } from "./chunk-OC4H6HJD.js";
8
8
  import "./chunk-YETJNRQM.js";
9
+ import {
10
+ getConfiguredRepoFromSettings,
11
+ hasMultipleRemotes
12
+ } from "./chunk-FXV24OYZ.js";
9
13
  import {
10
14
  GitHubService
11
- } from "./chunk-T7QPXANZ.js";
12
- import "./chunk-KQDEK2ZW.js";
15
+ } from "./chunk-IIPTBZQW.js";
16
+ import "./chunk-DGEKUT7Q.js";
13
17
  import "./chunk-PXZBAC2M.js";
14
18
  import {
15
19
  SettingsManager
16
- } from "./chunk-JBH2ZYYZ.js";
20
+ } from "./chunk-VYQLLHZ7.js";
17
21
  import "./chunk-JNKJ7NJV.js";
18
- import "./chunk-GEHQXLEI.js";
22
+ import {
23
+ logger
24
+ } from "./chunk-GEHQXLEI.js";
19
25
 
20
26
  // src/commands/add-issue.ts
21
27
  var AddIssueCommand = class {
22
- constructor(enhancementService) {
28
+ constructor(enhancementService, settingsManager) {
29
+ this.settingsManager = settingsManager ?? new SettingsManager();
23
30
  this.enhancementService = enhancementService ?? new IssueEnhancementService(
24
31
  new GitHubService(),
25
32
  new AgentManager(),
26
- new SettingsManager()
33
+ this.settingsManager
27
34
  );
28
35
  }
29
36
  /**
@@ -36,13 +43,21 @@ var AddIssueCommand = class {
36
43
  */
37
44
  async execute(input) {
38
45
  const { description } = input;
46
+ const settings = await this.settingsManager.loadSettings();
47
+ let repo;
48
+ const multipleRemotes = await hasMultipleRemotes();
49
+ if (multipleRemotes) {
50
+ repo = await getConfiguredRepoFromSettings(settings);
51
+ logger.info(`Using GitHub repository: ${repo}`);
52
+ }
39
53
  if (!description || !this.enhancementService.validateDescription(description)) {
40
54
  throw new Error("Description is required and must be more than 30 characters with at least 3 words");
41
55
  }
42
56
  const enhancedDescription = await this.enhancementService.enhanceDescription(description);
43
57
  const result = await this.enhancementService.createEnhancedIssue(
44
58
  description,
45
- enhancedDescription
59
+ enhancedDescription,
60
+ repo
46
61
  );
47
62
  await this.enhancementService.waitForReviewAndOpen(result.number);
48
63
  return result.number;
@@ -51,4 +66,4 @@ var AddIssueCommand = class {
51
66
  export {
52
67
  AddIssueCommand
53
68
  };
54
- //# sourceMappingURL=add-issue-22JBNOML.js.map
69
+ //# sourceMappingURL=add-issue-L5HX6LEV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/add-issue.ts"],"sourcesContent":["import type { AddIssueOptions } from '../types/index.js'\nimport { IssueEnhancementService } from '../lib/IssueEnhancementService.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { getConfiguredRepoFromSettings, hasMultipleRemotes } from '../utils/remote.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Input structure for AddIssueCommand\n */\nexport interface AddIssueCommandInput {\n\tdescription: string\n\toptions: AddIssueOptions\n}\n\n/**\n * Command to create and enhance GitHub issues without creating workspaces.\n * This separates the \"document the work\" step from the \"start the work\" step.\n */\nexport class AddIssueCommand {\n\tprivate enhancementService: IssueEnhancementService\n\tprivate settingsManager: SettingsManager\n\n\tconstructor(enhancementService?: IssueEnhancementService, settingsManager?: SettingsManager) {\n\t\t// Use provided service or create default\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\tthis.enhancementService = enhancementService ?? new IssueEnhancementService(\n\t\t\tnew GitHubService(),\n\t\t\tnew AgentManager(),\n\t\t\tthis.settingsManager\n\t\t)\n\t}\n\n\t/**\n\t * Execute the add-issue command workflow:\n\t * 1. Validate description format\n\t * 2. Enhance description with Claude AI\n\t * 3. Create GitHub issue\n\t * 4. Wait for keypress and open browser for review\n\t * 5. Return issue number\n\t */\n\tpublic async execute(input: AddIssueCommandInput): Promise<number> {\n\t\tconst { description } = input\n\n\t\t// Step 0: Load settings and get configured repo for GitHub operations\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tlet repo: string | undefined\n\n\t\tconst multipleRemotes = await hasMultipleRemotes()\n\t\tif (multipleRemotes) {\n\t\t\trepo = await getConfiguredRepoFromSettings(settings)\n\t\t\tlogger.info(`Using GitHub repository: ${repo}`)\n\t\t}\n\n\t\t// Step 1: Validate description format\n\t\tif (!description || !this.enhancementService.validateDescription(description)) {\n\t\t\tthrow new Error('Description is required and must be more than 30 characters with at least 3 words')\n\t\t}\n\n\t\t// Step 2: Enhance description using Claude AI\n\t\tconst enhancedDescription = await this.enhancementService.enhanceDescription(description)\n\n\t\t// Step 3: Create GitHub issue with original as title, enhanced as body\n\t\tconst result = await this.enhancementService.createEnhancedIssue(\n\t\t\tdescription,\n\t\t\tenhancedDescription,\n\t\t\trepo\n\t\t)\n\n\t\t// Step 4: Wait for keypress and open issue in browser for review\n\t\tawait this.enhancementService.waitForReviewAndOpen(result.number)\n\n\t\t// Step 5: Return issue number for reference\n\t\treturn result.number\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAoBO,IAAM,kBAAN,MAAsB;AAAA,EAI5B,YAAY,oBAA8C,iBAAmC;AAE5F,SAAK,kBAAkB,mBAAmB,IAAI,gBAAgB;AAC9D,SAAK,qBAAqB,sBAAsB,IAAI;AAAA,MACnD,IAAI,cAAc;AAAA,MAClB,IAAI,aAAa;AAAA,MACjB,KAAK;AAAA,IACN;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,QAAQ,OAA8C;AAClE,UAAM,EAAE,YAAY,IAAI;AAGxB,UAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa;AACzD,QAAI;AAEJ,UAAM,kBAAkB,MAAM,mBAAmB;AACjD,QAAI,iBAAiB;AACpB,aAAO,MAAM,8BAA8B,QAAQ;AACnD,aAAO,KAAK,4BAA4B,IAAI,EAAE;AAAA,IAC/C;AAGA,QAAI,CAAC,eAAe,CAAC,KAAK,mBAAmB,oBAAoB,WAAW,GAAG;AAC9E,YAAM,IAAI,MAAM,mFAAmF;AAAA,IACpG;AAGA,UAAM,sBAAsB,MAAM,KAAK,mBAAmB,mBAAmB,WAAW;AAGxF,UAAM,SAAS,MAAM,KAAK,mBAAmB;AAAA,MAC5C;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAGA,UAAM,KAAK,mBAAmB,qBAAqB,OAAO,MAAM;AAGhE,WAAO,OAAO;AAAA,EACf;AACD;","names":[]}
@@ -71,7 +71,7 @@ var UpdateNotifier = class {
71
71
  const content = await fs.readFile(this.cacheFilePath, "utf8");
72
72
  logger.debug(`getCachedCheck: Cache file content: ${content}`);
73
73
  const cache = JSON.parse(content);
74
- const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? "360", 10);
74
+ const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? "60", 10);
75
75
  const cacheTimeoutMs = cacheTimeoutMins * 60 * 1e3;
76
76
  logger.debug(`getCachedCheck: Using cache timeout of ${cacheTimeoutMins} minutes`);
77
77
  const now = Date.now();
@@ -200,4 +200,4 @@ export {
200
200
  UpdateNotifier,
201
201
  checkAndNotifyUpdate
202
202
  };
203
- //# sourceMappingURL=chunk-U3WU5OWO.js.map
203
+ //# sourceMappingURL=chunk-4XIDC3NF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/update-notifier.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport chalk from 'chalk'\nimport { logger } from './logger.js'\n\nexport interface UpdateCheckCache {\n lastCheck: number // timestamp\n latestVersion: string\n}\n\nexport interface UpdateCheckResult {\n currentVersion: string\n latestVersion: string\n updateAvailable: boolean\n}\n\nexport class UpdateNotifier {\n private cacheFilePath: string\n private currentVersion: string\n private packageName: string\n\n constructor(currentVersion: string, packageName: string) {\n this.currentVersion = currentVersion\n this.packageName = packageName\n // Cross-platform cache directory\n const configDir = path.join(os.homedir(), '.config', 'iloom-ai')\n this.cacheFilePath = path.join(configDir, 'update-check.json')\n logger.debug(`UpdateNotifier initialized: version=${currentVersion}, package=${packageName}, cachePath=${this.cacheFilePath}`)\n }\n\n /**\n * Check for updates, respecting 24hr cache\n * Returns UpdateCheckResult or null if check failed/not needed\n */\n async checkForUpdates(): Promise<UpdateCheckResult | null> {\n logger.debug('checkForUpdates: Starting update check')\n try {\n // Check cache first\n logger.debug('checkForUpdates: Checking cache')\n const cached = await this.getCachedCheck()\n if (cached !== null) {\n logger.debug(`checkForUpdates: Using cached result - latest=${cached.latestVersion}, lastCheck=${new Date(cached.lastCheck).toISOString()}`)\n const updateAvailable = this.isNewerVersion(this.currentVersion, cached.latestVersion)\n logger.debug(`checkForUpdates: Update available from cache: ${updateAvailable}`)\n return {\n currentVersion: this.currentVersion,\n latestVersion: cached.latestVersion,\n updateAvailable,\n }\n }\n\n logger.debug('checkForUpdates: No valid cache, querying npm registry')\n // Query npm registry\n const latestVersion = await this.fetchLatestVersion()\n if (latestVersion === null) {\n logger.debug('checkForUpdates: Failed to fetch latest version from npm')\n return null\n }\n\n logger.debug(`checkForUpdates: Fetched latest version: ${latestVersion}`)\n\n // Save to cache\n logger.debug('checkForUpdates: Saving to cache')\n await this.saveCacheFile(latestVersion)\n\n const updateAvailable = this.isNewerVersion(this.currentVersion, latestVersion)\n logger.debug(`checkForUpdates: Update available: ${updateAvailable} (current=${this.currentVersion}, latest=${latestVersion})`)\n\n return {\n currentVersion: this.currentVersion,\n latestVersion,\n updateAvailable,\n }\n } catch (error) {\n // Handle all errors gracefully - update check should never break user experience\n logger.debug(`checkForUpdates: Error during update check: ${error}`)\n return null\n }\n }\n\n /**\n * Read cache file, return null if stale or missing\n */\n private async getCachedCheck(): Promise<UpdateCheckCache | null> {\n logger.debug(`getCachedCheck: Checking cache file at ${this.cacheFilePath}`)\n try {\n if (!fs.existsSync(this.cacheFilePath)) {\n logger.debug('getCachedCheck: Cache file does not exist')\n return null\n }\n\n logger.debug('getCachedCheck: Cache file exists, reading contents')\n const content = await fs.readFile(this.cacheFilePath, 'utf8')\n logger.debug(`getCachedCheck: Cache file content: ${content}`)\n const cache = JSON.parse(content) as UpdateCheckCache\n\n // Check if cache is still fresh (< configurable hours)\n const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? '60', 10) // Default 1 hour\n const cacheTimeoutMs = cacheTimeoutMins * 60 * 1000\n logger.debug(`getCachedCheck: Using cache timeout of ${cacheTimeoutMins} minutes`)\n const now = Date.now()\n const age = now - cache.lastCheck\n const ageHours = age / (60 * 60 * 1000)\n logger.debug(`getCachedCheck: Cache age: ${ageHours.toFixed(2)} hours (threshold: ${cacheTimeoutMins / 60} hours)`)\n\n if (now - cache.lastCheck < cacheTimeoutMs) {\n logger.debug('getCachedCheck: Cache is fresh, returning cached data')\n return cache\n }\n\n logger.debug('getCachedCheck: Cache is stale, will query npm registry')\n return null\n } catch (error) {\n // If cache is corrupted or unreadable, treat as missing\n logger.debug(`getCachedCheck: Error reading cache: ${error}`)\n return null\n }\n }\n\n /**\n * Save successful check to cache\n */\n private async saveCacheFile(latestVersion: string): Promise<void> {\n logger.debug(`saveCacheFile: Attempting to save cache for version ${latestVersion}`)\n try {\n // Ensure cache directory exists\n const configDir = path.dirname(this.cacheFilePath)\n logger.debug(`saveCacheFile: Ensuring cache directory exists: ${configDir}`)\n await fs.ensureDir(configDir)\n\n // Write cache file\n const cache: UpdateCheckCache = {\n lastCheck: Date.now(),\n latestVersion,\n }\n const cacheJson = JSON.stringify(cache, null, 2)\n logger.debug(`saveCacheFile: Writing cache file: ${cacheJson}`)\n await fs.writeFile(this.cacheFilePath, cacheJson, 'utf8')\n logger.debug(`saveCacheFile: Cache file saved successfully to ${this.cacheFilePath}`)\n } catch (error) {\n // Log debug message but don't throw - cache write failure shouldn't break anything\n logger.debug(`saveCacheFile: Failed to save update check cache to ${this.cacheFilePath}: ${error}`)\n }\n }\n\n /**\n * Display update notification to user\n */\n displayUpdateNotification(result: UpdateCheckResult): void {\n logger.debug(`displayUpdateNotification: updateAvailable=${result.updateAvailable}, current=${result.currentVersion}, latest=${result.latestVersion}`)\n if (result.updateAvailable) {\n logger.debug('displayUpdateNotification: Displaying update notification to user')\n // Simple, clear update notification\n /* eslint-disable no-console */\n console.log('')\n console.log(' ' + chalk.bold(`Update available: ${result.currentVersion} → ${result.latestVersion}`))\n console.log(' ' + chalk.bold('Run: il update'))\n console.log('')\n /* eslint-enable no-console */\n } else {\n logger.debug('displayUpdateNotification: No update available, skipping notification')\n }\n }\n\n /**\n * Query npm registry for latest version\n */\n private async fetchLatestVersion(): Promise<string | null> {\n logger.debug(`fetchLatestVersion: Querying npm for package ${this.packageName}`)\n try {\n const { stdout } = await execa('npm', ['view', this.packageName, 'version'], {\n timeout: 5000,\n })\n const version = stdout.trim()\n logger.debug(`fetchLatestVersion: npm returned version: ${version}`)\n return version\n } catch (error) {\n // Network errors, timeouts, npm not available, or package not found\n logger.debug(`fetchLatestVersion: Failed to query npm: ${error}`)\n return null\n }\n }\n\n /**\n * Compare semver versions\n * Returns true if latest > current\n */\n private isNewerVersion(current: string, latest: string): boolean {\n logger.debug(`isNewerVersion: Comparing versions - current=${current}, latest=${latest}`)\n // Simple version comparison: split by dots and compare numerically\n try {\n const currentParts = current.split('.').map(p => parseInt(p, 10))\n const latestParts = latest.split('.').map(p => parseInt(p, 10))\n logger.debug(`isNewerVersion: Parsed parts - current=[${currentParts.join(', ')}], latest=[${latestParts.join(', ')}]`)\n\n for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {\n const curr = currentParts[i] ?? 0\n const next = latestParts[i] ?? 0\n\n logger.debug(`isNewerVersion: Comparing part ${i}: current=${curr}, latest=${next}`)\n\n if (next > curr) {\n logger.debug(`isNewerVersion: Latest is newer (${next} > ${curr})`)\n return true\n }\n if (next < curr) {\n logger.debug(`isNewerVersion: Current is newer (${curr} > ${next})`)\n return false\n }\n }\n\n logger.debug('isNewerVersion: Versions are equal')\n return false\n } catch (error) {\n // If parsing fails, assume no update\n logger.debug(`isNewerVersion: Error comparing versions: ${error}`)\n return false\n }\n }\n}\n\n/**\n * Main entry point for update check\n * Call from CLI postAction hook\n */\nexport async function checkAndNotifyUpdate(\n currentVersion: string,\n packageName: string,\n installMethod: string\n): Promise<void> {\n logger.debug(`checkAndNotifyUpdate: Called with version=${currentVersion}, package=${packageName}, installMethod=${installMethod}`)\n try {\n // Only check for global installations\n if (installMethod !== 'global') {\n logger.debug(`checkAndNotifyUpdate: Skipping update check - not a global installation (method=${installMethod})`)\n return\n }\n\n logger.debug('checkAndNotifyUpdate: Creating UpdateNotifier instance')\n const notifier = new UpdateNotifier(currentVersion, packageName)\n\n logger.debug('checkAndNotifyUpdate: Calling checkForUpdates()')\n const result = await notifier.checkForUpdates()\n\n if (result !== null) {\n logger.debug(`checkAndNotifyUpdate: Got result, calling displayUpdateNotification`)\n notifier.displayUpdateNotification(result)\n } else {\n logger.debug('checkAndNotifyUpdate: Result was null, not displaying notification')\n }\n\n logger.debug('checkAndNotifyUpdate: Completed')\n } catch (error) {\n // All errors handled internally - this should never throw\n logger.debug(`checkAndNotifyUpdate: Unexpected error: ${error}`)\n }\n}\n"],"mappings":";;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,aAAa;AACtB,OAAO,WAAW;AAcX,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YAAY,gBAAwB,aAAqB;AACvD,SAAK,iBAAiB;AACtB,SAAK,cAAc;AAEnB,UAAM,YAAY,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,UAAU;AAC/D,SAAK,gBAAgB,KAAK,KAAK,WAAW,mBAAmB;AAC7D,WAAO,MAAM,uCAAuC,cAAc,aAAa,WAAW,eAAe,KAAK,aAAa,EAAE;AAAA,EAC/H;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAqD;AACzD,WAAO,MAAM,wCAAwC;AACrD,QAAI;AAEF,aAAO,MAAM,iCAAiC;AAC9C,YAAM,SAAS,MAAM,KAAK,eAAe;AACzC,UAAI,WAAW,MAAM;AACnB,eAAO,MAAM,iDAAiD,OAAO,aAAa,eAAe,IAAI,KAAK,OAAO,SAAS,EAAE,YAAY,CAAC,EAAE;AAC3I,cAAMA,mBAAkB,KAAK,eAAe,KAAK,gBAAgB,OAAO,aAAa;AACrF,eAAO,MAAM,iDAAiDA,gBAAe,EAAE;AAC/E,eAAO;AAAA,UACL,gBAAgB,KAAK;AAAA,UACrB,eAAe,OAAO;AAAA,UACtB,iBAAAA;AAAA,QACF;AAAA,MACF;AAEA,aAAO,MAAM,wDAAwD;AAErE,YAAM,gBAAgB,MAAM,KAAK,mBAAmB;AACpD,UAAI,kBAAkB,MAAM;AAC1B,eAAO,MAAM,0DAA0D;AACvE,eAAO;AAAA,MACT;AAEA,aAAO,MAAM,4CAA4C,aAAa,EAAE;AAGxE,aAAO,MAAM,kCAAkC;AAC/C,YAAM,KAAK,cAAc,aAAa;AAEtC,YAAM,kBAAkB,KAAK,eAAe,KAAK,gBAAgB,aAAa;AAC9E,aAAO,MAAM,sCAAsC,eAAe,aAAa,KAAK,cAAc,YAAY,aAAa,GAAG;AAE9H,aAAO;AAAA,QACL,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAEd,aAAO,MAAM,+CAA+C,KAAK,EAAE;AACnE,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAmD;AAC/D,WAAO,MAAM,0CAA0C,KAAK,aAAa,EAAE;AAC3E,QAAI;AACF,UAAI,CAAC,GAAG,WAAW,KAAK,aAAa,GAAG;AACtC,eAAO,MAAM,2CAA2C;AACxD,eAAO;AAAA,MACT;AAEA,aAAO,MAAM,qDAAqD;AAClE,YAAM,UAAU,MAAM,GAAG,SAAS,KAAK,eAAe,MAAM;AAC5D,aAAO,MAAM,uCAAuC,OAAO,EAAE;AAC7D,YAAM,QAAQ,KAAK,MAAM,OAAO;AAGhC,YAAM,mBAAmB,SAAS,QAAQ,IAAI,mCAAmC,MAAM,EAAE;AACzF,YAAM,iBAAiB,mBAAmB,KAAK;AAC/C,aAAO,MAAM,0CAA0C,gBAAgB,UAAU;AACjF,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,MAAM,MAAM,MAAM;AACxB,YAAM,WAAW,OAAO,KAAK,KAAK;AAClC,aAAO,MAAM,8BAA8B,SAAS,QAAQ,CAAC,CAAC,sBAAsB,mBAAmB,EAAE,SAAS;AAElH,UAAI,MAAM,MAAM,YAAY,gBAAgB;AAC1C,eAAO,MAAM,uDAAuD;AACpE,eAAO;AAAA,MACT;AAEA,aAAO,MAAM,yDAAyD;AACtE,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,aAAO,MAAM,wCAAwC,KAAK,EAAE;AAC5D,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAc,eAAsC;AAChE,WAAO,MAAM,uDAAuD,aAAa,EAAE;AACnF,QAAI;AAEF,YAAM,YAAY,KAAK,QAAQ,KAAK,aAAa;AACjD,aAAO,MAAM,mDAAmD,SAAS,EAAE;AAC3E,YAAM,GAAG,UAAU,SAAS;AAG5B,YAAM,QAA0B;AAAA,QAC9B,WAAW,KAAK,IAAI;AAAA,QACpB;AAAA,MACF;AACA,YAAM,YAAY,KAAK,UAAU,OAAO,MAAM,CAAC;AAC/C,aAAO,MAAM,sCAAsC,SAAS,EAAE;AAC9D,YAAM,GAAG,UAAU,KAAK,eAAe,WAAW,MAAM;AACxD,aAAO,MAAM,mDAAmD,KAAK,aAAa,EAAE;AAAA,IACtF,SAAS,OAAO;AAEd,aAAO,MAAM,uDAAuD,KAAK,aAAa,KAAK,KAAK,EAAE;AAAA,IACpG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,0BAA0B,QAAiC;AACzD,WAAO,MAAM,8CAA8C,OAAO,eAAe,aAAa,OAAO,cAAc,YAAY,OAAO,aAAa,EAAE;AACrJ,QAAI,OAAO,iBAAiB;AAC1B,aAAO,MAAM,mEAAmE;AAGhF,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,OAAO,MAAM,KAAK,qBAAqB,OAAO,cAAc,WAAM,OAAO,aAAa,EAAE,CAAC;AACrG,cAAQ,IAAI,OAAO,MAAM,KAAK,gBAAgB,CAAC;AAC/C,cAAQ,IAAI,EAAE;AAAA,IAEhB,OAAO;AACL,aAAO,MAAM,uEAAuE;AAAA,IACtF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAA6C;AACzD,WAAO,MAAM,gDAAgD,KAAK,WAAW,EAAE;AAC/E,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM,MAAM,OAAO,CAAC,QAAQ,KAAK,aAAa,SAAS,GAAG;AAAA,QAC3E,SAAS;AAAA,MACX,CAAC;AACD,YAAM,UAAU,OAAO,KAAK;AAC5B,aAAO,MAAM,6CAA6C,OAAO,EAAE;AACnE,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,aAAO,MAAM,4CAA4C,KAAK,EAAE;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe,SAAiB,QAAyB;AAC/D,WAAO,MAAM,gDAAgD,OAAO,YAAY,MAAM,EAAE;AAExF,QAAI;AACF,YAAM,eAAe,QAAQ,MAAM,GAAG,EAAE,IAAI,OAAK,SAAS,GAAG,EAAE,CAAC;AAChE,YAAM,cAAc,OAAO,MAAM,GAAG,EAAE,IAAI,OAAK,SAAS,GAAG,EAAE,CAAC;AAC9D,aAAO,MAAM,2CAA2C,aAAa,KAAK,IAAI,CAAC,cAAc,YAAY,KAAK,IAAI,CAAC,GAAG;AAEtH,eAAS,IAAI,GAAG,IAAI,KAAK,IAAI,aAAa,QAAQ,YAAY,MAAM,GAAG,KAAK;AAC1E,cAAM,OAAO,aAAa,CAAC,KAAK;AAChC,cAAM,OAAO,YAAY,CAAC,KAAK;AAE/B,eAAO,MAAM,kCAAkC,CAAC,aAAa,IAAI,YAAY,IAAI,EAAE;AAEnF,YAAI,OAAO,MAAM;AACf,iBAAO,MAAM,oCAAoC,IAAI,MAAM,IAAI,GAAG;AAClE,iBAAO;AAAA,QACT;AACA,YAAI,OAAO,MAAM;AACf,iBAAO,MAAM,qCAAqC,IAAI,MAAM,IAAI,GAAG;AACnE,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,aAAO,MAAM,oCAAoC;AACjD,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,aAAO,MAAM,6CAA6C,KAAK,EAAE;AACjE,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAMA,eAAsB,qBACpB,gBACA,aACA,eACe;AACf,SAAO,MAAM,6CAA6C,cAAc,aAAa,WAAW,mBAAmB,aAAa,EAAE;AAClI,MAAI;AAEF,QAAI,kBAAkB,UAAU;AAC9B,aAAO,MAAM,mFAAmF,aAAa,GAAG;AAChH;AAAA,IACF;AAEA,WAAO,MAAM,wDAAwD;AACrE,UAAM,WAAW,IAAI,eAAe,gBAAgB,WAAW;AAE/D,WAAO,MAAM,iDAAiD;AAC9D,UAAM,SAAS,MAAM,SAAS,gBAAgB;AAE9C,QAAI,WAAW,MAAM;AACnB,aAAO,MAAM,qEAAqE;AAClF,eAAS,0BAA0B,MAAM;AAAA,IAC3C,OAAO;AACL,aAAO,MAAM,oEAAoE;AAAA,IACnF;AAEA,WAAO,MAAM,iCAAiC;AAAA,EAChD,SAAS,OAAO;AAEd,WAAO,MAAM,2CAA2C,KAAK,EAAE;AAAA,EACjE;AACF;","names":["updateAvailable"]}
@@ -1,15 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getRepoInfo
4
- } from "./chunk-KQDEK2ZW.js";
4
+ } from "./chunk-DGEKUT7Q.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-GEHQXLEI.js";
8
8
 
9
9
  // src/utils/mcp.ts
10
10
  import path from "path";
11
- async function generateGitHubCommentMcpConfig(contextType) {
12
- const repoInfo = await getRepoInfo();
11
+ async function generateGitHubCommentMcpConfig(contextType, repo) {
12
+ let owner;
13
+ let name;
14
+ if (repo) {
15
+ const parts = repo.split("/");
16
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
17
+ throw new Error(`Invalid repo format: ${repo}. Expected "owner/repo"`);
18
+ }
19
+ owner = parts[0];
20
+ name = parts[1];
21
+ } else {
22
+ const repoInfo = await getRepoInfo();
23
+ owner = repoInfo.owner;
24
+ name = repoInfo.name;
25
+ }
13
26
  const githubEventName = contextType === "issue" ? "issues" : contextType === "pr" ? "pull_request" : void 0;
14
27
  const mcpServerConfig = {
15
28
  mcpServers: {
@@ -18,8 +31,8 @@ async function generateGitHubCommentMcpConfig(contextType) {
18
31
  command: "node",
19
32
  args: [path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), "../dist/mcp/github-comment-server.js")],
20
33
  env: {
21
- REPO_OWNER: repoInfo.owner,
22
- REPO_NAME: repoInfo.name,
34
+ REPO_OWNER: owner,
35
+ REPO_NAME: name,
23
36
  GITHUB_API_URL: "https://api.github.com/",
24
37
  ...githubEventName && { GITHUB_EVENT_NAME: githubEventName }
25
38
  }
@@ -27,8 +40,8 @@ async function generateGitHubCommentMcpConfig(contextType) {
27
40
  }
28
41
  };
29
42
  logger.debug("Generated MCP config for GitHub comment broker", {
30
- repoOwner: repoInfo.owner,
31
- repoName: repoInfo.name,
43
+ repoOwner: owner,
44
+ repoName: name,
32
45
  contextType: contextType ?? "auto-detect",
33
46
  githubEventName: githubEventName ?? "auto-detect"
34
47
  });
@@ -38,4 +51,4 @@ async function generateGitHubCommentMcpConfig(contextType) {
38
51
  export {
39
52
  generateGitHubCommentMcpConfig
40
53
  };
41
- //# sourceMappingURL=chunk-SSR5AVRJ.js.map
54
+ //# sourceMappingURL=chunk-6OTVPRXH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/mcp.ts"],"sourcesContent":["import path from 'path'\nimport { getRepoInfo } from './github.js'\nimport { logger } from './logger.js'\n\n/**\n * Generate MCP configuration for GitHub comment broker\n * Uses a single server that can handle both issues and pull requests\n * Returns array of MCP server config objects\n * @param contextType - Optional context type (issue or pr)\n * @param repo - Optional repo in \"owner/repo\" format. If not provided, will auto-detect from git.\n */\nexport async function generateGitHubCommentMcpConfig(\n\tcontextType?: 'issue' | 'pr',\n\trepo?: string\n): Promise<Record<string, unknown>[]> {\n\t// Get repository information - either from provided repo string or auto-detect\n\tlet owner: string\n\tlet name: string\n\n\tif (repo) {\n\t\tconst parts = repo.split('/')\n\t\tif (parts.length !== 2 || !parts[0] || !parts[1]) {\n\t\t\tthrow new Error(`Invalid repo format: ${repo}. Expected \"owner/repo\"`)\n\t\t}\n\t\towner = parts[0]\n\t\tname = parts[1]\n\t} else {\n\t\tconst repoInfo = await getRepoInfo()\n\t\towner = repoInfo.owner\n\t\tname = repoInfo.name\n\t}\n\n\t// Map logical types to GitHub's webhook event names (handle GitHub's naming quirk here)\n\tconst githubEventName = contextType === 'issue' ? 'issues' : contextType === 'pr' ? 'pull_request' : undefined\n\n\t// Generate single MCP server config\n\tconst mcpServerConfig = {\n\t\tmcpServers: {\n\t\t\tgithub_comment: {\n\t\t\t\ttransport: 'stdio',\n\t\t\t\tcommand: 'node',\n\t\t\t\targs: [path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), '../dist/mcp/github-comment-server.js')],\n\t\t\t\tenv: {\n\t\t\t\t\tREPO_OWNER: owner,\n\t\t\t\t\tREPO_NAME: name,\n\t\t\t\t\tGITHUB_API_URL: 'https://api.github.com/',\n\t\t\t\t\t...(githubEventName && { GITHUB_EVENT_NAME: githubEventName }),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tlogger.debug('Generated MCP config for GitHub comment broker', {\n\t\trepoOwner: owner,\n\t\trepoName: name,\n\t\tcontextType: contextType ?? 'auto-detect',\n\t\tgithubEventName: githubEventName ?? 'auto-detect'\n\t})\n\n\treturn [mcpServerConfig]\n}"],"mappings":";;;;;;;;;AAAA,OAAO,UAAU;AAWjB,eAAsB,+BACrB,aACA,MACqC;AAErC,MAAI;AACJ,MAAI;AAEJ,MAAI,MAAM;AACT,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAI,MAAM,WAAW,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;AACjD,YAAM,IAAI,MAAM,wBAAwB,IAAI,yBAAyB;AAAA,IACtE;AACA,YAAQ,MAAM,CAAC;AACf,WAAO,MAAM,CAAC;AAAA,EACf,OAAO;AACN,UAAM,WAAW,MAAM,YAAY;AACnC,YAAQ,SAAS;AACjB,WAAO,SAAS;AAAA,EACjB;AAGA,QAAM,kBAAkB,gBAAgB,UAAU,WAAW,gBAAgB,OAAO,iBAAiB;AAGrG,QAAM,kBAAkB;AAAA,IACvB,YAAY;AAAA,MACX,gBAAgB;AAAA,QACf,WAAW;AAAA,QACX,SAAS;AAAA,QACT,MAAM,CAAC,KAAK,KAAK,KAAK,QAAQ,IAAI,WAAW,IAAI,YAAY,GAAG,EAAE,QAAQ,GAAG,sCAAsC,CAAC;AAAA,QACpH,KAAK;AAAA,UACJ,YAAY;AAAA,UACZ,WAAW;AAAA,UACX,gBAAgB;AAAA,UAChB,GAAI,mBAAmB,EAAE,mBAAmB,gBAAgB;AAAA,QAC7D;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAEA,SAAO,MAAM,kDAAkD;AAAA,IAC9D,WAAW;AAAA,IACX,UAAU;AAAA,IACV,aAAa,eAAe;AAAA,IAC5B,iBAAiB,mBAAmB;AAAA,EACrC,CAAC;AAED,SAAO,CAAC,eAAe;AACxB;","names":[]}
@@ -52,15 +52,19 @@ async function fetchGhIssue(issueNumber, repo) {
52
52
  }
53
53
  return executeGhCommand(args);
54
54
  }
55
- async function fetchGhPR(prNumber) {
56
- logger.debug("Fetching GitHub PR", { prNumber });
57
- return executeGhCommand([
55
+ async function fetchGhPR(prNumber, repo) {
56
+ logger.debug("Fetching GitHub PR", { prNumber, repo });
57
+ const args = [
58
58
  "pr",
59
59
  "view",
60
60
  String(prNumber),
61
61
  "--json",
62
62
  "number,title,body,state,headRefName,baseRefName,url,isDraft,mergeable,createdAt,updatedAt"
63
- ]);
63
+ ];
64
+ if (repo) {
65
+ args.push("--repo", repo);
66
+ }
67
+ return executeGhCommand(args);
64
68
  }
65
69
  async function fetchProjectList(owner) {
66
70
  const result = await executeGhCommand([
@@ -196,4 +200,4 @@ export {
196
200
  createIssue,
197
201
  getRepoInfo
198
202
  };
199
- //# sourceMappingURL=chunk-KQDEK2ZW.js.map
203
+ //# sourceMappingURL=chunk-DGEKUT7Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/github.ts"],"sourcesContent":["import { execa } from 'execa'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tGitHubAuthStatus,\n\tBranchNameStrategy,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { logger } from './logger.js'\n\n// Core GitHub CLI execution wrapper\nexport async function executeGhCommand<T = unknown>(\n\targs: string[],\n\toptions?: { cwd?: string; timeout?: number }\n): Promise<T> {\n\tconst result = await execa('gh', args, {\n\t\tcwd: options?.cwd ?? process.cwd(),\n\t\ttimeout: options?.timeout ?? 30000,\n\t\tencoding: 'utf8',\n\t})\n\n\t// Parse JSON output if --json flag, --format json, or --jq was used\n\tconst isJson =\n\t\targs.includes('--json') ||\n\t\targs.includes('--jq') ||\n\t\t(args.includes('--format') && args[args.indexOf('--format') + 1] === 'json')\n\tconst data = isJson ? JSON.parse(result.stdout) : result.stdout\n\n\treturn data as T\n}\n\n// Authentication checking\nexport async function checkGhAuth(): Promise<GitHubAuthStatus> {\n\ttry {\n\t\tconst output = await executeGhCommand<string>(['auth', 'status'])\n\n\t\t// Parse auth status output\n\t\tconst scopeMatch = output.match(/Token scopes: (.+)/)\n\t\tconst userMatch = output.match(/Logged in to github\\.com as ([^\\s]+)/)\n\n\t\tconst username = userMatch?.[1]\n\n\t\treturn {\n\t\t\thasAuth: true,\n\t\t\tscopes: scopeMatch?.[1]?.split(', ').map(scope => scope.replace(/^'|'$/g, '')) ?? [],\n\t\t\t...(username && { username }),\n\t\t}\n\t} catch (error) {\n\t\t// Only return \"no auth\" for specific authentication errors\n\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('You are not logged into any GitHub hosts')) {\n\t\t\treturn { hasAuth: false, scopes: [] }\n\t\t}\n\t\t// Re-throw unexpected errors\n\t\tthrow error\n\t}\n}\n\nexport async function hasProjectScope(): Promise<boolean> {\n\tconst auth = await checkGhAuth()\n\treturn auth.scopes.includes('project')\n}\n\n// Issue fetching\nexport async function fetchGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<GitHubIssue> {\n\tlogger.debug('Fetching GitHub issue', { issueNumber, repo })\n\n\tconst args = [\n\t\t'issue',\n\t\t'view',\n\t\tString(issueNumber),\n\t\t'--json',\n\t\t'number,title,body,state,labels,assignees,url,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubIssue>(args)\n}\n\n// PR fetching\nexport async function fetchGhPR(\n\tprNumber: number,\n\trepo?: string\n): Promise<GitHubPullRequest> {\n\tlogger.debug('Fetching GitHub PR', { prNumber, repo })\n\n\tconst args = [\n\t\t'pr',\n\t\t'view',\n\t\tString(prNumber),\n\t\t'--json',\n\t\t'number,title,body,state,headRefName,baseRefName,url,isDraft,mergeable,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubPullRequest>(args)\n}\n\n// Project operations\nexport async function fetchProjectList(\n\towner: string\n): Promise<GitHubProject[]> {\n\tconst result = await executeGhCommand<{ projects: GitHubProject[] }>([\n\t\t'project',\n\t\t'list',\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'100',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.projects ?? []\n}\n\nexport async function fetchProjectItems(\n\tprojectNumber: number,\n\towner: string\n): Promise<ProjectItem[]> {\n\tconst result = await executeGhCommand<{ items: ProjectItem[] }>([\n\t\t'project',\n\t\t'item-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'10000',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.items ?? []\n}\n\nexport async function fetchProjectFields(\n\tprojectNumber: number,\n\towner: string\n): Promise<{ fields: ProjectField[] }> {\n\tconst result = await executeGhCommand<{ fields: ProjectField[] }>([\n\t\t'project',\n\t\t'field-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result ?? { fields: [] }\n}\n\nexport async function updateProjectItemField(\n\titemId: string,\n\tprojectId: string,\n\tfieldId: string,\n\toptionId: string\n): Promise<void> {\n\tawait executeGhCommand([\n\t\t'project',\n\t\t'item-edit',\n\t\t'--id',\n\t\titemId,\n\t\t'--project-id',\n\t\tprojectId,\n\t\t'--field-id',\n\t\tfieldId,\n\t\t'--single-select-option-id',\n\t\toptionId,\n\t\t'--format',\n\t\t'json',\n\t])\n}\n\n// Branch name generation strategies\nexport class SimpleBranchNameStrategy implements BranchNameStrategy {\n\tasync generate(issueNumber: number, title: string): Promise<string> {\n\t\t// Create a simple slug from the title\n\t\tconst slug = title\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t.replace(/^-|-$/g, '')\n\t\t\t.substring(0, 20) // Keep it short for the simple strategy\n\n\t\treturn `feat/issue-${issueNumber}-${slug}`\n\t}\n}\n\nexport class ClaudeBranchNameStrategy implements BranchNameStrategy {\n\tconstructor(private claudeModel = 'haiku') {}\n\n\tasync generate(issueNumber: number, title: string): Promise<string> {\n\t\t// Import dynamically to avoid circular dependency\n\t\tconst { generateBranchName } = await import('../utils/claude.js')\n\n\t\t// Delegate to the shared implementation\n\t\treturn generateBranchName(title, issueNumber, this.claudeModel)\n\t}\n}\n\n// Template-based strategy for custom patterns\nexport class TemplateBranchNameStrategy implements BranchNameStrategy {\n\tconstructor(private template = '{prefix}/issue-{number}-{slug}') {}\n\n\tasync generate(issueNumber: number, title: string): Promise<string> {\n\t\t// Determine prefix based on title\n\t\tconst prefix = this.determinePrefix(title)\n\n\t\t// Create slug from title\n\t\tconst slug = title\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t.replace(/^-|-$/g, '')\n\t\t\t.substring(0, 30)\n\n\t\treturn this.template\n\t\t\t.replace('{prefix}', prefix)\n\t\t\t.replace('{number}', String(issueNumber))\n\t\t\t.replace('{slug}', slug)\n\t}\n\n\tprivate determinePrefix(title: string): string {\n\t\tconst lowerTitle = title.toLowerCase()\n\t\tif (lowerTitle.includes('fix') || lowerTitle.includes('bug')) return 'fix'\n\t\tif (lowerTitle.includes('doc')) return 'docs'\n\t\tif (lowerTitle.includes('test')) return 'test'\n\t\tif (lowerTitle.includes('refactor')) return 'refactor'\n\t\treturn 'feat'\n\t}\n}\n\n// GitHub Issue Operations\n\ninterface IssueCreateResponse {\n\tnumber: number\n\turl: string\n}\n\n/**\n * Create a new GitHub issue\n * @param title - The issue title\n * @param body - The issue body (markdown supported)\n * @param options - Optional configuration\n * @param options.repo - Repository in format \"owner/repo\" (uses current repo if not provided)\n * @param options.labels - Array of label names to add to the issue\n * @returns Issue metadata including number and URL\n */\nexport async function createIssue(\n\ttitle: string,\n\tbody: string,\n\toptions?: { repo?: string | undefined; labels?: string[] | undefined }\n): Promise<IssueCreateResponse> {\n\tconst { repo, labels } = options ?? {}\n\n\tlogger.debug('Creating GitHub issue', { title, repo, labels })\n\n\tconst args = [\n\t\t'issue',\n\t\t'create',\n\t\t'--title',\n\t\ttitle,\n\t\t'--body',\n\t\tbody,\n\t]\n\n\t// Add repo if provided\n\tif (repo) {\n\t\targs.splice(2, 0, '--repo', repo)\n\t}\n\n\t// Add labels if provided\n\tif (labels && labels.length > 0) {\n\t\targs.push('--label', labels.join(','))\n\t}\n\n\tconst execaOptions: { timeout: number; encoding: 'utf8'; cwd?: string } = {\n\t\ttimeout: 30000,\n\t\tencoding: 'utf8',\n\t}\n\n\tif (!repo) {\n\t\texecaOptions.cwd = process.cwd()\n\t}\n\n\tconst result = await execa('gh', args, execaOptions)\n\n\t// Parse the URL from the output (format: \"https://github.com/owner/repo/issues/123\")\n\tconst urlMatch = result.stdout.trim().match(/https:\\/\\/github\\.com\\/[^/]+\\/[^/]+\\/issues\\/(\\d+)/)\n\tif (!urlMatch?.[1]) {\n\t\tthrow new Error(`Failed to parse issue URL from gh output: ${result.stdout}`)\n\t}\n\n\tconst issueNumber = parseInt(urlMatch[1], 10)\n\tconst issueUrl = urlMatch[0]\n\n\treturn {\n\t\tnumber: issueNumber,\n\t\turl: issueUrl,\n\t}\n}\n\n/**\n * @deprecated Use createIssue with options.repo instead\n * Create a new GitHub issue in a specific repository\n * @param title - Issue title\n * @param body - Issue body (markdown)\n * @param repository - Repository in format \"owner/repo\"\n * @param labels - Optional array of label names to add to the issue\n * @returns Issue number and URL\n */\nexport async function createIssueInRepo(\n\ttitle: string,\n\tbody: string,\n\trepository: string,\n\tlabels?: string[]\n): Promise<IssueCreateResponse> {\n\treturn createIssue(title, body, { repo: repository, labels })\n}\n\n// GitHub Comment Operations\n\ninterface CommentResponse {\n\tid: number\n\turl: string\n\tcreated_at?: string\n\tupdated_at?: string\n}\n\ninterface RepoInfo {\n\towner: string\n\tname: string\n}\n\n/**\n * Create a comment on a GitHub issue\n * @param issueNumber - The issue number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createIssueComment(\n\tissueNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating issue comment', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/comments`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Update an existing GitHub comment\n * @param commentId - The comment ID\n * @param body - The updated comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Updated comment metadata\n */\nexport async function updateIssueComment(\n\tcommentId: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Updating issue comment', { commentId, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/comments/${commentId}`\n\t\t: `repos/:owner/:repo/issues/comments/${commentId}`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-X',\n\t\t'PATCH',\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, updated_at: .updated_at}',\n\t])\n}\n\n/**\n * Create a comment on a GitHub pull request\n * Note: PR comments use the same endpoint as issue comments\n * @param prNumber - The PR number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createPRComment(\n\tprNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating PR comment', { prNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${prNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${prNumber}/comments`\n\n\t// PR comments use the issues endpoint\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Get repository owner and name from current directory\n * @returns Repository owner and name\n */\nexport async function getRepoInfo(): Promise<RepoInfo> {\n\tlogger.debug('Fetching repository info')\n\n\tconst result = await executeGhCommand<{ owner: { login: string }; name: string }>([\n\t\t'repo',\n\t\t'view',\n\t\t'--json',\n\t\t'owner,name',\n\t])\n\n\treturn {\n\t\towner: result.owner.login,\n\t\tname: result.name,\n\t}\n}"],"mappings":"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","names":[]}
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/remote.ts
4
+ import { execa } from "execa";
5
+ async function parseGitRemotes(cwd) {
6
+ const result = await execa("git", ["remote", "-v"], {
7
+ cwd: cwd ?? process.cwd(),
8
+ encoding: "utf8"
9
+ });
10
+ const lines = result.stdout.trim().split("\n");
11
+ const remoteMap = /* @__PURE__ */ new Map();
12
+ for (const line of lines) {
13
+ const match = line.match(/^(\S+)\s+(\S+)\s+\((fetch|push)\)/);
14
+ if (!match) continue;
15
+ const name = match[1];
16
+ const url = match[2];
17
+ if (!name || !url) continue;
18
+ if (remoteMap.has(name)) continue;
19
+ const ownerRepo = extractOwnerRepoFromUrl(url);
20
+ if (!ownerRepo) continue;
21
+ remoteMap.set(name, {
22
+ name,
23
+ url,
24
+ owner: ownerRepo.owner,
25
+ repo: ownerRepo.repo
26
+ });
27
+ }
28
+ return Array.from(remoteMap.values());
29
+ }
30
+ function extractOwnerRepoFromUrl(url) {
31
+ const cleanUrl = url.replace(/\.git$/, "");
32
+ const httpsMatch = cleanUrl.match(/https?:\/\/github\.com\/([^/]+)\/([^/]+)/);
33
+ if ((httpsMatch == null ? void 0 : httpsMatch[1]) && (httpsMatch == null ? void 0 : httpsMatch[2])) {
34
+ return { owner: httpsMatch[1], repo: httpsMatch[2] };
35
+ }
36
+ const sshMatch = cleanUrl.match(/git@github\.com:([^/]+)\/(.+)/);
37
+ if ((sshMatch == null ? void 0 : sshMatch[1]) && (sshMatch == null ? void 0 : sshMatch[2])) {
38
+ return { owner: sshMatch[1], repo: sshMatch[2] };
39
+ }
40
+ return null;
41
+ }
42
+ async function hasMultipleRemotes(cwd) {
43
+ const remotes = await parseGitRemotes(cwd);
44
+ return remotes.length > 1;
45
+ }
46
+ async function getConfiguredRepoFromSettings(settings, cwd) {
47
+ var _a, _b;
48
+ const remoteName = (_b = (_a = settings.issueManagement) == null ? void 0 : _a.github) == null ? void 0 : _b.remote;
49
+ if (!remoteName) {
50
+ throw new Error(
51
+ 'GitHub remote not configured. Run "il init" to configure which repository to use for GitHub operations.'
52
+ );
53
+ }
54
+ await validateConfiguredRemote(remoteName, cwd);
55
+ const remotes = await parseGitRemotes(cwd);
56
+ const remote = remotes.find((r) => r.name === remoteName);
57
+ if (!remote) {
58
+ throw new Error(
59
+ `Configured remote "${remoteName}" not found in git remotes. Run "il init" to reconfigure.`
60
+ );
61
+ }
62
+ return `${remote.owner}/${remote.repo}`;
63
+ }
64
+ async function validateConfiguredRemote(remoteName, cwd) {
65
+ try {
66
+ await execa("git", ["remote", "get-url", remoteName], {
67
+ cwd: cwd ?? process.cwd(),
68
+ encoding: "utf8"
69
+ });
70
+ } catch {
71
+ throw new Error(
72
+ `Remote "${remoteName}" does not exist in git configuration. Run "il init" to reconfigure.`
73
+ );
74
+ }
75
+ }
76
+
77
+ export {
78
+ parseGitRemotes,
79
+ hasMultipleRemotes,
80
+ getConfiguredRepoFromSettings,
81
+ validateConfiguredRemote
82
+ };
83
+ //# sourceMappingURL=chunk-FXV24OYZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/remote.ts"],"sourcesContent":["import { execa } from 'execa'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\n\n/**\n * Represents a parsed git remote\n */\nexport interface GitRemote {\n\tname: string\n\turl: string\n\towner: string\n\trepo: string\n}\n\n/**\n * Parse git remotes from `git remote -v` output\n * Deduplicates fetch/push entries and extracts owner/repo from URLs\n */\nexport async function parseGitRemotes(cwd?: string): Promise<GitRemote[]> {\n\tconst result = await execa('git', ['remote', '-v'], {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tencoding: 'utf8',\n\t})\n\n\tconst lines = result.stdout.trim().split('\\n')\n\tconst remoteMap = new Map<string, GitRemote>()\n\n\tfor (const line of lines) {\n\t\t// Format: \"origin git@github.com:owner/repo.git (fetch)\"\n\t\t// Format: \"origin https://github.com/owner/repo.git (fetch)\"\n\t\tconst match = line.match(/^(\\S+)\\s+(\\S+)\\s+\\((fetch|push)\\)/)\n\t\tif (!match) continue\n\n\t\tconst name = match[1]\n\t\tconst url = match[2]\n\t\tif (!name || !url) continue\n\n\t\t// Skip if we already processed this remote\n\t\tif (remoteMap.has(name)) continue\n\n\t\t// Extract owner/repo from URL\n\t\tconst ownerRepo = extractOwnerRepoFromUrl(url)\n\t\tif (!ownerRepo) continue\n\n\t\tremoteMap.set(name, {\n\t\t\tname,\n\t\t\turl,\n\t\t\towner: ownerRepo.owner,\n\t\t\trepo: ownerRepo.repo,\n\t\t})\n\t}\n\n\treturn Array.from(remoteMap.values())\n}\n\n/**\n * Extract owner and repo from GitHub URL\n * Supports both HTTPS and SSH formats\n */\nfunction extractOwnerRepoFromUrl(url: string): { owner: string; repo: string } | null {\n\t// Remove .git suffix if present\n\tconst cleanUrl = url.replace(/\\.git$/, '')\n\n\t// HTTPS format: https://github.com/owner/repo\n\tconst httpsMatch = cleanUrl.match(/https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+)/)\n\tif (httpsMatch?.[1] && httpsMatch?.[2]) {\n\t\treturn { owner: httpsMatch[1], repo: httpsMatch[2] }\n\t}\n\n\t// SSH format: git@github.com:owner/repo\n\tconst sshMatch = cleanUrl.match(/git@github\\.com:([^/]+)\\/(.+)/)\n\tif (sshMatch?.[1] && sshMatch?.[2]) {\n\t\treturn { owner: sshMatch[1], repo: sshMatch[2] }\n\t}\n\n\treturn null\n}\n\n/**\n * Check if repository has multiple remotes\n */\nexport async function hasMultipleRemotes(cwd?: string): Promise<boolean> {\n\tconst remotes = await parseGitRemotes(cwd)\n\treturn remotes.length > 1\n}\n\n/**\n * Get configured repository string from settings\n * Returns \"owner/repo\" format for use with gh CLI --repo flag\n * Throws if configured remote not found\n */\nexport async function getConfiguredRepoFromSettings(\n\tsettings: IloomSettings,\n\tcwd?: string,\n): Promise<string> {\n\tconst remoteName = settings.issueManagement?.github?.remote\n\n\tif (!remoteName) {\n\t\tthrow new Error(\n\t\t\t'GitHub remote not configured. Run \"il init\" to configure which repository to use for GitHub operations.',\n\t\t)\n\t}\n\n\t// Validate configured remote exists\n\tawait validateConfiguredRemote(remoteName, cwd)\n\n\t// Parse remotes and find the configured one\n\tconst remotes = await parseGitRemotes(cwd)\n\tconst remote = remotes.find((r) => r.name === remoteName)\n\n\tif (!remote) {\n\t\tthrow new Error(\n\t\t\t`Configured remote \"${remoteName}\" not found in git remotes. Run \"il init\" to reconfigure.`,\n\t\t)\n\t}\n\n\treturn `${remote.owner}/${remote.repo}`\n}\n\n/**\n * Validate that a remote exists in git config\n * Throws if remote doesn't exist\n */\nexport async function validateConfiguredRemote(remoteName: string, cwd?: string): Promise<void> {\n\ttry {\n\t\tawait execa('git', ['remote', 'get-url', remoteName], {\n\t\t\tcwd: cwd ?? process.cwd(),\n\t\t\tencoding: 'utf8',\n\t\t})\n\t} catch {\n\t\tthrow new Error(\n\t\t\t`Remote \"${remoteName}\" does not exist in git configuration. Run \"il init\" to reconfigure.`,\n\t\t)\n\t}\n}\n"],"mappings":";;;AAAA,SAAS,aAAa;AAiBtB,eAAsB,gBAAgB,KAAoC;AACzE,QAAM,SAAS,MAAM,MAAM,OAAO,CAAC,UAAU,IAAI,GAAG;AAAA,IACnD,KAAK,OAAO,QAAQ,IAAI;AAAA,IACxB,UAAU;AAAA,EACX,CAAC;AAED,QAAM,QAAQ,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI;AAC7C,QAAM,YAAY,oBAAI,IAAuB;AAE7C,aAAW,QAAQ,OAAO;AAGzB,UAAM,QAAQ,KAAK,MAAM,mCAAmC;AAC5D,QAAI,CAAC,MAAO;AAEZ,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,MAAM,MAAM,CAAC;AACnB,QAAI,CAAC,QAAQ,CAAC,IAAK;AAGnB,QAAI,UAAU,IAAI,IAAI,EAAG;AAGzB,UAAM,YAAY,wBAAwB,GAAG;AAC7C,QAAI,CAAC,UAAW;AAEhB,cAAU,IAAI,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,OAAO,UAAU;AAAA,MACjB,MAAM,UAAU;AAAA,IACjB,CAAC;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,UAAU,OAAO,CAAC;AACrC;AAMA,SAAS,wBAAwB,KAAqD;AAErF,QAAM,WAAW,IAAI,QAAQ,UAAU,EAAE;AAGzC,QAAM,aAAa,SAAS,MAAM,0CAA0C;AAC5E,OAAI,yCAAa,QAAM,yCAAa,KAAI;AACvC,WAAO,EAAE,OAAO,WAAW,CAAC,GAAG,MAAM,WAAW,CAAC,EAAE;AAAA,EACpD;AAGA,QAAM,WAAW,SAAS,MAAM,+BAA+B;AAC/D,OAAI,qCAAW,QAAM,qCAAW,KAAI;AACnC,WAAO,EAAE,OAAO,SAAS,CAAC,GAAG,MAAM,SAAS,CAAC,EAAE;AAAA,EAChD;AAEA,SAAO;AACR;AAKA,eAAsB,mBAAmB,KAAgC;AACxE,QAAM,UAAU,MAAM,gBAAgB,GAAG;AACzC,SAAO,QAAQ,SAAS;AACzB;AAOA,eAAsB,8BACrB,UACA,KACkB;AA7FnB;AA8FC,QAAM,cAAa,oBAAS,oBAAT,mBAA0B,WAA1B,mBAAkC;AAErD,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AAGA,QAAM,yBAAyB,YAAY,GAAG;AAG9C,QAAM,UAAU,MAAM,gBAAgB,GAAG;AACzC,QAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AAExD,MAAI,CAAC,QAAQ;AACZ,UAAM,IAAI;AAAA,MACT,sBAAsB,UAAU;AAAA,IACjC;AAAA,EACD;AAEA,SAAO,GAAG,OAAO,KAAK,IAAI,OAAO,IAAI;AACtC;AAMA,eAAsB,yBAAyB,YAAoB,KAA6B;AAC/F,MAAI;AACH,UAAM,MAAM,OAAO,CAAC,UAAU,WAAW,UAAU,GAAG;AAAA,MACrD,KAAK,OAAO,QAAQ,IAAI;AAAA,MACxB,UAAU;AAAA,IACX,CAAC;AAAA,EACF,QAAQ;AACP,UAAM,IAAI;AAAA,MACT,WAAW,UAAU;AAAA,IACtB;AAAA,EACD;AACD;","names":[]}
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- calculatePortForBranch
4
- } from "./chunk-37DYYFVK.js";
3
+ calculatePortForBranch,
4
+ formatEnvLine,
5
+ parseEnvFile,
6
+ validateEnvVariable
7
+ } from "./chunk-ZMNQBJUI.js";
5
8
  import {
6
9
  runScript
7
10
  } from "./chunk-BLCTGFZN.js";
@@ -9,11 +12,6 @@ import {
9
12
  hasScript,
10
13
  readPackageJson
11
14
  } from "./chunk-2ZPFJQ3B.js";
12
- import {
13
- formatEnvLine,
14
- parseEnvFile,
15
- validateEnvVariable
16
- } from "./chunk-SJUQ2NDR.js";
17
15
  import {
18
16
  createLogger,
19
17
  logger
@@ -641,4 +639,4 @@ export {
641
639
  CLIIsolationManager,
642
640
  DatabaseManager
643
641
  };
644
- //# sourceMappingURL=chunk-HPJJSYNS.js.map
642
+ //# sourceMappingURL=chunk-H5LDRGVK.js.map