@iloom/cli 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +58 -16
  3. package/dist/{BranchNamingService-B5PVRR7F.js → BranchNamingService-FLPUUFOB.js} +2 -2
  4. package/dist/ClaudeContextManager-KE5TBZVZ.js +14 -0
  5. package/dist/ClaudeService-CRSETT3A.js +13 -0
  6. package/dist/{GitHubService-S2OGUTDR.js → GitHubService-O7U4UQ7N.js} +3 -3
  7. package/dist/{LoomLauncher-5LFM4LXB.js → LoomLauncher-NL65LSKP.js} +6 -6
  8. package/dist/{MetadataManager-DFI73J3G.js → MetadataManager-XJ2YB762.js} +2 -2
  9. package/dist/PRManager-2ABCWXHW.js +16 -0
  10. package/dist/{ProjectCapabilityDetector-S5FLNCFI.js → ProjectCapabilityDetector-UZYW32SY.js} +3 -3
  11. package/dist/{PromptTemplateManager-C3DK6XZL.js → PromptTemplateManager-7L3HJQQU.js} +2 -2
  12. package/dist/README.md +58 -16
  13. package/dist/{SettingsManager-35F5RUJH.js → SettingsManager-YU4VYPTW.js} +2 -2
  14. package/dist/agents/iloom-issue-analyze-and-plan.md +42 -17
  15. package/dist/agents/iloom-issue-analyzer.md +14 -14
  16. package/dist/agents/iloom-issue-complexity-evaluator.md +38 -15
  17. package/dist/agents/iloom-issue-enhancer.md +15 -15
  18. package/dist/agents/iloom-issue-implementer.md +44 -15
  19. package/dist/agents/iloom-issue-planner.md +121 -17
  20. package/dist/agents/iloom-issue-reviewer.md +15 -15
  21. package/dist/{build-FJVYP7EV.js → build-O2EJHDEW.js} +9 -9
  22. package/dist/{chunk-ZPSTA5PR.js → chunk-3CDWFEGL.js} +2 -2
  23. package/dist/{chunk-VU3QMIP2.js → chunk-453NC377.js} +91 -15
  24. package/dist/chunk-453NC377.js.map +1 -0
  25. package/dist/{chunk-UQIXZ3BA.js → chunk-5V74K5ZA.js} +2 -2
  26. package/dist/{chunk-7WANFUIK.js → chunk-6TL3BYH6.js} +2 -2
  27. package/dist/{chunk-5TXLVEXT.js → chunk-C3AKFAIR.js} +2 -2
  28. package/dist/{chunk-K7SEEHKO.js → chunk-CNSTXBJ3.js} +7 -419
  29. package/dist/chunk-CNSTXBJ3.js.map +1 -0
  30. package/dist/{chunk-5IWU3HXE.js → chunk-EPPPDVHD.js} +23 -11
  31. package/dist/chunk-EPPPDVHD.js.map +1 -0
  32. package/dist/{chunk-UB4TFAXJ.js → chunk-FEAJR6PN.js} +9 -55
  33. package/dist/chunk-FEAJR6PN.js.map +1 -0
  34. package/dist/{chunk-6YSFTPKW.js → chunk-FM4KBPVA.js} +18 -13
  35. package/dist/chunk-FM4KBPVA.js.map +1 -0
  36. package/dist/{chunk-AEIMYF4P.js → chunk-FP7G7DG3.js} +6 -2
  37. package/dist/chunk-FP7G7DG3.js.map +1 -0
  38. package/dist/{chunk-LT3SGBR7.js → chunk-GCPAZSGV.js} +36 -2
  39. package/dist/{chunk-LT3SGBR7.js.map → chunk-GCPAZSGV.js.map} +1 -1
  40. package/dist/chunk-GJMEKEI5.js +517 -0
  41. package/dist/chunk-GJMEKEI5.js.map +1 -0
  42. package/dist/{chunk-64O2UIWO.js → chunk-GV5X6XUE.js} +4 -4
  43. package/dist/{chunk-7Q66W4OH.js → chunk-HBJITKSZ.js} +37 -1
  44. package/dist/chunk-HBJITKSZ.js.map +1 -0
  45. package/dist/{chunk-7HIRPCKU.js → chunk-HVQNVRAF.js} +2 -2
  46. package/dist/{chunk-BXCPJJYM.js → chunk-ITN64ENQ.js} +1 -1
  47. package/dist/chunk-ITN64ENQ.js.map +1 -0
  48. package/dist/{chunk-6U6VI4SZ.js → chunk-KVS4XGBQ.js} +4 -4
  49. package/dist/{chunk-AXX3QIKK.js → chunk-LLWX3PCW.js} +2 -2
  50. package/dist/{chunk-WIJWIKAN.js → chunk-LQBLDI47.js} +105 -7
  51. package/dist/chunk-LQBLDI47.js.map +1 -0
  52. package/dist/{chunk-SN3Z6EZO.js → chunk-N7FVXZNI.js} +2 -2
  53. package/dist/chunk-NTIZLX42.js +822 -0
  54. package/dist/chunk-NTIZLX42.js.map +1 -0
  55. package/dist/{chunk-PMVWQBWS.js → chunk-S7YMZQUD.js} +31 -45
  56. package/dist/chunk-S7YMZQUD.js.map +1 -0
  57. package/dist/chunk-TIYJEEVO.js +79 -0
  58. package/dist/chunk-TIYJEEVO.js.map +1 -0
  59. package/dist/{chunk-EK3XCAAS.js → chunk-UDRZY65Y.js} +2 -2
  60. package/dist/{chunk-3PT7RKL5.js → chunk-USJSNHGG.js} +2 -2
  61. package/dist/{chunk-CFUWQHCJ.js → chunk-VWGKGNJP.js} +114 -35
  62. package/dist/chunk-VWGKGNJP.js.map +1 -0
  63. package/dist/{chunk-F6WVM437.js → chunk-WFQ5CLTR.js} +6 -3
  64. package/dist/chunk-WFQ5CLTR.js.map +1 -0
  65. package/dist/{chunk-TRQ76ISK.js → chunk-Z6BO53V7.js} +9 -9
  66. package/dist/{chunk-GEXP5IOF.js → chunk-ZA575VLF.js} +21 -8
  67. package/dist/chunk-ZA575VLF.js.map +1 -0
  68. package/dist/{claude-H33OQMXO.js → claude-6H36IBHO.js} +4 -2
  69. package/dist/{cleanup-OU2HFOOG.js → cleanup-ZPOMRSNN.js} +20 -16
  70. package/dist/cleanup-ZPOMRSNN.js.map +1 -0
  71. package/dist/cli.js +511 -959
  72. package/dist/cli.js.map +1 -1
  73. package/dist/commit-6S2RIA2K.js +237 -0
  74. package/dist/commit-6S2RIA2K.js.map +1 -0
  75. package/dist/{compile-ULNO5F7Q.js → compile-LRMAADUT.js} +9 -9
  76. package/dist/{contribute-T7ENST5N.js → contribute-GXKOIA42.js} +99 -31
  77. package/dist/contribute-GXKOIA42.js.map +1 -0
  78. package/dist/{dev-server-4RCDJ5MU.js → dev-server-GREJUEKW.js} +22 -74
  79. package/dist/dev-server-GREJUEKW.js.map +1 -0
  80. package/dist/{feedback-O4Q55SVS.js → feedback-G7G5QCY4.js} +10 -10
  81. package/dist/{git-FVMGBHC2.js → git-ENLT2VNI.js} +6 -4
  82. package/dist/hooks/iloom-hook.js +30 -2
  83. package/dist/{ignite-VHV65WEZ.js → ignite-YUAOJ5PP.js} +20 -20
  84. package/dist/ignite-YUAOJ5PP.js.map +1 -0
  85. package/dist/index.d.ts +71 -27
  86. package/dist/index.js +196 -266
  87. package/dist/index.js.map +1 -1
  88. package/dist/init-XQQMFDM6.js +21 -0
  89. package/dist/{lint-5JMCWE4Y.js → lint-OFVN7FT6.js} +9 -9
  90. package/dist/mcp/issue-management-server.js +359 -13
  91. package/dist/mcp/issue-management-server.js.map +1 -1
  92. package/dist/mcp/recap-server.js +13 -4
  93. package/dist/mcp/recap-server.js.map +1 -1
  94. package/dist/{open-WHVUYGPY.js → open-MCWQAPSZ.js} +25 -76
  95. package/dist/open-MCWQAPSZ.js.map +1 -0
  96. package/dist/{projects-SA76I4TZ.js → projects-PQOTWUII.js} +11 -4
  97. package/dist/projects-PQOTWUII.js.map +1 -0
  98. package/dist/prompts/init-prompt.txt +63 -59
  99. package/dist/prompts/issue-prompt.txt +132 -63
  100. package/dist/prompts/pr-prompt.txt +3 -3
  101. package/dist/prompts/regular-prompt.txt +16 -18
  102. package/dist/prompts/session-summary-prompt.txt +13 -13
  103. package/dist/{rebase-5EY3Q6XP.js → rebase-RKQED567.js} +53 -8
  104. package/dist/rebase-RKQED567.js.map +1 -0
  105. package/dist/{recap-VOOUXOGP.js → recap-ZKGHZCX6.js} +6 -6
  106. package/dist/{run-NCRK5NPR.js → run-CCG24PBC.js} +25 -76
  107. package/dist/run-CCG24PBC.js.map +1 -0
  108. package/dist/schema/settings.schema.json +14 -3
  109. package/dist/{shell-SBLXVOVJ.js → shell-2NNSIU34.js} +6 -6
  110. package/dist/{summary-CVFAMDOJ.js → summary-G6L3VAKK.js} +11 -10
  111. package/dist/{summary-CVFAMDOJ.js.map → summary-G6L3VAKK.js.map} +1 -1
  112. package/dist/{test-3KIVXI6J.js → test-QZDOEUIO.js} +9 -9
  113. package/dist/{test-git-ZB6AGGRW.js → test-git-E2BLXR6M.js} +4 -4
  114. package/dist/{test-prefix-FBGXKMPA.js → test-prefix-A7JGGYAA.js} +4 -4
  115. package/dist/{test-webserver-YVQD42W6.js → test-webserver-NRMGT2HB.js} +29 -8
  116. package/dist/test-webserver-NRMGT2HB.js.map +1 -0
  117. package/package.json +3 -1
  118. package/dist/ClaudeContextManager-6J2EB4QU.js +0 -14
  119. package/dist/ClaudeService-O2PB22GX.js +0 -13
  120. package/dist/PRManager-OCSB2HPT.js +0 -14
  121. package/dist/chunk-5IWU3HXE.js.map +0 -1
  122. package/dist/chunk-6YSFTPKW.js.map +0 -1
  123. package/dist/chunk-7Q66W4OH.js.map +0 -1
  124. package/dist/chunk-AEIMYF4P.js.map +0 -1
  125. package/dist/chunk-BXCPJJYM.js.map +0 -1
  126. package/dist/chunk-CFUWQHCJ.js.map +0 -1
  127. package/dist/chunk-F6WVM437.js.map +0 -1
  128. package/dist/chunk-GEXP5IOF.js.map +0 -1
  129. package/dist/chunk-K7SEEHKO.js.map +0 -1
  130. package/dist/chunk-PMVWQBWS.js.map +0 -1
  131. package/dist/chunk-UB4TFAXJ.js.map +0 -1
  132. package/dist/chunk-VU3QMIP2.js.map +0 -1
  133. package/dist/chunk-W6WVRHJ6.js +0 -251
  134. package/dist/chunk-W6WVRHJ6.js.map +0 -1
  135. package/dist/chunk-WIJWIKAN.js.map +0 -1
  136. package/dist/cleanup-OU2HFOOG.js.map +0 -1
  137. package/dist/contribute-T7ENST5N.js.map +0 -1
  138. package/dist/dev-server-4RCDJ5MU.js.map +0 -1
  139. package/dist/ignite-VHV65WEZ.js.map +0 -1
  140. package/dist/init-HB34Q5FH.js +0 -21
  141. package/dist/open-WHVUYGPY.js.map +0 -1
  142. package/dist/projects-SA76I4TZ.js.map +0 -1
  143. package/dist/rebase-5EY3Q6XP.js.map +0 -1
  144. package/dist/run-NCRK5NPR.js.map +0 -1
  145. package/dist/test-webserver-YVQD42W6.js.map +0 -1
  146. /package/dist/{BranchNamingService-B5PVRR7F.js.map → BranchNamingService-FLPUUFOB.js.map} +0 -0
  147. /package/dist/{ClaudeContextManager-6J2EB4QU.js.map → ClaudeContextManager-KE5TBZVZ.js.map} +0 -0
  148. /package/dist/{ClaudeService-O2PB22GX.js.map → ClaudeService-CRSETT3A.js.map} +0 -0
  149. /package/dist/{GitHubService-S2OGUTDR.js.map → GitHubService-O7U4UQ7N.js.map} +0 -0
  150. /package/dist/{LoomLauncher-5LFM4LXB.js.map → LoomLauncher-NL65LSKP.js.map} +0 -0
  151. /package/dist/{MetadataManager-DFI73J3G.js.map → MetadataManager-XJ2YB762.js.map} +0 -0
  152. /package/dist/{PRManager-OCSB2HPT.js.map → PRManager-2ABCWXHW.js.map} +0 -0
  153. /package/dist/{ProjectCapabilityDetector-S5FLNCFI.js.map → ProjectCapabilityDetector-UZYW32SY.js.map} +0 -0
  154. /package/dist/{PromptTemplateManager-C3DK6XZL.js.map → PromptTemplateManager-7L3HJQQU.js.map} +0 -0
  155. /package/dist/{SettingsManager-35F5RUJH.js.map → SettingsManager-YU4VYPTW.js.map} +0 -0
  156. /package/dist/{build-FJVYP7EV.js.map → build-O2EJHDEW.js.map} +0 -0
  157. /package/dist/{chunk-ZPSTA5PR.js.map → chunk-3CDWFEGL.js.map} +0 -0
  158. /package/dist/{chunk-UQIXZ3BA.js.map → chunk-5V74K5ZA.js.map} +0 -0
  159. /package/dist/{chunk-7WANFUIK.js.map → chunk-6TL3BYH6.js.map} +0 -0
  160. /package/dist/{chunk-5TXLVEXT.js.map → chunk-C3AKFAIR.js.map} +0 -0
  161. /package/dist/{chunk-64O2UIWO.js.map → chunk-GV5X6XUE.js.map} +0 -0
  162. /package/dist/{chunk-7HIRPCKU.js.map → chunk-HVQNVRAF.js.map} +0 -0
  163. /package/dist/{chunk-6U6VI4SZ.js.map → chunk-KVS4XGBQ.js.map} +0 -0
  164. /package/dist/{chunk-AXX3QIKK.js.map → chunk-LLWX3PCW.js.map} +0 -0
  165. /package/dist/{chunk-SN3Z6EZO.js.map → chunk-N7FVXZNI.js.map} +0 -0
  166. /package/dist/{chunk-EK3XCAAS.js.map → chunk-UDRZY65Y.js.map} +0 -0
  167. /package/dist/{chunk-3PT7RKL5.js.map → chunk-USJSNHGG.js.map} +0 -0
  168. /package/dist/{chunk-TRQ76ISK.js.map → chunk-Z6BO53V7.js.map} +0 -0
  169. /package/dist/{claude-H33OQMXO.js.map → claude-6H36IBHO.js.map} +0 -0
  170. /package/dist/{compile-ULNO5F7Q.js.map → compile-LRMAADUT.js.map} +0 -0
  171. /package/dist/{feedback-O4Q55SVS.js.map → feedback-G7G5QCY4.js.map} +0 -0
  172. /package/dist/{git-FVMGBHC2.js.map → git-ENLT2VNI.js.map} +0 -0
  173. /package/dist/{init-HB34Q5FH.js.map → init-XQQMFDM6.js.map} +0 -0
  174. /package/dist/{lint-5JMCWE4Y.js.map → lint-OFVN7FT6.js.map} +0 -0
  175. /package/dist/{recap-VOOUXOGP.js.map → recap-ZKGHZCX6.js.map} +0 -0
  176. /package/dist/{shell-SBLXVOVJ.js.map → shell-2NNSIU34.js.map} +0 -0
  177. /package/dist/{test-3KIVXI6J.js.map → test-QZDOEUIO.js.map} +0 -0
  178. /package/dist/{test-git-ZB6AGGRW.js.map → test-git-E2BLXR6M.js.map} +0 -0
  179. /package/dist/{test-prefix-FBGXKMPA.js.map → test-prefix-A7JGGYAA.js.map} +0 -0
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CommitManager,
4
+ ValidationRunner
5
+ } from "./chunk-NTIZLX42.js";
6
+ import "./chunk-LLWX3PCW.js";
7
+ import {
8
+ GitWorktreeManager
9
+ } from "./chunk-UDRZY65Y.js";
10
+ import "./chunk-ITN64ENQ.js";
11
+ import {
12
+ IssueManagementProviderFactory
13
+ } from "./chunk-GJMEKEI5.js";
14
+ import "./chunk-HBJITKSZ.js";
15
+ import {
16
+ extractIssueNumber,
17
+ getWorktreeRoot,
18
+ isValidGitRepo
19
+ } from "./chunk-ZA575VLF.js";
20
+ import {
21
+ SettingsManager
22
+ } from "./chunk-WFQ5CLTR.js";
23
+ import {
24
+ MetadataManager
25
+ } from "./chunk-VWGKGNJP.js";
26
+ import "./chunk-GCPAZSGV.js";
27
+ import "./chunk-ZX3GTM7O.js";
28
+ import "./chunk-FP7G7DG3.js";
29
+ import {
30
+ getLogger
31
+ } from "./chunk-6MLEBAYZ.js";
32
+ import "./chunk-VT4PDUYT.js";
33
+
34
+ // src/commands/commit.ts
35
+ import path from "path";
36
+ var WorktreeValidationError = class extends Error {
37
+ constructor(message, suggestion) {
38
+ super(message);
39
+ this.suggestion = suggestion;
40
+ this.name = "WorktreeValidationError";
41
+ }
42
+ };
43
+ var CommitCommand = class {
44
+ constructor(gitWorktreeManager = new GitWorktreeManager(), commitManager = new CommitManager(), settingsManager = new SettingsManager(), metadataManager = new MetadataManager(), validationRunner = new ValidationRunner()) {
45
+ this.gitWorktreeManager = gitWorktreeManager;
46
+ this.commitManager = commitManager;
47
+ this.settingsManager = settingsManager;
48
+ this.metadataManager = metadataManager;
49
+ this.validationRunner = validationRunner;
50
+ }
51
+ /**
52
+ * Execute the commit command
53
+ *
54
+ * @param input - Command input containing options
55
+ * @returns CommitResult when in JSON mode, void otherwise
56
+ */
57
+ async execute(input) {
58
+ var _a, _b, _c;
59
+ const logger = getLogger();
60
+ process.env.ILOOM = "1";
61
+ let worktreePath;
62
+ try {
63
+ worktreePath = await this.validateWorktreeContext();
64
+ } catch (error) {
65
+ if (error instanceof WorktreeValidationError) {
66
+ logger.error(error.message);
67
+ logger.info(error.suggestion);
68
+ throw error;
69
+ }
70
+ throw error;
71
+ }
72
+ const detected = await this.autoDetectIssue(worktreePath);
73
+ let trailerType = "Refs";
74
+ if (input.fixes) {
75
+ if (detected.loomType === "branch") {
76
+ logger.warn("--fixes flag ignored: not in an issue or PR worktree");
77
+ } else {
78
+ trailerType = "Fixes";
79
+ }
80
+ }
81
+ const status = await this.commitManager.detectUncommittedChanges(worktreePath);
82
+ if (!status.hasUncommittedChanges) {
83
+ logger.info("No uncommitted changes to commit");
84
+ if (input.json) {
85
+ return {
86
+ success: true,
87
+ trailerType,
88
+ issueNumber: detected.issueNumber
89
+ };
90
+ }
91
+ return;
92
+ }
93
+ let validationPassed = false;
94
+ if (!input.wipCommit) {
95
+ logger.info("Running pre-commit validations...");
96
+ const validationResult = await this.validationRunner.runValidations(worktreePath, {
97
+ dryRun: false
98
+ });
99
+ if (!validationResult.success) {
100
+ throw new Error("Validation failed. Fix errors before committing.");
101
+ }
102
+ logger.success("All validations passed");
103
+ validationPassed = true;
104
+ }
105
+ const settings = await this.settingsManager.loadSettings(worktreePath);
106
+ const providerType = ((_a = settings.issueManagement) == null ? void 0 : _a.provider) ?? "github";
107
+ const issuePrefix = IssueManagementProviderFactory.create(providerType).issuePrefix;
108
+ const shouldSkipVerify = input.wipCommit === true || validationPassed && (((_c = (_b = settings.workflows) == null ? void 0 : _b.issue) == null ? void 0 : _c.noVerify) ?? false);
109
+ let commitMessage = input.message;
110
+ if (input.wipCommit && !input.message) {
111
+ if (detected.issueNumber !== void 0) {
112
+ commitMessage = `WIP commit for Issue ${issuePrefix}${detected.issueNumber}`;
113
+ } else {
114
+ commitMessage = "WIP commit";
115
+ }
116
+ logger.debug(`Using hardcoded WIP message: ${commitMessage}`);
117
+ }
118
+ const commitOptions = {
119
+ issuePrefix,
120
+ skipVerify: shouldSkipVerify,
121
+ skipVerifySilent: input.wipCommit === true,
122
+ // Don't warn for --wip-commit
123
+ noReview: input.noReview ?? false,
124
+ trailerType,
125
+ ...commitMessage && { message: commitMessage },
126
+ ...detected.issueNumber !== void 0 && { issueNumber: detected.issueNumber }
127
+ };
128
+ const commitResult = await this.commitManager.commitChanges(worktreePath, commitOptions);
129
+ logger.success("Changes committed successfully");
130
+ if (input.json) {
131
+ return {
132
+ success: true,
133
+ trailerType,
134
+ issueNumber: detected.issueNumber,
135
+ message: commitResult == null ? void 0 : commitResult.message
136
+ };
137
+ }
138
+ }
139
+ /**
140
+ * Validate that the current directory is within an iloom-managed worktree
141
+ * Returns the worktree root path if valid
142
+ * @throws WorktreeValidationError if validation fails
143
+ */
144
+ async validateWorktreeContext() {
145
+ const currentDir = process.cwd();
146
+ const isGitRepo = await isValidGitRepo(currentDir);
147
+ if (!isGitRepo) {
148
+ throw new WorktreeValidationError(
149
+ "Not a git repository.",
150
+ "Run 'il commit' from within an iloom worktree created by 'il start'."
151
+ );
152
+ }
153
+ const worktreeRoot = await getWorktreeRoot(currentDir);
154
+ if (!worktreeRoot) {
155
+ throw new WorktreeValidationError(
156
+ "Could not determine repository root.",
157
+ "Run 'il commit' from within an iloom worktree created by 'il start'."
158
+ );
159
+ }
160
+ const worktrees = await this.gitWorktreeManager.listWorktrees();
161
+ const currentWorktree = worktrees.find((wt) => wt.path === worktreeRoot);
162
+ if (!currentWorktree) {
163
+ throw new WorktreeValidationError(
164
+ "This directory is not an iloom worktree.",
165
+ "Run 'il commit' from within a worktree created by 'il start <issue>'. Use 'il list' to see available worktrees."
166
+ );
167
+ }
168
+ const isMain = await this.gitWorktreeManager.isMainWorktree(currentWorktree, this.settingsManager);
169
+ if (isMain) {
170
+ throw new WorktreeValidationError(
171
+ "Cannot use il commit from the main worktree.",
172
+ "Navigate to a feature worktree created by 'il start <issue>' and run 'il commit' from there."
173
+ );
174
+ }
175
+ return worktreeRoot;
176
+ }
177
+ /**
178
+ * Auto-detect issue from current directory
179
+ * Similar to SummaryCommand.autoDetectFromCurrentDirectory()
180
+ */
181
+ async autoDetectIssue(worktreePath) {
182
+ var _a, _b, _c, _d, _e;
183
+ const logger = getLogger();
184
+ const currentDir = path.basename(worktreePath);
185
+ const prPattern = /_pr_(\d+)$/;
186
+ const prMatch = currentDir.match(prPattern);
187
+ if (prMatch == null ? void 0 : prMatch[1]) {
188
+ const prNumber = parseInt(prMatch[1], 10);
189
+ logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
190
+ const metadata2 = await this.metadataManager.readMetadata(worktreePath);
191
+ return {
192
+ issueNumber: ((_a = metadata2 == null ? void 0 : metadata2.pr_numbers) == null ? void 0 : _a[0]) ?? prNumber,
193
+ loomType: "pr"
194
+ };
195
+ }
196
+ const issueNumber = extractIssueNumber(currentDir);
197
+ if (issueNumber !== null) {
198
+ logger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`);
199
+ const metadata2 = await this.metadataManager.readMetadata(worktreePath);
200
+ return {
201
+ issueNumber: ((_b = metadata2 == null ? void 0 : metadata2.issue_numbers) == null ? void 0 : _b[0]) ?? issueNumber,
202
+ loomType: (metadata2 == null ? void 0 : metadata2.issueType) ?? "issue"
203
+ };
204
+ }
205
+ const repoInfo = await this.gitWorktreeManager.getRepoInfo();
206
+ const currentBranch = repoInfo.currentBranch;
207
+ if (currentBranch) {
208
+ const branchIssueNumber = extractIssueNumber(currentBranch);
209
+ if (branchIssueNumber !== null) {
210
+ logger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`);
211
+ const metadata2 = await this.metadataManager.readMetadata(worktreePath);
212
+ return {
213
+ issueNumber: ((_c = metadata2 == null ? void 0 : metadata2.issue_numbers) == null ? void 0 : _c[0]) ?? branchIssueNumber,
214
+ loomType: (metadata2 == null ? void 0 : metadata2.issueType) ?? "issue"
215
+ };
216
+ }
217
+ }
218
+ logger.debug("No issue number detected, treating as branch loom");
219
+ const metadata = await this.metadataManager.readMetadata(worktreePath);
220
+ let resolvedIssueNumber;
221
+ const loomType = (metadata == null ? void 0 : metadata.issueType) ?? "branch";
222
+ if (loomType === "issue" && ((_d = metadata == null ? void 0 : metadata.issue_numbers) == null ? void 0 : _d[0])) {
223
+ resolvedIssueNumber = metadata.issue_numbers[0];
224
+ } else if (loomType === "pr" && ((_e = metadata == null ? void 0 : metadata.pr_numbers) == null ? void 0 : _e[0])) {
225
+ resolvedIssueNumber = metadata.pr_numbers[0];
226
+ }
227
+ return {
228
+ issueNumber: resolvedIssueNumber,
229
+ loomType
230
+ };
231
+ }
232
+ };
233
+ export {
234
+ CommitCommand,
235
+ WorktreeValidationError
236
+ };
237
+ //# sourceMappingURL=commit-6S2RIA2K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/commit.ts"],"sourcesContent":["import path from 'path'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { CommitManager } from '../lib/CommitManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { ValidationRunner } from '../lib/ValidationRunner.js'\nimport { IssueManagementProviderFactory } from '../mcp/IssueManagementProviderFactory.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { extractIssueNumber, isValidGitRepo, getWorktreeRoot } from '../utils/git.js'\nimport type { CommitOptions } from '../types/index.js'\n\n/**\n * Input options for the commit command\n */\nexport interface CommitCommandInput {\n\tmessage?: string | undefined // Custom commit message (skip Claude generation)\n\tfixes?: boolean | undefined // Use \"Fixes #N\" trailer instead of \"Refs #N\"\n\tnoReview?: boolean | undefined // Skip commit message review prompt\n\tjson?: boolean | undefined // Output result as JSON\n\twipCommit?: boolean | undefined // Quick WIP commit: skip validations and pre-commit hooks\n}\n\n/**\n * Result of commit operation (returned in JSON mode)\n */\nexport interface CommitResult {\n\tsuccess: boolean\n\tcommitHash?: string | undefined\n\tmessage?: string | undefined\n\tfilesChanged?: number | undefined\n\tissueNumber?: string | number | undefined\n\ttrailerType: 'Refs' | 'Fixes'\n}\n\n/**\n * Error thrown when the commit command is run from an invalid location\n */\nexport class WorktreeValidationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly suggestion: string\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'WorktreeValidationError'\n\t}\n}\n\n/**\n * CommitCommand: Commit all uncommitted files with issue reference\n *\n * This command:\n * 1. Validates the current directory is an iloom-managed worktree\n * 2. Auto-detects the issue number from the worktree path/branch\n * 3. Commits all uncommitted changes with a Claude-generated or fallback message\n * 4. Uses \"Refs #N\" trailer by default (keeps issue open)\n * 5. Uses \"Fixes #N\" trailer with --fixes flag (closes issue)\n */\nexport class CommitCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate commitManager = new CommitManager(),\n\t\tprivate settingsManager = new SettingsManager(),\n\t\tprivate metadataManager = new MetadataManager(),\n\t\tprivate validationRunner = new ValidationRunner()\n\t) {}\n\n\t/**\n\t * Execute the commit command\n\t *\n\t * @param input - Command input containing options\n\t * @returns CommitResult when in JSON mode, void otherwise\n\t */\n\tasync execute(input: CommitCommandInput): Promise<CommitResult | void> {\n\t\tconst logger = getLogger()\n\n\t\t// Set ILOOM=1 so hooks know this is an iloom session\n\t\tprocess.env.ILOOM = '1'\n\n\t\t// Step 1: Validate worktree context\n\t\tlet worktreePath: string\n\t\ttry {\n\t\t\tworktreePath = await this.validateWorktreeContext()\n\t\t} catch (error) {\n\t\t\tif (error instanceof WorktreeValidationError) {\n\t\t\t\tlogger.error(error.message)\n\t\t\t\tlogger.info(error.suggestion)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\t// Step 2: Auto-detect issue from current directory\n\t\tconst detected = await this.autoDetectIssue(worktreePath)\n\n\t\t// Step 3: Determine trailer type\n\t\tlet trailerType: 'Refs' | 'Fixes' = 'Refs' // Default to Refs\n\t\tif (input.fixes) {\n\t\t\tif (detected.loomType === 'branch') {\n\t\t\t\t// Warn and ignore --fixes for branch looms (no issue to close)\n\t\t\t\tlogger.warn('--fixes flag ignored: not in an issue or PR worktree')\n\t\t\t} else {\n\t\t\t\ttrailerType = 'Fixes'\n\t\t\t}\n\t\t}\n\n\t\t// Step 4: Check for uncommitted changes\n\t\tconst status = await this.commitManager.detectUncommittedChanges(worktreePath)\n\t\tif (!status.hasUncommittedChanges) {\n\t\t\tlogger.info('No uncommitted changes to commit')\n\t\t\tif (input.json) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\ttrailerType,\n\t\t\t\t\tissueNumber: detected.issueNumber,\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// Step 5: Run validations unless --wip-commit is specified\n\t\tlet validationPassed = false\n\t\tif (!input.wipCommit) {\n\t\t\tlogger.info('Running pre-commit validations...')\n\t\t\tconst validationResult = await this.validationRunner.runValidations(worktreePath, {\n\t\t\t\tdryRun: false,\n\t\t\t})\n\t\t\tif (!validationResult.success) {\n\t\t\t\tthrow new Error('Validation failed. Fix errors before committing.')\n\t\t\t}\n\t\t\tlogger.success('All validations passed')\n\t\t\tvalidationPassed = true\n\t\t}\n\n\t\t// Step 6: Load settings to get issue prefix\n\t\tconst settings = await this.settingsManager.loadSettings(worktreePath)\n\t\tconst providerType = settings.issueManagement?.provider ?? 'github'\n\t\tconst issuePrefix = IssueManagementProviderFactory.create(providerType).issuePrefix\n\n\t\t// Determine whether to skip pre-commit hooks:\n\t\t// - With --wip-commit: always skip hooks (quick WIP commit)\n\t\t// - Otherwise: skip hooks only if validation passed AND noVerify setting is enabled\n\t\tconst shouldSkipVerify = input.wipCommit === true || (validationPassed && (settings.workflows?.issue?.noVerify ?? false))\n\n\t\t// Step 7: Determine commit message\n\t\t// For --wip-commit without a custom message, use a hardcoded WIP message to skip Claude generation\n\t\tlet commitMessage: string | undefined = input.message\n\t\tif (input.wipCommit && !input.message) {\n\t\t\tif (detected.issueNumber !== undefined) {\n\t\t\t\tcommitMessage = `WIP commit for Issue ${issuePrefix}${detected.issueNumber}`\n\t\t\t} else {\n\t\t\t\tcommitMessage = 'WIP commit'\n\t\t\t}\n\t\t\tlogger.debug(`Using hardcoded WIP message: ${commitMessage}`)\n\t\t}\n\n\t\t// Step 8: Build commit options\n\t\tconst commitOptions: CommitOptions = {\n\t\t\tissuePrefix,\n\t\t\tskipVerify: shouldSkipVerify,\n\t\t\tskipVerifySilent: input.wipCommit === true, // Don't warn for --wip-commit\n\t\t\tnoReview: input.noReview ?? false,\n\t\t\ttrailerType,\n\t\t\t...(commitMessage && { message: commitMessage }),\n\t\t\t...(detected.issueNumber !== undefined && { issueNumber: detected.issueNumber }),\n\t\t}\n\n\t\t// Step 9: Commit changes\n\t\tconst commitResult = await this.commitManager.commitChanges(worktreePath, commitOptions)\n\t\tlogger.success('Changes committed successfully')\n\n\t\t// Step 10: Return result in JSON mode\n\t\tif (input.json) {\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\ttrailerType,\n\t\t\t\tissueNumber: detected.issueNumber,\n\t\t\t\tmessage: commitResult?.message,\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate that the current directory is within an iloom-managed worktree\n\t * Returns the worktree root path if valid\n\t * @throws WorktreeValidationError if validation fails\n\t */\n\tprivate async validateWorktreeContext(): Promise<string> {\n\t\tconst currentDir = process.cwd()\n\n\t\t// Step 1: Check if we're in a git repository at all\n\t\tconst isGitRepo = await isValidGitRepo(currentDir)\n\t\tif (!isGitRepo) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'Not a git repository.',\n\t\t\t\t\"Run 'il commit' from within an iloom worktree created by 'il start'.\"\n\t\t\t)\n\t\t}\n\n\t\t// Step 2: Get the worktree root (handles subdirectories)\n\t\tconst worktreeRoot = await getWorktreeRoot(currentDir)\n\t\tif (!worktreeRoot) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'Could not determine repository root.',\n\t\t\t\t\"Run 'il commit' from within an iloom worktree created by 'il start'.\"\n\t\t\t)\n\t\t}\n\n\t\t// Step 3: Check if this path is a registered git worktree\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst currentWorktree = worktrees.find(wt => wt.path === worktreeRoot)\n\n\t\tif (!currentWorktree) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'This directory is not an iloom worktree.',\n\t\t\t\t\"Run 'il commit' from within a worktree created by 'il start <issue>'. Use 'il list' to see available worktrees.\"\n\t\t\t)\n\t\t}\n\n\t\t// Step 4: Check if this is the main worktree (we shouldn't commit from main with issue trailers)\n\t\tconst isMain = await this.gitWorktreeManager.isMainWorktree(currentWorktree, this.settingsManager)\n\t\tif (isMain) {\n\t\t\tthrow new WorktreeValidationError(\n\t\t\t\t'Cannot use il commit from the main worktree.',\n\t\t\t\t\"Navigate to a feature worktree created by 'il start <issue>' and run 'il commit' from there.\"\n\t\t\t)\n\t\t}\n\n\t\treturn worktreeRoot\n\t}\n\n\t/**\n\t * Auto-detect issue from current directory\n\t * Similar to SummaryCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectIssue(worktreePath: string): Promise<{\n\t\tissueNumber: string | number | undefined\n\t\tloomType: 'issue' | 'pr' | 'branch'\n\t}> {\n\t\tconst logger = getLogger()\n\t\tconst currentDir = path.basename(worktreePath)\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\n\t\t\t// Try to get issue number from metadata\n\t\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\t\t\treturn {\n\t\t\t\tissueNumber: metadata?.pr_numbers?.[0] ?? prNumber,\n\t\t\t\tloomType: 'pr',\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory name\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\n\t\t\t// Try to get issue number from metadata for more accuracy\n\t\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\t\t\treturn {\n\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? issueNumber,\n\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name and try to extract issue\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (currentBranch) {\n\t\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\t\tif (branchIssueNumber !== null) {\n\t\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\n\t\t\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\t\t\t\treturn {\n\t\t\t\t\tissueNumber: metadata?.issue_numbers?.[0] ?? branchIssueNumber,\n\t\t\t\t\tloomType: metadata?.issueType ?? 'issue',\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// No issue detected - treat as branch loom\n\t\tlogger.debug('No issue number detected, treating as branch loom')\n\t\tconst metadata = await this.metadataManager.readMetadata(worktreePath)\n\n\t\t// For branch looms, try to get issue number from metadata\n\t\tlet resolvedIssueNumber: string | number | undefined\n\t\tconst loomType = metadata?.issueType ?? 'branch'\n\n\t\tif (loomType === 'issue' && metadata?.issue_numbers?.[0]) {\n\t\t\tresolvedIssueNumber = metadata.issue_numbers[0]\n\t\t} else if (loomType === 'pr' && metadata?.pr_numbers?.[0]) {\n\t\t\tresolvedIssueNumber = metadata.pr_numbers[0]\n\t\t}\n\n\t\treturn {\n\t\t\tissueNumber: resolvedIssueNumber,\n\t\t\tloomType,\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["metadata"]}
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ScriptCommandBase
4
- } from "./chunk-TRQ76ISK.js";
4
+ } from "./chunk-Z6BO53V7.js";
5
+ import "./chunk-5V74K5ZA.js";
5
6
  import {
6
7
  runScript
7
- } from "./chunk-AXX3QIKK.js";
8
- import "./chunk-UQIXZ3BA.js";
8
+ } from "./chunk-LLWX3PCW.js";
9
+ import "./chunk-UDRZY65Y.js";
9
10
  import {
10
11
  getPackageScripts
11
- } from "./chunk-BXCPJJYM.js";
12
- import "./chunk-EK3XCAAS.js";
13
- import "./chunk-GEXP5IOF.js";
14
- import "./chunk-F6WVM437.js";
15
- import "./chunk-CFUWQHCJ.js";
12
+ } from "./chunk-ITN64ENQ.js";
13
+ import "./chunk-ZA575VLF.js";
14
+ import "./chunk-WFQ5CLTR.js";
15
+ import "./chunk-VWGKGNJP.js";
16
16
  import "./chunk-6MLEBAYZ.js";
