| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- /**
- * Directory Management
- *
- * Manages the .codegraph/ directory structure for CodeGraph data.
- */
- import * as fs from 'fs';
- import * as path from 'path';
- /**
- * CodeGraph directory name
- */
- export const CODEGRAPH_DIR = '.codegraph';
- /**
- * Get the .codegraph directory path for a project
- */
- export function getCodeGraphDir(projectRoot: string): string {
- return path.join(projectRoot, CODEGRAPH_DIR);
- }
- /**
- * Check if a project has been initialized with CodeGraph
- * Requires both .codegraph/ directory AND codegraph.db to exist
- */
- export function isInitialized(projectRoot: string): boolean {
- const codegraphDir = getCodeGraphDir(projectRoot);
- if (!fs.existsSync(codegraphDir) || !fs.statSync(codegraphDir).isDirectory()) {
- return false;
- }
- // Must have codegraph.db, not just .codegraph folder
- const dbPath = path.join(codegraphDir, 'codegraph.db');
- return fs.existsSync(dbPath);
- }
- /**
- * Find the nearest parent directory containing .codegraph/
- *
- * Walks up from the given path to find a CodeGraph-initialized project,
- * similar to how git finds .git/ directories.
- *
- * @param startPath - Directory to start searching from
- * @returns The project root containing .codegraph/, or null if not found
- */
- export function findNearestCodeGraphRoot(startPath: string): string | null {
- let current = path.resolve(startPath);
- const root = path.parse(current).root;
- while (current !== root) {
- if (isInitialized(current)) {
- return current;
- }
- const parent = path.dirname(current);
- if (parent === current) break; // Reached filesystem root
- current = parent;
- }
- // Check root as well
- if (isInitialized(current)) {
- return current;
- }
- return null;
- }
- /**
- * Create the .codegraph directory structure
- * Note: Only throws if codegraph.db already exists, not just if .codegraph/ exists.
- */
- export function createDirectory(projectRoot: string): void {
- const codegraphDir = getCodeGraphDir(projectRoot);
- const dbPath = path.join(codegraphDir, 'codegraph.db');
- // Only throw if CodeGraph is actually initialized (db exists)
- // .codegraph/ folder alone is fine
- if (fs.existsSync(dbPath)) {
- throw new Error(`CodeGraph already initialized in ${projectRoot}`);
- }
- // Create main directory (if it doesn't exist)
- fs.mkdirSync(codegraphDir, { recursive: true });
- // Create .gitignore inside .codegraph (if it doesn't exist)
- const gitignorePath = path.join(codegraphDir, '.gitignore');
- if (!fs.existsSync(gitignorePath)) {
- const gitignoreContent = `# CodeGraph data files
- # These are local to each machine and should not be committed
- # Database
- *.db
- *.db-wal
- *.db-shm
- # Cache
- cache/
- # Logs
- *.log
- `;
- fs.writeFileSync(gitignorePath, gitignoreContent, 'utf-8');
- }
- }
- /**
- * Remove the .codegraph directory
- */
- export function removeDirectory(projectRoot: string): void {
- const codegraphDir = getCodeGraphDir(projectRoot);
- if (!fs.existsSync(codegraphDir)) {
- return;
- }
- // Recursively remove directory
- fs.rmSync(codegraphDir, { recursive: true, force: true });
- }
- /**
- * Get all files in the .codegraph directory
- */
- export function listDirectoryContents(projectRoot: string): string[] {
- const codegraphDir = getCodeGraphDir(projectRoot);
- if (!fs.existsSync(codegraphDir)) {
- return [];
- }
- const files: string[] = [];
- function walkDir(dir: string, prefix: string = ''): void {
- const entries = fs.readdirSync(dir, { withFileTypes: true });
- for (const entry of entries) {
- const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
- if (entry.isDirectory()) {
- walkDir(path.join(dir, entry.name), relativePath);
- } else {
- files.push(relativePath);
- }
- }
- }
- walkDir(codegraphDir);
- return files;
- }
- /**
- * Get the total size of the .codegraph directory in bytes
- */
- export function getDirectorySize(projectRoot: string): number {
- const codegraphDir = getCodeGraphDir(projectRoot);
- if (!fs.existsSync(codegraphDir)) {
- return 0;
- }
- let totalSize = 0;
- function walkDir(dir: string): void {
- const entries = fs.readdirSync(dir, { withFileTypes: true });
- for (const entry of entries) {
- const fullPath = path.join(dir, entry.name);
- if (entry.isDirectory()) {
- walkDir(fullPath);
- } else {
- const stats = fs.statSync(fullPath);
- totalSize += stats.size;
- }
- }
- }
- walkDir(codegraphDir);
- return totalSize;
- }
- /**
- * Ensure a subdirectory exists within .codegraph
- */
- export function ensureSubdirectory(projectRoot: string, subdirName: string): string {
- const subdirPath = path.join(getCodeGraphDir(projectRoot), subdirName);
- if (!fs.existsSync(subdirPath)) {
- fs.mkdirSync(subdirPath, { recursive: true });
- }
- return subdirPath;
- }
- /**
- * Check if the .codegraph directory has valid structure
- */
- export function validateDirectory(projectRoot: string): {
- valid: boolean;
- errors: string[];
- } {
- const errors: string[] = [];
- const codegraphDir = getCodeGraphDir(projectRoot);
- if (!fs.existsSync(codegraphDir)) {
- errors.push('CodeGraph directory does not exist');
- return { valid: false, errors };
- }
- if (!fs.statSync(codegraphDir).isDirectory()) {
- errors.push('.codegraph exists but is not a directory');
- return { valid: false, errors };
- }
- // Check for required files
- const gitignorePath = path.join(codegraphDir, '.gitignore');
- if (!fs.existsSync(gitignorePath)) {
- errors.push('.gitignore missing in .codegraph directory');
- }
- return {
- valid: errors.length === 0,
- errors,
- };
- }
|