repowise 0.1.63 → 0.1.64

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 (2) hide show
  1. package/dist/bin/repowise.js +212 -44
  2. package/package.json +1 -1
@@ -3,26 +3,28 @@
3
3
  // bin/repowise.ts
4
4
  import { readFileSync } from "fs";
5
5
  import { fileURLToPath as fileURLToPath3 } from "url";
6
- import { dirname as dirname2, join as join15 } from "path";
6
+ import { dirname as dirname3, join as join15 } from "path";
7
7
  import { Command } from "commander";
8
8
 
9
9
  // ../listener/dist/main.js
10
- import { readFile as readFile4, writeFile as writeFile4, unlink, mkdir as mkdir4 } from "fs/promises";
10
+ import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir5 } from "fs/promises";
11
11
  import { homedir as homedir4 } from "os";
12
12
  import { join as join6 } from "path";
13
13
 
14
14
  // ../listener/dist/lib/config.js
15
- import { readFile } from "fs/promises";
15
+ import { readFile, writeFile, rename, mkdir, chmod } from "fs/promises";
16
16
  import { homedir } from "os";
17
17
  import { join } from "path";
18
18
  var CONFIG_DIR = join(homedir(), ".repowise");
19
19
  var CONFIG_PATH = join(CONFIG_DIR, "config.json");
20
20
  var DEFAULT_API_URL = "https://api.repowise.ai";