17
17
  import {
18
18
  logger
@@ -54,4 +54,4 @@ var CompileCommand = class extends ScriptCommandBase {
54
54
  export {
55
55
  CompileCommand
56
56
  };
57
- //# sourceMappingURL=compile-ULNO5F7Q.js.map
57
+ //# sourceMappingURL=compile-LRMAADUT.js.map
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ FirstRunManager
4
+ } from "./chunk-Q7POFB5Q.js";
2
5
  import {
3
6
  executeGitCommand
4
- } from "./chunk-GEXP5IOF.js";
5
- import "./chunk-F6WVM437.js";
6
- import "./chunk-CFUWQHCJ.js";
7
+ } from "./chunk-ZA575VLF.js";
8
+ import "./chunk-WFQ5CLTR.js";
9
+ import "./chunk-VWGKGNJP.js";
7
10
  import {
8
11
  checkGhAuth,
9
12
  executeGhCommand
10
- } from "./chunk-LT3SGBR7.js";
13
+ } from "./chunk-GCPAZSGV.js";
11
14
  import {
12
15
  promptInput
13
16
  } from "./chunk-ZX3GTM7O.js";
@@ -21,8 +24,7 @@ import { existsSync, accessSync, constants } from "fs";
21
24
  import { writeFile, mkdir } from "fs/promises";
