repowise 0.1.79 → 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 +165 -141
  2. package/package.json +2 -1
@@ -3,7 +3,7 @@
3
3
  // bin/repowise.ts
4
4
  import { readFileSync as readFileSync2 } from "fs";
5
5
  import { fileURLToPath as fileURLToPath3 } from "url";
6
- import { dirname as dirname3, join as join16 } from "path";
6
+ import { dirname as dirname6, join as join16 } from "path";
7
7
  import { Command } from "commander";
8
8
 
9
9
  // ../listener/dist/main.js
@@ -377,7 +377,7 @@ function notifyContextUpdated(repoId, fileCount) {
377
377
  // ../listener/dist/context-fetcher.js
378
378
  import { execFile } from "child_process";
379
379
  import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
380
- import { join as join5 } from "path";
380
+ import { dirname as dirname2, join as join5 } from "path";
381
381
  import { promisify } from "util";
382
382
 
383
383
  // ../listener/dist/file-writer.js
@@ -477,7 +477,9 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
477
477
  await mkdir4(contextDir, { recursive: true });
478
478
  const updatedFiles = [];
479
479
  for (const file of files) {
480
- const urlRes = await fetch(`${apiUrl}/v1/repos/${repoId}/context/${file.fileName}`, {
480
+ if (file.fileName.includes(".."))
481
+ continue;
482
+ const urlRes = await fetch(`${apiUrl}/v1/repos/${repoId}/context/files/${file.fileName}`, {
481
483
  headers
482
484
  });
483
485
  if (!urlRes.ok) {
@@ -496,7 +498,9 @@ async function fetchContextFromServer(repoId, localPath, apiUrl) {
496
498
  continue;
497
499
  }
498
500
  const content = await contentRes.text();
499
- await writeFile4(join5(contextDir, file.fileName), content, "utf-8");
501
+ const filePath = join5(contextDir, file.fileName);
502
+ await mkdir4(dirname2(filePath), { recursive: true });
503
+ await writeFile4(filePath, content, "utf-8");
500
504
  updatedFiles.push(file.fileName);
501
505
  }
502
506
  console.log(`Context fetch for ${repoId}: downloaded ${updatedFiles.length}/${files.length} file(s)`);
@@ -917,7 +921,7 @@ async function showWelcome(currentVersion) {
917
921
  // src/commands/create.ts
918
922
  import { execSync } from "child_process";
919
923
  import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
920
- import { join as join13 } from "path";
924
+ import { dirname as dirname4, join as join13 } from "path";
921
925
 
922
926
  // ../listener/dist/process-manager.js
923
927
  import { spawn } from "child_process";
@@ -1637,7 +1641,7 @@ async function selectAiTools() {
1637
1641
 
1638
1642
  // src/lib/ai-tools.ts
1639
1643
  import { readFile as readFile8, writeFile as writeFile10, mkdir as mkdir10, readdir } from "fs/promises";
1640
- import { join as join11, dirname as dirname2 } from "path";
1644
+ import { join as join11, dirname as dirname3 } from "path";
1641
1645
  var AI_TOOL_CONFIG = {
1642
1646
  cursor: {
1643
1647
  label: "Cursor",
@@ -1707,8 +1711,9 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
1707
1711
  const config2 = AI_TOOL_CONFIG[tool];
1708
1712
  const safeName = sanitizeRepoName(repoName);
1709
1713
  const fileLines = contextFiles.map((f) => {
1710
- const desc = fileDescriptionFromName(f.fileName);
1711
- const isOverview = f.fileName === "project-overview.md";
1714
+ const baseName = f.fileName.split("/").pop() ?? f.fileName;
1715
+ const desc = fileDescriptionFromName(baseName);
1716
+ const isOverview = baseName === "project-overview.md";
1712
1717
  return { path: f.relativePath, desc: isOverview ? `${desc} (full index of all files)` : desc };
1713
1718
  });
1714
1719
  const hasFiles = fileLines.length > 0;
@@ -1766,7 +1771,7 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
1766
1771
  async function updateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles) {
1767
1772
  const config2 = AI_TOOL_CONFIG[tool];
1768
1773
  const fullPath = join11(repoRoot, config2.filePath);
1769
- const dir = dirname2(fullPath);
1774
+ const dir = dirname3(fullPath);
1770
1775
  if (dir !== repoRoot) {
1771
1776
  await mkdir10(dir, { recursive: true });
1772
1777
  }
@@ -1796,11 +1801,19 @@ async function updateToolConfig(repoRoot, tool, repoName, contextFolder, context
1796
1801
  async function scanLocalContextFiles(repoRoot, contextFolder) {
1797
1802
  const folderPath = join11(repoRoot, contextFolder);
1798
1803
  try {
1799
- const entries = await readdir(folderPath, { withFileTypes: true });
1800
- return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => ({
1801
- fileName: e.name,
1802
- relativePath: `${contextFolder}/${e.name}`
1803
- })).sort((a, b) => a.fileName.localeCompare(b.fileName));
1804
+ const entries = await readdir(folderPath, { withFileTypes: true, recursive: true });
1805
+ const results = [];
1806
+ for (const entry of entries) {
1807
+ if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
1808
+ const parentDir = entry.parentPath ?? folderPath;
1809
+ const fullPath = join11(parentDir, entry.name);
1810
+ const relFromContext = fullPath.slice(folderPath.length + 1);
1811
+ results.push({
1812
+ fileName: relFromContext,
1813
+ relativePath: `${contextFolder}/${relFromContext}`
1814
+ });
1815
+ }
1816
+ return results.sort((a, b) => a.fileName.localeCompare(b.fileName));
1804
1817
  } catch (err) {
1805
1818
  if (err.code === "ENOENT") return [];
1806
1819
  throw err;
@@ -1919,6 +1932,7 @@ async function handleInterview(syncId, questionId, questionText, questionContext
1919
1932
 
1920
1933
  // src/lib/progress-renderer.ts
1921
1934
  import chalk4 from "chalk";
1935
+ import logUpdate from "log-update";
1922
1936
  var CORE_FILES = /* @__PURE__ */ new Set([
1923
1937
  "project-overview.md",
1924
1938
  "architecture.md",
@@ -1971,14 +1985,15 @@ function computeOverallProgress(syncResult) {
1971
1985
  var ProgressRenderer = class {
1972
1986
  privacyShieldShown = false;
1973
1987
  discoveryShown = false;
1988
+ scanHeaderShown = false;
1974
1989
  scanSummaryShown = false;
1975
1990
  validationShown = false;
1976
1991
  lastValidationSnapshot = "";
1977
- lastValidationLineCount = 0;
1978
1992
  pushShown = false;
1979
1993
  fileStatusHeaderShown = false;
1980
1994
  lastFileStatusSnapshot = "";
1981
- lastFileStatusLineCount = 0;
1995
+ /** Tracks which section logUpdate is currently managing */
1996
+ activeSection = null;
1982
1997
  renderPrivacyShield(enabled, spinner) {
1983
1998
  if (this.privacyShieldShown) return;
1984
1999
  this.privacyShieldShown = true;
@@ -2065,97 +2080,62 @@ var ProgressRenderer = class {
2065
2080
  printNode(child, "", idx === topLevel.length - 1);
2066
2081
  });
2067
2082
  }
2068
- renderScanSummary(summary, spinner) {
2069
- if (this.scanSummaryShown) return;
2070
- 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;
2071
2089
  spinner.stop();
2072
- console.log(
2073
- chalk4.dim(
2074
- ` Scan complete: ${summary.totalFiles} files, ${summary.totalFunctions} functions, ${summary.totalClasses} classes, ${summary.totalEndpoints} entry points`
2075
- )
2076
- );
2077
- console.log("");
2078
- spinner.start();
2079
2090
  }
2080
- renderValidation(progress, spinner) {
2081
- const resultMap = new Map(progress.personaResults.map((r) => [r.persona, r.score]));
2082
- const isComplete = progress.status === "complete";
2083
- if (isComplete && this.validationShown) return;
2084
- const snapshot = `${progress.round}:${progress.status}:${progress.personaResults.map((r) => `${r.persona}:${r.score}`).join(",")}`;
2085
- if (snapshot === this.lastValidationSnapshot) return;
2086
- this.lastValidationSnapshot = snapshot;
2087
- if (isComplete) this.validationShown = true;
2088
- spinner.stop();
2089
- if (this.lastValidationLineCount > 0) {
2090
- process.stdout.write(`\x1B[${this.lastValidationLineCount}A`);
2091
- }
2092
- const lines = [];
2093
- const title = isComplete ? "Validation Results" : "Validation";
2094
- lines.push(chalk4.cyan.bold(` \u2500\u2500 ${title} \u2500\u2500`));
2095
- if (!isComplete) {
2096
- lines.push(
2097
- chalk4.dim(
2098
- ` ${ALL_PERSONAS.length} AI reviewers checking context quality \u2014 issues are auto-fixed.`
2099
- )
2100
- );
2101
- }
2102
- const passCount = progress.personaResults.filter((r) => r.score === "PASS").length;
2103
- if (isComplete) {
2104
- const roundInfo = progress.round > 1 ? ` (${progress.round} rounds)` : "";
2105
- lines.push(chalk4.dim(` ${passCount}/${ALL_PERSONAS.length} PASS${roundInfo}`));
2106
- } else if (progress.personaResults.length > 0) {
2107
- const statusSuffix = progress.status === "regenerating" ? chalk4.dim(" \u2014 improving files based on feedback") : "";
2108
- lines.push(
2109
- ` Round ${progress.round}/${progress.maxRounds}: ${passCount}/${ALL_PERSONAS.length} passed${statusSuffix}`
2110
- );
2111
- } else {
2112
- lines.push(chalk4.dim(` Round ${progress.round}/${progress.maxRounds}: validating...`));
2113
- }
2114
- for (const persona of ALL_PERSONAS) {
2115
- const label = PERSONA_LABELS[persona] ?? persona;
2116
- const score = resultMap.get(persona);
2117
- if (score) {
2118
- const icon = score === "PASS" ? chalk4.green("\u2713") : chalk4.red("\u2717");
2119
- const scoreColor = score === "PASS" ? chalk4.green : score === "PARTIAL" ? chalk4.yellow : chalk4.red;
2120
- const fixingSuffix = progress.status === "regenerating" && score !== "PASS" ? chalk4.dim(" \u2192 fixing...") : "";
2121
- lines.push(` ${icon} ${label}: ${scoreColor(score)}${fixingSuffix}`);
2122
- } else {
2123
- 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;
2124
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
+ );
2125
2116
  }
2126
- if (isComplete && passCount < ALL_PERSONAS.length) {
2127
- lines.push(chalk4.yellow(" \u26A0 Continuing with best-effort context"));
2128
- }
2129
- lines.push("");
2130
- for (const line of lines) {
2131
- process.stdout.write(`\x1B[2K${line}
2132
- `);
2133
- }
2134
- for (let i = lines.length; i < this.lastValidationLineCount; i++) {
2135
- process.stdout.write("\x1B[2K\n");
2136
- }
2137
- this.lastValidationLineCount = lines.length;
2138
- spinner.start();
2139
2117
  }
2140
2118
  renderFileStatuses(fileStatuses, spinner) {
2141
2119
  const snapshot = fileStatuses.map((f) => `${f.fileName}:${f.status}`).join(",");
2142
2120
  if (snapshot === this.lastFileStatusSnapshot) return;
2143
2121
  this.lastFileStatusSnapshot = snapshot;
2144
- const completedCount = fileStatuses.filter((f) => f.status === "completed").length;
2145
- const totalCount = fileStatuses.length;
2146
- spinner.stop();
2147
2122
  if (!this.fileStatusHeaderShown) {
2148
2123
  this.fileStatusHeaderShown = true;
2124
+ if (this.activeSection === "scan") {
2125
+ logUpdate.done();
2126
+ this.activeSection = null;
2127
+ }
2128
+ spinner.stop();
2149
2129
  console.log("");
2150
2130
  console.log(chalk4.cyan.bold(" \u2500\u2500 RepoWise Context Generation \u2500\u2500"));
2151
2131
  console.log(chalk4.dim(" Building AI-optimized context files from your codebase."));
2152
2132
  console.log(
2153
- 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
+ )
2154
2136
  );
2155
2137
  }
2156
- if (this.lastFileStatusLineCount > 0) {
2157
- process.stdout.write(`\x1B[${this.lastFileStatusLineCount}A`);
2158
- }
2138
+ this.switchSection("generation", spinner);
2159
2139
  const coreFiles = [];
2160
2140
  const tailoredFiles = [];
2161
2141
  for (const file of fileStatuses) {
@@ -2184,8 +2164,6 @@ var ProgressRenderer = class {
2184
2164
  }
2185
2165
  };
2186
2166
  const lines = [];
2187
- lines.push(chalk4.dim(` Generated ${completedCount}/${totalCount} files`));
2188
- lines.push("");
2189
2167
  const coreCompleted = coreFiles.filter((f) => f.status === "completed").length;
2190
2168
  const coreGenerating = coreFiles.filter((f) => f.status === "generating");
2191
2169
  if (coreCompleted === coreFiles.length) {
@@ -2215,20 +2193,76 @@ var ProgressRenderer = class {
2215
2193
  }
2216
2194
  }
2217
2195
  }
2218
- lines.push("");
2219
- for (const line of lines) {
2220
- process.stdout.write(`\x1B[2K${line}
2221
- `);
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("");
2222
2213
  }
2223
- for (let i = lines.length; i < this.lastFileStatusLineCount; i++) {
2224
- 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;
2225
2257
  }
2226
- this.lastFileStatusLineCount = lines.length;
2227
- spinner.start();
2228
2258
  }
2229
2259
  renderPush(spinner) {
2230
2260
  if (this.pushShown) return;
2231
2261
  this.pushShown = true;
2262
+ if (this.activeSection) {
2263
+ logUpdate.done();
2264
+ this.activeSection = null;
2265
+ }
2232
2266
  spinner.stop();
2233
2267
  console.log("");
2234
2268
  console.log(chalk4.cyan.bold(" \u2500\u2500 Saving Context \u2500\u2500"));
@@ -2236,37 +2270,17 @@ var ProgressRenderer = class {
2236
2270
  console.log("");
2237
2271
  spinner.start();
2238
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
+ }
2239
2280
  getSpinnerText(syncResult) {
2240
2281
  const stepLabel = syncResult.stepLabel ?? syncResult.currentStep ?? "Processing";
2241
2282
  const overallPct = computeOverallProgress(syncResult);
2242
- let progressText = stepLabel;
2243
- if (syncResult.scanProgress && !syncResult.scanProgress.summary) {
2244
- progressText = `Scanning batch ${syncResult.scanProgress.currentBatch}/${syncResult.scanProgress.totalBatches}`;
2245
- } else if (syncResult.validationProgress && syncResult.validationProgress.status !== "complete") {
2246
- const vp = syncResult.validationProgress;
2247
- if (vp.status === "regenerating") {
2248
- progressText = `Improving files based on feedback (round ${vp.round})`;
2249
- } else {
2250
- progressText = `Validation round ${vp.round}/${vp.maxRounds}`;
2251
- }
2252
- } else if (syncResult.generationProgress) {
2253
- const gp = syncResult.generationProgress;
2254
- if (gp.fileStatuses && gp.fileStatuses.length > 0) {
2255
- const completed = gp.fileStatuses.filter((f) => f.status === "completed").length;
2256
- const total = gp.fileStatuses.length;
2257
- const generating = gp.fileStatuses.find((f) => f.status === "generating");
2258
- if (completed === total) {
2259
- progressText = stepLabel;
2260
- } else if (generating) {
2261
- progressText = `Generating ${generating.fileName} (${completed}/${total})`;
2262
- } else {
2263
- progressText = `Generating (${completed}/${total})`;
2264
- }
2265
- } else {
2266
- progressText = `Generating ${gp.currentFileName} (${gp.currentFile}/${gp.totalFiles})`;
2267
- }
2268
- }
2269
- return `${progressText}... ${chalk4.dim(`(${overallPct}%)`)}`;
2283
+ return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
2270
2284
  }
2271
2285
  update(syncResult, spinner) {
2272
2286
  if (syncResult.privacyShieldEnabled !== void 0) {
@@ -2275,8 +2289,8 @@ var ProgressRenderer = class {
2275
2289
  if (syncResult.discoveryResult) {
2276
2290
  this.renderDiscovery(syncResult.discoveryResult, spinner);
2277
2291
  }
2278
- if (syncResult.scanProgress?.summary && syncResult.scanProgress.summary.totalFiles > 0) {
2279
- this.renderScanSummary(syncResult.scanProgress.summary, spinner);
2292
+ if (syncResult.scanProgress) {
2293
+ this.renderScanProgress(syncResult.scanProgress, spinner);
2280
2294
  }
2281
2295
  if (syncResult.generationProgress?.fileStatuses && syncResult.generationProgress.fileStatuses.length > 0) {
2282
2296
  this.renderFileStatuses(syncResult.generationProgress.fileStatuses, spinner);
@@ -2287,7 +2301,9 @@ var ProgressRenderer = class {
2287
2301
  if (syncResult.currentStep === "push-context") {
2288
2302
  this.renderPush(spinner);
2289
2303
  }
2290
- spinner.text = this.getSpinnerText(syncResult);
2304
+ if (!this.activeSection) {
2305
+ spinner.text = this.getSpinnerText(syncResult);
2306
+ }
2291
2307
  }
2292
2308
  };
2293
2309
 
@@ -2319,7 +2335,7 @@ var MAX_POLL_ATTEMPTS = 7200;
2319
2335
  var DEFAULT_CONTEXT_FOLDER = "repowise-context";
2320
2336
  async function create() {
2321
2337
  const startTime = Date.now();
2322
- const spinner = ora("Checking authentication...").start();
2338
+ const spinner = ora({ text: "Checking authentication...", stream: process.stdout }).start();
2323
2339
  try {
2324
2340
  let credentials = await getValidCredentials2();
2325
2341
  if (!credentials) {
@@ -2498,6 +2514,7 @@ async function create() {
2498
2514
  continue;
2499
2515
  }
2500
2516
  if (syncResult.status === "completed") {
2517
+ progressRenderer.finalize();
2501
2518
  const generatedFiles = syncResult.filesGenerated ?? [];
2502
2519
  const fileCount = generatedFiles.length;
2503
2520
  if (fileCount > 0) {
@@ -2519,6 +2536,7 @@ async function create() {
2519
2536
  break;
2520
2537
  }
2521
2538
  if (syncResult.status === "failed") {
2539
+ progressRenderer.finalize();
2522
2540
  spinner.fail(chalk5.red(`Pipeline failed: ${syncResult.error ?? "Unknown error"}`));
2523
2541
  process.exitCode = 1;
2524
2542
  return;
@@ -2535,16 +2553,18 @@ async function create() {
2535
2553
  let downloadedCount = 0;
2536
2554
  let failedCount = 0;
2537
2555
  for (const file of files) {
2538
- if (file.fileName.includes("..") || file.fileName.includes("/")) {
2556
+ if (file.fileName.includes("..")) {
2539
2557
  failedCount++;
2540
2558
  continue;
2541
2559
  }
2542
- const urlResult = await apiRequest(`/v1/repos/${repoId}/context/${file.fileName}`);
2560
+ const urlResult = await apiRequest(`/v1/repos/${repoId}/context/files/${file.fileName}`);
2543
2561
  const presignedUrl = urlResult.data?.url ?? urlResult.url;
2544
2562
  const response = await fetch(presignedUrl);
2545
2563
  if (response.ok) {
2546
2564
  const content = await response.text();
2547
- writeFileSync2(join13(contextDir, file.fileName), content, "utf-8");
2565
+ const filePath = join13(contextDir, file.fileName);
2566
+ mkdirSync(dirname4(filePath), { recursive: true });
2567
+ writeFileSync2(filePath, content, "utf-8");
2548
2568
  downloadedCount++;
2549
2569
  } else {
2550
2570
  failedCount++;
@@ -2796,7 +2816,7 @@ async function status() {
2796
2816
  // src/commands/sync.ts
2797
2817
  import { execSync as execSync2 } from "child_process";
2798
2818
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
2799
- import { join as join15 } from "path";
2819
+ import { dirname as dirname5, join as join15 } from "path";
2800
2820
  import chalk8 from "chalk";
2801
2821
  import ora3 from "ora";
2802
2822
  var POLL_INTERVAL_MS2 = 3e3;
@@ -2826,7 +2846,7 @@ function formatElapsed2(ms) {
2826
2846
  }
2827
2847
  async function sync() {
2828
2848
  const startTime = Date.now();
2829
- const spinner = ora3("Checking authentication...").start();
2849
+ const spinner = ora3({ text: "Checking authentication...", stream: process.stdout }).start();
2830
2850
  try {
2831
2851
  let credentials = await getValidCredentials2();
2832
2852
  if (!credentials) {
@@ -2932,6 +2952,7 @@ async function sync() {
2932
2952
  continue;
2933
2953
  }
2934
2954
  if (syncResult.status === "completed") {
2955
+ progressRenderer.finalize();
2935
2956
  const generatedFiles = syncResult.filesGenerated ?? [];
2936
2957
  const fileCount = generatedFiles.length;
2937
2958
  if (fileCount > 0) {
@@ -2942,6 +2963,7 @@ async function sync() {
2942
2963
  break;
2943
2964
  }
2944
2965
  if (syncResult.status === "failed") {
2966
+ progressRenderer.finalize();
2945
2967
  spinner.fail(chalk8.red(`Sync failed: ${syncResult.error ?? "Unknown error"}`));
2946
2968
  process.exitCode = 1;
2947
2969
  return;
@@ -2957,16 +2979,18 @@ async function sync() {
2957
2979
  let downloadedCount = 0;
2958
2980
  let failedCount = 0;
2959
2981
  for (const file of files) {
2960
- if (file.fileName.includes("..") || file.fileName.includes("/")) {
2982
+ if (file.fileName.includes("..")) {
2961
2983
  failedCount++;
2962
2984
  continue;
2963
2985
  }
2964
- const urlResult = await apiRequest(`/v1/repos/${repoId}/context/${file.fileName}`);
2986
+ const urlResult = await apiRequest(`/v1/repos/${repoId}/context/files/${file.fileName}`);
2965
2987
  const presignedUrl = urlResult.data?.url ?? urlResult.url;
2966
2988
  const response = await fetch(presignedUrl);
2967
2989
  if (response.ok) {
2968
2990
  const content = await response.text();
2969
- writeFileSync3(join15(contextDir, file.fileName), content, "utf-8");
2991
+ const filePath = join15(contextDir, file.fileName);
2992
+ mkdirSync2(dirname5(filePath), { recursive: true });
2993
+ writeFileSync3(filePath, content, "utf-8");
2970
2994
  downloadedCount++;
2971
2995
  } else {
2972
2996
  failedCount++;
@@ -3184,7 +3208,7 @@ async function config() {
3184
3208
 
3185
3209
  // bin/repowise.ts
3186
3210
  var __filename = fileURLToPath3(import.meta.url);
3187
- var __dirname = dirname3(__filename);
3211
+ var __dirname = dirname6(__filename);
3188
3212
  var pkg = JSON.parse(readFileSync2(join16(__dirname, "..", "..", "package.json"), "utf-8"));
3189
3213
  var program = new Command();
3190
3214
  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.79",
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"