21
+ var rawConfigCache = null;
21
22
  async function getListenerConfig() {
22
23
  const apiUrl = process.env["REPOWISE_API_URL"] ?? DEFAULT_API_URL;
23
24
  try {
24
25
  const data = await readFile(CONFIG_PATH, "utf-8");
25
26
  const raw = JSON.parse(data);
27
+ rawConfigCache = raw;
26
28
  const validRepos = (raw.repos ?? []).filter((r) => typeof r === "object" && r !== null && typeof r.repoId === "string" && typeof r.localPath === "string");
27
29
  return {
28
30
  defaultApiUrl: raw.apiUrl ?? apiUrl,
@@ -32,9 +34,22 @@ async function getListenerConfig() {
32
34
  return { defaultApiUrl: apiUrl, repos: [] };
33
35
  }
34
36
  }
37
+ async function saveListenerConfig(config2) {
38
+ await mkdir(CONFIG_DIR, { recursive: true, mode: 448 });
39
+ const output = {
40
+ ...rawConfigCache ?? {},
41
+ apiUrl: config2.defaultApiUrl,
42
+ repos: config2.repos
43
+ };
44
+ const tmpPath = CONFIG_PATH + ".tmp";
45
+ await writeFile(tmpPath, JSON.stringify(output, null, 2));
46
+ await chmod(tmpPath, 384);
47
+ await rename(tmpPath, CONFIG_PATH);
48
+ rawConfigCache = output;
49
+ }
35
50
 
36
51
  // ../listener/dist/lib/state.js
37
- import { readFile as readFile2, writeFile, mkdir, chmod } from "fs/promises";
52
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
38
53
  import { homedir as homedir2 } from "os";
39
54
  import { join as join2 } from "path";
40
55
  var CONFIG_DIR2 = join2(homedir2(), ".repowise");
@@ -54,13 +69,109 @@ async function loadState() {
54
69
  }
55
70
  }
56
71
  async function saveState(state) {
57
- await mkdir(CONFIG_DIR2, { recursive: true, mode: 448 });
58
- await writeFile(STATE_PATH, JSON.stringify(state, null, 2));
59
- await chmod(STATE_PATH, 384);
72
+ await mkdir2(CONFIG_DIR2, { recursive: true, mode: 448 });
73
+ await writeFile2(STATE_PATH, JSON.stringify(state, null, 2));
74
+ await chmod2(STATE_PATH, 384);
75
+ }
76
+
77
+ // ../listener/dist/lib/reconcile.js
78
+ import { basename, dirname } from "path";
79
+ function reconcileRepos(configRepos, activeRepos, state, apiUrl) {
80
+ if (activeRepos.length === 0) {
81
+ return { updated: false, repos: configRepos, changes: [] };
82
+ }
83
+ const changes = [];
84
+ let updated = false;
85
+ const updatedRepos = configRepos.map((repo) => {
86
+ if (repo.apiUrl && repo.apiUrl !== apiUrl) {
87
+ return repo;
88
+ }
89
+ if (activeRepos.some((ar) => ar.repoId === repo.repoId)) {
90
+ if (!repo.platform || !repo.externalId) {
91
+ const match = activeRepos.find((ar) => ar.repoId === repo.repoId);
92
+ if (match) {
93
+ changes.push(`Backfilled platform+externalId for ${match.fullName} (${repo.repoId})`);
94
+ updated = true;
95
+ return {
96
+ ...repo,
97
+ platform: match.platform,
98
+ externalId: match.externalId
99
+ };
100
+ }
101
+ }
102
+ return repo;
103
+ }
104
+ if (repo.platform && repo.externalId) {
105
+ const match = activeRepos.find((ar) => ar.platform === repo.platform && ar.externalId === repo.externalId);
106
+ if (match) {
107
+ const oldId = repo.repoId;
108
+ changes.push(`Updated repoId for ${match.fullName}: ${oldId} \u2192 ${match.repoId}`);
109
+ migrateState(state, oldId, match.repoId);
110
+ updated = true;
111
+ return {
112
+ ...repo,
113
+ repoId: match.repoId,
114
+ platform: match.platform,
115
+ externalId: match.externalId
116
+ };
117
+ }
118
+ }
119
+ const dirName = basename(repo.localPath);
120
+ const parentDir = basename(dirname(repo.localPath));
121
+ const fullPathName = `${parentDir}/${dirName}`;
122
+ let matches = activeRepos.filter((ar) => ar.fullName === fullPathName);
123
+ if (matches.length === 0) {
124
+ matches = activeRepos.filter((ar) => ar.name === dirName);
125
+ }
126
+ if (matches.length === 1) {
127
+ const match = matches[0];
128
+ if (match.repoId !== repo.repoId) {
129
+ const oldId = repo.repoId;
130
+ changes.push(`Updated repoId for ${match.fullName} (name match): ${oldId} \u2192 ${match.repoId}`);
131
+ migrateState(state, oldId, match.repoId);
132
+ updated = true;
133
+ return {
134
+ ...repo,
135
+ repoId: match.repoId,
136
+ platform: match.platform,
137
+ externalId: match.externalId
138
+ };
139
+ }
140
+ if (!repo.platform || !repo.externalId) {
141
+ changes.push(`Backfilled platform+externalId for ${match.fullName} (name match)`);
142
+ updated = true;
143
+ return {
144
+ ...repo,
145
+ platform: match.platform,
146
+ externalId: match.externalId
147
+ };
148
+ }
149
+ } else if (matches.length > 1) {
150
+ console.warn(`Ambiguous name match for ${repo.localPath} \u2014 ${matches.length} repos match "${dirName}". Skipping auto-update. Add platform+externalId to config or re-run \`repowise create\`.`);
151
+ }
152
+ return repo;
153
+ });
154
+ return { updated, repos: updatedRepos, changes };
155
+ }
156
+ function migrateState(state, oldId, newId) {
157
+ const oldState = state.repos[oldId];
158
+ if (oldState) {
159
+ const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
160
+ state.repos[newId] = {
161
+ lastSyncTimestamp: twentyFourHoursAgo,
162
+ lastSyncCommitSha: null
163
+ };
164
+ delete state.repos[oldId];
165
+ } else {
166
+ state.repos[newId] = {
167
+ lastSyncTimestamp: new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString(),
168
+ lastSyncCommitSha: null
169
+ };
170
+ }
60
171
  }
61
172
 
62
173
  // ../listener/dist/lib/auth.js
63
- import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
174
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, chmod as chmod3 } from "fs/promises";
64
175
  import { homedir as homedir3 } from "os";
