repowise 0.1.55 → 0.1.57

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 (82) hide show
  1. package/dist/bin/repowise.js +1547 -1712
  2. package/package.json +5 -3
  3. package/dist/src/commands/config.d.ts +0 -2
  4. package/dist/src/commands/config.d.ts.map +0 -1
  5. package/dist/src/commands/config.js +0 -95
  6. package/dist/src/commands/config.js.map +0 -1
  7. package/dist/src/commands/create.d.ts +0 -2
  8. package/dist/src/commands/create.d.ts.map +0 -1
  9. package/dist/src/commands/create.js +0 -309
  10. package/dist/src/commands/create.js.map +0 -1
  11. package/dist/src/commands/listen.d.ts +0 -5
  12. package/dist/src/commands/listen.d.ts.map +0 -1
  13. package/dist/src/commands/listen.js +0 -47
  14. package/dist/src/commands/listen.js.map +0 -1
  15. package/dist/src/commands/login.d.ts +0 -5
  16. package/dist/src/commands/login.d.ts.map +0 -1
  17. package/dist/src/commands/login.js +0 -58
  18. package/dist/src/commands/login.js.map +0 -1
  19. package/dist/src/commands/logout.d.ts +0 -2
  20. package/dist/src/commands/logout.d.ts.map +0 -1
  21. package/dist/src/commands/logout.js +0 -12
  22. package/dist/src/commands/logout.js.map +0 -1
  23. package/dist/src/commands/start.d.ts +0 -2
  24. package/dist/src/commands/start.d.ts.map +0 -1
  25. package/dist/src/commands/start.js +0 -17
  26. package/dist/src/commands/start.js.map +0 -1
  27. package/dist/src/commands/status.d.ts +0 -2
  28. package/dist/src/commands/status.d.ts.map +0 -1
  29. package/dist/src/commands/status.js +0 -63
  30. package/dist/src/commands/status.js.map +0 -1
  31. package/dist/src/commands/stop.d.ts +0 -2
  32. package/dist/src/commands/stop.d.ts.map +0 -1
  33. package/dist/src/commands/stop.js +0 -17
  34. package/dist/src/commands/stop.js.map +0 -1
  35. package/dist/src/commands/sync.d.ts +0 -2
  36. package/dist/src/commands/sync.d.ts.map +0 -1
  37. package/dist/src/commands/sync.js +0 -205
  38. package/dist/src/commands/sync.js.map +0 -1
  39. package/dist/src/lib/ai-tools.d.ts +0 -23
  40. package/dist/src/lib/ai-tools.d.ts.map +0 -1
  41. package/dist/src/lib/ai-tools.js +0 -193
  42. package/dist/src/lib/ai-tools.js.map +0 -1
  43. package/dist/src/lib/api.d.ts +0 -2
  44. package/dist/src/lib/api.d.ts.map +0 -1
  45. package/dist/src/lib/api.js +0 -38
  46. package/dist/src/lib/api.js.map +0 -1
  47. package/dist/src/lib/auth.d.ts +0 -28
  48. package/dist/src/lib/auth.d.ts.map +0 -1
  49. package/dist/src/lib/auth.js +0 -271
  50. package/dist/src/lib/auth.js.map +0 -1
  51. package/dist/src/lib/config.d.ts +0 -15
  52. package/dist/src/lib/config.d.ts.map +0 -1
  53. package/dist/src/lib/config.js +0 -19
  54. package/dist/src/lib/config.js.map +0 -1
  55. package/dist/src/lib/env.d.ts +0 -10
  56. package/dist/src/lib/env.d.ts.map +0 -1
  57. package/dist/src/lib/env.js +0 -26
  58. package/dist/src/lib/env.js.map +0 -1
  59. package/dist/src/lib/interview-handler.d.ts +0 -2
  60. package/dist/src/lib/interview-handler.d.ts.map +0 -1
  61. package/dist/src/lib/interview-handler.js +0 -100
  62. package/dist/src/lib/interview-handler.js.map +0 -1
  63. package/dist/src/lib/progress-renderer.d.ts +0 -84
  64. package/dist/src/lib/progress-renderer.d.ts.map +0 -1
  65. package/dist/src/lib/progress-renderer.js +0 -388
  66. package/dist/src/lib/progress-renderer.js.map +0 -1
  67. package/dist/src/lib/prompts.d.ts +0 -7
  68. package/dist/src/lib/prompts.d.ts.map +0 -1
  69. package/dist/src/lib/prompts.js +0 -33
  70. package/dist/src/lib/prompts.js.map +0 -1
  71. package/dist/src/lib/welcome.d.ts +0 -6
  72. package/dist/src/lib/welcome.d.ts.map +0 -1
  73. package/dist/src/lib/welcome.js +0 -42
  74. package/dist/src/lib/welcome.js.map +0 -1
  75. package/dist/src/types/index.d.ts +0 -4
  76. package/dist/src/types/index.d.ts.map +0 -1
  77. package/dist/src/types/index.js +0 -2
  78. package/dist/src/types/index.js.map +0 -1
  79. package/dist/tsup.config.d.ts +0 -3
  80. package/dist/tsup.config.d.ts.map +0 -1
  81. package/dist/tsup.config.js +0 -18
  82. package/dist/tsup.config.js.map +0 -1
@@ -1,80 +1,27 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
- };
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
2
 
12
- // src/lib/env.ts
13
- function setStagingMode(value) {
14
- staging = value;
15
- }
16
- function isStagingMode() {
17
- return staging;
18
- }
19
- function getEnvConfig() {
20
- return staging ? STAGING : PRODUCTION;
21
- }
22
- var staging, PRODUCTION, STAGING;
23
- var init_env = __esm({
24
- "src/lib/env.ts"() {
25
- "use strict";
26
- staging = false;
27
- PRODUCTION = {
28
- apiUrl: "https://api.repowise.ai",
29
- cognitoDomain: "auth.repowise.ai",
30
- cognitoClientId: "",
31
- // TODO: set after production Cognito deploy
32
- cognitoRegion: "us-east-1",
33
- customDomain: true
34
- };
35
- STAGING = {
36
- apiUrl: "https://staging-api.repowise.ai",
37
- cognitoDomain: "auth-staging.repowise.ai",
38
- cognitoClientId: "7h0l0dhjcb1v5erer0gaclv0q6",
39
- cognitoRegion: "us-east-1",
40
- customDomain: true
41
- };
42
- }
43
- });
3
+ // bin/repowise.ts
4
+ import { readFileSync } from "fs";
5
+ import { fileURLToPath as fileURLToPath3 } from "url";
6
+ import { dirname as dirname2, join as join15 } from "path";
7
+ import { Command } from "commander";
44
8
 
45
- // src/lib/config.ts
46
- import { readFile, writeFile, mkdir } from "fs/promises";
47
- import { homedir } from "os";
48
- import { join } from "path";
49
- async function getConfig() {
50
- try {
51
- const data = await readFile(CONFIG_PATH, "utf-8");
52
- return JSON.parse(data);
53
- } catch {
54
- return {};
55
- }
56
- }
57
- async function saveConfig(config2) {
58
- await mkdir(CONFIG_DIR, { recursive: true });
59
- await writeFile(CONFIG_PATH, JSON.stringify(config2, null, 2));
60
- }
61
- var CONFIG_DIR, CONFIG_PATH;
62
- var init_config = __esm({
63
- "src/lib/config.ts"() {
64
- "use strict";
65
- CONFIG_DIR = join(homedir(), ".repowise");
66
- CONFIG_PATH = join(CONFIG_DIR, "config.json");
67
- }
68
- });
9
+ // ../listener/dist/main.js
10
+ import { readFile as readFile4, writeFile as writeFile4, unlink, mkdir as mkdir4 } from "fs/promises";
11
+ import { homedir as homedir4 } from "os";
12
+ import { join as join6 } from "path";
69
13
 
70
14
  // ../listener/dist/lib/config.js
