Skip to content

📜 TypeScript Scripts

Advanced TypeScript scripts for automation, customization, and integration with LookAtni File Markers.

Core TypeScript API

Installation & Setup

# Install TypeScript API
npm install lookatni-core

# Install type definitions
npm install @types/lookatni-core

Basic Usage

import { LookAtni, GenerateOptions, ExtractOptions } from 'lookatni-core';

// Initialize LookAtni instance
const lookatni = new LookAtni({
  version: '1.0',
  enableLogging: true,
  tempDirectory: './temp'
});

// Generate markers
const generateOptions: GenerateOptions = {
  source: './my-project',
  output: './markers/project.txt',
  include: ['src/**/*.ts', 'docs/**/*.md'],
  exclude: ['node_modules/**', '**/*.test.*'],
  compress: true,
  preserveStructure: true
};

const result = await lookatni.generate(generateOptions);
console.log(`Generated ${result.fileCount} files`);

Custom Generation Scripts

Automated Project Packaging

// scripts/package-project.ts
import { LookAtni, FileScanner, ProjectAnalyzer } from 'lookatni-core';
import { glob } from 'glob';
import * as path from 'path';
import * as fs from 'fs/promises';

interface PackageConfig {
  name: string;
  version: string;
  environments: {
    [key: string]: {
      include: string[];
      exclude: string[];
      compress: boolean;
    };
  };
}

class ProjectPackager {
  private lookatni: LookAtni;
  private config: PackageConfig;

  constructor(configPath: string) {
    this.lookatni = new LookAtni();
    this.config = require(configPath);
  }

  async packageAllEnvironments(): Promise<void> {
    for (const [env, settings] of Object.entries(this.config.environments)) {
      console.log(`📦 Packaging ${env} environment...`);

      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const outputFile = `${this.config.name}-${env}-${this.config.version}-${timestamp}.txt`;

      await this.packageEnvironment(env, settings, outputFile);
    }
  }

  private async packageEnvironment(
    environment: string, 
    settings: any, 
    outputFile: string
  ): Promise<void> {
    // Analyze project structure first
    const analyzer = new ProjectAnalyzer();
    const analysis = await analyzer.analyze('./');

    // Generate environment-specific markers
    const result = await this.lookatni.generate({
      source: './',
      output: `./packages/${outputFile}`,
      include: settings.include,
      exclude: settings.exclude,
      compress: settings.compress,
      metadata: {
        environment,
        analysis: analysis.summary,
        buildInfo: {
          timestamp: new Date().toISOString(),
          nodeVersion: process.version,
          platform: process.platform
        }
      }
    });

    // Validate the generated package
    const validation = await this.lookatni.validate(`./packages/${outputFile}`);

    if (!validation.isValid) {
      throw new Error(`Package validation failed: ${validation.errors.join(', ')}`);
    }

    console.log(`✅ ${environment} package created: ${outputFile}`);
    console.log(`   Files: ${result.fileCount}, Size: ${result.totalSize}`);
  }
}

// Usage
async function main() {
  const packager = new ProjectPackager('./package-config.json');
  await packager.packageAllEnvironments();
}

main().catch(console.error);

Smart Dependency Bundler

// scripts/dependency-bundler.ts
import { DependencyAnalyzer, FileGraph } from 'lookatni-core';
import * as ts from 'typescript';

class SmartDependencyBundler {
  private dependencyAnalyzer: DependencyAnalyzer;

  constructor() {
    this.dependencyAnalyzer = new DependencyAnalyzer();
  }

  async createComponentBundle(componentPath: string): Promise<string[]> {
    // Analyze TypeScript dependencies
    const sourceFile = ts.createSourceFile(
      componentPath,
      await fs.readFile(componentPath, 'utf8'),
      ts.ScriptTarget.Latest,
      true
    );

    const dependencies = this.extractDependencies(sourceFile);
    const fileGraph = await this.buildDependencyGraph(dependencies);

    return this.resolveBundleFiles(fileGraph);
  }

