feat: add progress bar to init/reinit with per-file tracking
- processFiles() now accepts onProgress callback (completed, total, currentFile) - InitOptions.onProgress receives structured ProgressInfo object - initProject aggregates progress across all directories - CLI renders ASCII progress bar: [████░░░░░░] 3/20 | filename.ts - Pi extension sends structured updates with percentage + file details - All 52 tests passing
This commit is contained in:
+48
-7
@@ -12,11 +12,19 @@ import { writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import type { LLMClient } from "./llm/llm-client.js";
|
||||
|
||||
export interface ProgressInfo {
|
||||
message: string;
|
||||
completed: number;
|
||||
total: number;
|
||||
currentFile?: string;
|
||||
dir?: string;
|
||||
}
|
||||
|
||||
export interface InitOptions {
|
||||
verbose?: boolean;
|
||||
llmClient?: LLMClient;
|
||||
cacheDir?: string;
|
||||
onProgress?: (message: string) => void;
|
||||
onProgress?: (info: ProgressInfo) => void;
|
||||
}
|
||||
|
||||
export async function initProject(
|
||||
@@ -24,15 +32,40 @@ export async function initProject(
|
||||
options: InitOptions = {},
|
||||
): Promise<void> {
|
||||
const entries = discoverProject(rootPath);
|
||||
options.onProgress?.(`Scanning ${entries.length} directories...`);
|
||||
const totalFiles = entries.reduce((sum, e) => sum + e.files.length, 0);
|
||||
let globalCompleted = 0;
|
||||
|
||||
options.onProgress?.({
|
||||
message: `Scanning ${entries.length} directories (${totalFiles} files)...`,
|
||||
completed: 0,
|
||||
total: totalFiles,
|
||||
});
|
||||
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
options.onProgress?.(`[${i + 1}/${entries.length}] Analyzing ${entry.relativePath} (${entry.files.length} files)...`);
|
||||
await generateDirectoryMap(entry, options.llmClient, options.cacheDir, options.onProgress);
|
||||
await generateDirectoryMap(
|
||||
entry,
|
||||
options.llmClient,
|
||||
options.cacheDir,
|
||||
(info) => {
|
||||
globalCompleted = info.completed + entries
|
||||
.slice(0, i)
|
||||
.reduce((sum, e) => sum + e.files.length, 0);
|
||||
options.onProgress?.({
|
||||
...info,
|
||||
completed: globalCompleted,
|
||||
total: totalFiles,
|
||||
dir: entry.relativePath,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
options.onProgress?.(`Generated ${entries.length} .pi-map.md files`);
|
||||
options.onProgress?.({
|
||||
message: `Generated ${entries.length} .pi-map.md files`,
|
||||
completed: totalFiles,
|
||||
total: totalFiles,
|
||||
});
|
||||
if (options.verbose !== false) {
|
||||
console.log(`Generated ${entries.length} .pi-map.md files`);
|
||||
}
|
||||
@@ -42,19 +75,27 @@ export async function generateDirectoryMap(
|
||||
entry: DirectoryEntry,
|
||||
llmClient?: LLMClient,
|
||||
cacheDir?: string,
|
||||
onProgress?: (message: string) => void,
|
||||
onProgress?: (info: ProgressInfo) => void,
|
||||
): Promise<FileEntry[]> {
|
||||
// Process files in parallel (4 concurrent) with retry logic
|
||||
const fileData = await processFiles(
|
||||
entry.files,
|
||||
async (file) => {
|
||||
const filePath = join(entry.dirPath, file);
|
||||
onProgress?.(` → ${file}`);
|
||||
const llmData = await extractFileLLM(filePath, llmClient, cacheDir);
|
||||
const astData = await extractFileAST(filePath);
|
||||
return mergeFileData(file, llmData, astData);
|
||||
},
|
||||
{ concurrency: 4, batchDelayMs: 100, maxRetries: 2 },
|
||||
(completed, total, currentFile) => {
|
||||
onProgress?.({
|
||||
message: ` → ${currentFile}`,
|
||||
completed,
|
||||
total,
|
||||
currentFile,
|
||||
dir: entry.relativePath,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const packageData = await extractPackageLLM(
|
||||
|
||||
Reference in New Issue
Block a user