@iloom/cli 0.1.14

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 (161) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +711 -0
  3. package/dist/ClaudeContextManager-XOSXQ67R.js +13 -0
  4. package/dist/ClaudeContextManager-XOSXQ67R.js.map +1 -0
  5. package/dist/ClaudeService-YSZ6EXWP.js +12 -0
  6. package/dist/ClaudeService-YSZ6EXWP.js.map +1 -0
  7. package/dist/GitHubService-F7Z3XJOS.js +11 -0
  8. package/dist/GitHubService-F7Z3XJOS.js.map +1 -0
  9. package/dist/LoomLauncher-MODG2SEM.js +263 -0
  10. package/dist/LoomLauncher-MODG2SEM.js.map +1 -0
  11. package/dist/NeonProvider-PAGPUH7F.js +12 -0
  12. package/dist/NeonProvider-PAGPUH7F.js.map +1 -0
  13. package/dist/PromptTemplateManager-7FINLRDE.js +9 -0
  14. package/dist/PromptTemplateManager-7FINLRDE.js.map +1 -0
  15. package/dist/SettingsManager-VAZF26S2.js +19 -0
  16. package/dist/SettingsManager-VAZF26S2.js.map +1 -0
  17. package/dist/SettingsMigrationManager-MTQIMI54.js +146 -0
  18. package/dist/SettingsMigrationManager-MTQIMI54.js.map +1 -0
  19. package/dist/add-issue-22JBNOML.js +54 -0
  20. package/dist/add-issue-22JBNOML.js.map +1 -0
  21. package/dist/agents/iloom-issue-analyze-and-plan.md +580 -0
  22. package/dist/agents/iloom-issue-analyzer.md +290 -0
  23. package/dist/agents/iloom-issue-complexity-evaluator.md +224 -0
  24. package/dist/agents/iloom-issue-enhancer.md +266 -0
  25. package/dist/agents/iloom-issue-implementer.md +262 -0
  26. package/dist/agents/iloom-issue-planner.md +358 -0
  27. package/dist/agents/iloom-issue-reviewer.md +63 -0
  28. package/dist/chunk-2ZPFJQ3B.js +63 -0
  29. package/dist/chunk-2ZPFJQ3B.js.map +1 -0
  30. package/dist/chunk-37DYYFVK.js +29 -0
  31. package/dist/chunk-37DYYFVK.js.map +1 -0
  32. package/dist/chunk-BLCTGFZN.js +121 -0
  33. package/dist/chunk-BLCTGFZN.js.map +1 -0
  34. package/dist/chunk-CP2NU2JC.js +545 -0
  35. package/dist/chunk-CP2NU2JC.js.map +1 -0
  36. package/dist/chunk-CWR2SANQ.js +39 -0
  37. package/dist/chunk-CWR2SANQ.js.map +1 -0
  38. package/dist/chunk-F3XBU2R7.js +110 -0
  39. package/dist/chunk-F3XBU2R7.js.map +1 -0
  40. package/dist/chunk-GEHQXLEI.js +130 -0
  41. package/dist/chunk-GEHQXLEI.js.map +1 -0
  42. package/dist/chunk-GYCR2LOU.js +143 -0
  43. package/dist/chunk-GYCR2LOU.js.map +1 -0
  44. package/dist/chunk-GZP4UGGM.js +48 -0
  45. package/dist/chunk-GZP4UGGM.js.map +1 -0
  46. package/dist/chunk-H4E4THUZ.js +55 -0
  47. package/dist/chunk-H4E4THUZ.js.map +1 -0
  48. package/dist/chunk-HPJJSYNS.js +644 -0
  49. package/dist/chunk-HPJJSYNS.js.map +1 -0
  50. package/dist/chunk-JBH2ZYYZ.js +220 -0
  51. package/dist/chunk-JBH2ZYYZ.js.map +1 -0
  52. package/dist/chunk-JNKJ7NJV.js +78 -0
  53. package/dist/chunk-JNKJ7NJV.js.map +1 -0
  54. package/dist/chunk-JQ7VOSTC.js +437 -0
  55. package/dist/chunk-JQ7VOSTC.js.map +1 -0
  56. package/dist/chunk-KQDEK2ZW.js +199 -0
  57. package/dist/chunk-KQDEK2ZW.js.map +1 -0
  58. package/dist/chunk-O2QWO64Z.js +179 -0
  59. package/dist/chunk-O2QWO64Z.js.map +1 -0
  60. package/dist/chunk-OC4H6HJD.js +248 -0
  61. package/dist/chunk-OC4H6HJD.js.map +1 -0
  62. package/dist/chunk-PR7FKQBG.js +120 -0
  63. package/dist/chunk-PR7FKQBG.js.map +1 -0
  64. package/dist/chunk-PXZBAC2M.js +250 -0
  65. package/dist/chunk-PXZBAC2M.js.map +1 -0
  66. package/dist/chunk-QEPVTTHD.js +383 -0
  67. package/dist/chunk-QEPVTTHD.js.map +1 -0
  68. package/dist/chunk-RSRO7564.js +203 -0
  69. package/dist/chunk-RSRO7564.js.map +1 -0
  70. package/dist/chunk-SJUQ2NDR.js +146 -0
  71. package/dist/chunk-SJUQ2NDR.js.map +1 -0
  72. package/dist/chunk-SPYPLHMK.js +177 -0
  73. package/dist/chunk-SPYPLHMK.js.map +1 -0
  74. package/dist/chunk-SSCQCCJ7.js +75 -0
  75. package/dist/chunk-SSCQCCJ7.js.map +1 -0
  76. package/dist/chunk-SSR5AVRJ.js +41 -0
  77. package/dist/chunk-SSR5AVRJ.js.map +1 -0
  78. package/dist/chunk-T7QPXANZ.js +315 -0
  79. package/dist/chunk-T7QPXANZ.js.map +1 -0
  80. package/dist/chunk-U3WU5OWO.js +203 -0
  81. package/dist/chunk-U3WU5OWO.js.map +1 -0
  82. package/dist/chunk-W3DQTW63.js +124 -0
  83. package/dist/chunk-W3DQTW63.js.map +1 -0
  84. package/dist/chunk-WKEWRSDB.js +151 -0
  85. package/dist/chunk-WKEWRSDB.js.map +1 -0
  86. package/dist/chunk-Y7SAGNUT.js +66 -0
  87. package/dist/chunk-Y7SAGNUT.js.map +1 -0
  88. package/dist/chunk-YETJNRQM.js +39 -0
  89. package/dist/chunk-YETJNRQM.js.map +1 -0
  90. package/dist/chunk-YYSKGAZT.js +384 -0
  91. package/dist/chunk-YYSKGAZT.js.map +1 -0
  92. package/dist/chunk-ZZZWQGTS.js +169 -0
  93. package/dist/chunk-ZZZWQGTS.js.map +1 -0
  94. package/dist/claude-7LUVDZZ4.js +17 -0
  95. package/dist/claude-7LUVDZZ4.js.map +1 -0
  96. package/dist/cleanup-3LUWPSM7.js +412 -0
  97. package/dist/cleanup-3LUWPSM7.js.map +1 -0
  98. package/dist/cli-overrides-XFZWY7CM.js +16 -0
  99. package/dist/cli-overrides-XFZWY7CM.js.map +1 -0
  100. package/dist/cli.js +603 -0
  101. package/dist/cli.js.map +1 -0
  102. package/dist/color-ZVALX37U.js +21 -0
  103. package/dist/color-ZVALX37U.js.map +1 -0
  104. package/dist/enhance-XJIQHVPD.js +166 -0
  105. package/dist/enhance-XJIQHVPD.js.map +1 -0
  106. package/dist/env-MDFL4ZXL.js +23 -0
  107. package/dist/env-MDFL4ZXL.js.map +1 -0
  108. package/dist/feedback-23CLXKFT.js +158 -0
  109. package/dist/feedback-23CLXKFT.js.map +1 -0
  110. package/dist/finish-CY4CIH6O.js +1608 -0
  111. package/dist/finish-CY4CIH6O.js.map +1 -0
  112. package/dist/git-LVRZ57GJ.js +43 -0
  113. package/dist/git-LVRZ57GJ.js.map +1 -0
  114. package/dist/ignite-WXEF2ID5.js +359 -0
  115. package/dist/ignite-WXEF2ID5.js.map +1 -0
  116. package/dist/index.d.ts +1341 -0
  117. package/dist/index.js +3058 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/init-RHACUR4E.js +123 -0
  120. package/dist/init-RHACUR4E.js.map +1 -0
  121. package/dist/installation-detector-VARGFFRZ.js +11 -0
  122. package/dist/installation-detector-VARGFFRZ.js.map +1 -0
  123. package/dist/logger-MKYH4UDV.js +12 -0
  124. package/dist/logger-MKYH4UDV.js.map +1 -0
  125. package/dist/mcp/chunk-6SDFJ42P.js +62 -0
  126. package/dist/mcp/chunk-6SDFJ42P.js.map +1 -0
  127. package/dist/mcp/claude-YHHHLSXH.js +249 -0
  128. package/dist/mcp/claude-YHHHLSXH.js.map +1 -0
  129. package/dist/mcp/color-QS5BFCNN.js +168 -0
  130. package/dist/mcp/color-QS5BFCNN.js.map +1 -0
  131. package/dist/mcp/github-comment-server.js +165 -0
  132. package/dist/mcp/github-comment-server.js.map +1 -0
  133. package/dist/mcp/terminal-SDCMDVD7.js +202 -0
  134. package/dist/mcp/terminal-SDCMDVD7.js.map +1 -0
  135. package/dist/open-X6BTENPV.js +278 -0
  136. package/dist/open-X6BTENPV.js.map +1 -0
  137. package/dist/prompt-ANTQWHUF.js +13 -0
  138. package/dist/prompt-ANTQWHUF.js.map +1 -0
  139. package/dist/prompts/issue-prompt.txt +230 -0
  140. package/dist/prompts/pr-prompt.txt +35 -0
  141. package/dist/prompts/regular-prompt.txt +14 -0
  142. package/dist/run-2JCPQAX3.js +278 -0
  143. package/dist/run-2JCPQAX3.js.map +1 -0
  144. package/dist/schema/settings.schema.json +221 -0
  145. package/dist/start-LWVRBJ6S.js +982 -0
  146. package/dist/start-LWVRBJ6S.js.map +1 -0
  147. package/dist/terminal-3D6TUAKJ.js +16 -0
  148. package/dist/terminal-3D6TUAKJ.js.map +1 -0
  149. package/dist/test-git-XPF4SZXJ.js +52 -0
  150. package/dist/test-git-XPF4SZXJ.js.map +1 -0
  151. package/dist/test-prefix-XGFXFAYN.js +68 -0
  152. package/dist/test-prefix-XGFXFAYN.js.map +1 -0
  153. package/dist/test-tabs-JRKY3QMM.js +69 -0
  154. package/dist/test-tabs-JRKY3QMM.js.map +1 -0
  155. package/dist/test-webserver-M2I3EV4J.js +62 -0
  156. package/dist/test-webserver-M2I3EV4J.js.map +1 -0
  157. package/dist/update-3ZT2XX2G.js +79 -0
  158. package/dist/update-3ZT2XX2G.js.map +1 -0
  159. package/dist/update-notifier-QSSEB5KC.js +11 -0
  160. package/dist/update-notifier-QSSEB5KC.js.map +1 -0
  161. package/package.json +113 -0
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ClaudeBranchNameStrategy,
4
+ SimpleBranchNameStrategy,
5
+ createIssue,
6
+ executeGhCommand,
7
+ fetchGhIssue,
8
+ fetchGhPR,
9
+ fetchProjectFields,
10
+ fetchProjectItems,
11
+ fetchProjectList,
12
+ hasProjectScope,
13
+ updateProjectItemField
14
+ } from "./chunk-KQDEK2ZW.js";
15
+ import {
16
+ promptConfirmation
17
+ } from "./chunk-JNKJ7NJV.js";
18
+ import {
19
+ logger
20
+ } from "./chunk-GEHQXLEI.js";
21
+
22
+ // src/types/github.ts
23
+ var GitHubError = class extends Error {
24
+ constructor(code, message, details) {
25
+ super(message);
26
+ this.code = code;
27
+ this.details = details;
28
+ this.name = "GitHubError";
29
+ }
30
+ };
31
+
32
+ // src/lib/GitHubService.ts
33
+ var GitHubService = class {
34
+ constructor(options) {
35
+ if (options == null ? void 0 : options.branchNameStrategy) {
36
+ this.defaultBranchNameStrategy = options.branchNameStrategy;
37
+ } else if ((options == null ? void 0 : options.useClaude) !== false) {
38
+ this.defaultBranchNameStrategy = new ClaudeBranchNameStrategy(
39
+ options == null ? void 0 : options.claudeModel
40
+ );
41
+ } else {
42
+ this.defaultBranchNameStrategy = new SimpleBranchNameStrategy();
43
+ }
44
+ this.prompter = (options == null ? void 0 : options.prompter) ?? promptConfirmation;
45
+ }
46
+ // Input detection
47
+ async detectInputType(input) {
48
+ const numberMatch = input.match(/^#?(\d+)$/);
49
+ if (!(numberMatch == null ? void 0 : numberMatch[1])) {
50
+ return { type: "unknown", number: null, rawInput: input };
51
+ }
52
+ const number = parseInt(numberMatch[1], 10);
53
+ logger.debug("Checking if input is a PR", { number });
54
+ const pr = await this.isValidPR(number);
55
+ if (pr) {
56
+ return { type: "pr", number, rawInput: input };
57
+ }
58
+ logger.debug("Checking if input is an issue", { number });
59
+ const issue = await this.isValidIssue(number);
60
+ if (issue) {
61
+ return { type: "issue", number, rawInput: input };
62
+ }
63
+ return { type: "unknown", number: null, rawInput: input };
64
+ }
65
+ // Issue fetching with validation
66
+ async fetchIssue(issueNumber) {
67
+ var _a;
68
+ try {
69
+ return await this.fetchIssueInternal(issueNumber);
70
+ } catch (error) {
71
+ if (error instanceof Error && "stderr" in error && ((_a = error.stderr) == null ? void 0 : _a.includes("Could not resolve"))) {
72
+ throw new GitHubError(
73
+ "NOT_FOUND" /* NOT_FOUND */,
74
+ `Issue #${issueNumber} not found`,
75
+ error
76
+ );
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+ // Silent issue validation (for detection phase)
82
+ async isValidIssue(issueNumber) {
83
+ var _a;
84
+ try {
85
+ return await this.fetchIssueInternal(issueNumber);
86
+ } catch (error) {
87
+ if (error instanceof Error && "stderr" in error && ((_a = error.stderr) == null ? void 0 : _a.includes("Could not resolve"))) {
88
+ return false;
89
+ }
90
+ throw error;
91
+ }
92
+ }
93
+ // Internal issue fetching logic (shared by fetchIssue and isValidIssue)
94
+ async fetchIssueInternal(issueNumber) {
95
+ const ghIssue = await fetchGhIssue(issueNumber);
96
+ return this.mapGitHubIssueToIssue(ghIssue);
97
+ }
98
+ async validateIssueState(issue) {
99
+ if (issue.state === "closed") {
100
+ const response = await this.promptUserConfirmation(
101
+ `Issue #${issue.number} is closed. Continue anyway?`
102
+ );
103
+ if (!response) {
104
+ throw new GitHubError(
105
+ "INVALID_STATE" /* INVALID_STATE */,
106
+ "User cancelled due to closed issue"
107
+ );
108
+ }
109
+ }
110
+ }
111
+ // PR fetching with validation
112
+ async fetchPR(prNumber) {
113
+ var _a;
114
+ try {
115
+ return await this.fetchPRInternal(prNumber);
116
+ } catch (error) {
117
+ if (error instanceof Error && "stderr" in error && ((_a = error.stderr) == null ? void 0 : _a.includes("Could not resolve"))) {
118
+ throw new GitHubError(
119
+ "NOT_FOUND" /* NOT_FOUND */,
120
+ `PR #${prNumber} not found`,
121
+ error
122
+ );
123
+ }
124
+ throw error;
125
+ }
126
+ }
127
+ // Silent PR validation (for detection phase)
128
+ async isValidPR(prNumber) {
129
+ var _a;
130
+ try {
131
+ return await this.fetchPRInternal(prNumber);
132
+ } catch (error) {
133
+ if (error instanceof Error && "stderr" in error && ((_a = error.stderr) == null ? void 0 : _a.includes("Could not resolve"))) {
134
+ return false;
135
+ }
136
+ throw error;
137
+ }
138
+ }
139
+ // Internal PR fetching logic (shared by fetchPR and isValidPR)
140
+ async fetchPRInternal(prNumber) {
141
+ const ghPR = await fetchGhPR(prNumber);
142
+ return this.mapGitHubPRToPullRequest(ghPR);
143
+ }
144
+ async validatePRState(pr) {
145
+ if (pr.state === "closed" || pr.state === "merged") {
146
+ const response = await this.promptUserConfirmation(
147
+ `PR #${pr.number} is ${pr.state}. Continue anyway?`
148
+ );
149
+ if (!response) {
150
+ throw new GitHubError(
151
+ "INVALID_STATE" /* INVALID_STATE */,
152
+ `User cancelled due to ${pr.state} PR`
153
+ );
154
+ }
155
+ }
156
+ }
157
+ // Branch name generation using strategy pattern
158
+ async generateBranchName(options) {
159
+ const { issueNumber, title, strategy } = options;
160
+ const nameStrategy = strategy ?? this.defaultBranchNameStrategy;
161
+ logger.debug("Generating branch name", {
162
+ issueNumber,
163
+ title,
164
+ strategy: nameStrategy.constructor.name
165
+ });
166
+ return nameStrategy.generate(issueNumber, title);
167
+ }
168
+ // Issue creation
169
+ async createIssue(title, body, repository, labels) {
170
+ return createIssue(title, body, { repo: repository, labels });
171
+ }
172
+ async getIssueUrl(issueNumber, repo) {
173
+ logger.debug("Fetching issue URL", { issueNumber, repo });
174
+ const issue = await fetchGhIssue(issueNumber, repo);
175
+ return issue.url;
176
+ }
177
+ // GitHub Projects integration
178
+ async moveIssueToInProgress(issueNumber) {
179
+ logger.info("Moving issue to In Progress in GitHub Projects", {
180
+ issueNumber
181
+ });
182
+ if (!await hasProjectScope()) {
183
+ logger.warn("Missing project scope in GitHub CLI auth");
184
+ throw new GitHubError(
185
+ "MISSING_SCOPE" /* MISSING_SCOPE */,
186
+ "GitHub CLI lacks project scope. Run: gh auth refresh -s project"
187
+ );
188
+ }
189
+ let owner;
190
+ try {
191
+ const repoInfo = await executeGhCommand(["repo", "view", "--json", "owner,name"]);
192
+ owner = repoInfo.owner.login;
193
+ } catch (error) {
194
+ logger.warn("Could not determine repository info", { error });
195
+ return;
196
+ }
197
+ let projects;
198
+ try {
199
+ projects = await fetchProjectList(owner);
200
+ } catch (error) {
201
+ logger.warn("Could not fetch projects", { owner, error });
202
+ return;
203
+ }
204
+ if (!projects.length) {
205
+ logger.warn("No projects found", { owner });
206
+ return;
207
+ }
208
+ for (const project of projects) {
209
+ await this.updateIssueStatusInProject(project, issueNumber, owner);
210
+ }
211
+ }
212
+ async updateIssueStatusInProject(project, issueNumber, owner) {
213
+ var _a;
214
+ let items;
215
+ try {
216
+ items = await fetchProjectItems(project.number, owner);
217
+ } catch (error) {
218
+ logger.debug("Could not fetch project items", { project: project.number, error });
219
+ return;
220
+ }
221
+ const item = items.find(
222
+ (i) => i.content.type === "Issue" && i.content.number === issueNumber
223
+ );
224
+ if (!item) {
225
+ logger.debug("Issue not found in project", {
226
+ issueNumber,
227
+ projectNumber: project.number
228
+ });
229
+ return;
230
+ }
231
+ let fieldsData;
232
+ try {
233
+ fieldsData = await fetchProjectFields(project.number, owner);
234
+ } catch (error) {
235
+ logger.debug("Could not fetch project fields", { project: project.number, error });
236
+ return;
237
+ }
238
+ const statusField = fieldsData.fields.find((f) => f.name === "Status");
239
+ if (!statusField) {
240
+ logger.debug("No Status field found in project", { projectNumber: project.number });
241
+ return;
242
+ }
243
+ const inProgressOption = (_a = statusField.options) == null ? void 0 : _a.find(
244
+ (o) => o.name === "In Progress" || o.name === "In progress"
245
+ );
246
+ if (!inProgressOption) {
247
+ logger.debug("No In Progress option found in Status field", { projectNumber: project.number });
248
+ return;
249
+ }
250
+ try {
251
+ await updateProjectItemField(
252
+ item.id,
253
+ project.id,
254
+ statusField.id,
255
+ inProgressOption.id
256
+ );
257
+ logger.info("Updated issue status in project", {
258
+ issueNumber,
259
+ projectNumber: project.number
260
+ });
261
+ } catch (error) {
262
+ logger.debug("Could not update project item", { item: item.id, error });
263
+ }
264
+ }
265
+ // Utility methods
266
+ extractContext(entity) {
267
+ if ("branch" in entity) {
268
+ return `Pull Request #${entity.number}: ${entity.title}
269
+ Branch: ${entity.branch}
270
+ State: ${entity.state}`;
271
+ } else {
272
+ return `GitHub Issue #${entity.number}: ${entity.title}
273
+ State: ${entity.state}`;
274
+ }
275
+ }
276
+ mapGitHubIssueToIssue(ghIssue) {
277
+ return {
278
+ number: ghIssue.number,
279
+ title: ghIssue.title,
280
+ body: ghIssue.body,
281
+ state: ghIssue.state.toLowerCase(),
282
+ labels: ghIssue.labels.map((l) => l.name),
283
+ assignees: ghIssue.assignees.map((a) => a.login),
284
+ url: ghIssue.url
285
+ };
286
+ }
287
+ mapGitHubPRToPullRequest(ghPR) {
288
+ return {
289
+ number: ghPR.number,
290
+ title: ghPR.title,
291
+ body: ghPR.body,
292
+ state: ghPR.state.toLowerCase(),
293
+ branch: ghPR.headRefName,
294
+ baseBranch: ghPR.baseRefName,
295
+ url: ghPR.url,
296
+ isDraft: ghPR.isDraft
297
+ };
298
+ }
299
+ async promptUserConfirmation(message) {
300
+ return this.prompter(message);
301
+ }
302
+ // Allow setting strategy at runtime for specific operations
303
+ setDefaultBranchNameStrategy(strategy) {
304
+ this.defaultBranchNameStrategy = strategy;
305
+ }
306
+ // Get current strategy for testing
307
+ getBranchNameStrategy() {
308
+ return this.defaultBranchNameStrategy;
309
+ }
310
+ };
311
+
312
+ export {
313
+ GitHubService
314
+ };
315
+ //# sourceMappingURL=chunk-T7QPXANZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/github.ts","../src/lib/GitHubService.ts"],"sourcesContent":["// Core GitHub response types\nexport interface GitHubIssue {\n\tnumber: number\n\ttitle: string\n\tbody: string\n\tstate: 'OPEN' | 'CLOSED' // GitHub GraphQL format\n\tlabels: { name: string }[]\n\tassignees: { login: string }[]\n\turl: string\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\n// Pull Request types\nexport interface GitHubPullRequest {\n\tnumber: number\n\ttitle: string\n\tbody: string\n\tstate: 'OPEN' | 'CLOSED' | 'MERGED'\n\theadRefName: string // source branch\n\tbaseRefName: string // target branch\n\turl: string\n\tisDraft: boolean\n\tmergeable: 'CONFLICTING' | 'MERGEABLE' | 'UNKNOWN'\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\n// GitHub Projects types\nexport interface GitHubProject {\n\tnumber: number\n\tid: string\n\tname: string\n\tfields: ProjectField[]\n}\n\nexport interface ProjectField {\n\tid: string\n\tname: string\n\tdataType: 'SINGLE_SELECT' | 'TEXT' | 'NUMBER' | 'DATE'\n\toptions?: ProjectFieldOption[]\n}\n\nexport interface ProjectFieldOption {\n\tid: string\n\tname: string\n}\n\nexport interface ProjectItem {\n\tid: string\n\tcontent: {\n\t\ttype: 'Issue' | 'PullRequest' | 'DraftIssue'\n\t\tnumber: number\n\t}\n\tfieldValues: Record<string, unknown>\n}\n\n// Command result types\nexport interface GitHubCommandResult<T = unknown> {\n\tsuccess: boolean\n\tdata?: T\n\terror?: string\n\trateLimitRemaining?: number\n\trateLimitReset?: Date\n}\n\nexport interface GitHubAuthStatus {\n\thasAuth: boolean\n\tscopes: string[]\n\tusername?: string\n}\n\n// Input detection types\nexport interface GitHubInputDetection {\n\ttype: 'issue' | 'pr' | 'unknown'\n\tnumber: number | null\n\trawInput: string\n}\n\n// Branch name generation strategy interface\nexport interface BranchNameStrategy {\n\tgenerate(issueNumber: number, title: string): Promise<string>\n}\n\nexport interface BranchGenerationOptions {\n\tissueNumber: number\n\ttitle: string\n\tstrategy?: BranchNameStrategy\n}\n\n// Context and error types\nexport interface GitHubContext {\n\tissue?: GitHubIssue\n\tpullRequest?: GitHubPullRequest\n\tformattedContext: string\n}\n\nexport enum GitHubErrorCode {\n\tNOT_FOUND = 'NOT_FOUND',\n\tUNAUTHORIZED = 'UNAUTHORIZED',\n\tRATE_LIMITED = 'RATE_LIMITED',\n\tNETWORK_ERROR = 'NETWORK_ERROR',\n\tINVALID_STATE = 'INVALID_STATE',\n\tMISSING_SCOPE = 'MISSING_SCOPE',\n}\n\nexport class GitHubError extends Error {\n\tconstructor(\n\t\tpublic code: GitHubErrorCode,\n\t\tmessage: string,\n\t\tpublic details?: unknown\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'GitHubError'\n\t}\n}\n","import type { Issue, PullRequest } from '../types/index.js'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tGitHubInputDetection,\n\tBranchGenerationOptions,\n\tBranchNameStrategy,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { GitHubError, GitHubErrorCode } from '../types/github.js'\nimport {\n\texecuteGhCommand,\n\thasProjectScope,\n\tfetchGhIssue,\n\tfetchGhPR,\n\tfetchProjectList,\n\tfetchProjectItems,\n\tfetchProjectFields,\n\tupdateProjectItemField,\n\tcreateIssue,\n\tSimpleBranchNameStrategy,\n\tClaudeBranchNameStrategy,\n} from '../utils/github.js'\nimport { logger } from '../utils/logger.js'\nimport { promptConfirmation } from '../utils/prompt.js'\n\nexport class GitHubService {\n\tprivate defaultBranchNameStrategy: BranchNameStrategy\n\tprivate prompter: (message: string) => Promise<boolean>\n\n\tconstructor(options?: {\n\t\tbranchNameStrategy?: BranchNameStrategy\n\t\tuseClaude?: boolean\n\t\tclaudeModel?: string\n\t\tprompter?: (message: string) => Promise<boolean>\n\t}) {\n\t\t// Set up default strategy based on options\n\t\tif (options?.branchNameStrategy) {\n\t\t\tthis.defaultBranchNameStrategy = options.branchNameStrategy\n\t\t} else if (options?.useClaude !== false) {\n\t\t\tthis.defaultBranchNameStrategy = new ClaudeBranchNameStrategy(\n\t\t\t\toptions?.claudeModel\n\t\t\t)\n\t\t} else {\n\t\t\tthis.defaultBranchNameStrategy = new SimpleBranchNameStrategy()\n\t\t}\n\n\t\t// Set up prompter (use provided or default to promptConfirmation)\n\t\tthis.prompter = options?.prompter ?? promptConfirmation\n\t}\n\n\t// Input detection\n\tpublic async detectInputType(input: string): Promise<GitHubInputDetection> {\n\t\t// Pattern: #123 or just 123\n\t\tconst numberMatch = input.match(/^#?(\\d+)$/)\n\n\t\tif (!numberMatch?.[1]) {\n\t\t\treturn { type: 'unknown', number: null, rawInput: input }\n\t\t}\n\n\t\tconst number = parseInt(numberMatch[1], 10)\n\n\t\t// Try PR first (based on bash script logic at lines 500-533)\n\t\tlogger.debug('Checking if input is a PR', { number })\n\t\tconst pr = await this.isValidPR(number)\n\t\tif (pr) {\n\t\t\treturn { type: 'pr', number, rawInput: input }\n\t\t}\n\n\t\t// Try issue next (lines 536-575 in bash)\n\t\tlogger.debug('Checking if input is an issue', { number })\n\t\tconst issue = await this.isValidIssue(number)\n\t\tif (issue) {\n\t\t\treturn { type: 'issue', number, rawInput: input }\n\t\t}\n\n\t\t// Neither PR nor issue found\n\t\treturn { type: 'unknown', number: null, rawInput: input }\n\t}\n\n\t// Issue fetching with validation\n\tpublic async fetchIssue(issueNumber: number): Promise<Issue> {\n\t\ttry {\n\t\t\treturn await this.fetchIssueInternal(issueNumber)\n\t\t} catch (error) {\n\t\t\t// Only throw NOT_FOUND for actual \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.NOT_FOUND,\n\t\t\t\t\t`Issue #${issueNumber} not found`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t\t// Re-throw all other errors unchanged\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Silent issue validation (for detection phase)\n\tpublic async isValidIssue(issueNumber: number): Promise<Issue | false> {\n\t\ttry {\n\t\t\treturn await this.fetchIssueInternal(issueNumber)\n\t\t} catch (error) {\n\t\t\t// Silently return false for \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Internal issue fetching logic (shared by fetchIssue and isValidIssue)\n\tprivate async fetchIssueInternal(issueNumber: number): Promise<Issue> {\n\t\tconst ghIssue = await fetchGhIssue(issueNumber)\n\t\treturn this.mapGitHubIssueToIssue(ghIssue)\n\t}\n\n\tpublic async validateIssueState(issue: Issue): Promise<void> {\n\t\tif (issue.state === 'closed') {\n\t\t\tconst response = await this.promptUserConfirmation(\n\t\t\t\t`Issue #${issue.number} is closed. Continue anyway?`\n\t\t\t)\n\t\t\tif (!response) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.INVALID_STATE,\n\t\t\t\t\t'User cancelled due to closed issue'\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// PR fetching with validation\n\tpublic async fetchPR(prNumber: number): Promise<PullRequest> {\n\t\ttry {\n\t\t\treturn await this.fetchPRInternal(prNumber)\n\t\t} catch (error) {\n\t\t\t// Only throw NOT_FOUND for actual \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.NOT_FOUND,\n\t\t\t\t\t`PR #${prNumber} not found`,\n\t\t\t\t\terror\n\t\t\t\t)\n\t\t\t}\n\t\t\t// Re-throw all other errors unchanged\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Silent PR validation (for detection phase)\n\tpublic async isValidPR(prNumber: number): Promise<PullRequest | false> {\n\t\ttry {\n\t\t\treturn await this.fetchPRInternal(prNumber)\n\t\t} catch (error) {\n\t\t\t// Silently return false for \"not found\" errors\n\t\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('Could not resolve')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Internal PR fetching logic (shared by fetchPR and isValidPR)\n\tprivate async fetchPRInternal(prNumber: number): Promise<PullRequest> {\n\t\tconst ghPR = await fetchGhPR(prNumber)\n\t\treturn this.mapGitHubPRToPullRequest(ghPR)\n\t}\n\n\tpublic async validatePRState(pr: PullRequest): Promise<void> {\n\t\tif (pr.state === 'closed' || pr.state === 'merged') {\n\t\t\tconst response = await this.promptUserConfirmation(\n\t\t\t\t`PR #${pr.number} is ${pr.state}. Continue anyway?`\n\t\t\t)\n\t\t\tif (!response) {\n\t\t\t\tthrow new GitHubError(\n\t\t\t\t\tGitHubErrorCode.INVALID_STATE,\n\t\t\t\t\t`User cancelled due to ${pr.state} PR`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Branch name generation using strategy pattern\n\tpublic async generateBranchName(\n\t\toptions: BranchGenerationOptions\n\t): Promise<string> {\n\t\tconst { issueNumber, title, strategy } = options\n\n\t\t// Use provided strategy or fall back to default\n\t\tconst nameStrategy = strategy ?? this.defaultBranchNameStrategy\n\n\t\tlogger.debug('Generating branch name', {\n\t\t\tissueNumber,\n\t\t\ttitle,\n\t\t\tstrategy: nameStrategy.constructor.name,\n\t\t})\n\n\t\treturn nameStrategy.generate(issueNumber, title)\n\t}\n\n\t// Issue creation\n\tpublic async createIssue(\n\t\ttitle: string,\n\t\tbody: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: number; url: string }> {\n\t\t// logger.info('Creating GitHub issue', { title })\n\t\treturn createIssue(title, body, { repo: repository, labels })\n\t}\n\n\tpublic async getIssueUrl(issueNumber: number, repo?: string): Promise<string> {\n\t\tlogger.debug('Fetching issue URL', { issueNumber, repo })\n\t\tconst issue = await fetchGhIssue(issueNumber, repo)\n\t\treturn issue.url\n\t}\n\n\t// GitHub Projects integration\n\tpublic async moveIssueToInProgress(issueNumber: number): Promise<void> {\n\t\t// Based on bash script lines 374-463\n\t\tlogger.info('Moving issue to In Progress in GitHub Projects', {\n\t\t\tissueNumber,\n\t\t})\n\n\t\t// Check for project scope\n\t\tif (!(await hasProjectScope())) {\n\t\t\tlogger.warn('Missing project scope in GitHub CLI auth')\n\t\t\tthrow new GitHubError(\n\t\t\t\tGitHubErrorCode.MISSING_SCOPE,\n\t\t\t\t'GitHub CLI lacks project scope. Run: gh auth refresh -s project'\n\t\t\t)\n\t\t}\n\n\t\t// Get repository info\n\t\tlet owner: string\n\t\ttry {\n\t\t\tconst repoInfo = await executeGhCommand<{\n\t\t\t\towner: { login: string }\n\t\t\t\tname: string\n\t\t\t}>(['repo', 'view', '--json', 'owner,name'])\n\t\t\towner = repoInfo.owner.login\n\t\t} catch (error) {\n\t\t\tlogger.warn('Could not determine repository info', { error })\n\t\t\treturn\n\t\t}\n\n\t\t// List all projects\n\t\tlet projects: GitHubProject[]\n\t\ttry {\n\t\t\tprojects = await fetchProjectList(owner)\n\t\t} catch (error) {\n\t\t\tlogger.warn('Could not fetch projects', { owner, error })\n\t\t\treturn\n\t\t}\n\n\t\tif (!projects.length) {\n\t\t\tlogger.warn('No projects found', { owner })\n\t\t\treturn\n\t\t}\n\n\t\t// Process each project (lines 404-460 in bash)\n\t\tfor (const project of projects) {\n\t\t\tawait this.updateIssueStatusInProject(project, issueNumber, owner)\n\t\t}\n\t}\n\n\tprivate async updateIssueStatusInProject(\n\t\tproject: GitHubProject,\n\t\tissueNumber: number,\n\t\towner: string\n\t): Promise<void> {\n\t\t// Check if issue is in project\n\t\tlet items: ProjectItem[]\n\t\ttry {\n\t\t\titems = await fetchProjectItems(project.number, owner)\n\t\t} catch (error) {\n\t\t\tlogger.debug('Could not fetch project items', { project: project.number, error })\n\t\t\treturn\n\t\t}\n\n\t\t// Find issue item\n\t\tconst item = items.find(\n\t\t\t(i: ProjectItem) =>\n\t\t\t\ti.content.type === 'Issue' && i.content.number === issueNumber\n\t\t)\n\n\t\tif (!item) {\n\t\t\tlogger.debug('Issue not found in project', {\n\t\t\t\tissueNumber,\n\t\t\t\tprojectNumber: project.number,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Fetch project fields separately (like bash script does)\n\t\tlet fieldsData: { fields: ProjectField[] }\n\t\ttry {\n\t\t\tfieldsData = await fetchProjectFields(project.number, owner)\n\t\t} catch (error) {\n\t\t\tlogger.debug('Could not fetch project fields', { project: project.number, error })\n\t\t\treturn\n\t\t}\n\n\t\t// Find Status field and In Progress option\n\t\tconst statusField = fieldsData.fields.find((f) => f.name === 'Status')\n\t\tif (!statusField) {\n\t\t\tlogger.debug('No Status field found in project', { projectNumber: project.number })\n\t\t\treturn\n\t\t}\n\n\t\tconst inProgressOption = statusField.options?.find(\n\t\t\t(o: { id: string; name: string }) => o.name === 'In Progress' || o.name === 'In progress'\n\t\t)\n\n\t\tif (!inProgressOption) {\n\t\t\tlogger.debug('No In Progress option found in Status field', { projectNumber: project.number })\n\t\t\treturn\n\t\t}\n\n\t\t// Update status\n\t\ttry {\n\t\t\tawait updateProjectItemField(\n\t\t\t\titem.id,\n\t\t\t\tproject.id,\n\t\t\t\tstatusField.id,\n\t\t\t\tinProgressOption.id\n\t\t\t)\n\n\t\t\tlogger.info('Updated issue status in project', {\n\t\t\t\tissueNumber,\n\t\t\t\tprojectNumber: project.number,\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tlogger.debug('Could not update project item', { item: item.id, error })\n\t\t}\n\t}\n\n\t// Utility methods\n\tpublic extractContext(entity: Issue | PullRequest): string {\n\t\tif ('branch' in entity) {\n\t\t\t// It's a PullRequest\n\t\t\treturn `Pull Request #${entity.number}: ${entity.title}\\nBranch: ${entity.branch}\\nState: ${entity.state}`\n\t\t} else {\n\t\t\t// It's an Issue\n\t\t\treturn `GitHub Issue #${entity.number}: ${entity.title}\\nState: ${entity.state}`\n\t\t}\n\t}\n\n\tprivate mapGitHubIssueToIssue(ghIssue: GitHubIssue): Issue {\n\t\treturn {\n\t\t\tnumber: ghIssue.number,\n\t\t\ttitle: ghIssue.title,\n\t\t\tbody: ghIssue.body,\n\t\t\tstate: ghIssue.state.toLowerCase() as 'open' | 'closed',\n\t\t\tlabels: ghIssue.labels.map((l) => l.name),\n\t\t\tassignees: ghIssue.assignees.map((a) => a.login),\n\t\t\turl: ghIssue.url,\n\t\t}\n\t}\n\n\tprivate mapGitHubPRToPullRequest(ghPR: GitHubPullRequest): PullRequest {\n\t\treturn {\n\t\t\tnumber: ghPR.number,\n\t\t\ttitle: ghPR.title,\n\t\t\tbody: ghPR.body,\n\t\t\tstate: ghPR.state.toLowerCase() as 'open' | 'closed' | 'merged',\n\t\t\tbranch: ghPR.headRefName,\n\t\t\tbaseBranch: ghPR.baseRefName,\n\t\t\turl: ghPR.url,\n\t\t\tisDraft: ghPR.isDraft,\n\t\t}\n\t}\n\n\tprivate async promptUserConfirmation(message: string): Promise<boolean> {\n\t\treturn this.prompter(message)\n\t}\n\n\t// Allow setting strategy at runtime for specific operations\n\tpublic setDefaultBranchNameStrategy(strategy: BranchNameStrategy): void {\n\t\tthis.defaultBranchNameStrategy = strategy\n\t}\n\n\t// Get current strategy for testing\n\tpublic getBranchNameStrategy(): BranchNameStrategy {\n\t\treturn this.defaultBranchNameStrategy\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger
4
+ } from "./chunk-GEHQXLEI.js";
5
+
6
+ // src/utils/update-notifier.ts
7
+ import os from "os";
8
+ import path from "path";
9
+ import fs from "fs-extra";
10
+ import { execa } from "execa";
11
+ import chalk from "chalk";
12
+ var UpdateNotifier = class {
13
+ constructor(currentVersion, packageName) {
14
+ this.currentVersion = currentVersion;
15
+ this.packageName = packageName;
16
+ const configDir = path.join(os.homedir(), ".config", "iloom-ai");
17
+ this.cacheFilePath = path.join(configDir, "update-check.json");
18
+ logger.debug(`UpdateNotifier initialized: version=${currentVersion}, package=${packageName}, cachePath=${this.cacheFilePath}`);
19
+ }
20
+ /**
21
+ * Check for updates, respecting 24hr cache
22
+ * Returns UpdateCheckResult or null if check failed/not needed
23
+ */
24
+ async checkForUpdates() {
25
+ logger.debug("checkForUpdates: Starting update check");
26
+ try {
27
+ logger.debug("checkForUpdates: Checking cache");
28
+ const cached = await this.getCachedCheck();
29
+ if (cached !== null) {
30
+ logger.debug(`checkForUpdates: Using cached result - latest=${cached.latestVersion}, lastCheck=${new Date(cached.lastCheck).toISOString()}`);
31
+ const updateAvailable2 = this.isNewerVersion(this.currentVersion, cached.latestVersion);
32
+ logger.debug(`checkForUpdates: Update available from cache: ${updateAvailable2}`);
33
+ return {
34
+ currentVersion: this.currentVersion,
35
+ latestVersion: cached.latestVersion,
36
+ updateAvailable: updateAvailable2
37
+ };
38
+ }
39
+ logger.debug("checkForUpdates: No valid cache, querying npm registry");
40
+ const latestVersion = await this.fetchLatestVersion();
41
+ if (latestVersion === null) {
42
+ logger.debug("checkForUpdates: Failed to fetch latest version from npm");
43
+ return null;
44
+ }
45
+ logger.debug(`checkForUpdates: Fetched latest version: ${latestVersion}`);
46
+ logger.debug("checkForUpdates: Saving to cache");
47
+ await this.saveCacheFile(latestVersion);
48
+ const updateAvailable = this.isNewerVersion(this.currentVersion, latestVersion);
49
+ logger.debug(`checkForUpdates: Update available: ${updateAvailable} (current=${this.currentVersion}, latest=${latestVersion})`);
50
+ return {
51
+ currentVersion: this.currentVersion,
52
+ latestVersion,
53
+ updateAvailable
54
+ };
55
+ } catch (error) {
56
+ logger.debug(`checkForUpdates: Error during update check: ${error}`);
57
+ return null;
58
+ }
59
+ }
60
+ /**
61
+ * Read cache file, return null if stale or missing
62
+ */
63
+ async getCachedCheck() {
64
+ logger.debug(`getCachedCheck: Checking cache file at ${this.cacheFilePath}`);
65
+ try {
66
+ if (!fs.existsSync(this.cacheFilePath)) {
67
+ logger.debug("getCachedCheck: Cache file does not exist");
68
+ return null;
69
+ }
70
+ logger.debug("getCachedCheck: Cache file exists, reading contents");
71
+ const content = await fs.readFile(this.cacheFilePath, "utf8");
72
+ logger.debug(`getCachedCheck: Cache file content: ${content}`);
73
+ const cache = JSON.parse(content);
74
+ const cacheTimeoutMins = parseInt(process.env.ILOOM_UPDATE_CACHE_TIMEOUT_MINS ?? "360", 10);
75
+ const cacheTimeoutMs = cacheTimeoutMins * 60 * 1e3;
76
+ logger.debug(`getCachedCheck: Using cache timeout of ${cacheTimeoutMins} minutes`);
77
+ const now = Date.now();
78
+ const age = now - cache.lastCheck;
79
+ const ageHours = age / (60 * 60 * 1e3);
80
+ logger.debug(`getCachedCheck: Cache age: ${ageHours.toFixed(2)} hours (threshold: ${cacheTimeoutMins / 60} hours)`);
81
+ if (now - cache.lastCheck < cacheTimeoutMs) {
82
+ logger.debug("getCachedCheck: Cache is fresh, returning cached data");
83
+ return cache;
84
+ }
85
+ logger.debug("getCachedCheck: Cache is stale, will query npm registry");
86
+ return null;
87
+ } catch (error) {
88
+ logger.debug(`getCachedCheck: Error reading cache: ${error}`);
89
+ return null;
90
+ }
91
+ }
92
+ /**
93
+ * Save successful check to cache
94
+ */
95
+ async saveCacheFile(latestVersion) {
96
+ logger.debug(`saveCacheFile: Attempting to save cache for version ${latestVersion}`);
97
+ try {
98
+ const configDir = path.dirname(this.cacheFilePath);
99
+ logger.debug(`saveCacheFile: Ensuring cache directory exists: ${configDir}`);
100
+ await fs.ensureDir(configDir);
101
+ const cache = {
102
+ lastCheck: Date.now(),
103
+ latestVersion
104
+ };
105
+ const cacheJson = JSON.stringify(cache, null, 2);
106
+ logger.debug(`saveCacheFile: Writing cache file: ${cacheJson}`);
107
+ await fs.writeFile(this.cacheFilePath, cacheJson, "utf8");
108
+ logger.debug(`saveCacheFile: Cache file saved successfully to ${this.cacheFilePath}`);
109
+ } catch (error) {
110
+ logger.debug(`saveCacheFile: Failed to save update check cache to ${this.cacheFilePath}: ${error}`);
111
+ }
112
+ }
113
+ /**
114
+ * Display update notification to user
115
+ */
116
+ displayUpdateNotification(result) {
117
+ logger.debug(`displayUpdateNotification: updateAvailable=${result.updateAvailable}, current=${result.currentVersion}, latest=${result.latestVersion}`);
118
+ if (result.updateAvailable) {
119
+ logger.debug("displayUpdateNotification: Displaying update notification to user");
120
+ console.log("");
121
+ console.log(" " + chalk.bold(`Update available: ${result.currentVersion} \u2192 ${result.latestVersion}`));
122
+ console.log(" " + chalk.bold("Run: il update"));
123
+ console.log("");
124
+ } else {
125
+ logger.debug("displayUpdateNotification: No update available, skipping notification");
126
+ }
127
+ }
128
+ /**
129
+ * Query npm registry for latest version
130
+ */
131
+ async fetchLatestVersion() {
132
+ logger.debug(`fetchLatestVersion: Querying npm for package ${this.packageName}`);
133
+ try {
134
+ const { stdout } = await execa("npm", ["view", this.packageName, "version"], {
135
+ timeout: 5e3
136
+ });
137
+ const version = stdout.trim();
138
+ logger.debug(`fetchLatestVersion: npm returned version: ${version}`);
139
+ return version;
140
+ } catch (error) {
141
+ logger.debug(`fetchLatestVersion: Failed to query npm: ${error}`);
142
+ return null;
143
+ }
144
+ }
145
+ /**
146
+ * Compare semver versions
147
+ * Returns true if latest > current
148
+ */
149
+ isNewerVersion(current, latest) {
150
+ logger.debug(`isNewerVersion: Comparing versions - current=${current}, latest=${latest}`);
151
+ try {
152
+ const currentParts = current.split(".").map((p) => parseInt(p, 10));
153
+ const latestParts = latest.split(".").map((p) => parseInt(p, 10));
154
+ logger.debug(`isNewerVersion: Parsed parts - current=[${currentParts.join(", ")}], latest=[${latestParts.join(", ")}]`);
155
+ for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
156
+ const curr = currentParts[i] ?? 0;
157
+ const next = latestParts[i] ?? 0;
158
+ logger.debug(`isNewerVersion: Comparing part ${i}: current=${curr}, latest=${next}`);
159
+ if (next > curr) {
160
+ logger.debug(`isNewerVersion: Latest is newer (${next} > ${curr})`);
161
+ return true;
162
+ }
163
+ if (next < curr) {
164
+ logger.debug(`isNewerVersion: Current is newer (${curr} > ${next})`);
165
+ return false;
166
+ }
167
+ }
168
+ logger.debug("isNewerVersion: Versions are equal");
169
+ return false;
170
+ } catch (error) {
171
+ logger.debug(`isNewerVersion: Error comparing versions: ${error}`);
172
+ return false;
173
+ }
174
+ }
175
+ };
176
+ async function checkAndNotifyUpdate(currentVersion, packageName, installMethod) {
177
+ logger.debug(`checkAndNotifyUpdate: Called with version=${currentVersion}, package=${packageName}, installMethod=${installMethod}`);
178
+ try {
179
+ if (installMethod !== "global") {
180
+ logger.debug(`checkAndNotifyUpdate: Skipping update check - not a global installation (method=${installMethod})`);
181
+ return;
182
+ }
183
+ logger.debug("checkAndNotifyUpdate: Creating UpdateNotifier instance");
184
+ const notifier = new UpdateNotifier(currentVersion, packageName);
185
+ logger.debug("checkAndNotifyUpdate: Calling checkForUpdates()");
186
+ const result = await notifier.checkForUpdates();
187
+ if (result !== null) {
188
+ logger.debug(`checkAndNotifyUpdate: Got result, calling displayUpdateNotification`);
189
+ notifier.displayUpdateNotification(result);
190
+ } else {
191
+ logger.debug("checkAndNotifyUpdate: Result was null, not displaying notification");
192
+ }
193
+ logger.debug("checkAndNotifyUpdate: Completed");
194
+ } catch (error) {
195
+ logger.debug(`checkAndNotifyUpdate: Unexpected error: ${error}`);
196
+ }
197
+ }
198
+
199
+ export {
200
+ UpdateNotifier,
201
+ checkAndNotifyUpdate
202
+ };
203
+ //# sourceMappingURL=chunk-U3WU5OWO.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 ?? '360', 10) // Default 6 hours\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":"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","names":["updateAvailable"]}