  private extractDependencies(sourceFile: ts.SourceFile): string[] {
    const dependencies: string[] = [];

    const visit = (node: ts.Node) => {
      if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
        const moduleSpecifier = node.moduleSpecifier;
        if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier)) {
          dependencies.push(moduleSpecifier.text);
        }
      }
      ts.forEachChild(node, visit);
    };

    visit(sourceFile);
    return dependencies;
  }

  private async buildDependencyGraph(dependencies: string[]): Promise<FileGraph> {
    return await this.dependencyAnalyzer.buildGraph({
      entryPoints: dependencies,
      resolveOptions: {
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
        modules: ['node_modules', 'src']
      }
    });
  }

  private resolveBundleFiles(graph: FileGraph): string[] {
    // Topological sort to determine bundle order
    return graph.getTopologicalOrder();
  }
}

// Usage example
const bundler = new SmartDependencyBundler();
const files = await bundler.createComponentBundle('./src/components/UserProfile.tsx');

const lookatni = new LookAtni();
await lookatni.generate({
  source: './',
  include: files,
  output: './bundles/user-profile-component.txt',
  preserveStructure: true
});

Custom Validation Scripts

Advanced Validation Framework

// scripts/custom-validator.ts
import { ValidationRule, ValidationContext, ValidationResult } from 'lookatni-core';