22
25
  import path from "path";
23
26
  import chalk from "chalk";
24
- var ILOOM_REPO = "iloom-ai/iloom-cli";
25
- var UPSTREAM_URL = "https://github.com/iloom-ai/iloom-cli.git";
27
+ var DEFAULT_REPO = "iloom-ai/iloom-cli";
26
28
  var MAX_PATH_LENGTH = 255;
27
29
  var RESERVED_NAMES = [
28
30
  "CON",
@@ -69,6 +71,35 @@ function validateDirectoryName(directoryName) {
69
71
  }
70
72
  return { isValid: true };
71
73
  }
74
+ function parseGitHubRepoUrl(input) {
75
+ const trimmed = input.trim();
76
+ const fullUrlMatch = trimmed.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/i);
77
+ if (fullUrlMatch) {
78
+ return `${fullUrlMatch[1]}/${fullUrlMatch[2]}`;
79
+ }
80
+ const shortUrlMatch = trimmed.match(/^github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/i);
81
+ if (shortUrlMatch) {
82
+ return `${shortUrlMatch[1]}/${shortUrlMatch[2]}`;
83
+ }
84
+ const directMatch = trimmed.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/);
85
+ if (directMatch) {
86
+ return `${directMatch[1]}/${directMatch[2]}`;
87
+ }
88
+ throw new Error(
89
+ `Invalid repository format: "${input}". Expected formats: "owner/repo", "github.com/owner/repo", or "https://github.com/owner/repo"`
90
+ );
91
+ }
92
+ async function validateRepoExists(repoPath) {
93
+ try {
94
+ await executeGhCommand(["api", `repos/${repoPath}`]);
95
+ return true;
96
+ } catch (error) {
97
+ if (error instanceof Error && error.message.includes("Not Found")) {
98
+ return false;
99
+ }
100
+ throw error;
101
+ }
102
+ }
72
103
  function validateDirectoryPath(directoryPath) {
73
104
  const nameValidation = validateDirectoryName(directoryPath);
74
105
  if (!nameValidation.isValid) {
@@ -102,29 +133,45 @@ var ContributeCommand = class {
102
133
  /**
103
134
  * Main entry point for the contribute command
104
135
  * Automates fork creation, cloning, and upstream configuration
136
+ * @param repository - Optional repository in various formats (owner/repo, github.com/owner/repo, or full URL)
105
137
  */
106
- async execute() {
107
- logger.info(chalk.bold("Setting up iloom contributor environment..."));
138
+ async execute(repository) {
139
+ let repoPath;
140
+ if (repository) {
141
+ repoPath = parseGitHubRepoUrl(repository);
142
+ logger.info(`Validating repository ${chalk.cyan(repoPath)}...`);
143
+ const exists = await validateRepoExists(repoPath);
144
+ if (!exists) {
145
+ throw new Error(`Repository not found: ${repoPath}. Please check the repository exists and you have access.`);
146
+ }
147
+ } else {
148
+ repoPath = DEFAULT_REPO;
149
+ }
150
+ const parts = repoPath.split("/");
151
+ const owner = parts[0];
152
+ const repoName = parts[1];
153
+ const upstreamUrl = `https://github.com/${repoPath}.git`;
154
+ logger.info(chalk.bold(`Setting up contributor environment for ${chalk.cyan(repoPath)}...`));
108
155
  const username = await this.getAuthenticatedUsername();
109
156
  logger.success(`Authenticated as ${chalk.cyan(username)}`);
110
- const hasFork = await this.forkExists(username);
157
+ const hasFork = await this.forkExists(username, repoName);
111
158
  if (!hasFork) {
112
- logger.info("Creating fork of iloom-ai/iloom-cli...");
113
- await this.createFork();
159
+ logger.info(`Creating fork of ${repoPath}...`);
160
+ await this.createFork(repoPath);
114
161
  logger.success("Fork created successfully");
115
162
  } else {
116
163
  logger.info("Using existing fork");
117
164
  }
118
- const directory = await this.promptForDirectory();
165
+ const directory = await this.promptForDirectory(repoName);
119
166
  if (!directory) {
120
167
  logger.info("Setup cancelled by user");
121
168
  process.exit(0);
122
169
  }
123
170
  const absolutePath = path.resolve(directory);
124
171
  logger.info(`Cloning repository to ${directory}...`);
125
- await this.cloneRepository(username, directory);
172
+ await this.cloneRepository(username, repoName, directory);
126
173
  logger.success("Repository cloned successfully");
127
- await this.addUpstreamRemote(absolutePath);
174
+ await this.addUpstreamRemote(absolutePath, upstreamUrl);
128
175
  logger.info("Configuring iloom settings...");
129
176
  await this.configureSettings(absolutePath);
130
177
  logger.success("Settings configured");
@@ -132,10 +179,16 @@ var ContributeCommand = class {
132
179
  logger.info(`
133
180
  Next steps:`);
134
181
  logger.info(` 1. cd ${directory}`);
135
- logger.info(` 2. pnpm install`);
136
- logger.info(` 3. iloom start <issue_number>`);
182
+ if (repoPath === DEFAULT_REPO) {
183
+ logger.info(` 2. pnpm install`);
184
+ logger.info(` 3. iloom start <issue_number>`);
185
+ } else {
186
+ logger.info(` 2. See README.md or CONTRIBUTING.md for setup instructions`);
187
+ logger.info(` 3. If this is not a JavaScript/TypeScript project, run:`);
188
+ logger.info(` iloom init "help me set up iloom for this non-javascript/typescript project"`);
189
+ }
137
190
  logger.info(`
138
- Happy contributing!`);
191
+ Happy contributing to ${owner}/${repoName}!`);
139
192
  }
140
193
  /**
141
194
  * Get authenticated GitHub username
@@ -160,11 +213,13 @@ Happy contributing!`);
160
213
  return authStatus.username;
161
214
  }
162
215
  /**
163
- * Check if user already has a fork of iloom-cli
216
+ * Check if user already has a fork of the target repository
217
+ * @param username - GitHub username
218
+ * @param repoName - Repository name (e.g., 'iloom-cli' from 'iloom-ai/iloom-cli')
164
219
  */
165
- async forkExists(username) {
220
+ async forkExists(username, repoName) {
166
221
  try {
167
- await executeGhCommand(["api", `repos/${username}/iloom-cli`]);
222
+ await executeGhCommand(["api", `repos/${username}/${repoName}`]);
168
223
  return true;
169
224
  } catch (error) {
170
225
  if (error instanceof Error && error.message.includes("Not Found")) {
@@ -174,29 +229,35 @@ Happy contributing!`);
174
229
  }
175
230
  }
176
231
  /**
177
- * Create a fork of iloom-cli without cloning
232
+ * Create a fork of the target repository without cloning
233
+ * @param repoPath - Full repository path (e.g., 'iloom-ai/iloom-cli')
178
234
  */
179
- async createFork() {
180
- await executeGhCommand(["repo", "fork", ILOOM_REPO, "--clone=false"]);
235
+ async createFork(repoPath) {
236
+ await executeGhCommand(["repo", "fork", repoPath, "--clone=false"]);
181
237
  }
182
238
  /**
183
239
  * Clone the repository using simplified gh CLI approach
240
+ * @param username - GitHub username
241
+ * @param repoName - Repository name (e.g., 'iloom-cli')
242
+ * @param directory - Target directory for clone
184
243
  */
185
- async cloneRepository(username, directory) {
186
- const repoIdentifier = `${username}/iloom-cli`;
244
+ async cloneRepository(username, repoName, directory) {
245
+ const repoIdentifier = `${username}/${repoName}`;
187
246
  await executeGhCommand(["repo", "clone", repoIdentifier, directory]);
188
247
  }
189
248
  /**
190
249
  * Add upstream remote if it doesn't already exist
250
+ * @param directory - Cloned repository directory
251
+ * @param upstreamUrl - URL for the upstream remote
191
252
  */
192
- async addUpstreamRemote(directory) {
253
+ async addUpstreamRemote(directory, upstreamUrl) {
193
254
  try {
194
255
  await executeGitCommand(["remote", "get-url", "upstream"], { cwd: directory });
195
256
  logger.info("Upstream remote already configured");
196
257
  } catch {
197
258
  logger.info("Adding upstream remote...");
198
259
  await executeGitCommand(
199
- ["remote", "add", "upstream", UPSTREAM_URL],
260
+ ["remote", "add", "upstream", upstreamUrl],
200
261
  { cwd: directory }
201
262
  );
202
263
  logger.success("Upstream remote configured");
@@ -204,15 +265,17 @@ Happy contributing!`);
204
265
  }
205
266
  /**
206
267
  * Prompt for directory with validation and retry loop
268
+ * @param repoName - Repository name for default directory suggestion
207
269
  * @returns The validated directory path, or null if user cancels
208
270
  */
209
- async promptForDirectory() {
271
+ async promptForDirectory(repoName) {
210
272
  const maxRetries = 3;
211
273
  let attempts = 0;
274
+ const defaultDir = `./${repoName}`;
212
275
  while (attempts < maxRetries) {
213
276
  const directory = await promptInput(
214
277
  "Where should the repository be cloned?",
215
- "./iloom-cli"
278
+ defaultDir
216
279
  );
217
280
  if (!directory || directory.trim() === "") {
218
281
  return null;
@@ -252,11 +315,16 @@ Happy contributing!`);
252
315
  }
253
316
  };
254
317
  await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
318
+ const firstRunManager = new FirstRunManager();
319
+ await firstRunManager.markProjectAsConfigured(directory);
320
+ logger.debug("Project marked as configured", { directory });
255
321
  }
256
322
  };
257
323
  export {
258
324
  ContributeCommand,
325
+ parseGitHubRepoUrl,
259
326
  validateDirectoryName,
260
- validateDirectoryPath
327
+ validateDirectoryPath,
328
+ validateRepoExists
261
329
  };
262
- //# sourceMappingURL=contribute-T7ENST5N.js.map
330
+ //# sourceMappingURL=contribute-GXKOIA42.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/contribute.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { checkGhAuth, executeGhCommand } from '../utils/github.js'\nimport { executeGitCommand } from '../utils/git.js'\nimport { promptInput } from '../utils/prompt.js'\nimport { existsSync, accessSync, constants } from 'fs'\nimport { writeFile, mkdir } from 'fs/promises'\nimport path from 'path'\nimport { InitCommand } from './init.js'\nimport chalk from 'chalk'\nimport { FirstRunManager } from '../utils/FirstRunManager.js'\n\nconst DEFAULT_REPO = 'iloom-ai/iloom-cli'\n\n// Maximum path length for most file systems\nconst MAX_PATH_LENGTH = 255\n\n// Reserved names on Windows (also avoid on all platforms for portability)\nconst RESERVED_NAMES = [\n\t'CON', 'PRN', 'AUX', 'NUL',\n\t'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',\n\t'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9',\n]\n\n// Invalid characters for directory names (cross-platform)\n// eslint-disable-next-line no-control-regex\nconst INVALID_CHARS_PATTERN = /[<>:\"|?*\\x00-\\x1f]/\n\n\n/**\n * Validation result for directory input\n */\ninterface DirectoryValidationResult {\n\tisValid: boolean\n\terror?: string\n}\n\n/**\n * Validate directory name format\n * @param directoryName - The directory name (not full path)\n * @returns Validation result with error message if invalid\n */\nexport function validateDirectoryName(directoryName: string): DirectoryValidationResult {\n\t// Check for empty or whitespace-only\n\tif (!directoryName || directoryName.trim() === '') {\n\t\treturn { isValid: false, error: 'Directory name cannot be empty' }\n\t}\n\n\tconst trimmed = directoryName.trim()\n\tconst baseName = path.basename(trimmed)\n\n\t// Check for invalid characters\n\tif (INVALID_CHARS_PATTERN.test(baseName)) {\n\t\treturn { isValid: false, error: 'Directory name contains invalid characters (<>:\"|?*)' }\n\t}\n\n\t// Check for reserved names (case-insensitive)\n\tif (RESERVED_NAMES.includes(baseName.toUpperCase())) {\n\t\treturn { isValid: false, error: `\"${baseName}\" is a reserved name and cannot be used` }\n\t}\n\n\t// Check for names that start/end with dots or spaces (problematic on some systems)\n\tif (baseName.startsWith('.') && baseName === '.') {\n\t\treturn { isValid: false, error: 'Directory name cannot be just a dot' }\n\t}\n\tif (baseName.endsWith('.') || baseName.endsWith(' ')) {\n\t\treturn { isValid: false, error: 'Directory name cannot end with a dot or space' }\n\t}\n\n\treturn { isValid: true }\n}\n\n/**\n * Parse GitHub repository URL in multiple formats and return normalized 'owner/repo' format\n * Supported formats:\n * - Full URL: https://github.com/owner/repo\n * - Shortened: github.com/owner/repo\n * - Direct: owner/repo\n * @param input - The repository URL/identifier to parse\n * @returns Normalized 'owner/repo' format\n * @throws Error if input doesn't match any supported format\n */\nexport function parseGitHubRepoUrl(input: string): string {\n\tconst trimmed = input.trim()\n\n\t// Pattern 1: Full URL - https://github.com/owner/repo or http://github.com/owner/repo\n\tconst fullUrlMatch = trimmed.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/i)\n\tif (fullUrlMatch) {\n\t\treturn `${fullUrlMatch[1]}/${fullUrlMatch[2]}`\n\t}\n\n\t// Pattern 2: Shortened URL - github.com/owner/repo\n\tconst shortUrlMatch = trimmed.match(/^github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/i)\n\tif (shortUrlMatch) {\n\t\treturn `${shortUrlMatch[1]}/${shortUrlMatch[2]}`\n\t}\n\n\t// Pattern 3: Direct format - owner/repo (must have exactly one slash, no other special chars)\n\tconst directMatch = trimmed.match(/^([a-zA-Z0-9_.-]+)\\/([a-zA-Z0-9_.-]+)$/)\n\tif (directMatch) {\n\t\treturn `${directMatch[1]}/${directMatch[2]}`\n\t}\n\n\tthrow new Error(\n\t\t`Invalid repository format: \"${input}\". ` +\n\t\t`Expected formats: \"owner/repo\", \"github.com/owner/repo\", or \"https://github.com/owner/repo\"`\n\t)\n}\n\n/**\n * Validate that a GitHub repository exists\n * @param repoPath - Repository in 'owner/repo' format\n * @returns true if repository exists, false otherwise\n * @throws Error for unexpected API errors (not 404)\n */\nexport async function validateRepoExists(repoPath: string): Promise<boolean> {\n\ttry {\n\t\tawait executeGhCommand(['api', `repos/${repoPath}`])\n\t\treturn true\n\t} catch (error) {\n\t\t// 404 means repo doesn't exist or user doesn't have access\n\t\tif (error instanceof Error && error.message.includes('Not Found')) {\n\t\t\treturn false\n\t\t}\n\t\t// Re-throw unexpected errors\n\t\tthrow error\n\t}\n}\n\n/**\n * Validate full directory path\n * @param directoryPath - The full directory path\n * @returns Validation result with error message if invalid\n */\nexport function validateDirectoryPath(directoryPath: string): DirectoryValidationResult {\n\t// First validate the directory name component\n\tconst nameValidation = validateDirectoryName(directoryPath)\n\tif (!nameValidation.isValid) {\n\t\treturn nameValidation\n\t}\n\n\tconst trimmed = directoryPath.trim()\n\tconst absolutePath = path.resolve(trimmed)\n\n\t// Check path length\n\tif (absolutePath.length > MAX_PATH_LENGTH) {\n\t\treturn {\n\t\t\tisValid: false,\n\t\t\terror: `Path is too long (${absolutePath.length} characters). Maximum is ${MAX_PATH_LENGTH} characters.`\n\t\t}\n\t}\n\n\t// Check if directory already exists\n\tif (existsSync(absolutePath)) {\n\t\treturn { isValid: false, error: `Directory already exists: ${trimmed}` }\n\t}\n\n\t// Check if parent directory exists\n\tconst parentDir = path.dirname(absolutePath)\n\tif (!existsSync(parentDir)) {\n\t\treturn { isValid: false, error: `Parent directory does not exist: ${parentDir}` }\n\t}\n\n\t// Check if parent directory is writable\n\ttry {\n\t\taccessSync(parentDir, constants.W_OK)\n\t} catch {\n\t\treturn { isValid: false, error: `Parent directory is not writable: ${parentDir}` }\n\t}\n\n\treturn { isValid: true }\n}\n\n\n/**\n * ContributeCommand - Set up local development environment for contributing to iloom\n * Implements issue #220: streamlined contributor onboarding workflow\n */\nexport class ContributeCommand {\n\tconstructor(_initCommand?: InitCommand) {}\n\n\t/**\n\t * Main entry point for the contribute command\n\t * Automates fork creation, cloning, and upstream configuration\n\t * @param repository - Optional repository in various formats (owner/repo, github.com/owner/repo, or full URL)\n\t */\n\tpublic async execute(repository?: string): Promise<void> {\n\t\t// Parse and validate repository if provided, otherwise use default\n\t\tlet repoPath: string\n\t\tif (repository) {\n\t\t\trepoPath = parseGitHubRepoUrl(repository)\n\t\t\tlogger.info(`Validating repository ${chalk.cyan(repoPath)}...`)\n\t\t\tconst exists = await validateRepoExists(repoPath)\n\t\t\tif (!exists) {\n\t\t\t\tthrow new Error(`Repository not found: ${repoPath}. Please check the repository exists and you have access.`)\n\t\t\t}\n\t\t} else {\n\t\t\trepoPath = DEFAULT_REPO\n\t\t}\n\n\t\t// Extract owner and repo name from path (guaranteed to be valid format after parseGitHubRepoUrl)\n\t\tconst parts = repoPath.split('/')\n\t\tconst owner = parts[0] as string\n\t\tconst repoName = parts[1] as string\n\t\tconst upstreamUrl = `https://github.com/${repoPath}.git`\n\n\t\tlogger.info(chalk.bold(`Setting up contributor environment for ${chalk.cyan(repoPath)}...`))\n\n\t\t// Step 1: Verify gh CLI authenticated\n\t\tconst username = await this.getAuthenticatedUsername()\n\t\tlogger.success(`Authenticated as ${chalk.cyan(username)}`)\n\n\t\t// Step 2: Check for existing fork\n\t\tconst hasFork = await this.forkExists(username, repoName)\n\n\t\t// Step 3: Create fork if needed\n\t\tif (!hasFork) {\n\t\t\tlogger.info(`Creating fork of ${repoPath}...`)\n\t\t\tawait this.createFork(repoPath)\n\t\t\tlogger.success('Fork created successfully')\n\t\t} else {\n\t\t\tlogger.info('Using existing fork')\n\t\t}\n\n\t\t// Step 4: Prompt for directory with validation and retry loop\n\t\tconst directory = await this.promptForDirectory(repoName)\n\n\t\t// Handle cancelled input\n\t\tif (!directory) {\n\t\t\tlogger.info('Setup cancelled by user')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tconst absolutePath = path.resolve(directory)\n\n\t\t// Step 5: Clone repository (gh CLI handles SSH/HTTPS automatically based on git config)\n\t\tlogger.info(`Cloning repository to ${directory}...`)\n\t\tawait this.cloneRepository(username, repoName, directory)\n\t\tlogger.success('Repository cloned successfully')\n\n\t\t// Step 6: Add upstream remote if it doesn't exist\n\t\tawait this.addUpstreamRemote(absolutePath, upstreamUrl)\n\n\t\t// Step 7: Configure settings\n\t\tlogger.info('Configuring iloom settings...')\n\t\tawait this.configureSettings(absolutePath)\n\t\tlogger.success('Settings configured')\n\n\t\tlogger.success(chalk.bold.green('\\nContributor environment setup complete!'))\n\t\tlogger.info(`\\nNext steps:`)\n\t\tlogger.info(` 1. cd ${directory}`)\n\t\tif (repoPath === DEFAULT_REPO) {\n\t\t\tlogger.info(` 2. pnpm install`)\n\t\t\tlogger.info(` 3. iloom start <issue_number>`)\n\t\t} else {\n\t\t\tlogger.info(` 2. See README.md or CONTRIBUTING.md for setup instructions`)\n\t\t\tlogger.info(` 3. If this is not a JavaScript/TypeScript project, run:`)\n\t\t\tlogger.info(` iloom init \"help me set up iloom for this non-javascript/typescript project\"`)\n\t\t}\n\t\tlogger.info(`\\nHappy contributing to ${owner}/${repoName}!`)\n\t}\n\n\t/**\n\t * Get authenticated GitHub username\n\t * @throws Error if not authenticated\n\t */\n\tprivate async getAuthenticatedUsername(): Promise<string> {\n\t\tconst authStatus = await checkGhAuth()\n\n\t\tif (!authStatus.hasAuth) {\n\t\t\tthrow new Error(\n\t\t\t\t'GitHub CLI is not authenticated. Please run: gh auth login'\n\t\t\t)\n\t\t}\n\n\t\tif (!authStatus.username) {\n\t\t\t// Try to fetch username from gh api if not in auth status\n\t\t\ttry {\n\t\t\t\tconst user = await executeGhCommand<{ login: string }>(['api', 'user', '--json', 'login'])\n\t\t\t\treturn user.login\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\tthrow new Error(`Unable to determine GitHub username: ${message}`)\n\t\t\t}\n\t\t}\n\n\t\treturn authStatus.username\n\t}\n\n\t/**\n\t * Check if user already has a fork of the target repository\n\t * @param username - GitHub username\n\t * @param repoName - Repository name (e.g., 'iloom-cli' from 'iloom-ai/iloom-cli')\n\t */\n\tprivate async forkExists(username: string, repoName: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait executeGhCommand(['api', `repos/${username}/${repoName}`])\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\t// 404 means no fork exists\n\t\t\tif (error instanceof Error && error.message.includes('Not Found')) {\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/**\n\t * Create a fork of the target repository without cloning\n\t * @param repoPath - Full repository path (e.g., 'iloom-ai/iloom-cli')\n\t */\n\tprivate async createFork(repoPath: string): Promise<void> {\n\t\tawait executeGhCommand(['repo', 'fork', repoPath, '--clone=false'])\n\t}\n\n\n\t/**\n\t * Clone the repository using simplified gh CLI approach\n\t * @param username - GitHub username\n\t * @param repoName - Repository name (e.g., 'iloom-cli')\n\t * @param directory - Target directory for clone\n\t */\n\tprivate async cloneRepository(\n\t\tusername: string,\n\t\trepoName: string,\n\t\tdirectory: string\n\t): Promise<void> {\n\t\tconst repoIdentifier = `${username}/${repoName}`\n\t\t// Always use gh repo clone - it handles SSH/HTTPS based on user's git config\n\t\tawait executeGhCommand(['repo', 'clone', repoIdentifier, directory])\n\t}\n\n\t/**\n\t * Add upstream remote if it doesn't already exist\n\t * @param directory - Cloned repository directory\n\t * @param upstreamUrl - URL for the upstream remote\n\t */\n\tprivate async addUpstreamRemote(directory: string, upstreamUrl: string): Promise<void> {\n\t\ttry {\n\t\t\t// Check if upstream remote exists\n\t\t\tawait executeGitCommand(['remote', 'get-url', 'upstream'], { cwd: directory })\n\t\t\tlogger.info('Upstream remote already configured')\n\t\t} catch {\n\t\t\t// Upstream doesn't exist, add it\n\t\t\tlogger.info('Adding upstream remote...')\n\t\t\tawait executeGitCommand(\n\t\t\t\t['remote', 'add', 'upstream', upstreamUrl],\n\t\t\t\t{ cwd: directory }\n\t\t\t)\n\t\t\tlogger.success('Upstream remote configured')\n\t\t}\n\t}\n\n\t/**\n\t * Prompt for directory with validation and retry loop\n\t * @param repoName - Repository name for default directory suggestion\n\t * @returns The validated directory path, or null if user cancels\n\t */\n\tprivate async promptForDirectory(repoName: string): Promise<string | null> {\n\t\tconst maxRetries = 3\n\t\tlet attempts = 0\n\t\tconst defaultDir = `./${repoName}`\n\n\t\twhile (attempts < maxRetries) {\n\t\t\tconst directory = await promptInput(\n\t\t\t\t'Where should the repository be cloned?',\n\t\t\t\tdefaultDir\n\t\t\t)\n\n\t\t\t// Handle empty input (user cancelled by entering empty string after exhausting default)\n\t\t\tif (!directory || directory.trim() === '') {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst trimmed = directory.trim()\n\n\t\t\t// Validate the directory path\n\t\t\tconst validation = validateDirectoryPath(trimmed)\n\t\t\tif (validation.isValid) {\n\t\t\t\treturn trimmed\n\t\t\t}\n\n\t\t\t// Show error and increment attempts\n\t\t\tattempts++\n\t\t\tif (attempts < maxRetries) {\n\t\t\t\tlogger.error(`${validation.error}`)\n\t\t\t\tlogger.info(`Please try again (${maxRetries - attempts} attempts remaining)`)\n\t\t\t} else {\n\t\t\t\tlogger.error(`${validation.error}`)\n\t\t\t\tlogger.error('Maximum retry attempts reached')\n\t\t\t\tthrow new Error(`Invalid directory after ${maxRetries} attempts: ${validation.error}`)\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n\n\n\t/**\n\t * Configure .iloom/settings.json with upstream remote\n\t */\n\tprivate async configureSettings(directory: string): Promise<void> {\n\t\tconst iloomDir = path.join(directory, '.iloom')\n\t\tconst settingsPath = path.join(iloomDir, 'settings.local.json')\n\n\t\t// Create .iloom directory\n\t\tawait mkdir(iloomDir, { recursive: true })\n\n\t\t// Create settings.json with upstream remote configuration and github-pr mode\n\t\tconst settings = {\n\t\t\tissueManagement: {\n\t\t\t\tgithub: {\n\t\t\t\t\tremote: 'upstream',\n\t\t\t\t},\n\t\t\t},\n\t\t\tmergeBehavior: {\n\t\t\t\tmode: 'github-draft-pr',\n\t\t\t},\n\t\t}\n\n\t\tawait writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\\n')\n\n\t\t// Mark project as configured for il projects list and VSCode extension detection\n\t\tconst firstRunManager = new FirstRunManager()\n\t\tawait firstRunManager.markProjectAsConfigured(directory)\n\t\tlogger.debug('Project marked as configured', { directory })\n\t}\n}\n"],"mappings":"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","names":[]}