repowise 0.1.80 → 0.1.81

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 +124 -121
  2. package/package.json +2 -1
@@ -1932,6 +1932,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
1932
1932
 
1933
1933
  // src/lib/progress-renderer.ts
1934
1934
  import chalk4 from "chalk";
1935
+ import logUpdate from "log-update";
1935
1936
  var CORE_FILES = /* @__PURE__ */ new Set([
1936
1937
  "project-overview.md",
1937
1938
  "architecture.md",
@@ -1984,14 +1985,15 @@ function computeOverallProgress(syncResult) {
1984
1985
  var ProgressRenderer = class {
1985
1986
  privacyShieldShown = false;
1986
1987
  discoveryShown = false;
1988
+ scanHeaderShown = false;
1987
1989
  scanSummaryShown = false;
1988
1990
  validationShown = false;
1989
1991
  lastValidationSnapshot = "";
1990
- lastValidationLineCount = 0;
1991
1992
  pushShown = false;
1992
1993
  fileStatusHeaderShown = false;
1993
1994
  lastFileStatusSnapshot = "";
1994
- lastFileStatusLineCount = 0;
1995
+ /** Tracks which section logUpdate is currently managing */
1996
+ activeSection = null;
1995
1997
  renderPrivacyShield(enabled, spinner) {
1996
1998
  if (this.privacyShieldShown) return;
1997
1999
  this.privacyShieldShown = true;
@@ -2078,95 +2080,62 @@ var ProgressRenderer = class {
2078
2080
  printNode(child, "", idx === topLevel.length - 1);
2079
2081
  });
2080
2082
  }
2081
- renderScanSummary(summary, spinner) {
2082
- if (this.scanSummaryShown) return;
2083
- this.scanSummaryShown = true;
2083
+ switchSection(section, spinner) {
2084
+ if (this.activeSection === section) return;
2085
+ if (this.activeSection) {
2086
+ logUpdate.done();
2087
+ }
2088
+ this.activeSection = section;
2084
2089
  spinner.stop();
2085
- console.log(
2086
- chalk4.dim(
2087
- ` Scan complete: ${summary.totalFiles} files, ${summary.totalFunctions} functions, ${summary.totalClasses} classes, ${summary.totalEndpoints} entry points`
2088
- )
2089
- );
2090
- console.log("");
2091
- spinner.start();
2092
2090
  }
2093
- renderValidation(progress, spinner) {
2094
- const resultMap = new Map(progress.personaResults.map((r) => [r.persona, r.score]));
2095
- const isComplete = progress.status === "complete";
2096
- if (isComplete && this.validationShown) return;
2097
- const snapshot = `${progress.round}:${progress.status}:${progress.personaResults.map((r) => `${r.persona}:${r.score}`).join(",")}`;
2098
- if (snapshot === this.lastValidationSnapshot) return;
2099
- this.lastValidationSnapshot = snapshot;
2100
- if (isComplete) this.validationShown = true;
2101
- spinner.stop();
2102
- if (this.lastValidationLineCount > 0) {
2103
- process.stdout.write(`\x1B[${this.lastValidationLineCount}A`);
2104
- }
2105
- const lines = [];
2106
- const title = isComplete ? "Validation Results" : "Validation";
2107
- lines.push(chalk4.cyan.bold(` \u2500\u2500 ${title} \u2500\u2500`));
2108
- if (!isComplete) {
2109
- lines.push(
2110
- chalk4.dim(
2111
- ` ${ALL_PERSONAS.length} AI reviewers checking context quality \u2014 issues are auto-fixed.`
2112
- )
2113
- );
2114
- }
2115
- const passCount = progress.personaResults.filter((r) => r.score === "PASS").length;
2116
- if (isComplete) {
2117
- const roundInfo = progress.round > 1 ? ` (${progress.round} rounds)` : "";
2118
- lines.push(chalk4.dim(` ${passCount}/${ALL_PERSONAS.length} PASS${roundInfo}`));
2119
- } else if (progress.personaResults.length > 0) {
2120
- const statusSuffix = progress.status === "regenerating" ? chalk4.dim(" \u2014 improving files based on feedback") : "";
2121
- lines.push(
2122
- ` Round ${progress.round}/${progress.maxRounds}: ${passCount}/${ALL_PERSONAS.length} passed${statusSuffix}`
2123
- );
2124
- } else {
2125
- lines.push(chalk4.dim(` Round ${progress.round}/${progress.maxRounds}: validating...`));
2126
- }
2127
- for (const persona of ALL_PERSONAS) {
2128
- const label = PERSONA_LABELS[persona] ?? persona;
2129
- const score = resultMap.get(persona);
2130
- if (score) {
2131
- const icon = score === "PASS" ? chalk4.green("\u2713") : chalk4.red("\u2717");
2132
- const scoreColor = score === "PASS" ? chalk4.green : score === "PARTIAL" ? chalk4.yellow : chalk4.red;
2133
- const fixingSuffix = progress.status === "regenerating" && score !== "PASS" ? chalk4.dim(" \u2192 fixing...") : "";
2134
- lines.push(` ${icon} ${label}: ${scoreColor(score)}${fixingSuffix}`);
2135
- } else {
2136
- lines.push(` ${chalk4.dim("\u25CB")} ${chalk4.dim(label)}`);
2091
+ renderScanProgress(progress, spinner) {
2092
+ if (!this.scanHeaderShown) {
2093
+ this.scanHeaderShown = true;
2094
+ spinner.stop();
2095
+ console.log("");
2096
+ console.log(chalk4.cyan.bold(" \u2500\u2500 Code Analysis \u2500\u2500"));
2097
+ console.log(chalk4.dim(" Analyzing your codebase structure, functions, and relationships."));
2098
+ }
2099
+ this.switchSection("scan", spinner);
2100
+ if (progress.summary) {
2101
+ if (!this.scanSummaryShown) {
2102
+ this.scanSummaryShown = true;
2103
+ const s = progress.summary;
2104
+ logUpdate(
2105
+ ` ${chalk4.green("\u2713")} Scan complete: ${s.totalFiles} files, ${s.totalFunctions} functions, ${s.totalClasses} classes, ${s.totalEndpoints} entry points`
2106
+ );
2107
+ logUpdate.done();
2108
+ console.log("");
2109
+ this.activeSection = null;
2137
2110
  }
2111
+ } else {
2112
+ const pct = progress.totalBatches > 0 ? Math.round(progress.currentBatch / progress.totalBatches * 100) : 0;
2113
+ logUpdate(
2114
+ ` Scanning batch ${progress.currentBatch}/${progress.totalBatches} ${chalk4.dim(`(${pct}%)`)}`
2115
+ );
2138
2116
  }
2139
- if (isComplete && passCount < ALL_PERSONAS.length) {
2140
- lines.push(chalk4.yellow(" \u26A0 Continuing with best-effort context"));
2141
- }
2142
- lines.push("");
2143
- for (const line of lines) {
2144
- process.stdout.write(`\x1B[2K${line}
2145
- `);
2146
- }
2147
- for (let i = lines.length; i < this.lastValidationLineCount; i++) {
2148
- process.stdout.write("\x1B[2K\n");
2149
- }
2150
- this.lastValidationLineCount = lines.length;
2151
- spinner.start();
2152
2117
  }
2153
2118
  renderFileStatuses(fileStatuses, spinner) {
2154
2119
  const snapshot = fileStatuses.map((f) => `${f.fileName}:${f.status}`).join(",");
2155
2120
  if (snapshot === this.lastFileStatusSnapshot) return;
2156
2121
  this.lastFileStatusSnapshot = snapshot;
2157
- spinner.stop();
2158
2122
  if (!this.fileStatusHeaderShown) {
2159
2123
  this.fileStatusHeaderShown = true;
2124
+ if (this.activeSection === "scan") {
2125
+ logUpdate.done();
2126
+ this.activeSection = null;
2127
+ }
2128
+ spinner.stop();
2160
2129
  console.log("");
2161
2130
  console.log(chalk4.cyan.bold(" \u2500\u2500 RepoWise Context Generation \u2500\u2500"));
2162
2131
  console.log(chalk4.dim(" Building AI-optimized context files from your codebase."));
2163
2132
  console.log(
2164
- chalk4.cyan(" \u2615 This takes a few minutes \u2014 grab a coffee, we'll handle the rest!")
2133
+ chalk4.cyan(
2134
+ " \u2615 This takes a few minutes \u2014 grab a coffee, we'll handle the rest!"
2135
+ )
2165
2136
  );
2166
2137
  }
2167
- if (this.lastFileStatusLineCount > 0) {
2168
- process.stdout.write(`\x1B[${this.lastFileStatusLineCount}A`);
2169
- }
2138
+ this.switchSection("generation", spinner);
2170
2139
  const coreFiles = [];
2171
2140
  const tailoredFiles = [];
2172
2141
  for (const file of fileStatuses) {
@@ -2224,20 +2193,76 @@ var ProgressRenderer = class {
2224
2193
  }
2225
2194
  }
2226
2195
  }
2227
- lines.push("");
2228
- for (const line of lines) {
2229
- process.stdout.write(`\x1B[2K${line}
2230
- `);
2196
+ logUpdate(lines.join("\n"));
2197
+ }
2198
+ renderValidation(progress, spinner) {
2199
+ const resultMap = new Map(progress.personaResults.map((r) => [r.persona, r.score]));
2200
+ const isComplete = progress.status === "complete";
2201
+ if (isComplete && this.validationShown) return;
2202
+ const snapshot = `${progress.round}:${progress.status}:${progress.personaResults.map((r) => `${r.persona}:${r.score}`).join(",")}`;
2203
+ if (snapshot === this.lastValidationSnapshot) return;
2204
+ this.lastValidationSnapshot = snapshot;
2205
+ if (isComplete) this.validationShown = true;
2206
+ if (this.activeSection !== "validation") {
2207
+ if (this.activeSection) {
2208
+ logUpdate.done();
2209
+ this.activeSection = null;
2210
+ }
2211
+ spinner.stop();
2212
+ console.log("");
2231
2213
  }
2232
- for (let i = lines.length; i < this.lastFileStatusLineCount; i++) {
2233
- process.stdout.write("\x1B[2K\n");
2214
+ this.switchSection("validation", spinner);
2215
+ const lines = [];
2216
+ const title = isComplete ? "Validation Results" : "Validation";
2217
+ lines.push(chalk4.cyan.bold(` \u2500\u2500 ${title} \u2500\u2500`));
2218
+ if (!isComplete) {
2219
+ lines.push(
2220
+ chalk4.dim(
2221
+ ` ${ALL_PERSONAS.length} AI reviewers checking context quality \u2014 issues are auto-fixed.`
2222
+ )
2223
+ );
2224
+ }
2225
+ const passCount = progress.personaResults.filter((r) => r.score === "PASS").length;
2226
+ if (isComplete) {
2227
+ const roundInfo = progress.round > 1 ? ` (${progress.round} rounds)` : "";
2228
+ lines.push(chalk4.dim(` ${passCount}/${ALL_PERSONAS.length} PASS${roundInfo}`));
2229
+ } else if (progress.personaResults.length > 0) {
2230
+ const statusSuffix = progress.status === "regenerating" ? chalk4.dim(" \u2014 improving files based on feedback") : "";
2231
+ lines.push(
2232
+ ` Round ${progress.round}/${progress.maxRounds}: ${passCount}/${ALL_PERSONAS.length} passed${statusSuffix}`
2233
+ );
2234
+ } else {
2235
+ lines.push(chalk4.dim(` Round ${progress.round}/${progress.maxRounds}: validating...`));
2236
+ }
2237
+ for (const persona of ALL_PERSONAS) {
2238
+ const label = PERSONA_LABELS[persona] ?? persona;
2239
+ const score = resultMap.get(persona);
2240
+ if (score) {
2241
+ const icon = score === "PASS" ? chalk4.green("\u2713") : chalk4.red("\u2717");
2242
+ const scoreColor = score === "PASS" ? chalk4.green : score === "PARTIAL" ? chalk4.yellow : chalk4.red;
2243
+ const fixingSuffix = progress.status === "regenerating" && score !== "PASS" ? chalk4.dim(" \u2192 fixing...") : "";
2244
+ lines.push(` ${icon} ${label}: ${scoreColor(score)}${fixingSuffix}`);
2245
+ } else {
2246
+ lines.push(` ${chalk4.dim("\u25CB")} ${chalk4.dim(label)}`);
2247
+ }
2248
+ }
2249
+ if (isComplete && passCount < ALL_PERSONAS.length) {
2250
+ lines.push(chalk4.yellow(" \u26A0 Continuing with best-effort context"));
2251
+ }
2252
+ logUpdate(lines.join("\n"));
2253
+ if (isComplete) {
2254
+ logUpdate.done();
2255
+ console.log("");
2256
+ this.activeSection = null;
2234
2257
  }
2235
- this.lastFileStatusLineCount = lines.length;
2236
- spinner.start();
2237
2258
  }
2238
2259
  renderPush(spinner) {
2239
2260
  if (this.pushShown) return;
2240
2261
  this.pushShown = true;
2262
+ if (this.activeSection) {
2263
+ logUpdate.done();
2264
+ this.activeSection = null;
2265
+ }
2241
2266
  spinner.stop();
2242
2267
  console.log("");
2243
2268
  console.log(chalk4.cyan.bold(" \u2500\u2500 Saving Context \u2500\u2500"));
@@ -2245,45 +2270,17 @@ var ProgressRenderer = class {
2245
2270
  console.log("");
2246
2271
  spinner.start();
2247
2272
  }
2273
+ /** Finalize any active logUpdate block. Call before resuming ora. */
2274
+ finalize() {
2275
+ if (this.activeSection) {
2276
+ logUpdate.done();
2277
+ this.activeSection = null;
2278
+ }
2279
+ }
2248
2280
  getSpinnerText(syncResult) {
2249
2281
  const stepLabel = syncResult.stepLabel ?? syncResult.currentStep ?? "Processing";
2250
2282
  const overallPct = computeOverallProgress(syncResult);
2251
- let progressText = stepLabel;
2252
- if (syncResult.scanProgress && !syncResult.scanProgress.summary) {
2253
- progressText = `Scanning batch ${syncResult.scanProgress.currentBatch}/${syncResult.scanProgress.totalBatches}`;
2254
- } else if (syncResult.validationProgress && syncResult.validationProgress.status !== "complete") {
2255
- const vp = syncResult.validationProgress;
2256
- if (vp.status === "regenerating") {
2257
- progressText = `Improving files based on feedback (round ${vp.round})`;
2258
- } else {
2259
- progressText = `Validation round ${vp.round}/${vp.maxRounds}`;
2260
- }
2261
- } else if (syncResult.generationProgress) {
2262
- const gp = syncResult.generationProgress;
2263
- if (gp.fileStatuses && gp.fileStatuses.length > 0) {
2264
- const completed = gp.fileStatuses.filter((f) => f.status === "completed").length;
2265
- const total = gp.fileStatuses.length;
2266
- const generating = gp.fileStatuses.find((f) => f.status === "generating");
2267
- if (completed === total) {
2268
- progressText = stepLabel;
2269
- } else if (generating) {
2270
- const genBaseName = generating.fileName.split("/").pop() ?? generating.fileName;
2271
- const isCore = CORE_FILES.has(genBaseName);
2272
- const sectionFiles = gp.fileStatuses.filter((f) => {
2273
- const bn = f.fileName.split("/").pop() ?? f.fileName;
2274
- return isCore ? CORE_FILES.has(bn) : !CORE_FILES.has(bn);
2275
- });
2276
- const sectionCompleted = sectionFiles.filter((f) => f.status === "completed").length;
2277
- const sectionLabel = isCore ? "core" : "tailored";
2278
- progressText = `Generating ${generating.fileName} (${sectionCompleted}/${sectionFiles.length} ${sectionLabel})`;
2279
- } else {
2280
- progressText = `Generating (${completed}/${total})`;
2281
- }
2282
- } else {
2283
- progressText = `Generating ${gp.currentFileName} (${gp.currentFile}/${gp.totalFiles})`;
2284
- }
2285
- }
2286
- return `${progressText}... ${chalk4.dim(`(${overallPct}%)`)}`;
2283
+ return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
2287
2284
  }
2288
2285
  update(syncResult, spinner) {
2289
2286
  if (syncResult.privacyShieldEnabled !== void 0) {
@@ -2292,8 +2289,8 @@ var ProgressRenderer = class {
2292
2289
  if (syncResult.discoveryResult) {
2293
2290
  this.renderDiscovery(syncResult.discoveryResult, spinner);
2294
2291
  }
2295
- if (syncResult.scanProgress?.summary && syncResult.scanProgress.summary.totalFiles > 0) {
2296
- this.renderScanSummary(syncResult.scanProgress.summary, spinner);
2292
+ if (syncResult.scanProgress) {
2293
+ this.renderScanProgress(syncResult.scanProgress, spinner);
2297
2294
  }
2298
2295
  if (syncResult.generationProgress?.fileStatuses && syncResult.generationProgress.fileStatuses.length > 0) {
2299
2296
  this.renderFileStatuses(syncResult.generationProgress.fileStatuses, spinner);
@@ -2304,7 +2301,9 @@ var ProgressRenderer = class {
2304
2301
  if (syncResult.currentStep === "push-context") {
2305
2302
  this.renderPush(spinner);
2306
2303
  }
2307
- spinner.text = this.getSpinnerText(syncResult);
2304
+ if (!this.activeSection) {
2305
+ spinner.text = this.getSpinnerText(syncResult);
2306
+ }
2308
2307
  }
2309
2308
  };
2310
2309
 
@@ -2515,6 +2514,7 @@ async function create() {
2515
2514
  continue;
2516
2515
  }
2517
2516
  if (syncResult.status === "completed") {
2517
+ progressRenderer.finalize();
2518
2518
  const generatedFiles = syncResult.filesGenerated ?? [];
2519
2519
  const fileCount = generatedFiles.length;
2520
2520
  if (fileCount > 0) {
@@ -2536,6 +2536,7 @@ async function create() {
2536
2536
  break;
2537
2537
  }
2538
2538
  if (syncResult.status === "failed") {
2539
+ progressRenderer.finalize();
2539
2540
  spinner.fail(chalk5.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
2540
2541
  process.exitCode = 1;
2541
2542
  return;
@@ -2951,6 +2952,7 @@ async function sync() {
2951
2952
  continue;
2952
2953
  }
2953
2954
  if (syncResult.status === "completed") {
2955
+ progressRenderer.finalize();
2954
2956
  const generatedFiles = syncResult.filesGenerated ?? [];
2955
2957
  const fileCount = generatedFiles.length;
2956
2958
  if (fileCount > 0) {
@@ -2961,6 +2963,7 @@ async function sync() {
2961
2963
  break;
2962
2964
  }
2963
2965
  if (syncResult.status === "failed") {
2966
+ progressRenderer.finalize();
2964
2967
  spinner.fail(chalk8.red(`Sync failed: ${syncResult.error ?? "Unknown error"}`));
2965
2968
  process.exitCode = 1;
2966
2969
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowise",
3
- "version": "0.1.80",
3
+ "version": "0.1.81",
4
4
  "type": "module",
5
5
  "description": "AI-optimized codebase context generator",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "chalk": "^5.4.0",
22
22
  "commander": "^12.1.0",
23
23
  "inquirer": "^12.3.0",
24
+ "log-update": "^6.1.0",
24
25
  "node-notifier": "^10.0.0",
25
26
  "open": "^11.0.0",
26
27
  "ora": "^8.2.0"