class CustomValidationRules {
  static securityRule: ValidationRule = {
    name: 'security-check',
    description: 'Check for sensitive data in files',
    severity: 'error',

    async validate(context: ValidationContext): Promise<ValidationResult> {
      const sensitivePatterns = [
        /(?:password|pwd|pass)\s*[:=]\s*['\"]?([^'\"\\s]+)/gi,
        /(?:api[_-]?key|apikey)\s*[:=]\s*['\"]?([^'\"\\s]+)/gi,
        /(?:secret|token)\s*[:=]\s*['\"]?([^'\"\\s]+)/gi
      ];

      const violations = [];

      for (const file of context.files) {
        for (const pattern of sensitivePatterns) {
          const matches = file.content.match(pattern);
          if (matches) {
            violations.push({
              file: file.path,
              line: this.findLineNumber(file.content, matches[0]),
              message: `Potential sensitive data: ${matches[0]}`,
              suggestion: 'Use environment variables or config files'
            });
          }
        }
      }

      return {
        passed: violations.length === 0,
        violations
      };
    },

    findLineNumber(content: string, match: string): number {
      const lines = content.substring(0, content.indexOf(match)).split('\n');
      return lines.length;
    }
  };

  static consistencyRule: ValidationRule = {
    name: 'naming-consistency',
    description: 'Enforce consistent file naming',
    severity: 'warning',

    async validate(context: ValidationContext): Promise<ValidationResult> {
      const violations = [];
      const patterns = {
        components: /^[A-Z][a-zA-Z]*\.(tsx?|jsx?)$/,
        utils: /^[a-z][a-zA-Z]*\.(ts|js)$/,
        tests: /^[a-zA-Z]+\.(test|spec)\.(ts|js)$/
      };

      for (const file of context.files) {
        const relativePath = file.path;

        if (relativePath.includes('/components/')) {
          if (!patterns.components.test(path.basename(relativePath))) {
            violations.push({
              file: file.path,
              message: 'Component files should use PascalCase',
              suggestion: 'Rename to PascalCase format (e.g., UserProfile.tsx)'
            });
          }
        }

        if (relativePath.includes('/utils/')) {
          if (!patterns.utils.test(path.basename(relativePath))) {
            violations.push({
              file: file.path,
              message: 'Utility files should use camelCase',
              suggestion: 'Rename to camelCase format (e.g., formatDate.ts)'
            });
          }
        }
      }

      return {
        passed: violations.length === 0,
        violations
      };
    }
  };
}

// Custom validator implementation
class ProjectValidator {
  private rules: ValidationRule[] = [
    CustomValidationRules.securityRule,
    CustomValidationRules.consistencyRule
  ];

  async validateProject(markerFile: string): Promise<void> {
    const lookatni = new LookAtni();
    const markers = await lookatni.parseMarkers(markerFile);

    const context: ValidationContext = {
      files: markers.entries,
      projectMetadata: markers.metadata,
      config: markers.config
    };

    console.log('🔍 Running custom validation rules...');

    for (const rule of this.rules) {
      console.log(`  Checking: ${rule.description}`);

      const result = await rule.validate(context);

      if (!result.passed) {
        console.log(`  ❌ ${rule.name}: ${result.violations.length} violations`);
        this.reportViolations(result.violations, rule.severity);
      } else {
        console.log(`  ✅ ${rule.name}: passed`);
      }
    }
  }

  private reportViolations(violations: any[], severity: string): void {
    violations.forEach(violation => {
      const icon = severity === 'error' ? '🚨' : '⚠️';
      console.log(`    ${icon} ${violation.file}:${violation.line || '?'}`);
      console.log(`       ${violation.message}`);
      if (violation.suggestion) {
        console.log(`       💡 ${violation.suggestion}`);
      }
    });
  }
}

// Usage
const validator = new ProjectValidator();
await validator.validateProject('./project-markers.txt');

Extraction Automation Scripts

Intelligent Extraction Manager

// scripts/extraction-manager.ts
import { ExtractionStrategy, ConflictResolver } from 'lookatni-core';
import * as inquirer from 'inquirer';

class IntelligentExtractionManager {
  private strategies: Map<string, ExtractionStrategy> = new Map();

  constructor() {
    this.setupStrategies();
  }

  private setupStrategies(): void {
    // Safe extraction strategy
    this.strategies.set('safe', {
      name: 'Safe Extraction',
      description: 'Never overwrites existing files',

      async extract(markers: any, outputPath: string): Promise<void> {
        const existingFiles = await this.scanExistingFiles(outputPath);
        const conflicts = this.detectConflicts(markers.entries, existingFiles);

        if (conflicts.length > 0) {
          console.log(`⚠️ Found ${conflicts.length} potential conflicts`);
          const resolution = await this.promptConflictResolution(conflicts);

          if (resolution === 'abort') {
            throw new Error('Extraction aborted due to conflicts');
          }
        }

        await this.extractWithBackups(markers, outputPath);
      }
    });

    // Merge extraction strategy
    this.strategies.set('merge', {
      name: 'Intelligent Merge',
      description: 'Attempts to merge compatible files',

      async extract(markers: any, outputPath: string): Promise<void> {
        for (const entry of markers.entries) {
          const targetPath = path.join(outputPath, entry.path);

          if (await this.fileExists(targetPath)) {
            if (await this.canMerge(entry, targetPath)) {
              await this.mergeFiles(entry, targetPath);
            } else {
              await this.handleConflict(entry, targetPath);
            }
          } else {
            await this.extractFile(entry, targetPath);
          }
        }
      }
    });
  }

  async extractWithStrategy(
    markerFile: string, 
    outputPath: string, 
    strategyName: string
  ): Promise<void> {
    const strategy = this.strategies.get(strategyName);
    if (!strategy) {
      throw new Error(`Unknown strategy: ${strategyName}`);
    }

    console.log(`🚀 Using strategy: ${strategy.name}`);
    console.log(`   ${strategy.description}`);

    const lookatni = new LookAtni();
    const markers = await lookatni.parseMarkers(markerFile);

    await strategy.extract(markers, outputPath);

    console.log('✅ Extraction completed successfully');
  }

  private async promptConflictResolution(conflicts: any[]): Promise<string> {
    const answers = await inquirer.prompt([
      {
        type: 'list',
        name: 'resolution',
        message: `How should conflicts be handled?`,
        choices: [
          { name: 'Create backups and overwrite', value: 'backup' },
          { name: 'Skip conflicting files', value: 'skip' },
          { name: 'Abort extraction', value: 'abort' },
          { name: 'Review each conflict individually', value: 'individual' }
        ]
      }
    ]);

    return answers.resolution;
  }

  private async canMerge(entry: any, existingPath: string): Promise<boolean> {
    // Check if files can be intelligently merged
    const existingContent = await fs.readFile(existingPath, 'utf8');

    // Simple merge compatibility check
    if (entry.type === 'json') {
      return this.canMergeJson(entry.content, existingContent);
    }

    if (entry.type === 'markdown') {
      return true; // Markdown can usually be safely appended
    }

    return false; // Default to no merge for code files
  }

  private canMergeJson(newContent: string, existingContent: string): boolean {
    try {
      const newObj = JSON.parse(newContent);
      const existingObj = JSON.parse(existingContent);

      // Check for key conflicts
      const newKeys = Object.keys(newObj);
      const existingKeys = Object.keys(existingObj);
      const conflicts = newKeys.filter(key => existingKeys.includes(key));

      return conflicts.length === 0;
    } catch {
      return false;
    }
  }

  private async mergeFiles(entry: any, targetPath: string): Promise<void> {
    console.log(`🔀 Merging ${entry.path}...`);

    if (entry.type === 'json') {
      await this.mergeJsonFiles(entry, targetPath);
    } else if (entry.type === 'markdown') {
      await this.mergeMarkdownFiles(entry, targetPath);
    }
  }

  private async mergeJsonFiles(entry: any, targetPath: string): Promise<void> {
    const existingContent = await fs.readFile(targetPath, 'utf8');
    const existingObj = JSON.parse(existingContent);
    const newObj = JSON.parse(entry.content);

    const merged = { ...existingObj, ...newObj };
    await fs.writeFile(targetPath, JSON.stringify(merged, null, 2));
  }

  private async mergeMarkdownFiles(entry: any, targetPath: string): Promise<void> {
    const existingContent = await fs.readFile(targetPath, 'utf8');
    const separator = '\n\n---\n\n';
    const merged = existingContent + separator + entry.content;

    await fs.writeFile(targetPath, merged);
  }
}

// Usage
const manager = new IntelligentExtractionManager();
await manager.extractWithStrategy(
  './project-markers.txt', 
  './output', 
  'merge'
);

Integration Scripts

CI/CD Integration

// scripts/ci-integration.ts
import { LookAtni, ContinuousIntegration } from 'lookatni-core';

class CIIntegration {
  private lookatni: LookAtni;
  private ci: ContinuousIntegration;

  constructor() {
    this.lookatni = new LookAtni();
    this.ci = new ContinuousIntegration();
  }

  async runBuildPipeline(): Promise<void> {
    console.log('🚀 Starting LookAtni CI Pipeline...');

    try {
      // Step 1: Generate markers for current build
      await this.generateBuildMarkers();

      // Step 2: Validate generated markers
      await this.validateMarkers();

      // Step 3: Run integration tests
      await this.runIntegrationTests();

      // Step 4: Create release artifacts
      await this.createReleaseArtifacts();

      console.log('✅ CI Pipeline completed successfully');

    } catch (error) {
      console.error('❌ CI Pipeline failed:', error);
      process.exit(1);
    }
  }

  private async generateBuildMarkers(): Promise<void> {
    const buildInfo = this.ci.getBuildInfo();

    await this.lookatni.generate({
      source: './',
      output: `./artifacts/build-${buildInfo.number}.txt`,
      include: ['src/**', 'docs/**', '*.json', '*.md'],
      exclude: ['node_modules/**', 'coverage/**', '**/*.test.*'],
      metadata: {
        buildNumber: buildInfo.number,
        commitSha: buildInfo.commitSha,
        branch: buildInfo.branch,
        timestamp: new Date().toISOString()
      }
    });
  }

  private async validateMarkers(): Promise<void> {
    const buildInfo = this.ci.getBuildInfo();
    const markerFile = `./artifacts/build-${buildInfo.number}.txt`;

    const result = await this.lookatni.validate(markerFile, {
      strict: true,
      customRules: ['security-check', 'naming-consistency']
    });

    if (!result.isValid) {
      throw new Error(`Validation failed: ${result.errors.join(', ')}`);
    }
  }

  private async runIntegrationTests(): Promise<void> {
    // Extract markers to temporary directory and run tests
    const buildInfo = this.ci.getBuildInfo();
    const markerFile = `./artifacts/build-${buildInfo.number}.txt`;
    const testDir = `./temp/test-${buildInfo.number}`;

    await this.lookatni.extract(markerFile, testDir);

    // Run tests in extracted directory
    await this.ci.runCommand('npm test', { cwd: testDir });

    // Cleanup
    await fs.rmdir(testDir, { recursive: true });
  }

  private async createReleaseArtifacts(): Promise<void> {
    const buildInfo = this.ci.getBuildInfo();

    if (buildInfo.isRelease) {
      // Create production release markers
      await this.lookatni.generate({
        source: './',
        output: `./artifacts/release-${buildInfo.version}.txt`,
        include: ['src/**', 'README.md', 'LICENSE', 'package.json'],
        exclude: ['**/*.test.*', '**/*.spec.*'],
        compress: true,
        metadata: {
          version: buildInfo.version,
          releaseNotes: buildInfo.releaseNotes
        }
      });

      console.log(`📦 Release artifact created: release-${buildInfo.version}.txt`);
    }
  }
}

// Usage in CI pipeline
if (process.env.CI) {
  const ci = new CIIntegration();
  ci.runBuildPipeline();
}

Next: Explore API Reference for complete interface documentation.