65
176
  import { join as join3 } from "path";
66
177
  var CONFIG_DIR3 = join3(homedir3(), ".repowise");
@@ -113,9 +224,9 @@ async function getStoredCredentials() {
113
224
  }
114
225
  }
115
226
  async function storeCredentials(credentials) {
116
- await mkdir2(CONFIG_DIR3, { recursive: true, mode: 448 });
117
- await writeFile2(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
118
- await chmod2(CREDENTIALS_PATH, 384);
227
+ await mkdir3(CONFIG_DIR3, { recursive: true, mode: 448 });
228
+ await writeFile3(CREDENTIALS_PATH, JSON.stringify(credentials, null, 2));
229
+ await chmod3(CREDENTIALS_PATH, 384);
119
230
  }
120
231
  async function getValidCredentials() {
121
232
  const creds = await getStoredCredentials();
@@ -146,7 +257,7 @@ var PollClient = class {
146
257
  constructor(apiUrl) {
147
258
  this.apiUrl = apiUrl;
148
259
  }
149
- async poll(repoIds, since) {
260
+ async poll(repoIds, since, options) {
150
261
  const credentials = await getValidCredentials();
151
262
  if (!credentials) {
152
263
  throw new AuthError("Not logged in. Run `repowise login` first.");
@@ -155,6 +266,9 @@ var PollClient = class {
155
266
  repoIds: repoIds.join(","),
156
267
  since
157
268
  });
269
+ if (options?.includeActiveRepos) {
270
+ params.set("includeActiveRepos", "true");
271
+ }
158
272
  const controller = new AbortController();
159
273
  const timeoutId = setTimeout(() => controller.abort(), POLL_TIMEOUT_MS);
160
274
  try {
@@ -239,7 +353,7 @@ function notifyContextUpdated(repoId, fileCount) {
239
353
 
240
354
  // ../listener/dist/context-fetcher.js
241
355
  import { execFile } from "child_process";
242
- import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
356
+ import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
243
357
  import { join as join5 } from "path";
244
358
  import { promisify } from "util";
245
359
 
@@ -337,7 +451,7 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
337
451
  return { success: true, updatedFiles: [] };
338
452
  }
339
453
  const contextDir = join5(localPath, "repowise-context");
340
- await mkdir3(contextDir, { recursive: true });
454
+ await mkdir4(contextDir, { recursive: true });
341
455
  const updatedFiles = [];
342
456
  for (const file of files) {
343
457
  const urlRes = await fetch(`${apiUrl}/v1/repos/${repoId}/context/${file.fileName}`, {
@@ -359,7 +473,7 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
359
473
  continue;
360
474
  }
361
475
  const content = await contentRes.text();
362
- await writeFile3(join5(contextDir, file.fileName), content, "utf-8");
476
+ await writeFile4(join5(contextDir, file.fileName), content, "utf-8");
363
477
  updatedFiles.push(file.fileName);
364
478
  }
365
479
  console.log(`Context fetch for ${repoId}: downloaded ${updatedFiles.length}/${files.length} file(s)`);
@@ -377,8 +491,8 @@ var PID_PATH = join6(homedir4(), ".repowise", "listener.pid");
377
491
  var running = false;
378
492
  var sleepResolve = null;
379
493
  async function writePidFile() {
380
- await mkdir4(join6(homedir4(), ".repowise"), { recursive: true });
381
- await writeFile4(PID_PATH, String(process.pid));
494
+ await mkdir5(join6(homedir4(), ".repowise"), { recursive: true });
495
+ await writeFile5(PID_PATH, String(process.pid));
382
496
  }
383
497
  async function removePidFile() {
384
498
  try {
@@ -541,6 +655,8 @@ async function startListener(options) {
541
655
  }
542
656
  }
543
657
  let pollIntervalMs = 5e3;
658
+ let pollCycleCount = 0;
659
+ const RECONCILE_EVERY_N_CYCLES = 60;
544
660
  const origLog = console.log.bind(console);
545
661
  const origError = console.error.bind(console);
546
662
  const origWarn = console.warn.bind(console);
@@ -566,6 +682,8 @@ async function startListener(options) {
566
682
  let authErrorGroup = null;
567
683
  let minPollInterval = pollIntervalMs;
568
684
  let connectionLostNotified = false;
685
+ pollCycleCount++;
686
+ const shouldReconcile = pollCycleCount % RECONCILE_EVERY_N_CYCLES === 0;
569
687
  for (const group of groups) {
570
688
  if (!running)
571
689
  break;
@@ -577,7 +695,31 @@ async function startListener(options) {
577
695
  const ts2 = state.repos[id]?.lastSyncTimestamp ?? now;
578
696
  return ts2 < earliest ? ts2 : earliest;
579
697
  }, now);
580
- const response = await group.pollClient.poll(group.repoIds, sinceTimestamp);
698
+ const response = await group.pollClient.poll(group.repoIds, sinceTimestamp, {
699
+ includeActiveRepos: shouldReconcile
700
+ });
701
+ if (response.activeRepos && response.activeRepos.length > 0) {
702
+ try {
703
+ const result = reconcileRepos(config2.repos, response.activeRepos, state, group.apiUrl);
704
+ if (result.updated) {
705
+ config2.repos = result.repos;
706
+ await saveListenerConfig(config2);
707
+ await saveState(state);
708
+ group.repoIds = result.repos.filter((r) => (r.apiUrl ?? config2.defaultApiUrl) === group.apiUrl).map((r) => r.repoId);
709
+ group.repoLocalPaths.clear();
710
+ for (const r of result.repos) {
711
+ if ((r.apiUrl ?? config2.defaultApiUrl) === group.apiUrl) {
712
+ group.repoLocalPaths.set(r.repoId, r.localPath);
713
+ }
714
+ }
715
+ for (const change of result.changes) {
716
+ console.log(`[reconcile] ${change}`);
717
+ }
718
+ }
719
+ } catch (reconcileErr) {
720
+ console.warn("Repo reconciliation failed \u2014 will retry next cycle", reconcileErr instanceof Error ? reconcileErr.message : String(reconcileErr));
721
+ }
722
+ }
581
723
  if (group.offline.isOffline) {
582
724
  await handleCatchUp(group.offline, group.pollClient, group.repoIds, state, group.repoLocalPaths, group.apiUrl);
583
725
  group.offline.isOffline = false;
@@ -684,7 +826,7 @@ function getEnvConfig() {
684
826
  import chalk from "chalk";
685
827
 
686
828
  // src/lib/config.ts
687
- import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
829
+ import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
688
830
  import { homedir as homedir5 } from "os";
689
831
  import { join as join7 } from "path";
690
832
  var CONFIG_DIR4 = join7(homedir5(), ".repowise");
@@ -698,8 +840,8 @@ async function getConfig() {
698
840
  }
699
841
  }
700
842
  async function saveConfig(config2) {
701
- await mkdir5(CONFIG_DIR4, { recursive: true });
702
- await writeFile5(CONFIG_PATH2, JSON.stringify(config2, null, 2));
843
+ await mkdir6(CONFIG_DIR4, { recursive: true });
844
+ await writeFile6(CONFIG_PATH2, JSON.stringify(config2, null, 2));
703
845
  }
704
846
 
705
847
  // src/lib/welcome.ts
@@ -743,7 +885,7 @@ import { join as join12 } from "path";
743
885
  // ../listener/dist/process-manager.js
744
886
  import { spawn } from "child_process";
745
887
  import { openSync, closeSync } from "fs";
746
- import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir6, unlink as unlink2 } from "fs/promises";
888
+ import { readFile as readFile6, writeFile as writeFile7, mkdir as mkdir7, unlink as unlink2 } from "fs/promises";
747
889
  import { homedir as homedir6 } from "os";
748
890
  import { join as join8 } from "path";
749
891
  import { createRequire } from "module";
@@ -781,7 +923,7 @@ function isAlive(pid) {
781
923
  }
782
924
  }
783
925
  async function startBackground() {
784
- await mkdir6(LOG_DIR, { recursive: true });
926
+ await mkdir7(LOG_DIR, { recursive: true });
785
927
  const cmd = resolveListenerCommand();
786
928
  const stdoutFd = openSync(join8(LOG_DIR, "listener-stdout.log"), "a");
787
929
  const stderrFd = openSync(join8(LOG_DIR, "listener-stderr.log"), "a");
@@ -797,7 +939,7 @@ async function startBackground() {
797
939
  const pid = child.pid;
798
940
  if (!pid)
799
941
  throw new Error("Failed to spawn listener process");
800
- await writeFile6(PID_PATH2, String(pid));
942
+ await writeFile7(PID_PATH2, String(pid));
801
943
  return pid;
802
944
  }
803
945
  async function stopProcess() {
@@ -846,7 +988,7 @@ async function removePidFile2() {
846
988
 
847
989
  // ../listener/dist/service-installer.js
848
990
  import { execFile as execFile2 } from "child_process";
849
- import { writeFile as writeFile7, mkdir as mkdir7, unlink as unlink3 } from "fs/promises";
991
+ import { writeFile as writeFile8, mkdir as mkdir8, unlink as unlink3 } from "fs/promises";
850
992
  import { homedir as homedir7 } from "os";
851
993
  import { join as join9 } from "path";
852
994
  import { createRequire as createRequire2 } from "module";
@@ -908,13 +1050,13 @@ ${programArgs}
908
1050
  </plist>`;
909
1051
  }
910
1052
  async function darwinInstall() {
911
- await mkdir7(logDir(), { recursive: true });
912
- await mkdir7(join9(homedir7(), "Library", "LaunchAgents"), { recursive: true });
1053
+ await mkdir8(logDir(), { recursive: true });
1054
+ await mkdir8(join9(homedir7(), "Library", "LaunchAgents"), { recursive: true });
913
1055
  try {
914
1056
  await exec("launchctl", ["unload", plistPath()]);
915
1057
  } catch {
916
1058
  }
917
- await writeFile7(plistPath(), buildPlist());
1059
+ await writeFile8(plistPath(), buildPlist());
918
1060
  await exec("launchctl", ["load", plistPath()]);
919
1061
  }
920
1062
  async function darwinUninstall() {
@@ -960,9 +1102,9 @@ StandardError=append:${join9(logs, "listener-stderr.log")}
960
1102
  WantedBy=default.target`;
961
1103
  }
962
1104
  async function linuxInstall() {
963
- await mkdir7(logDir(), { recursive: true });
964
- await mkdir7(join9(homedir7(), ".config", "systemd", "user"), { recursive: true });
965
- await writeFile7(unitPath(), buildUnit());
1105
+ await mkdir8(logDir(), { recursive: true });
1106
+ await mkdir8(join9(homedir7(), ".config", "systemd", "user"), { recursive: true });
1107
+ await writeFile8(unitPath(), buildUnit());
966
1108
  await exec("systemctl", ["--user", "daemon-reload"]);
967
1109
  await exec("systemctl", ["--user", "enable", SYSTEMD_SERVICE]);
968
1110
  await exec("systemctl", ["--user", "start", SYSTEMD_SERVICE]);
@@ -995,7 +1137,7 @@ async function linuxIsInstalled() {
995
1137
  }
996
1138
  var TASK_NAME = "RepoWise Listener";
997
1139
  async function win32Install() {
998
- await mkdir7(logDir(), { recursive: true });
1140
+ await mkdir8(logDir(), { recursive: true });
999
1141
  const cmd = resolveListenerCommand2();
1000
1142
  const taskCmd = [process.execPath, cmd.script, ...cmd.args].map((a) => `"${a}"`).join(" ");
1001
1143
  await exec("schtasks", [
@@ -1079,7 +1221,7 @@ import ora from "ora";
1079
1221
 
1080
1222
  // src/lib/auth.ts
1081
1223
  import { createHash, randomBytes } from "crypto";
1082
- import { readFile as readFile7, writeFile as writeFile8, mkdir as mkdir8, chmod as chmod3, unlink as unlink4 } from "fs/promises";
1224
+ import { readFile as readFile7, writeFile as writeFile9, mkdir as mkdir9, chmod as chmod4, unlink as unlink4 } from "fs/promises";
1083
1225
  import http from "http";
1084
1226
  import { homedir as homedir8 } from "os";
1085
1227
  import { join as join10 } from "path";
@@ -1260,9 +1402,9 @@ async function getStoredCredentials2() {
1260
1402
  }
1261
1403
  }
1262
1404
  async function storeCredentials2(credentials) {
1263
- await mkdir8(CONFIG_DIR5, { recursive: true, mode: 448 });
1264
- await writeFile8(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2));
1265
- await chmod3(CREDENTIALS_PATH2, 384);
1405
+ await mkdir9(CONFIG_DIR5, { recursive: true, mode: 448 });
1406
+ await writeFile9(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2));
1407
+ await chmod4(CREDENTIALS_PATH2, 384);
1266
1408
  }
1267
1409
  async function clearCredentials() {
1268
1410
  try {
@@ -1440,8 +1582,8 @@ async function selectAiTools() {
1440
1582
  }
1441
1583
 
1442
1584
  // src/lib/ai-tools.ts
1443
- import { readFile as readFile8, writeFile as writeFile9, mkdir as mkdir9, readdir } from "fs/promises";
1444
- import { join as join11, dirname } from "path";
1585
+ import { readFile as readFile8, writeFile as writeFile10, mkdir as mkdir10, readdir } from "fs/promises";
1586
+ import { join as join11, dirname as dirname2 } from "path";
1445
1587
  var AI_TOOL_CONFIG = {
1446
1588
  cursor: {
1447
1589
  label: "Cursor",
@@ -1567,9 +1709,9 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
1567
1709
  async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
1568
1710
  const config2 = AI_TOOL_CONFIG[tool];
1569
1711
  const fullPath = join11(repoRoot, config2.filePath);
1570
- const dir = dirname(fullPath);
1712
+ const dir = dirname2(fullPath);
1571
1713
  if (dir !== repoRoot) {
1572
- await mkdir9(dir, { recursive: true });
1714
+ await mkdir10(dir, { recursive: true });
1573
1715
  }
1574
1716
  const referenceBlock = generateReference(tool, repoName, contextFolder, contextFiles);
1575
1717
  let existing = "";
@@ -1591,7 +1733,7 @@ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, context
1591
1733
  const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n\n" : existing.length > 0 ? "\n" : "";
1592
1734
  content = existing + separator + referenceBlock + "\n";
1593
1735
  }
1594
- await writeFile9(fullPath, content, "utf-8");
1736
+ await writeFile10(fullPath, content, "utf-8");
1595
1737
  return { created };
1596
1738
  }
1597
1739
  async function scanLocalContextFiles(repoRoot, contextFolder) {
@@ -2098,6 +2240,8 @@ async function create() {
2098
2240
  let repoId;
2099
2241
  let repoName;
2100
2242
  let repoRoot;
2243
+ let repoPlatform;
2244
+ let repoExternalId;
2101
2245
  spinner.start("Checking for pending repository...");
2102
2246
  try {
2103
2247
  const pending = await apiRequest("/v1/onboarding/pending");
@@ -2130,6 +2274,8 @@ async function create() {
2130
2274
  const match = repos.find((r) => r.name === repoName || r.fullName.endsWith(`/${repoName}`));
2131
2275
  if (match) {
2132
2276
  repoId = match.repoId;
2277
+ repoPlatform = match.platform;
2278
+ repoExternalId = match.externalId;
2133
2279
  }
2134
2280
  } catch {
2135
2281
  }
@@ -2148,6 +2294,26 @@ async function create() {
2148
2294
  process.exitCode = 1;
2149
2295
  return;
2150
2296
  }
2297
+ try {
2298
+ const pricing = await apiRequest(
2299
+ `/v1/repos/${repoId}/rescan-pricing`
2300
+ );
2301
+ if (pricing.lastFullScanAt) {
2302
+ spinner.fail(chalk5.red("This repository already has context generated."));
2303
+ console.log(
2304
+ chalk5.cyan(
2305
+ `
2306
+ To trigger a new full scan, visit the dashboard:
2307
+ https://app.repowise.ai/repos/${repoId}
2308
+ `
2309
+ )
2310
+ );
2311
+ console.log(chalk5.dim(" To sync recent changes, use: repowise sync\n"));
2312
+ process.exitCode = 1;
2313
+ return;
2314
+ }
2315
+ } catch {
2316
+ }
2151
2317
  const { tools, hasOther } = await selectAiTools();
2152
2318
  if (hasOther) {
2153
2319
  console.log(
@@ -2328,6 +2494,8 @@ Files are stored on our servers (not in git). Retry when online.`
2328
2494
  if (isStagingMode()) {
2329
2495
  repoEntry.apiUrl = getEnvConfig().apiUrl;
2330
2496
  }
2497
+ if (repoPlatform) repoEntry.platform = repoPlatform;
2498
+ if (repoExternalId) repoEntry.externalId = repoExternalId;
2331
2499
  updatedRepos.push(repoEntry);
2332
2500
  }
2333
2501
  await saveConfig({
@@ -2445,7 +2613,7 @@ async function logout() {
2445
2613
  // src/commands/status.ts
2446
2614
  import { readFile as readFile9 } from "fs/promises";
2447
2615
  import { homedir as homedir9 } from "os";
2448
- import { basename, join as join13 } from "path";
2616
+ import { basename as basename2, join as join13 } from "path";
2449
2617
  var STATE_PATH2 = join13(homedir9(), ".repowise", "listener-state.json");
2450
2618
  var CONFIG_PATH3 = join13(homedir9(), ".repowise", "config.json");
2451
2619
  async function status() {
@@ -2490,7 +2658,7 @@ async function status() {
2490
2658
  const configData = await readFile9(CONFIG_PATH3, "utf-8");
2491
2659
  const config2 = JSON.parse(configData);
2492
2660
  for (const repo of config2.repos ?? []) {
2493
- repoNames.set(repo.repoId, basename(repo.localPath));
2661
+ repoNames.set(repo.repoId, basename2(repo.localPath));
2494
2662
  }
2495
2663
  } catch {
2496
2664
  }
@@ -2859,7 +3027,7 @@ async function config() {
2859
3027
 
2860
3028
  // bin/repowise.ts
2861
3029
  var __filename = fileURLToPath3(import.meta.url);
2862
- var __dirname = dirname2(__filename);
3030
+ var __dirname = dirname3(__filename);
2863
3031
  var pkg = JSON.parse(readFileSync(join15(__dirname, "..", "..", "package.json"), "utf-8"));
2864
3032
  var program = new Command();
2865
3033
  program.name("repowise").description("AI-optimized codebase context generator").version(pkg.version).option("--staging", "Use the staging environment").hook("preAction", async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowise",
3
- "version": "0.1.63",
3
+ "version": "0.1.64",
4
4
  "type": "module",
5
5
  "description": "AI-optimized codebase context generator",
6
6
  "bin": {