repowise 0.1.78 → 0.1.80

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.
@@ -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;
@@ -2141,8 +2154,6 @@ var ProgressRenderer = class {
2141
2154
  const snapshot = fileStatuses.map((f) => `${f.fileName}:${f.status}`).join(",");
2142
2155
  if (snapshot === this.lastFileStatusSnapshot) return;
2143
2156
  this.lastFileStatusSnapshot = snapshot;
2144
- const completedCount = fileStatuses.filter((f) => f.status === "completed").length;
2145
- const totalCount = fileStatuses.length;
2146
2157
  spinner.stop();
2147
2158
  if (!this.fileStatusHeaderShown) {
2148
2159
  this.fileStatusHeaderShown = true;
@@ -2184,17 +2195,33 @@ var ProgressRenderer = class {
2184
2195
  }
2185
2196
  };
2186
2197
  const lines = [];
2187
- lines.push(chalk4.dim(` Generated ${completedCount}/${totalCount} files`));
2188
- lines.push("");
2189
- lines.push(` ${chalk4.bold("Core")}`);
2190
- for (const file of coreFiles) {
2191
- lines.push(formatFileLine(file, maxCoreLen));
2198
+ const coreCompleted = coreFiles.filter((f) => f.status === "completed").length;
2199
+ const coreGenerating = coreFiles.filter((f) => f.status === "generating");
2200
+ if (coreCompleted === coreFiles.length) {
2201
+ lines.push(
2202
+ ` ${chalk4.bold("Core")} ${chalk4.green("\u2713")} ${coreCompleted}/${coreFiles.length} completed`
2203
+ );
2204
+ } else {
2205
+ lines.push(` ${chalk4.bold("Core")} (${coreCompleted}/${coreFiles.length} completed)`);
2206
+ for (const file of coreGenerating) {
2207
+ lines.push(formatFileLine(file, maxCoreLen));
2208
+ }
2192
2209
  }
2193
2210
  if (tailoredFiles.length > 0) {
2211
+ const tailoredCompleted = tailoredFiles.filter((f) => f.status === "completed").length;
2212
+ const tailoredGenerating = tailoredFiles.filter((f) => f.status === "generating");
2194
2213
  lines.push("");
2195
- lines.push(` ${chalk4.bold("Tailored")}`);
2196
- for (const file of tailoredFiles) {
2197
- lines.push(formatFileLine(file, maxTailoredLen));
2214
+ if (tailoredCompleted === tailoredFiles.length) {
2215
+ lines.push(
2216
+ ` ${chalk4.bold("Tailored")} ${chalk4.green("\u2713")} ${tailoredCompleted}/${tailoredFiles.length} completed`
2217
+ );
2218
+ } else {
2219
+ lines.push(
2220
+ ` ${chalk4.bold("Tailored")} (${tailoredCompleted}/${tailoredFiles.length} completed)`
2221
+ );
2222
+ for (const file of tailoredGenerating) {
2223
+ lines.push(formatFileLine(file, maxTailoredLen));
2224
+ }
2198
2225
  }
2199
2226
  }
2200
2227
  lines.push("");
@@ -2224,6 +2251,13 @@ var ProgressRenderer = class {
2224
2251
  let progressText = stepLabel;
2225
2252
  if (syncResult.scanProgress && !syncResult.scanProgress.summary) {
2226
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
+ }
2227
2261
  } else if (syncResult.generationProgress) {
2228
2262
  const gp = syncResult.generationProgress;
2229
2263
  if (gp.fileStatuses && gp.fileStatuses.length > 0) {
@@ -2233,20 +2267,21 @@ var ProgressRenderer = class {
2233
2267
  if (completed === total) {
2234
2268
  progressText = stepLabel;
2235
2269
  } else if (generating) {
2236
- progressText = `Generating ${generating.fileName} (${completed}/${total})`;
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})`;
2237
2279
  } else {
2238
2280
  progressText = `Generating (${completed}/${total})`;
2239
2281
  }
2240
2282
  } else {
2241
2283
  progressText = `Generating ${gp.currentFileName} (${gp.currentFile}/${gp.totalFiles})`;
2242
2284
  }
2243
- } else if (syncResult.validationProgress && syncResult.validationProgress.status !== "complete") {
2244
- const vp = syncResult.validationProgress;
2245
- if (vp.status === "regenerating") {
2246
- progressText = `Improving files based on feedback (round ${vp.round})`;
2247
- } else {
2248
- progressText = `Validation round ${vp.round}/${vp.maxRounds}`;
2249
- }
2250
2285
  }
2251
2286
  return `${progressText}... ${chalk4.dim(`(${overallPct}%)`)}`;
2252
2287
  }
@@ -2301,7 +2336,7 @@ var MAX_POLL_ATTEMPTS = 7200;
2301
2336
  var DEFAULT_CONTEXT_FOLDER = "repowise-context";
2302
2337
  async function create() {
2303
2338
  const startTime = Date.now();
2304
- const spinner = ora("Checking authentication...").start();
2339
+ const spinner = ora({ text: "Checking authentication...", stream: process.stdout }).start();
2305
2340
  try {
2306
2341
  let credentials = await getValidCredentials2();
2307
2342
  if (!credentials) {
@@ -2517,16 +2552,18 @@ async function create() {
2517
2552
  let downloadedCount = 0;
2518
2553
  let failedCount = 0;
2519
2554
  for (const file of files) {
2520
- if (file.fileName.includes("..") || file.fileName.includes("/")) {
2555
+ if (file.fileName.includes("..")) {
2521
2556
  failedCount++;
2522
2557
  continue;
2523
2558
  }
2524
- const urlResult = await apiRequest(`/v1/repos/${repoId}/context/${file.fileName}`);
2559
+ const urlResult = await apiRequest(`/v1/repos/${repoId}/context/files/${file.fileName}`);
2525
2560
  const presignedUrl = urlResult.data?.url ?? urlResult.url;
2526
2561
  const response = await fetch(presignedUrl);
2527
2562
  if (response.ok) {
2528
2563
  const content = await response.text();
2529
- writeFileSync2(join13(contextDir, file.fileName), content, "utf-8");
2564
+ const filePath = join13(contextDir, file.fileName);
2565
+ mkdirSync(dirname4(filePath), { recursive: true });
2566
+ writeFileSync2(filePath, content, "utf-8");
2530
2567
  downloadedCount++;
2531
2568
  } else {
2532
2569
  failedCount++;
@@ -2778,7 +2815,7 @@ async function status() {
2778
2815
  // src/commands/sync.ts
2779
2816
  import { execSync as execSync2 } from "child_process";
2780
2817
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
2781
- import { join as join15 } from "path";
2818
+ import { dirname as dirname5, join as join15 } from "path";
2782
2819
  import chalk8 from "chalk";
2783
2820
  import ora3 from "ora";
2784
2821
  var POLL_INTERVAL_MS2 = 3e3;
@@ -2808,7 +2845,7 @@ function formatElapsed2(ms) {
2808
2845
  }
2809
2846
  async function sync() {
2810
2847
  const startTime = Date.now();
2811
- const spinner = ora3("Checking authentication...").start();
2848
+ const spinner = ora3({ text: "Checking authentication...", stream: process.stdout }).start();
2812
2849
  try {
2813
2850
  let credentials = await getValidCredentials2();
2814
2851
  if (!credentials) {
@@ -2939,16 +2976,18 @@ async function sync() {
2939
2976
  let downloadedCount = 0;
2940
2977
  let failedCount = 0;
2941
2978
  for (const file of files) {
2942
- if (file.fileName.includes("..") || file.fileName.includes("/")) {
2979
+ if (file.fileName.includes("..")) {
2943
2980
  failedCount++;
2944
2981
  continue;
2945
2982
  }
2946
- const urlResult = await apiRequest(`/v1/repos/${repoId}/context/${file.fileName}`);
2983
+ const urlResult = await apiRequest(`/v1/repos/${repoId}/context/files/${file.fileName}`);
2947
2984
  const presignedUrl = urlResult.data?.url ?? urlResult.url;
2948
2985
  const response = await fetch(presignedUrl);
2949
2986
  if (response.ok) {
2950
2987
  const content = await response.text();
2951
- writeFileSync3(join15(contextDir, file.fileName), content, "utf-8");
2988
+ const filePath = join15(contextDir, file.fileName);
2989
+ mkdirSync2(dirname5(filePath), { recursive: true });
2990
+ writeFileSync3(filePath, content, "utf-8");
2952
2991
  downloadedCount++;
2953
2992
  } else {
2954
2993
  failedCount++;
@@ -3166,7 +3205,7 @@ async function config() {
3166
3205
 
3167
3206
  // bin/repowise.ts
3168
3207
  var __filename = fileURLToPath3(import.meta.url);
3169
- var __dirname = dirname3(__filename);
3208
+ var __dirname = dirname6(__filename);
3170
3209
  var pkg = JSON.parse(readFileSync2(join16(__dirname, "..", "..", "package.json"), "utf-8"));
3171
3210
  var program = new Command();
3172
3211
  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.78",
3
+ "version": "0.1.80",
4
4
  "type": "module",
5
5
  "description": "AI-optimized codebase context generator",
6
6
  "bin": {