71
- import { readFile as readFile2 } from "fs/promises";
72
- import { homedir as homedir2 } from "os";
73
- import { join as join2 } from "path";
15
+ import { readFile } from "fs/promises";
16
+ import { homedir } from "os";
17
+ import { join } from "path";
18
+ var CONFIG_DIR = join(homedir(), ".repowise");
19
+ var CONFIG_PATH = join(CONFIG_DIR, "config.json");
20
+ var DEFAULT_API_URL = "https://api.repowise.ai";
74
21
  async function getListenerConfig() {
75
22
  const apiUrl = process.env["REPOWISE_API_URL"] ?? DEFAULT_API_URL;
76
23
  try {
77
- const data = await readFile2(CONFIG_PATH2, "utf-8");
24
+ const data = await readFile(CONFIG_PATH, "utf-8");
78
25
  const raw = JSON.parse(data);
79
26
  const validRepos = (raw.repos ?? []).filter((r) => typeof r === "object" && r !== null && typeof r.repoId === "string" && typeof r.localPath === "string");
80
27
  return {
@@ -85,26 +32,19 @@ async function getListenerConfig() {
85
32
  return { defaultApiUrl: apiUrl, repos: [] };
86
33
  }
87
34
  }
88
- var CONFIG_DIR2, CONFIG_PATH2, DEFAULT_API_URL;
89
- var init_config2 = __esm({
90
- "../listener/dist/lib/config.js"() {
91
- "use strict";
92
- CONFIG_DIR2 = join2(homedir2(), ".repowise");
93
- CONFIG_PATH2 = join2(CONFIG_DIR2, "config.json");
94
- DEFAULT_API_URL = "https://api.repowise.ai";
95
- }
96
- });
97
35
 
98
36
  // ../listener/dist/lib/state.js
99
- import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, chmod } from "fs/promises";
100
- import { homedir as homedir3 } from "os";
101
- import { join as join3 } from "path";
37
+ import { readFile as readFile2, writeFile, mkdir, chmod } from "fs/promises";
38
+ import { homedir as homedir2 } from "os";
39
+ import { join as join2 } from "path";
40
+ var CONFIG_DIR2 = join2(homedir2(), ".repowise");
41
+ var STATE_PATH = join2(CONFIG_DIR2, "listener-state.json");
102
42
  function emptyState() {
103
43
  return { repos: {} };
104
44
  }
105
45
  async function loadState() {
106
46
  try {
107
- const data = await readFile3(STATE_PATH, "utf-8");
47
+ const data = await readFile2(STATE_PATH, "utf-8");
108
48
  return JSON.parse(data);
109
49
  } catch (err) {
110
50
  if (err.code === "ENOENT" || err instanceof SyntaxError) {
@@ -114,23 +54,17 @@ async function loadState() {
114
54
  }
115
55
  }
116
56
  async function saveState(state) {
117
- await mkdir2(CONFIG_DIR3, { recursive: true, mode: 448 });
118
- await writeFile2(STATE_PATH, JSON.stringify(state, null, 2));
57
+ await mkdir(CONFIG_DIR2, { recursive: true, mode: 448 });
58
+ await writeFile(STATE_PATH, JSON.stringify(state, null, 2));
119
59
  await chmod(STATE_PATH, 384);
120
60
  }
121
- var CONFIG_DIR3, STATE_PATH;
122
- var init_state = __esm({
123
- "../listener/dist/lib/state.js"() {
124
- "use strict";
125
- CONFIG_DIR3 = join3(homedir3(), ".repowise");
126
- STATE_PATH = join3(CONFIG_DIR3, "listener-state.json");
127
- }
128
- });
129
61
 
130
62
  // ../listener/dist/lib/auth.js
131
- import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
132
- import { homedir as homedir4 } from "os";
133
- import { join as join4 } from "path";
63
+ import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
64
+ import { homedir as homedir3 } from "os";
65
+ import { join as join3 } from "path";
66
+ var CONFIG_DIR3 = join3(homedir3(), ".repowise");
67
+ var CREDENTIALS_PATH = join3(CONFIG_DIR3, "credentials.json");
134
68
  function getTokenUrl(creds) {
135
69
  const cognito = creds?.cognito;
136
70
  const domain = process.env["REPOWISE_COGNITO_DOMAIN"] ?? cognito?.domain ?? "auth-repowise-dev";
@@ -169,7 +103,7 @@ async function refreshTokens(refreshToken, creds) {
169
103
  }
170
104
  async function getStoredCredentials() {
171
105
  try {
172
- const data = await readFile4(CREDENTIALS_PATH, "utf-8");
106
+ const data = await readFile3(CREDENTIALS_PATH, "utf-8");
173
107
  return JSON.parse(data);
174
108
  } catch (err) {
175
109
  if (err.code === "ENOENT" || err instanceof SyntaxError) {
@@ -179,8 +113,8 @@ async function getStoredCredentials() {
179
113
  }
180
114
  }
181
115
  async function storeCredentials(credentials) {
182
- await mkdir3(CONFIG_DIR4, { recursive: true, mode: 448 });
183
- await writeFile3(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
116
+ await mkdir2(CONFIG_DIR3, { recursive: true, mode: 448 });
117
+ await writeFile2(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
184
118
  await chmod2(CREDENTIALS_PATH, 384);
185
119
  }
186
120
  async function getValidCredentials() {
@@ -198,102 +132,83 @@ async function getValidCredentials() {
198
132
  }
199
133
  return creds;
200
134
  }
201
- var CONFIG_DIR4, CREDENTIALS_PATH;
202
- var init_auth = __esm({
203
- "../listener/dist/lib/auth.js"() {
204
- "use strict";
205
- CONFIG_DIR4 = join4(homedir4(), ".repowise");
206
- CREDENTIALS_PATH = join4(CONFIG_DIR4, "credentials.json");
207
- }
208
- });
209
135
 
210
136
  // ../listener/dist/poll-client.js
211
- var POLL_TIMEOUT_MS, AuthError, PollClient;
212
- var init_poll_client = __esm({
213
- "../listener/dist/poll-client.js"() {
214
- "use strict";
215
- init_auth();
216
- POLL_TIMEOUT_MS = 3e4;
217
- AuthError = class extends Error {
218
- constructor(message) {
219
- super(message);
220
- this.name = "AuthError";
221
- }
222
- };
223
- PollClient = class {
224
- apiUrl;
225
- constructor(apiUrl) {
226
- this.apiUrl = apiUrl;
137
+ var POLL_TIMEOUT_MS = 3e4;
138
+ var AuthError = class extends Error {
139
+ constructor(message) {
140
+ super(message);
141
+ this.name = "AuthError";
142
+ }
143
+ };
144
+ var PollClient = class {
145
+ apiUrl;
146
+ constructor(apiUrl) {
147
+ this.apiUrl = apiUrl;
148
+ }
149
+ async poll(repoIds, since) {
150
+ const credentials = await getValidCredentials();
151
+ if (!credentials) {
152
+ throw new AuthError("Not logged in. Run `repowise login` first.");
153
+ }
154
+ const params = new URLSearchParams({
155
+ repoIds: repoIds.join(","),
156
+ since
157
+ });
158
+ const controller = new AbortController();
159
+ const timeoutId = setTimeout(() => controller.abort(), POLL_TIMEOUT_MS);
160
+ try {
161
+ const response = await fetch(`${this.apiUrl}/v1/listeners/poll?${params.toString()}`, {
162
+ headers: {
163
+ Authorization: `Bearer ${credentials.accessToken}`,
164
+ "Content-Type": "application/json"
165
+ },
166
+ signal: controller.signal
167
+ });
168
+ if (response.status === 401 || response.status === 403) {
169
+ throw new AuthError("Session expired. Run `repowise login` again.");
227
170
  }
228
- async poll(repoIds, since) {
229
- const credentials = await getValidCredentials();
230
- if (!credentials) {
231
- throw new AuthError("Not logged in. Run `repowise login` first.");
232
- }
233
- const params = new URLSearchParams({
234
- repoIds: repoIds.join(","),
235
- since
236
- });
237
- const controller = new AbortController();
238
- const timeoutId = setTimeout(() => controller.abort(), POLL_TIMEOUT_MS);
239
- try {
240
- const response = await fetch(`${this.apiUrl}/v1/listeners/poll?${params.toString()}`, {
241
- headers: {
242
- Authorization: `Bearer ${credentials.accessToken}`,
243
- "Content-Type": "application/json"
244
- },
245
- signal: controller.signal
246
- });
247
- if (response.status === 401) {
248
- throw new AuthError("Session expired. Run `repowise login` again.");
249
- }
250
- if (!response.ok) {
251
- throw new Error(`Poll request failed: ${response.status}`);
252
- }
253
- const json = await response.json();
254
- return json.data;
255
- } finally {
256
- clearTimeout(timeoutId);
257
- }
171
+ if (!response.ok) {
172
+ throw new Error(`Poll request failed: ${response.status}`);
258
173
  }
259
- };
174
+ const json = await response.json();
175
+ return json.data;
176
+ } finally {
177
+ clearTimeout(timeoutId);
178
+ }
260
179
  }
261
- });
180
+ };
262
181
 
263
182
  // ../listener/dist/reconnection.js
264
- var DEFAULT_CONFIG, BackoffCalculator;
265
- var init_reconnection = __esm({
266
- "../listener/dist/reconnection.js"() {
267
- "use strict";
268
- DEFAULT_CONFIG = {
269
- initialDelay: 1e3,
270
- maxDelay: 6e4,
271
- jitterMax: 1e3
272
- };
273
- BackoffCalculator = class {
274
- config;
275
- attempt = 0;
276
- constructor(config2 = {}) {
277
- this.config = { ...DEFAULT_CONFIG, ...config2 };
278
- }
279
- nextDelay() {
280
- const baseDelay = Math.min(this.config.initialDelay * Math.pow(2, this.attempt), this.config.maxDelay);
281
- const jitter = Math.random() * this.config.jitterMax;
282
- this.attempt++;
283
- return baseDelay + jitter;
284
- }
285
- reset() {
286
- this.attempt = 0;
287
- }
288
- getAttempt() {
289
- return this.attempt;
290
- }
291
- };
183
+ var DEFAULT_CONFIG = {
184
+ initialDelay: 1e3,
185
+ maxDelay: 6e4,
186
+ jitterMax: 1e3
187
+ };
188
+ var BackoffCalculator = class {
189
+ config;
190
+ attempt = 0;
191
+ constructor(config2 = {}) {
192
+ this.config = { ...DEFAULT_CONFIG, ...config2 };
292
193
  }
293
- });
194
+ nextDelay() {
195
+ const baseDelay = Math.min(this.config.initialDelay * Math.pow(2, this.attempt), this.config.maxDelay);
196
+ const jitter = Math.random() * this.config.jitterMax;
197
+ this.attempt++;
198
+ return baseDelay + jitter;
199
+ }
200
+ reset() {
201
+ this.attempt = 0;
202
+ }
203
+ getAttempt() {
204
+ return this.attempt;
205
+ }
206
+ };
294
207
 
295
208
  // ../listener/dist/notification.js
296
209
  import notifier from "node-notifier";
210
+ var TITLE = "RepoWise";
211
+ var notify = notifier.notify.bind(notifier);
297
212
  function notifyConnectionLost() {
298
213
  try {
299
214
  notify({
@@ -321,37 +236,27 @@ function notifyContextUpdated(repoId, fileCount) {
321
236
  } catch {
322
237
  }
323
238
  }
324
- var TITLE, notify;
325
- var init_notification = __esm({
326
- "../listener/dist/notification.js"() {
327
- "use strict";
328
- TITLE = "RepoWise";
329
- notify = notifier.notify.bind(notifier);
330
- }
331
- });
239
+
240
+ // ../listener/dist/context-fetcher.js
241
+ import { execFile } from "child_process";
242
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
243
+ import { join as join5 } from "path";
244
+ import { promisify } from "util";
332
245
 
333
246
  // ../listener/dist/file-writer.js
334
247
  import { access } from "fs/promises";
335
- import { join as join5 } from "path";
248
+ import { join as join4 } from "path";
336
249
  async function verifyContextFolder(localPath) {
337
250
  try {
338
- await access(join5(localPath, "repowise-context"));
251
+ await access(join4(localPath, "repowise-context"));
339
252
  return true;
340
253
  } catch {
341
254
  return false;
342
255
  }
343
256
  }
344
- var init_file_writer = __esm({
345
- "../listener/dist/file-writer.js"() {
346
- "use strict";
347
- }
348
- });
349
257
 
350
258
  // ../listener/dist/context-fetcher.js
351
- import { execFile } from "child_process";
352
- import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
353
- import { join as join6 } from "path";
354
- import { promisify } from "util";
259
+ var execFileAsync = promisify(execFile);
355
260
  async function fetchContextUpdates(localPath) {
356
261
  try {
357
262
  const { stdout: beforeSha } = await execFileAsync("git", [
@@ -389,39 +294,75 @@ async function fetchContextUpdates(localPath) {
389
294
  return { success: false, updatedFiles: [] };
390
295
  }
391
296
  }
392
- async function fetchContextFromServer(repoId, localPath, apiUrl, authToken) {
297
+ function delay(ms) {
298
+ return new Promise((resolve) => setTimeout(resolve, ms));
299
+ }
300
+ async function fetchContextFromServer(repoId, localPath, apiUrl) {
393
301
  try {
394
- const headers = { Authorization: `Bearer ${authToken}`, "Content-Type": "application/json" };
395
- const listRes = await fetch(`${apiUrl}/v1/repos/${repoId}/context`, { headers });
396
- if (!listRes.ok) {
397
- console.error(`Context list failed (${listRes.status}) for repo ${repoId}`);
302
+ const credentials = await getValidCredentials();
303
+ if (!credentials) {
304
+ console.error(`Context fetch for ${repoId}: no valid credentials`);
305
+ return { success: false, updatedFiles: [] };
306
+ }
307
+ const headers = {
308
+ Authorization: `Bearer ${credentials.accessToken}`,
309
+ "Content-Type": "application/json"
310
+ };
311
+ let listRes;
312
+ for (let attempt = 1; attempt <= 3; attempt++) {
313
+ try {
314
+ listRes = await fetch(`${apiUrl}/v1/repos/${repoId}/context`, { headers });
315
+ if (listRes.status === 401 || listRes.status === 403) {
316
+ console.error(`Context list auth error (${listRes.status}) for repo ${repoId}`);
317
+ return { success: false, updatedFiles: [] };
318
+ }
319
+ if (listRes.ok)
320
+ break;
321
+ console.error(`Context list failed (${listRes.status}) for repo ${repoId}, attempt ${attempt}/3`);
322
+ } catch (err) {
323
+ const msg = err instanceof Error ? err.message : String(err);
324
+ console.error(`Context list network error for repo ${repoId}, attempt ${attempt}/3: ${msg}`);
325
+ }
326
+ if (attempt < 3)
327
+ await delay(2e3);
328
+ }
329
+ if (!listRes || !listRes.ok) {
330
+ console.error(`Context list failed after 3 attempts for repo ${repoId}`);
398
331
  return { success: false, updatedFiles: [] };
399
332
  }
400
333
  const listData = await listRes.json();
401
334
  const files = listData.data?.files ?? [];
335
+ console.log(`Context fetch for ${repoId}: ${files.length} file(s) available`);
402
336
  if (files.length === 0) {
403
337
  return { success: true, updatedFiles: [] };
404
338
  }
405
- const contextDir = join6(localPath, "repowise-context");
406
- await mkdir4(contextDir, { recursive: true });
339
+ const contextDir = join5(localPath, "repowise-context");
340
+ await mkdir3(contextDir, { recursive: true });
407
341
  const updatedFiles = [];
408
342
  for (const file of files) {
409
343
  const urlRes = await fetch(`${apiUrl}/v1/repos/${repoId}/context/${file.fileName}`, {
410
344
  headers
411
345
  });
412
- if (!urlRes.ok)
346
+ if (!urlRes.ok) {
347
+ console.error(`Context fetch for ${repoId}: failed to get URL for ${file.fileName} (${urlRes.status})`);
413
348
  continue;
349
+ }
414
350
  const urlData = await urlRes.json();
415
351
  const presignedUrl = urlData.data?.url;
416
- if (!presignedUrl)
352
+ if (!presignedUrl) {
353
+ console.error(`Context fetch for ${repoId}: no presigned URL returned for ${file.fileName}`);
417
354
  continue;
355
+ }
418
356
  const contentRes = await fetch(presignedUrl);
419
- if (!contentRes.ok)
357
+ if (!contentRes.ok) {
358
+ console.error(`Context fetch for ${repoId}: download failed for ${file.fileName} (${contentRes.status})`);
420
359
  continue;
360
+ }
421
361
  const content = await contentRes.text();
422
- await writeFile4(join6(contextDir, file.fileName), content, "utf-8");
362
+ await writeFile3(join5(contextDir, file.fileName), content, "utf-8");
423
363
  updatedFiles.push(file.fileName);
424
364
  }
365
+ console.log(`Context fetch for ${repoId}: downloaded ${updatedFiles.length}/${files.length} file(s)`);
425
366
  return { success: true, updatedFiles };
426
367
  } catch (err) {
427
368
  const message = err instanceof Error ? err.message : "Unknown error";
@@ -429,27 +370,21 @@ async function fetchContextFromServer(repoId, localPath, apiUrl, authToken) {
429
370
  return { success: false, updatedFiles: [] };
430
371
  }
431
372
  }
432
- var execFileAsync;
433
- var init_context_fetcher = __esm({
434
- "../listener/dist/context-fetcher.js"() {
435
- "use strict";
436
- init_file_writer();
437
- execFileAsync = promisify(execFile);
438
- }
439
- });
440
373
 
441
374
  // ../listener/dist/main.js
442
- var main_exports = {};
443
- __export(main_exports, {
444
- startListener: () => startListener,
445
- stop: () => stop
446
- });
447
- import { readFile as readFile5, writeFile as writeFile5, unlink, mkdir as mkdir5 } from "fs/promises";
448
- import { homedir as homedir5 } from "os";
449
- import { join as join7 } from "path";
375
+ var origLog = console.log.bind(console);
376
+ var origError = console.error.bind(console);
377
+ var origWarn = console.warn.bind(console);
378
+ console.log = (...args) => origLog(`[${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
379
+ console.error = (...args) => origError(`[${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
380
+ console.warn = (...args) => origWarn(`[${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
381
+ var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
382
+ var PID_PATH = join6(homedir4(), ".repowise", "listener.pid");
383
+ var running = false;
384
+ var sleepResolve = null;
450
385
  async function writePidFile() {
451
- await mkdir5(join7(homedir5(), ".repowise"), { recursive: true });
452
- await writeFile5(PID_PATH, String(process.pid));
386
+ await mkdir4(join6(homedir4(), ".repowise"), { recursive: true });
387
+ await writeFile4(PID_PATH, String(process.pid));
453
388
  }
454
389
  async function removePidFile() {
455
390
  try {
@@ -459,7 +394,7 @@ async function removePidFile() {
459
394
  }
460
395
  async function handleStalePid() {
461
396
  try {
462
- const content = await readFile5(PID_PATH, "utf-8");
397
+ const content = await readFile4(PID_PATH, "utf-8");
463
398
  const pid = parseInt(content.trim(), 10);
464
399
  if (!Number.isNaN(pid) && pid !== process.pid) {
465
400
  try {
@@ -490,32 +425,52 @@ function sleep(ms) {
490
425
  }, ms);
491
426
  });
492
427
  }
493
- async function processNotifications(notifications, state, repoLocalPaths, apiUrl, authToken) {
428
+ function decodeEmailFromIdToken(idToken) {
429
+ try {
430
+ const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString());
431
+ return typeof payload.email === "string" ? payload.email : null;
432
+ } catch {
433
+ return null;
434
+ }
435
+ }
436
+ async function processNotifications(notifications, state, repoLocalPaths, apiUrl) {
494
437
  let updateCount = 0;
495
438
  for (const notif of notifications) {
496
439
  if (notif.type === "sync.completed") {
497
440
  const localPath = repoLocalPaths.get(notif.repoId);
498
441
  if (localPath) {
499
442
  let result;
500
- if (apiUrl && authToken) {
501
- result = await fetchContextFromServer(notif.repoId, localPath, apiUrl, authToken);
443
+ if (apiUrl) {
444
+ result = await fetchContextFromServer(notif.repoId, localPath, apiUrl);
502
445
  } else {
503
446
  result = await fetchContextUpdates(localPath);
504
447
  }
505
448
  if (result.success) {
506
449
  updateCount++;
507
450
  notifyContextUpdated(notif.repoId, result.updatedFiles.length);
451
+ state.repos[notif.repoId] = {
452
+ lastSyncTimestamp: notif.createdAt,
453
+ lastSyncCommitSha: notif.commitSha
454
+ };
455
+ } else {
456
+ console.error(`Download failed for repo ${notif.repoId} \u2014 will retry on next poll`);
508
457
  }
458
+ } else {
459
+ state.repos[notif.repoId] = {
460
+ lastSyncTimestamp: notif.createdAt,
461
+ lastSyncCommitSha: notif.commitSha
462
+ };
509
463
  }
464
+ } else {
465
+ state.repos[notif.repoId] = {
466
+ lastSyncTimestamp: notif.createdAt,
467
+ lastSyncCommitSha: notif.commitSha
468
+ };
510
469
  }
511
- state.repos[notif.repoId] = {
512
- lastSyncTimestamp: notif.createdAt,
513
- lastSyncCommitSha: notif.commitSha
514
- };
515
470
  }
516
471
  return updateCount;
517
472
  }
518
- async function handleCatchUp(offlineState, pollClient, repoIds, state, repoLocalPaths, apiUrl, authToken) {
473
+ async function handleCatchUp(offlineState, pollClient, repoIds, state, repoLocalPaths, apiUrl) {
519
474
  if (!offlineState.offlineSince)
520
475
  return;
521
476
  const offlineDuration = Date.now() - new Date(offlineState.offlineSince).getTime();
@@ -523,8 +478,8 @@ async function handleCatchUp(offlineState, pollClient, repoIds, state, repoLocal
523
478
  let syncCount = 0;
524
479
  for (const [repoId, localPath] of repoLocalPaths) {
525
480
  let result;
526
- if (apiUrl && authToken) {
527
- result = await fetchContextFromServer(repoId, localPath, apiUrl, authToken);
481
+ if (apiUrl) {
482
+ result = await fetchContextFromServer(repoId, localPath, apiUrl);
528
483
  } else {
529
484
  result = await fetchContextUpdates(localPath);
530
485
  }
@@ -535,7 +490,7 @@ async function handleCatchUp(offlineState, pollClient, repoIds, state, repoLocal
535
490
  } else {
536
491
  const sinceTimestamp = offlineState.offlineSince;
537
492
  const response = await pollClient.poll(repoIds, sinceTimestamp);
538
- const updateCount = await processNotifications(response.notifications, state, repoLocalPaths, apiUrl, authToken);
493
+ const updateCount = await processNotifications(response.notifications, state, repoLocalPaths, apiUrl);
539
494
  await saveState(state);
540
495
  notifyBackOnline(updateCount);
541
496
  }
@@ -598,9 +553,8 @@ async function startListener(options) {
598
553
  process.on("SIGTERM", () => void shutdown());
599
554
  process.on("SIGINT", () => void shutdown());
600
555
  while (running) {
601
- const freshCredentials = await getValidCredentials();
602
- const authToken = freshCredentials?.accessToken;
603
556
  let anyAuthError = null;
557
+ let authErrorGroup = null;
604
558
  let minPollInterval = pollIntervalMs;
605
559
  let connectionLostNotified = false;
606
560
  for (const group of groups) {
@@ -616,14 +570,14 @@ async function startListener(options) {
616
570
  }, now);
617
571
  const response = await group.pollClient.poll(group.repoIds, sinceTimestamp);
618
572
  if (group.offline.isOffline) {
619
- await handleCatchUp(group.offline, group.pollClient, group.repoIds, state, group.repoLocalPaths, group.apiUrl, authToken);
573
+ await handleCatchUp(group.offline, group.pollClient, group.repoIds, state, group.repoLocalPaths, group.apiUrl);
620
574
  group.offline.isOffline = false;
621
575
  group.offline.offlineSince = null;
622
576
  group.offline.attemptCount = 0;
623
577
  group.offline.nextRetryAt = 0;
624
578
  group.backoff.reset();
625
579
  } else if (response.notifications.length > 0) {
626
- await processNotifications(response.notifications, state, group.repoLocalPaths, group.apiUrl, authToken);
580
+ await processNotifications(response.notifications, state, group.repoLocalPaths, group.apiUrl);
627
581
  await saveState(state);
628
582
  }
629
583
  minPollInterval = Math.min(minPollInterval, response.pollIntervalMs);
@@ -632,6 +586,7 @@ async function startListener(options) {
632
586
  break;
633
587
  if (err instanceof AuthError) {
634
588
  anyAuthError = err;
589
+ authErrorGroup = group;
635
590
  break;
636
591
  }
637
592
  if (!group.offline.isOffline) {
@@ -644,1415 +599,1453 @@ async function startListener(options) {
644
599
  }
645
600
  }
646
601
  group.offline.attemptCount++;
647
- const delay = group.backoff.nextDelay();
648
- group.offline.nextRetryAt = Date.now() + delay;
602
+ const delay2 = group.backoff.nextDelay();
603
+ group.offline.nextRetryAt = Date.now() + delay2;
649
604
  const message = err instanceof Error ? err.message : "Unknown error";
650
- console.error(`Poll failed for ${group.apiUrl} (attempt ${group.offline.attemptCount}): ${message}. Retrying in ${Math.round(delay / 1e3)}s`);
605
+ console.error(`Poll failed for ${group.apiUrl} (attempt ${group.offline.attemptCount}): ${message}. Retrying in ${Math.round(delay2 / 1e3)}s`);
651
606
  }
652
607
  }
653
608
  if (anyAuthError) {
654
- console.error(anyAuthError.message);
655
- process.exitCode = 1;
656
- break;
609
+ console.error("Session expired. Sending notification email and waiting for re-authentication...");
610
+ const creds = await getStoredCredentials();
611
+ const email = creds?.idToken ? decodeEmailFromIdToken(creds.idToken) : null;
612
+ if (email && authErrorGroup) {
613
+ try {
614
+ await fetch(`${authErrorGroup.apiUrl}/v1/public/notify-auth-expired`, {
615
+ method: "POST",
616
+ headers: { "Content-Type": "application/json" },
617
+ body: JSON.stringify({ email })
618
+ });
619
+ } catch {
620
+ }
621
+ }
622
+ while (running) {
623
+ await sleep(5 * 60 * 1e3);
624
+ if (!running)
625
+ break;
626
+ const fresh = await getValidCredentials();
627
+ if (fresh) {
628
+ console.log("Re-authenticated \u2014 resuming listener");
629
+ break;
630
+ }
631
+ }
632
+ continue;
657
633
  }
658
634
  pollIntervalMs = minPollInterval;
659
635
  await sleep(pollIntervalMs);
660
636
  }
661
637
  await removePidFile();
662
638
  }
663
- var TWENTY_FOUR_HOURS_MS, PID_PATH, running, sleepResolve, isDirectRun;
664
- var init_main = __esm({
665
- "../listener/dist/main.js"() {
666
- "use strict";
667
- init_config2();
668
- init_state();
669
- init_auth();
670
- init_poll_client();
671
- init_reconnection();
672
- init_notification();
673
- init_context_fetcher();
674
- TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
675
- PID_PATH = join7(homedir5(), ".repowise", "listener.pid");
676
- running = false;
677
- sleepResolve = null;
678
- isDirectRun = process.argv[1]?.endsWith("main.js") || process.argv[1]?.endsWith("main.ts");
679
- if (isDirectRun) {
680
- startListener().catch((err) => {
681
- console.error("Listener fatal error:", err);
682
- process.exitCode = 1;
683
- });
684
- }
685
- }
686
- });
639
+ var isDirectRun = process.argv[1]?.endsWith("main.js") || process.argv[1]?.endsWith("main.ts");
640
+ if (isDirectRun) {
641
+ startListener().catch((err) => {
642
+ console.error("Listener fatal error:", err);
643
+ process.exitCode = 1;
644
+ });
645
+ }
687
646
 
688
- // src/lib/auth.ts
689
- import { createHash, randomBytes } from "crypto";
690
- import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir6, chmod as chmod3, unlink as unlink2 } from "fs/promises";
691
- import http from "http";
692
- import { homedir as homedir6 } from "os";
693
- import { join as join8 } from "path";
694
- function getCognitoConfigForStorage() {
695
- const { domain, clientId, region, customDomain } = getCognitoConfig();
696
- return { domain, clientId, region, customDomain };
647
+ // src/lib/env.ts
648
+ var staging = false;
649
+ function setStagingMode(value) {
650
+ staging = value;
697
651
  }
698
- function getCognitoConfig() {
699
- const env = getEnvConfig();
700
- return {
701
- domain: process.env["REPOWISE_COGNITO_DOMAIN"] ?? env.cognitoDomain,
702
- clientId: process.env["REPOWISE_COGNITO_CLIENT_ID"] ?? env.cognitoClientId,
703
- region: process.env["REPOWISE_COGNITO_REGION"] ?? env.cognitoRegion,
704
- customDomain: env.customDomain
705
- };
652
+ function isStagingMode() {
653
+ return staging;
706
654
  }
707
- function getCognitoBaseUrl() {
708
- const { domain, region, customDomain } = getCognitoConfig();
709
- return customDomain ? `https://${domain}` : `https://${domain}.auth.${region}.amazoncognito.com`;
655
+ var PRODUCTION = {
656
+ apiUrl: "https://api.repowise.ai",
657
+ cognitoDomain: "auth-repowise",
658
+ cognitoClientId: "",
659
+ // TODO: set after production Cognito deploy
660
+ cognitoRegion: "us-east-1",
661
+ customDomain: false
662
+ };
663
+ var STAGING = {
664
+ apiUrl: "https://staging-api.repowise.ai",
665
+ cognitoDomain: "auth-staging.repowise.ai",
666
+ cognitoClientId: "7h0l0dhjcb1v5erer0gaclv0q6",
667
+ cognitoRegion: "us-east-1",
668
+ customDomain: true
669
+ };
670
+ function getEnvConfig() {
671
+ return staging ? STAGING : PRODUCTION;
710
672
  }
711
- function generateCodeVerifier() {
712
- return randomBytes(32).toString("base64url");
673
+
674
+ // src/lib/welcome.ts
675
+ import chalk from "chalk";
676
+
677
+ // src/lib/config.ts
678
+ import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
679
+ import { homedir as homedir5 } from "os";
680
+ import { join as join7 } from "path";
681
+ var CONFIG_DIR4 = join7(homedir5(), ".repowise");
682
+ var CONFIG_PATH2 = join7(CONFIG_DIR4, "config.json");
683
+ async function getConfig() {
684
+ try {
685
+ const data = await readFile5(CONFIG_PATH2, "utf-8");
686
+ return JSON.parse(data);
687
+ } catch {
688
+ return {};
689
+ }
713
690
  }
714
- function generateCodeChallenge(verifier) {
715
- return createHash("sha256").update(verifier).digest("base64url");
691
+ async function saveConfig(config2) {
692
+ await mkdir5(CONFIG_DIR4, { recursive: true });
693
+ await writeFile5(CONFIG_PATH2, JSON.stringify(config2, null, 2));
716
694
  }
717
- function generateState() {
718
- return randomBytes(32).toString("hex");
695
+
696
+ // src/lib/welcome.ts
697
+ var W = 41;
698
+ function row(styled, visible) {
699
+ return chalk.cyan(" \u2502") + styled + " ".repeat(W - visible) + chalk.cyan("\u2502");
719
700
  }
720
- function getAuthorizeUrl(codeChallenge, state) {
721
- const { clientId } = getCognitoConfig();
722
- if (!clientId) {
723
- throw new Error(
724
- "Missing REPOWISE_COGNITO_CLIENT_ID environment variable. Configure it before running login."
725
- );
701
+ async function showWelcome(currentVersion) {
702
+ try {
703
+ const config2 = await getConfig();
704
+ if (config2.lastSeenVersion === currentVersion) return;
705
+ const isUpgrade = !!config2.lastSeenVersion;
706
+ const tag = isUpgrade ? "updated" : "installed";
707
+ const titleText = `RepoWise v${currentVersion}`;
708
+ const titleStyled = " " + chalk.bold(titleText) + chalk.green(` \u2713 ${tag}`);
709
+ const titleVisible = 3 + titleText.length + 3 + tag.length;
710
+ const border = "\u2500".repeat(W);
711
+ console.log("");
712
+ console.log(chalk.cyan(` \u256D${border}\u256E`));
713
+ console.log(row("", 0));
714
+ console.log(row(titleStyled, titleVisible));
715
+ console.log(row("", 0));
716
+ console.log(row(" " + chalk.dim("Get started:"), 15));
717
+ console.log(row(" $ " + chalk.bold("repowise create"), 22));
718
+ console.log(row("", 0));
719
+ console.log(row(" " + chalk.dim("Thank you for using RepoWise!"), 32));
720
+ console.log(row(" " + chalk.dim("https://repowise.ai"), 22));
721
+ console.log(row("", 0));
722
+ console.log(chalk.cyan(` \u2570${border}\u256F`));
723
+ console.log("");
724
+ await saveConfig({ ...config2, lastSeenVersion: currentVersion });
725
+ } catch {
726
726
  }
727
- const params = new URLSearchParams({
728
- response_type: "code",
729
- client_id: clientId,
730
- redirect_uri: `http://localhost:${CLI_CALLBACK_PORT}/callback`,
731
- code_challenge: codeChallenge,
732
- code_challenge_method: "S256",
733
- scope: "openid email profile",
734
- state
735
- });
736
- return `${getCognitoBaseUrl()}/oauth2/authorize?${params.toString()}`;
737
- }
738
- function getTokenUrl2() {
739
- return `${getCognitoBaseUrl()}/oauth2/token`;
740
727
  }
741
- function startCallbackServer() {
742
- return new Promise((resolve, reject) => {
743
- const server = http.createServer((req, res) => {
744
- const url = new URL(req.url, `http://localhost:${CLI_CALLBACK_PORT}`);
745
- if (url.pathname !== "/callback") {
746
- res.writeHead(404);
747
- res.end();
748
- return;
749
- }
750
- const code = url.searchParams.get("code");
751
- const state = url.searchParams.get("state");
752
- const error = url.searchParams.get("error");
753
- if (error) {
754
- res.writeHead(200, { "Content-Type": "text/html" });
755
- res.end(
756
- callbackPage(
757
- "Authentication Failed",
758
- "Something went wrong. Please close this tab and try again.",
759
- true
760
- )
761
- );
762
- server.close();
763
- reject(new Error(`Authentication error: ${error}`));
764
- return;
765
- }
766
- if (!code || !state) {
767
- res.writeHead(400, { "Content-Type": "text/html" });
768
- res.end(
769
- callbackPage(
770
- "Missing Parameters",
771
- "The callback was missing required data. Please close this tab and try again.",
772
- true
773
- )
774
- );
775
- server.close();
776
- reject(new Error("Missing code or state in callback"));
777
- return;
778
- }
779
- res.writeHead(200, { "Content-Type": "text/html" });
780
- res.end(
781
- callbackPage(
782
- "Authentication Successful",
783
- "You can close this tab and return to the terminal.",
784
- false
785
- )
786
- );
787
- server.close();
788
- resolve({ code, state });
789
- });
790
- server.listen(CLI_CALLBACK_PORT, "127.0.0.1");
791
- server.on("error", (err) => {
792
- if (err.code === "EADDRINUSE") {
793
- reject(
794
- new Error(
795
- `Port ${CLI_CALLBACK_PORT} is already in use. Close the conflicting process and try again.`
796
- )
797
- );
798
- } else {
799
- reject(err);
800
- }
801
- });
802
- const timeout = setTimeout(() => {
803
- server.close();
804
- reject(new Error("Authentication timed out. Please try again."));
805
- }, CALLBACK_TIMEOUT_MS);
806
- server.on("close", () => clearTimeout(timeout));
807
- });
808
- }
809
- async function exchangeCodeForTokens(code, codeVerifier) {
810
- const response = await fetch(getTokenUrl2(), {
811
- method: "POST",
812
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
813
- body: new URLSearchParams({
814
- grant_type: "authorization_code",
815
- client_id: getCognitoConfig().clientId,
816
- redirect_uri: `http://localhost:${CLI_CALLBACK_PORT}/callback`,
817
- code,
818
- code_verifier: codeVerifier
819
- })
820
- });
821
- if (!response.ok) {
822
- const text = await response.text();
823
- throw new Error(`Token exchange failed: ${response.status} ${text}`);
824
- }
825
- const data = await response.json();
826
- return {
827
- accessToken: data.access_token,
828
- refreshToken: data.refresh_token,
829
- idToken: data.id_token,
830
- expiresAt: Date.now() + data.expires_in * 1e3
831
- };
832
- }
833
- async function refreshTokens2(refreshToken) {
834
- const response = await fetch(getTokenUrl2(), {
835
- method: "POST",
836
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
837
- body: new URLSearchParams({
838
- grant_type: "refresh_token",
839
- client_id: getCognitoConfig().clientId,
840
- refresh_token: refreshToken
841
- })
842
- });
843
- if (!response.ok) {
844
- throw new Error(`Token refresh failed: ${response.status}`);
728
+
729
+ // src/commands/create.ts
730
+ import { execSync } from "child_process";
731
+ import { mkdirSync, writeFileSync } from "fs";
732
+ import { join as join12 } from "path";
733
+
734
+ // ../listener/dist/process-manager.js
735
+ import { spawn } from "child_process";
736
+ import { openSync, closeSync } from "fs";
737
+ import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir6, unlink as unlink2 } from "fs/promises";
738
+ import { homedir as homedir6 } from "os";
739
+ import { join as join8 } from "path";
740
+ import { createRequire } from "module";
741
+ import { fileURLToPath } from "url";
742
+ var REPOWISE_DIR = join8(homedir6(), ".repowise");
743
+ var PID_PATH2 = join8(REPOWISE_DIR, "listener.pid");
744
+ var LOG_DIR = join8(REPOWISE_DIR, "logs");
745
+ function resolveListenerCommand() {
746
+ try {
747
+ const require2 = createRequire(import.meta.url);
748
+ const mainPath = require2.resolve("@repowise/listener/main");
749
+ return { script: mainPath, args: [] };
750
+ } catch {
751
+ const bundlePath = fileURLToPath(import.meta.url);
752
+ return { script: bundlePath, args: ["__listener"] };
845
753
  }
846
- const data = await response.json();
847
- return {
848
- accessToken: data.access_token,
849
- refreshToken,
850
- // Cognito does not return a new refresh token
851
- idToken: data.id_token,
852
- expiresAt: Date.now() + data.expires_in * 1e3
853
- };
854
754
  }
855
- async function getStoredCredentials2() {
755
+ async function readPid() {
856
756
  try {
857
- const data = await readFile6(CREDENTIALS_PATH2, "utf-8");
858
- return JSON.parse(data);
757
+ const content = await readFile6(PID_PATH2, "utf-8");
758
+ const pid = parseInt(content.trim(), 10);
759
+ return Number.isNaN(pid) ? null : pid;
859
760
  } catch (err) {
860
- if (err.code === "ENOENT" || err instanceof SyntaxError) {
761
+ if (err.code === "ENOENT")
861
762
  return null;
862
- }
863
763
  throw err;
864
764
  }
865
765
  }
866
- async function storeCredentials2(credentials) {
867
- await mkdir6(CONFIG_DIR5, { recursive: true, mode: 448 });
868
- await writeFile6(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2));
869
- await chmod3(CREDENTIALS_PATH2, 384);
870
- }
871
- async function clearCredentials() {
766
+ function isAlive(pid) {
872
767
  try {
873
- await unlink2(CREDENTIALS_PATH2);
874
- } catch (err) {
875
- if (err.code !== "ENOENT") throw err;
768
+ process.kill(pid, 0);
769
+ return true;
770
+ } catch {
771
+ return false;
876
772
  }
877
773
  }
878
- async function getValidCredentials2() {
879
- const creds = await getStoredCredentials2();
880
- if (!creds) return null;
881
- if (Date.now() > creds.expiresAt - 5 * 60 * 1e3) {
774
+ async function startBackground() {
775
+ await mkdir6(LOG_DIR, { recursive: true });
776
+ const cmd = resolveListenerCommand();
777
+ const stdoutFd = openSync(join8(LOG_DIR, "listener-stdout.log"), "a");
778
+ const stderrFd = openSync(join8(LOG_DIR, "listener-stderr.log"), "a");
779
+ const child = spawn(process.execPath, [cmd.script, ...cmd.args], {
780
+ detached: true,
781
+ stdio: ["ignore", stdoutFd, stderrFd],
782
+ cwd: homedir6(),
783
+ env: { ...process.env }
784
+ });
785
+ child.unref();
786
+ closeSync(stdoutFd);
787
+ closeSync(stderrFd);
788
+ const pid = child.pid;
789
+ if (!pid)
790
+ throw new Error("Failed to spawn listener process");
791
+ await writeFile6(PID_PATH2, String(pid));
792
+ return pid;
793
+ }
794
+ async function stopProcess() {
795
+ const pid = await readPid();
796
+ if (pid === null)
797
+ return;
798
+ if (!isAlive(pid)) {
799
+ await removePidFile2();
800
+ return;
801
+ }
802
+ try {
803
+ process.kill(pid, "SIGTERM");
804
+ } catch {
805
+ }
806
+ const deadline = Date.now() + 5e3;
807
+ while (Date.now() < deadline && isAlive(pid)) {
808
+ await new Promise((r) => setTimeout(r, 200));
809
+ }
810
+ if (isAlive(pid)) {
882
811
  try {
883
- const refreshed = await refreshTokens2(creds.refreshToken);
884
- refreshed.cognito = creds.cognito;
885
- await storeCredentials2(refreshed);
886
- return refreshed;
812
+ process.kill(pid, "SIGKILL");
887
813
  } catch {
888
- await clearCredentials();
889
- return null;
890
814
  }
891
815
  }
892
- return creds;
816
+ await removePidFile2();
893
817
  }
894
- async function performLogin() {
895
- const codeVerifier = generateCodeVerifier();
896
- const codeChallenge = generateCodeChallenge(codeVerifier);
897
- const state = generateState();
898
- const authorizeUrl = getAuthorizeUrl(codeChallenge, state);
899
- const callbackPromise = startCallbackServer();
818
+ async function isRunning() {
819
+ const pid = await readPid();
820
+ if (pid === null)
821
+ return false;
822
+ return isAlive(pid);
823
+ }
824
+ async function getStatus() {
825
+ const pid = await readPid();
826
+ if (pid === null)
827
+ return { running: false, pid: null };
828
+ const alive = isAlive(pid);
829
+ return { running: alive, pid: alive ? pid : null };
830
+ }
831
+ async function removePidFile2() {
900
832
  try {
901
- const open = (await import("open")).default;
902
- await open(authorizeUrl);
833
+ await unlink2(PID_PATH2);
903
834
  } catch {
904
- console.log(`
905
- Open this URL in your browser to authenticate:
906
- `);
907
- console.log(authorizeUrl);
908
835
  }
909
- const { code, state: returnedState } = await callbackPromise;
910
- if (returnedState !== state) {
911
- throw new Error("State mismatch \u2014 possible CSRF attack. Please try again.");
836
+ }
837
+
838
+ // ../listener/dist/service-installer.js
839
+ import { execFile as execFile2 } from "child_process";
840
+ import { writeFile as writeFile7, mkdir as mkdir7, unlink as unlink3 } from "fs/promises";
841
+ import { homedir as homedir7 } from "os";
842
+ import { join as join9 } from "path";
843
+ import { createRequire as createRequire2 } from "module";
844
+ import { fileURLToPath as fileURLToPath2 } from "url";
845
+ function resolveListenerCommand2() {
846
+ try {
847
+ const require2 = createRequire2(import.meta.url);
848
+ const mainPath = require2.resolve("@repowise/listener/main");
849
+ return { script: mainPath, args: [] };
850
+ } catch {
851
+ const bundlePath = fileURLToPath2(import.meta.url);
852
+ return { script: bundlePath, args: ["__listener"] };
912
853
  }
913
- const credentials = await exchangeCodeForTokens(code, codeVerifier);
914
- const { domain, clientId, region, customDomain } = getCognitoConfig();
915
- credentials.cognito = { domain, clientId, region, customDomain };
916
- await storeCredentials2(credentials);
917
- return credentials;
918
854
  }
919
- function callbackPage(title, message, isError) {
920
- const icon = isError ? '<svg width="48" height="48" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke="#ef4444" stroke-width="2"/><path stroke="#ef4444" stroke-width="2" stroke-linecap="round" d="M15 9l-6 6M9 9l6 6"/></svg>' : '<svg width="48" height="48" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke="#10b981" stroke-width="2"/><path stroke="#10b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M8 12l3 3 5-5"/></svg>';
921
- return `<!DOCTYPE html>
922
- <html lang="en">
923
- <head>
924
- <meta charset="UTF-8">
925
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
926
- <title>${title} \u2014 RepoWise</title>
927
- <link rel="icon" href="https://staging.repowise.ai/favicon.svg" type="image/svg+xml">
928
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
929
- <style>
930
- * { margin: 0; padding: 0; box-sizing: border-box; }
931
- body { font-family: 'Inter', system-ui, sans-serif; background: #0a0b14; color: #e4e4e7; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
932
- .card { text-align: center; max-width: 440px; padding: 48px 40px; }
933
- .logo { margin-bottom: 32px; }
934
- .logo svg { height: 48px; width: auto; }
935
- .icon { margin-bottom: 20px; }
936
- h1 { font-size: 24px; font-weight: 700; margin-bottom: 8px; color: ${isError ? "#ef4444" : "#e4e4e7"}; }
937
- p { font-size: 15px; color: #a1a1aa; line-height: 1.5; }
938
- </style>
939
- </head>
940
- <body>
941
- <div class="card">
942
- <div class="logo">
943
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 50" height="48">
944
- <text x="0" y="38" font-family="Inter, system-ui, sans-serif" font-weight="700" font-size="36" fill="#e4e4e7">Repo<tspan fill="#6c5ce7">Wise</tspan></text>
945
- </svg>
946
- </div>
947
- <div class="icon">${icon}</div>
948
- <h1>${title}</h1>
949
- <p>${message}</p>
950
- </div>
951
- </body>
952
- </html>`;
855
+ function exec(cmd, args) {
856
+ return new Promise((resolve, reject) => {
857
+ execFile2(cmd, args, (err, stdout) => {
858
+ if (err) {
859
+ reject(err);
860
+ return;
861
+ }
862
+ resolve(String(stdout ?? ""));
863
+ });
864
+ });
953
865
  }
954
- function decodeIdToken(idToken) {
866
+ var PLIST_LABEL = "com.repowise.listener";
867
+ function plistPath() {
868
+ return join9(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
869
+ }
870
+ function logDir() {
871
+ return join9(homedir7(), ".repowise", "logs");
872
+ }
873
+ function buildPlist() {
874
+ const cmd = resolveListenerCommand2();
875
+ const logs = logDir();
876
+ const programArgs = [process.execPath, cmd.script, ...cmd.args].map((a) => ` <string>${a}</string>`).join("\n");
877
+ return `<?xml version="1.0" encoding="UTF-8"?>
878
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
879
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
880
+ <plist version="1.0">
881
+ <dict>
882
+ <key>Label</key>
883
+ <string>${PLIST_LABEL}</string>
884
+ <key>ProgramArguments</key>
885
+ <array>
886
+ ${programArgs}
887
+ </array>
888
+ <key>RunAtLoad</key>
889
+ <true/>
890
+ <key>KeepAlive</key>
891
+ <true/>
892
+ <key>StandardOutPath</key>
893
+ <string>${join9(logs, "listener-stdout.log")}</string>
894
+ <key>StandardErrorPath</key>
895
+ <string>${join9(logs, "listener-stderr.log")}</string>
896
+ <key>ProcessType</key>
897
+ <string>Background</string>
898
+ </dict>
899
+ </plist>`;
900
+ }
901
+ async function darwinInstall() {
902
+ await mkdir7(logDir(), { recursive: true });
903
+ await mkdir7(join9(homedir7(), "Library", "LaunchAgents"), { recursive: true });
955
904
  try {
956
- const parts = idToken.split(".");
957
- if (parts.length < 2) return { email: "unknown" };
958
- const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
959
- return { email: payload.email ?? "unknown", tenantId: payload["custom:tenant_id"] };
905
+ await exec("launchctl", ["unload", plistPath()]);
960
906
  } catch {
961
- return { email: "unknown" };
962
907
  }
908
+ await writeFile7(plistPath(), buildPlist());
909
+ await exec("launchctl", ["load", plistPath()]);
963
910
  }
964
- var CONFIG_DIR5, CREDENTIALS_PATH2, CLI_CALLBACK_PORT, CALLBACK_TIMEOUT_MS;
965
- var init_auth2 = __esm({
966
- "src/lib/auth.ts"() {
967
- "use strict";
968
- init_env();
969
- CONFIG_DIR5 = join8(homedir6(), ".repowise");
970
- CREDENTIALS_PATH2 = join8(CONFIG_DIR5, "credentials.json");
971
- CLI_CALLBACK_PORT = 19876;
972
- CALLBACK_TIMEOUT_MS = 12e4;
911
+ async function darwinUninstall() {
912
+ try {
913
+ await exec("launchctl", ["unload", plistPath()]);
914
+ } catch {
973
915
  }
974
- });
916
+ try {
917
+ await unlink3(plistPath());
918
+ } catch {
919
+ }
920
+ }
921
+ async function darwinIsInstalled() {
922
+ try {
923
+ const stdout = await exec("launchctl", ["list"]);
924
+ return stdout.includes(PLIST_LABEL);
925
+ } catch {
926
+ return false;
927
+ }
928
+ }
929
+ var SYSTEMD_SERVICE = "repowise-listener";
930
+ function unitPath() {
931
+ return join9(homedir7(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
932
+ }
933
+ function buildUnit() {
934
+ const cmd = resolveListenerCommand2();
935
+ const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
936
+ const logs = logDir();
937
+ return `[Unit]
938
+ Description=RepoWise Listener
939
+ After=network-online.target
940
+ Wants=network-online.target
975
941
 
976
- // src/lib/api.ts
977
- function getApiUrl() {
978
- return process.env["REPOWISE_API_URL"] ?? getEnvConfig().apiUrl;
942
+ [Service]
943
+ Type=simple
944
+ ExecStart=${execStart}
945
+ Restart=on-failure
946
+ RestartSec=10
947
+ StandardOutput=append:${join9(logs, "listener-stdout.log")}
948
+ StandardError=append:${join9(logs, "listener-stderr.log")}
949
+
950
+ [Install]
951
+ WantedBy=default.target`;
979
952
  }
980
- async function apiRequest(path, options) {
981
- const credentials = await getValidCredentials2();
982
- if (!credentials) {
983
- throw new Error("Not logged in. Run `repowise login` first.");
953
+ async function linuxInstall() {
954
+ await mkdir7(logDir(), { recursive: true });
955
+ await mkdir7(join9(homedir7(), ".config", "systemd", "user"), { recursive: true });
956
+ await writeFile7(unitPath(), buildUnit());
957
+ await exec("systemctl", ["--user", "daemon-reload"]);
958
+ await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
959
+ await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
960
+ }
961
+ async function linuxUninstall() {
962
+ try {
963
+ await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
964
+ } catch {
984
965
  }
985
- const response = await fetch(`${getApiUrl()}${path}`, {
986
- ...options,
987
- headers: {
988
- "Content-Type": "application/json",
989
- Authorization: `Bearer ${credentials.accessToken}`,
990
- ...options?.headers
991
- }
992
- });
993
- if (response.status === 401) {
994
- await clearCredentials();
995
- throw new Error("Session expired. Run `repowise login` again.");
966
+ try {
967
+ await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
968
+ } catch {
996
969
  }
997
- if (!response.ok) {
998
- let message = `Request failed with status ${response.status}`;
999
- try {
1000
- const body = await response.json();
1001
- if (body.error?.message) message = body.error.message;
1002
- } catch {
1003
- }
1004
- throw new Error(message);
970
+ try {
971
+ await unlink3(unitPath());
972
+ } catch {
973
+ }
974
+ try {
975
+ await exec("systemctl", ["--user", "daemon-reload"]);
976
+ } catch {
1005
977
  }
1006
- const json = await response.json();
1007
- return json.data;
1008
978
  }
1009
- var init_api = __esm({
1010
- "src/lib/api.ts"() {
1011
- "use strict";
1012
- init_auth2();
1013
- init_env();
979
+ async function linuxIsInstalled() {
980
+ try {
981
+ const stdout = await exec("systemctl", ["--user", "is-enabled", SYSTEMD_SERVICE]);
982
+ return stdout.trim() === "enabled";
983
+ } catch {
984
+ return false;
1014
985
  }
1015
- });
1016
-
1017
- // src/lib/prompts.ts
1018
- import { checkbox, confirm } from "@inquirer/prompts";
1019
- import chalk2 from "chalk";
1020
- async function selectAiTools() {
1021
- const choices = [
1022
- { name: "Cursor", value: "cursor" },
1023
- { name: "Claude Code", value: "claude-code" },
1024
- { name: "GitHub Copilot", value: "copilot" },
1025
- { name: "Windsurf", value: "windsurf" },
1026
- { name: "Cline", value: "cline" },
1027
- { name: "Codex", value: "codex" },
1028
- { name: "Roo Code", value: "roo-code" },
1029
- { name: "Other (manual setup)", value: "other" }
1030
- ];
1031
- while (true) {
1032
- console.log(chalk2.dim(" Use Space to select, Enter to continue.\n"));
1033
- const selected = await checkbox({
1034
- message: chalk2.bold("Which AI tools do you use?"),
1035
- choices
1036
- });
1037
- if (selected.length === 0) {
1038
- const goBack = await confirm({
1039
- message: "No tools selected. Go back and choose?",
1040
- default: true
1041
- });
1042
- if (goBack) continue;
1043
- }
1044
- const hasOther = selected.includes("other");
1045
- const tools = selected.filter((s) => s !== "other");
1046
- return { tools, hasOther };
986
+ }
987
+ var TASK_NAME = "RepoWise Listener";
988
+ async function win32Install() {
989
+ await mkdir7(logDir(), { recursive: true });
990
+ const cmd = resolveListenerCommand2();
991
+ const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
992
+ await exec("schtasks", [
993
+ "/create",
994
+ "/tn",
995
+ TASK_NAME,
996
+ "/tr",
997
+ taskCmd,
998
+ "/sc",
999
+ "onlogon",
1000
+ "/ru",
1001
+ process.env.USERNAME ?? "",
1002
+ "/f"
1003
+ ]);
1004
+ await exec("schtasks", ["/run", "/tn", TASK_NAME]);
1005
+ }
1006
+ async function win32Uninstall() {
1007
+ try {
1008
+ await exec("schtasks", ["/end", "/tn", TASK_NAME]);
1009
+ } catch {
1010
+ }
1011
+ try {
1012
+ await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
1013
+ } catch {
1047
1014
  }
1048
1015
  }
1049
- var init_prompts = __esm({
1050
- "src/lib/prompts.ts"() {
1051
- "use strict";
1016
+ async function win32IsInstalled() {
1017
+ try {
1018
+ await exec("schtasks", ["/query", "/tn", TASK_NAME]);
1019
+ return true;
1020
+ } catch {
1021
+ return false;
1052
1022
  }
1053
- });
1054
-
1055
- // src/lib/ai-tools.ts
1056
- import { readFile as readFile7, writeFile as writeFile7, mkdir as mkdir7, readdir } from "fs/promises";
1057
- import { join as join9, dirname } from "path";
1058
- function sanitizeRepoName(name) {
1059
- return name.replace(/[<>[\]`()|\\]/g, "");
1060
1023
  }
1061
- function fileDescriptionFromName(fileName) {
1062
- return fileName.replace(/\.md$/, "").split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1024
+ async function install() {
1025
+ switch (process.platform) {
1026
+ case "darwin":
1027
+ await darwinInstall();
1028
+ break;
1029
+ case "linux":
1030
+ await linuxInstall();
1031
+ break;
1032
+ case "win32":
1033
+ await win32Install();
1034
+ break;
1035
+ default:
1036
+ throw new Error(`Unsupported platform: ${process.platform}`);
1037
+ }
1063
1038
  }
1064
- function generateReference(tool, repoName, contextFolder, contextFiles) {
1065
- const config2 = AI_TOOL_CONFIG[tool];
1066
- const safeName = sanitizeRepoName(repoName);
1067
- const fileLines = contextFiles.map((f) => {
1068
- const desc = fileDescriptionFromName(f.fileName);
1069
- const isOverview = f.fileName === "project-overview.md";
1070
- return { path: f.relativePath, desc: isOverview ? `${desc} (full index of all files)` : desc };
1071
- });
1072
- const hasFiles = fileLines.length > 0;
1073
- if (config2.format === "markdown") {
1074
- const lines2 = [
1075
- config2.markerStart,
1076
- "",
1077
- `## Project Context \u2014 ${safeName}`,
1078
- "",
1079
- `This repository has AI-optimized context files generated by RepoWise.`,
1080
- `Before making changes, read the relevant context files in \`${contextFolder}/\` to understand the project's architecture, coding patterns, conventions, and domain knowledge.`,
1081
- "",
1082
- `**Start here:** \`${contextFolder}/project-overview.md\` \u2014 the routing document that describes every context file and when to read it.`,
1083
- ""
1084
- ];
1085
- if (hasFiles) {
1086
- lines2.push(
1087
- `**Core context files:**`,
1088
- "",
1089
- ...fileLines.map((f) => `- \`${f.path}\` \u2014 ${f.desc}`),
1090
- "",
1091
- `> Additional context files may exist beyond this list. Check \`project-overview.md\` for the complete index.`
1092
- );
1093
- }
1094
- lines2.push("", config2.markerEnd);
1095
- return lines2.join("\n");
1096
- }
1097
- const lines = [
1098
- config2.markerStart,
1099
- `# Project Context \u2014 ${safeName}`,
1100
- "#",
1101
- `# This repository has AI-optimized context files generated by RepoWise.`,
1102
- `# Before making changes, read the relevant context files in ${contextFolder}/`,
1103
- `# to understand the project's architecture, coding patterns, conventions, and domain knowledge.`,
1104
- "#",
1105
- `# Start here: ${contextFolder}/project-overview.md`,
1106
- `# The routing document that describes every context file and when to read it.`
1107
- ];
1108
- if (hasFiles) {
1109
- lines.push(
1110
- "#",
1111
- `# Core context files:`,
1112
- ...fileLines.map((f) => `# ${f.path} \u2014 ${f.desc}`),
1113
- "#",
1114
- "# Additional context files may exist beyond this list.",
1115
- "# Check project-overview.md for the complete index."
1116
- );
1117
- }
1118
- lines.push(config2.markerEnd);
1119
- return lines.join("\n");
1120
- }
1121
- async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
1122
- const config2 = AI_TOOL_CONFIG[tool];
1123
- const fullPath = join9(repoRoot, config2.filePath);
1124
- const dir = dirname(fullPath);
1125
- if (dir !== repoRoot) {
1126
- await mkdir7(dir, { recursive: true });
1127
- }
1128
- const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles);
1129
- let existing = "";
1130
- let created = true;
1131
- try {
1132
- existing = await readFile7(fullPath, "utf-8");
1133
- created = false;
1134
- } catch (err) {
1135
- if (err.code !== "ENOENT") throw err;
1136
- }
1137
- const startIdx = existing.indexOf(config2.markerStart);
1138
- const endIdx = existing.indexOf(config2.markerEnd);
1139
- let content;
1140
- if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
1141
- const before = existing.slice(0, startIdx);
1142
- const after = existing.slice(endIdx + config2.markerEnd.length);
1143
- content = before + referenceBlock + after;
1144
- } else {
1145
- const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
1146
- content = existing + separator + referenceBlock + "\n";
1039
+ async function uninstall() {
1040
+ switch (process.platform) {
1041
+ case "darwin":
1042
+ await darwinUninstall();
1043
+ break;
1044
+ case "linux":
1045
+ await linuxUninstall();
1046
+ break;
1047
+ case "win32":
1048
+ await win32Uninstall();
1049
+ break;
1050
+ default:
1051
+ throw new Error(`Unsupported platform: ${process.platform}`);
1147
1052
  }
1148
- await writeFile7(fullPath, content, "utf-8");
1149
- return { created };
1150
1053
  }
1151
- async function scanLocalContextFiles(repoRoot, contextFolder) {
1152
- const folderPath = join9(repoRoot, contextFolder);
1153
- try {
1154
- const entries = await readdir(folderPath, { withFileTypes: true });
1155
- return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => ({
1156
- fileName: e.name,
1157
- relativePath: `${contextFolder}/${e.name}`
1158
- })).sort((a, b) => a.fileName.localeCompare(b.fileName));
1159
- } catch (err) {
1160
- if (err.code === "ENOENT") return [];
1161
- throw err;
1054
+ async function isInstalled() {
1055
+ switch (process.platform) {
1056
+ case "darwin":
1057
+ return darwinIsInstalled();
1058
+ case "linux":
1059
+ return linuxIsInstalled();
1060
+ case "win32":
1061
+ return win32IsInstalled();
1062
+ default:
1063
+ return false;
1162
1064
  }
1163
1065
  }
1164
- var AI_TOOL_CONFIG, SUPPORTED_TOOLS;
1165
- var init_ai_tools = __esm({
1166
- "src/lib/ai-tools.ts"() {
1167
- "use strict";
1168
- AI_TOOL_CONFIG = {
1169
- cursor: {
1170
- label: "Cursor",
1171
- fileName: ".cursorrules",
1172
- filePath: ".cursorrules",
1173
- markerStart: "# --- repowise-start ---",
1174
- markerEnd: "# --- repowise-end ---",
1175
- format: "plain-text"
1176
- },
1177
- "claude-code": {
1178
- label: "Claude Code",
1179
- fileName: "CLAUDE.md",
1180
- filePath: "CLAUDE.md",
1181
- markerStart: "<!-- repowise-start -->",
1182
- markerEnd: "<!-- repowise-end -->",
1183
- format: "markdown"
1184
- },
1185
- copilot: {
1186
- label: "GitHub Copilot",
1187
- fileName: "copilot-instructions.md",
1188
- filePath: ".github/copilot-instructions.md",
1189
- markerStart: "<!-- repowise-start -->",
1190
- markerEnd: "<!-- repowise-end -->",
1191
- format: "markdown"
1192
- },
1193
- windsurf: {
1194
- label: "Windsurf",
1195
- fileName: ".windsurfrules",
1196
- filePath: ".windsurfrules",
1197
- markerStart: "# --- repowise-start ---",
1198
- markerEnd: "# --- repowise-end ---",
1199
- format: "plain-text"
1200
- },
1201
- cline: {
1202
- label: "Cline",
1203
- fileName: ".clinerules",
1204
- filePath: ".clinerules",
1205
- markerStart: "# --- repowise-start ---",
1206
- markerEnd: "# --- repowise-end ---",
1207
- format: "plain-text"
1208
- },
1209
- codex: {
1210
- label: "Codex",
1211
- fileName: "AGENTS.md",
1212
- filePath: "AGENTS.md",
1213
- markerStart: "<!-- repowise-start -->",
1214
- markerEnd: "<!-- repowise-end -->",
1215
- format: "markdown"
1216
- },
1217
- "roo-code": {
1218
- label: "Roo Code",
1219
- fileName: "rules.md",
1220
- filePath: ".roo/rules.md",
1221
- markerStart: "<!-- repowise-start -->",
1222
- markerEnd: "<!-- repowise-end -->",
1223
- format: "markdown"
1224
- }
1225
- };
1226
- SUPPORTED_TOOLS = Object.keys(AI_TOOL_CONFIG);
1227
- }
1228
- });
1229
1066
 
1230
- // src/lib/interview-handler.ts
1231
- import chalk3 from "chalk";
1232
- import { input } from "@inquirer/prompts";
1233
- async function handleInterview(syncId, questionId, questionText, questionContext, estimatedQuestions) {
1234
- questionCounter++;
1235
- if (questionCounter === 1) {
1236
- console.log("");
1237
- console.log(chalk3.cyan.bold(" \u2500\u2500 Interview \u2500\u2500"));
1238
- console.log(chalk3.dim(" Help us understand your project better. Answer a few short"));
1239
- console.log(
1240
- chalk3.dim(
1241
- ` questions so we can generate more relevant context files (up to ${MAX_QUESTIONS}).`
1242
- )
1067
+ // src/commands/create.ts
1068
+ import chalk5 from "chalk";
1069
+ import ora from "ora";
1070
+
1071
+ // src/lib/auth.ts
1072
+ import { createHash, randomBytes } from "crypto";
1073
+ import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8, chmod as chmod3, unlink as unlink4 } from "fs/promises";
1074
+ import http from "http";
1075
+ import { homedir as homedir8 } from "os";
1076
+ import { join as join10 } from "path";
1077
+ var CONFIG_DIR5 = join10(homedir8(), ".repowise");
1078
+ var CREDENTIALS_PATH2 = join10(CONFIG_DIR5, "credentials.json");
1079
+ var CLI_CALLBACK_PORT = 19876;
1080
+ var CALLBACK_TIMEOUT_MS = 12e4;
1081
+ function getCognitoConfigForStorage() {
1082
+ const { domain, clientId, region, customDomain } = getCognitoConfig();
1083
+ return { domain, clientId, region, customDomain };
1084
+ }
1085
+ function getCognitoConfig() {
1086
+ const env = getEnvConfig();
1087
+ return {
1088
+ domain: process.env["REPOWISE_COGNITO_DOMAIN"] ?? env.cognitoDomain,
1089
+ clientId: process.env["REPOWISE_COGNITO_CLIENT_ID"] ?? env.cognitoClientId,
1090
+ region: process.env["REPOWISE_COGNITO_REGION"] ?? env.cognitoRegion,
1091
+ customDomain: env.customDomain
1092
+ };
1093
+ }
1094
+ function getCognitoBaseUrl() {
1095
+ const { domain, region, customDomain } = getCognitoConfig();
1096
+ return customDomain ? `https://${domain}` : `https://${domain}.auth.${region}.amazoncognito.com`;
1097
+ }
1098
+ function generateCodeVerifier() {
1099
+ return randomBytes(32).toString("base64url");
1100
+ }
1101
+ function generateCodeChallenge(verifier) {
1102
+ return createHash("sha256").update(verifier).digest("base64url");
1103
+ }
1104
+ function generateState() {
1105
+ return randomBytes(32).toString("hex");
1106
+ }
1107
+ function getAuthorizeUrl(codeChallenge, state) {
1108
+ const { clientId } = getCognitoConfig();
1109
+ if (!clientId) {
1110
+ throw new Error(
1111
+ "Missing REPOWISE_COGNITO_CLIENT_ID environment variable. Configure it before running login."
1243
1112
  );
1244
1113
  }
1245
- const total = Math.min(estimatedQuestions ?? MAX_QUESTIONS, MAX_QUESTIONS);
1246
- console.log("");
1247
- console.log(chalk3.cyan.bold(` Question ${questionCounter}/${total}`));
1248
- if (questionContext) {
1249
- console.log(chalk3.dim(` ${questionContext}`));
1250
- }
1251
- console.log(` ${questionText}`);
1252
- console.log(chalk3.dim(' (Enter to skip \xB7 "done" to finish early)'));
1253
- let answer;
1254
- try {
1255
- answer = await Promise.race([
1256
- input({
1257
- message: chalk3.cyan(">"),
1258
- theme: { prefix: " " }
1259
- }),
1260
- new Promise(
1261
- (_, reject) => setTimeout(() => reject(new Error("INTERVIEW_TIMEOUT")), INTERVIEW_TIMEOUT_MS)
1262
- )
1263
- ]);
1264
- } catch (err) {
1265
- if (err instanceof Error && err.message === "INTERVIEW_TIMEOUT") {
1266
- console.log(chalk3.yellow(" Timed out \u2014 auto-skipping this question."));
1267
- answer = "skip";
1268
- } else {
1269
- throw err;
1270
- }
1271
- }
1272
- const trimmed = answer.trim();
1273
- let action;
1274
- let answerText = "";
1275
- if (trimmed.toLowerCase() === "done") {
1276
- action = "done";
1277
- } else if (trimmed === "" || trimmed.toLowerCase() === "skip") {
1278
- action = "skip";
1279
- } else {
1280
- action = "answer";
1281
- answerText = trimmed;
1282
- }
1283
- if (questionCounter >= MAX_QUESTIONS && action !== "done") {
1284
- action = "done";
1285
- console.log(chalk3.green(" Thanks for your answers! Wrapping up the interview."));
1286
- }
1287
- try {
1288
- await apiRequest(`/v1/sync/${syncId}/answer`, {
1289
- method: "POST",
1290
- body: JSON.stringify({ questionId, answerText, action })
1291
- });
1292
- } catch (err) {
1293
- const message = err instanceof Error ? err.message : String(err);
1294
- if (message.includes("not awaiting input") || message.includes("expired")) {
1295
- console.log(chalk3.dim(" Pipeline has already moved on \u2014 continuing."));
1296
- return;
1297
- }
1298
- try {
1299
- await new Promise((r) => setTimeout(r, 1e3));
1300
- await apiRequest(`/v1/sync/${syncId}/answer`, {
1301
- method: "POST",
1302
- body: JSON.stringify({ questionId, answerText, action })
1303
- });
1304
- } catch {
1305
- console.log(chalk3.yellow(" Could not submit answer \u2014 pipeline will continue."));
1306
- return;
1307
- }
1308
- }
1309
- if (action === "done") {
1310
- console.log(chalk3.dim(" Interview ended early."));
1311
- } else if (action === "skip") {
1312
- console.log(chalk3.dim(" Skipped."));
1313
- } else {
1314
- console.log(chalk3.dim(" Answer recorded."));
1315
- }
1316
- console.log("");
1114
+ const params = new URLSearchParams({
1115
+ response_type: "code",
1116
+ client_id: clientId,
1117
+ redirect_uri: `http://localhost:${CLI_CALLBACK_PORT}/callback`,
1118
+ code_challenge: codeChallenge,
1119
+ code_challenge_method: "S256",
1120
+ scope: "openid email profile",
1121
+ state
1122
+ });
1123
+ return `${getCognitoBaseUrl()}/oauth2/authorize?${params.toString()}`;
1317
1124
  }
1318
- var INTERVIEW_TIMEOUT_MS, MAX_QUESTIONS, questionCounter;
1319
- var init_interview_handler = __esm({
1320
- "src/lib/interview-handler.ts"() {
1321
- "use strict";
1322
- init_api();
1323
- INTERVIEW_TIMEOUT_MS = 5 * 60 * 1e3;
1324
- MAX_QUESTIONS = 10;
1325
- questionCounter = 0;
1326
- }
1327
- });
1328
-
1329
- // src/lib/progress-renderer.ts
1330
- import chalk4 from "chalk";
1331
- function computeOverallProgress(syncResult) {
1332
- const stepNumber = syncResult.stepNumber ?? 1;
1333
- const totalSteps = syncResult.totalSteps ?? 6;
1334
- const stepPct = syncResult.progressPercentage ?? 0;
1335
- return Math.min(100, Math.round(((stepNumber - 1) * 100 + stepPct) / totalSteps));
1125
+ function getTokenUrl2() {
1126
+ return `${getCognitoBaseUrl()}/oauth2/token`;
1336
1127
  }
1337
- var CORE_FILES, FILE_DESCRIPTIONS, ALL_PERSONAS, PERSONA_LABELS, ProgressRenderer;
1338
- var init_progress_renderer = __esm({
1339
- "src/lib/progress-renderer.ts"() {
1340
- "use strict";
1341
- CORE_FILES = /* @__PURE__ */ new Set([
1342
- "project-overview.md",
1343
- "architecture.md",
1344
- "data-models.md",
1345
- "api-contracts.md",
1346
- "coding-patterns.md"
1347
- ]);
1348
- FILE_DESCRIPTIONS = {
1349
- // Core
1350
- "project-overview.md": "Project overview & file index",
1351
- "architecture.md": "System design & components",
1352
- "data-models.md": "Schemas, entities & relationships",
1353
- "api-contracts.md": "API endpoints & contracts",
1354
- "coding-patterns.md": "Code conventions & patterns",
1355
- // Tailored
1356
- "domain-knowledge.md": "Business domain & terminology",
1357
- "testing-strategy.md": "Test frameworks & coverage",
1358
- "deployment-workflows.md": "CI/CD & release process",
1359
- "state-management.md": "State & caching patterns",
1360
- "performance-optimization.md": "Performance & optimization",
1361
- "accessibility-patterns.md": "Accessibility & ARIA patterns",
1362
- "tech-stack.md": "Technology inventory & versions",
1363
- "tribal-knowledge.md": "Team knowledge & conventions",
1364
- "development-setup.md": "Dev environment setup",
1365
- "ui-patterns.md": "UI components & design system",
1366
- "ux-patterns.md": "UX interactions & feedback",
1367
- "user-flows.md": "User journeys & navigation",
1368
- "security-patterns.md": "Auth & security patterns",
1369
- "error-handling.md": "Error handling & recovery",
1370
- "integration-patterns.md": "External integrations & APIs",
1371
- "configuration.md": "Config & environment settings"
1372
- };
1373
- ALL_PERSONAS = ["pm", "architect", "dev", "analyst", "tea", "ux", "sm", "techWriter"];
1374
- PERSONA_LABELS = {
1375
- pm: "Product Manager",
1376
- architect: "Architect",
1377
- dev: "Developer",
1378
- analyst: "Business Analyst",
1379
- tea: "Test Architect",
1380
- ux: "UX Designer",
1381
- sm: "Scrum Master",
1382
- techWriter: "Tech Writer"
1383
- };
1384
- ProgressRenderer = class {
1385
- privacyShieldShown = false;
1386
- discoveryShown = false;
1387
- scanSummaryShown = false;
1388
- validationShown = false;
1389
- lastValidationSnapshot = "";
1390
- lastValidationLineCount = 0;
1391
- pushShown = false;
1392
- fileStatusHeaderShown = false;
1393
- lastFileStatusSnapshot = "";
1394
- lastFileStatusLineCount = 0;
1395
- renderPrivacyShield(enabled, spinner) {
1396
- if (this.privacyShieldShown) return;
1397
- this.privacyShieldShown = true;
1398
- spinner.stop();
1399
- console.log("");
1400
- console.log(chalk4.cyan.bold(" \u2500\u2500 Privacy Shield \u2500\u2500"));
1401
- if (enabled) {
1402
- console.log(` ${chalk4.green("\u2713")} Privacy Shield active`);
1403
- console.log(` ${chalk4.green("\u2713")} Private connection established`);
1404
- } else {
1405
- console.log(` ${chalk4.yellow("\u2139")} Privacy Shield not in current plan`);
1406
- console.log(chalk4.dim(" Shield your data from the open internet."));
1407
- }
1408
- console.log("");
1409
- spinner.start();
1128
+ function startCallbackServer() {
1129
+ return new Promise((resolve, reject) => {
1130
+ const server = http.createServer((req, res) => {
1131
+ const url = new URL(req.url, `http://localhost:${CLI_CALLBACK_PORT}`);
1132
+ if (url.pathname !== "/callback") {
1133
+ res.writeHead(404);
1134
+ res.end();
1135
+ return;
1410
1136
  }
1411
- renderDiscovery(result, spinner) {
1412
- if (this.discoveryShown) return;
1413
- this.discoveryShown = true;
1414
- spinner.stop();
1415
- console.log("");
1416
- console.log(chalk4.cyan.bold(" \u2500\u2500 Repository Discovery \u2500\u2500"));
1417
- if (result.languages.length > 0) {
1418
- const langs = result.languages.slice(0, 5).map((l) => `${l.name} (${Math.round(l.percentage)}%)`).join(", ");
1419
- console.log(` ${chalk4.dim("Languages:")} ${langs}`);
1420
- }
1421
- if (result.frameworks.length > 0) {
1422
- console.log(
1423
- ` ${chalk4.dim("Frameworks:")} ${result.frameworks.map((f) => f.name).join(", ")}`
1424
- );
1425
- }
1426
- console.log(
1427
- ` ${chalk4.dim("Structure:")} ${result.structureType} ${chalk4.dim(`(${result.fileCount} files)`)}`
1137
+ const code = url.searchParams.get("code");
1138
+ const state = url.searchParams.get("state");
1139
+ const error = url.searchParams.get("error");
1140
+ if (error) {
1141
+ res.writeHead(200, { "Content-Type": "text/html" });
1142
+ res.end(
1143
+ callbackPage(
1144
+ "Authentication Failed",
1145
+ "Something went wrong. Please close this tab and try again.",
1146
+ true
1147
+ )
1428
1148
  );
1429
- if (result.existingDocs.length > 0) {
1430
- console.log(` ${chalk4.dim("Existing docs:")} ${result.existingDocs.join(", ")}`);
1431
- }
1432
- if (result.fileTree && result.fileTree.length > 0) {
1433
- this.renderTree(result.fileTree);
1434
- }
1435
- console.log("");
1436
- spinner.start();
1437
- }
1438
- renderTree(entries) {
1439
- console.log("");
1440
- console.log(chalk4.cyan.bold(" \u2500\u2500 Project Structure \u2500\u2500"));
1441
- const root = { name: "", type: "tree", children: /* @__PURE__ */ new Map() };
1442
- for (const entry of entries) {
1443
- const parts = entry.path.split("/");
1444
- let current = root;
1445
- for (let i = 0; i < parts.length; i++) {
1446
- const part = parts[i];
1447
- if (!current.children.has(part)) {
1448
- const isLast = i === parts.length - 1;
1449
- current.children.set(part, {
1450
- name: part,
1451
- type: isLast ? entry.type : "tree",
1452
- children: /* @__PURE__ */ new Map()
1453
- });
1454
- }
1455
- current = current.children.get(part);
1456
- }
1457
- }
1458
- const printNode = (node, prefix, isLast) => {
1459
- const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1460
- const display = node.type === "tree" ? chalk4.bold.dim(`${node.name}/`) : node.name;
1461
- console.log(` ${prefix}${connector}${display}`);
1462
- const sorted = [...node.children.values()].sort((a, b) => {
1463
- if (a.type === "tree" && b.type !== "tree") return -1;
1464
- if (a.type !== "tree" && b.type === "tree") return 1;
1465
- return a.name.localeCompare(b.name);
1466
- });
1467
- const childPrefix = prefix + (isLast ? " " : "\u2502 ");
1468
- sorted.forEach((child, idx) => {
1469
- printNode(child, childPrefix, idx === sorted.length - 1);
1470
- });
1471
- };
1472
- const topLevel = [...root.children.values()].sort((a, b) => {
1473
- if (a.type === "tree" && b.type !== "tree") return -1;
1474
- if (a.type !== "tree" && b.type === "tree") return 1;
1475
- return a.name.localeCompare(b.name);
1476
- });
1477
- topLevel.forEach((child, idx) => {
1478
- printNode(child, "", idx === topLevel.length - 1);
1479
- });
1149
+ server.close();
1150
+ reject(new Error(`Authentication error: ${error}`));
1151
+ return;
1480
1152
  }
1481
- renderScanSummary(summary, spinner) {
1482
- if (this.scanSummaryShown) return;
1483
- this.scanSummaryShown = true;
1484
- spinner.stop();
1485
- console.log(
1486
- chalk4.dim(
1487
- ` Scan complete: ${summary.totalFiles} files, ${summary.totalFunctions} functions, ${summary.totalClasses} classes, ${summary.totalEndpoints} endpoints`
1153
+ if (!code || !state) {
1154
+ res.writeHead(400, { "Content-Type": "text/html" });
1155
+ res.end(
1156
+ callbackPage(
1157
+ "Missing Parameters",
1158
+ "The callback was missing required data. Please close this tab and try again.",
1159
+ true
1488
1160
  )
1489
1161
  );
1490
- console.log("");
1491
- spinner.start();
1492
- }
1493
- renderValidation(progress, spinner) {
1494
- const resultMap = new Map(progress.personaResults.map((r) => [r.persona, r.score]));
1495
- const isComplete = progress.status === "complete";
1496
- if (isComplete && this.validationShown) return;
1497
- const snapshot = `${progress.round}:${progress.status}:${progress.personaResults.map((r) => `${r.persona}:${r.score}`).join(",")}`;
1498
- if (snapshot === this.lastValidationSnapshot) return;
1499
- this.lastValidationSnapshot = snapshot;
1500
- if (isComplete) this.validationShown = true;
1501
- spinner.stop();
1502
- if (this.lastValidationLineCount > 0) {
1503
- process.stdout.write(`\x1B[${this.lastValidationLineCount}A`);
1504
- }
1505
- const lines = [];
1506
- const title = isComplete ? "Validation Results" : "Validation";
1507
- lines.push(chalk4.cyan.bold(` \u2500\u2500 ${title} \u2500\u2500`));
1508
- if (!isComplete) {
1509
- lines.push(
1510
- chalk4.dim(
1511
- ` ${ALL_PERSONAS.length} AI reviewers checking context quality \u2014 issues are auto-fixed.`
1512
- )
1513
- );
1514
- }
1515
- const passCount = progress.personaResults.filter((r) => r.score === "PASS").length;
1516
- if (isComplete) {
1517
- const roundInfo = progress.round > 1 ? ` (${progress.round} rounds)` : "";
1518
- lines.push(chalk4.dim(` ${passCount}/${ALL_PERSONAS.length} PASS${roundInfo}`));
1519
- } else if (progress.personaResults.length > 0) {
1520
- const statusSuffix = progress.status === "regenerating" ? chalk4.dim(" \u2014 improving files based on feedback") : "";
1521
- lines.push(
1522
- ` Round ${progress.round}/${progress.maxRounds}: ${passCount}/${ALL_PERSONAS.length} passed${statusSuffix}`
1523
- );
1524
- } else {
1525
- lines.push(chalk4.dim(` Round ${progress.round}/${progress.maxRounds}: validating...`));
1526
- }
1527
- for (const persona of ALL_PERSONAS) {
1528
- const label = PERSONA_LABELS[persona] ?? persona;
1529
- const score = resultMap.get(persona);
1530
- if (score) {
1531
- const icon = score === "PASS" ? chalk4.green("\u2713") : chalk4.red("\u2717");
1532
- const scoreColor = score === "PASS" ? chalk4.green : score === "PARTIAL" ? chalk4.yellow : chalk4.red;
1533
- const fixingSuffix = progress.status === "regenerating" && score !== "PASS" ? chalk4.dim(" \u2192 fixing...") : "";
1534
- lines.push(` ${icon} ${label}: ${scoreColor(score)}${fixingSuffix}`);
1535
- } else {
1536
- lines.push(` ${chalk4.dim("\u25CB")} ${chalk4.dim(label)}`);
1537
- }
1538
- }
1539
- if (isComplete && passCount < ALL_PERSONAS.length) {
1540
- lines.push(chalk4.yellow(" \u26A0 Continuing with best-effort context"));
1541
- }
1542
- lines.push("");
1543
- for (const line of lines) {
1544
- process.stdout.write(`\x1B[2K${line}
1545
- `);
1546
- }
1547
- for (let i = lines.length; i < this.lastValidationLineCount; i++) {
1548
- process.stdout.write("\x1B[2K\n");
1549
- }
1550
- this.lastValidationLineCount = lines.length;
1551
- spinner.start();
1552
- }
1553
- renderFileStatuses(fileStatuses, spinner) {
1554
- const snapshot = fileStatuses.map((f) => `${f.fileName}:${f.status}`).join(",");
1555
- if (snapshot === this.lastFileStatusSnapshot) return;
1556
- this.lastFileStatusSnapshot = snapshot;
1557
- const completedCount = fileStatuses.filter((f) => f.status === "completed").length;
1558
- const totalCount = fileStatuses.length;
1559
- spinner.stop();
1560
- if (!this.fileStatusHeaderShown) {
1561
- this.fileStatusHeaderShown = true;
1562
- console.log("");
1563
- console.log(chalk4.cyan.bold(" \u2500\u2500 RepoWise Context Generation \u2500\u2500"));
1564
- console.log(chalk4.dim(" Building AI-optimized context files from your codebase."));
1565
- }
1566
- if (this.lastFileStatusLineCount > 0) {
1567
- process.stdout.write(`\x1B[${this.lastFileStatusLineCount}A`);
1568
- }
1569
- const coreFiles = [];
1570
- const tailoredFiles = [];
1571
- for (const file of fileStatuses) {
1572
- const baseName = file.fileName.split("/").pop() ?? file.fileName;
1573
- if (CORE_FILES.has(baseName)) {
1574
- coreFiles.push(file);
1575
- } else {
1576
- tailoredFiles.push(file);
1577
- }
1578
- }
1579
- const maxCoreLen = coreFiles.reduce((m, f) => Math.max(m, f.fileName.length), 0);
1580
- const maxTailoredLen = tailoredFiles.reduce((m, f) => Math.max(m, f.fileName.length), 0);
1581
- const formatFileLine = (file, padLen) => {
1582
- const baseName = file.fileName.split("/").pop() ?? file.fileName;
1583
- const desc = FILE_DESCRIPTIONS[baseName] ?? baseName.replace(/\.md$/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
1584
- const padded = file.fileName.padEnd(padLen);
1585
- switch (file.status) {
1586
- case "completed":
1587
- return ` ${chalk4.green("\u2713")} ${padded} ${chalk4.dim(`\u2014 ${desc}`)}`;
1588
- case "generating":
1589
- return ` ${chalk4.cyan("\u27F3")} ${padded} ${chalk4.dim(`\u2014 ${desc}`)}`;
1590
- case "failed":
1591
- return ` ${chalk4.red("\u2717")} ${padded} ${chalk4.dim(`\u2014 ${desc}`)}`;
1592
- case "pending":
1593
- return ` ${chalk4.dim("\u25CB")} ${chalk4.dim(`${padded} \u2014 ${desc}`)}`;
1594
- }
1595
- };
1596
- const lines = [];
1597
- lines.push(chalk4.dim(` Generated ${completedCount}/${totalCount} files`));
1598
- lines.push("");
1599
- lines.push(` ${chalk4.bold("Core")}`);
1600
- for (const file of coreFiles) {
1601
- lines.push(formatFileLine(file, maxCoreLen));
1602
- }
1603
- if (tailoredFiles.length > 0) {
1604
- lines.push("");
1605
- lines.push(` ${chalk4.bold("Tailored")}`);
1606
- for (const file of tailoredFiles) {
1607
- lines.push(formatFileLine(file, maxTailoredLen));
1608
- }
1609
- }
1610
- lines.push("");
1611
- for (const line of lines) {
1612
- process.stdout.write(`\x1B[2K${line}
1613
- `);
1614
- }
1615
- for (let i = lines.length; i < this.lastFileStatusLineCount; i++) {
1616
- process.stdout.write("\x1B[2K\n");
1617
- }
1618
- this.lastFileStatusLineCount = lines.length;
1619
- spinner.start();
1620
- }
1621
- renderPush(spinner) {
1622
- if (this.pushShown) return;
1623
- this.pushShown = true;
1624
- spinner.stop();
1625
- console.log("");
1626
- console.log(chalk4.cyan.bold(" \u2500\u2500 Saving Context \u2500\u2500"));
1627
- console.log(` ${chalk4.dim("Encrypting and saving context files to RepoWise servers...")}`);
1628
- console.log("");
1629
- spinner.start();
1630
- }
1631
- getSpinnerText(syncResult) {
1632
- const stepLabel = syncResult.stepLabel ?? syncResult.currentStep ?? "Processing";
1633
- const overallPct = computeOverallProgress(syncResult);
1634
- let progressText = stepLabel;
1635
- if (syncResult.scanProgress && !syncResult.scanProgress.summary) {
1636
- progressText = `Scanning batch ${syncResult.scanProgress.currentBatch}/${syncResult.scanProgress.totalBatches}`;
1637
- } else if (syncResult.generationProgress) {
1638
- const gp = syncResult.generationProgress;
1639
- if (gp.fileStatuses && gp.fileStatuses.length > 0) {
1640
- const completed = gp.fileStatuses.filter((f) => f.status === "completed").length;
1641
- const total = gp.fileStatuses.length;
1642
- const generating = gp.fileStatuses.find((f) => f.status === "generating");
1643
- if (completed === total) {
1644
- progressText = stepLabel;
1645
- } else if (generating) {
1646
- progressText = `Generating ${generating.fileName} (${completed}/${total})`;
1647
- } else {
1648
- progressText = `Generating (${completed}/${total})`;
1649
- }
1650
- } else {
1651
- progressText = `Generating ${gp.currentFileName} (${gp.currentFile}/${gp.totalFiles})`;
1652
- }
1653
- } else if (syncResult.validationProgress && syncResult.validationProgress.status !== "complete") {
1654
- const vp = syncResult.validationProgress;
1655
- if (vp.status === "regenerating") {
1656
- progressText = `Improving files based on feedback (round ${vp.round})`;
1657
- } else {
1658
- progressText = `Validation round ${vp.round}/${vp.maxRounds}`;
1659
- }
1660
- }
1661
- return `${progressText}... ${chalk4.dim(`(${overallPct}%)`)}`;
1162
+ server.close();
1163
+ reject(new Error("Missing code or state in callback"));
1164
+ return;
1662
1165
  }
1663
- update(syncResult, spinner) {
1664
- if (syncResult.privacyShieldEnabled !== void 0) {
1665
- this.renderPrivacyShield(syncResult.privacyShieldEnabled, spinner);
1666
- }
1667
- if (syncResult.discoveryResult) {
1668
- this.renderDiscovery(syncResult.discoveryResult, spinner);
1669
- }
1670
- if (syncResult.scanProgress?.summary && syncResult.scanProgress.summary.totalFiles > 0) {
1671
- this.renderScanSummary(syncResult.scanProgress.summary, spinner);
1672
- }
1673
- if (syncResult.generationProgress?.fileStatuses && syncResult.generationProgress.fileStatuses.length > 0) {
1674
- this.renderFileStatuses(syncResult.generationProgress.fileStatuses, spinner);
1675
- }
1676
- if (syncResult.validationProgress) {
1677
- this.renderValidation(syncResult.validationProgress, spinner);
1678
- }
1679
- if (syncResult.currentStep === "push-context") {
1680
- this.renderPush(spinner);
1681
- }
1682
- spinner.text = this.getSpinnerText(syncResult);
1166
+ res.writeHead(200, { "Content-Type": "text/html" });
1167
+ res.end(
1168
+ callbackPage(
1169
+ "Authentication Successful",
1170
+ "You can close this tab and return to the terminal.",
1171
+ false
1172
+ )
1173
+ );
1174
+ server.close();
1175
+ resolve({ code, state });
1176
+ });
1177
+ server.listen(CLI_CALLBACK_PORT, "127.0.0.1");
1178
+ server.on("error", (err) => {
1179
+ if (err.code === "EADDRINUSE") {
1180
+ reject(
1181
+ new Error(
1182
+ `Port ${CLI_CALLBACK_PORT} is already in use. Close the conflicting process and try again.`
1183
+ )
1184
+ );
1185
+ } else {
1186
+ reject(err);
1683
1187
  }
1684
- };
1685
- }
1686
- });
1687
-
1688
- // ../listener/dist/service-installer.js
1689
- var service_installer_exports = {};
1690
- __export(service_installer_exports, {
1691
- install: () => install,
1692
- isInstalled: () => isInstalled,
1693
- uninstall: () => uninstall
1694
- });
1695
- import { execFile as execFile2 } from "child_process";
1696
- import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink3 } from "fs/promises";
1697
- import { homedir as homedir7 } from "os";
1698
- import { join as join10 } from "path";
1699
- import { createRequire } from "module";
1700
- import { fileURLToPath } from "url";
1701
- function resolveListenerCommand() {
1702
- try {
1703
- const require2 = createRequire(import.meta.url);
1704
- const mainPath = require2.resolve("@repowise/listener/main");
1705
- return { script: mainPath, args: [] };
1706
- } catch {
1707
- const bundlePath = fileURLToPath(import.meta.url);
1708
- return { script: bundlePath, args: ["__listener"] };
1188
+ });
1189
+ const timeout = setTimeout(() => {
1190
+ server.close();
1191
+ reject(new Error("Authentication timed out. Please try again."));
1192
+ }, CALLBACK_TIMEOUT_MS);
1193
+ server.on("close", () => clearTimeout(timeout));
1194
+ });
1195
+ }
1196
+ async function exchangeCodeForTokens(code, codeVerifier) {
1197
+ const response = await fetch(getTokenUrl2(), {
1198
+ method: "POST",
1199
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1200
+ body: new URLSearchParams({
1201
+ grant_type: "authorization_code",
1202
+ client_id: getCognitoConfig().clientId,
1203
+ redirect_uri: `http://localhost:${CLI_CALLBACK_PORT}/callback`,
1204
+ code,
1205
+ code_verifier: codeVerifier
1206
+ })
1207
+ });
1208
+ if (!response.ok) {
1209
+ const text = await response.text();
1210
+ throw new Error(`Token exchange failed: ${response.status} ${text}`);
1709
1211
  }
1212
+ const data = await response.json();
1213
+ return {
1214
+ accessToken: data.access_token,
1215
+ refreshToken: data.refresh_token,
1216
+ idToken: data.id_token,
1217
+ expiresAt: Date.now() + data.expires_in * 1e3
1218
+ };
1710
1219
  }
1711
- function exec(cmd, args) {
1712
- return new Promise((resolve, reject) => {
1713
- execFile2(cmd, args, (err, stdout) => {
1714
- if (err) {
1715
- reject(err);
1716
- return;
1717
- }
1718
- resolve(String(stdout ?? ""));
1719
- });
1220
+ async function refreshTokens2(refreshToken) {
1221
+ const response = await fetch(getTokenUrl2(), {
1222
+ method: "POST",
1223
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1224
+ body: new URLSearchParams({
1225
+ grant_type: "refresh_token",
1226
+ client_id: getCognitoConfig().clientId,
1227
+ refresh_token: refreshToken
1228
+ })
1720
1229
  });
1230
+ if (!response.ok) {
1231
+ throw new Error(`Token refresh failed: ${response.status}`);
1232
+ }
1233
+ const data = await response.json();
1234
+ return {
1235
+ accessToken: data.access_token,
1236
+ refreshToken,
1237
+ // Cognito does not return a new refresh token
1238
+ idToken: data.id_token,
1239
+ expiresAt: Date.now() + data.expires_in * 1e3
1240
+ };
1721
1241
  }
1722
- function plistPath() {
1723
- return join10(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
1724
- }
1725
- function logDir() {
1726
- return join10(homedir7(), ".repowise", "logs");
1242
+ async function getStoredCredentials2() {
1243
+ try {
1244
+ const data = await readFile7(CREDENTIALS_PATH2, "utf-8");
1245
+ return JSON.parse(data);
1246
+ } catch (err) {
1247
+ if (err.code === "ENOENT" || err instanceof SyntaxError) {
1248
+ return null;
1249
+ }
1250
+ throw err;
1251
+ }
1727
1252
  }
1728
- function buildPlist() {
1729
- const cmd = resolveListenerCommand();
1730
- const logs = logDir();
1731
- const programArgs = [process.execPath, cmd.script, ...cmd.args].map((a) => ` <string>${a}</string>`).join("\n");
1732
- return `<?xml version="1.0" encoding="UTF-8"?>
1733
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
1734
- "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1735
- <plist version="1.0">
1736
- <dict>
1737
- <key>Label</key>
1738
- <string>${PLIST_LABEL}</string>
1739
- <key>ProgramArguments</key>
1740
- <array>
1741
- ${programArgs}
1742
- </array>
1743
- <key>RunAtLoad</key>
1744
- <true/>
1745
- <key>KeepAlive</key>
1746
- <true/>
1747
- <key>StandardOutPath</key>
1748
- <string>${join10(logs, "listener-stdout.log")}</string>
1749
- <key>StandardErrorPath</key>
1750
- <string>${join10(logs, "listener-stderr.log")}</string>
1751
- <key>ProcessType</key>
1752
- <string>Background</string>
1753
- </dict>
1754
- </plist>`;
1253
+ async function storeCredentials2(credentials) {
1254
+ await mkdir8(CONFIG_DIR5, { recursive: true, mode: 448 });
1255
+ await writeFile8(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2));
1256
+ await chmod3(CREDENTIALS_PATH2, 384);
1755
1257
  }
1756
- async function darwinInstall() {
1757
- await mkdir8(logDir(), { recursive: true });
1758
- await mkdir8(join10(homedir7(), "Library", "LaunchAgents"), { recursive: true });
1258
+ async function clearCredentials() {
1759
1259
  try {
1760
- await exec("launchctl", ["unload", plistPath()]);
1761
- } catch {
1260
+ await unlink4(CREDENTIALS_PATH2);
1261
+ } catch (err) {
1262
+ if (err.code !== "ENOENT") throw err;
1762
1263
  }
1763
- await writeFile8(plistPath(), buildPlist());
1764
- await exec("launchctl", ["load", plistPath()]);
1765
1264
  }
1766
- async function darwinUninstall() {
1767
- try {
1768
- await exec("launchctl", ["unload", plistPath()]);
1769
- } catch {
1265
+ async function getValidCredentials2() {
1266
+ const creds = await getStoredCredentials2();
1267
+ if (!creds) return null;
1268
+ if (Date.now() > creds.expiresAt - 5 * 60 * 1e3) {
1269
+ try {
1270
+ const refreshed = await refreshTokens2(creds.refreshToken);
1271
+ refreshed.cognito = creds.cognito;
1272
+ await storeCredentials2(refreshed);
1273
+ return refreshed;
1274
+ } catch {
1275
+ await clearCredentials();
1276
+ return null;
1277
+ }
1770
1278
  }
1279
+ return creds;
1280
+ }
1281
+ async function performLogin() {
1282
+ const codeVerifier = generateCodeVerifier();
1283
+ const codeChallenge = generateCodeChallenge(codeVerifier);
1284
+ const state = generateState();
1285
+ const authorizeUrl = getAuthorizeUrl(codeChallenge, state);
1286
+ const callbackPromise = startCallbackServer();
1771
1287
  try {
1772
- await unlink3(plistPath());
1288
+ const open = (await import("open")).default;
1289
+ await open(authorizeUrl);
1773
1290
  } catch {
1291
+ console.log(`
1292
+ Open this URL in your browser to authenticate:
1293
+ `);
1294
+ console.log(authorizeUrl);
1295
+ }
1296
+ const { code, state: returnedState } = await callbackPromise;
1297
+ if (returnedState !== state) {
1298
+ throw new Error("State mismatch \u2014 possible CSRF attack. Please try again.");
1774
1299
  }
1300
+ const credentials = await exchangeCodeForTokens(code, codeVerifier);
1301
+ const { domain, clientId, region, customDomain } = getCognitoConfig();
1302
+ credentials.cognito = { domain, clientId, region, customDomain };
1303
+ await storeCredentials2(credentials);
1304
+ return credentials;
1775
1305
  }
1776
- async function darwinIsInstalled() {
1306
+ function callbackPage(title, message, isError) {
1307
+ const icon = isError ? '<svg width="48" height="48" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke="#ef4444" stroke-width="2"/><path stroke="#ef4444" stroke-width="2" stroke-linecap="round" d="M15 9l-6 6M9 9l6 6"/></svg>' : '<svg width="48" height="48" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke="#10b981" stroke-width="2"/><path stroke="#10b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M8 12l3 3 5-5"/></svg>';
1308
+ return `<!DOCTYPE html>
1309
+ <html lang="en">
1310
+ <head>
1311
+ <meta charset="UTF-8">
1312
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1313
+ <title>${title} \u2014 RepoWise</title>
1314
+ <link rel="icon" href="https://staging.repowise.ai/favicon.svg" type="image/svg+xml">
1315
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
1316
+ <style>
1317
+ * { margin: 0; padding: 0; box-sizing: border-box; }
1318
+ body { font-family: 'Inter', system-ui, sans-serif; background: #0a0b14; color: #e4e4e7; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
1319
+ .card { text-align: center; max-width: 440px; padding: 48px 40px; }
1320
+ .logo { margin-bottom: 32px; }
1321
+ .logo svg { height: 48px; width: auto; }
1322
+ .icon { margin-bottom: 20px; }
1323
+ h1 { font-size: 24px; font-weight: 700; margin-bottom: 8px; color: ${isError ? "#ef4444" : "#e4e4e7"}; }
1324
+ p { font-size: 15px; color: #a1a1aa; line-height: 1.5; }
1325
+ </style>
1326
+ </head>
1327
+ <body>
1328
+ <div class="card">
1329
+ <div class="logo">
1330
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 50" height="48">
1331
+ <text x="0" y="38" font-family="Inter, system-ui, sans-serif" font-weight="700" font-size="36" fill="#e4e4e7">Repo<tspan fill="#6c5ce7">Wise</tspan></text>
1332
+ </svg>
1333
+ </div>
1334
+ <div class="icon">${icon}</div>
1335
+ <h1>${title}</h1>
1336
+ <p>${message}</p>
1337
+ </div>
1338
+ </body>
1339
+ </html>`;
1340
+ }
1341
+ function decodeIdToken(idToken) {
1777
1342
  try {
1778
- const stdout = await exec("launchctl", ["list"]);
1779
- return stdout.includes(PLIST_LABEL);
1343
+ const parts = idToken.split(".");
1344
+ if (parts.length < 2) return { email: "unknown" };
1345
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
1346
+ return { email: payload.email ?? "unknown", tenantId: payload["custom:tenant_id"] };
1780
1347
  } catch {
1781
- return false;
1348
+ return { email: "unknown" };
1782
1349
  }
1783
1350
  }
1784
- function unitPath() {
1785
- return join10(homedir7(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
1786
- }
1787
- function buildUnit() {
1788
- const cmd = resolveListenerCommand();
1789
- const execStart = [process.execPath, cmd.script, ...cmd.args].join(" ");
1790
- const logs = logDir();
1791
- return `[Unit]
1792
- Description=RepoWise Listener
1793
- After=network-online.target
1794
- Wants=network-online.target
1795
-
1796
- [Service]
1797
- Type=simple
1798
- ExecStart=${execStart}
1799
- Restart=on-failure
1800
- RestartSec=10
1801
- StandardOutput=append:${join10(logs, "listener-stdout.log")}
1802
- StandardError=append:${join10(logs, "listener-stderr.log")}
1803
1351
 
1804
- [Install]
1805
- WantedBy=default.target`;
1806
- }
1807
- async function linuxInstall() {
1808
- await mkdir8(logDir(), { recursive: true });
1809
- await mkdir8(join10(homedir7(), ".config", "systemd", "user"), { recursive: true });
1810
- await writeFile8(unitPath(), buildUnit());
1811
- await exec("systemctl", ["--user", "daemon-reload"]);
1812
- await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
1813
- await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
1352
+ // src/lib/api.ts
1353
+ function getApiUrl() {
1354
+ return process.env["REPOWISE_API_URL"] ?? getEnvConfig().apiUrl;
1814
1355
  }
1815
- async function linuxUninstall() {
1816
- try {
1817
- await exec("systemctl", ["--user", "stop", SYSTEMD_SERVICE]);
1818
- } catch {
1356
+ var BILLING_URL = "https://app.repowise.ai/billing";
1357
+ async function apiRequest(path, options) {
1358
+ const credentials = await getValidCredentials2();
1359
+ if (!credentials) {
1360
+ throw new Error("Not logged in. Run `repowise login` first.");
1819
1361
  }
1820
- try {
1821
- await exec("systemctl", ["--user", "disable", SYSTEMD_SERVICE]);
1822
- } catch {
1362
+ const response = await fetch(`${getApiUrl()}${path}`, {
1363
+ ...options,
1364
+ headers: {
1365
+ "Content-Type": "application/json",
1366
+ Authorization: `Bearer ${credentials.accessToken}`,
1367
+ ...options?.headers
1368
+ }
1369
+ });
1370
+ if (response.status === 401) {
1371
+ await clearCredentials();
1372
+ throw new Error("Session expired. Run `repowise login` again.");
1823
1373
  }
1824
- try {
1825
- await unlink3(unitPath());
1826
- } catch {
1374
+ if (response.status === 402) {
1375
+ let message = "Your subscription is inactive.";
1376
+ try {
1377
+ const body = await response.json();
1378
+ if (body.error?.message) message = body.error.message;
1379
+ } catch {
1380
+ }
1381
+ throw new Error(`${message} Visit ${BILLING_URL} to update your subscription.`);
1827
1382
  }
1828
- try {
1829
- await exec("systemctl", ["--user", "daemon-reload"]);
1830
- } catch {
1383
+ if (!response.ok) {
1384
+ let message = `Request failed with status ${response.status}`;
1385
+ let code = "";
1386
+ try {
1387
+ const body = await response.json();
1388
+ if (body.error?.message) message = body.error.message;
1389
+ if (body.error?.code) code = body.error.code;
1390
+ } catch {
1391
+ }
1392
+ if (code === "BILLING_LIMIT_EXCEEDED" || code === "SYNC_LIMIT_EXCEEDED" || code === "BILLING_SUBSCRIPTION_INACTIVE") {
1393
+ throw new Error(`${message} Visit ${BILLING_URL} to manage your plan.`);
1394
+ }
1395
+ throw new Error(message);
1396
+ }
1397
+ const json = await response.json();
1398
+ return json.data;
1399
+ }
1400
+
1401
+ // src/lib/prompts.ts
1402
+ import { checkbox, confirm } from "@inquirer/prompts";
1403
+ import chalk2 from "chalk";
1404
+ async function selectAiTools() {
1405
+ const choices = [
1406
+ { name: "Cursor", value: "cursor" },
1407
+ { name: "Claude Code", value: "claude-code" },
1408
+ { name: "GitHub Copilot", value: "copilot" },
1409
+ { name: "Windsurf", value: "windsurf" },
1410
+ { name: "Cline", value: "cline" },
1411
+ { name: "Codex", value: "codex" },
1412
+ { name: "Roo Code", value: "roo-code" },
1413
+ { name: "Other (manual setup)", value: "other" }
1414
+ ];
1415
+ while (true) {
1416
+ console.log(chalk2.dim(" Use Space to select, Enter to continue.\n"));
1417
+ const selected = await checkbox({
1418
+ message: chalk2.bold("Which AI tools do you use?"),
1419
+ choices
1420
+ });
1421
+ if (selected.length === 0) {
1422
+ const goBack = await confirm({
1423
+ message: "No tools selected. Go back and choose?",
1424
+ default: true
1425
+ });
1426
+ if (goBack) continue;
1427
+ }
1428
+ const hasOther = selected.includes("other");
1429
+ const tools = selected.filter((s) => s !== "other");
1430
+ return { tools, hasOther };
1831
1431
  }
1832
1432
  }
1833
- async function linuxIsInstalled() {
1834
- try {
1835
- const stdout = await exec("systemctl", ["--user", "is-enabled", SYSTEMD_SERVICE]);
1836
- return stdout.trim() === "enabled";
1837
- } catch {
1838
- return false;
1433
+
1434
+ // src/lib/ai-tools.ts
1435
+ import { readFile as readFile8, writeFile as writeFile9, mkdir as mkdir9, readdir } from "fs/promises";
1436
+ import { join as join11, dirname } from "path";
1437
+ var AI_TOOL_CONFIG = {
1438
+ cursor: {
1439
+ label: "Cursor",
1440
+ fileName: ".cursorrules",
1441
+ filePath: ".cursorrules",
1442
+ markerStart: "# --- repowise-start ---",
1443
+ markerEnd: "# --- repowise-end ---",
1444
+ format: "plain-text"
1445
+ },
1446
+ "claude-code": {
1447
+ label: "Claude Code",
1448
+ fileName: "CLAUDE.md",
1449
+ filePath: "CLAUDE.md",
1450
+ markerStart: "<!-- repowise-start -->",
1451
+ markerEnd: "<!-- repowise-end -->",
1452
+ format: "markdown"
1453
+ },
1454
+ copilot: {
1455
+ label: "GitHub Copilot",
1456
+ fileName: "copilot-instructions.md",
1457
+ filePath: ".github/copilot-instructions.md",
1458
+ markerStart: "<!-- repowise-start -->",
1459
+ markerEnd: "<!-- repowise-end -->",
1460
+ format: "markdown"
1461
+ },
1462
+ windsurf: {
1463
+ label: "Windsurf",
1464
+ fileName: ".windsurfrules",
1465
+ filePath: ".windsurfrules",
1466
+ markerStart: "# --- repowise-start ---",
1467
+ markerEnd: "# --- repowise-end ---",
1468
+ format: "plain-text"
1469
+ },
1470
+ cline: {
1471
+ label: "Cline",
1472
+ fileName: ".clinerules",
1473
+ filePath: ".clinerules",
1474
+ markerStart: "# --- repowise-start ---",
1475
+ markerEnd: "# --- repowise-end ---",
1476
+ format: "plain-text"
1477
+ },
1478
+ codex: {
1479
+ label: "Codex",
1480
+ fileName: "AGENTS.md",
1481
+ filePath: "AGENTS.md",
1482
+ markerStart: "<!-- repowise-start -->",
1483
+ markerEnd: "<!-- repowise-end -->",
1484
+ format: "markdown"
1485
+ },
1486
+ "roo-code": {
1487
+ label: "Roo Code",
1488
+ fileName: "rules.md",
1489
+ filePath: ".roo/rules.md",
1490
+ markerStart: "<!-- repowise-start -->",
1491
+ markerEnd: "<!-- repowise-end -->",
1492
+ format: "markdown"
1839
1493
  }
1494
+ };
1495
+ var SUPPORTED_TOOLS = Object.keys(AI_TOOL_CONFIG);
1496
+ function sanitizeRepoName(name) {
1497
+ return name.replace(/[<>[\]`()|\\]/g, "");
1840
1498
  }
1841
- async function win32Install() {
1842
- await mkdir8(logDir(), { recursive: true });
1843
- const cmd = resolveListenerCommand();
1844
- const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
1845
- await exec("schtasks", [
1846
- "/create",
1847
- "/tn",
1848
- TASK_NAME,
1849
- "/tr",
1850
- taskCmd,
1851
- "/sc",
1852
- "onlogon",
1853
- "/ru",
1854
- process.env.USERNAME ?? "",
1855
- "/f"
1856
- ]);
1857
- await exec("schtasks", ["/run", "/tn", TASK_NAME]);
1499
+ function fileDescriptionFromName(fileName) {
1500
+ return fileName.replace(/\.md$/, "").split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1858
1501
  }
1859
- async function win32Uninstall() {
1860
- try {
1861
- await exec("schtasks", ["/end", "/tn", TASK_NAME]);
1862
- } catch {
1502
+ function generateReference(tool, repoName, contextFolder, contextFiles) {
1503
+ const config2 = AI_TOOL_CONFIG[tool];
1504
+ const safeName = sanitizeRepoName(repoName);
1505
+ const fileLines = contextFiles.map((f) => {
1506
+ const desc = fileDescriptionFromName(f.fileName);
1507
+ const isOverview = f.fileName === "project-overview.md";
1508
+ return { path: f.relativePath, desc: isOverview ? `${desc} (full index of all files)` : desc };
1509
+ });
1510
+ const hasFiles = fileLines.length > 0;
1511
+ if (config2.format === "markdown") {
1512
+ const lines2 = [
1513
+ config2.markerStart,
1514
+ "",
1515
+ `## Project Context \u2014 ${safeName}`,
1516
+ "",
1517
+ `This repository has AI-optimized context files generated by RepoWise.`,
1518
+ `Before making changes, read the relevant context files in \`${contextFolder}/\` to understand the project's architecture, coding patterns, conventions, and domain knowledge.`,
1519
+ "",
1520
+ `**Start here:** \`${contextFolder}/project-overview.md\` \u2014 the routing document that describes every context file and when to read it.`,
1521
+ ""
1522
+ ];
1523
+ if (hasFiles) {
1524
+ lines2.push(
1525
+ `**Core context files:**`,
1526
+ "",
1527
+ ...fileLines.map((f) => `- \`${f.path}\` \u2014 ${f.desc}`),
1528
+ "",
1529
+ `> Additional context files may exist beyond this list. Check \`project-overview.md\` for the complete index.`
1530
+ );
1531
+ }
1532
+ lines2.push("", config2.markerEnd);
1533
+ return lines2.join("\n");
1863
1534
  }
1864
- try {
1865
- await exec("schtasks", ["/delete", "/tn", TASK_NAME, "/f"]);
1866
- } catch {
1535
+ const lines = [
1536
+ config2.markerStart,
1537
+ `# Project Context \u2014 ${safeName}`,
1538
+ "#",
1539
+ `# This repository has AI-optimized context files generated by RepoWise.`,
1540
+ `# Before making changes, read the relevant context files in ${contextFolder}/`,
1541
+ `# to understand the project's architecture, coding patterns, conventions, and domain knowledge.`,
1542
+ "#",
1543
+ `# Start here: ${contextFolder}/project-overview.md`,
1544
+ `# The routing document that describes every context file and when to read it.`
1545
+ ];
1546
+ if (hasFiles) {
1547
+ lines.push(
1548
+ "#",
1549
+ `# Core context files:`,
1550
+ ...fileLines.map((f) => `# ${f.path} \u2014 ${f.desc}`),
1551
+ "#",
1552
+ "# Additional context files may exist beyond this list.",
1553
+ "# Check project-overview.md for the complete index."
1554
+ );
1867
1555
  }
1556
+ lines.push(config2.markerEnd);
1557
+ return lines.join("\n");
1868
1558
  }
1869
- async function win32IsInstalled() {
1559
+ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
1560
+ const config2 = AI_TOOL_CONFIG[tool];
1561
+ const fullPath = join11(repoRoot, config2.filePath);
1562
+ const dir = dirname(fullPath);
1563
+ if (dir !== repoRoot) {
1564
+ await mkdir9(dir, { recursive: true });
1565
+ }
1566
+ const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles);
1567
+ let existing = "";
1568
+ let created = true;
1870
1569
  try {
1871
- await exec("schtasks", ["/query", "/tn", TASK_NAME]);
1872
- return true;
1873
- } catch {
1874
- return false;
1570
+ existing = await readFile8(fullPath, "utf-8");
1571
+ created = false;
1572
+ } catch (err) {
1573
+ if (err.code !== "ENOENT") throw err;
1875
1574
  }
1876
- }
1877
- async function install() {
1878
- switch (process.platform) {
1879
- case "darwin":
1880
- await darwinInstall();
1881
- break;
1882
- case "linux":
1883
- await linuxInstall();
1884
- break;
1885
- case "win32":
1886
- await win32Install();
1887
- break;
1888
- default:
1889
- throw new Error(`Unsupported platform: ${process.platform}`);
1575
+ const startIdx = existing.indexOf(config2.markerStart);
1576
+ const endIdx = existing.indexOf(config2.markerEnd);
1577
+ let content;
1578
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
1579
+ const before = existing.slice(0, startIdx);
1580
+ const after = existing.slice(endIdx + config2.markerEnd.length);
1581
+ content = before + referenceBlock + after;
1582
+ } else {
1583
+ const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
1584
+ content = existing + separator + referenceBlock + "\n";
1890
1585
  }
1586
+ await writeFile9(fullPath, content, "utf-8");
1587
+ return { created };
1891
1588
  }
1892
- async function uninstall() {
1893
- switch (process.platform) {
1894
- case "darwin":
1895
- await darwinUninstall();
1896
- break;
1897
- case "linux":
1898
- await linuxUninstall();
1899
- break;
1900
- case "win32":
1901
- await win32Uninstall();
1902
- break;
1903
- default:
1904
- throw new Error(`Unsupported platform: ${process.platform}`);
1589
+ async function scanLocalContextFiles(repoRoot, contextFolder) {
1590
+ const folderPath = join11(repoRoot, contextFolder);
1591
+ try {
1592
+ const entries = await readdir(folderPath, { withFileTypes: true });
1593
+ return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => ({
1594
+ fileName: e.name,
1595
+ relativePath: `${contextFolder}/${e.name}`
1596
+ })).sort((a, b) => a.fileName.localeCompare(b.fileName));
1597
+ } catch (err) {
1598
+ if (err.code === "ENOENT") return [];
1599
+ throw err;
1905
1600
  }
1906
1601
  }
1907
- async function isInstalled() {
1908
- switch (process.platform) {
1909
- case "darwin":
1910
- return darwinIsInstalled();
1911
- case "linux":
1912
- return linuxIsInstalled();
1913
- case "win32":
1914
- return win32IsInstalled();
1915
- default:
1916
- return false;
1602
+
1603
+ // src/lib/interview-handler.ts
1604
+ import chalk3 from "chalk";
1605
+ import { input } from "@inquirer/prompts";
1606
+ var INTERVIEW_TIMEOUT_MS = 5 * 60 * 1e3;
1607
+ var MAX_QUESTIONS = 10;
1608
+ var questionCounter = 0;
1609
+ async function handleInterview(syncId, questionId, questionText, questionContext, estimatedQuestions) {
1610
+ questionCounter++;
1611
+ if (questionCounter === 1) {
1612
+ console.log("");
1613
+ console.log(chalk3.cyan.bold(" \u2500\u2500 Interview \u2500\u2500"));
1614
+ console.log(chalk3.dim(" Help us understand your project better. Answer a few short"));
1615
+ console.log(
1616
+ chalk3.dim(
1617
+ ` questions so we can generate more relevant context files (up to ${MAX_QUESTIONS}).`
1618
+ )
1619
+ );
1917
1620
  }
1918
- }
1919
- var PLIST_LABEL, SYSTEMD_SERVICE, TASK_NAME;
1920
- var init_service_installer = __esm({
1921
- "../listener/dist/service-installer.js"() {
1922
- "use strict";
1923
- PLIST_LABEL = "com.repowise.listener";
1924
- SYSTEMD_SERVICE = "repowise-listener";
1925
- TASK_NAME = "RepoWise Listener";
1621
+ const total = Math.min(estimatedQuestions ?? MAX_QUESTIONS, MAX_QUESTIONS);
1622
+ console.log("");
1623
+ console.log(chalk3.cyan.bold(` Question ${questionCounter}/${total}`));
1624
+ if (questionContext) {
1625
+ console.log(chalk3.dim(` ${questionContext}`));
1926
1626
  }
1927
- });
1928
-
1929
- // ../listener/dist/process-manager.js
1930
- var process_manager_exports = {};
1931
- __export(process_manager_exports, {
1932
- getStatus: () => getStatus,
1933
- isRunning: () => isRunning,
1934
- startBackground: () => startBackground,
1935
- stopProcess: () => stopProcess
1936
- });
1937
- import { spawn } from "child_process";
1938
- import { openSync, closeSync } from "fs";
1939
- import { readFile as readFile8, writeFile as writeFile9, mkdir as mkdir9, unlink as unlink4 } from "fs/promises";
1940
- import { homedir as homedir8 } from "os";
1941
- import { join as join11 } from "path";
1942
- import { createRequire as createRequire2 } from "module";
1943
- import { fileURLToPath as fileURLToPath2 } from "url";
1944
- function resolveListenerCommand2() {
1627
+ console.log(` ${questionText}`);
1628
+ console.log(chalk3.dim(' (Enter to skip \xB7 "done" to finish early)'));
1629
+ let answer;
1945
1630
  try {
1946
- const require2 = createRequire2(import.meta.url);
1947
- const mainPath = require2.resolve("@repowise/listener/main");
1948
- return { script: mainPath, args: [] };
1949
- } catch {
1950
- const bundlePath = fileURLToPath2(import.meta.url);
1951
- return { script: bundlePath, args: ["__listener"] };
1631
+ answer = await Promise.race([
1632
+ input({
1633
+ message: chalk3.cyan(">"),
1634
+ theme: { prefix: " " }
1635
+ }),
1636
+ new Promise(
1637
+ (_, reject) => setTimeout(() => reject(new Error("INTERVIEW_TIMEOUT")), INTERVIEW_TIMEOUT_MS)
1638
+ )
1639
+ ]);
1640
+ } catch (err) {
1641
+ if (err instanceof Error && err.message === "INTERVIEW_TIMEOUT") {
1642
+ console.log(chalk3.yellow(" Timed out \u2014 auto-skipping this question."));
1643
+ answer = "skip";
1644
+ } else {
1645
+ throw err;
1646
+ }
1647
+ }
1648
+ const trimmed = answer.trim();
1649
+ let action;
1650
+ let answerText = "";
1651
+ if (trimmed.toLowerCase() === "done") {
1652
+ action = "done";
1653
+ } else if (trimmed === "" || trimmed.toLowerCase() === "skip") {
1654
+ action = "skip";
1655
+ } else {
1656
+ action = "answer";
1657
+ answerText = trimmed;
1658
+ }
1659
+ if (questionCounter >= MAX_QUESTIONS && action !== "done") {
1660
+ action = "done";
1661
+ console.log(chalk3.green(" Thanks for your answers! Wrapping up the interview."));
1952
1662
  }
1953
- }
1954
- async function readPid() {
1955
1663
  try {
1956
- const content = await readFile8(PID_PATH2, "utf-8");
1957
- const pid = parseInt(content.trim(), 10);
1958
- return Number.isNaN(pid) ? null : pid;
1664
+ await apiRequest(`/v1/sync/${syncId}/answer`, {
1665
+ method: "POST",
1666
+ body: JSON.stringify({ questionId, answerText, action })
1667
+ });
1959
1668
  } catch (err) {
1960
- if (err.code === "ENOENT")
1961
- return null;
1962
- throw err;
1669
+ const message = err instanceof Error ? err.message : String(err);
1670
+ if (message.includes("not awaiting input") || message.includes("expired")) {
1671
+ console.log(chalk3.dim(" Pipeline has already moved on \u2014 continuing."));
1672
+ return;
1673
+ }
1674
+ try {
1675
+ await new Promise((r) => setTimeout(r, 1e3));
1676
+ await apiRequest(`/v1/sync/${syncId}/answer`, {
1677
+ method: "POST",
1678
+ body: JSON.stringify({ questionId, answerText, action })
1679
+ });
1680
+ } catch {
1681
+ console.log(chalk3.yellow(" Could not submit answer \u2014 pipeline will continue."));
1682
+ return;
1683
+ }
1963
1684
  }
1964
- }
1965
- function isAlive(pid) {
1966
- try {
1967
- process.kill(pid, 0);
1968
- return true;
1969
- } catch {
1970
- return false;
1685
+ if (action === "done") {
1686
+ console.log(chalk3.dim(" Interview ended early."));
1687
+ } else if (action === "skip") {
1688
+ console.log(chalk3.dim(" Skipped."));
1689
+ } else {
1690
+ console.log(chalk3.dim(" Answer recorded."));
1971
1691
  }
1692
+ console.log("");
1972
1693
  }
1973
- async function startBackground() {
1974
- await mkdir9(LOG_DIR, { recursive: true });
1975
- const cmd = resolveListenerCommand2();
1976
- const stdoutFd = openSync(join11(LOG_DIR, "listener-stdout.log"), "a");
1977
- const stderrFd = openSync(join11(LOG_DIR, "listener-stderr.log"), "a");
1978
- const child = spawn(process.execPath, [cmd.script, ...cmd.args], {
1979
- detached: true,
1980
- stdio: ["ignore", stdoutFd, stderrFd],
1981
- cwd: homedir8(),
1982
- env: { ...process.env }
1983
- });
1984
- child.unref();
1985
- closeSync(stdoutFd);
1986
- closeSync(stderrFd);
1987
- const pid = child.pid;
1988
- if (!pid)
1989
- throw new Error("Failed to spawn listener process");
1990
- await writeFile9(PID_PATH2, String(pid));
1991
- return pid;
1694
+
1695
+ // src/lib/progress-renderer.ts
1696
+ import chalk4 from "chalk";
1697
+ var CORE_FILES = /* @__PURE__ */ new Set([
1698
+ "project-overview.md",
1699
+ "architecture.md",
1700
+ "data-models.md",
1701
+ "api-contracts.md",
1702
+ "coding-patterns.md"
1703
+ ]);
1704
+ var FILE_DESCRIPTIONS = {
1705
+ // Core
1706
+ "project-overview.md": "Project overview & file index",
1707
+ "architecture.md": "System design & components",
1708
+ "data-models.md": "Schemas, entities & relationships",
1709
+ "api-contracts.md": "API endpoints & contracts",
1710
+ "coding-patterns.md": "Code conventions & patterns",
1711
+ // Tailored
1712
+ "domain-knowledge.md": "Business domain & terminology",
1713
+ "testing-strategy.md": "Test frameworks & coverage",
1714
+ "deployment-workflows.md": "CI/CD & release process",
1715
+ "state-management.md": "State & caching patterns",
1716
+ "performance-optimization.md": "Performance & optimization",
1717
+ "accessibility-patterns.md": "Accessibility & ARIA patterns",
1718
+ "tech-stack.md": "Technology inventory & versions",
1719
+ "tribal-knowledge.md": "Team knowledge & conventions",
1720
+ "development-setup.md": "Dev environment setup",
1721
+ "ui-patterns.md": "UI components & design system",
1722
+ "ux-patterns.md": "UX interactions & feedback",
1723
+ "user-flows.md": "User journeys & navigation",
1724
+ "security-patterns.md": "Auth & security patterns",
1725
+ "error-handling.md": "Error handling & recovery",
1726
+ "integration-patterns.md": "External integrations & APIs",
1727
+ "configuration.md": "Config & environment settings"
1728
+ };
1729
+ var ALL_PERSONAS = ["pm", "architect", "dev", "analyst", "tea", "ux", "sm", "techWriter"];
1730
+ var PERSONA_LABELS = {
1731
+ pm: "Product Manager",
1732
+ architect: "Architect",
1733
+ dev: "Developer",
1734
+ analyst: "Business Analyst",
1735
+ tea: "Test Architect",
1736
+ ux: "UX Designer",
1737
+ sm: "Scrum Master",
1738
+ techWriter: "Tech Writer"
1739
+ };
1740
+ function computeOverallProgress(syncResult) {
1741
+ const stepNumber = syncResult.stepNumber ?? 1;
1742
+ const totalSteps = syncResult.totalSteps ?? 6;
1743
+ const stepPct = syncResult.progressPercentage ?? 0;
1744
+ return Math.min(100, Math.round(((stepNumber - 1) * 100 + stepPct) / totalSteps));
1992
1745
  }
1993
- async function stopProcess() {
1994
- const pid = await readPid();
1995
- if (pid === null)
1996
- return;
1997
- if (!isAlive(pid)) {
1998
- await removePidFile2();
1999
- return;
1746
+ var ProgressRenderer = class {
1747
+ privacyShieldShown = false;
1748
+ discoveryShown = false;
1749
+ scanSummaryShown = false;
1750
+ validationShown = false;
1751
+ lastValidationSnapshot = "";
1752
+ lastValidationLineCount = 0;
1753
+ pushShown = false;
1754
+ fileStatusHeaderShown = false;
1755
+ lastFileStatusSnapshot = "";
1756
+ lastFileStatusLineCount = 0;
1757
+ renderPrivacyShield(enabled, spinner) {
1758
+ if (this.privacyShieldShown) return;
1759
+ this.privacyShieldShown = true;
1760
+ spinner.stop();
1761
+ console.log("");
1762
+ console.log(chalk4.cyan.bold(" \u2500\u2500 Privacy Shield \u2500\u2500"));
1763
+ if (enabled) {
1764
+ console.log(` ${chalk4.green("\u2713")} Privacy Shield active`);
1765
+ console.log(` ${chalk4.green("\u2713")} Private connection established`);
1766
+ } else {
1767
+ console.log(` ${chalk4.yellow("\u2139")} Privacy Shield not in current plan`);
1768
+ console.log(chalk4.dim(" Shield your data from the open internet."));
1769
+ }
1770
+ console.log("");
1771
+ spinner.start();
2000
1772
  }
2001
- try {
2002
- process.kill(pid, "SIGTERM");
2003
- } catch {
1773
+ renderDiscovery(result, spinner) {
1774
+ if (this.discoveryShown) return;
1775
+ this.discoveryShown = true;
1776
+ spinner.stop();
1777
+ console.log("");
1778
+ console.log(chalk4.cyan.bold(" \u2500\u2500 Repository Discovery \u2500\u2500"));
1779
+ if (result.languages.length > 0) {
1780
+ const langs = result.languages.slice(0, 5).map((l) => `${l.name} (${Math.round(l.percentage)}%)`).join(", ");
1781
+ console.log(` ${chalk4.dim("Languages:")} ${langs}`);
1782
+ }
1783
+ if (result.frameworks.length > 0) {
1784
+ console.log(
1785
+ ` ${chalk4.dim("Frameworks:")} ${result.frameworks.map((f) => f.name).join(", ")}`
1786
+ );
1787
+ }
1788
+ console.log(
1789
+ ` ${chalk4.dim("Structure:")} ${result.structureType} ${chalk4.dim(`(${result.fileCount} files)`)}`
1790
+ );
1791
+ if (result.existingDocs.length > 0) {
1792
+ console.log(` ${chalk4.dim("Existing docs:")} ${result.existingDocs.join(", ")}`);
1793
+ }
1794
+ if (result.fileTree && result.fileTree.length > 0) {
1795
+ this.renderTree(result.fileTree);
1796
+ }
1797
+ console.log("");
1798
+ spinner.start();
2004
1799
  }
2005
- const deadline = Date.now() + 5e3;
2006
- while (Date.now() < deadline && isAlive(pid)) {
2007
- await new Promise((r) => setTimeout(r, 200));
1800
+ renderTree(entries) {
1801
+ console.log("");
1802
+ console.log(chalk4.cyan.bold(" \u2500\u2500 Project Structure \u2500\u2500"));
1803
+ const root = { name: "", type: "tree", children: /* @__PURE__ */ new Map() };
1804
+ for (const entry of entries) {
1805
+ const parts = entry.path.split("/");
1806
+ let current = root;
1807
+ for (let i = 0; i < parts.length; i++) {
1808
+ const part = parts[i];
1809
+ if (!current.children.has(part)) {
1810
+ const isLast = i === parts.length - 1;
1811
+ current.children.set(part, {
1812
+ name: part,
1813
+ type: isLast ? entry.type : "tree",
1814
+ children: /* @__PURE__ */ new Map()
1815
+ });
1816
+ }
1817
+ current = current.children.get(part);
1818
+ }
1819
+ }
1820
+ const printNode = (node, prefix, isLast) => {
1821
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1822
+ const display = node.type === "tree" ? chalk4.bold.dim(`${node.name}/`) : node.name;
1823
+ console.log(` ${prefix}${connector}${display}`);
1824
+ const sorted = [...node.children.values()].sort((a, b) => {
1825
+ if (a.type === "tree" && b.type !== "tree") return -1;
1826
+ if (a.type !== "tree" && b.type === "tree") return 1;
1827
+ return a.name.localeCompare(b.name);
1828
+ });
1829
+ const childPrefix = prefix + (isLast ? " " : "\u2502 ");
1830
+ sorted.forEach((child, idx) => {
1831
+ printNode(child, childPrefix, idx === sorted.length - 1);
1832
+ });
1833
+ };
1834
+ const topLevel = [...root.children.values()].sort((a, b) => {
1835
+ if (a.type === "tree" && b.type !== "tree") return -1;
1836
+ if (a.type !== "tree" && b.type === "tree") return 1;
1837
+ return a.name.localeCompare(b.name);
1838
+ });
1839
+ topLevel.forEach((child, idx) => {
1840
+ printNode(child, "", idx === topLevel.length - 1);
1841
+ });
2008
1842
  }
2009
- if (isAlive(pid)) {
2010
- try {
2011
- process.kill(pid, "SIGKILL");
2012
- } catch {
1843
+ renderScanSummary(summary, spinner) {
1844
+ if (this.scanSummaryShown) return;
1845
+ this.scanSummaryShown = true;
1846
+ spinner.stop();
1847
+ console.log(
1848
+ chalk4.dim(
1849
+ ` Scan complete: ${summary.totalFiles} files, ${summary.totalFunctions} functions, ${summary.totalClasses} classes, ${summary.totalEndpoints} endpoints`
1850
+ )
1851
+ );
1852
+ console.log("");
1853
+ spinner.start();
1854
+ }
1855
+ renderValidation(progress, spinner) {
1856
+ const resultMap = new Map(progress.personaResults.map((r) => [r.persona, r.score]));
1857
+ const isComplete = progress.status === "complete";
1858
+ if (isComplete && this.validationShown) return;
1859
+ const snapshot = `${progress.round}:${progress.status}:${progress.personaResults.map((r) => `${r.persona}:${r.score}`).join(",")}`;
1860
+ if (snapshot === this.lastValidationSnapshot) return;
1861
+ this.lastValidationSnapshot = snapshot;
1862
+ if (isComplete) this.validationShown = true;
1863
+ spinner.stop();
1864
+ if (this.lastValidationLineCount > 0) {
1865
+ process.stdout.write(`\x1B[${this.lastValidationLineCount}A`);
1866
+ }
1867
+ const lines = [];
1868
+ const title = isComplete ? "Validation Results" : "Validation";
1869
+ lines.push(chalk4.cyan.bold(` \u2500\u2500 ${title} \u2500\u2500`));
1870
+ if (!isComplete) {
1871
+ lines.push(
1872
+ chalk4.dim(
1873
+ ` ${ALL_PERSONAS.length} AI reviewers checking context quality \u2014 issues are auto-fixed.`
1874
+ )
1875
+ );
1876
+ }
1877
+ const passCount = progress.personaResults.filter((r) => r.score === "PASS").length;
1878
+ if (isComplete) {
1879
+ const roundInfo = progress.round > 1 ? ` (${progress.round} rounds)` : "";
1880
+ lines.push(chalk4.dim(` ${passCount}/${ALL_PERSONAS.length} PASS${roundInfo}`));
1881
+ } else if (progress.personaResults.length > 0) {
1882
+ const statusSuffix = progress.status === "regenerating" ? chalk4.dim(" \u2014 improving files based on feedback") : "";
1883
+ lines.push(
1884
+ ` Round ${progress.round}/${progress.maxRounds}: ${passCount}/${ALL_PERSONAS.length} passed${statusSuffix}`
1885
+ );
1886
+ } else {
1887
+ lines.push(chalk4.dim(` Round ${progress.round}/${progress.maxRounds}: validating...`));
1888
+ }
1889
+ for (const persona of ALL_PERSONAS) {
1890
+ const label = PERSONA_LABELS[persona] ?? persona;
1891
+ const score = resultMap.get(persona);
1892
+ if (score) {
1893
+ const icon = score === "PASS" ? chalk4.green("\u2713") : chalk4.red("\u2717");
1894
+ const scoreColor = score === "PASS" ? chalk4.green : score === "PARTIAL" ? chalk4.yellow : chalk4.red;
1895
+ const fixingSuffix = progress.status === "regenerating" && score !== "PASS" ? chalk4.dim(" \u2192 fixing...") : "";
1896
+ lines.push(` ${icon} ${label}: ${scoreColor(score)}${fixingSuffix}`);
1897
+ } else {
1898
+ lines.push(` ${chalk4.dim("\u25CB")} ${chalk4.dim(label)}`);
1899
+ }
2013
1900
  }
1901
+ if (isComplete && passCount < ALL_PERSONAS.length) {
1902
+ lines.push(chalk4.yellow(" \u26A0 Continuing with best-effort context"));
1903
+ }
1904
+ lines.push("");
1905
+ for (const line of lines) {
1906
+ process.stdout.write(`\x1B[2K${line}
1907
+ `);
1908
+ }
1909
+ for (let i = lines.length; i < this.lastValidationLineCount; i++) {
1910
+ process.stdout.write("\x1B[2K\n");
1911
+ }
1912
+ this.lastValidationLineCount = lines.length;
1913
+ spinner.start();
1914
+ }
1915
+ renderFileStatuses(fileStatuses, spinner) {
1916
+ const snapshot = fileStatuses.map((f) => `${f.fileName}:${f.status}`).join(",");
1917
+ if (snapshot === this.lastFileStatusSnapshot) return;
1918
+ this.lastFileStatusSnapshot = snapshot;
1919
+ const completedCount = fileStatuses.filter((f) => f.status === "completed").length;
1920
+ const totalCount = fileStatuses.length;
1921
+ spinner.stop();
1922
+ if (!this.fileStatusHeaderShown) {
1923
+ this.fileStatusHeaderShown = true;
1924
+ console.log("");
1925
+ console.log(chalk4.cyan.bold(" \u2500\u2500 RepoWise Context Generation \u2500\u2500"));
1926
+ console.log(chalk4.dim(" Building AI-optimized context files from your codebase."));
1927
+ }
1928
+ if (this.lastFileStatusLineCount > 0) {
1929
+ process.stdout.write(`\x1B[${this.lastFileStatusLineCount}A`);
1930
+ }
1931
+ const coreFiles = [];
1932
+ const tailoredFiles = [];
1933
+ for (const file of fileStatuses) {
1934
+ const baseName = file.fileName.split("/").pop() ?? file.fileName;
1935
+ if (CORE_FILES.has(baseName)) {
1936
+ coreFiles.push(file);
1937
+ } else {
1938
+ tailoredFiles.push(file);
1939
+ }
1940
+ }
1941
+ const maxCoreLen = coreFiles.reduce((m, f) => Math.max(m, f.fileName.length), 0);
1942
+ const maxTailoredLen = tailoredFiles.reduce((m, f) => Math.max(m, f.fileName.length), 0);
1943
+ const formatFileLine = (file, padLen) => {
1944
+ const baseName = file.fileName.split("/").pop() ?? file.fileName;
1945
+ const desc = FILE_DESCRIPTIONS[baseName] ?? baseName.replace(/\.md$/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
1946
+ const padded = file.fileName.padEnd(padLen);
1947
+ switch (file.status) {
1948
+ case "completed":
1949
+ return ` ${chalk4.green("\u2713")} ${padded} ${chalk4.dim(`\u2014 ${desc}`)}`;
1950
+ case "generating":
1951
+ return ` ${chalk4.cyan("\u27F3")} ${padded} ${chalk4.dim(`\u2014 ${desc}`)}`;
1952
+ case "failed":
1953
+ return ` ${chalk4.red("\u2717")} ${padded} ${chalk4.dim(`\u2014 ${desc}`)}`;
1954
+ case "pending":
1955
+ return ` ${chalk4.dim("\u25CB")} ${chalk4.dim(`${padded} \u2014 ${desc}`)}`;
1956
+ }
1957
+ };
1958
+ const lines = [];
1959
+ lines.push(chalk4.dim(` Generated ${completedCount}/${totalCount} files`));
1960
+ lines.push("");
1961
+ lines.push(` ${chalk4.bold("Core")}`);
1962
+ for (const file of coreFiles) {
1963
+ lines.push(formatFileLine(file, maxCoreLen));
1964
+ }
1965
+ if (tailoredFiles.length > 0) {
1966
+ lines.push("");
1967
+ lines.push(` ${chalk4.bold("Tailored")}`);
1968
+ for (const file of tailoredFiles) {
1969
+ lines.push(formatFileLine(file, maxTailoredLen));
1970
+ }
1971
+ }
1972
+ lines.push("");
1973
+ for (const line of lines) {
1974
+ process.stdout.write(`\x1B[2K${line}
1975
+ `);
1976
+ }
1977
+ for (let i = lines.length; i < this.lastFileStatusLineCount; i++) {
1978
+ process.stdout.write("\x1B[2K\n");
1979
+ }
1980
+ this.lastFileStatusLineCount = lines.length;
1981
+ spinner.start();
2014
1982
  }
2015
- await removePidFile2();
2016
- }
2017
- async function isRunning() {
2018
- const pid = await readPid();
2019
- if (pid === null)
2020
- return false;
2021
- return isAlive(pid);
2022
- }
2023
- async function getStatus() {
2024
- const pid = await readPid();
2025
- if (pid === null)
2026
- return { running: false, pid: null };
2027
- const alive = isAlive(pid);
2028
- return { running: alive, pid: alive ? pid : null };
2029
- }
2030
- async function removePidFile2() {
2031
- try {
2032
- await unlink4(PID_PATH2);
2033
- } catch {
1983
+ renderPush(spinner) {
1984
+ if (this.pushShown) return;
1985
+ this.pushShown = true;
1986
+ spinner.stop();
1987
+ console.log("");
1988
+ console.log(chalk4.cyan.bold(" \u2500\u2500 Saving Context \u2500\u2500"));
1989
+ console.log(` ${chalk4.dim("Encrypting and saving context files to RepoWise servers...")}`);
1990
+ console.log("");
1991
+ spinner.start();
1992
+ }
1993
+ getSpinnerText(syncResult) {
1994
+ const stepLabel = syncResult.stepLabel ?? syncResult.currentStep ?? "Processing";
1995
+ const overallPct = computeOverallProgress(syncResult);
1996
+ let progressText = stepLabel;
1997
+ if (syncResult.scanProgress && !syncResult.scanProgress.summary) {
1998
+ progressText = `Scanning batch ${syncResult.scanProgress.currentBatch}/${syncResult.scanProgress.totalBatches}`;
1999
+ } else if (syncResult.generationProgress) {
2000
+ const gp = syncResult.generationProgress;
2001
+ if (gp.fileStatuses && gp.fileStatuses.length > 0) {
2002
+ const completed = gp.fileStatuses.filter((f) => f.status === "completed").length;
2003
+ const total = gp.fileStatuses.length;
2004
+ const generating = gp.fileStatuses.find((f) => f.status === "generating");
2005
+ if (completed === total) {
2006
+ progressText = stepLabel;
2007
+ } else if (generating) {
2008
+ progressText = `Generating ${generating.fileName} (${completed}/${total})`;
2009
+ } else {
2010
+ progressText = `Generating (${completed}/${total})`;
2011
+ }
2012
+ } else {
2013
+ progressText = `Generating ${gp.currentFileName} (${gp.currentFile}/${gp.totalFiles})`;
2014
+ }
2015
+ } else if (syncResult.validationProgress && syncResult.validationProgress.status !== "complete") {
2016
+ const vp = syncResult.validationProgress;
2017
+ if (vp.status === "regenerating") {
2018
+ progressText = `Improving files based on feedback (round ${vp.round})`;
2019
+ } else {
2020
+ progressText = `Validation round ${vp.round}/${vp.maxRounds}`;
2021
+ }
2022
+ }
2023
+ return `${progressText}... ${chalk4.dim(`(${overallPct}%)`)}`;
2034
2024
  }
2035
- }
2036
- var REPOWISE_DIR, PID_PATH2, LOG_DIR;
2037
- var init_process_manager = __esm({
2038
- "../listener/dist/process-manager.js"() {
2039
- "use strict";
2040
- REPOWISE_DIR = join11(homedir8(), ".repowise");
2041
- PID_PATH2 = join11(REPOWISE_DIR, "listener.pid");
2042
- LOG_DIR = join11(REPOWISE_DIR, "logs");
2025
+ update(syncResult, spinner) {
2026
+ if (syncResult.privacyShieldEnabled !== void 0) {
2027
+ this.renderPrivacyShield(syncResult.privacyShieldEnabled, spinner);
2028
+ }
2029
+ if (syncResult.discoveryResult) {
2030
+ this.renderDiscovery(syncResult.discoveryResult, spinner);
2031
+ }
2032
+ if (syncResult.scanProgress?.summary && syncResult.scanProgress.summary.totalFiles > 0) {
2033
+ this.renderScanSummary(syncResult.scanProgress.summary, spinner);
2034
+ }
2035
+ if (syncResult.generationProgress?.fileStatuses && syncResult.generationProgress.fileStatuses.length > 0) {
2036
+ this.renderFileStatuses(syncResult.generationProgress.fileStatuses, spinner);
2037
+ }
2038
+ if (syncResult.validationProgress) {
2039
+ this.renderValidation(syncResult.validationProgress, spinner);
2040
+ }
2041
+ if (syncResult.currentStep === "push-context") {
2042
+ this.renderPush(spinner);
2043
+ }
2044
+ spinner.text = this.getSpinnerText(syncResult);
2043
2045
  }
2044
- });
2046
+ };
2045
2047
 
2046
2048
  // src/commands/create.ts
2047
- var create_exports = {};
2048
- __export(create_exports, {
2049
- create: () => create
2050
- });
2051
- import { execSync } from "child_process";
2052
- import { mkdirSync, writeFileSync } from "fs";
2053
- import { join as join12 } from "path";
2054
- import chalk5 from "chalk";
2055
- import ora from "ora";
2056
2049
  function detectRepoRoot() {
2057
2050
  return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
2058
2051
  }
@@ -2075,6 +2068,9 @@ function formatElapsed(ms) {
2075
2068
  if (minutes === 0) return `${seconds}s`;
2076
2069
  return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
2077
2070
  }
2071
+ var POLL_INTERVAL_MS = 3e3;
2072
+ var MAX_POLL_ATTEMPTS = 600;
2073
+ var DEFAULT_CONTEXT_FOLDER = "repowise-context";
2078
2074
  async function create() {
2079
2075
  const startTime = Date.now();
2080
2076
  const spinner = ora("Checking authentication...").start();
@@ -2331,10 +2327,8 @@ Files are stored on our servers (not in git). Retry when online.`
2331
2327
  });
2332
2328
  let listenerRunning = false;
2333
2329
  try {
2334
- const { install: install2 } = await Promise.resolve().then(() => (init_service_installer(), service_installer_exports));
2335
- const { startBackground: startBackground2 } = await Promise.resolve().then(() => (init_process_manager(), process_manager_exports));
2336
- await install2();
2337
- await startBackground2();
2330
+ await install();
2331
+ await startBackground();
2338
2332
  listenerRunning = true;
2339
2333
  } catch {
2340
2334
  console.log(
@@ -2372,29 +2366,8 @@ Files are stored on our servers (not in git). Retry when online.`
2372
2366
  process.exitCode = 1;
2373
2367
  }
2374
2368
  }
2375
- var POLL_INTERVAL_MS, MAX_POLL_ATTEMPTS, DEFAULT_CONTEXT_FOLDER;
2376
- var init_create = __esm({
2377
- "src/commands/create.ts"() {
2378
- "use strict";
2379
- init_auth2();
2380
- init_api();
2381
- init_prompts();
2382
- init_ai_tools();
2383
- init_config();
2384
- init_env();
2385
- init_interview_handler();
2386
- init_progress_renderer();
2387
- POLL_INTERVAL_MS = 3e3;
2388
- MAX_POLL_ATTEMPTS = 600;
2389
- DEFAULT_CONTEXT_FOLDER = "repowise-context";
2390
- }
2391
- });
2392
2369
 
2393
2370
  // src/commands/login.ts
2394
- var login_exports = {};
2395
- __export(login_exports, {
2396
- login: () => login
2397
- });
2398
2371
  import chalk6 from "chalk";
2399
2372
  import ora2 from "ora";
2400
2373
  async function login(options = {}) {
@@ -2445,18 +2418,8 @@ Waiting for authentication...`);
2445
2418
  process.exitCode = 1;
2446
2419
  }
2447
2420
  }
2448
- var init_login = __esm({
2449
- "src/commands/login.ts"() {
2450
- "use strict";
2451
- init_auth2();
2452
- }
2453
- });
2454
2421
 
2455
2422
  // src/commands/logout.ts
2456
- var logout_exports = {};
2457
- __export(logout_exports, {
2458
- logout: () => logout
2459
- });
2460
2423
  import chalk7 from "chalk";
2461
2424
  async function logout() {
2462
2425
  const creds = await getStoredCredentials2();
@@ -2467,21 +2430,12 @@ async function logout() {
2467
2430
  await clearCredentials();
2468
2431
  console.log(chalk7.green("Logged out successfully."));
2469
2432
  }
2470
- var init_logout = __esm({
2471
- "src/commands/logout.ts"() {
2472
- "use strict";
2473
- init_auth2();
2474
- }
2475
- });
2476
2433
 
2477
2434
  // src/commands/status.ts
2478
- var status_exports = {};
2479
- __export(status_exports, {
2480
- status: () => status
2481
- });
2482
2435
  import { readFile as readFile9 } from "fs/promises";
2483
2436
  import { homedir as homedir9 } from "os";
2484
2437
  import { join as join13 } from "path";
2438
+ var STATE_PATH2 = join13(homedir9(), ".repowise", "listener-state.json");
2485
2439
  async function status() {
2486
2440
  let state = null;
2487
2441
  try {
@@ -2492,16 +2446,14 @@ async function status() {
2492
2446
  let processRunning = false;
2493
2447
  let pid = null;
2494
2448
  try {
2495
- const { getStatus: getStatus2 } = await Promise.resolve().then(() => (init_process_manager(), process_manager_exports));
2496
- const processStatus = await getStatus2();
2449
+ const processStatus = await getStatus();
2497
2450
  processRunning = processStatus.running;
2498
2451
  pid = processStatus.pid;
2499
2452
  } catch {
2500
2453
  }
2501
2454
  let serviceInstalled = false;
2502
2455
  try {
2503
- const { isInstalled: isInstalled2 } = await Promise.resolve().then(() => (init_service_installer(), service_installer_exports));
2504
- serviceInstalled = await isInstalled2();
2456
+ serviceInstalled = await isInstalled();
2505
2457
  } catch {
2506
2458
  }
2507
2459
  console.log("RepoWise Status");
@@ -2528,24 +2480,16 @@ async function status() {
2528
2480
  console.log(` ${repoId}: last sync ${syncTime} (commit: ${commit})`);
2529
2481
  }
2530
2482
  }
2531
- var STATE_PATH2;
2532
- var init_status = __esm({
2533
- "src/commands/status.ts"() {
2534
- "use strict";
2535
- STATE_PATH2 = join13(homedir9(), ".repowise", "listener-state.json");
2536
- }
2537
- });
2538
2483
 
2539
2484
  // src/commands/sync.ts
2540
- var sync_exports = {};
2541
- __export(sync_exports, {
2542
- sync: () => sync
2543
- });
2544
2485
  import { execSync as execSync2 } from "child_process";
2545
2486
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
2546
2487
  import { join as join14 } from "path";
2547
2488
  import chalk8 from "chalk";
2548
2489
  import ora3 from "ora";
2490
+ var POLL_INTERVAL_MS2 = 3e3;
2491
+ var MAX_POLL_ATTEMPTS2 = 600;
2492
+ var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
2549
2493
  function detectRepoRoot2() {
2550
2494
  return execSync2("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
2551
2495
  }
@@ -2730,30 +2674,12 @@ Retry when online.`));
2730
2674
  process.exitCode = 1;
2731
2675
  }
2732
2676
  }
2733
- var POLL_INTERVAL_MS2, MAX_POLL_ATTEMPTS2, DEFAULT_CONTEXT_FOLDER2;
2734
- var init_sync = __esm({
2735
- "src/commands/sync.ts"() {
2736
- "use strict";
2737
- init_auth2();
2738
- init_api();
2739
- init_interview_handler();
2740
- init_progress_renderer();
2741
- POLL_INTERVAL_MS2 = 3e3;
2742
- MAX_POLL_ATTEMPTS2 = 600;
2743
- DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
2744
- }
2745
- });
2746
2677
 
2747
2678
  // src/commands/listen.ts
2748
- var listen_exports = {};
2749
- __export(listen_exports, {
2750
- listen: () => listen
2751
- });
2752
2679
  async function listen(options) {
2753
2680
  if (options.install) {
2754
2681
  try {
2755
- const { install: install2 } = await Promise.resolve().then(() => (init_service_installer(), service_installer_exports));
2756
- await install2();
2682
+ await install();
2757
2683
  console.log("Auto-start service installed. The listener will start on boot.");
2758
2684
  } catch (err) {
2759
2685
  const message = err instanceof Error ? err.message : "Unknown error";
@@ -2765,8 +2691,7 @@ async function listen(options) {
2765
2691
  }
2766
2692
  if (options.uninstall) {
2767
2693
  try {
2768
- const { uninstall: uninstall2 } = await Promise.resolve().then(() => (init_service_installer(), service_installer_exports));
2769
- await uninstall2();
2694
+ await uninstall();
2770
2695
  console.log("Auto-start service removed.");
2771
2696
  } catch (err) {
2772
2697
  const message = err instanceof Error ? err.message : "Unknown error";
@@ -2783,33 +2708,21 @@ async function listen(options) {
2783
2708
  }
2784
2709
  console.log("Starting RepoWise listener...");
2785
2710
  try {
2786
- const { startListener: startListener2 } = await Promise.resolve().then(() => (init_main(), main_exports));
2787
- await startListener2();
2711
+ await startListener();
2788
2712
  } catch {
2789
- console.error("Failed to start listener. Ensure @repowise/listener is installed.");
2713
+ console.error("Failed to start listener.");
2790
2714
  process.exitCode = 1;
2791
2715
  }
2792
2716
  }
2793
- var init_listen = __esm({
2794
- "src/commands/listen.ts"() {
2795
- "use strict";
2796
- init_auth2();
2797
- }
2798
- });
2799
2717
 
2800
2718
  // src/commands/start.ts
2801
- var start_exports = {};
2802
- __export(start_exports, {
2803
- start: () => start
2804
- });
2805
2719
  async function start() {
2806
2720
  try {
2807
- const { isRunning: isRunning2, startBackground: startBackground2 } = await Promise.resolve().then(() => (init_process_manager(), process_manager_exports));
2808
- if (await isRunning2()) {
2721
+ if (await isRunning()) {
2809
2722
  console.log("Listener is already running.");
2810
2723
  return;
2811
2724
  }
2812
- const pid = await startBackground2();
2725
+ const pid = await startBackground();
2813
2726
  console.log(`Listener started (PID: ${pid}).`);
2814
2727
  } catch (err) {
2815
2728
  const message = err instanceof Error ? err.message : "Unknown error";
@@ -2817,25 +2730,15 @@ async function start() {
2817
2730
  process.exitCode = 1;
2818
2731
  }
2819
2732
  }
2820
- var init_start = __esm({
2821
- "src/commands/start.ts"() {
2822
- "use strict";
2823
- }
2824
- });
2825
2733
 
2826
2734
  // src/commands/stop.ts
2827
- var stop_exports = {};
2828
- __export(stop_exports, {
2829
- stop: () => stop2
2830
- });
2831
2735
  async function stop2() {
2832
2736
  try {
2833
- const { isRunning: isRunning2, stopProcess: stopProcess2 } = await Promise.resolve().then(() => (init_process_manager(), process_manager_exports));
2834
- if (!await isRunning2()) {
2737
+ if (!await isRunning()) {
2835
2738
  console.log("Listener is not running.");
2836
2739
  return;
2837
2740
  }
2838
- await stopProcess2();
2741
+ await stopProcess();
2839
2742
  console.log("Listener stopped.");
2840
2743
  } catch (err) {
2841
2744
  const message = err instanceof Error ? err.message : "Unknown error";
@@ -2843,17 +2746,8 @@ async function stop2() {
2843
2746
  process.exitCode = 1;
2844
2747
  }
2845
2748
  }
2846
- var init_stop = __esm({
2847
- "src/commands/stop.ts"() {
2848
- "use strict";
2849
- }
2850
- });
2851
2749
 
2852
2750
  // src/commands/config.ts
2853
- var config_exports = {};
2854
- __export(config_exports, {
2855
- config: () => config
2856
- });
2857
2751
  import chalk9 from "chalk";
2858
2752
  import ora4 from "ora";
2859
2753
  import { select } from "@inquirer/prompts";
@@ -2939,58 +2833,8 @@ async function config() {
2939
2833
  process.exitCode = 1;
2940
2834
  }
2941
2835
  }
2942
- var init_config3 = __esm({
2943
- "src/commands/config.ts"() {
2944
- "use strict";
2945
- init_auth2();
2946
- init_api();
2947
- }
2948
- });
2949
-
2950
- // bin/repowise.ts
2951
- init_env();
2952
- import { readFileSync } from "fs";
2953
- import { fileURLToPath as fileURLToPath3 } from "url";
2954
- import { dirname as dirname2, join as join15 } from "path";
2955
- import { Command } from "commander";
2956
-
2957
- // src/lib/welcome.ts
2958
- init_config();
2959
- import chalk from "chalk";
2960
- var W = 41;
2961
- function row(styled, visible) {
2962
- return chalk.cyan(" \u2502") + styled + " ".repeat(W - visible) + chalk.cyan("\u2502");
2963
- }
2964
- async function showWelcome(currentVersion) {
2965
- try {
2966
- const config2 = await getConfig();
2967
- if (config2.lastSeenVersion === currentVersion) return;
2968
- const isUpgrade = !!config2.lastSeenVersion;
2969
- const tag = isUpgrade ? "updated" : "installed";
2970
- const titleText = `RepoWise v${currentVersion}`;
2971
- const titleStyled = " " + chalk.bold(titleText) + chalk.green(` \u2713 ${tag}`);
2972
- const titleVisible = 3 + titleText.length + 3 + tag.length;
2973
- const border = "\u2500".repeat(W);
2974
- console.log("");
2975
- console.log(chalk.cyan(` \u256D${border}\u256E`));
2976
- console.log(row("", 0));
2977
- console.log(row(titleStyled, titleVisible));
2978
- console.log(row("", 0));
2979
- console.log(row(" " + chalk.dim("Get started:"), 15));
2980
- console.log(row(" $ " + chalk.bold("repowise create"), 22));
2981
- console.log(row("", 0));
2982
- console.log(row(" " + chalk.dim("Thank you for using RepoWise!"), 32));
2983
- console.log(row(" " + chalk.dim("https://repowise.ai"), 22));
2984
- console.log(row("", 0));
2985
- console.log(chalk.cyan(` \u2570${border}\u256F`));
2986
- console.log("");
2987
- await saveConfig({ ...config2, lastSeenVersion: currentVersion });
2988
- } catch {
2989
- }
2990
- }
2991
2836
 
2992
2837
  // bin/repowise.ts
2993
- init_main();
2994
2838
  var __filename = fileURLToPath3(import.meta.url);
2995
2839
  var __dirname = dirname2(__filename);
2996
2840
  var pkg = JSON.parse(readFileSync(join15(__dirname, "..", "..", "package.json"), "utf-8"));
@@ -3002,40 +2846,31 @@ program.name("repowise").description("AI-optimized codebase context generator").
3002
2846
  await showWelcome(pkg.version);
3003
2847
  });
3004
2848
  program.command("create").description("Create context for a repository").action(async () => {
3005
- const { create: create2 } = await Promise.resolve().then(() => (init_create(), create_exports));
3006
- await create2();
2849
+ await create();
3007
2850
  });
3008
2851
  program.command("login").description("Authenticate with RepoWise").option("--no-browser", "Print login URL instead of opening browser").action(async (options) => {
3009
- const { login: login2 } = await Promise.resolve().then(() => (init_login(), login_exports));
3010
- await login2(options);
2852
+ await login(options);
3011
2853
  });
3012
2854
  program.command("logout").description("Sign out of RepoWise").action(async () => {
3013
- const { logout: logout2 } = await Promise.resolve().then(() => (init_logout(), logout_exports));
3014
- await logout2();
2855
+ await logout();
3015
2856
  });
3016
2857
  program.command("status").description("Show current status").action(async () => {
3017
- const { status: status2 } = await Promise.resolve().then(() => (init_status(), status_exports));
3018
- await status2();
2858
+ await status();
3019
2859
  });
3020
2860
  program.command("sync").description("Trigger a manual sync").action(async () => {
3021
- const { sync: sync2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
3022
- await sync2();
2861
+ await sync();
3023
2862
  });
3024
2863
  program.command("listen").description("Start the context listener").option("--install", "Install auto-start service").option("--uninstall", "Remove auto-start service").action(async (options) => {
3025
- const { listen: listen2 } = await Promise.resolve().then(() => (init_listen(), listen_exports));
3026
- await listen2(options);
2864
+ await listen(options);
3027
2865
  });
3028
2866
  program.command("start").description("Start the listener as a background process").action(async () => {
3029
- const { start: start2 } = await Promise.resolve().then(() => (init_start(), start_exports));
3030
- await start2();
2867
+ await start();
3031
2868
  });
3032
2869
  program.command("stop").description("Stop the running listener process").action(async () => {
3033
- const { stop: stop3 } = await Promise.resolve().then(() => (init_stop(), stop_exports));
3034
- await stop3();
2870
+ await stop2();
3035
2871
  });
3036
2872
  program.command("config").description("Manage configuration").action(async () => {
3037
- const { config: config2 } = await Promise.resolve().then(() => (init_config3(), config_exports));
3038
- await config2();
2873
+ await config();
3039
2874
  });
3040
2875
  if (process.argv[2] === "__listener") {
3041
2876
  process.on("uncaughtException", (err) => {