DevOps

Vigil: AI-Powered GitHub Repository Intelligence

Austin H.•September 19, 2026•14 min read
#github#ai#automation#security#ci-cd#remediation

Why Vigil?

Maintaining healthy repositories at scale is manual, repetitive, and error-prone. Vigil automates the entire audit → fix → verify loop using AI agents that understand your codebase.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        Vigil Pipeline                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐  │
│  │  Scan    │───▶│  Analyze │───▶│  Fix     │───▶│  Verify  │  │
│  │  Repo    │    │  Issues  │    │  Generate│    │  & PR    │  │
│  └──────────┘    └──────────┘    └──────────┘    └──────────┘  │
│       │              │              │              │            │
│       ▼              ▼              ▼              ▼            │
│  • Files      • Categorize   • Generate     • Run tests        │
│  • Config     • Prioritize   • Validate    • Security scan     │
│  • History    • Deduplicate  • Test        • Build check       │
│  • Dependencies                • Format     • Merge when green  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Audit Categories

1. Documentation Health

// src/audits/docs.ts
export async function auditDocs(repo: RepoContext): Promise<Issue[]> {
  const issues: Issue[] = [];
  
  // README completeness
  if (!repo.hasFile('README.md')) {
    issues.push({ type: 'missing-readme', severity: 'high' });
  }
  
  // API docs coverage
  const apiCoverage = await calculateAPICoverage(repo);
  if (apiCoverage < 0.8) {
    issues.push({ type: 'low-api-docs', severity: 'medium', value: apiCoverage });
  }
  
  // Stale docs detection
  const staleDocs = await findStaleDocs(repo);
  issues.push(...staleDocs.map(d => ({ type: 'stale-docs', file: d, severity: 'low' })));
  
  return issues;
}

2. CI/CD Health

// src/audits/ci.ts
export async function auditCI(repo: RepoContext): Promise<Issue[]> {
  const issues: Issue[] = [];
  const workflows = await repo.getWorkflows();
  
  for (const wf of workflows) {
    // Missing security scanning
    if (!wf.hasJob('security')) {
      issues.push({ type: 'missing-security-scan', workflow: wf.name, severity: 'high' });
    }
    
    // No dependency review
    if (!wf.hasStep('dependency-review')) {
      issues.push({ type: 'missing-dep-review', workflow: wf.name, severity: 'medium' });
    }
    
    // Flaky test detection
    const flaky = await detectFlakyTests(wf);
    if (flaky.length > 0) {
      issues.push({ type: 'flaky-tests', tests: flaky, severity: 'medium' });
    }
  }
  
  return issues;
}

3. Security Posture

// src/audits/security.ts
export async function auditSecurity(repo: RepoContext): Promise<Issue[]> {
  const issues: Issue[] = [];
  
  // Secret scanning
  const secrets = await scanSecrets(repo);
  issues.push(...secrets.map(s => ({ type: 'exposed-secret', ...s, severity: 'critical' })));
  
  // Dependency vulnerabilities
  const vulns = await scanDependencies(repo);
  issues.push(...vulns.map(v => ({ type: 'vulnerable-dependency', ...v, severity: v.severity })));
  
  // CodeQL / Semgrep findings
  const staticFindings = await runStaticAnalysis(repo);
  issues.push(...staticFindings.map(f => ({ type: 'static-analysis', ...f })));
  
  return issues;
}

4. Test Coverage & Quality

// src/audits/tests.ts
export async function auditTests(repo: RepoContext): Promise<Issue[]> {
  const issues: Issue[] = [];
  const coverage = await getCoverage(repo);
  
  if (coverage.lines < 0.8) {
    issues.push({ type: 'low-coverage', metric: 'lines', value: coverage.lines, severity: 'medium' });
  }
  
  // Missing test types
  const hasUnit = await repo.hasTests('unit');
  const hasIntegration = await repo.hasTests('integration');
  const hasE2E = await repo.hasTests('e2e');
  
  if (!hasUnit) issues.push({ type: 'missing-unit-tests', severity: 'high' });
  if (!hasIntegration) issues.push({ type: 'missing-integration-tests', severity: 'medium' });
  if (!hasE2E) issues.push({ type: 'missing-e2e-tests', severity: 'low' });
  
  return issues;
}

Automated Fix Generation

Each issue type has a corresponding fix generator:

// src/fixes/generator.ts
export class FixGenerator {
  private generators = new Map<IssueType, FixGeneratorFn>([
    ['missing-readme', generateREADME],
    ['missing-security-scan', addSecurityWorkflow],
    ['exposed-secret', rotateSecretAndRemove],
    ['vulnerable-dependency', updateDependency],
    ['low-coverage', generateMissingTests],
    ['stale-docs', updateDocumentation],
    // ... 50+ generators
  ]);
  
  async generateFix(issue: Issue, context: RepoContext): Promise<Fix> {
    const generator = this.generators.get(issue.type);
    if (!generator) return { type: 'manual', reason: 'No generator available' };
    
    return await generator(issue, context);
  }
}

Fix Verification Pipeline

Every generated fix goes through verification before PR creation:

// src/verification/pipeline.ts
export async function verifyFix(fix: Fix, repo: RepoContext): Promise<VerificationResult> {
  // 1. Create temporary branch
  const branch = await repo.createBranch(`vigil/fix-${fix.id}`);
  
  // 2. Apply fix
  await applyFix(branch, fix);
  
  // 3. Run verification checks
  const results = await Promise.all([
    runTests(branch),
    runSecurityScan(branch),
    runBuild(branch),
    runLint(branch),
    runTypeCheck(branch),
  ]);
  
  // 4. All must pass
  const allPass = results.every(r => r.pass);
  
  if (allPass) {
    // 5. Create PR with fix
    await createPR(branch, fix);
    return { verified: true, prUrl: pr.url };
  } else {
    // 6. Clean up, report failure
    await branch.delete();
    return { verified: false, failures: results.filter(r => !r.pass) };
  }
}

Results

| Metric | Before Vigil | After Vigil | |--------|-------------|-------------| | Avg issues/repo | 47 | 3 | | Time to fix critical | 4 hours | 12 minutes | | Security findings in prod | 12/quarter | 0 | | Test coverage | 62% | 91% | | Stale docs | 34% | 2% |

Key Takeaways

  • AI agents can reliably audit entire repo health across 6+ dimensions
  • Fix generation + verification eliminates "PR roulette" where fixes break things
  • Prioritization by severity + exploitability focuses effort where it matters
  • Automated PRs with passing CI mean maintainers just review and merge
  • The system learns: fixed patterns become prevention rules for new repos

Code References

Further Reading

Conclusion

Vigil transforms repository maintenance from reactive firefighting to proactive health management. Across 50+ repos, it's caught critical vulnerabilities before deployment, eliminated entire classes of CI failures, and raised our org-wide test coverage from 62% to 91%. The key insight: audit → fix → verify as a single atomic pipeline, not three separate manual steps.

Thanks for reading!

Read More Articles